Merge "Compose gesture integrated fully into Launcher" into ub-launcher3-rvc-dev
diff --git a/Android.mk b/Android.mk
index 9cfcf17..fcd4a94 100644
--- a/Android.mk
+++ b/Android.mk
@@ -129,6 +129,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := \
+ SystemUI-statsd \
SystemUISharedLib \
launcherprotosnano \
launcher_log_protos_lite
@@ -201,6 +202,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := \
+ SystemUI-statsd \
SystemUISharedLib \
launcherprotosnano \
launcher_log_protos_lite
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index cac2d8f..f1b71e8 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -95,7 +95,18 @@
// Represents folder in a closed state.
message FolderIcon {
+ // Number of items inside folder.
optional int32 cardinality = 1;
+
+ // State of the folder label before the event.
+ optional FromState from_label_state = 2;
+
+ // State of the folder label after the event.
+ optional ToState to_label_state = 3;
+
+ // Details about actual folder label.
+ // Populated when folder label is not a PII.
+ optional string label_info = 4;
}
//////////////////////////////////////////////
@@ -120,3 +131,78 @@
HotseatContainer hotseat = 5;
}
}
+
+// Represents state of EditText field before update.
+enum FromState {
+ // Default value.
+ // Used when a FromState is not applicable, for example, during folder creation.
+ FROM_STATE_UNSPECIFIED = 0;
+
+ // EditText was empty.
+ // Eg: When a folder label is updated from empty string.
+ FROM_EMPTY = 1;
+
+ // EditText was non-empty and manually entered by the user.
+ // Eg: When a folder label is updated from a user-entered value.
+ FROM_CUSTOM = 2;
+
+ // EditText was non-empty and one of the suggestions.
+ // Eg: When a folder label is updated from a suggested value.
+ FROM_SUGGESTED = 3;
+}
+
+// Represents state of EditText field after update.
+enum ToState {
+ // Default value.
+ // Used when ToState is not applicable, for example, when folder label is updated to a different
+ // value when folder label suggestion feature is disabled.
+ TO_STATE_UNSPECIFIED = 0;
+
+ // User attempted to change the EditText, but was not changed.
+ UNCHANGED = 1;
+
+ // New label matches with primary(aka top) suggestion.
+ TO_SUGGESTION0 = 2;
+
+ // New value matches with second top suggestion even though the top suggestion was non-empty.
+ TO_SUGGESTION1_WITH_VALID_PRIMARY = 3;
+
+ // New value matches with second top suggestion given that top suggestion was empty.
+ TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 4;
+
+ // New value matches with third top suggestion even though the top suggestion was non-empty.
+ TO_SUGGESTION2_WITH_VALID_PRIMARY = 5;
+
+ // New value matches with third top suggestion given that top suggestion was empty.
+ TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 6;
+
+ // New value matches with 4th top suggestion even though the top suggestion was non-empty.
+ TO_SUGGESTION3_WITH_VALID_PRIMARY = 7;
+
+ // New value matches with 4th top suggestion given that top suggestion was empty.
+ TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 8;
+
+ // New value is empty even though the top suggestion was non-empty.
+ TO_EMPTY_WITH_VALID_PRIMARY = 9;
+
+ // New value is empty given that top suggestion was empty.
+ TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 10;
+
+ // New value is empty given that no suggestions were provided.
+ TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 11;
+
+ // New value is empty given that suggestions feature was disabled.
+ TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 12;
+
+ // New value is non-empty and does not match with any of the suggestions even though the top suggestion was non-empty.
+ TO_CUSTOM_WITH_VALID_PRIMARY = 13;
+
+ // New value is non-empty and not match with any suggestions given that top suggestion was empty.
+ TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 14;
+
+ // New value is non-empty and also no suggestions were provided.
+ TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 15;
+
+ // New value is non-empty and also suggestions feature was disable.
+ TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 16;
+}
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 04506b5..b2286f1 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -95,12 +95,6 @@
android:clearTaskOnLaunch="true"
android:exported="false" />
- <activity android:name="com.android.quickstep.LockScreenRecentsActivity"
- android:theme="@android:style/Theme.NoDisplay"
- android:showOnLockScreen="true"
- android:taskAffinity="${packageName}.locktask"
- android:directBootAware="true" />
-
<activity
android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:autoRemoveFromRecents="true"
diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
index c93cad6..b9621e4 100644
--- a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
+++ b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
@@ -24,13 +24,13 @@
<View
android:layout_width="match_parent"
android:layout_height="32dp"
- android:backgroundTint="?android:attr/colorAccent"
+ android:backgroundTint="?attr/eduHalfSheetBGColor"
android:background="@drawable/bottom_sheet_top_border" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/colorAccent"
+ android:background="?attr/eduHalfSheetBGColor"
android:orientation="vertical">
<TextView
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
index d93aea4..8477b10 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
@@ -26,10 +26,10 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.views.ArrowTipView;
import com.android.systemui.shared.system.LauncherEventUtil;
@@ -71,17 +71,16 @@
public static void scheduleShowIfNeeded(Launcher launcher) {
if (!hasSeenAllAppsTip(launcher)) {
- launcher.getStateManager().addStateListener(
- new LauncherStateManager.StateListener() {
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- if (finalState == ALL_APPS) {
- if (showAllAppsTipIfNecessary(launcher)) {
- launcher.getStateManager().removeStateListener(this);
- }
- }
+ launcher.getStateManager().addStateListener(new StateListener<LauncherState>() {
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if (finalState == ALL_APPS) {
+ if (showAllAppsTipIfNecessary(launcher)) {
+ launcher.getStateManager().removeStateListener(this);
}
- });
+ }
+ }
+ });
}
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
index 81a6070..914d9e9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -38,18 +38,18 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.Themes;
/**
* A view which shows a horizontal divider
*/
@TargetApi(Build.VERSION_CODES.O)
-public class AppsDividerView extends View implements LauncherStateManager.StateListener,
+public class AppsDividerView extends View implements StateListener<LauncherState>,
FloatingHeaderRow {
private static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index e68627a..ab3c71a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -32,7 +32,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
@@ -41,6 +40,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.MainThreadInitializedObject;
@@ -63,8 +63,8 @@
* 4) Maintains the current active client id (for the predictions) and all updates are performed on
* that client id.
*/
-public class PredictionUiStateManager implements StateListener, ItemInfoUpdateReceiver,
- OnIDPChangeListener, OnUpdateListener {
+public class PredictionUiStateManager implements StateListener<LauncherState>,
+ ItemInfoUpdateReceiver, OnIDPChangeListener, OnUpdateListener {
public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 7f8f0a0..e4d0adf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -121,7 +121,7 @@
if (!putIntoFolder.isEmpty()) {
ItemInfo firstItem = putIntoFolder.get(0);
FolderInfo folderInfo = new FolderInfo();
- folderInfo.title = "";
+ folderInfo.setTitle("");
mLauncher.getModelWriter().addItemToDatabase(folderInfo, firstItem.container,
firstItem.screenId, firstItem.cellX, firstItem.cellY);
folderInfo.contents.addAll(putIntoFolder);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 7520688..6761148 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -46,6 +46,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.views.DoubleShadowBubbleTextView;
/**
@@ -65,6 +66,7 @@
private final int mNormalizedIconRadius;
private final BlurMaskFilter mShadowFilter;
private int mPlateColor;
+ boolean mDrawForDrag = false;
public PredictedAppIcon(Context context) {
@@ -188,6 +190,10 @@
}
private void drawEffect(Canvas canvas, boolean isBadged) {
+ // Don't draw ring effect if item is about to be dragged.
+ if (mDrawForDrag) {
+ return;
+ }
mRingPath.reset();
getShape().addToPath(mRingPath, getOutlineOffsetX(), getOutlineOffsetY(),
mNormalizedIconRadius);
@@ -208,6 +214,26 @@
canvas.drawPath(mRingPath, mIconRingPaint);
}
+ @Override
+ public void getSourceVisualDragBounds(Rect bounds) {
+ super.getSourceVisualDragBounds(bounds);
+ if (!mIsPinned) {
+ int internalSize = (int) (bounds.width() * RING_EFFECT_RATIO);
+ bounds.inset(internalSize, internalSize);
+ }
+ }
+
+ @Override
+ public SafeCloseable prepareDrawDragView() {
+ mDrawForDrag = true;
+ invalidate();
+ SafeCloseable r = super.prepareDrawDragView();
+ return () -> {
+ r.close();
+ mDrawForDrag = false;
+ };
+ }
+
/**
* Creates and returns a new instance of PredictedAppIcon from WorkspaceItemInfo
*/
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 1876424..ad6a10b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -30,6 +30,7 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
@@ -38,7 +39,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AtomicAnimationFactory;
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -49,6 +49,8 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
@@ -98,7 +100,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- onStateOrResumeChanged();
+ onStateOrResumeChanging(false /* inTransition */);
}
@Override
@@ -113,11 +115,9 @@
@Override
protected void onActivityFlagsChanged(int changeBits) {
super.onActivityFlagsChanged(changeBits);
-
if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED
- | ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0
- && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0) {
- onStateOrResumeChanged();
+ | ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
+ onStateOrResumeChanging((getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0);
}
if (mHotseatPredictionController != null && ((changeBits & ACTIVITY_STATE_STARTED) != 0
@@ -162,14 +162,16 @@
/**
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
*/
- private void onStateOrResumeChanged() {
+ private void onStateOrResumeChanging(boolean inTransition) {
LauncherState state = getStateManager().getState();
DeviceProfile profile = getDeviceProfile();
- boolean visible = (state == NORMAL || state == OVERVIEW) && isUserActive()
+ boolean willUserBeActive = (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
+ boolean visible = (state == NORMAL || state == OVERVIEW)
+ && (willUserBeActive || isUserActive())
&& !profile.isVerticalBarLayout();
UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
profile.hotseatBarSizePx);
- if (state == NORMAL) {
+ if (state == NORMAL && !inTransition) {
((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
}
}
@@ -244,6 +246,9 @@
@Override
public TouchController[] createTouchControllers() {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.1");
+ }
Mode mode = SysUINavigationMode.getMode(this);
ArrayList<TouchController> list = new ArrayList<>();
@@ -251,7 +256,13 @@
if (mode == NO_BUTTON) {
list.add(new NoButtonQuickSwitchTouchController(this));
list.add(new NavBarToHomeTouchController(this));
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.2");
+ }
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.3");
+ }
list.add(new NoButtonNavbarToOverviewTouchController(this));
} else {
list.add(new FlingAndHoldTouchController(this));
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 357e9ec..fa0d3f3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -17,6 +17,7 @@
import android.content.Context;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -54,11 +55,7 @@
@Override
public float[] getOverviewScaleAndOffset(Launcher launcher) {
- return new float[] {getOverviewScale(launcher), NO_OFFSET};
- }
-
- private float getOverviewScale(Launcher launcher) {
- return ((RecentsView) launcher.getOverviewPanel()).getMaxScaleForFullScreen();
+ return getOverviewScaleAndOffsetForBackgroundState(launcher);
}
@Override
@@ -88,4 +85,11 @@
protected float getDepthUnchecked(Context context) {
return 1f;
}
+
+ public static float[] getOverviewScaleAndOffsetForBackgroundState(
+ BaseDraggingActivity activity) {
+ return new float[] {
+ ((RecentsView) activity.getOverviewPanel()).getMaxScaleForFullScreen(),
+ NO_OFFSET};
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 4e868b0..d5b0687 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -16,11 +16,10 @@
package com.android.launcher3.uioverrides.states;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Rect;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.views.RecentsView;
@@ -49,22 +48,23 @@
@Override
public float[] getOverviewScaleAndOffset(Launcher launcher) {
- Resources res = launcher.getBaseContext().getResources();
-
- Rect out = new Rect();
- launcher.<RecentsView>getOverviewPanel().getTaskSize(out);
- int taskHeight = out.height();
-
- float topMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
- float bottomMargin = res.getDimension(R.dimen.task_thumbnail_bottom_margin_with_actions);
- float newHeight = taskHeight + topMargin + bottomMargin;
- float scale = newHeight / taskHeight;
-
- return new float[] {scale, 0};
+ return getOverviewScaleAndOffsetForModalState(launcher);
}
@Override
public float getOverviewModalness() {
return 1.0f;
}
+
+ public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
+ Rect out = new Rect();
+ activity.<RecentsView>getOverviewPanel().getTaskSize(out);
+ int taskHeight = out.height();
+ activity.<RecentsView>getOverviewPanel().getModalTaskSize(out);
+ int newHeight = out.height();
+
+ float scale = (float) newHeight / taskHeight;
+
+ return new float[] {scale, NO_OFFSET};
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 6bc69f9..11593a1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -62,12 +62,12 @@
import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.LauncherStateManager.AtomicAnimationFactory;
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.SysUINavigationMode;
@@ -76,7 +76,7 @@
/**
* Animation factory for quickstep specific transitions
*/
-public class QuickstepAtomicAnimationFactory extends AtomicAnimationFactory {
+public class QuickstepAtomicAnimationFactory extends AtomicAnimationFactory<LauncherState> {
// Scale recents takes before animating in
private static final float RECENTS_PREPARE_SCALE = 1.33f;
@@ -153,7 +153,7 @@
config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
}
- LauncherStateManager stateManager = mLauncher.getStateManager();
+ StateManager<LauncherState> stateManager = mLauncher.getStateManager();
return stateManager.createAtomicAnimation(
stateManager.getCurrentStableState(), OVERVIEW, config);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index 05dd797..fac478e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -37,6 +37,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -47,6 +48,7 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.VibratorWrapper;
@@ -178,6 +180,9 @@
@Override
public boolean onDrag(float displacement, MotionEvent event) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "FlingAndHoldTouchController");
+ }
float upDisplacement = -displacement;
mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
|| upDisplacement < mMotionPauseMinDisplacement
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 06a481b..bf0690c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -25,6 +25,7 @@
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.animation.ValueAnimator;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
@@ -41,6 +42,7 @@
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -94,18 +96,37 @@
}
private boolean canInterceptTouch(MotionEvent ev) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NavBarToHomeTouchController.canInterceptTouch "
+ + ev);
+ }
boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0;
if (!cameFromNavBar) {
return false;
}
if (mStartState.overviewUi || mStartState == ALL_APPS) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED,
+ "NavBarToHomeTouchController.canInterceptTouch true 1 "
+ + mStartState.overviewUi + " " + (mStartState == ALL_APPS));
+ }
return true;
}
if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED,
+ "NavBarToHomeTouchController.canInterceptTouch true 2 "
+ + AbstractFloatingView.getTopOpenView(mLauncher).getClass()
+ .getSimpleName());
+ }
return true;
}
if (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
&& AssistantUtilities.isExcludedAssistantRunning()) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED,
+ "NavBarToHomeTouchController.canInterceptTouch true 3");
+ }
return true;
}
return false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 381ecf1..966e25b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -27,14 +27,16 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.graphics.PointF;
+import android.util.Log;
import android.view.MotionEvent;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
@@ -63,6 +65,9 @@
public NoButtonNavbarToOverviewTouchController(Launcher l) {
super(l);
mRecentsView = l.getOverviewPanel();
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NoButtonNavbarToOverviewTouchController.ctor");
+ }
}
@Override
@@ -146,6 +151,9 @@
@Override
public boolean onDrag(float yDisplacement, float xDisplacement, MotionEvent event) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NoButtonNavbarToOverviewTouchController");
+ }
if (mMotionPauseDetector.isPaused()) {
if (!mReachedOverview) {
mStartDisplacement.set(xDisplacement, yDisplacement);
@@ -165,7 +173,7 @@
protected void goToOverviewOnDragEnd(float velocity) {
float velocityDp = dpiFromPx(velocity);
boolean isFling = Math.abs(velocityDp) > 1;
- LauncherStateManager stateManager = mLauncher.getStateManager();
+ StateManager<LauncherState> stateManager = mLauncher.getStateManager();
boolean goToHomeInsteadOfOverview = isFling;
if (goToHomeInsteadOfOverview) {
if (velocity > 0) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 1f3b82c..0ee5d04 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -58,6 +58,7 @@
private final SingleAxisSwipeDetector mDetector;
private final RecentsView mRecentsView;
private final int[] mTempCords = new int[2];
+ private final boolean mIsRtl;
private PendingAnimation mPendingAnimation;
private AnimatorPlaybackController mCurrentAnimation;
@@ -75,6 +76,7 @@
public TaskViewTouchController(T activity) {
mActivity = activity;
mRecentsView = activity.getOverviewPanel();
+ mIsRtl = Utilities.isRtl(activity.getResources());
SingleAxisSwipeDetector.Direction dir =
mRecentsView.getPagedOrientationHandler().getOppositeSwipeDirection();
mDetector = new SingleAxisSwipeDetector(activity, this, dir);
@@ -201,8 +203,8 @@
mCurrentAnimationIsGoingUp = goingUp;
BaseDragLayer dl = mActivity.getDragLayer();
final int secondaryLayerDimension = orientationHandler.getSecondaryDimension(dl);
- long maxDuration = (long) (2 * secondaryLayerDimension);
- int verticalFactor = -orientationHandler.getTaskDismissDirectionFactor();
+ long maxDuration = 2 * secondaryLayerDimension;
+ int verticalFactor = orientationHandler.getTaskDragDisplacementFactor(mIsRtl);
int secondaryTaskDimension = orientationHandler.getSecondaryDimension(mTaskBeingDragged);
if (goingUp) {
mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
@@ -236,7 +238,7 @@
public void onDragStart(boolean start, float startDisplacement) {
PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler();
if (mCurrentAnimation == null) {
- reInitAnimationController(orientationHandler.isGoingUp(startDisplacement));
+ reInitAnimationController(orientationHandler.isGoingUp(startDisplacement, mIsRtl));
mDisplacementShift = 0;
} else {
mDisplacementShift = mCurrentAnimation.getProgressFraction() / mProgressMultiplier;
@@ -250,7 +252,7 @@
PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler();
float totalDisplacement = displacement + mDisplacementShift;
boolean isGoingUp = totalDisplacement == 0 ? mCurrentAnimationIsGoingUp :
- orientationHandler.isGoingUp(totalDisplacement);
+ orientationHandler.isGoingUp(totalDisplacement, mIsRtl);
if (isGoingUp != mCurrentAnimationIsGoingUp) {
reInitAnimationController(isGoingUp);
mFlingBlockCheck.blockFling();
@@ -282,7 +284,7 @@
float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress();
if (fling) {
logAction = Touch.FLING;
- boolean goingUp = orientationHandler.isGoingUp(velocity);
+ boolean goingUp = orientationHandler.isGoingUp(velocity, mIsRtl);
goingToEnd = goingUp == mCurrentAnimationIsGoingUp;
} else {
logAction = Touch.SWIPE;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 1eb3bec..f38ff10 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
@@ -32,9 +31,9 @@
import android.view.View;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.TransformParams;
@@ -48,20 +47,21 @@
*
* @param <T> activity that contains the overview
*/
-final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> extends
+final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extends
RemoteAnimationProvider {
private static final long RECENTS_LAUNCH_DURATION = 250;
private static final String TAG = "AppToOverviewAnimationProvider";
- private final BaseActivityInterface<T> mActivityInterface;
+ private final BaseActivityInterface<?, T> mActivityInterface;
// The id of the currently running task that is transitioning to overview.
private final int mTargetTaskId;
private T mActivity;
private RecentsView mRecentsView;
- AppToOverviewAnimationProvider(BaseActivityInterface<T> activityInterface, int targetTaskId) {
+ AppToOverviewAnimationProvider(
+ BaseActivityInterface<?, T> activityInterface, int targetTaskId) {
mActivityInterface = activityInterface;
mTargetTaskId = targetTaskId;
}
@@ -75,14 +75,10 @@
boolean onActivityReady(T activity, Boolean wasVisible) {
activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId);
AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
- BaseActivityInterface.AnimationFactory factory =
- mActivityInterface.prepareRecentsUI(wasVisible,
- false /* animate activity */, (controller) -> {
+ BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
+ wasVisible, (controller) -> {
controller.dispatchOnStart();
- ValueAnimator anim = controller.getAnimationPlayer()
- .setDuration(RECENTS_LAUNCH_DURATION);
- anim.setInterpolator(FAST_OUT_SLOW_IN);
- anim.start();
+ controller.getAnimationPlayer().end();
});
factory.onRemoteAnimationReceived(null);
factory.createActivityInterface(RECENTS_LAUNCH_DURATION);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index ea0d840..bbee67c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -23,44 +25,48 @@
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Matrix;
+import android.graphics.Matrix.ScaleToFit;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
-import android.util.Pair;
import android.view.MotionEvent;
-import android.view.View;
import android.view.animation.Interpolator;
+import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.FloatingIconView;
-import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
-import com.android.quickstep.util.AppWindowAnimationHelper;
-import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
import java.util.ArrayList;
import java.util.function.Consumer;
@@ -69,7 +75,7 @@
* Base class for swipe up handler with some utility methods
*/
@TargetApi(Build.VERSION_CODES.Q)
-public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q extends RecentsView>
+public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
implements RecentsAnimationListener {
private static final String TAG = "BaseSwipeUpHandler";
@@ -90,10 +96,12 @@
protected final Context mContext;
protected final RecentsAnimationDeviceState mDeviceState;
protected final GestureState mGestureState;
- protected final BaseActivityInterface<T> mActivityInterface;
+ protected final BaseActivityInterface<?, T> mActivityInterface;
protected final InputConsumerController mInputConsumer;
- protected AppWindowAnimationHelper mAppWindowAnimationHelper;
+ protected final TaskViewSimulator mTaskViewSimulator;
+ private AnimatorPlaybackController mWindowTransitionController;
+
protected final TransformParams mTransformParams = new TransformParams();
// Shift in the range of [0, 1].
@@ -113,16 +121,14 @@
protected T mActivity;
protected Q mRecentsView;
protected DeviceProfile mDp;
- private final int mPageSpacing;
protected Runnable mGestureEndCallback;
protected MultiStateCallback mStateCallback;
protected boolean mCanceled;
- protected int mLastStartedTaskId = -1;
- private RecentsOrientedState mOrientedState;
+ private boolean mRecentsViewScrollLinked = false;
protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, InputConsumerController inputConsumer) {
@@ -132,8 +138,7 @@
mActivityInterface = gestureState.getActivityInterface();
mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
mInputConsumer = inputConsumer;
- mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
- mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
+ mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
}
/**
@@ -193,10 +198,10 @@
updateFinalShift();
}
});
- mRecentsView.setAppWindowAnimationHelper(mAppWindowAnimationHelper);
runOnRecentsAnimationStart(() ->
mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
mRecentsAnimationTargets));
+ mRecentsViewScrollLinked = true;
}
protected void startNewTask(Consumer<Boolean> resultCallback) {
@@ -210,11 +215,16 @@
if (!mCanceled) {
TaskView nextTask = mRecentsView.getTaskView(taskId);
if (nextTask != null) {
- mLastStartedTaskId = taskId;
+ mGestureState.updateLastStartedTaskId(taskId);
nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
success -> {
resultCallback.accept(success);
- if (!success) {
+ if (success) {
+ if (mRecentsView.indexOfChild(nextTask)
+ == getLastAppearedTaskIndex()) {
+ onRestartLastAppearedTask();
+ }
+ } else {
mActivityInterface.onLaunchTaskFailed();
nextTask.notifyTaskLaunchFailed(TAG);
mRecentsAnimationController.finish(true /* toRecents */, null);
@@ -228,6 +238,19 @@
}
/**
+ * Called when we successfully startNewTask() on the task that was previously running. Normally
+ * we call resumeLastTask() when returning to the previously running task, but this handles a
+ * specific edge case: if we switch from A to B, and back to A before B appears, we need to
+ * start A again to ensure it stays on top.
+ */
+ @CallSuper
+ protected void onRestartLastAppearedTask() {
+ // Finish the controller here, since we won't get onTaskAppeared() for a task that already
+ // appeared.
+ mRecentsAnimationController.finish(false, null);
+ }
+
+ /**
* Runs the given {@param action} if the recents animation has already started, or queues it to
* be run when it is next started.
*/
@@ -247,36 +270,29 @@
return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
}
- protected void updateSource(Rect stackBounds, RemoteAnimationTargetCompat runningTarget) {
- mAppWindowAnimationHelper.updateSource(stackBounds, runningTarget);
- }
-
@Override
public void onRecentsAnimationStart(RecentsAnimationController recentsAnimationController,
RecentsAnimationTargets targets) {
mRecentsAnimationController = recentsAnimationController;
mRecentsAnimationTargets = targets;
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
- final Rect overviewStackBounds;
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
mGestureState.getRunningTaskId());
if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
- overviewStackBounds = mActivityInterface
+ Rect overviewStackBounds = mActivityInterface
.getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
dp = dp.getMultiWindowProfile(mContext, overviewStackBounds);
} else {
// If we are not in multi-window mode, home insets should be same as system insets.
dp = dp.copy(mContext);
- overviewStackBounds = getStackBounds(dp);
}
dp.updateInsets(targets.homeContentInsets);
dp.updateIsSeascape(mContext);
if (runningTaskTarget != null) {
- updateSource(overviewStackBounds, runningTaskTarget);
+ mTaskViewSimulator.setPreview(runningTaskTarget);
}
- mAppWindowAnimationHelper.prepareAnimation(dp);
initTransitionEndpoints(dp);
// Notify when the animation starts
@@ -330,16 +346,12 @@
: mRecentsView.getRunningTaskIndex();
}
- private Rect getStackBounds(DeviceProfile dp) {
- if (mActivity != null) {
- int loc[] = new int[2];
- View rootView = mActivity.getRootView();
- rootView.getLocationOnScreen(loc);
- return new Rect(loc[0], loc[1], loc[0] + rootView.getWidth(),
- loc[1] + rootView.getHeight());
- } else {
- return new Rect(0, 0, dp.widthPx, dp.heightPx);
- }
+ /**
+ * @return Whether we are continuing a gesture that already landed on a new task,
+ * but before that task appeared.
+ */
+ protected boolean hasStartedNewTask() {
+ return mGestureState.getLastStartedTaskId() != -1;
}
protected void initTransitionEndpoints(DeviceProfile dp) {
@@ -347,34 +359,36 @@
mTransitionDragLength = mActivityInterface.getSwipeUpDestinationAndLength(
dp, mContext, TEMP_RECT);
+ mTaskViewSimulator.setDp(dp);
+ mTaskViewSimulator.setLayoutRotation(
+ mDeviceState.getCurrentActiveRotation(),
+ mDeviceState.getDisplayRotation());
- if (!dp.isMultiWindowMode) {
- // When updating the target rect, also update the home bounds since the location on
- // screen of the launcher window may be stale (position is not updated until first
- // traversal after the window is resized). We only do this for non-multiwindow because
- // we otherwise use the minimized home bounds provided by the system.
- mAppWindowAnimationHelper.updateHomeBounds(getStackBounds(dp));
- }
- int displayRotation = 0;
- if (mOrientedState != null && mOrientedState.isMultipleOrientationSupportedByDevice()) {
- // TODO(b/150300347): The first recents animation after launcher is started with the
- // foreground app not in landscape will look funky until that bug is fixed
- displayRotation = mOrientedState.getDisplayRotation();
-
- RectF tempRectF = new RectF(TEMP_RECT);
- mOrientedState.mapRectFromRotation(displayRotation,
- tempRectF, dp.widthPx, dp.heightPx);
- tempRectF.roundOut(TEMP_RECT);
- }
- mAppWindowAnimationHelper.updateTargetRect(TEMP_RECT);
if (mDeviceState.isFullyGesturalNavMode()) {
// We can drag all the way to the top of the screen.
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
- Pair<Float, Float> dragFactorStartAndMaxProgress =
- mActivityInterface.getSwipeUpPullbackStartAndMaxProgress();
- mDragLengthFactorStartPullback = dragFactorStartAndMaxProgress.first;
- mDragLengthFactorMaxPullback = dragFactorStartAndMaxProgress.second;
+
+ float startScale = mTaskViewSimulator.getFullScreenScale();
+ // Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f.
+ mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale);
+ mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale);
+ } else {
+ mDragLengthFactor = 1;
+ mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1;
}
+
+ AnimatorSet anim = new AnimatorSet();
+ anim.setDuration(mTransitionDragLength * 2);
+ anim.setInterpolator(t -> t * mDragLengthFactor);
+ anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.recentsViewScale,
+ AnimatedFloat.VALUE,
+ mTaskViewSimulator.getFullScreenScale(), 1));
+ anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.fullScreenProgress,
+ AnimatedFloat.VALUE,
+ BACKGROUND_APP.getOverviewFullscreenProgress(),
+ OVERVIEW.getOverviewFullscreenProgress()));
+ mWindowTransitionController =
+ AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2);
}
/**
@@ -385,9 +399,6 @@
protected boolean onActivityInit(Boolean alreadyOnHome) {
T createdActivity = mActivityInterface.getCreatedActivity();
if (createdActivity != null) {
- mOrientedState = ((RecentsView) createdActivity.getOverviewPanel())
- .getPagedViewOrientedState();
- mAppWindowAnimationHelper = new AppWindowAnimationHelper(mOrientedState, mContext);
initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
.getDeviceProfile(mContext));
}
@@ -436,34 +447,23 @@
}
/**
- * Applies the transform on the recents animation without any additional null checks
+ * Applies the transform on the recents animation
*/
- protected void applyTransformUnchecked() {
- float shift = mCurrentShift.value;
- float offset = mRecentsView == null ? 0 : mRecentsView.getScrollOffsetScaled();
- float taskSize = getOrientationHandler()
- .getPrimarySize(mAppWindowAnimationHelper.getTargetRect());
- float offsetScale = getTaskCurveScaleForOffset(offset, taskSize);
- mTransformParams
- .setProgress(shift)
- .setOffset(offset)
- .setOffsetScale(offsetScale)
- .setTargetSet(mRecentsAnimationTargets);
- mAppWindowAnimationHelper.applyTransform(mTransformParams);
- }
+ protected void applyWindowTransform() {
+ if (mWindowTransitionController != null) {
+ float progress = mCurrentShift.value / mDragLengthFactor;
+ mWindowTransitionController.setPlayFraction(progress);
+ mTransformParams.setTargetSet(mRecentsAnimationTargets);
- private float getTaskCurveScaleForOffset(float offset, float taskSize) {
- int dpPixel = getOrientationHandler().getShortEdgeLength(mDp);
- float distanceToReachEdge = dpPixel / 2 + taskSize / 2 + mPageSpacing;
- float interpolation = Math.min(1, offset / distanceToReachEdge);
- return TaskView.getCurveScaleForInterpolation(interpolation);
+ if (mRecentsViewScrollLinked) {
+ mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
+ }
+ mTaskViewSimulator.apply(mTransformParams);
+ }
}
protected PagedOrientationHandler getOrientationHandler() {
- if (mOrientedState == null) {
- return PagedOrientationHandler.PORTRAIT;
- }
- return mOrientedState.getOrientationHandler();
+ return mTaskViewSimulator.getOrientationState().getOrientationHandler();
}
/**
@@ -474,102 +474,40 @@
protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
- final View floatingView = homeAnimationFactory.getFloatingView();
- final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
- final RectF startRect = new RectF(mAppWindowAnimationHelper.applyTransform(
- mTransformParams.setProgress(startProgress)
- .setTargetSet(mRecentsAnimationTargets)));
- if (isFloatingIconView) {
- mOrientedState.mapInverseRectFromNormalOrientation(
- startRect, mDp.widthPx, mDp.heightPx);
- }
+ final FloatingIconView fiv = homeAnimationFactory.mIconView;
+ final boolean isFloatingIconView = fiv != null;
+
+ mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor);
+ mTaskViewSimulator.apply(mTransformParams
+ .setProgress(startProgress)
+ .setTargetSet(mRecentsAnimationTargets));
+ RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
+
+ // Matrix to map a rect in Launcher space to window space
+ Matrix homeToWindowPositionMap = new Matrix();
+ mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap);
+
+ final RectF startRect = new RectF(cropRectF);
+ mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
+ // Move the startRect to Launcher space as floatingIconView runs in Launcher
+ Matrix windowToHomePositionMap = new Matrix();
+ homeToWindowPositionMap.invert(windowToHomePositionMap);
+ windowToHomePositionMap.mapRect(startRect);
+
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
if (isFloatingIconView) {
- FloatingIconView fiv = (FloatingIconView) floatingView;
anim.addAnimatorListener(fiv);
fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
fiv.setFastFinishRunnable(anim::end);
}
- AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome();
-
- // End on a "round-enough" radius so that the shape reveal doesn't have to do too much
- // rounding at the end of the animation.
- float startRadius = mAppWindowAnimationHelper.getCurrentCornerRadius();
- float endRadius = startRect.width() / 6f;
-
- float startTransformProgress = mTransformParams.getProgress();
- float endTransformProgress = 1;
-
- // We want the window alpha to be 0 once this threshold is met, so that the
- // FolderIconView can be seen morphing into the icon shape.
- final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
- final RectF rotatedRect = new RectF();
- anim.addOnUpdateListener(new RectFSpringAnim.OnUpdateListener() {
-
- @Override
- public void onUpdate(RectF currentRect, float progress) {
- homeAnim.setPlayFraction(progress);
-
- rotatedRect.set(currentRect);
- if (isFloatingIconView) {
- mOrientedState.mapRectFromNormalOrientation(
- rotatedRect, mDp.widthPx, mDp.heightPx);
- mTransformParams.setCornerRadius(endRadius * progress + startRadius
- * (1f - progress));
- }
- mTransformParams.setProgress(
- Utilities.mapRange(progress, startTransformProgress, endTransformProgress))
- .setCurrentRect(rotatedRect)
- .setTargetAlpha(getWindowAlpha(progress));
- mAppWindowAnimationHelper.applyTransform(mTransformParams);
-
- if (isFloatingIconView) {
- ((FloatingIconView) floatingView).update(currentRect, 1f, progress,
- windowAlphaThreshold, mAppWindowAnimationHelper.getCurrentCornerRadius(),
- false);
- }
- }
-
- @Override
- public void onCancel() {
- if (isFloatingIconView) {
- ((FloatingIconView) floatingView).fastFinish();
- }
- }
- });
- anim.addAnimatorListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- homeAnim.dispatchOnStart();
- }
-
- @Override
- public void onAnimationSuccess(Animator animator) {
- homeAnim.getAnimationPlayer().end();
- }
- });
+ SpringAnimationRunner runner = new SpringAnimationRunner(
+ homeAnimationFactory, cropRectF, homeToWindowPositionMap);
+ anim.addOnUpdateListener(runner);
+ anim.addAnimatorListener(runner);
return anim;
}
- /**
- * @param progress The progress of the animation to the home screen.
- * @return The current alpha to set on the animating app window.
- */
- protected float getWindowAlpha(float progress) {
- // Alpha interpolates between [1, 0] between progress values [start, end]
- final float start = 0f;
- final float end = 0.85f;
-
- if (progress <= start) {
- return 1f;
- }
- if (progress >= end) {
- return 0f;
- }
- return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
- }
-
public interface Factory {
BaseSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs,
@@ -609,4 +547,135 @@
};
}
}
+
+ /**
+ * @param progress The progress of the animation to the home screen.
+ * @return The current alpha to set on the animating app window.
+ */
+ protected float getWindowAlpha(float progress) {
+ // Alpha interpolates between [1, 0] between progress values [start, end]
+ final float start = 0f;
+ final float end = 0.85f;
+
+ if (progress <= start) {
+ return 1f;
+ }
+ if (progress >= end) {
+ return 0f;
+ }
+ return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
+ }
+
+ protected abstract class HomeAnimationFactory {
+
+ private FloatingIconView mIconView;
+
+ public HomeAnimationFactory(@Nullable FloatingIconView iconView) {
+ mIconView = iconView;
+ }
+
+ public @NonNull RectF getWindowTargetRect() {
+ PagedOrientationHandler orientationHandler = getOrientationHandler();
+ DeviceProfile dp = mDp;
+ final int halfIconSize = dp.iconSizePx / 2;
+ float primaryDimension = orientationHandler
+ .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
+ float secondaryDimension = orientationHandler
+ .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
+ final float targetX = primaryDimension / 2f;
+ final float targetY = secondaryDimension - dp.hotseatBarSizePx;
+ // Fallback to animate to center of screen.
+ return new RectF(targetX - halfIconSize, targetY - halfIconSize,
+ targetX + halfIconSize, targetY + halfIconSize);
+ }
+
+ public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome();
+
+ public void playAtomicAnimation(float velocity) {
+ // No-op
+ }
+ }
+
+ private class SpringAnimationRunner extends AnimationSuccessListener
+ implements RectFSpringAnim.OnUpdateListener, BuilderProxy {
+
+ final Rect mCropRect = new Rect();
+ final Matrix mMatrix = new Matrix();
+
+ final RectF mWindowCurrentRect = new RectF();
+ final Matrix mHomeToWindowPositionMap;
+
+ final FloatingIconView mFIV;
+ final AnimatorPlaybackController mHomeAnim;
+ final RectF mCropRectF;
+
+ final float mStartRadius;
+ final float mEndRadius;
+ final float mWindowAlphaThreshold;
+
+ SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF,
+ Matrix homeToWindowPositionMap) {
+ mHomeAnim = factory.createActivityAnimationToHome();
+ mCropRectF = cropRectF;
+ mHomeToWindowPositionMap = homeToWindowPositionMap;
+
+ cropRectF.roundOut(mCropRect);
+ mFIV = factory.mIconView;
+
+ // End on a "round-enough" radius so that the shape reveal doesn't have to do too much
+ // rounding at the end of the animation.
+ mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
+ mEndRadius = cropRectF.width() / 2f;
+
+ // We want the window alpha to be 0 once this threshold is met, so that the
+ // FolderIconView can be seen morphing into the icon shape.
+ mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f;
+ }
+
+ @Override
+ public void onUpdate(RectF currentRect, float progress) {
+ mHomeAnim.setPlayFraction(progress);
+ mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect);
+
+ mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
+ float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
+ mTransformParams
+ .setTargetAlpha(getWindowAlpha(progress))
+ .setCornerRadius(cornerRadius);
+
+ mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
+ if (mFIV != null) {
+ mFIV.update(currentRect, 1f, progress,
+ mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false);
+ }
+ }
+
+ @Override
+ public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app, int targetMode,
+ TransformParams params) {
+ if (app.mode == targetMode
+ && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+ builder.withMatrix(mMatrix)
+ .withWindowCrop(mCropRect)
+ .withCornerRadius(params.getCornerRadius());
+ }
+ }
+
+ @Override
+ public void onCancel() {
+ if (mFIV != null) {
+ mFIV.fastFinish();
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mHomeAnim.dispatchOnStart();
+ }
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ mHomeAnim.getAnimationPlayer().end();
+ }
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index 88dbbe1..4b3af31 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -15,27 +15,27 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.fallback.FallbackRecentsView.ZOOM_PROGRESS;
-import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
+import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP;
+import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
+import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.RectF;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -48,19 +48,18 @@
* currently running one and apps should interact with the {@link RecentsActivity} as opposed
* to the in-launcher one.
*/
-public final class FallbackActivityInterface implements
- BaseActivityInterface<RecentsActivity> {
+public final class FallbackActivityInterface extends
+ BaseActivityInterface<RecentsState, RecentsActivity> {
- public FallbackActivityInterface() { }
+ public static final FallbackActivityInterface INSTANCE = new FallbackActivityInterface();
- @Override
- public void onTransitionCancelled(boolean activityVisible) {
- // TODO:
+ private FallbackActivityInterface() {
+ super(false);
}
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
- FALLBACK_RECENTS_SIZE_STRATEGY.calculateTaskSize(context, dp, outRect);
+ calculateTaskSize(context, dp, outRect);
if (dp.isVerticalBarLayout()
&& SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
Rect targetInsets = dp.getInsets();
@@ -72,71 +71,23 @@
}
@Override
- public void onSwipeUpToRecentsComplete() {
- RecentsActivity activity = getCreatedActivity();
- if (activity == null) {
- return;
- }
- RecentsView recentsView = activity.getOverviewPanel();
- recentsView.getClearAllButton().setVisibilityAlpha(1);
- recentsView.setDisallowScrollToClearAll(false);
- }
-
- @Override
public void onAssistantVisibilityChanged(float visibility) {
// This class becomes active when the screen is locked.
// Rather than having it handle assistant visibility changes, the assistant visibility is
// set to zero prior to this class becoming active.
}
- @NonNull
@Override
- public HomeAnimationFactory prepareHomeUI() {
+ public AnimationFactory prepareRecentsUI(
+ boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
RecentsActivity activity = getCreatedActivity();
- RecentsView recentsView = activity.getOverviewPanel();
-
- return new HomeAnimationFactory() {
- @NonNull
- @Override
- public RectF getWindowTargetRect() {
- float centerX = recentsView.getPivotX();
- float centerY = recentsView.getPivotY();
- return new RectF(centerX, centerY, centerX, centerY);
- }
-
- @NonNull
- @Override
- public AnimatorPlaybackController createActivityAnimationToHome() {
- Animator anim = ObjectAnimator.ofFloat(recentsView, CONTENT_ALPHA, 0);
- anim.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- recentsView.startHome();
- }
- });
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.play(anim);
- long accuracy = 2 * Math.max(recentsView.getWidth(), recentsView.getHeight());
- return AnimatorPlaybackController.wrap(animatorSet, accuracy);
- }
- };
- }
-
- @Override
- public AnimationFactory prepareRecentsUI(boolean activityVisible,
- boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
- RecentsActivity activity = getCreatedActivity();
- if (activityVisible) {
+ if (activity == null) {
return (transitionLength) -> { };
}
+ activity.getStateManager().goToState(BACKGROUND_APP);
FallbackRecentsView rv = activity.getOverviewPanel();
rv.setContentAlpha(0);
- rv.getClearAllButton().setVisibilityAlpha(0);
- rv.setDisallowScrollToClearAll(true);
-
- boolean fromState = !animateActivity;
- rv.setInOverviewState(fromState);
return new AnimationFactory() {
@@ -154,27 +105,19 @@
@Override
public void createActivityInterface(long transitionLength) {
- AnimatorSet animatorSet = new AnimatorSet();
+ PendingAnimation pa = new PendingAnimation(transitionLength * 2);
+
if (isAnimatingToRecents) {
- ObjectAnimator anim = ObjectAnimator.ofFloat(rv, CONTENT_ALPHA, 0, 1);
- anim.setDuration(transitionLength).setInterpolator(LINEAR);
- animatorSet.play(anim);
+ pa.addFloat(rv, CONTENT_ALPHA, 0, 1, LINEAR);
}
- ObjectAnimator anim = ObjectAnimator.ofFloat(rv, ZOOM_PROGRESS, 1, 0);
- anim.setDuration(transitionLength).setInterpolator(LINEAR);
- animatorSet.play(anim);
-
- AnimatorPlaybackController controller =
- AnimatorPlaybackController.wrap(animatorSet, transitionLength);
+ pa.addFloat(rv, SCALE_PROPERTY, rv.getMaxScaleForFullScreen(), 1, LINEAR);
+ pa.addFloat(rv, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
+ AnimatorPlaybackController controller = pa.createPlaybackController();
// Since we are changing the start position of the UI, reapply the state, at the end
- controller.setEndAction(() -> {
- boolean endState = true;
- rv.setInOverviewState(controller.getInterpolatedProgress() > 0.5 ?
- endState : fromState);
- });
-
+ controller.setEndAction(() -> activity.getStateManager().goToState(
+ controller.getInterpolatedProgress() > 0.5 ? DEFAULT : BACKGROUND_APP));
callback.accept(controller);
}
};
@@ -190,7 +133,7 @@
@Nullable
@Override
public RecentsActivity getCreatedActivity() {
- return BaseRecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
+ return RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
@@ -245,11 +188,14 @@
}
@Override
- public void onLaunchTaskSuccess() {
- RecentsActivity activity = getCreatedActivity();
- if (activity == null) {
- return;
- }
- activity.onTaskLaunched();
+ public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
+ out.set(dp.widthPx, dp.heightPx);
+ }
+
+ @Override
+ protected float getExtraSpace(Context context, DeviceProfile dp) {
+ return showOverviewActions(context)
+ ? context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height)
+ : 0;
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index d24b16a..db41bd6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -32,17 +32,16 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.PointF;
-import android.graphics.RectF;
import android.os.Bundle;
import android.util.ArrayMap;
import android.view.MotionEvent;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.ObjectWrapper;
-import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
+import com.android.quickstep.BaseActivityInterface.AnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.util.RectFSpringAnim;
@@ -105,6 +104,12 @@
private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
private RunningWindowAnim mFinishAnimation;
+ // Used to control Recents components throughout the swipe gesture.
+ private AnimatorPlaybackController mLauncherTransitionController;
+ private boolean mHasLauncherTransitionControllerStarted;
+
+ private AnimationFactory mAnimationFactory = (t) -> { };
+
public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, InputConsumerController inputConsumer,
boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
@@ -156,7 +161,7 @@
private void onLauncherAlphaChanged() {
if (mRecentsAnimationTargets != null && mGestureState.getEndTarget() == null) {
- applyTransformUnchecked();
+ applyWindowTransform();
}
}
@@ -167,10 +172,6 @@
mRecentsView = mActivity.getOverviewPanel();
mRecentsView.setOnPageTransitionEndCallback(null);
linkRecentsViewScroll();
- mRecentsView.setDisallowScrollToClearAll(true);
- mRecentsView.getClearAllButton().setVisibilityAlpha(0);
- mRecentsView.setZoomProgress(1);
-
if (!mContinuingLastGesture) {
if (mRunningOverHome) {
mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
@@ -180,10 +181,49 @@
}
mStateCallback.setStateOnUiThread(STATE_RECENTS_PRESENT);
mDeviceState.enableMultipleRegions(false);
+
+ mAnimationFactory = mActivityInterface.prepareRecentsUI(alreadyOnHome,
+ this::onAnimatorPlaybackControllerCreated);
+ mAnimationFactory.createActivityInterface(mTransitionDragLength);
return true;
}
@Override
+ protected void initTransitionEndpoints(DeviceProfile dp) {
+ super.initTransitionEndpoints(dp);
+ if (canCreateNewOrUpdateExistingLauncherTransitionController()) {
+ mAnimationFactory.createActivityInterface(mTransitionDragLength);
+ }
+ }
+
+ private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) {
+ mLauncherTransitionController = anim;
+ mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor);
+ mLauncherTransitionController.dispatchOnStart();
+ updateLauncherTransitionProgress();
+ }
+
+ private void updateLauncherTransitionProgress() {
+ if (mLauncherTransitionController == null
+ || !canCreateNewOrUpdateExistingLauncherTransitionController()) {
+ return;
+ }
+ // Normalize the progress to 0 to 1, as the animation controller will clamp it to that
+ // anyway. The controller mimics the drag length factor by applying it to its interpolators.
+ float progress = mCurrentShift.value / mDragLengthFactor;
+ mLauncherTransitionController.setPlayFraction(progress);
+ }
+
+ /**
+ * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
+ * (it has its own animation) or if we're already animating the current controller.
+ * @return Whether we can create the launcher controller or update its progress.
+ */
+ private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
+ return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted;
+ }
+
+ @Override
protected boolean moveWindowWithRecentsScroll() {
return mInQuickSwitchMode;
}
@@ -261,9 +301,8 @@
updateOverviewThresholdPassed(mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW);
}
- if (mRecentsAnimationTargets != null) {
- applyTransformUnchecked();
- }
+ applyWindowTransform();
+ updateLauncherTransitionProgress();
}
@Override
@@ -468,11 +507,7 @@
RecentsAnimationTargets targets) {
super.onRecentsAnimationStart(controller, targets);
mRecentsAnimationController.enableInputConsumer();
-
- if (mRunningOverHome) {
- mAppWindowAnimationHelper.prepareAnimation(mDp);
- }
- applyTransformUnchecked();
+ applyWindowTransform();
mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
}
@@ -495,18 +530,7 @@
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
*/
private RectFSpringAnim createWindowAnimationToHome(float startProgress, long duration) {
- HomeAnimationFactory factory = new HomeAnimationFactory() {
- @Override
- public RectF getWindowTargetRect() {
- PagedOrientationHandler orientationHandler = mRecentsView != null
- ? mRecentsView.getPagedOrientationHandler()
- : (mDp.isLandscape
- ? PagedOrientationHandler.LANDSCAPE
- : PagedOrientationHandler.PORTRAIT);
- return HomeAnimationFactory
- .getDefaultWindowTargetRect(orientationHandler, mDp);
- }
-
+ HomeAnimationFactory factory = new HomeAnimationFactory(null) {
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
AnimatorSet anim = new AnimatorSet();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index d4175e2..4ebfbd6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -17,32 +17,30 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_SHELF_ANIM;
import static com.android.quickstep.LauncherSwipeHandler.RECENTS_ATTACH_DURATION;
-import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
+import static com.android.quickstep.SysUINavigationMode.getMode;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
+import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.RectF;
-import android.os.UserHandle;
import android.util.Log;
-import android.util.Pair;
import android.view.MotionEvent;
-import android.view.View;
import android.view.animation.Interpolator;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -51,25 +49,23 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
-import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
-import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
@@ -78,14 +74,18 @@
/**
* {@link BaseActivityInterface} for the in-launcher recents.
*/
-public final class LauncherActivityInterface implements BaseActivityInterface<Launcher> {
+public final class LauncherActivityInterface extends
+ BaseActivityInterface<LauncherState, Launcher> {
- private Pair<Float, Float> mSwipeUpPullbackStartAndMaxProgress =
- BaseActivityInterface.super.getSwipeUpPullbackStartAndMaxProgress();
+ public static final LauncherActivityInterface INSTANCE = new LauncherActivityInterface();
+
+ private LauncherActivityInterface() {
+ super(true);
+ }
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
- LAUNCHER_ACTIVITY_SIZE_STRATEGY.calculateTaskSize(context, dp, outRect);
+ calculateTaskSize(context, dp, outRect);
if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
Rect targetInsets = dp.getInsets();
int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
@@ -96,29 +96,12 @@
}
@Override
- public Pair<Float, Float> getSwipeUpPullbackStartAndMaxProgress() {
- return mSwipeUpPullbackStartAndMaxProgress;
- }
-
- @Override
- public void onTransitionCancelled(boolean activityVisible) {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- LauncherState startState = launcher.getStateManager().getRestState();
- launcher.getStateManager().goToState(startState, activityVisible);
- }
-
- @Override
public void onSwipeUpToRecentsComplete() {
- // Re apply state in case we did something funky during the transition.
+ super.onSwipeUpToRecentsComplete();
Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
+ if (launcher != null) {
+ DiscoveryBounce.showForOverviewIfNeeded(launcher);
}
- launcher.getStateManager().reapplyState();
- DiscoveryBounce.showForOverviewIfNeeded(launcher);
}
@Override
@@ -130,15 +113,7 @@
// Ensure recents is at the correct position for NORMAL state. For example, when we detach
// recents, we assume the first task is invisible, making translation off by one task.
launcher.getStateManager().reapplyState();
- setLauncherHideBackArrow(false);
- }
-
- private void setLauncherHideBackArrow(boolean hideBackArrow) {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- launcher.getRootView().setForceHideBackArrow(hideBackArrow);
+ launcher.getRootView().setForceHideBackArrow(false);
}
@Override
@@ -150,66 +125,9 @@
launcher.onAssistantVisibilityChanged(visibility);
}
- @NonNull
@Override
- public HomeAnimationFactory prepareHomeUI() {
- Launcher launcher = getCreatedActivity();
- final DeviceProfile dp = launcher.getDeviceProfile();
- final RecentsView recentsView = launcher.getOverviewPanel();
- final TaskView runningTaskView = recentsView.getRunningTaskView();
- final View workspaceView;
- if (runningTaskView != null && runningTaskView.getTask().key.getComponent() != null) {
- workspaceView = launcher.getWorkspace().getFirstMatchForAppClose(
- runningTaskView.getTask().key.getComponent().getPackageName(),
- UserHandle.of(runningTaskView.getTask().key.userId));
- } else {
- workspaceView = null;
- }
- final RectF iconLocation = new RectF();
- boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
- FloatingIconView floatingIconView = canUseWorkspaceView
- ? FloatingIconView.getFloatingIconView(launcher, workspaceView,
- true /* hideOriginal */, iconLocation, false /* isOpening */)
- : null;
- setLauncherHideBackArrow(true);
- return new HomeAnimationFactory() {
- @Nullable
- @Override
- public View getFloatingView() {
- return floatingIconView;
- }
-
- @NonNull
- @Override
- public RectF getWindowTargetRect() {
- if (canUseWorkspaceView) {
- return iconLocation;
- } else {
- return HomeAnimationFactory
- .getDefaultWindowTargetRect(recentsView.getPagedOrientationHandler(), dp);
- }
- }
-
- @NonNull
- @Override
- public AnimatorPlaybackController createActivityAnimationToHome() {
- // Return an empty APC here since we have an non-user controlled animation to home.
- long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
- return launcher.getStateManager().createAnimationToNewWorkspace(NORMAL, accuracy,
- 0 /* animComponents */);
- }
-
- @Override
- public void playAtomicAnimation(float velocity) {
- new StaggeredWorkspaceAnim(launcher, velocity, true /* animateOverviewScrim */)
- .start();
- }
- };
- }
-
- @Override
- public AnimationFactory prepareRecentsUI(boolean activityVisible,
- boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
+ public AnimationFactory prepareRecentsUI(
+ boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
BaseQuickstepLauncher launcher = getCreatedActivity();
final LauncherState startState = launcher.getStateManager().getState();
@@ -219,8 +137,7 @@
}
launcher.getStateManager().setRestState(resetState);
- final LauncherState fromState = animateActivity ? BACKGROUND_APP : OVERVIEW;
- launcher.getStateManager().goToState(fromState, false);
+ launcher.getStateManager().goToState(BACKGROUND_APP, false);
// Since all apps is not visible, we can safely reset the scroll position.
// This ensures then the next swipe up to all-apps starts from scroll 0.
launcher.getAppsView().reset(false /* animate */);
@@ -231,7 +148,7 @@
@Override
public void createActivityInterface(long transitionLength) {
- createActivityInterfaceInternal(launcher, fromState, transitionLength, callback);
+ callback.accept(createBackgroundToOverviewAnim(launcher, transitionLength));
// Creating the activity controller animation sometimes reapplies the launcher state
// (because we set the animation as the current state animation), so we reapply the
// attached state here as well to ensure recents is shown/hidden appropriately.
@@ -285,74 +202,45 @@
};
}
- private void createActivityInterfaceInternal(Launcher activity, LauncherState fromState,
- long transitionLength, Consumer<AnimatorPlaybackController> callback) {
- LauncherState endState = OVERVIEW;
- if (fromState == endState) {
- return;
- }
+ private AnimatorPlaybackController createBackgroundToOverviewAnim(
+ Launcher activity, long transitionLength) {
- AnimatorSet anim = new AnimatorSet();
+ PendingAnimation pa = new PendingAnimation(transitionLength * 2);
+
if (!activity.getDeviceProfile().isVerticalBarLayout()
&& SysUINavigationMode.getMode(activity) != Mode.NO_BUTTON) {
// Don't animate the shelf when the mode is NO_BUTTON, because we update it atomically.
- anim.play(activity.getStateManager().createStateElementAnimation(
+ pa.add(activity.getStateManager().createStateElementAnimation(
INDEX_SHELF_ANIM,
- fromState.getVerticalProgress(activity),
- endState.getVerticalProgress(activity)));
+ BACKGROUND_APP.getVerticalProgress(activity),
+ OVERVIEW.getVerticalProgress(activity)));
}
// Animate the blur and wallpaper zoom
- DepthController depthController = getDepthController();
- float fromDepthRatio = fromState.getDepth(activity);
- float toDepthRatio = endState.getDepth(activity);
- Animator depthAnimator = ObjectAnimator.ofFloat(depthController,
- new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
- fromDepthRatio, toDepthRatio);
- anim.play(depthAnimator);
+ float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
+ float toDepthRatio = OVERVIEW.getDepth(activity);
+ pa.addFloat(getDepthController(), new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
+ fromDepthRatio, toDepthRatio, LINEAR);
- playScaleDownAnim(anim, activity, fromState, endState);
- anim.setDuration(transitionLength * 2);
- anim.setInterpolator(LINEAR);
- AnimatorPlaybackController controller =
- AnimatorPlaybackController.wrap(anim, transitionLength * 2);
+ // Scale down recents from being full screen to being in overview.
+ RecentsView recentsView = activity.getOverviewPanel();
+ pa.addFloat(recentsView, SCALE_PROPERTY,
+ BACKGROUND_APP.getOverviewScaleAndOffset(activity)[0],
+ OVERVIEW.getOverviewScaleAndOffset(activity)[0],
+ LINEAR);
+ pa.addFloat(recentsView, FULLSCREEN_PROGRESS,
+ BACKGROUND_APP.getOverviewFullscreenProgress(),
+ OVERVIEW.getOverviewFullscreenProgress(),
+ LINEAR);
+
+ AnimatorPlaybackController controller = pa.createPlaybackController();
activity.getStateManager().setCurrentUserControlledAnimation(controller);
// Since we are changing the start position of the UI, reapply the state, at the end
- controller.setEndAction(() -> {
- activity.getStateManager().goToState(
- controller.getInterpolatedProgress() > 0.5 ? endState : fromState, false);
- });
- callback.accept(controller);
- }
-
- /**
- * Scale down recents from the center task being full screen to being in overview.
- */
- private void playScaleDownAnim(AnimatorSet anim, Launcher launcher, LauncherState fromState,
- LauncherState endState) {
- RecentsView recentsView = launcher.getOverviewPanel();
- if (recentsView.getCurrentPageTaskView() == null) {
- return;
- }
-
- float fromFullscreenProgress = fromState.getOverviewFullscreenProgress();
- float endFullscreenProgress = endState.getOverviewFullscreenProgress();
-
- float fromScale = fromState.getOverviewScaleAndOffset(launcher)[0];
- float endScale = endState.getOverviewScaleAndOffset(launcher)[0];
-
- Animator scale = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, fromScale, endScale);
- Animator applyFullscreenProgress = ObjectAnimator.ofFloat(recentsView,
- RecentsView.FULLSCREEN_PROGRESS, fromFullscreenProgress, endFullscreenProgress);
- anim.playTogether(scale, applyFullscreenProgress);
-
- // Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f.
- float pullbackStartProgress = (0.75f - fromScale) / (endScale - fromScale);
- float pullbackMaxProgress = (0.5f - fromScale) / (endScale - fromScale);
- mSwipeUpPullbackStartAndMaxProgress = new Pair<>(
- pullbackStartProgress, pullbackMaxProgress);
+ controller.setEndAction(() -> activity.getStateManager().goToState(
+ controller.getInterpolatedProgress() > 0.5 ? OVERVIEW : BACKGROUND_APP, false));
+ return controller;
}
@Override
@@ -403,6 +291,11 @@
}
@Override
+ public void setHintUserWillBeActive() {
+ getCreatedActivity().setHintUserWillBeActive();
+ }
+
+ @Override
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
return deviceState.isInDeferredGestureRegion(ev);
}
@@ -441,15 +334,6 @@
}
@Override
- public void onLaunchTaskSuccess() {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- launcher.getStateManager().moveToRestState();
- }
-
- @Override
public void closeOverlay() {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
@@ -464,23 +348,6 @@
}
@Override
- public void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
- Runnable onFinishRunnable) {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- RecentsView recentsView = launcher.getOverviewPanel();
- if (recentsView == null) {
- if (onFinishRunnable != null) {
- onFinishRunnable.run();
- }
- return;
- }
- recentsView.switchToScreenshot(thumbnailData, onFinishRunnable);
- }
-
- @Override
public void setOnDeferredActivityLaunchCallback(Runnable r) {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
@@ -508,4 +375,50 @@
}
return launcher.getDepthController();
}
+
+ @Override
+ public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
+ DeviceProfile fullDp = dp.getFullScreenProfile();
+ // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
+ // account for system insets
+ out.set(fullDp.availableWidthPx, fullDp.availableHeightPx);
+ float halfDividerSize = context.getResources()
+ .getDimension(R.dimen.multi_window_task_divider_size) / 2;
+
+ if (fullDp.isLandscape) {
+ out.x = out.x / 2 - halfDividerSize;
+ } else {
+ out.y = out.y / 2 - halfDividerSize;
+ }
+ }
+
+ @Override
+ protected float getExtraSpace(Context context, DeviceProfile dp) {
+ if (dp.isVerticalBarLayout()) {
+ return 0;
+ } else {
+ Resources res = context.getResources();
+ if (showOverviewActions(context)) {
+ //TODO: this needs to account for the swipe gesture height and accessibility
+ // UI when shown.
+ float actionsBottomMargin = 0;
+ if (getMode(context) == Mode.THREE_BUTTONS) {
+ actionsBottomMargin = res.getDimensionPixelSize(
+ R.dimen.overview_actions_bottom_margin_three_button);
+ } else {
+ actionsBottomMargin = res.getDimensionPixelSize(
+ R.dimen.overview_actions_bottom_margin_gesture);
+ }
+ float actionsHeight = actionsBottomMargin
+ + res.getDimensionPixelSize(R.dimen.overview_actions_height);
+ return actionsHeight;
+ } else {
+ return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
+ + res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_extra_vertical_size)
+ + res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_bottom_padding);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index e0393c7..82a3e79 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -17,8 +17,7 @@
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
@@ -36,22 +35,20 @@
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
-import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
import android.view.ViewTreeObserver.OnDrawListener;
@@ -62,27 +59,28 @@
import androidx.annotation.UiThread;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.BaseActivityInterface.AnimationFactory;
-import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
-import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.util.TransformParams.TargetAlphaProvider;
import com.android.quickstep.views.LiveTileOverlay;
import com.android.quickstep.views.RecentsView;
@@ -96,8 +94,8 @@
* Handles the navigation gestures when Launcher is the default home activity.
*/
@TargetApi(Build.VERSION_CODES.O)
-public class LauncherSwipeHandler<T extends BaseDraggingActivity>
- extends BaseSwipeUpHandler<T, RecentsView> implements OnApplyWindowInsetsListener {
+public class LauncherSwipeHandler extends BaseSwipeUpHandler<Launcher, RecentsView>
+ implements OnApplyWindowInsetsListener {
private static final String TAG = LauncherSwipeHandler.class.getSimpleName();
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
@@ -151,7 +149,6 @@
STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
public static final long MAX_SWIPE_DURATION = 350;
- public static final long MIN_SWIPE_DURATION = 80;
public static final long MIN_OVERSHOOT_DURATION = 120;
public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f;
@@ -180,9 +177,6 @@
private AnimatorPlaybackController mLauncherTransitionController;
private boolean mHasLauncherTransitionControllerStarted;
- private final TaskViewSimulator mTaskViewSimulator;
- private AnimatorPlaybackController mWindowTransitionController;
-
private AnimationFactory mAnimationFactory = (t) -> { };
private boolean mWasLauncherAlreadyVisible;
@@ -207,7 +201,6 @@
mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
- mTaskViewSimulator = new TaskViewSimulator(context, LAUNCHER_ACTIVITY_SIZE_STRATEGY);
initAfterSubclassConstructor();
initStateCallbacks();
@@ -279,7 +272,7 @@
@Override
protected boolean onActivityInit(Boolean alreadyOnHome) {
super.onActivityInit(alreadyOnHome);
- final T activity = mActivityInterface.getCreatedActivity();
+ final Launcher activity = mActivityInterface.getCreatedActivity();
if (mActivity == activity) {
return true;
}
@@ -301,7 +294,6 @@
mRecentsView = activity.getOverviewPanel();
mRecentsView.setOnPageTransitionEndCallback(null);
- linkRecentsViewScroll();
addLiveTileOverlay();
mStateCallback.setState(STATE_LAUNCHER_PRESENT);
@@ -318,6 +310,8 @@
// so we need to kick off switching to the overview predictions as soon as possible
mActivityInterface.updateOverviewPredictionState();
}
+ linkRecentsViewScroll();
+
return true;
}
@@ -327,7 +321,7 @@
}
private void onLauncherStart() {
- final T activity = mActivityInterface.getCreatedActivity();
+ final Launcher activity = mActivityInterface.getCreatedActivity();
if (mActivity != activity) {
return;
}
@@ -340,8 +334,7 @@
if (mGestureState.getEndTarget() != HOME) {
Runnable initAnimFactory = () -> {
mAnimationFactory = mActivityInterface.prepareRecentsUI(
- mWasLauncherAlreadyVisible, true,
- this::onAnimatorPlaybackControllerCreated);
+ mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated);
maybeUpdateRecentsAttachedState(false /* animate */);
};
if (mWasLauncherAlreadyVisible) {
@@ -518,34 +511,6 @@
mAnimationFactory.createActivityInterface(mTransitionDragLength);
}
- @Override
- protected void updateSource(Rect stackBounds, RemoteAnimationTargetCompat runningTarget) {
- super.updateSource(stackBounds, runningTarget);
- mTaskViewSimulator.setPreview(runningTarget, mRecentsAnimationTargets);
- }
-
- @Override
- protected void initTransitionEndpoints(DeviceProfile dp) {
- super.initTransitionEndpoints(dp);
- mTaskViewSimulator.setDp(dp);
- mTaskViewSimulator.setLayoutRotation(
- mDeviceState.getCurrentActiveRotation(),
- mDeviceState.getDisplayRotation());
-
- AnimatorSet anim = new AnimatorSet();
- anim.setDuration(mTransitionDragLength * 2);
- anim.setInterpolator(t -> t * mDragLengthFactor);
- anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.recentsViewScale,
- AnimatedFloat.VALUE,
- mTaskViewSimulator.getFullScreenScale(), 1));
- anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.fullScreenProgress,
- AnimatedFloat.VALUE,
- BACKGROUND_APP.getOverviewFullscreenProgress(),
- OVERVIEW.getOverviewFullscreenProgress()));
- mWindowTransitionController =
- AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2);
- }
-
/**
* We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
* (it has its own animation) or if we're already animating the current controller.
@@ -576,18 +541,11 @@
@Override
public void updateFinalShift() {
- if (mRecentsAnimationTargets != null) {
- // Base class expects applyTransformUnchecked to be called here.
- // TODO: Remove this dependency for swipe-up animation.
- // applyTransformUnchecked();
- updateSysUiFlags(mCurrentShift.value);
- }
-
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationTargets != null) {
LiveTileOverlay.INSTANCE.update(
- mAppWindowAnimationHelper.getCurrentRectWithInsets(),
- mAppWindowAnimationHelper.getCurrentCornerRadius());
+ mTaskViewSimulator.getCurrentCropRect(),
+ mTaskViewSimulator.getCurrentCornerRadius());
}
}
@@ -599,14 +557,8 @@
}
}
- if (mWindowTransitionController != null) {
- float progress = mCurrentShift.value / mDragLengthFactor;
- mWindowTransitionController.setPlayFraction(progress);
- mTransformParams.setTargetSet(mRecentsAnimationTargets);
-
- mTaskViewSimulator.setScroll(mRecentsView == null ? 0 : mRecentsView.getScrollOffset());
- mTaskViewSimulator.apply(mTransformParams);
- }
+ updateSysUiFlags(mCurrentShift.value);
+ applyWindowTransform();
updateLauncherTransitionProgress();
}
@@ -634,9 +586,8 @@
boolean quickswitchThresholdPassed = centermostTask != runningTask;
// We will handle the sysui flags based on the centermost task view.
- mRecentsAnimationController.setUseLauncherSystemBarFlags(
- (swipeUpThresholdPassed || quickswitchThresholdPassed)
- && centermostTaskFlags != 0);
+ mRecentsAnimationController.setUseLauncherSystemBarFlags(swipeUpThresholdPassed
+ || (quickswitchThresholdPassed && centermostTaskFlags != 0));
mRecentsAnimationController.setSplitScreenMinimized(swipeUpThresholdPassed);
int sysuiFlags = swipeUpThresholdPassed ? 0 : centermostTaskFlags;
@@ -680,7 +631,7 @@
*/
@UiThread
private void notifyGestureStartedAsync() {
- final T curActivity = mActivity;
+ final Launcher curActivity = mActivity;
if (curActivity != null) {
// Once the gesture starts, we can no longer transition home through the button, so
// reset the force override of the activity visibility
@@ -731,7 +682,7 @@
setTargetAlphaProvider(LauncherSwipeHandler::getHiddenTargetAlpha);
}
- BaseDraggingActivity activity = mActivityInterface.getCreatedActivity();
+ StatefulActivity activity = mActivityInterface.getCreatedActivity();
return activity == null ? InputConsumer.NO_OP
: new OverviewInputConsumer(mGestureState, activity, null, true);
}
@@ -771,7 +722,8 @@
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return false;
}
- if (appearedTaskTarget.taskId == mLastStartedTaskId) {
+ if (mStateCallback.hasStates(STATE_START_NEW_TASK)
+ && appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) {
reset();
return true;
}
@@ -962,26 +914,64 @@
Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
// Set the state, but don't notify until the animation completes
mGestureState.setEndTarget(target, false /* isAtomic */);
-
maybeUpdateRecentsAttachedState();
if (mGestureState.getEndTarget() == HOME) {
HomeAnimationFactory homeAnimFactory;
if (mActivity != null) {
- homeAnimFactory = mActivityInterface.prepareHomeUI();
- } else {
- homeAnimFactory = new HomeAnimationFactory() {
- @NonNull
+ final TaskView runningTaskView = mRecentsView.getRunningTaskView();
+ final View workspaceView;
+ if (runningTaskView != null
+ && runningTaskView.getTask().key.getComponent() != null) {
+ workspaceView = mActivity.getWorkspace().getFirstMatchForAppClose(
+ runningTaskView.getTask().key.getComponent().getPackageName(),
+ UserHandle.of(runningTaskView.getTask().key.userId));
+ } else {
+ workspaceView = null;
+ }
+ final RectF iconLocation = new RectF();
+ boolean canUseWorkspaceView =
+ workspaceView != null && workspaceView.isAttachedToWindow();
+ FloatingIconView floatingIconView = canUseWorkspaceView
+ ? FloatingIconView.getFloatingIconView(mActivity, workspaceView,
+ true /* hideOriginal */, iconLocation, false /* isOpening */)
+ : null;
+
+ mActivity.getRootView().setForceHideBackArrow(true);
+ mActivityInterface.setHintUserWillBeActive();
+
+ homeAnimFactory = new HomeAnimationFactory(floatingIconView) {
+
@Override
public RectF getWindowTargetRect() {
- RectF fallbackTarget = new RectF(mAppWindowAnimationHelper.getTargetRect());
- Utilities.scaleRectFAboutCenter(fallbackTarget, 0.25f);
- return fallbackTarget;
+ if (canUseWorkspaceView) {
+ return iconLocation;
+ } else {
+ return super.getWindowTargetRect();
+ }
}
@NonNull
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
+ // Return an empty APC here since we have an non-user controlled animation
+ // to home.
+ long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+ return mActivity.getStateManager().createAnimationToNewWorkspace(
+ NORMAL, accuracy, 0 /* animComponents */);
+ }
+
+ @Override
+ public void playAtomicAnimation(float velocity) {
+ new StaggeredWorkspaceAnim(mActivity, velocity,
+ true /* animateOverviewScrim */).start();
+ }
+ };
+
+ } else {
+ homeAnimFactory = new HomeAnimationFactory(null) {
+ @Override
+ public AnimatorPlaybackController createActivityAnimationToHome() {
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
}
};
@@ -1025,17 +1015,19 @@
if (mRecentsView != null) {
int taskToLaunch = mRecentsView.getNextPage();
int runningTask = getLastAppearedTaskIndex();
- if (target == NEW_TASK && taskToLaunch == runningTask) {
+ boolean hasStartedNewTask = hasStartedNewTask();
+ if (target == NEW_TASK && taskToLaunch == runningTask
+ && !hasStartedNewTask) {
// We are about to launch the current running task, so use LAST_TASK
// state instead of NEW_TASK. This could happen, for example, if our
// scroll is aborted after we determined the target to be NEW_TASK.
mGestureState.setEndTarget(LAST_TASK);
- } else if (target == LAST_TASK && taskToLaunch != runningTask) {
+ } else if (target == LAST_TASK && hasStartedNewTask) {
// We are about to re-launch the previously running task, but we can't
// just finish the controller like we normally would because that would
- // instead resume the last task that appeared. As a workaround, launch
- // the task as if it were a new task.
- // TODO: is this expected?
+ // instead resume the last task that appeared, and not ensure that this
+ // task is restored to the top. To address this, re-launch the task as
+ // if it were a new task.
mGestureState.setEndTarget(NEW_TASK);
}
}
@@ -1173,6 +1165,12 @@
});
}
+ @Override
+ protected void onRestartLastAppearedTask() {
+ super.onRestartLastAppearedTask();
+ reset();
+ }
+
private void reset() {
mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LockScreenRecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LockScreenRecentsActivity.java
deleted file mode 100644
index 65f323c..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LockScreenRecentsActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-/**
- * Empty activity to start a recents transition
- */
-public class LockScreenRecentsActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- finish();
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index c6b719a..8846727 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -28,9 +28,10 @@
import android.view.ViewConfiguration;
import androidx.annotation.BinderThread;
-import com.android.launcher3.BaseDraggingActivity;
+
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
@@ -111,7 +112,7 @@
protected void onTransitionComplete() {
// TODO(b/138729100) This doesn't execute first time launcher is run
if (mTriggeredFromAltTab) {
- RecentsView rv = (RecentsView) mActivityInterface.getVisibleRecentsView();
+ RecentsView rv = mActivityInterface.getVisibleRecentsView();
if (rv == null) {
return;
}
@@ -136,7 +137,7 @@
@Override
protected boolean handleCommand(long elapsedTime) {
- RecentsView recents = (RecentsView) mActivityInterface.getVisibleRecentsView();
+ RecentsView recents = mActivityInterface.getVisibleRecentsView();
if (recents == null) {
return false;
}
@@ -150,9 +151,9 @@
}
}
- private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
+ private class RecentsActivityCommand<T extends StatefulActivity<?>> implements Runnable {
- protected final BaseActivityInterface<T> mActivityInterface;
+ protected final BaseActivityInterface<?, T> mActivityInterface;
private final long mCreateTime;
private final AppToOverviewAnimationProvider<T> mAnimationProvider;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index 86cfbdf..a4670fd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -15,6 +15,9 @@
*/
package com.android.quickstep;
+import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
@@ -29,21 +32,32 @@
import android.app.ActivityOptions;
import android.content.Intent;
import android.content.res.Configuration;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.view.View;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAnimationRunner;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.ObjectWrapper;
+import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.fallback.FallbackRecentsStateController;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsRootView;
+import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityOptionsCompat;
@@ -51,26 +65,40 @@
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* A recents activity that shows the recently launched tasks as swipable task cards.
* See {@link com.android.quickstep.views.RecentsView}.
*/
-public final class RecentsActivity extends BaseRecentsActivity {
+public final class RecentsActivity extends StatefulActivity<RecentsState> {
public static final String EXTRA_THUMBNAIL = "thumbnailData";
public static final String EXTRA_TASK_ID = "taskID";
+ public static final ActivityTracker<RecentsActivity> ACTIVITY_TRACKER =
+ new ActivityTracker<>();
private Handler mUiHandler = new Handler(Looper.getMainLooper());
private RecentsRootView mRecentsRootView;
private FallbackRecentsView mFallbackRecentsView;
+ private OverviewActionsView mActionsView;
- @Override
+ private Configuration mOldConfig;
+
+ private StateManager<RecentsState> mStateManager;
+
+ /**
+ * Init drag layer and overview panel views.
+ */
protected void initViews() {
setContentView(R.layout.fallback_recents_activity);
mRecentsRootView = findViewById(R.id.drag_layer);
mFallbackRecentsView = findViewById(R.id.overview_panel);
+ mActionsView = findViewById(R.id.overview_actions_view);
+
mRecentsRootView.recreateControllers();
- mFallbackRecentsView.init(findViewById(R.id.overview_actions_view));
+ mFallbackRecentsView.init(mActionsView);
}
@Override
@@ -103,25 +131,38 @@
intent.removeExtra(EXTRA_TASK_ID);
intent.removeExtra(EXTRA_THUMBNAIL);
super.onNewIntent(intent);
+ ACTIVITY_TRACKER.handleNewIntent(this, intent);
}
- @Override
+ /**
+ * Logic for when device configuration changes (rotation, screen size change, multi-window,
+ * etc.)
+ */
protected void onHandleConfigChanged() {
- super.onHandleConfigChanged();
+ mUserEventDispatcher = null;
+ initDeviceProfile();
+
+ AbstractFloatingView.closeOpenViews(this, true,
+ AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
+ dispatchDeviceProfileChanged();
+
+ reapplyUi();
mRecentsRootView.recreateControllers();
}
- @Override
- protected void reapplyUi() {
- mRecentsRootView.dispatchInsets();
- }
-
- @Override
+ /**
+ * Generate the device profile to use in this activity.
+ * @return device profile
+ */
protected DeviceProfile createDeviceProfile() {
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
+ DeviceProfile dp1 = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
+
+ // In case we are reusing IDP, create a copy so that we don't conflict with Launcher
+ // activity.
return (mRecentsRootView != null) && isInMultiWindowMode()
? dp.getMultiWindowProfile(this, getMultiWindowDisplaySize())
- : super.createDeviceProfile();
+ : dp1.copy(this);
}
@Override
@@ -139,6 +180,10 @@
return (T) mFallbackRecentsView;
}
+ public OverviewActionsView getActionsView() {
+ return mActionsView;
+ }
+
@Override
public void returnToHomescreen() {
super.returnToHomescreen();
@@ -160,12 +205,7 @@
RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
wallpaperTargets);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mFallbackRecentsView.resetViewUI();
- }
- });
+ anim.addListener(resetStateListener());
result.setAnimation(anim, RecentsActivity.this);
}
};
@@ -193,12 +233,7 @@
.createAdjacentPageAnimForTaskLaunch(taskView);
adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
adjacentAnimation.setDuration(RECENTS_LAUNCH_DURATION);
- adjacentAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mFallbackRecentsView.resetTaskVisuals();
- }
- });
+ adjacentAnimation.addListener(resetStateListener());
target.play(adjacentAnimation);
}
return target;
@@ -210,13 +245,14 @@
// onActivityStart callback.
mFallbackRecentsView.setContentAlpha(1);
super.onStart();
- mFallbackRecentsView.resetTaskVisuals();
}
@Override
protected void onStop() {
super.onStop();
- mFallbackRecentsView.reset();
+
+ // Workaround for b/78520668, explicitly trim memory once UI is hidden
+ onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
}
@Override
@@ -225,7 +261,97 @@
AccessibilityManagerCompat.sendStateEventToTest(getBaseContext(), OVERVIEW_STATE_ORDINAL);
}
- public void onTaskLaunched() {
- mFallbackRecentsView.resetTaskVisuals();
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mStateManager = new StateManager<>(this, RecentsState.DEFAULT);
+
+ mOldConfig = new Configuration(getResources().getConfiguration());
+ initDeviceProfile();
+ initViews();
+
+ getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
+ Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
+ ACTIVITY_TRACKER.handleCreate(this);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ int diff = newConfig.diff(mOldConfig);
+ if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
+ onHandleConfigChanged();
+ }
+ mOldConfig.setTo(newConfig);
+ super.onConfigurationChanged(newConfig);
+ }
+
+ /**
+ * Initialize/update the device profile.
+ */
+ private void initDeviceProfile() {
+ mDeviceProfile = createDeviceProfile();
+ onDeviceProfileInitiated();
+ }
+
+ @Override
+ public void onEnterAnimationComplete() {
+ super.onEnterAnimationComplete();
+ // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
+ // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+ // overview
+ RecentsModel.INSTANCE.get(this).getThumbnailCache()
+ .getHighResLoadingState().setVisible(true);
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ RecentsModel.INSTANCE.get(this).onTrimMemory(level);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ ACTIVITY_TRACKER.onActivityDestroyed(this);
+ }
+
+ @Override
+ public void onBackPressed() {
+ // TODO: Launch the task we came from
+ startHome();
+ }
+
+ public void startHome() {
+ startActivity(new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+ @Override
+ protected StateHandler<RecentsState>[] createStateHandlers() {
+ return new StateHandler[] { new FallbackRecentsStateController(this) };
+ }
+
+ @Override
+ public StateManager<RecentsState> getStateManager() {
+ return mStateManager;
+ }
+
+ @Override
+ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.println(prefix + "Misc:");
+ dumpMisc(prefix + "\t", writer);
+ }
+
+ private AnimatorListenerAdapter resetStateListener() {
+ return new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mFallbackRecentsView.resetTaskVisuals();
+ mStateManager.reapplyState();
+ }
+ };
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index b44d6df..042c542 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -22,6 +22,7 @@
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.widget.Toast;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
@@ -109,16 +110,26 @@
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) {
ImageActionsApi imageApi = new ImageActionsApi(
mApplicationContext, mThumbnailView::getThumbnail);
+ final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+
getActionsView().setCallbacks(new OverlayUICallbacks() {
@Override
public void onShare() {
- imageApi.startShareActivity();
+ if (isAllowedByPolicy) {
+ imageApi.startShareActivity();
+ } else {
+ showBlockedByPolicyMessage();
+ }
}
@Override
public void onScreenshot() {
- imageApi.saveScreenshot(mThumbnailView.getThumbnail(),
- getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key.id);
+ if (isAllowedByPolicy) {
+ imageApi.saveScreenshot(mThumbnailView.getThumbnail(),
+ getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key.id);
+ } else {
+ showBlockedByPolicyMessage();
+ }
}
});
}
@@ -152,6 +163,13 @@
// TODO: return the real insets
return Insets.of(0, 0, 0, 0);
}
+
+ private void showBlockedByPolicyMessage() {
+ Toast.makeText(
+ mThumbnailView.getContext(),
+ R.string.blocked_by_policy,
+ Toast.LENGTH_LONG).show();
+ }
}
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 8eb15cf..acc7794 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -38,6 +38,7 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Icon;
import android.os.Build;
@@ -62,6 +63,7 @@
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.provider.RestoreDbTask;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tracing.nano.LauncherTraceProto;
@@ -69,6 +71,7 @@
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
@@ -81,6 +84,7 @@
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AssistantUtilities;
import com.android.quickstep.util.ProtoTracer;
+import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.OverscrollPlugin;
import com.android.systemui.plugins.PluginListener;
@@ -118,7 +122,7 @@
/**
* Service connected by system-UI for handling touch interaction.
*/
-@TargetApi(Build.VERSION_CODES.Q)
+@TargetApi(Build.VERSION_CODES.R)
public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin>,
ProtoTraceable<LauncherTraceProto> {
@@ -229,6 +233,11 @@
MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
}
+ public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets) {
+ WindowBounds wb = new WindowBounds(bounds, insets);
+ MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
+ }
+
/** Deprecated methods **/
public void onQuickStep(MotionEvent motionEvent) { }
@@ -518,6 +527,7 @@
ActiveGestureLog.INSTANCE.generateAndSetLogId());
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
gestureState.updateRunningTask(mGestureState.getRunningTask());
+ gestureState.updateLastStartedTaskId(mGestureState.getLastStartedTaskId());
} else {
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
() -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
@@ -601,15 +611,24 @@
}
private void handleOrientationSetup(InputConsumer baseInputConsumer) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.1");
+ }
if (!isFixedRotationTransformEnabled(this)) {
return;
}
mDeviceState.enableMultipleRegions(baseInputConsumer instanceof OtherActivityInputConsumer);
BaseDraggingActivity activity =
mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.2");
+ }
if (activity == null || !(activity.getOverviewPanel() instanceof RecentsView)) {
return;
}
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.3");
+ }
((RecentsView) activity.getOverviewPanel())
.setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
mDeviceState.getDisplayRotation());
@@ -687,7 +706,7 @@
public InputConsumer createOverviewInputConsumer(GestureState previousGestureState,
GestureState gestureState, MotionEvent event,
boolean forceOverviewInputConsumer) {
- BaseDraggingActivity activity = gestureState.getActivityInterface().getCreatedActivity();
+ StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity();
if (activity == null) {
return mResetGestureInputConsumer;
}
@@ -736,7 +755,7 @@
return;
}
- final BaseActivityInterface<BaseDraggingActivity> activityInterface =
+ final BaseActivityInterface activityInterface =
mOverviewComponentObserver.getActivityInterface();
final Intent overviewIntent = new Intent(
mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
new file mode 100644
index 0000000..3f1e7ba
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.fallback;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
+import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
+import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
+
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.views.ClearAllButton;
+
+/**
+ * State controller for fallback recents activity
+ */
+public class FallbackRecentsStateController implements StateHandler<RecentsState> {
+
+ private final StateAnimationConfig mNoConfig = new StateAnimationConfig();
+ private final RecentsActivity mActivity;
+ private final FallbackRecentsView mRecentsView;
+
+ public FallbackRecentsStateController(RecentsActivity activity) {
+ mActivity = activity;
+ mRecentsView = activity.getOverviewPanel();
+ }
+
+ @Override
+ public void setState(RecentsState state) {
+ mRecentsView.updateEmptyMessage();
+ mRecentsView.resetTaskVisuals();
+ setProperties(state, mNoConfig, PropertySetter.NO_ANIM_PROPERTY_SETTER);
+ }
+
+ @Override
+ public void setStateWithAnimation(RecentsState toState, StateAnimationConfig config,
+ PendingAnimation setter) {
+ if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) {
+ // The entire recents animation is played atomically.
+ return;
+ }
+ if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
+ return;
+ }
+ // While animating into recents, update the visible task data as needed
+ setter.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
+ mRecentsView.updateEmptyMessage();
+
+ setProperties(toState, config, setter);
+ }
+
+ private void setProperties(RecentsState state, StateAnimationConfig config,
+ PropertySetter setter) {
+ float buttonAlpha = state.hasButtons() ? 1 : 0;
+ setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
+ buttonAlpha, LINEAR);
+ setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
+ MultiValueAlpha.VALUE, buttonAlpha, LINEAR);
+
+ float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
+ setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndOffset[0],
+ config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
+ setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1],
+ config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
+
+ setter.setFloat(mRecentsView, TASK_MODALNESS, state.getOverviewModalness(),
+ config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
+ setter.setFloat(mRecentsView, FULLSCREEN_PROGRESS, state.isFullScreen() ? 1 : 0, LINEAR);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 559004c..f958e6d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -15,17 +15,17 @@
*/
package com.android.quickstep.fallback;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
+import static com.android.quickstep.fallback.RecentsState.DEFAULT;
+import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
+import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
-import android.graphics.Canvas;
+import android.os.Build;
import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.view.View;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
@@ -34,26 +34,9 @@
import java.util.ArrayList;
-public class FallbackRecentsView extends RecentsView<RecentsActivity> {
-
- public static final FloatProperty<FallbackRecentsView> ZOOM_PROGRESS =
- new FloatProperty<FallbackRecentsView> ("zoomInProgress") {
-
- @Override
- public void setValue(FallbackRecentsView view, float value) {
- view.setZoomProgress(value);
- }
-
- @Override
- public Float get(FallbackRecentsView view) {
- return view.mZoomInProgress;
- }
- };
-
- private float mZoomInProgress = 0;
- private boolean mInOverviewState = true;
-
- private float mZoomScale = 1f;
+@TargetApi(Build.VERSION_CODES.R)
+public class FallbackRecentsView extends RecentsView<RecentsActivity>
+ implements StateListener<RecentsState> {
private RunningTaskInfo mRunningTaskInfo;
@@ -62,7 +45,8 @@
}
public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr, FALLBACK_RECENTS_SIZE_STRATEGY);
+ super(context, attrs, defStyleAttr, FallbackActivityInterface.INSTANCE);
+ mActivity.getStateManager().addStateListener(this);
}
@Override
@@ -78,70 +62,11 @@
}
@Override
- public void onViewAdded(View child) {
- super.onViewAdded(child);
- updateEmptyMessage();
- }
-
- @Override
- public void onViewRemoved(View child) {
- super.onViewRemoved(child);
- updateEmptyMessage();
- }
-
- @Override
- public void draw(Canvas canvas) {
- maybeDrawEmptyMessage(canvas);
- super.draw(canvas);
- }
-
- @Override
- public void reset() {
- super.reset();
- resetViewUI();
- }
-
- @Override
public boolean shouldUseMultiWindowTaskSizeStrategy() {
// Just use the activity task size for multi-window as well.
return false;
}
- public void resetViewUI() {
- setZoomProgress(0);
- resetTaskVisuals();
- }
-
- public void setInOverviewState(boolean inOverviewState) {
- if (mInOverviewState != inOverviewState) {
- mInOverviewState = inOverviewState;
- if (mInOverviewState) {
- resetTaskVisuals();
- } else {
- setZoomProgress(1);
- }
- }
- }
-
- @Override
- public void resetTaskVisuals() {
- super.resetTaskVisuals();
- setFullscreenProgress(mFullscreenProgress);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mZoomScale = getMaxScaleForFullScreen();
- setZoomProgress(mZoomInProgress);
- }
-
- public void setZoomProgress(float progress) {
- mZoomInProgress = progress;
- SCALE_PROPERTY.set(this, Utilities.mapRange(mZoomInProgress, 1, mZoomScale));
- FULLSCREEN_PROGRESS.set(this, mZoomInProgress);
- }
-
public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
mRunningTaskInfo = runningTaskInfo;
onGestureAnimationStart(runningTaskInfo == null ? -1 : runningTaskInfo.taskId);
@@ -178,4 +103,37 @@
}
super.applyLoadPlan(tasks);
}
+
+ @Override
+ public void setModalStateEnabled(boolean isModalState) {
+ super.setModalStateEnabled(isModalState);
+ if (isModalState) {
+ mActivity.getStateManager().goToState(RecentsState.MODAL_TASK);
+ } else {
+ if (mActivity.isInState(RecentsState.MODAL_TASK)) {
+ mActivity.getStateManager().goToState(DEFAULT);
+ }
+ }
+ }
+
+ @Override
+ public void onStateTransitionStart(RecentsState toState) {
+ setOverviewStateEnabled(true);
+ setFreezeViewVisibility(true);
+ }
+
+ @Override
+ public void onStateTransitionComplete(RecentsState finalState) {
+ setOverlayEnabled(finalState == DEFAULT || finalState == MODAL_TASK);
+ setFreezeViewVisibility(false);
+ }
+
+ @Override
+ public void setOverviewStateEnabled(boolean enabled) {
+ super.setOverviewStateEnabled(enabled);
+ if (enabled) {
+ RecentsState state = mActivity.getStateManager().getState();
+ setDisallowScrollToClearAll(!state.hasButtons());
+ }
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java
new file mode 100644
index 0000000..211a30c
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.fallback;
+
+import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState;
+import static com.android.launcher3.uioverrides.states.OverviewModalTaskState.getOverviewScaleAndOffsetForModalState;
+
+import android.content.Context;
+
+import com.android.launcher3.statemanager.BaseState;
+import com.android.quickstep.RecentsActivity;
+
+/**
+ * State definition for Fallback recents
+ */
+public class RecentsState implements BaseState<RecentsState> {
+
+ private static final int FLAG_MODAL = BaseState.getFlag(0);
+ private static final int FLAG_HAS_BUTTONS = BaseState.getFlag(1);
+ private static final int FLAG_FULL_SCREEN = BaseState.getFlag(2);
+
+ public static final RecentsState DEFAULT = new RecentsState(0, FLAG_HAS_BUTTONS);
+ public static final RecentsState MODAL_TASK = new ModalState(1,
+ FLAG_DISABLE_RESTORE | FLAG_HAS_BUTTONS | FLAG_MODAL);
+ public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
+ FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN);
+
+ public final int ordinal;
+ private final int mFlags;
+
+ private static final float NO_OFFSET = 0;
+ private static final float NO_SCALE = 1;
+
+ public RecentsState(int id, int flags) {
+ this.ordinal = id;
+ this.mFlags = flags;
+ }
+
+
+ @Override
+ public String toString() {
+ return "Ordinal-" + ordinal;
+ }
+
+ @Override
+ public final boolean hasFlag(int mask) {
+ return (mFlags & mask) != 0;
+ }
+
+ @Override
+ public int getTransitionDuration(Context context) {
+ return 250;
+ }
+
+ @Override
+ public RecentsState getHistoryForState(RecentsState previousState) {
+ return DEFAULT;
+ }
+
+ /**
+ * For this state, how modal should over view been shown. 0 modalness means all tasks drawn,
+ * 1 modalness means the current task is show on its own.
+ */
+ public float getOverviewModalness() {
+ return hasFlag(FLAG_MODAL) ? 1 : 0;
+ }
+
+ public boolean isFullScreen() {
+ return hasFlag(FLAG_FULL_SCREEN);
+ }
+
+ public boolean hasButtons() {
+ return hasFlag(FLAG_HAS_BUTTONS);
+ }
+
+ public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+ return new float[] { NO_SCALE, NO_OFFSET };
+ }
+
+
+ private static class ModalState extends RecentsState {
+
+ public ModalState(int id, int flags) {
+ super(id, flags);
+ }
+
+ @Override
+ public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+ return getOverviewScaleAndOffsetForModalState(activity);
+ }
+ }
+
+ private static class BackgroundAppState extends RecentsState {
+ public BackgroundAppState(int id, int flags) {
+ super(id, flags);
+ }
+
+ @Override
+ public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+ return getOverviewScaleAndOffsetForBackgroundState(activity);
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index adf19df..2dc7f5f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -18,14 +18,15 @@
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_UP;
-
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
-import android.content.ComponentName;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
@@ -34,15 +35,14 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
-
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
-import com.android.quickstep.LockScreenRecentsActivity;
import com.android.quickstep.MultiStateCallback;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
@@ -52,6 +52,7 @@
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.TransformParams;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -61,8 +62,6 @@
public class DeviceLockedInputConsumer implements InputConsumer,
RecentsAnimationCallbacks.RecentsAnimationListener {
- private static final float SCALE_DOWN = 0.75f;
-
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[2] : null;
private static int getFlagForIndex(int index, String name) {
if (DEBUG_STATES) {
@@ -93,6 +92,7 @@
private float mProgress;
private boolean mThresholdCrossed = false;
+ private boolean mHomeLaunched = false;
private RecentsAnimationController mRecentsAnimationController;
private RecentsAnimationTargets mRecentsAnimationTargets;
@@ -176,7 +176,6 @@
* the animation can still be running.
*/
private void finishTouchTracking(MotionEvent ev) {
- mStateCallback.setState(STATE_HANDLER_INVALIDATED);
if (mThresholdCrossed && ev.getAction() == ACTION_UP) {
mVelocityTracker.computeCurrentVelocity(1000,
ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
@@ -192,12 +191,34 @@
} else {
dismissTask = mProgress >= (1 - MIN_PROGRESS_FOR_OVERVIEW);
}
- if (dismissTask) {
- // For now, just start the home intent so user is prompted to unlock the device.
- mContext.startActivity(new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- }
+
+ // Animate back to fullscreen before finishing
+ ValueAnimator animator = ValueAnimator.ofFloat(mTransformParams.getProgress(), 0f);
+ animator.setDuration(100);
+ animator.setInterpolator(Interpolators.ACCEL);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ mTransformParams.setProgress((float) valueAnimator.getAnimatedValue());
+ mAppWindowAnimationHelper.applyTransform(mTransformParams);
+ }
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (dismissTask) {
+ // For now, just start the home intent so user is prompted to unlock the device.
+ mContext.startActivity(new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ mHomeLaunched = true;
+ }
+ mStateCallback.setState(STATE_HANDLER_INVALIDATED);
+ }
+ });
+ animator.start();
+ } else {
+ mStateCallback.setState(STATE_HANDLER_INVALIDATED);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
@@ -205,13 +226,11 @@
private void startRecentsTransition() {
mThresholdCrossed = true;
+ mHomeLaunched = false;
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitorCompat.pilferPointers();
- Intent intent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_DEFAULT)
- .setComponent(new ComponentName(mContext, LockScreenRecentsActivity.class))
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ Intent intent = mGestureState.getHomeIntent()
.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
mTaskAnimationManager.startRecentsAnimation(mGestureState, intent, this);
}
@@ -229,8 +248,9 @@
mAppWindowAnimationHelper.updateSource(displaySize, targetCompat);
}
- Utilities.scaleRectAboutCenter(displaySize, SCALE_DOWN);
- displaySize.offsetTo(displaySize.left, 0);
+ // Offset the surface slightly
+ displaySize.offset(0, mContext.getResources().getDimensionPixelSize(
+ R.dimen.device_locked_y_offset));
mTransformParams.setTargetSet(mRecentsAnimationTargets);
mAppWindowAnimationHelper.updateTargetRect(displaySize);
mAppWindowAnimationHelper.applyTransform(mTransformParams);
@@ -245,7 +265,9 @@
}
private void endRemoteAnimation() {
- if (mRecentsAnimationController != null) {
+ if (mHomeLaunched) {
+ ActivityManagerWrapper.getInstance().cancelRecentsAnimation(false);
+ } else if (mRecentsAnimationController != null) {
mRecentsAnimationController.finishController(
false /* toRecents */, null /* callback */, false /* sendUserLeaveHint */);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 6bfabcd..11fee2f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -18,13 +18,14 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
@@ -40,11 +41,11 @@
/**
* Input consumer for handling touch on the recents/Launcher activity.
*/
-public class OverviewInputConsumer<T extends BaseDraggingActivity>
+public class OverviewInputConsumer<T extends StatefulActivity<?>>
implements InputConsumer {
private final T mActivity;
- private final BaseActivityInterface<T> mActivityInterface;
+ private final BaseActivityInterface<?, T> mActivityInterface;
private final BaseDragLayer mTarget;
private final InputMonitorCompat mInputMonitor;
@@ -95,6 +96,9 @@
ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
}
ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "OverviewInputConsumer");
+ }
boolean handled = mEventReceiver.test(ev);
ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
ev.setEdgeFlags(flags);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index d3bd012..a7979cc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -222,18 +222,8 @@
mCurrentRect.set(params.getCurrentRect());
} else {
mTmpRectF.set(mTargetRect);
- Utilities.scaleRectFAboutCenter(mTmpRectF, params.getOffsetScale());
mCurrentRect.set(mRectFEvaluator.evaluate(
params.getProgress(), mSourceRect, mTmpRectF));
- if (mOrientedState == null
- || !mOrientedState.isMultipleOrientationSupportedByDevice()) {
- mCurrentRect.offset(params.getOffset(), 0);
- } else {
- int displayRotation = mOrientedState.getDisplayRotation();
- int launcherRotation = mOrientedState.getLauncherRotation();
- mOrientedState.getOrientationHandler().offsetTaskRect(
- mCurrentRect, params.getOffset(), displayRotation, launcherRotation);
- }
}
updateClipRect(params);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index 832f0e2..a3db940 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep.util;
-import static android.view.Surface.ROTATION_0;
-
import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
@@ -33,7 +31,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.views.RecentsView.ScrollState;
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
import com.android.quickstep.views.TaskView;
@@ -53,7 +51,7 @@
private final RecentsOrientedState mOrientationState;
private final Context mContext;
- private final WindowSizeStrategy mSizeStrategy;
+ private final BaseActivityInterface mSizeStrategy;
private final Rect mTaskRect = new Rect();
private final PointF mPivot = new PointF();
@@ -61,7 +59,6 @@
private final Matrix mMatrix = new Matrix();
private RemoteAnimationTargetCompat mRunningTarget;
- private RecentsAnimationTargets mAllTargets;
// Thumbnail view properties
private final Rect mThumbnailPosition = new Rect();
@@ -83,14 +80,11 @@
private boolean mLayoutValid = false;
private boolean mScrollValid = false;
- public TaskViewSimulator(Context context, WindowSizeStrategy sizeStrategy) {
+ public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
mContext = context;
mSizeStrategy = sizeStrategy;
mOrientationState = new RecentsOrientedState(context, sizeStrategy, i -> { });
- // We do not need to attach listeners as the simulator is created just for the gesture
- // duration, and any settings are unlikely to change during this
- mOrientationState.initWithoutListeners();
mCurrentFullscreenParams = new FullscreenDrawParams(context);
mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
@@ -109,15 +103,7 @@
* @see com.android.quickstep.views.RecentsView#setLayoutRotation(int, int)
*/
public void setLayoutRotation(int touchRotation, int displayRotation) {
- int launcherRotation;
- if (!mOrientationState.isMultipleOrientationSupportedByDevice()
- || mOrientationState.isHomeRotationAllowed()) {
- launcherRotation = displayRotation;
- } else {
- launcherRotation = ROTATION_0;
- }
-
- mOrientationState.update(touchRotation, displayRotation, launcherRotation);
+ mOrientationState.update(touchRotation, displayRotation);
mLayoutValid = false;
}
@@ -135,10 +121,8 @@
/**
* Sets the targets which the simulator will control
*/
- public void setPreview(
- RemoteAnimationTargetCompat runningTarget, RecentsAnimationTargets allTargets) {
+ public void setPreview(RemoteAnimationTargetCompat runningTarget) {
mRunningTarget = runningTarget;
- mAllTargets = allTargets;
mThumbnailData.insets.set(mRunningTarget.contentInsets);
// TODO: What is this?
@@ -161,6 +145,43 @@
}
/**
+ * Returns the current clipped/visible window bounds in the window coordinate space
+ */
+ public RectF getCurrentCropRect() {
+ // Crop rect is the inverse of thumbnail matrix
+ RectF insets = mCurrentFullscreenParams.mCurrentDrawnInsets;
+ mTempRectF.set(-insets.left, -insets.top,
+ mTaskRect.width() + insets.right, mTaskRect.height() + insets.bottom);
+ mInversePositionMatrix.mapRect(mTempRectF);
+ return mTempRectF;
+ }
+
+ public RecentsOrientedState getOrientationState() {
+ return mOrientationState;
+ }
+
+ /**
+ * Returns the current transform applied to the window
+ */
+ public Matrix getCurrentMatrix() {
+ return mMatrix;
+ }
+
+ /**
+ * Applies the rotation on the matrix to so that it maps from launcher coordinate space to
+ * window coordinate space.
+ */
+ public void applyWindowToHomeRotation(Matrix matrix) {
+ mMatrix.postTranslate(mDp.windowX, mDp.windowY);
+ postDisplayRotation(deltaRotation(
+ mOrientationState.getLauncherRotation(), mOrientationState.getDisplayRotation()),
+ mDp.widthPx, mDp.heightPx, matrix);
+ if (mRunningTarget != null) {
+ matrix.postTranslate(-mRunningTarget.position.x, -mRunningTarget.position.y);
+ }
+ }
+
+ /**
* Applies the target to the previously set parameters
*/
public void apply(TransformParams params) {
@@ -217,11 +238,7 @@
// Apply recensView matrix
mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);
- postDisplayRotation(deltaRotation(
- mOrientationState.getLauncherRotation(), mOrientationState.getDisplayRotation()),
- mDp.widthPx, mDp.heightPx, mMatrix);
- mMatrix.postTranslate(mDp.windowX - mRunningTarget.position.x,
- mDp.windowY - mRunningTarget.position.y);
+ applyWindowToHomeRotation(mMatrix);
// Crop rect is the inverse of thumbnail matrix
mTempRectF.set(-insets.left, -insets.top,
@@ -235,7 +252,7 @@
@Override
public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app,
int targetMode, TransformParams params) {
- if (app.mode == mAllTargets.targetMode
+ if (app.mode == targetMode
&& app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
builder.withMatrix(mMatrix)
.withWindowCrop(mTmpCropRect)
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
index 02e2142..83b64db 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
@@ -30,8 +30,6 @@
public class TransformParams {
private float mProgress;
- private float mOffset;
- private float mOffsetScale;
private @Nullable RectF mCurrentRect;
private float mTargetAlpha;
private float mCornerRadius;
@@ -43,8 +41,6 @@
public TransformParams() {
mProgress = 0;
- mOffset = 0;
- mOffsetScale = 1;
mCurrentRect = null;
mTargetAlpha = 1;
mCornerRadius = -1;
@@ -90,26 +86,6 @@
}
/**
- * If {@link #mCurrentRect} is null (i.e. {@link #setCurrentRect(RectF)} hasn't overridden
- * the default), then offset the current rect by this amount after computing the rect based
- * on {@link #mProgress}.
- */
- public TransformParams setOffset(float offset) {
- mOffset = offset;
- return this;
- }
-
- /**
- * If {@link #mCurrentRect} is null (i.e. {@link #setCurrentRect(RectF)} hasn't overridden
- * the default), then scale the current rect by this amount after computing the rect based
- * on {@link #mProgress}.
- */
- public TransformParams setOffsetScale(float offsetScale) {
- mOffsetScale = offsetScale;
- return this;
- }
-
- /**
* Specifies the set of RemoteAnimationTargetCompats that are included in the transformation
* that these TransformParams help compute. These TransformParams generally only apply to
* the targetSet.apps which match the targetSet.targetMode (e.g. the MODE_CLOSING app when
@@ -183,14 +159,6 @@
return mProgress;
}
- public float getOffset() {
- return mOffset;
- }
-
- public float getOffsetScale() {
- return mOffsetScale;
- }
-
@Nullable
public RectF getCurrentRect() {
return mCurrentRect;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
index 1018211..2c85618 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
@@ -64,7 +64,13 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mParent = (RecentsView) getParent();
- mIsRtl = !mParent.getPagedOrientationHandler().getRecentsRtlSetting(getResources());
+ mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ }
+
+ @Override
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+ mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 6d3077e..3d89403 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -25,30 +25,28 @@
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
-import android.graphics.Canvas;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
-import android.view.View;
import android.widget.FrameLayout;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ScrimView;
+import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.PluginListener;
@@ -59,7 +57,7 @@
*/
@TargetApi(Build.VERSION_CODES.O)
public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
- implements StateListener {
+ implements StateListener<LauncherState> {
private final TransformParams mTransformParams = new TransformParams();
@@ -91,7 +89,7 @@
}
public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr, LAUNCHER_ACTIVITY_SIZE_STRATEGY);
+ super(context, attrs, defStyleAttr, LauncherActivityInterface.INSTANCE);
mActivity.getStateManager().addStateListener(this);
}
@@ -123,24 +121,6 @@
}
}
- @Override
- public void draw(Canvas canvas) {
- maybeDrawEmptyMessage(canvas);
- super.draw(canvas);
- }
-
- @Override
- public void onViewAdded(View child) {
- super.onViewAdded(child);
- updateEmptyMessage();
- }
-
- @Override
- protected void onTaskStackUpdated() {
- // Lazily update the empty message only when the task stack is reapplied
- updateEmptyMessage();
- }
-
/**
* Animates adjacent tasks and translate hotseat off screen as well.
*/
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
index 0af1c0e..f06a6a4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
@@ -31,6 +31,7 @@
import com.android.launcher3.R;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
import java.lang.annotation.Retention;
@@ -141,4 +142,19 @@
public AlphaProperty getVisibilityAlpha() {
return mMultiValueAlpha.getProperty(INDEX_VISIBILITY_ALPHA);
}
+
+ /** Updates vertical margins for different navigation mode. */
+ public void updateVerticalMarginForNavModeChange(Mode mode) {
+ int bottomMargin = 0;
+ if (mode == Mode.THREE_BUTTONS) {
+ bottomMargin = getResources()
+ .getDimensionPixelSize(R.dimen.overview_actions_bottom_margin_three_button);
+ } else {
+ bottomMargin = getResources()
+ .getDimensionPixelSize(R.dimen.overview_actions_bottom_margin_gesture);
+ }
+ LayoutParams params = (LayoutParams) getLayoutParams();
+ params.setMargins(
+ params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 49aca30..253e83c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -16,6 +16,8 @@
package com.android.quickstep.views;
+import static android.view.Surface.ROTATION_0;
+
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
@@ -30,8 +32,8 @@
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_DISMISS_SWIPE_UP;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_LAUNCH_SWIPE_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
@@ -55,7 +57,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.PointF;
@@ -114,6 +115,7 @@
import com.android.launcher3.util.OverScroller;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool;
+import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
@@ -125,14 +127,13 @@
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.util.TransformParams;
-import com.android.quickstep.util.WindowSizeStrategy;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ConfigurationCompat;
import com.android.systemui.shared.system.LauncherEventUtil;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
@@ -147,7 +148,8 @@
@TargetApi(Build.VERSION_CODES.P)
public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable,
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
- InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener {
+ InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
+ SplitScreenBounds.OnChangeListener {
private static final String TAG = RecentsView.class.getSimpleName();
@@ -207,7 +209,7 @@
};
protected final RecentsOrientedState mOrientationState;
- protected final WindowSizeStrategy mSizeStrategy;
+ protected final BaseActivityInterface mSizeStrategy;
protected RecentsAnimationController mRecentsAnimationController;
protected RecentsAnimationTargets mRecentsAnimationTargets;
protected AppWindowAnimationHelper mAppWindowAnimationHelper;
@@ -379,7 +381,7 @@
};
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
- WindowSizeStrategy sizeStrategy) {
+ BaseActivityInterface sizeStrategy) {
super(context, attrs, defStyleAttr);
setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
setEnableFreeScroll(true);
@@ -509,7 +511,8 @@
mIPinnedStackAnimationListener.setActivity(mActivity);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
mIPinnedStackAnimationListener);
- mOrientationState.init();
+ mOrientationState.initListeners();
+ SplitScreenBounds.INSTANCE.addOnChangeListener(this);
}
@Override
@@ -523,8 +526,9 @@
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
mIdp.removeOnChangeListener(this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
+ SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
mIPinnedStackAnimationListener.setActivity(null);
- mOrientationState.destroy();
+ mOrientationState.destroyListeners();
}
@Override
@@ -550,6 +554,13 @@
child.setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_LTR : View.LAYOUT_DIRECTION_RTL);
updateTaskStartIndex(child);
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, false);
+ updateEmptyMessage();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ maybeDrawEmptyMessage(canvas);
+ super.draw(canvas);
}
private void updateTaskStartIndex(View affectingView) {
@@ -617,15 +628,6 @@
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- int windowConfigurationRotation = ConfigurationCompat
- .getWindowConfigurationRotation(getResources().getConfiguration());
- setLayoutInternal(mOrientationState.getTouchRotation(),
- mOrientationState.getDisplayRotation(), windowConfigurationRotation);
- }
-
- @Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
final int x = (int) ev.getX();
@@ -771,7 +773,10 @@
return taskViewCount;
}
- protected void onTaskStackUpdated() { }
+ protected void onTaskStackUpdated() {
+ // Lazily update the empty message only when the task stack is reapplied
+ updateEmptyMessage();
+ }
public void resetTaskVisuals() {
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
@@ -838,6 +843,11 @@
mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
}
+ /** Gets the task size for modal state. */
+ public void getModalTaskSize(Rect outRect) {
+ mSizeStrategy.calculateModalTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
+ }
+
@Override
protected boolean computeScrollHelper() {
boolean scrolling = super.computeScrollHelper();
@@ -1314,7 +1324,8 @@
ComponentKey compKey = TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key);
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
endState.logAction, Direction.UP, index, compKey);
- mActivity.getStatsLogManager().log(TASK_DISMISS_SWIPE_UP, taskView.buildProto());
+ mActivity.getStatsLogManager().log(
+ LAUNCHER_TASK_DISMISS_SWIPE_UP, taskView.buildProto());
}
}
@@ -1594,19 +1605,19 @@
}
public void setLayoutRotation(int touchRotation, int displayRotation) {
- int launcherRotation = mOrientationState.getLauncherRotation();
- setLayoutInternal(touchRotation, displayRotation, launcherRotation);
- }
-
- private void setLayoutInternal(int touchRotation, int displayRotation, int launcherRotation) {
- if (mOrientationState.update(touchRotation, displayRotation, launcherRotation)) {
+ if (mOrientationState.update(touchRotation, displayRotation)) {
mOrientationHandler = mOrientationState.getOrientationHandler();
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
- setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
+ setLayoutDirection(mIsRtl
+ ? View.LAYOUT_DIRECTION_RTL
+ : View.LAYOUT_DIRECTION_LTR);
+ mClearAllButton.setLayoutDirection(mIsRtl
+ ? View.LAYOUT_DIRECTION_LTR
+ : View.LAYOUT_DIRECTION_RTL);
mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
mActivity.getDragLayer().recreateControllers();
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
- touchRotation != 0 || launcherRotation != 0);
+ touchRotation != 0 || mOrientationState.getLauncherRotation() != ROTATION_0);
requestLayout();
}
}
@@ -1702,7 +1713,8 @@
}
int count = getChildCount();
- TaskView runningTask = mRunningTaskId == -1 ? null : getTaskView(mRunningTaskId);
+ TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden
+ ? null : getTaskView(mRunningTaskId);
int midPoint = runningTask == null ? -1 : indexOfChild(runningTask);
int currentPage = getCurrentPage();
@@ -1892,8 +1904,8 @@
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
endState.logAction, Direction.DOWN, indexOfChild(tv),
TaskUtils.getLaunchComponentKeyForTask(task.key));
- mActivity.getStatsLogManager().log(TASK_LAUNCH_SWIPE_DOWN, tv.buildProto()
- );
+ mActivity.getStatsLogManager().log(
+ LAUNCHER_TASK_LAUNCH_SWIPE_DOWN, tv.buildProto());
}
} else {
onTaskLaunched(false);
@@ -2079,14 +2091,6 @@
return getScrollForPage(getRunningTaskIndex()) - mOrientationHandler.getPrimaryScroll(this);
}
- /**
- * @return How many pixels the running task is offset on the x-axis due to the current scrollX
- * and parent scale.
- */
- public float getScrollOffsetScaled() {
- return getScrollOffset() * mOrientationHandler.getPrimaryScale(this);
- }
-
public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
float degreesRotated;
if (navbarRotation == 0) {
@@ -2165,18 +2169,6 @@
updatePageOffsets();
if (getCurrentPageTaskView() != null) {
getCurrentPageTaskView().setModalness(modalness);
- TaskView tv = getCurrentPageTaskView();
-
- // Move the task view up as it scales...
- // ...the icon on taskview is hidden in modal state, so consider the top of the task
- mTempFloatPoint[0] = 0;
- mTempFloatPoint[1] = tv.getTop() + mTaskTopMargin;
- // ...find the top after the transformation
- getMatrix().mapPoints(mTempFloatPoint);
-
- // ...make it match the top inset
- float calcOffset = (mInsets.top - mTempFloatPoint[1]) * mTaskModalness;
- tv.setTranslationY(calcOffset);
}
}
@@ -2185,6 +2177,13 @@
return null;
}
+ @Override
+ public void onSecondaryWindowBoundsChanged() {
+ // Invalidate the task view size
+ setInsets(mInsets);
+ requestLayout();
+ }
+
/**
* Enables or disables modal state for RecentsView
* @param isModalState
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 97652aa..6b759ba 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -30,7 +30,7 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_LAUNCH_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -212,7 +212,7 @@
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
TaskUtils.getLaunchComponentKeyForTask(getTask().key));
- mActivity.getStatsLogManager().log(TASK_LAUNCH_TAP, buildProto());
+ mActivity.getStatsLogManager().log(LAUNCHER_TASK_LAUNCH_TAP, buildProto());
});
mCurrentFullscreenParams = new FullscreenDrawParams(context);
@@ -449,13 +449,13 @@
public void setOrientationState(RecentsOrientedState orientationState) {
PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
- boolean isRtl = orientationHandler.getRecentsRtlSetting(getResources());
+ boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
switch (orientationHandler.getRotation()) {
case Surface.ROTATION_90:
- iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
+ iconParams.gravity = (isRtl ? START : END) | CENTER_VERTICAL;
iconParams.rightMargin = -thumbnailPadding;
iconParams.leftMargin = 0;
iconParams.topMargin = snapshotParams.topMargin / 2;
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index e5606a3..c1cf68e 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Splitscreen"</string>
- <string name="recent_task_option_pin" msgid="7929860679018978258">"Anpinnen"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixieren"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform-Modus"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Keine kürzlich verwendeten Elemente"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Einstellungen zur App-Nutzung"</string>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 8586804..f1ea6bb 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -17,14 +17,15 @@
<resources>
<dimen name="task_thumbnail_top_margin">24dp</dimen>
- <dimen name="task_thumbnail_bottom_margin_with_actions">44dp</dimen>
<dimen name="task_thumbnail_half_top_margin">12dp</dimen>
<dimen name="task_thumbnail_icon_size">48dp</dimen>
<!-- For screens without rounded corners -->
<dimen name="task_corner_radius_small">2dp</dimen>
<!-- Overrideable in overlay that provides the Overview Actions. -->
- <dimen name="overview_actions_height">110dp</dimen>
+ <dimen name="overview_actions_height">66dp</dimen>
+ <dimen name="overview_actions_bottom_margin_gesture">16dp</dimen>
+ <dimen name="overview_actions_bottom_margin_three_button">8dp</dimen>
<dimen name="overview_actions_horizontal_margin">16dp</dimen>
<dimen name="recents_page_spacing">10dp</dimen>
@@ -61,7 +62,8 @@
<dimen name="task_card_menu_shadow_height">3dp</dimen>
<dimen name="task_card_menu_horizontal_padding">0dp</dimen>
<dimen name="portrait_task_card_horz_space">136dp</dimen>
- <dimen name="portrait_task_card_horz_space_big_overview">24dp</dimen>
+ <dimen name="portrait_task_card_horz_space_big_overview">96dp</dimen>
+ <dimen name="portrait_modal_task_card_horz_space">60dp</dimen>
<dimen name="landscape_task_card_horz_space">200dp</dimen>
<dimen name="multi_window_task_card_horz_space">100dp</dimen>
<!-- Copied from framework resource:
@@ -77,6 +79,9 @@
<!-- Distance to move elements when swiping up to go home from launcher -->
<dimen name="home_pullback_distance">28dp</dimen>
+ <!-- Distance to move the tasks when swiping up while the device is locked -->
+ <dimen name="device_locked_y_offset">-80dp</dimen>
+
<!-- Overscroll Gesture -->
<dimen name="gestures_overscroll_fling_threshold">40dp</dimen>
<dimen name="gestures_overscroll_active_threshold">80dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index b474a32..c841170 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -129,16 +129,18 @@
<!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] -->
<string name="home_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up</string>
- <!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] -->
- <string name="gesture_tutorial_confirm_title" translatable="false">All set</string>
- <!-- Button text shown on a button on the confirm screen. [CHAR LIMIT=14] -->
- <string name="gesture_tutorial_action_button_label" translatable="false">Done</string>
- <!-- Button text shown on a text button on the confirm screen. [CHAR LIMIT=14] -->
- <string name="gesture_tutorial_action_text_button_label" translatable="false">Settings</string>
+ <!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] -->
+ <string name="gesture_tutorial_confirm_title" translatable="false">All set</string>
+ <!-- Button text shown on a button on the confirm screen to leave the tutorial. [CHAR LIMIT=14] -->
+ <string name="gesture_tutorial_action_button_label_done" translatable="false">Done</string>
+ <!-- Button text shown on a button to go to Settings. [CHAR LIMIT=14] -->
+ <string name="gesture_tutorial_action_button_label_settings" translatable="false">Settings</string>
<!-- ******* Overview ******* -->
<!-- Label for a button that causes the current overview app to be shared. [CHAR_LIMIT=40] -->
<string name="action_share">Share</string>
<!-- Label for a button that causes a screen shot of the current app to be taken. [CHAR_LIMIT=40] -->
<string name="action_screenshot">Screenshot</string>
+ <!-- Message shown when an action is blocked by a policy enforced by the app or the organization managing the device. [CHAR_LIMIT=NONE] -->
+ <string name="blocked_by_policy">This action isn\'t allowed by the app or your organization</string>
</resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 10e3a28..0968d8e 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -30,7 +30,6 @@
import android.os.Bundle;
import android.os.CancellationSignal;
-import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
@@ -38,6 +37,7 @@
import com.android.launcher3.proxy.StartActivityParams;
import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.UiThreadHelper;
@@ -92,6 +92,9 @@
@Override
public void onNavigationModeChanged(Mode newMode) {
getDragLayer().recreateControllers();
+ if (mActionsView != null && isOverviewActionsEnabled()) {
+ mActionsView.updateVerticalMarginForNavModeChange(newMode);
+ }
}
@Override
@@ -150,6 +153,7 @@
@Override
protected void onDeferredResumed() {
+ super.onDeferredResumed();
if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
// Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
@@ -167,13 +171,18 @@
mActionsView = findViewById(R.id.overview_actions_view);
((RecentsView) getOverviewPanel()).init(mActionsView);
- if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this)) {
+ if (isOverviewActionsEnabled()) {
// Overview is above all other launcher elements, including qsb, so move it to the top.
getOverviewPanel().bringToFront();
mActionsView.bringToFront();
+ mActionsView.updateVerticalMarginForNavModeChange(SysUINavigationMode.getMode(this));
}
}
+ private boolean isOverviewActionsEnabled() {
+ return FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this);
+ }
+
public <T extends OverviewActionsView> T getActionsView() {
return (T) mActionsView;
}
@@ -186,7 +195,7 @@
}
@Override
- protected StateHandler[] createStateHandlers() {
+ protected StateHandler<LauncherState>[] createStateHandlers() {
return new StateHandler[] {
getAllAppsController(),
getWorkspace(),
@@ -200,9 +209,8 @@
}
@Override
- protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs,
- LauncherStateManager stateManager) {
- return new QuickstepOnboardingPrefs(this, sharedPrefs, stateManager);
+ protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
+ return new QuickstepOnboardingPrefs(this, sharedPrefs);
}
@Override
@@ -296,4 +304,8 @@
public ShelfPeekAnim getShelfPeekAnim() {
return mShelfPeekAnim;
}
+
+ public void setHintUserWillBeActive() {
+ addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
index 075a483..13501a4 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
@@ -16,14 +16,13 @@
package com.android.launcher3.statehandlers;
-import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.AnimatedFloat.VALUE;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.UiThreadHelper;
import com.android.quickstep.AnimatedFloat;
@@ -33,7 +32,7 @@
/**
* State handler for animating back button alpha
*/
-public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler {
+public class BackButtonAlphaHandler implements StateHandler<LauncherState> {
private final BaseQuickstepLauncher mLauncher;
private final AnimatedFloat mBackAlpha = new AnimatedFloat(this::updateBackAlpha);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 8c778c0..8292a92 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -26,10 +26,10 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SurfaceControlCompat;
@@ -39,7 +39,7 @@
/**
* Controls blur and wallpaper zoom, for the Launcher surface only.
*/
-public class DepthController implements LauncherStateManager.StateHandler {
+public class DepthController implements StateHandler<LauncherState> {
public static final FloatProperty<DepthController> DEPTH =
new FloatProperty<DepthController>("depth") {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 47fff5e..ec3a490 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -36,9 +36,9 @@
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.graphics.OverviewScrim;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.quickstep.views.RecentsView;
@@ -49,7 +49,7 @@
* @param <T> the recents view
*/
public abstract class BaseRecentsViewStateController<T extends RecentsView>
- implements StateHandler {
+ implements StateHandler<LauncherState> {
protected final T mRecentsView;
protected final BaseQuickstepLauncher mLauncher;
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index d51d6df..f29f0ff 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -15,27 +15,34 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
+import static com.android.quickstep.SysUINavigationMode.getMode;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
+
import android.annotation.TargetApi;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.os.Build;
-import android.util.Pair;
import android.view.MotionEvent;
-import android.view.View;
import android.view.animation.Interpolator;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.util.WindowBounds;
+import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.ShelfPeekAnim;
+import com.android.quickstep.util.SplitScreenBounds;
+import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -46,95 +53,234 @@
* Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
*/
@TargetApi(Build.VERSION_CODES.P)
-public interface BaseActivityInterface<T extends BaseDraggingActivity> {
+public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_TYPE>,
+ ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>> {
- void onTransitionCancelled(boolean activityVisible);
+ private final PointF mTempPoint = new PointF();
+ public final boolean rotationSupportedByActivity;
- int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
-
- /**
- * @return The progress of the swipe where we start resisting the user, where 0 is fullscreen
- * and 1 is recents. These values should probably be greater than 1 to let the user swipe past
- * recents before we start resisting them.
- */
- default Pair<Float, Float> getSwipeUpPullbackStartAndMaxProgress() {
- return new Pair<>(1.4f, 1.8f);
+ protected BaseActivityInterface(boolean rotationSupportedByActivity) {
+ this.rotationSupportedByActivity = rotationSupportedByActivity;
}
- void onSwipeUpToRecentsComplete();
+ public void onTransitionCancelled(boolean activityVisible) {
+ ACTIVITY_TYPE activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
+ STATE_TYPE startState = activity.getStateManager().getRestState();
+ activity.getStateManager().goToState(startState, activityVisible);
+ }
- default void onSwipeUpToHomeComplete() { }
- void onAssistantVisibilityChanged(float visibility);
+ public abstract int getSwipeUpDestinationAndLength(
+ DeviceProfile dp, Context context, Rect outRect);
- @NonNull HomeAnimationFactory prepareHomeUI();
+ public void onSwipeUpToRecentsComplete() {
+ // Re apply state in case we did something funky during the transition.
+ ACTIVITY_TYPE activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
+ activity.getStateManager().reapplyState();
+ }
- AnimationFactory prepareRecentsUI(boolean activityVisible, boolean animateActivity,
- Consumer<AnimatorPlaybackController> callback);
+ public void onSwipeUpToHomeComplete() { }
- ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener);
+ public abstract void onAssistantVisibilityChanged(float visibility);
+
+ public abstract AnimationFactory prepareRecentsUI(
+ boolean activityVisible, Consumer<AnimatorPlaybackController> callback);
+
+ public abstract ActivityInitListener createActivityInitListener(
+ Predicate<Boolean> onInitListener);
/**
* Sets a callback to be run when an activity launch happens while launcher is not yet resumed.
*/
- default void setOnDeferredActivityLaunchCallback(Runnable r) {}
+ public void setOnDeferredActivityLaunchCallback(Runnable r) {}
@Nullable
- T getCreatedActivity();
+ public abstract ACTIVITY_TYPE getCreatedActivity();
@Nullable
- default DepthController getDepthController() {
+ public DepthController getDepthController() {
return null;
}
- default boolean isResumed() {
- BaseDraggingActivity activity = getCreatedActivity();
+ public final boolean isResumed() {
+ ACTIVITY_TYPE activity = getCreatedActivity();
return activity != null && activity.hasBeenResumed();
}
- default boolean isStarted() {
- BaseDraggingActivity activity = getCreatedActivity();
+ public final boolean isStarted() {
+ ACTIVITY_TYPE activity = getCreatedActivity();
return activity != null && activity.isStarted();
}
@UiThread
@Nullable
- <T extends View> T getVisibleRecentsView();
+ public abstract <T extends RecentsView> T getVisibleRecentsView();
@UiThread
- boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
+ public abstract boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
- Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target);
+ public abstract Rect getOverviewWindowBounds(
+ Rect homeBounds, RemoteAnimationTargetCompat target);
- boolean allowMinimizeSplitScreen();
+ public abstract boolean allowMinimizeSplitScreen();
- default boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+ public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
return true;
}
/**
* Updates the prediction state to the overview state.
*/
- default void updateOverviewPredictionState() {
- // By default overview predictions are not supported
+ public void updateOverviewPredictionState() {
+ // By public overview predictions are not supported
}
/**
* Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
*/
- int getContainerType();
+ public abstract int getContainerType();
- boolean isInLiveTileMode();
+ public abstract boolean isInLiveTileMode();
- void onLaunchTaskFailed();
+ public abstract void onLaunchTaskFailed();
- void onLaunchTaskSuccess();
+ public void onLaunchTaskSuccess() {
+ ACTIVITY_TYPE activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
+ activity.getStateManager().moveToRestState();
+ }
- default void closeOverlay() { }
+ public void closeOverlay() { }
- default void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
- Runnable runnable) {}
+ public void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {
+ ACTIVITY_TYPE activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
+ RecentsView recentsView = activity.getOverviewPanel();
+ if (recentsView == null) {
+ if (runnable != null) {
+ runnable.run();
+ }
+ return;
+ }
+ recentsView.switchToScreenshot(thumbnailData, runnable);
+ }
- interface AnimationFactory {
+ public void setHintUserWillBeActive() {}
+
+ /**
+ * Sets the expected window size in multi-window mode
+ */
+ public abstract void getMultiWindowSize(Context context, DeviceProfile dp, PointF out);
+
+ /**
+ * Calculates the taskView size for the provided device configuration
+ */
+ public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
+ calculateTaskSize(context, dp, getExtraSpace(context, dp), outRect);
+ }
+
+ protected abstract float getExtraSpace(Context context, DeviceProfile dp);
+
+ private void calculateTaskSize(
+ Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect) {
+ Resources res = context.getResources();
+ final boolean showLargeTaskSize = showOverviewActions(context);
+
+ final int paddingResId;
+ if (dp.isMultiWindowMode) {
+ paddingResId = R.dimen.multi_window_task_card_horz_space;
+ } else if (dp.isVerticalBarLayout()) {
+ paddingResId = R.dimen.landscape_task_card_horz_space;
+ } else if (showLargeTaskSize) {
+ paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
+ } else {
+ paddingResId = R.dimen.portrait_task_card_horz_space;
+ }
+ float paddingHorz = res.getDimension(paddingResId);
+ float paddingVert = showLargeTaskSize
+ ? 0 : res.getDimension(R.dimen.task_card_vert_space);
+
+ calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
+ res.getDimension(R.dimen.task_thumbnail_top_margin), outRect);
+ }
+
+ private void calculateTaskSizeInternal(Context context, DeviceProfile dp,
+ float extraVerticalSpace, float paddingHorz, float paddingVert, float topIconMargin,
+ Rect outRect) {
+ float taskWidth, taskHeight;
+ Rect insets = dp.getInsets();
+ if (dp.isMultiWindowMode) {
+ WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
+ taskWidth = bounds.availableSize.x;
+ taskHeight = bounds.availableSize.y;
+ } else {
+ taskWidth = dp.availableWidthPx;
+ taskHeight = dp.availableHeightPx;
+ }
+
+ // Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
+ // we override the insets ourselves.
+ int launcherVisibleWidth = dp.widthPx - insets.left - insets.right;
+ int launcherVisibleHeight = dp.heightPx - insets.top - insets.bottom;
+
+ float availableHeight = launcherVisibleHeight
+ - topIconMargin - extraVerticalSpace - paddingVert;
+ float availableWidth = launcherVisibleWidth - paddingHorz;
+
+ float scale = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
+ float outWidth = scale * taskWidth;
+ float outHeight = scale * taskHeight;
+
+ // Center in the visible space
+ float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
+ float y = insets.top + Math.max(topIconMargin,
+ (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
+ outRect.set(Math.round(x), Math.round(y),
+ Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
+ }
+
+ /**
+ * Calculates the modal taskView size for the provided device configuration
+ */
+ public void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
+ float paddingHorz = context.getResources().getDimension(dp.isMultiWindowMode
+ ? R.dimen.multi_window_task_card_horz_space
+ : dp.isVerticalBarLayout()
+ ? R.dimen.landscape_task_card_horz_space
+ : R.dimen.portrait_modal_task_card_horz_space);
+ float extraVerticalSpace = getOverviewActionsHeight(context);
+ float paddingVert = 0;
+ float topIconMargin = 0;
+ calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
+ topIconMargin, outRect);
+ }
+
+ /** Gets the space that the overview actions will take, including margins. */
+ public float getOverviewActionsHeight(Context context) {
+ Resources res = context.getResources();
+ float actionsBottomMargin = 0;
+ if (getMode(context) == Mode.THREE_BUTTONS) {
+ actionsBottomMargin = res.getDimensionPixelSize(
+ R.dimen.overview_actions_bottom_margin_three_button);
+ } else {
+ actionsBottomMargin = res.getDimensionPixelSize(
+ R.dimen.overview_actions_bottom_margin_gesture);
+ }
+ float overviewActionsHeight = actionsBottomMargin
+ + res.getDimensionPixelSize(R.dimen.overview_actions_height);
+ return overviewActionsHeight;
+ }
+
+ public interface AnimationFactory {
default void onRemoteAnimationReceived(RemoteAnimationTargets targets) { }
@@ -153,34 +299,7 @@
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
}
- interface HomeAnimationFactory {
-
- /** Return the floating view that will animate in sync with the closing window. */
- default @Nullable View getFloatingView() {
- return null;
- }
-
- @NonNull RectF getWindowTargetRect();
-
- @NonNull AnimatorPlaybackController createActivityAnimationToHome();
-
- default void playAtomicAnimation(float velocity) {
- // No-op
- }
-
- static RectF getDefaultWindowTargetRect(PagedOrientationHandler orientationHandler,
- DeviceProfile dp) {
- final int halfIconSize = dp.iconSizePx / 2;
- float primaryDimension = orientationHandler
- .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
- float secondaryDimension = orientationHandler
- .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
- final float targetX = primaryDimension / 2f;
- final float targetY = secondaryDimension - dp.hotseatBarSizePx;
- // Fallback to animate to center of screen.
- return new RectF(targetX - halfIconSize, targetY - halfIconSize,
- targetX + halfIconSize, targetY + halfIconSize);
- }
-
+ protected static boolean showOverviewActions(Context context) {
+ return ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context);
}
}
diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
deleted file mode 100644
index 1b9158b..0000000
--- a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
-
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Bundle;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.util.ActivityTracker;
-import com.android.launcher3.util.SystemUiController;
-import com.android.launcher3.util.Themes;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * A base fallback recents activity that provides support for device profile changes, activity
- * lifecycle tracking, and basic input handling from recents.
- *
- * This class is only used as a fallback in case the default launcher does not have a recents
- * implementation.
- */
-public abstract class BaseRecentsActivity extends BaseDraggingActivity {
-
- public static final ActivityTracker<BaseRecentsActivity> ACTIVITY_TRACKER =
- new ActivityTracker<>();
- private Configuration mOldConfig;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mOldConfig = new Configuration(getResources().getConfiguration());
- initDeviceProfile();
- initViews();
-
- getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
- Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
- ACTIVITY_TRACKER.handleCreate(this);
- }
-
- /**
- * Init drag layer and overview panel views.
- */
- abstract protected void initViews();
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- int diff = newConfig.diff(mOldConfig);
- if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
- onHandleConfigChanged();
- }
- mOldConfig.setTo(newConfig);
- super.onConfigurationChanged(newConfig);
- }
-
- /**
- * Logic for when device configuration changes (rotation, screen size change, multi-window,
- * etc.)
- */
- protected void onHandleConfigChanged() {
- mUserEventDispatcher = null;
- initDeviceProfile();
-
- AbstractFloatingView.closeOpenViews(this, true,
- AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
- dispatchDeviceProfileChanged();
-
- reapplyUi();
- }
-
- /**
- * Initialize/update the device profile.
- */
- private void initDeviceProfile() {
- mDeviceProfile = createDeviceProfile();
- onDeviceProfileInitiated();
- }
-
- /**
- * Generate the device profile to use in this activity.
- * @return device profile
- */
- protected DeviceProfile createDeviceProfile() {
- DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
-
- // In case we are reusing IDP, create a copy so that we don't conflict with Launcher
- // activity.
- return dp.copy(this);
- }
-
-
- @Override
- protected void onStop() {
- super.onStop();
-
- // Workaround for b/78520668, explicitly trim memory once UI is hidden
- onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
- }
-
- @Override
- public void onEnterAnimationComplete() {
- super.onEnterAnimationComplete();
- // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
- // as a part of quickstep, so that high-res thumbnails can load the next time we enter
- // overview
- RecentsModel.INSTANCE.get(this).getThumbnailCache()
- .getHighResLoadingState().setVisible(true);
- }
-
- @Override
- public void onTrimMemory(int level) {
- super.onTrimMemory(level);
- RecentsModel.INSTANCE.get(this).onTrimMemory(level);
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- ACTIVITY_TRACKER.handleNewIntent(this, intent);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- ACTIVITY_TRACKER.onActivityDestroyed(this);
- }
-
- @Override
- public void onBackPressed() {
- // TODO: Launch the task we came from
- startHome();
- }
-
- public void startHome() {
- startActivity(new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- }
-
- @Override
- public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
- super.dump(prefix, fd, writer, args);
- writer.println(prefix + "Misc:");
- dumpMisc(prefix + "\t", writer);
- }
-}
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 5adcc2e..8e14bbb 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -15,13 +15,14 @@
*/
package com.android.quickstep;
-import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Intent;
+import android.os.Build;
-import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -33,6 +34,7 @@
* Manages the state for an active system gesture, listens for events from the system and Launcher,
* and fires events when the states change.
*/
+@TargetApi(Build.VERSION_CODES.R)
public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener {
/**
@@ -125,6 +127,7 @@
private ActivityManager.RunningTaskInfo mRunningTask;
private GestureEndTarget mEndTarget;
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+ private int mLastStartedTaskId = -1;
public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
mHomeIntent = componentObserver.getHomeIntent();
@@ -143,6 +146,7 @@
mRunningTask = other.mRunningTask;
mEndTarget = other.mEndTarget;
mLastAppearedTaskTarget = other.mLastAppearedTaskTarget;
+ mLastStartedTaskId = other.mLastStartedTaskId;
}
public GestureState() {
@@ -192,7 +196,7 @@
/**
* @return the interface to the activity handing the UI updates for this gesture.
*/
- public <T extends BaseDraggingActivity> BaseActivityInterface<T> getActivityInterface() {
+ public <T extends StatefulActivity<?>> BaseActivityInterface<?, T> getActivityInterface() {
return mActivityInterface;
}
@@ -239,6 +243,21 @@
}
/**
+ * Updates the last task that we started via startActivityFromRecents() during this gesture.
+ */
+ public void updateLastStartedTaskId(int lastStartedTaskId) {
+ mLastStartedTaskId = lastStartedTaskId;
+ }
+
+ /**
+ * @return The id of the task that was most recently started during this gesture, or -1 if
+ * no task has been started yet (i.e. we haven't settled on a new task).
+ */
+ public int getLastStartedTaskId() {
+ return mLastStartedTaskId;
+ }
+
+ /**
* @return the end target for this gesture (if known).
*/
public GestureEndTarget getEndTarget() {
@@ -304,6 +323,7 @@
pw.println(" runningTask=" + mRunningTask);
pw.println(" endTarget=" + mEndTarget);
pw.println(" lastAppearedTaskTarget=" + mLastAppearedTaskTarget);
+ pw.println(" lastStartedTaskId=" + mLastStartedTaskId);
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 231ee72..0449d0c 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -140,7 +140,7 @@
if (!mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
- mActivityInterface = new LauncherActivityInterface();
+ mActivityInterface = LauncherActivityInterface.INSTANCE;
mIsHomeAndOverviewSame = true;
mOverviewIntent = mMyHomeIntent;
mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
@@ -150,7 +150,7 @@
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
- mActivityInterface = new FallbackActivityInterface();
+ mActivityInterface = FallbackActivityInterface.INSTANCE;
mIsHomeAndOverviewSame = false;
mOverviewIntent = mFallbackIntent;
mCurrentHomeIntent.setComponent(defaultHome);
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index b0ce8e6..2d9c56f 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -154,8 +154,7 @@
* Loads and creates a list of all the recent tasks.
*/
@VisibleForTesting
- ArrayList<Task> loadTasksInBackground(int numTasks,
- boolean loadKeysOnly) {
+ ArrayList<Task> loadTasksInBackground(int numTasks, boolean loadKeysOnly) {
int currentUserId = Process.myUserHandle().getIdentifier();
ArrayList<Task> allTasks = new ArrayList<>();
List<ActivityManager.RecentTaskInfo> rawTasks =
@@ -174,9 +173,7 @@
}
};
- int taskCount = rawTasks.size();
- for (int i = 0; i < taskCount; i++) {
- ActivityManager.RecentTaskInfo rawTask = rawTasks.get(i);
+ for (ActivityManager.RecentTaskInfo rawTask : rawTasks) {
Task.TaskKey taskKey = new Task.TaskKey(rawTask);
Task task;
if (!loadKeysOnly) {
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index fe95e83..58870ed 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -68,7 +68,7 @@
@Override
Integer getActionButtonStringId() {
if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
- return R.string.gesture_tutorial_action_button_label;
+ return R.string.gesture_tutorial_action_button_label_done;
}
return null;
}
@@ -76,7 +76,7 @@
@Override
Integer getActionTextButtonStringId() {
if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
- return R.string.gesture_tutorial_action_text_button_label;
+ return R.string.gesture_tutorial_action_button_label_settings;
}
return null;
}
@@ -89,7 +89,6 @@
@Override
void onActionTextButtonClicked(View button) {
mTutorialFragment.startSystemNavigationSetting();
- mTutorialFragment.closeTutorial();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 0e45376..524cbaf 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -60,7 +60,7 @@
@Override
Integer getActionButtonStringId() {
if (mTutorialType == HOME_NAVIGATION_COMPLETE) {
- return R.string.gesture_tutorial_action_button_label;
+ return R.string.gesture_tutorial_action_button_label_done;
}
return null;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 3a56b0e..44c1a5d 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep.interaction;
-import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.Insets;
import android.os.Bundle;
@@ -35,8 +34,6 @@
import com.android.launcher3.R;
import com.android.quickstep.interaction.TutorialController.TutorialType;
-import java.net.URISyntaxException;
-
abstract class TutorialFragment extends Fragment implements OnTouchListener {
private static final String LOG_TAG = "TutorialFragment";
@@ -182,14 +179,6 @@
}
void startSystemNavigationSetting() {
- try {
- startActivityForResult(
- Intent.parseUri(SYSTEM_NAVIGATION_SETTING_INTENT, /* flags= */ 0),
- /* requestCode= */ 0);
- } catch (URISyntaxException e) {
- Log.e(LOG_TAG, "The launch Intent Uri is wrong syntax: " + e);
- } catch (ActivityNotFoundException e) {
- Log.e(LOG_TAG, "The launch Activity not found: " + e);
- }
+ startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
}
}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 2d51732..8889560 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,17 +16,17 @@
package com.android.quickstep.logging;
-import static android.stats.launcher.nano.Launcher.ALLAPPS;
-import static android.stats.launcher.nano.Launcher.BACKGROUND;
-import static android.stats.launcher.nano.Launcher.HOME;
-import static android.stats.launcher.nano.Launcher.OVERVIEW;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
+import static com.android.launcher3.logger.LauncherAtom.ItemInfo.ItemCase.WIDGET;
import android.content.Context;
+import android.util.Log;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.BgDataModel;
@@ -34,11 +34,13 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.LogConfig;
+import com.android.systemui.shared.system.SysUiStatsLog;
import java.util.ArrayList;
/**
- * This method calls the StatsLog hidden method until they are made available public.
+ * This class calls StatsLog compile time generated methods.
*
* To see if the logs are properly sent to statsd, execute following command.
* $ adb root && adb shell statsd
@@ -47,35 +49,98 @@
*/
public class StatsLogCompatManager extends StatsLogManager {
- private static final int SUPPORTED_TARGET_DEPTH = 2;
private static final String TAG = "StatsLog";
- private static final boolean DEBUG = false;
+ private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
+
private static Context sContext;
+ private static final int DEFAULT_WIDGET_SPAN_XY = 1;
+ private static final int DEFAULT_WORKSPACE_GRID_XY = -1;
+ private static final int DEFAULT_PAGE_INDEX = -2;
+ private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
+
public StatsLogCompatManager(Context context) {
sContext = context;
}
+ /**
+ * Logs a {@link LauncherEvent}.
+ */
@Override
- public void verify() {
- if (!(StatsLogUtils.LAUNCHER_STATE_ALLAPPS == ALLAPPS
- && StatsLogUtils.LAUNCHER_STATE_BACKGROUND == BACKGROUND
- && StatsLogUtils.LAUNCHER_STATE_OVERVIEW == OVERVIEW
- && StatsLogUtils.LAUNCHER_STATE_HOME == HOME)) {
- throw new IllegalStateException(
- "StatsLogUtil constants doesn't match enums in launcher.proto");
+ public void log(LauncherEvent event) {
+ log(event, DEFAULT_INSTANCE_ID, LauncherAtom.ItemInfo.getDefaultInstance());
+ }
+
+ /**
+ * Logs an event and accompanying {@link InstanceId}.
+ */
+ @Override
+ public void log(LauncherEvent event, InstanceId instanceId) {
+ log(event, instanceId, LauncherAtom.ItemInfo.getDefaultInstance());
+ }
+
+ /**
+ * Logs an event and accompanying {@link ItemInfo}.
+ */
+ @Override
+ public void log(LauncherEvent event, LauncherAtom.ItemInfo itemInfo) {
+ log(event, DEFAULT_INSTANCE_ID, itemInfo);
+ }
+
+ /**
+ * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
+ */
+ @Override
+ public void log(LauncherEvent event, InstanceId instanceId, LauncherAtom.ItemInfo itemInfo) {
+ if (IS_VERBOSE) {
+ Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
+ ? String.format("\n%s\n%s", event.name(), itemInfo)
+ : String.format("%s(InstanceId:%s)\n%s", event.name(), instanceId, itemInfo));
}
+
+ if (!Utilities.ATLEAST_R) {
+ return;
+ }
+
+ SysUiStatsLog.write(
+ SysUiStatsLog.LAUNCHER_EVENT,
+ SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
+ SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME /* TODO */,
+ SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND /* TODO */,
+ null /* launcher extensions, deprecated */,
+ false /* quickstep_enabled, deprecated */,
+ event.getId() /* event_id */,
+ itemInfo.getItemCase().getNumber() /* target_id */,
+ instanceId.getId() /* instance_id TODO */,
+ 0 /* uid TODO */,
+ getPackageName(itemInfo) /* package_name */,
+ getComponentName(itemInfo) /* component_name */,
+ getGridX(itemInfo, false) /* grid_x */,
+ getGridY(itemInfo, false) /* grid_y */,
+ getPageId(itemInfo, false) /* page_id */,
+ getGridX(itemInfo, true) /* grid_x_parent */,
+ getGridY(itemInfo, true) /* grid_y_parent */,
+ getPageId(itemInfo, true) /* page_id_parent */,
+ getHierarchy(itemInfo) /* hierarchy */,
+ itemInfo.getIsWork() /* is_work_profile */,
+ itemInfo.getRank() /* rank */,
+ 0 /* fromState */,
+ 0 /* toState */,
+ null /* edittext */,
+ 0 /* cardinality */);
}
/**
* Logs the workspace layout information on the model thread.
*/
+ @Override
public void logSnapshot() {
LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
new SnapshotWorker());
}
private class SnapshotWorker extends BaseModelUpdateTask {
+
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
IntSparseArrayMap<FolderInfo> folders = dataModel.folders.clone();
@@ -84,18 +149,161 @@
for (ItemInfo info : workspaceItems) {
LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
- // call StatsLog method
+ writeSnapshot(atomInfo);
}
for (FolderInfo fInfo : folders) {
for (ItemInfo info : fInfo.contents) {
LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
- // call StatsLog method
+ writeSnapshot(atomInfo);
}
}
for (ItemInfo info : appWidgets) {
LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
- // call StatsLog method
+ writeSnapshot(atomInfo);
}
}
}
+
+ private static void writeSnapshot(LauncherAtom.ItemInfo itemInfo) {
+ if (IS_VERBOSE) {
+ Log.d(TAG, "\nwriteSnapshot:" + itemInfo);
+ }
+ if (!Utilities.ATLEAST_R) {
+ return;
+ }
+ SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
+ 0 /* event_id */,
+ itemInfo.getItemCase().getNumber() /* target_id */,
+ 0 /* instance_id */,
+ 0 /* uid */,
+ getPackageName(itemInfo) /* package_name */,
+ getComponentName(itemInfo) /* component_name */,
+ getGridX(itemInfo, false) /* grid_x */,
+ getGridY(itemInfo, false) /* grid_y */,
+ getPageId(itemInfo, false) /* page_id */,
+ getGridX(itemInfo, true) /* grid_x_parent */,
+ getGridY(itemInfo, true) /* grid_y_parent */,
+ getPageId(itemInfo, true) /* page_id_parent */,
+ getHierarchy(itemInfo) /* hierarchy */,
+ itemInfo.getIsWork() /* is_work_profile */,
+ 0 /* origin TODO */,
+ 0 /* cardinality */,
+ getSpanX(itemInfo),
+ getSpanY(itemInfo));
+ }
+
+ private static int getSpanX(LauncherAtom.ItemInfo atomInfo) {
+ if (atomInfo.getItemCase() != WIDGET) {
+ return DEFAULT_WIDGET_SPAN_XY;
+ }
+ return atomInfo.getWidget().getSpanX();
+ }
+
+ private static int getSpanY(LauncherAtom.ItemInfo atomInfo) {
+ if (atomInfo.getItemCase() != WIDGET) {
+ return DEFAULT_WIDGET_SPAN_XY;
+ }
+ return atomInfo.getWidget().getSpanY();
+ }
+
+ private static String getPackageName(LauncherAtom.ItemInfo atomInfo) {
+ switch (atomInfo.getItemCase()) {
+ case APPLICATION:
+ return atomInfo.getApplication().getPackageName();
+ case SHORTCUT:
+ return atomInfo.getShortcut().getShortcutName();
+ case WIDGET:
+ return atomInfo.getWidget().getPackageName();
+ case TASK:
+ return atomInfo.getTask().getPackageName();
+ default:
+ return null;
+ }
+ }
+
+ private static String getComponentName(LauncherAtom.ItemInfo atomInfo) {
+ switch (atomInfo.getItemCase()) {
+ case APPLICATION:
+ return atomInfo.getApplication().getComponentName();
+ case SHORTCUT:
+ return atomInfo.getShortcut().getShortcutName();
+ case WIDGET:
+ return atomInfo.getWidget().getComponentName();
+ case TASK:
+ return atomInfo.getTask().getComponentName();
+ default:
+ return null;
+ }
+ }
+
+ private static int getGridX(LauncherAtom.ItemInfo info, boolean parent) {
+ switch (info.getContainerInfo().getContainerCase()) {
+ case WORKSPACE:
+ if (parent) {
+ return DEFAULT_WORKSPACE_GRID_XY;
+ } else {
+ return info.getContainerInfo().getWorkspace().getGridX();
+ }
+ case FOLDER:
+ if (parent) {
+ switch (info.getContainerInfo().getFolder().getParentContainerCase()) {
+ case WORKSPACE:
+ return info.getContainerInfo().getFolder().getWorkspace().getGridX();
+ default:
+ return DEFAULT_WORKSPACE_GRID_XY;
+ }
+ } else {
+ return info.getContainerInfo().getFolder().getGridX();
+ }
+ default:
+ return DEFAULT_WORKSPACE_GRID_XY;
+ }
+ }
+
+ private static int getGridY(LauncherAtom.ItemInfo info, boolean parent) {
+ switch (info.getContainerInfo().getContainerCase()) {
+ case WORKSPACE:
+ if (parent) {
+ return DEFAULT_WORKSPACE_GRID_XY;
+ } else {
+ return info.getContainerInfo().getWorkspace().getGridY();
+ }
+ case FOLDER:
+ if (parent) {
+ switch (info.getContainerInfo().getFolder().getParentContainerCase()) {
+ case WORKSPACE:
+ return info.getContainerInfo().getFolder().getWorkspace().getGridY();
+ default:
+ return DEFAULT_WORKSPACE_GRID_XY;
+ }
+ } else {
+ return info.getContainerInfo().getFolder().getGridY();
+ }
+ default:
+ return DEFAULT_WORKSPACE_GRID_XY;
+ }
+ }
+
+ private static int getPageId(LauncherAtom.ItemInfo info, boolean parent) {
+ switch (info.getContainerInfo().getContainerCase()) {
+ case HOTSEAT:
+ return info.getContainerInfo().getHotseat().getIndex();
+ case WORKSPACE:
+ return info.getContainerInfo().getWorkspace().getPageIndex();
+ default:
+ return DEFAULT_PAGE_INDEX;
+ }
+ }
+
+ /**
+ *
+ */
+ private static int getHierarchy(LauncherAtom.ItemInfo info) {
+ // TODO
+ if (info.getContainerInfo().getContainerCase() == FOLDER) {
+ return info.getContainerInfo().getFolder().getParentContainerCase().getNumber() + 100;
+ } else {
+ return info.getContainerInfo().getContainerCase().getNumber();
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index fa53be2..c1b276a 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
import android.content.Context;
import android.graphics.Rect;
@@ -26,6 +25,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode;
public class LayoutUtils {
@@ -45,7 +45,7 @@
// Track the bottom of the window.
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
Rect taskSize = new Rect();
- LAUNCHER_ACTIVITY_SIZE_STRATEGY.calculateTaskSize(context, dp, taskSize);
+ LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize);
return (dp.heightPx - taskSize.height()) / 2;
}
int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index aa6d56a..2d8bba2 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -25,8 +25,8 @@
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.LauncherStateManager.StateListener;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.quickstep.SysUINavigationMode;
@@ -35,23 +35,23 @@
*/
public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLauncher> {
- public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs,
- LauncherStateManager stateManager) {
- super(launcher, sharedPrefs, stateManager);
+ public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs) {
+ super(launcher, sharedPrefs);
+ StateManager<LauncherState> stateManager = launcher.getStateManager();
if (!getBoolean(HOME_BOUNCE_SEEN)) {
- mStateManager.addStateListener(new StateListener() {
+ stateManager.addStateListener(new StateListener<LauncherState>() {
@Override
public void onStateTransitionComplete(LauncherState finalState) {
boolean swipeUpEnabled = SysUINavigationMode.INSTANCE
.get(mLauncher).getMode().hasGestures;
- LauncherState prevState = mStateManager.getLastState();
+ LauncherState prevState = stateManager.getLastState();
if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
&& finalState == ALL_APPS && prevState == NORMAL) ||
hasReachedMaxCount(HOME_BOUNCE_COUNT))) {
mSharedPrefs.edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
- mStateManager.removeStateListener(this);
+ stateManager.removeStateListener(this);
}
}
});
@@ -65,27 +65,27 @@
mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, shelfBounceSeen).apply();
}
if (!shelfBounceSeen) {
- mStateManager.addStateListener(new StateListener() {
+ stateManager.addStateListener(new StateListener<LauncherState>() {
@Override
public void onStateTransitionComplete(LauncherState finalState) {
- LauncherState prevState = mStateManager.getLastState();
+ LauncherState prevState = stateManager.getLastState();
if ((finalState == ALL_APPS && prevState == OVERVIEW) ||
hasReachedMaxCount(SHELF_BOUNCE_COUNT)) {
mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
- mStateManager.removeStateListener(this);
+ stateManager.removeStateListener(this);
}
}
});
}
if (!hasReachedMaxCount(ALL_APPS_COUNT)) {
- mStateManager.addStateListener(new StateListener() {
+ stateManager.addStateListener(new StateListener<LauncherState>() {
@Override
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState == ALL_APPS) {
if (incrementEventCount(ALL_APPS_COUNT)) {
- mStateManager.removeStateListener(this);
+ stateManager.removeStateListener(this);
mLauncher.getScrimView().updateDragHandleVisibility();
}
}
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index f6c4e66..e03f4b8 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -23,6 +23,7 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static com.android.launcher3.logging.LoggerUtils.extractObjectNameAndAddress;
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -36,7 +37,6 @@
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
@@ -51,6 +51,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.WindowBounds;
+import com.android.quickstep.BaseActivityInterface;
import java.lang.annotation.Retention;
import java.util.function.IntConsumer;
@@ -84,7 +86,7 @@
private @SurfaceRotation int mTouchRotation = ROTATION_0;
private @SurfaceRotation int mDisplayRotation = ROTATION_0;
- private @SurfaceRotation int mLauncherRotation = Surface.ROTATION_0;
+ private @SurfaceRotation int mLauncherRotation = ROTATION_0;
// Launcher activity supports multiple orientation, but fallback activity does not
private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY = 1 << 0;
@@ -102,6 +104,8 @@
private static final int FLAG_ROTATION_WATCHER_SUPPORTED = 1 << 6;
// Whether to enable rotation watcher when multi-rotation is supported
private static final int FLAG_ROTATION_WATCHER_ENABLED = 1 << 7;
+ // Enable home rotation for UI tests, ignoring home rotation value from prefs
+ private static final int FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING = 1 << 8;
private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
@@ -118,10 +122,9 @@
private final ContentResolver mContentResolver;
private final SharedPreferences mSharedPrefs;
private final OrientationEventListener mOrientationListener;
- private final WindowSizeStrategy mSizeStrategy;
+ private final BaseActivityInterface mSizeStrategy;
private final Matrix mTmpMatrix = new Matrix();
- private final Matrix mTmpInverseMatrix = new Matrix();
private int mFlags;
private int mPreviousRotation = ROTATION_0;
@@ -131,7 +134,7 @@
* is enabled
* @see #setRotationWatcherEnabled(boolean)
*/
- public RecentsOrientedState(Context context, WindowSizeStrategy sizeStrategy,
+ public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy,
IntConsumer rotationChangeListener) {
mContext = context;
mContentResolver = context.getContentResolver();
@@ -163,6 +166,10 @@
if (mOrientationListener.canDetectOrientation()) {
mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
}
+
+ // initialize external flags
+ updateAutoRotateSetting();
+ updateHomeRotationSetting();
}
/**
@@ -181,13 +188,15 @@
* false otherwise
*/
public boolean update(
- @SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation,
- @SurfaceRotation int launcherRotation) {
+ @SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation) {
if (!isMultipleOrientationSupportedByDevice()) {
return false;
}
- if (mDisplayRotation == displayRotation && mTouchRotation == touchRotation
- && launcherRotation == mLauncherRotation) {
+
+ int launcherRotation = inferLauncherRotation(displayRotation);
+ if (mDisplayRotation == displayRotation
+ && mTouchRotation == touchRotation
+ && mLauncherRotation == launcherRotation) {
return false;
}
@@ -195,11 +204,10 @@
mDisplayRotation = displayRotation;
mTouchRotation = touchRotation;
- if (canLauncherRotate() || mLauncherRotation == mTouchRotation) {
- // TODO(b/153476489) Need to determine when launcher is rotated
+ if (mLauncherRotation == mTouchRotation) {
mOrientationHandler = PagedOrientationHandler.HOME_ROTATED;
if (DEBUG) {
- Log.d(TAG, "Set Orientation Handler: " + mOrientationHandler);
+ Log.d(TAG, "current RecentsOrientedState: " + this);
}
return true;
}
@@ -212,11 +220,20 @@
mOrientationHandler = PagedOrientationHandler.PORTRAIT;
}
if (DEBUG) {
- Log.d(TAG, "Set Orientation Handler: " + mOrientationHandler);
+ Log.d(TAG, "current RecentsOrientedState: " + this);
}
return true;
}
+ @SurfaceRotation
+ private int inferLauncherRotation(@SurfaceRotation int displayRotation) {
+ if (!isMultipleOrientationSupportedByDevice() || isHomeRotationAllowed()) {
+ return displayRotation;
+ } else {
+ return ROTATION_0;
+ }
+ }
+
private void setFlag(int mask, boolean enabled) {
boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation
&& mFlags == VALUE_ROTATION_WATCHER_ENABLED;
@@ -241,7 +258,9 @@
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
- updateHomeRotationSetting();
+ if (ALLOW_ROTATION_PREFERENCE_KEY.equals(s)) {
+ updateHomeRotationSetting();
+ }
}
private void updateAutoRotateSetting() {
@@ -255,23 +274,24 @@
}
/**
- * Initializes aany system values and registers corresponding change listeners. It must be
- * paired with {@link #destroy()} call
+ * Initializes any system values and registers corresponding change listeners. It must be
+ * paired with {@link #destroyListeners()} call
*/
- public void init() {
+ public void initListeners() {
if (isMultipleOrientationSupportedByDevice()) {
mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
mContentResolver.registerContentObserver(
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
false, mSystemAutoRotateObserver);
}
- initWithoutListeners();
+ updateAutoRotateSetting();
+ updateHomeRotationSetting();
}
/**
* Unregisters any previously registered listeners.
*/
- public void destroy() {
+ public void destroyListeners() {
if (isMultipleOrientationSupportedByDevice()) {
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
@@ -279,13 +299,8 @@
setRotationWatcherEnabled(false);
}
- /**
- * Initializes the OrientationState without attaching any listeners. This can be used when
- * the object is short lived.
- */
- public void initWithoutListeners() {
- updateAutoRotateSetting();
- updateHomeRotationSetting();
+ public void forceAllowRotationForTesting(boolean forceAllow) {
+ setFlag(FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING, forceAllow);
}
@SurfaceRotation
@@ -310,7 +325,8 @@
public boolean isHomeRotationAllowed() {
return (mFlags & (FLAG_HOME_ROTATION_ALLOWED_IN_PREFS | FLAG_MULTIWINDOW_ROTATION_ALLOWED))
- != 0;
+ != 0 ||
+ (mFlags & FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING) != 0;
}
public boolean canLauncherRotate() {
@@ -347,7 +363,8 @@
float fullHeight = dp.heightPx - insets.top - insets.bottom;
if (dp.isMultiWindowMode) {
- mSizeStrategy.getMultiWindowSize(mContext, dp, outPivot);
+ WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(mContext);
+ outPivot.set(bounds.availableSize.x, bounds.availableSize.y);
} else {
outPivot.set(fullWidth, fullHeight);
}
@@ -403,23 +420,6 @@
*/
}
- public void mapRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight) {
- mapRectFromRotation(mDisplayRotation, src, screenWidth, screenHeight);
- }
-
- public void mapRectFromRotation(int rotation, RectF src, int screenWidth, int screenHeight) {
- mTmpMatrix.reset();
- postDisplayRotation(rotation, screenWidth, screenHeight, mTmpMatrix);
- mTmpMatrix.mapRect(src);
- }
-
- public void mapInverseRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight) {
- mTmpMatrix.reset();
- postDisplayRotation(mDisplayRotation, screenWidth, screenHeight, mTmpMatrix);
- mTmpMatrix.invert(mTmpInverseMatrix);
- mTmpInverseMatrix.mapRect(src);
- }
-
@SurfaceRotation
public static int getRotationForUserDegreesRotated(float degrees, int currentRotation) {
if (degrees == ORIENTATION_UNKNOWN) {
@@ -440,9 +440,13 @@
if (degrees < (90 - threshold)) {
return ROTATION_0;
}
- if (degrees > (90 + threshold)) {
+ if (degrees > (90 + threshold) && degrees < 180) {
return ROTATION_180;
}
+ // flip from seascape to landscape
+ if (degrees > (180 + threshold) && degrees < 360) {
+ return ROTATION_90;
+ }
break;
case ROTATION_180:
if (degrees < (180 - threshold)) {
@@ -453,12 +457,16 @@
}
break;
case ROTATION_90:
- if (degrees < (270 - threshold)) {
+ if (degrees < (270 - threshold) && degrees > 90) {
return ROTATION_180;
}
- if (degrees > (270 + threshold)) {
+ if (degrees > (270 + threshold) && degrees < 360) {
return ROTATION_0;
}
+ // flip from landscape to seascape
+ if (degrees > threshold && degrees < 180) {
+ return ROTATION_270;
+ }
break;
}
@@ -506,13 +514,15 @@
public String toString() {
boolean systemRotationOn = (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0;
return "["
- + "mDisplayRotation=" + mDisplayRotation
+ + "this=" + extractObjectNameAndAddress(super.toString())
+ + " mOrientationHandler=" +
+ extractObjectNameAndAddress(mOrientationHandler.toString())
+ + " mDisplayRotation=" + mDisplayRotation
+ " mTouchRotation=" + mTouchRotation
+ " mLauncherRotation=" + mLauncherRotation
+ " mHomeRotation=" + isHomeRotationAllowed()
+ " mSystemRotation=" + systemRotationOn
+ " mFlags=" + mFlags
- + " mOrientationHandler=" + mOrientationHandler
+ "]";
}
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
new file mode 100644
index 0000000..a770e8e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.WindowInsets.Type;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.WindowBounds;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to hold the information abound a window bounds for split screen
+ */
+@TargetApi(Build.VERSION_CODES.R)
+public class SplitScreenBounds {
+
+ public static final SplitScreenBounds INSTANCE = new SplitScreenBounds();
+ private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
+
+ @Nullable
+ private WindowBounds mBounds;
+
+ private SplitScreenBounds() { }
+
+ @UiThread
+ public void setSecondaryWindowBounds(@NonNull WindowBounds bounds) {
+ if (!bounds.equals(mBounds)) {
+ mBounds = bounds;
+ for (OnChangeListener listener : mListeners) {
+ listener.onSecondaryWindowBoundsChanged();
+ }
+ }
+ }
+
+ public @NonNull WindowBounds getSecondaryWindowBounds(Context context) {
+ if (mBounds == null) {
+ mBounds = createDefaultWindowBounds(context);
+ }
+ return mBounds;
+ }
+
+ /**
+ * Creates window bounds as 50% of device size
+ */
+ private static WindowBounds createDefaultWindowBounds(Context context) {
+ WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
+ Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
+
+ WindowBounds bounds = new WindowBounds(wm.getBounds(),
+ new Rect(insets.left, insets.top, insets.right, insets.bottom));
+ int rotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation;
+ int halfDividerSize = context.getResources()
+ .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
+
+ if (rotation == ROTATION_0 || rotation == ROTATION_180) {
+ bounds.bounds.top = bounds.insets.top + bounds.availableSize.y / 2 + halfDividerSize;
+ bounds.insets.top = 0;
+ } else {
+ bounds.bounds.left = bounds.insets.left + bounds.availableSize.x / 2 + halfDividerSize;
+ bounds.insets.left = 0;
+ }
+ return new WindowBounds(bounds.bounds, bounds.insets);
+ }
+
+ public void addOnChangeListener(OnChangeListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void removeOnChangeListener(OnChangeListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Interface to receive window bounds changes
+ */
+ public interface OnChangeListener {
+
+ /**
+ * Called when window bounds for secondary window changes
+ */
+ void onSecondaryWindowBoundsChanged();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java b/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
deleted file mode 100644
index 8bb0d70..0000000
--- a/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.PointF;
-import android.graphics.Rect;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-
-/**
- * Utility class to wrap different layout behavior for Launcher and RecentsView
- * TODO: Merge is with {@link com.android.quickstep.BaseActivityInterface} once we remove the
- * state dependent members from {@link com.android.quickstep.LauncherActivityInterface}
- */
-public abstract class WindowSizeStrategy {
-
- private final PointF mTempPoint = new PointF();
- public final boolean rotationSupportedByActivity;
-
- private WindowSizeStrategy(boolean rotationSupportedByActivity) {
- this.rotationSupportedByActivity = rotationSupportedByActivity;
- }
-
- /**
- * Sets the expected window size in multi-window mode
- */
- public abstract void getMultiWindowSize(Context context, DeviceProfile dp, PointF out);
-
- /**
- * Calculates the taskView size for the provided device configuration
- */
- public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
- calculateTaskSize(context, dp, getExtraSpace(context, dp), outRect);
- }
-
- abstract float getExtraSpace(Context context, DeviceProfile dp);
-
- private void calculateTaskSize(
- Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect) {
- float taskWidth, taskHeight, paddingHorz;
- Resources res = context.getResources();
- Rect insets = dp.getInsets();
- final boolean showLargeTaskSize = showOverviewActions(context);
-
- if (dp.isMultiWindowMode) {
- getMultiWindowSize(context, dp, mTempPoint);
- taskWidth = mTempPoint.x;
- taskHeight = mTempPoint.y;
- paddingHorz = res.getDimension(R.dimen.multi_window_task_card_horz_space);
- } else {
- taskWidth = dp.availableWidthPx;
- taskHeight = dp.availableHeightPx;
-
- final int paddingResId;
- if (dp.isVerticalBarLayout()) {
- paddingResId = R.dimen.landscape_task_card_horz_space;
- } else if (showLargeTaskSize) {
- paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
- } else {
- paddingResId = R.dimen.portrait_task_card_horz_space;
- }
- paddingHorz = res.getDimension(paddingResId);
- }
-
- float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
- float paddingVert = showLargeTaskSize
- ? 0 : res.getDimension(R.dimen.task_card_vert_space);
-
- // Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
- // we override the insets ourselves.
- int launcherVisibleWidth = dp.widthPx - insets.left - insets.right;
- int launcherVisibleHeight = dp.heightPx - insets.top - insets.bottom;
-
- float availableHeight = launcherVisibleHeight
- - topIconMargin - extraVerticalSpace - paddingVert;
- float availableWidth = launcherVisibleWidth - paddingHorz;
-
- float scale = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
- float outWidth = scale * taskWidth;
- float outHeight = scale * taskHeight;
-
- // Center in the visible space
- float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
- float y = insets.top + Math.max(topIconMargin,
- (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
- outRect.set(Math.round(x), Math.round(y),
- Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
- }
-
-
- public static final WindowSizeStrategy LAUNCHER_ACTIVITY_SIZE_STRATEGY =
- new WindowSizeStrategy(true) {
-
- @Override
- public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
- DeviceProfile fullDp = dp.getFullScreenProfile();
- // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
- // account for system insets
- out.set(fullDp.availableWidthPx, fullDp.availableHeightPx);
- float halfDividerSize = context.getResources()
- .getDimension(R.dimen.multi_window_task_divider_size) / 2;
-
- if (fullDp.isLandscape) {
- out.x = out.x / 2 - halfDividerSize;
- } else {
- out.y = out.y / 2 - halfDividerSize;
- }
- }
-
- @Override
- float getExtraSpace(Context context, DeviceProfile dp) {
- if (dp.isVerticalBarLayout()) {
- return 0;
- } else {
- Resources res = context.getResources();
- if (showOverviewActions(context)) {
- //TODO: this needs to account for the swipe gesture height and accessibility
- // UI when shown.
- return res.getDimensionPixelSize(R.dimen.overview_actions_height);
- } else {
- return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
- + res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_extra_vertical_size)
- + res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_bottom_padding);
- }
- }
- }
- };
-
- public static final WindowSizeStrategy FALLBACK_RECENTS_SIZE_STRATEGY =
- new WindowSizeStrategy(false) {
- @Override
- public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
- out.set(dp.widthPx, dp.heightPx);
- }
-
- @Override
- float getExtraSpace(Context context, DeviceProfile dp) {
- return showOverviewActions(context)
- ? context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height)
- : 0;
- }
- };
-
- static boolean showOverviewActions(Context context) {
- return ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context);
- }
-}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 75fcfe2..bf093fd 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -42,6 +42,7 @@
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.views.RecentsView;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,6 +54,18 @@
public void setUp() throws Exception {
super.setUp();
TaplTestsLauncher3.initialize(this);
+ executeOnLauncher(launcher -> {
+ RecentsView recentsView = launcher.getOverviewPanel();
+ recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(true);
+ });
+ }
+
+ @After
+ public void tearDown() {
+ executeOnLauncher(launcher -> {
+ RecentsView recentsView = launcher.getOverviewPanel();
+ recentsView.getPagedViewOrientedState().forceAllowRotationForTesting(false);
+ });
}
private void startTestApps() throws Exception {
diff --git a/res/layout/work_profile_edu.xml b/res/layout/work_profile_edu.xml
index 5506b94..c3c7010 100644
--- a/res/layout/work_profile_edu.xml
+++ b/res/layout/work_profile_edu.xml
@@ -23,13 +23,13 @@
android:layout_width="match_parent"
android:layout_height="32dp"
android:background="@drawable/bottom_sheet_top_border"
- android:backgroundTint="?android:attr/colorAccent" />
+ android:backgroundTint="?attr/eduHalfSheetBGColor" />
<LinearLayout
android:id="@+id/view_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/colorAccent"
+ android:background="?attr/eduHalfSheetBGColor"
android:orientation="vertical"
android:paddingLeft="@dimen/bottom_sheet_edu_padding"
android:paddingRight="@dimen/bottom_sheet_edu_padding">
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 3276d44..af40f5c 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -131,7 +131,7 @@
<string name="accessibility_close" msgid="2277148124685870734">"بستن"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"اعلان رد شد"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"شخصی"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"محل کار"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"کاری"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"نمایه کاری"</string>
<string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"دادههای شخصی از برنامههای کاری جدا است و از آن پنهان است"</string>
<string name="work_profile_edu_work_apps" msgid="237051938268703058">"برنامههای کاری و دادهها برای سرپرست فناوری اطلاعات نمایان هستند"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 1d69f86..0aa2f1b 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="work_folder_name" msgid="3753320833950115786">"કાર્યાલય"</string>
+ <string name="work_folder_name" msgid="3753320833950115786">"ઑફિસ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ઍપ્લિકેશન ઇન્સ્ટોલ થઈ નથી."</string>
<string name="activity_not_available" msgid="7456344436509528827">"ઍપ્લિકેશન ઉપલબ્ધ નથી"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"સુરક્ષિત મોડમાં ડાઉનલોડ કરેલ ઍપ્લિકેશન અક્ષમ કરી"</string>
@@ -131,7 +131,7 @@
<string name="accessibility_close" msgid="2277148124685870734">"બંધ કરો"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"સૂચના છોડી દીધી"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"મનગમતી ઍપ"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"કાર્યાલયની ઍપ"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"ઑફિસની ઍપ"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"ઑફિસની પ્રોફાઇલ"</string>
<string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"વ્યક્તિગત ડેટા ઑફિસ માટેની ઍપથી અલગ અને છુપાવીને રાખેલો છે"</string>
<string name="work_profile_edu_work_apps" msgid="237051938268703058">"ઑફિસ માટેની ઍપ અને ડેટા તમારા IT વ્યવસ્થાપકને દેખાય છે"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 3fa16ab..4667ede 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -53,11 +53,11 @@
<string name="app_info_drop_target_label" msgid="692894985365717661">"अनुप्रयोगको जानकारी"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"स्थापना गर्नुहोस्"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
- <string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा एपलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"गृह सेटिङहरू र सर्टकटहरू पढ्नुहोस्"</string>
- <string name="permdesc_read_settings" msgid="5833423719057558387">"गृहमा एउटा अनुप्रयोगलाई सेटिङहरू र सर्टकटहरू पढ्न अनुमति दिनुहोस्।"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"गृहमा एउटा एपलाई सेटिङहरू र सर्टकटहरू पढ्न अनुमति दिनुहोस्।"</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"गृह सेटिङहरू र सर्टकटहरू लेख्नुहोस्"</string>
- <string name="permdesc_write_settings" msgid="5440712911516509985">"गृहमा एउटा अनुप्रयोगलाई सेटिङ र सर्टकट बदल्न अनुमति दिनुहोस्।"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"गृहमा एउटा एपलाई सेटिङ र सर्टकट बदल्न अनुमति दिनुहोस्।"</string>
<string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले फोन कलहरू गर्न अनुमति छैन"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"समस्या लोडिङ गर्ने विजेट"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index e548b87..87a1948 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"ଲଞ୍ଚର୍3"</string>
- <string name="work_folder_name" msgid="3753320833950115786">"କାମ"</string>
+ <string name="work_folder_name" msgid="3753320833950115786">"ୱାର୍କ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"ଆପ୍ ଇନଷ୍ଟଲ୍ ହୋଇନାହିଁ"</string>
<string name="activity_not_available" msgid="7456344436509528827">"ଆପ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"ନିରାପଦ ମୋଡରେ ଡାଉନଲୋଡ୍ ହେଇଥିବା ଆପ୍ ଅକ୍ଷମ କରାଗଲା"</string>
@@ -131,7 +131,7 @@
<string name="accessibility_close" msgid="2277148124685870734">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"ବିଜ୍ଞପ୍ତି ଖାରଜ କରାଗଲା"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"ବ୍ୟକ୍ତିଗତ"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"କାମ"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"ୱାର୍କ"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string>
<string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ବ୍ୟକ୍ତିଗତ ଡାଟା କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପଗୁଡ଼ିକ ଠାରୁ ପୃଥକ୍ ଓ ଲୁକ୍କାୟିତ ଅଟେ"</string>
<string name="work_profile_edu_work_apps" msgid="237051938268703058">"କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପଗୁଡ଼ିକ ଓ ଡାଟା ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦେଖାଯାଏ"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index d4cb6d7..2875006 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -35,6 +35,7 @@
<attr name="widgetsTheme" format="reference" />
<attr name="loadingIconColor" format="color" />
<attr name="iconOnlyShortcutColor" format="color"/>
+ <attr name="eduHalfSheetBGColor" format="color"/>
<attr name="folderDotColor" format="color" />
<attr name="folderFillColor" format="color" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5f4bd8e..2efa66f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -103,9 +103,9 @@
<!-- Label for install drop target. [CHAR_LIMIT=20] -->
<string name="install_drop_target_label">Install</string>
<!-- Label for install dismiss prediction. -->
- <string translatable="false" name="dismiss_prediction_label">Don\'t suggest app</string>
+ <string name="dismiss_prediction_label">Don\'t suggest app</string>
<!-- Label for pinning predicted app. -->
- <string name="pin_prediction" translatable="false">Pin Prediction</string>
+ <string name="pin_prediction">Pin Prediction</string>
<!-- Permissions: -->
@@ -342,7 +342,7 @@
<!--- heading shown when user opens work apps tab while work apps are paused -->
<string name="work_apps_paused_title">Work profile is paused</string>
<!--- body shown when user opens work apps tab while work apps are paused -->
- <string name="work_apps_paused_body">Work apps can\’t send you notifications, use your battery, or access your location</string>
+ <string name="work_apps_paused_body">Work apps can\'t send you notifications, use your battery, or access your location</string>
<!-- content description for paused work apps list -->
<string name="work_apps_paused_content_description">Work profile is paused. Work apps can\’t send you notifications, use your battery, or access your location</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a2c0f23..26b7205 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -53,6 +53,7 @@
<item name="loadingIconColor">#CCFFFFFF</item>
<item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
<item name="workProfileOverlayTextColor">#FF212121</item>
+ <item name="eduHalfSheetBGColor">?android:attr/colorAccent</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
@@ -90,7 +91,7 @@
<item name="android:textColorHint">#A0FFFFFF</item>
<item name="android:colorControlHighlight">#A0FFFFFF</item>
<item name="android:colorPrimary">#FF212121</item>
- <item name="allAppsScrimColor">#FF212121</item>
+ <item name="allAppsScrimColor">#FF000000</item>
<item name="allAppsInterimScrimAlpha">102</item>
<item name="allAppsNavBarScrimColor">#80000000</item>
<item name="popupColorPrimary">#3C4043</item> <!-- Gray 800 -->
@@ -106,6 +107,7 @@
<item name="loadingIconColor">#99FFFFFF</item>
<item name="iconOnlyShortcutColor">#B3FFFFFF</item>
<item name="workProfileOverlayTextColor">@android:color/white</item>
+ <item name="eduHalfSheetBGColor">#DD000000</item>
</style>
<style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
@@ -229,9 +231,7 @@
<style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
<style name="PrimaryMediumText" parent="@android:style/TextAppearance.DeviceDefault.Medium"/>
- <style name="PrimaryHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
- <item name="android:textStyle">bold</item>
- </style>
+ <style name="PrimaryHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
<style name="TextTitle" parent="@android:style/TextAppearance.DeviceDefault" />
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 7de44a3..7d80d81 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -108,9 +108,14 @@
public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4;
/**
+ * State flag indicating if the user will be active shortly.
+ */
+ public static final int ACTIVITY_STATE_USER_WILL_BE_ACTIVE = 1 << 5;
+
+ /**
* State flag indicating that a state transition is in progress
*/
- public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 5;
+ public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 6;
@Retention(SOURCE)
@IntDef(
@@ -180,6 +185,7 @@
@Override
protected void onResume() {
addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
+ removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
super.onResume();
}
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 09fe64a..239d8a3 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -16,7 +16,7 @@
package com.android.launcher3;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.APP_LAUNCH_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.util.DefaultDisplay.CHANGE_ROTATION;
import android.app.ActivityOptions;
@@ -183,8 +183,7 @@
sourceContainer);
}
getUserEventDispatcher().logAppLaunch(v, intent, user);
-
- getStatsLogManager().log(APP_LAUNCH_TAP, item == null ? null
+ getStatsLogManager().log(LAUNCHER_APP_LAUNCH_TAP, item == null ? null
: item.buildProto(null));
return true;
} catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index d7d4a27..60b6da6 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -60,6 +60,7 @@
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.model.data.PromiseAppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.IconLabelDotView;
@@ -722,17 +723,34 @@
}
@Override
- public void getVisualDragBounds(Rect bounds) {
+ public void getWorkspaceVisualDragBounds(Rect bounds) {
DeviceProfile grid = mActivity.getDeviceProfile();
BubbleTextView.getIconBounds(this, bounds, grid.iconSizePx);
}
+ private int getIconSizeForDisplay(int display) {
+ DeviceProfile grid = mActivity.getDeviceProfile();
+ switch (display) {
+ case DISPLAY_ALL_APPS:
+ return grid.allAppsIconSizePx;
+ case DISPLAY_WORKSPACE:
+ case DISPLAY_FOLDER:
+ default:
+ return grid.iconSizePx;
+ }
+ }
+
+ public void getSourceVisualDragBounds(Rect bounds) {
+ BubbleTextView.getIconBounds(this, bounds, getIconSizeForDisplay(mDisplay));
+ }
+
@Override
- public void prepareDrawDragView() {
+ public SafeCloseable prepareDrawDragView() {
if (getIcon() instanceof FastBitmapDrawable) {
FastBitmapDrawable icon = (FastBitmapDrawable) getIcon();
icon.setScale(1f);
}
setForceHideDot(true);
+ return () -> { };
}
}
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index b4c5f96..d75d712 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_CANCEL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.UNDO;
@@ -27,6 +29,7 @@
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -38,6 +41,8 @@
public class DeleteDropTarget extends ButtonDropTarget {
+ private final StatsLogManager mStatsLogManager;
+
private int mControlType = ControlType.DEFAULT_CONTROLTYPE;
public DeleteDropTarget(Context context, AttributeSet attrs) {
@@ -46,6 +51,7 @@
public DeleteDropTarget(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ this.mStatsLogManager = StatsLogManager.newInstance(context);
}
@Override
@@ -120,6 +126,11 @@
d.dragInfo.container = NO_ID;
}
super.onDrop(d, options);
+ mStatsLogManager.log(
+ mControlType == ControlType.REMOVE_TARGET
+ ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
+ : LAUNCHER_ITEM_DROPPED_ON_CANCEL,
+ d.logInstanceId);
}
@Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 873b066..59476dd 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -67,7 +67,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.Handler;
import android.os.Parcelable;
import android.os.Process;
import android.os.StrictMode;
@@ -87,13 +86,12 @@
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
+import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.LauncherStateManager.AtomicAnimationFactory;
-import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore;
@@ -131,6 +129,10 @@
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.qsb.QsbContainerView;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
@@ -194,7 +196,7 @@
/**
* Default launcher application.
*/
-public class Launcher extends BaseDraggingActivity implements LauncherExterns,
+public class Launcher extends StatefulActivity<LauncherState> implements LauncherExterns,
Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
public static final String TAG = "Launcher";
@@ -241,7 +243,7 @@
public static final String ON_RESUME_EVT = "Launcher.onResume";
public static final String ON_NEW_INTENT_EVT = "Launcher.onNewIntent";
- private LauncherStateManager mStateManager;
+ private StateManager<LauncherState> mStateManager;
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
@@ -325,10 +327,6 @@
private RotationHelper mRotationHelper;
- final Handler mHandler = new Handler();
- private final Runnable mHandleDeferredResume = this::handleDeferredResume;
- private boolean mDeferredResumePending;
-
private float mCurrentAssistantVisibility = 0f;
protected LauncherOverlayManager mOverlayManager;
@@ -375,9 +373,9 @@
mDragController = new DragController(this);
mAllAppsController = new AllAppsTransitionController(this);
- mStateManager = new LauncherStateManager(this);
+ mStateManager = new StateManager<>(this, NORMAL);
- mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs, mStateManager);
+ mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
mAppWidgetManager = new WidgetManagerHelper(this);
mAppWidgetHost = new LauncherAppWidgetHost(this,
@@ -440,7 +438,7 @@
mRotationHelper.initialize();
- mStateManager.addStateListener(new LauncherStateManager.StateListener() {
+ mStateManager.addStateListener(new StateListener<LauncherState>() {
@Override
public void onStateTransitionComplete(LauncherState finalState) {
@@ -467,9 +465,8 @@
return new LauncherOverlayManager() { };
}
- protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs,
- LauncherStateManager stateManager) {
- return new OnboardingPrefs<>(this, sharedPrefs, stateManager);
+ protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
+ return new OnboardingPrefs<>(this, sharedPrefs);
}
public OnboardingPrefs getOnboardingPrefs() {
@@ -523,13 +520,9 @@
}
@Override
- public void reapplyUi() {
- reapplyUi(true /* cancelCurrentAnimation */);
- }
-
public void reapplyUi(boolean cancelCurrentAnimation) {
getRootView().dispatchInsets();
- getStateManager().reapplyState(cancelCurrentAnimation);
+ super.reapplyUi(cancelCurrentAnimation);
}
@Override
@@ -583,7 +576,8 @@
return mFocusHandler;
}
- public LauncherStateManager getStateManager() {
+ @Override
+ public StateManager<LauncherState> getStateManager() {
return mStateManager;
}
@@ -890,11 +884,7 @@
@Override
protected void onStop() {
- final boolean wasActive = isUserActive();
- final LauncherState origState = getStateManager().getState();
- final int origDragLayerChildCount = mDragLayer.getChildCount();
super.onStop();
-
if (mDeferOverlayCallbacks) {
checkIfOverlayStillDeferred();
} else {
@@ -902,28 +892,8 @@
}
logStopAndResume(Action.Command.STOP);
-
mAppWidgetHost.setListenIfResumed(false);
-
NotificationListener.removeNotificationsChangedListener();
- getStateManager().moveToRestState();
-
- // Workaround for b/78520668, explicitly trim memory once UI is hidden
- onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
-
- if (wasActive) {
- // The expected condition is that this activity is stopped because the device goes to
- // sleep and the UI may have noticeable changes.
- mDragLayer.post(() -> {
- if ((!getStateManager().isInStableState(origState)
- // The drag layer may be animating (e.g. dismissing QSB).
- || mDragLayer.getAlpha() < 1
- // Maybe an ArrowPopup is closed.
- || mDragLayer.getChildCount() != origDragLayerChildCount)) {
- onUiChangedWhileSleeping();
- }
- });
- }
}
@Override
@@ -939,35 +909,27 @@
TraceHelper.INSTANCE.endSection(traceToken);
}
- private void handleDeferredResume() {
- if (hasBeenResumed() && !mStateManager.getState().hasFlag(FLAG_NON_INTERACTIVE)) {
- logStopAndResume(Action.Command.RESUME);
- getUserEventDispatcher().startSession();
+ @Override
+ @CallSuper
+ protected void onDeferredResumed() {
+ logStopAndResume(Action.Command.RESUME);
+ getUserEventDispatcher().startSession();
- AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
+ AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
- // Process any items that were added while Launcher was away.
- InstallShortcutReceiver.disableAndFlushInstallQueue(
- InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
+ // Process any items that were added while Launcher was away.
+ InstallShortcutReceiver.disableAndFlushInstallQueue(
+ InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
- // Refresh shortcuts if the permission changed.
- mModel.refreshShortcutsIfRequired();
+ // Refresh shortcuts if the permission changed.
+ mModel.refreshShortcutsIfRequired();
- // Set the notification listener and fetch updated notifications when we resume
- NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
+ // Set the notification listener and fetch updated notifications when we resume
+ NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
- DiscoveryBounce.showForHomeIfNeeded(this);
-
- onDeferredResumed();
- addActivityFlags(ACTIVITY_STATE_DEFERRED_RESUMED);
-
- mDeferredResumePending = false;
- } else {
- mDeferredResumePending = true;
- }
+ DiscoveryBounce.showForHomeIfNeeded(this);
}
- protected void onDeferredResumed() { }
private void logStopAndResume(int command) {
int containerType = mStateManager.getState().containerType;
@@ -1016,10 +978,9 @@
return mOverlayManager;
}
+ @Override
public void onStateSetStart(LauncherState state) {
- if (mDeferredResumePending) {
- handleDeferredResume();
- }
+ super.onStateSetStart(state);
if (mDeferOverlayCallbacks) {
scheduleDeferredCheck();
}
@@ -1042,7 +1003,9 @@
mWorkspace.getPageIndicator().setShouldAutoHide(!state.hasFlag(FLAG_MULTI_PAGE));
}
+ @Override
public void onStateSetEnd(LauncherState state) {
+ super.onStateSetStart(state);
getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE));
@@ -1068,9 +1031,6 @@
TraceHelper.FLAG_UI_EVENT);
super.onResume();
- mHandler.removeCallbacks(mHandleDeferredResume);
- Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
-
if (!mOnResumeCallbacks.isEmpty()) {
final ArrayList<OnResumeCallback> resumeCallbacks = new ArrayList<>(mOnResumeCallbacks);
mOnResumeCallbacks.clear();
@@ -1113,10 +1073,6 @@
}
}
- public boolean isInState(LauncherState state) {
- return mStateManager.getState() == state;
- }
-
/**
* Restores the previous state, if it exists.
*
@@ -1355,8 +1311,6 @@
}
};
- protected void onUiChangedWhileSleeping() { }
-
private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
mWorkspace.updateNotificationDots(updatedDots);
mAppsView.getAppsStore().updateNotificationDots(updatedDots);
@@ -2721,17 +2675,10 @@
return super.onKeyUp(keyCode, event);
}
- protected StateHandler[] createStateHandlers() {
+ protected StateHandler<LauncherState>[] createStateHandlers() {
return new StateHandler[] { getAllAppsController(), getWorkspace() };
}
- /**
- * Creates a factory for atomic state animations
- */
- public AtomicAnimationFactory createAtomicAnimationFactory() {
- return new AtomicAnimationFactory(0);
- }
-
public TouchController[] createTouchControllers() {
return new TouchController[] {getDragController(), new AllAppsSwipeController(this)};
}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index e133d31..db2a6cd 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -29,6 +29,8 @@
import android.content.Context;
import android.view.animation.Interpolator;
+import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.HintState;
import com.android.launcher3.states.SpringLoadedState;
import com.android.launcher3.uioverrides.states.AllAppsState;
@@ -40,7 +42,7 @@
/**
* Base state for various states used for the Launcher
*/
-public abstract class LauncherState {
+public abstract class LauncherState implements BaseState<LauncherState> {
/**
* Set of elements indicating various workspace elements which change visibility across states
@@ -60,25 +62,22 @@
HOTSEAT_SEARCH_BOX | ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
// Flag indicating workspace has multiple pages visible.
- public static final int FLAG_MULTI_PAGE = 1 << 0;
+ public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);
// Flag indicating that workspace and its contents are not accessible
- public static final int FLAG_WORKSPACE_INACCESSIBLE = 1 << 1;
+ public static final int FLAG_WORKSPACE_INACCESSIBLE = BaseState.getFlag(1);
- public static final int FLAG_DISABLE_RESTORE = 1 << 2;
// Flag indicating the state allows workspace icons to be dragged.
- public static final int FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED = 1 << 3;
+ public static final int FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED = BaseState.getFlag(2);
// Flag to indicate that workspace should draw page background
- public static final int FLAG_WORKSPACE_HAS_BACKGROUNDS = 1 << 4;
- // Flag to indicate that Launcher is non-interactive in this state
- public static final int FLAG_NON_INTERACTIVE = 1 << 5;
+ public static final int FLAG_WORKSPACE_HAS_BACKGROUNDS = BaseState.getFlag(3);
// True if the back button should be hidden when in this state (assuming no floating views are
// open, launcher has window focus, etc).
- public static final int FLAG_HIDE_BACK_BUTTON = 1 << 6;
+ public static final int FLAG_HIDE_BACK_BUTTON = BaseState.getFlag(4);
// Flag to indicate if the state would have scrim over sysui region: statu sbar and nav bar
- public static final int FLAG_HAS_SYS_UI_SCRIM = 1 << 7;
+ public static final int FLAG_HAS_SYS_UI_SCRIM = BaseState.getFlag(5);
// Flag to inticate that all popups should be closed when this state is enabled.
- public static final int FLAG_CLOSE_POPUPS = 1 << 8;
- public static final int FLAG_OVERVIEW_UI = 1 << 9;
+ public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(6);
+ public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(7);
public static final float NO_OFFSET = 0;
@@ -151,26 +150,15 @@
/**
* Returns if the state has the provided flag
*/
+ @Override
public final boolean hasFlag(int mask) {
return (mFlags & mask) != 0;
}
- /**
- * @return true if the state can be persisted across activity restarts.
- */
- public final boolean shouldDisableRestore() {
- return hasFlag(FLAG_DISABLE_RESTORE);
- }
-
public static LauncherState[] values() {
return Arrays.copyOf(sAllStates, sAllStates.length);
}
- /**
- * @return How long the animation to this state should take (or from this state to NORMAL).
- */
- public abstract int getTransitionDuration(Context context);
-
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
return new ScaleAndTranslation(NO_SCALE, NO_OFFSET, NO_OFFSET);
}
@@ -264,14 +252,20 @@
};
}
+ @Override
public LauncherState getHistoryForState(LauncherState previousState) {
// No history is supported
return NORMAL;
}
+ @Override
+ public String toString() {
+ return "Ordinal-" + ordinal;
+ }
+
public void onBackPressed(Launcher launcher) {
if (this != NORMAL) {
- LauncherStateManager lsm = launcher.getStateManager();
+ StateManager<LauncherState> lsm = launcher.getStateManager();
LauncherState lastState = lsm.getLastState();
lsm.goToState(lastState);
}
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 9dbb5fc..fbac0bd 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -7,6 +7,10 @@
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DISMISS_PREDICTION;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.UNINSTALL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_UNINSTALL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_UNINSTALL_CANCELLED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_UNINSTALL_COMPLETED;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SYSTEM_NO;
@@ -34,6 +38,7 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -58,7 +63,7 @@
private static final long CACHE_EXPIRE_TIMEOUT = 5000;
private final ArrayMap<UserHandle, Boolean> mUninstallDisabledCache = new ArrayMap<>(1);
-
+ private final StatsLogManager mStatsLogManager;
private final Alarm mCacheExpireAlarm;
private boolean mHadPendingAlarm;
@@ -69,8 +74,8 @@
public SecondaryDropTarget(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
-
mCacheExpireAlarm = new Alarm();
+ mStatsLogManager = StatsLogManager.newInstance(context);
}
@Override
@@ -214,6 +219,11 @@
// Defer onComplete
d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
super.onDrop(d, options);
+ if (mCurrentAccessibilityAction == UNINSTALL) {
+ mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL, d.logInstanceId);
+ } else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
+ mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST, d.logInstanceId);
+ }
}
@Override
@@ -338,8 +348,10 @@
mDragObject.dragInfo.user, PackageManager.MATCH_UNINSTALLED_PACKAGES) == null) {
mDragObject.dragSource = mOriginal;
mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, true);
+ mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_COMPLETED, mDragObject.logInstanceId);
} else {
sendFailure();
+ mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_CANCELLED, mDragObject.logInstanceId);
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 412eef1..286b522 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -89,6 +89,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.WorkspaceTouchListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -119,7 +120,7 @@
*/
public class Workspace extends PagedView<WorkspacePageIndicator>
implements DropTarget, DragSource, View.OnTouchListener,
- DragController.DragListener, Insettable, LauncherStateManager.StateHandler,
+ DragController.DragListener, Insettable, StateHandler<LauncherState>,
WorkspaceLayoutManager {
/** The value that {@link #mTransitionProgress} must be greater than for
@@ -1441,6 +1442,10 @@
mOutlineProvider = previewProvider;
+ if (draggableView == null && child instanceof DraggableView) {
+ draggableView = (DraggableView) child;
+ }
+
// The drag bitmap follows the touch point around on the screen
final Bitmap b = previewProvider.createDragBitmap();
int halfPadding = previewProvider.previewPadding / 2;
@@ -1451,12 +1456,8 @@
Point dragVisualizeOffset = null;
Rect dragRect = new Rect();
- if (draggableView == null && child instanceof DraggableView) {
- draggableView = (DraggableView) child;
- }
-
if (draggableView != null) {
- draggableView.getVisualDragBounds(dragRect);
+ draggableView.getSourceVisualDragBounds(dragRect);
dragLayerY += dragRect.top;
dragVisualizeOffset = new Point(- halfPadding, halfPadding);
}
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 29cf803..06a73db 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -66,7 +66,7 @@
}
/**
- * @see com.android.launcher3.LauncherStateManager.StateHandler#setStateWithAnimation
+ * @see com.android.launcher3.statemanager.StateManager.StateHandler#setStateWithAnimation
*/
public void setStateWithAnimation(
LauncherState toState, StateAnimationConfig config, PendingAnimation animation) {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 21dd141..f057036 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -28,11 +28,11 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.Themes;
@@ -50,8 +50,8 @@
* If release velocity < THRES1, snap according to either top or bottom depending on whether it's
* closer to top or closer to the page indicator.
*/
-public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener,
- PluginListener<AllAppsSearchPlugin> {
+public class AllAppsTransitionController implements StateHandler<LauncherState>,
+ OnDeviceProfileChangeListener, PluginListener<AllAppsSearchPlugin> {
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 0648682..5397942 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -31,9 +31,9 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.OnboardingPrefs;
/**
@@ -46,7 +46,7 @@
private final Launcher mLauncher;
private final Animator mDiscoBounceAnimation;
- private final StateListener mStateListener = new StateListener() {
+ private final StateListener<LauncherState> mStateListener = new StateListener<LauncherState>() {
@Override
public void onStateTransitionStart(LauncherState toState) {
handleClose(false);
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index f6766c4..80b6a5a 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -22,7 +22,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.views.WorkEduView;
/**
@@ -32,7 +32,7 @@
private final Launcher mLauncher;
- private LauncherStateManager.StateListener mWorkTabListener;
+ private StateListener<LauncherState> mWorkTabListener;
public LauncherAllAppsContainerView(Context context) {
this(context, null);
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index a95a5e1..740f7f2 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -105,6 +105,13 @@
add(anim);
}
+ public <T> void addFloat(T target, FloatProperty<T> property, float from, float to,
+ TimeInterpolator interpolator) {
+ Animator anim = ObjectAnimator.ofFloat(target, property, from, to);
+ anim.setDuration(mDuration).setInterpolator(interpolator);
+ add(anim);
+ }
+
@Override
public <T> void setInt(T target, IntProperty<T> property, int value,
TimeInterpolator interpolator) {
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
index 770df03..a9702b4 100644
--- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.FloatProperty;
@@ -197,9 +196,9 @@
animator.setDuration(getDuration()).setInterpolator(LINEAR);
animator.addUpdateListener(anim ->
property.set(target, getInterpolatedValue(anim.getAnimatedFraction())));
- animator.addListener(new AnimatorListenerAdapter() {
+ animator.addListener(new AnimationSuccessListener() {
@Override
- public void onAnimationEnd(Animator animation) {
+ public void onAnimationSuccess(Animator animation) {
property.set(target, mEndValue);
}
});
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 970c5a0..ddf44ca 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -274,19 +274,29 @@
scale *= childScale;
int toX = Math.round(coord[0]);
int toY = Math.round(coord[1]);
+
float toScale = scale;
if (child instanceof DraggableView) {
+ // This code is fairly subtle. Please verify drag and drop is pixel-perfect in a number
+ // of scenarios before modifying (from all apps, from workspace, different grid-sizes,
+ // shortcuts from in and out of Launcher etc).
DraggableView d = (DraggableView) child;
- d.getVisualDragBounds(dragViewBounds);
+ Rect destRect = new Rect();
+ d.getWorkspaceVisualDragBounds(destRect);
+
+ // In most cases this additional scale factor should be a no-op (1). It mainly accounts
+ // for alternate grids where the source and destination icon sizes are different
+ toScale *= ((1f * destRect.width())
+ / (dragView.getMeasuredWidth() - dragView.getBlurSizeOutline()));
// This accounts for the offset of the DragView created by scaling it about its
// center as it animates into place.
- float scaleShiftX = dragView.getMeasuredWidth() * (1 - scale) / 2;
- float scaleShiftY = dragView.getMeasuredHeight() * (1 - scale) / 2;
+ float scaleShiftX = dragView.getMeasuredWidth() * (1 - toScale) / 2;
+ float scaleShiftY = dragView.getMeasuredHeight() * (1 - toScale) / 2;
- toX += scale * (dragViewBounds.left - dragView.getBlurSizeOutline() / 2) - scaleShiftX;
- toY += scale * (dragViewBounds.top - dragView.getBlurSizeOutline() / 2) - scaleShiftY;
+ toX += scale * destRect.left - toScale * dragView.getBlurSizeOutline() / 2 - scaleShiftX;
+ toY += scale * destRect.top - toScale * dragView.getBlurSizeOutline() / 2 - scaleShiftY;
}
child.setVisibility(INVISIBLE);
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 1e23bb6..de0fa1a 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -49,18 +49,18 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import java.util.Arrays;
-public class DragView extends View implements LauncherStateManager.StateListener {
+public class DragView extends View implements StateListener<LauncherState> {
private static final ColorMatrix sTempMatrix1 = new ColorMatrix();
private static final ColorMatrix sTempMatrix2 = new ColorMatrix();
diff --git a/src/com/android/launcher3/dragndrop/DraggableView.java b/src/com/android/launcher3/dragndrop/DraggableView.java
index df99902..f7dcf6b 100644
--- a/src/com/android/launcher3/dragndrop/DraggableView.java
+++ b/src/com/android/launcher3/dragndrop/DraggableView.java
@@ -18,6 +18,10 @@
import android.graphics.Rect;
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.util.SafeCloseable;
+
/**
* Interface defining methods required for drawing and previewing DragViews, drag previews, and
* related animations
@@ -42,9 +46,12 @@
int getViewType();
/**
- * Before rendering as a DragView bitmap, some views need a preparation step.
+ * Before rendering as a DragView bitmap, some views need a preparation step. Returns a
+ * callback to clear any preparation work
*/
- default void prepareDrawDragView() { }
+ @NonNull default SafeCloseable prepareDrawDragView() {
+ return () -> { };
+ }
/**
* If an actual View subclass, this method returns the rectangle (within the View's coordinates)
@@ -53,5 +60,14 @@
*
* @param bounds Visual bounds in the views coordinates will be written here.
*/
- default void getVisualDragBounds(Rect bounds) { }
+ default void getWorkspaceVisualDragBounds(Rect bounds) { }
+
+ /**
+ * Same as above, but accounts for differing icon sizes between source and destination
+ *
+ * @param bounds Visual bounds in the views coordinates will be written here.
+ */
+ default void getSourceVisualDragBounds(Rect bounds) {
+ getWorkspaceVisualDragBounds(bounds);
+ }
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 9a36b3e..f7fe535 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -18,23 +18,17 @@
import static android.text.TextUtils.isEmpty;
-import static androidx.core.util.Preconditions.checkNotNull;
-
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
import static com.android.launcher3.model.data.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
import static java.util.Arrays.asList;
-import static java.util.Arrays.stream;
import static java.util.Optional.ofNullable;
import android.animation.Animator;
@@ -94,12 +88,6 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pageindicators.PageIndicatorDots;
-import com.android.launcher3.userevent.LauncherLogProto.Action;
-import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.LauncherLogProto.ItemType;
-import com.android.launcher3.userevent.LauncherLogProto.LauncherEvent;
-import com.android.launcher3.userevent.LauncherLogProto.Target;
-import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelState;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Thunk;
@@ -111,10 +99,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
-import java.util.Optional;
-import java.util.OptionalInt;
import java.util.stream.Collectors;
-import java.util.stream.IntStream;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -213,8 +198,7 @@
@Thunk int mScrollHintDir = SCROLL_NONE;
@Thunk int mCurrentScrollDir = SCROLL_NONE;
- private String mPreviousLabel;
- private boolean mIsPreviousLabelSuggested;
+ private StatsLogManager mStatsLogManager;
/**
* Used to inflate the Workspace from XML.
@@ -227,10 +211,12 @@
setAlwaysDrawnWithCacheEnabled(false);
mLauncher = Launcher.getLauncher(context);
+ mStatsLogManager = StatsLogManager.newInstance(context);
// We need this view to be focusable in touch mode so that when text editing of the folder
// name is complete, we have something to focus on, thus hiding the cursor and giving
// reliable behavior when clicking the text field (since it will always gain focus on click).
setFocusableInTouchMode(true);
+
}
@Override
@@ -348,9 +334,9 @@
if (DEBUG) {
Log.d(TAG, "onBackKey newTitle=" + newTitle);
}
-
- mInfo.title = newTitle;
- mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !getAcceptedSuggestionIndex().isPresent(),
+ mInfo.setTitle(newTitle);
+ mInfo.fromCustom = mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
+ mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !mInfo.getAcceptedSuggestionIndex().isPresent(),
mLauncher.getModelWriter());
mFolderIcon.onTitleChanged(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
@@ -441,8 +427,6 @@
}
mItemsInvalidated = true;
mInfo.addListener(this);
- Optional.ofNullable(mInfo.title).ifPresent(title -> mPreviousLabel = title.toString());
- mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
if (!isEmpty(mInfo.title)) {
mFolderName.setText(mInfo.title);
@@ -1347,10 +1331,8 @@
if (d.stateAnnouncer != null) {
d.stateAnnouncer.completeAction(R.string.item_moved);
}
- StatsLogManager.newInstance(getContext())
- .log(StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
- d.logInstanceId,
- d.dragInfo.buildProto(mInfo));
+ mStatsLogManager
+ .log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo.buildProto(mInfo));
}
// This is used so the item doesn't immediately appear in the folder when added. In one case
@@ -1455,7 +1437,8 @@
if (hasFocus) {
startEditingFolderName();
} else {
- logCurrentFolderLabelState();
+ mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo.buildProto());
+ logFolderLabelState();
mFolderName.dispatchBackKey();
}
}
@@ -1654,147 +1637,14 @@
return mContent;
}
- protected void logCurrentFolderLabelState() {
- LauncherEvent launcherEvent = LauncherEvent.newBuilder()
- .setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD))
- .addSrcTarget(newEditTextTargetBuilder()
- .setFromFolderLabelState(getFromFolderLabelState())
- .setToFolderLabelState(getToFolderLabelState()))
- .addSrcTarget(newFolderTargetBuilder())
- .addSrcTarget(newParentContainerTarget())
- .build();
- mLauncher.getUserEventDispatcher().logLauncherEvent(launcherEvent);
- mPreviousLabel = mFolderName.getText().toString();
- mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
- }
-
- private Target.FromFolderLabelState getFromFolderLabelState() {
- return mPreviousLabel == null
- ? FROM_FOLDER_LABEL_STATE_UNSPECIFIED
- : mPreviousLabel.isEmpty()
- ? FROM_EMPTY
- : mIsPreviousLabelSuggested
- ? FROM_SUGGESTED
- : FROM_CUSTOM;
- }
-
- private Target.ToFolderLabelState getToFolderLabelState() {
- String newLabel =
- checkNotNull(mFolderName.getText().toString(),
- "Expected valid folder label, but found null");
- if (newLabel.equals(mPreviousLabel)) {
- return Target.ToFolderLabelState.UNCHANGED;
- }
-
- if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- return newLabel.isEmpty()
- ? ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED
- : ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
- }
-
- Optional<String[]> suggestedLabels = getSuggestedLabels();
- boolean isEmptySuggestions = suggestedLabels
- .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
- .orElse(true);
- if (isEmptySuggestions) {
- return newLabel.isEmpty()
- ? ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS
- : ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
- }
-
- boolean hasValidPrimary = suggestedLabels
- .map(labels -> !isEmpty(labels[0]))
- .orElse(false);
- if (newLabel.isEmpty()) {
- return hasValidPrimary ? ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
- }
-
- OptionalInt accepted_suggestion_index = getAcceptedSuggestionIndex();
- if (!accepted_suggestion_index.isPresent()) {
- return hasValidPrimary ? ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
- }
-
- switch (accepted_suggestion_index.getAsInt()) {
- case 0:
- return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
- case 1:
- return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
- case 2:
- return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
- case 3:
- return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
- default:
- // fall through
- }
- return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
-
- }
-
- private Optional<String[]> getSuggestedLabels() {
- return ofNullable(mInfo)
- .map(info -> info.suggestedFolderNames)
- .map(
- folderNames ->
- (FolderNameInfo[])
- folderNames.getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
- .map(
- folderNameInfoArray ->
- stream(folderNameInfoArray)
- .filter(Objects::nonNull)
- .map(FolderNameInfo::getLabel)
- .filter(Objects::nonNull)
- .map(CharSequence::toString)
- .toArray(String[]::new));
- }
-
- private OptionalInt getAcceptedSuggestionIndex() {
- String newLabel = checkNotNull(mFolderName.getText().toString(),
- "Expected valid folder label, but found null");
- return getSuggestedLabels()
- .map(suggestionsArray ->
- IntStream.range(0, suggestionsArray.length)
- .filter(
- index -> !isEmpty(suggestionsArray[index])
- && newLabel.equalsIgnoreCase(suggestionsArray[index]))
- .sequential()
- .findFirst()
- ).orElse(OptionalInt.empty());
-
- }
-
-
- private Target.Builder newEditTextTargetBuilder() {
- return Target.newBuilder().setType(Target.Type.ITEM).setItemType(ItemType.EDITTEXT);
- }
-
- private Target.Builder newFolderTargetBuilder() {
- return Target.newBuilder()
- .setType(Target.Type.CONTAINER)
- .setContainerType(ContainerType.FOLDER)
- .setPageIndex(mInfo.screenId)
- .setGridX(mInfo.cellX)
- .setGridY(mInfo.cellY)
- .setCardinality(mInfo.contents.size());
- }
-
- private Target.Builder newParentContainerTarget() {
- Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
- switch (mInfo.container) {
- case CONTAINER_HOTSEAT:
- return builder.setContainerType(ContainerType.HOTSEAT);
- case CONTAINER_DESKTOP:
- return builder.setContainerType(ContainerType.WORKSPACE);
- default:
- throw new AssertionError(String
- .format("Expected container to be either %s or %s but found %s.",
- CONTAINER_HOTSEAT,
- CONTAINER_DESKTOP,
- mInfo.container));
- }
+ /**
+ * Logs current folder label info.
+ *
+ * @deprecated This method is only used for log validation and soon will be removed.
+ */
+ @Deprecated
+ public void logFolderLabelState() {
+ mLauncher.getUserEventDispatcher()
+ .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent());
}
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index e2963d7..153d6bc 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -53,6 +54,7 @@
import com.android.launcher3.Reorderable;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
+import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.FolderDotInfo;
@@ -61,6 +63,8 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.icons.DotRenderer;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.FolderInfo.FolderListener;
@@ -385,6 +389,14 @@
float finalAlpha = index < MAX_NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
float finalScale = scale * scaleRelativeToDragLayer;
+
+ // Account for potentially different icon sizes with non-default grid settings
+ if (d.dragSource instanceof AllAppsContainerView) {
+ DeviceProfile grid = mActivity.getDeviceProfile();
+ float containerScale = (1f * grid.iconSizePx / grid.allAppsIconSizePx);
+ finalScale *= containerScale;
+ }
+
dragLayer.animateView(animateView, from, to, finalAlpha,
1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
Interpolators.DEACCEL_2, Interpolators.ACCEL_2,
@@ -401,10 +413,10 @@
Executors.UI_HELPER_EXECUTOR.post(() -> {
d.folderNameProvider.getSuggestedFolderName(
getContext(), mInfo.contents, nameInfos);
- showFinalView(finalIndex, item, nameInfos);
+ showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
});
} else {
- showFinalView(finalIndex, item, nameInfos);
+ showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
}
} else {
addItem(item);
@@ -412,12 +424,12 @@
}
private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
- FolderNameInfo[] nameInfos) {
+ FolderNameInfo[] nameInfos, InstanceId instanceId) {
postDelayed(() -> {
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
- setLabelSuggestion(nameInfos);
- mFolder.logCurrentFolderLabelState();
+ setLabelSuggestion(nameInfos, instanceId);
+ mFolder.logFolderLabelState();
invalidate();
}, DROP_IN_ANIMATION_DURATION);
}
@@ -425,7 +437,7 @@
/**
* Set the suggested folder name.
*/
- public void setLabelSuggestion(FolderNameInfo[] nameInfos) {
+ public void setLabelSuggestion(FolderNameInfo[] nameInfos, InstanceId instanceId) {
if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
return;
}
@@ -436,7 +448,9 @@
if (nameInfos == null || nameInfos[0] == null || isEmpty(nameInfos[0].getLabel())) {
return;
}
- mInfo.title = nameInfos[0].getLabel();
+ mInfo.setTitle(nameInfos[0].getLabel());
+ StatsLogManager.newInstance(getContext())
+ .log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo.buildProto());
onTitleChanged(mInfo.title);
mFolder.mFolderName.setText(mInfo.title);
mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
@@ -758,7 +772,7 @@
}
@Override
- public void getVisualDragBounds(Rect bounds) {
+ public void getWorkspaceVisualDragBounds(Rect bounds) {
getPreviewBounds(bounds);
}
}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 848c04a..21822a3 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -35,6 +35,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.nio.ByteBuffer;
@@ -76,11 +77,12 @@
if (mView instanceof DraggableView) {
DraggableView dv = (DraggableView) mView;
- dv.prepareDrawDragView();
- dv.getVisualDragBounds(mTempRect);
- destCanvas.translate(blurSizeOutline / 2 - mTempRect.left,
- blurSizeOutline / 2 - mTempRect.top);
- mView.draw(destCanvas);
+ try (SafeCloseable t = dv.prepareDrawDragView()) {
+ dv.getSourceVisualDragBounds(mTempRect);
+ destCanvas.translate(blurSizeOutline / 2 - mTempRect.left,
+ blurSizeOutline / 2 - mTempRect.top);
+ mView.draw(destCanvas);
+ }
}
destCanvas.restoreToCount(saveCount);
}
@@ -95,7 +97,7 @@
// Assume scaleX == scaleY, which is always the case for workspace items.
float scale = mView.getScaleX();
if (mView instanceof DraggableView) {
- ((DraggableView) mView).getVisualDragBounds(mTempRect);
+ ((DraggableView) mView).getSourceVisualDragBounds(mTempRect);
width = mTempRect.width();
height = mTempRect.height();
} else {
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 20eec9a..350f221 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -29,6 +29,7 @@
import android.view.Display;
import android.view.SurfaceControlViewHost;
import android.view.View;
+import android.view.animation.AccelerateDecelerateInterpolator;
import com.android.launcher3.InvariantDeviceProfile;
@@ -37,6 +38,8 @@
/** Render preview using surface view. */
public class PreviewSurfaceRenderer implements IBinder.DeathRecipient {
+ private static final int FADE_IN_ANIMATION_DURATION = 200;
+
private static final String KEY_HOST_TOKEN = "host_token";
private static final String KEY_VIEW_WIDTH = "width";
private static final String KEY_VIEW_HEIGHT = "height";
@@ -78,10 +81,12 @@
binderDied();
}
+ SurfaceControlViewHost.SurfacePackage surfacePackage;
try {
mSurfaceControlViewHost = MAIN_EXECUTOR
.submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))
.get(5, TimeUnit.SECONDS);
+ surfacePackage = mSurfaceControlViewHost.getSurfacePackage();
mHostToken.linkToDeath(this, 0);
} catch (Exception e) {
e.printStackTrace();
@@ -89,6 +94,14 @@
}
MAIN_EXECUTOR.execute(() -> {
+ // If mSurfaceControlViewHost is null due to any reason (e.g. binder died,
+ // happening when user leaves the preview screen before preview rendering finishes),
+ // we should return here.
+ SurfaceControlViewHost host = mSurfaceControlViewHost;
+ if (host == null) {
+ return;
+ }
+
View view = new LauncherPreviewRenderer(mContext, mIdp).getRenderedView();
// This aspect scales the view to fit in the surface and centers it
final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(),
@@ -99,14 +112,19 @@
view.setPivotY(0);
view.setTranslationX((mWidth - scale * view.getWidth()) / 2);
view.setTranslationY((mHeight - scale * view.getHeight()) / 2);
- mSurfaceControlViewHost.setView(view, view.getMeasuredWidth(),
+ view.setAlpha(0);
+ view.animate().alpha(1)
+ .setInterpolator(new AccelerateDecelerateInterpolator())
+ .setDuration(FADE_IN_ANIMATION_DURATION)
+ .start();
+ host.setView(view, view.getMeasuredWidth(),
view.getMeasuredHeight());
});
Bundle result = new Bundle();
- result.putParcelable(KEY_SURFACE_PACKAGE, mSurfaceControlViewHost.getSurfacePackage());
+ result.putParcelable(KEY_SURFACE_PACKAGE, surfacePackage);
- Handler handler = new Handler(Looper.getMainLooper(), Loopermessage -> {
+ Handler handler = new Handler(Looper.getMainLooper(), message -> {
binderDied();
return true;
});
@@ -120,8 +138,10 @@
@Override
public void binderDied() {
if (mSurfaceControlViewHost != null) {
- mSurfaceControlViewHost.release();
- mSurfaceControlViewHost = null;
+ MAIN_EXECUTOR.execute(() -> {
+ mSurfaceControlViewHost.release();
+ mSurfaceControlViewHost = null;
+ });
}
mHostToken.unlinkToDeath(this, 0);
}
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 0f79bd6..cd4f034 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -41,6 +41,7 @@
private static final ArrayMap<Class, SparseArray<String>> sNameCache = new ArrayMap<>();
private static final String UNKNOWN = "UNKNOWN";
private static final int DEFAULT_PREDICTED_RANK = 10000;
+ private static final String DELIMITER_DOT = "\\.";
public static String getFieldName(int value, Class c) {
SparseArray<String> cache;
@@ -173,4 +174,17 @@
targets.toArray(targetsArray);
return newLauncherEvent(action, targetsArray);
}
+
+ /**
+ * String conversion for only the helpful parts of {@link Object#toString()} method
+ * @param stringToExtract "foo.bar.baz.MyObject@1234"
+ * @return "MyObject@1234"
+ */
+ public static String extractObjectNameAndAddress(String stringToExtract) {
+ String[] superStringParts = stringToExtract.split(DELIMITER_DOT);
+ if (superStringParts.length == 0) {
+ return "";
+ }
+ return superStringParts[superStringParts.length - 1];
+ }
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index abf027d..b240f0b 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -16,47 +16,79 @@
package com.android.launcher3.logging;
import android.content.Context;
-import android.util.Log;
import com.android.launcher3.R;
-import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.ItemInfo;
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.util.ResourceBasedOverride;
/**
* Handles the user event logging in R+.
+ * All of the event ids are defined here.
+ * Most of the methods are dummy methods for Launcher3
+ * Actual call happens only for Launcher variant that implements QuickStep.
*/
public class StatsLogManager implements ResourceBasedOverride {
- private static final String TAG = "StatsLogManager";
-
interface EventEnum {
int getId();
}
public enum LauncherEvent implements EventEnum {
@LauncherUiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
- APP_LAUNCH_TAP(1),
+ LAUNCHER_APP_LAUNCH_TAP(338),
+
@LauncherUiEvent(doc = "Task launched from overview using TAP")
- TASK_LAUNCH_TAP(2),
+ LAUNCHER_TASK_LAUNCH_TAP(339),
+
@LauncherUiEvent(doc = "Task launched from overview using SWIPE DOWN")
- TASK_LAUNCH_SWIPE_DOWN(2),
+ LAUNCHER_TASK_LAUNCH_SWIPE_DOWN(340),
+
@LauncherUiEvent(doc = "TASK dismissed from overview using SWIPE UP")
- TASK_DISMISS_SWIPE_UP(3),
+ LAUNCHER_TASK_DISMISS_SWIPE_UP(341),
+
@LauncherUiEvent(doc = "User dragged a launcher item")
LAUNCHER_ITEM_DRAG_STARTED(383),
+
@LauncherUiEvent(doc = "A dragged launcher item is successfully dropped")
LAUNCHER_ITEM_DROP_COMPLETED(385),
+
@LauncherUiEvent(doc = "A dragged launcher item is successfully dropped on another item "
- + "resulting in new folder creation")
- LAUNCHER_ITEM_DROP_FOLDER_CREATED(386);
+ + "resulting in a new folder creation")
+ LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
+
+ @LauncherUiEvent(doc = "User action resulted in or manually updated the folder label to "
+ + "new/same value.")
+ LAUNCHER_FOLDER_LABEL_UPDATED(460),
+
+ @LauncherUiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
+ LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
+
+ @LauncherUiEvent(doc = "A dragged item is dropped on 'Cancel' button in the target bar")
+ LAUNCHER_ITEM_DROPPED_ON_CANCEL(466),
+
+ @LauncherUiEvent(doc = "A predicted item is dragged and dropped on 'Don't suggest app'"
+ + " button in the target bar")
+ LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST(467),
+
+ @LauncherUiEvent(doc = "A dragged item is dropped on 'Uninstall' button in target bar")
+ LAUNCHER_ITEM_DROPPED_ON_UNINSTALL(468),
+
+ @LauncherUiEvent(doc = "User completed uninstalling the package after dropping on "
+ + "the icon onto 'Uninstall' button in the target bar")
+ LAUNCHER_ITEM_UNINSTALL_COMPLETED(469),
+
+ @LauncherUiEvent(doc = "User cancelled uninstalling the package after dropping on "
+ + "the icon onto 'Uninstall' button in the target bar")
+ LAUNCHER_ITEM_UNINSTALL_CANCELLED(470);
// ADD MORE
private final int mId;
+
LauncherEvent(int id) {
mId = id;
}
+
public int getId() {
return mId;
}
@@ -75,30 +107,36 @@
StatsLogManager mgr = Overrides.getObject(StatsLogManager.class,
context.getApplicationContext(), R.string.stats_log_manager_class);
mgr.mStateProvider = stateProvider;
- mgr.verify();
return mgr;
}
/**
- * Logs an event and accompanying {@link ItemInfo}
+ * Logs a {@link LauncherEvent}.
*/
- public void log(LauncherEvent event, LauncherAtom.ItemInfo itemInfo) {
- Log.d(TAG, String.format("%s\n%s", event.name(), itemInfo));
- // Call StatsLog method
+ public void log(LauncherEvent event) {
}
/**
- * Logs an event and accompanying {@link ItemInfo}
+ * Logs an event and accompanying {@link InstanceId}.
*/
- public void log(LauncherEvent event, InstanceId instanceId, LauncherAtom.ItemInfo itemInfo) {
- Log.d(TAG, String.format("%s(InstanceId:%s)\n%s", event.name(), instanceId, itemInfo));
- // Call StatsLog method
+ public void log(LauncherEvent event, InstanceId instanceId) {
+ }
+
+ /**
+ * Logs an event and accompanying {@link ItemInfo}.
+ */
+ public void log(LauncherEvent event, ItemInfo itemInfo) {
+ }
+
+ /**
+ * Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
+ */
+ public void log(LauncherEvent event, InstanceId instanceId, ItemInfo itemInfo) {
}
/**
* Logs snapshot, or impression of the current workspace.
*/
- public void logSnapshot() { }
-
- public void verify() {} // TODO: should move into robo tests
+ public void logSnapshot() {
+ }
}
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
index 97aaf84..10d88e5 100644
--- a/src/com/android/launcher3/logging/StatsLogUtils.java
+++ b/src/com/android/launcher3/logging/StatsLogUtils.java
@@ -1,7 +1,5 @@
package com.android.launcher3.logging;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.DEFAULT_CONTAINERTYPE;
-
import android.view.View;
import android.view.ViewParent;
@@ -13,6 +11,7 @@
import java.util.ArrayList;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.DEFAULT_CONTAINERTYPE;
public class StatsLogUtils {
@@ -31,8 +30,6 @@
/**
* Implemented by containers to provide a container source for a given child.
- *
- * Currently,
*/
public interface LogContainerProvider {
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index da081a0..7818ff5 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -145,7 +145,9 @@
LauncherEvent event = newLauncherEvent(action, targets);
ItemInfo info = v == null ? null : (ItemInfo) v.getTag();
if (info != null && Utilities.IS_DEBUG_DEVICE && FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
- FileLog.d(TAG, "appLaunch: packageName:" + info.getTargetComponent().getPackageName()
+ final String pkg = info.getTargetComponent() != null
+ ? info.getTargetComponent().getPackageName() : "unknown";
+ FileLog.d(TAG, "appLaunch: packageName:" + pkg
+ ",isWorkApp:" + (info.user != null && !Process.myUserHandle().equals(
userHandle)) + ",launchLocation:" + info.container);
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 90aaf44..9e6282e 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -851,20 +851,23 @@
}
private List<AppInfo> loadCachedPredictions() {
- List<ComponentKey> componentKeys = mApp.getPredictionModel().getPredictionComponentKeys();
- List<AppInfo> results = new ArrayList<>();
- if (componentKeys == null) return results;
- List<LauncherActivityInfo> l;
- mBgDataModel.cachedPredictedItems.clear();
- for (ComponentKey key : componentKeys) {
- l = mLauncherApps.getActivityList(key.componentName.getPackageName(), key.user);
- if (l.size() == 0) continue;
- boolean quietMode = mUserManager.isQuietModeEnabled(key.user);
- AppInfo info = new AppInfo(l.get(0), key.user, quietMode);
- mBgDataModel.cachedPredictedItems.add(info);
- mIconCache.getTitleAndIcon(info, false);
+ synchronized (mBgDataModel) {
+ List<ComponentKey> componentKeys =
+ mApp.getPredictionModel().getPredictionComponentKeys();
+ List<AppInfo> results = new ArrayList<>();
+ if (componentKeys == null) return results;
+ List<LauncherActivityInfo> l;
+ mBgDataModel.cachedPredictedItems.clear();
+ for (ComponentKey key : componentKeys) {
+ l = mLauncherApps.getActivityList(key.componentName.getPackageName(), key.user);
+ if (l.size() == 0) continue;
+ boolean quietMode = mUserManager.isQuietModeEnabled(key.user);
+ AppInfo info = new AppInfo(l.get(0), key.user, quietMode);
+ mBgDataModel.cachedPredictedItems.add(info);
+ mIconCache.getTitleAndIcon(info, false);
+ }
+ return results;
}
- return results;
}
private List<LauncherActivityInfo> loadAllApps() {
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 3ac6a22..096743a 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -16,16 +16,45 @@
package com.android.launcher3.model.data;
+import static android.text.TextUtils.isEmpty;
+
+import static androidx.core.util.Preconditions.checkNotNull;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
+
+import static java.util.Arrays.stream;
+import static java.util.Optional.ofNullable;
+
import android.content.Intent;
import android.os.Process;
+import android.text.TextUtils;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.folder.FolderNameInfo;
import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.userevent.LauncherLogProto;
+import com.android.launcher3.userevent.LauncherLogProto.Target;
+import com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState;
+import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelState;
import com.android.launcher3.util.ContentWriter;
import java.util.ArrayList;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.OptionalInt;
+import java.util.StringJoiner;
+import java.util.stream.IntStream;
+
/**
* Represents a folder containing shortcuts or apps.
@@ -57,6 +86,20 @@
public Intent suggestedFolderNames;
+ // Represents the title before current.
+ // Primarily used for logging purpose.
+ private CharSequence mPreviousTitle;
+
+ // True if the title before was manually entered, suggested otherwise.
+ // Primarily used for logging purpose.
+ public boolean fromCustom;
+
+ /**
+ * Used for separating {@link #mPreviousTitle} and {@link #title} when concatenating them
+ * for logging.
+ */
+ private static final CharSequence FOLDER_LABEL_DELIMITER = "=>";
+
/**
* The apps and shortcuts
*/
@@ -160,9 +203,20 @@
@Override
public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
return getDefaultItemInfoBuilder()
- .setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
- .setContainerInfo(getContainerInfo())
- .build();
+ .setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
+ .setRank(rank)
+ .setContainerInfo(getContainerInfo())
+ .build();
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mPreviousTitle = this.title;
+ this.title = title;
+ }
+
+ public CharSequence getPreviousTitle() {
+ return mPreviousTitle;
}
@Override
@@ -172,4 +226,244 @@
folderInfo.contents = this.contents;
return folderInfo;
}
+
+ /**
+ * Returns {@link LauncherAtom.FolderIcon} wrapped as {@link LauncherAtom.ItemInfo} for logging.
+ */
+ @Override
+ public LauncherAtom.ItemInfo buildProto() {
+ FromState fromFolderLabelState = getFromFolderLabelState();
+ ToState toFolderLabelState = getToFolderLabelState();
+ LauncherAtom.FolderIcon.Builder folderIconBuilder = LauncherAtom.FolderIcon.newBuilder()
+ .setCardinality(contents.size())
+ .setFromLabelState(fromFolderLabelState)
+ .setToLabelState(toFolderLabelState);
+
+ // If the folder label is suggested, it is logged to improve prediction model.
+ // When both old and new labels are logged together delimiter is used.
+ StringJoiner labelInfoBuilder = new StringJoiner(FOLDER_LABEL_DELIMITER);
+ if (fromFolderLabelState.equals(FromState.FROM_SUGGESTED)) {
+ labelInfoBuilder.add(mPreviousTitle);
+ }
+ if (toFolderLabelState.toString().startsWith("TO_SUGGESTION")) {
+ labelInfoBuilder.add(title);
+ }
+ if (labelInfoBuilder.length() > 0) {
+ folderIconBuilder.setLabelInfo(labelInfoBuilder.toString());
+ }
+
+ return getDefaultItemInfoBuilder()
+ .setFolderIcon(folderIconBuilder)
+ .setContainerInfo(getContainerInfo())
+ .build();
+ }
+
+ /**
+ * Returns index of the accepted suggestion.
+ */
+ public OptionalInt getAcceptedSuggestionIndex() {
+ String newLabel = checkNotNull(title,
+ "Expected valid folder label, but found null").toString();
+ return getSuggestedLabels()
+ .map(suggestionsArray ->
+ IntStream.range(0, suggestionsArray.length)
+ .filter(
+ index -> !isEmpty(suggestionsArray[index])
+ && newLabel.equalsIgnoreCase(
+ suggestionsArray[index]))
+ .sequential()
+ .findFirst()
+ ).orElse(OptionalInt.empty());
+
+ }
+
+ private LauncherAtom.ToState getToFolderLabelState() {
+ if (title == null) {
+ return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
+ }
+
+ if (title.equals(mPreviousTitle)) {
+ return LauncherAtom.ToState.UNCHANGED;
+ }
+
+ if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
+ return title.length() > 0
+ ? LauncherAtom.ToState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED
+ : LauncherAtom.ToState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
+ }
+
+ Optional<String[]> suggestedLabels = getSuggestedLabels();
+ boolean isEmptySuggestions = suggestedLabels
+ .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
+ .orElse(true);
+ if (isEmptySuggestions) {
+ return title.length() > 0
+ ? LauncherAtom.ToState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS
+ : LauncherAtom.ToState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
+ }
+
+ boolean hasValidPrimary = suggestedLabels
+ .map(labels -> !isEmpty(labels[0]))
+ .orElse(false);
+ if (title.length() == 0) {
+ return hasValidPrimary ? LauncherAtom.ToState.TO_EMPTY_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+ }
+
+ OptionalInt accepted_suggestion_index = getAcceptedSuggestionIndex();
+ if (!accepted_suggestion_index.isPresent()) {
+ return hasValidPrimary ? LauncherAtom.ToState.TO_CUSTOM_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+ }
+
+ switch (accepted_suggestion_index.getAsInt()) {
+ case 0:
+ return LauncherAtom.ToState.TO_SUGGESTION0;
+ case 1:
+ return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION1_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
+ case 2:
+ return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION2_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
+ case 3:
+ return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION3_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
+ default:
+ // fall through
+ }
+ return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
+
+ }
+
+ private LauncherAtom.FromState getFromFolderLabelState() {
+ return mPreviousTitle == null
+ ? LauncherAtom.FromState.FROM_STATE_UNSPECIFIED
+ : mPreviousTitle.length() == 0
+ ? LauncherAtom.FromState.FROM_EMPTY
+ : fromCustom
+ ? LauncherAtom.FromState.FROM_CUSTOM
+ : LauncherAtom.FromState.FROM_SUGGESTED;
+ }
+
+ private Optional<String[]> getSuggestedLabels() {
+ return ofNullable(suggestedFolderNames)
+ .map(folderNames ->
+ (FolderNameInfo[])
+ folderNames.getParcelableArrayExtra(EXTRA_FOLDER_SUGGESTIONS))
+ .map(folderNameInfoArray ->
+ stream(folderNameInfoArray)
+ .filter(Objects::nonNull)
+ .map(FolderNameInfo::getLabel)
+ .filter(Objects::nonNull)
+ .map(CharSequence::toString)
+ .toArray(String[]::new));
+ }
+
+ /**
+ * Returns {@link LauncherLogProto.LauncherEvent} to log current folder label info.
+ *
+ * @deprecated This method is used only for validation purpose and soon will be removed.
+ */
+ @Deprecated
+ public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent() {
+ return LauncherLogProto.LauncherEvent.newBuilder()
+ .setAction(LauncherLogProto.Action
+ .newBuilder()
+ .setType(LauncherLogProto.Action.Type.SOFT_KEYBOARD))
+ .addSrcTarget(Target
+ .newBuilder()
+ .setType(Target.Type.ITEM)
+ .setItemType(LauncherLogProto.ItemType.EDITTEXT)
+ .setFromFolderLabelState(convertFolderLabelState(getFromFolderLabelState()))
+ .setToFolderLabelState(convertFolderLabelState(getToFolderLabelState())))
+ .addSrcTarget(Target.newBuilder()
+ .setType(Target.Type.CONTAINER)
+ .setContainerType(LauncherLogProto.ContainerType.FOLDER)
+ .setPageIndex(screenId)
+ .setGridX(cellX)
+ .setGridY(cellY)
+ .setCardinality(contents.size()))
+ .addSrcTarget(newParentContainerTarget())
+ .build();
+ }
+
+ /**
+ * @deprecated This method is used only for validation purpose and soon will be removed.
+ */
+ @Deprecated
+ private Target.Builder newParentContainerTarget() {
+ Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
+ switch (container) {
+ case CONTAINER_HOTSEAT:
+ return builder.setContainerType(LauncherLogProto.ContainerType.HOTSEAT);
+ case CONTAINER_DESKTOP:
+ return builder.setContainerType(LauncherLogProto.ContainerType.WORKSPACE);
+ default:
+ throw new AssertionError(String
+ .format("Expected container to be either %s or %s but found %s.",
+ CONTAINER_HOTSEAT,
+ CONTAINER_DESKTOP,
+ container));
+ }
+ }
+
+ /**
+ * @deprecated This method is used only for validation purpose and soon will be removed.
+ */
+ @Deprecated
+ private static FromFolderLabelState convertFolderLabelState(FromState fromState) {
+ switch (fromState) {
+ case FROM_EMPTY:
+ return FROM_EMPTY;
+ case FROM_SUGGESTED:
+ return FROM_SUGGESTED;
+ case FROM_CUSTOM:
+ return FROM_CUSTOM;
+ default:
+ return FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
+ }
+ }
+
+ /**
+ * @deprecated This method is used only for validation purpose and soon will be removed.
+ */
+ @Deprecated
+ private static ToFolderLabelState convertFolderLabelState(ToState toState) {
+ switch (toState) {
+ case UNCHANGED:
+ return ToFolderLabelState.UNCHANGED;
+ case TO_SUGGESTION0:
+ return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
+ case TO_SUGGESTION1_WITH_VALID_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY;
+ case TO_SUGGESTION1_WITH_EMPTY_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
+ case TO_SUGGESTION2_WITH_VALID_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY;
+ case TO_SUGGESTION2_WITH_EMPTY_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
+ case TO_SUGGESTION3_WITH_VALID_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY;
+ case TO_SUGGESTION3_WITH_EMPTY_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
+ case TO_EMPTY_WITH_VALID_PRIMARY:
+ return ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY;
+ case TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
+ return ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+ case TO_EMPTY_WITH_EMPTY_SUGGESTIONS:
+ return ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
+ case TO_EMPTY_WITH_SUGGESTIONS_DISABLED:
+ return ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
+ case TO_CUSTOM_WITH_VALID_PRIMARY:
+ return ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY;
+ case TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
+ return ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+ case TO_CUSTOM_WITH_EMPTY_SUGGESTIONS:
+ return ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
+ case TO_CUSTOM_WITH_SUGGESTIONS_DISABLED:
+ return ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
+ default:
+ return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
+ }
+ }
}
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 4359f25..f2b7e54 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -252,6 +252,13 @@
/**
* Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
*/
+ public LauncherAtom.ItemInfo buildProto() {
+ return buildProto(null);
+ }
+
+ /**
+ * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
+ */
public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder();
Optional<ComponentName> nullableComponent = Optional.ofNullable(getTargetComponent());
@@ -303,7 +310,7 @@
break;
}
itemBuilder.setContainerInfo(ContainerInfo.newBuilder().setFolder(folderBuilder));
- } else {
+ } else if (getContainerInfo().getContainerCase().getNumber() > 0) {
itemBuilder.setContainerInfo(getContainerInfo());
}
return itemBuilder.build();
@@ -345,4 +352,8 @@
itemInfo.copyFrom(this);
return itemInfo;
}
+
+ public void setTitle(CharSequence title) {
+ this.title = title;
+ }
}
diff --git a/src/com/android/launcher3/statemanager/BaseState.java b/src/com/android/launcher3/statemanager/BaseState.java
new file mode 100644
index 0000000..daec1d8
--- /dev/null
+++ b/src/com/android/launcher3/statemanager/BaseState.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.statemanager;
+
+import android.content.Context;
+
+/**
+ * Interface representing a state of a StatefulActivity
+ */
+public interface BaseState<T extends BaseState> {
+
+ // Flag to indicate that Launcher is non-interactive in this state
+ int FLAG_NON_INTERACTIVE = 1 << 0;
+ int FLAG_DISABLE_RESTORE = 1 << 1;
+
+ static int getFlag(int index) {
+ // reserve few spots to base flags
+ return 1 << (index + 2);
+ }
+
+ /**
+ * @return How long the animation to this state should take (or from this state to NORMAL).
+ */
+ int getTransitionDuration(Context context);
+
+ /**
+ * Returns the state to go back to from this state
+ */
+ T getHistoryForState(T previousState);
+
+ /**
+ * @return true if the state can be persisted across activity restarts.
+ */
+ default boolean shouldDisableRestore() {
+ return hasFlag(FLAG_DISABLE_RESTORE);
+ }
+
+ /**
+ * Returns if the state has the provided flag
+ */
+ boolean hasFlag(int flagMask);
+}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
similarity index 76%
rename from src/com/android/launcher3/LauncherStateManager.java
rename to src/com/android/launcher3/statemanager/StateManager.java
index f6de48e..4447166 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.statemanager;
-import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import android.animation.Animator;
@@ -27,6 +26,7 @@
import android.os.Looper;
import android.util.Log;
+import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
@@ -38,83 +38,48 @@
import java.util.ArrayList;
/**
- * TODO: figure out what kind of tests we can write for this
- *
- * Things to test when changing the following class.
- * - Home from workspace
- * - from center screen
- * - from other screens
- * - Home from all apps
- * - from center screen
- * - from other screens
- * - Back from all apps
- * - from center screen
- * - from other screens
- * - Launch app from workspace and quit
- * - with back
- * - with home
- * - Launch app from all apps and quit
- * - with back
- * - with home
- * - Go to a screen that's not the default, then all
- * apps, and launch and app, and go back
- * - with back
- * -with home
- * - On workspace, long press power and go back
- * - with back
- * - with home
- * - On all apps, long press power and go back
- * - with back
- * - with home
- * - On workspace, power off
- * - On all apps, power off
- * - Launch an app and turn off the screen while in that app
- * - Go back with home key
- * - Go back with back key TODO: make this not go to workspace
- * - From all apps
- * - From workspace
- * - Enter and exit car mode (becase it causes an extra configuration changed)
- * - From all apps
- * - From the center workspace
- * - From another workspace
+ * Class to manage transitions between different states for a StatefulActivity based on different
+ * states
*/
-public class LauncherStateManager {
+public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
public static final String TAG = "StateManager";
private final AnimationState mConfig = new AnimationState();
private final Handler mUiHandler;
- private final Launcher mLauncher;
- private final ArrayList<StateListener> mListeners = new ArrayList<>();
+ private final StatefulActivity<STATE_TYPE> mActivity;
+ private final ArrayList<StateListener<STATE_TYPE>> mListeners = new ArrayList<>();
+ private final STATE_TYPE mBaseState;
// Animators which are run on properties also controlled by state animations.
private final AtomicAnimationFactory mAtomicAnimationFactory;
- private StateHandler[] mStateHandlers;
- private LauncherState mState = NORMAL;
+ private StateHandler<STATE_TYPE>[] mStateHandlers;
+ private STATE_TYPE mState;
- private LauncherState mLastStableState = NORMAL;
- private LauncherState mCurrentStableState = NORMAL;
+ private STATE_TYPE mLastStableState;
+ private STATE_TYPE mCurrentStableState;
- private LauncherState mRestState;
+ private STATE_TYPE mRestState;
- public LauncherStateManager(Launcher l) {
+ public StateManager(StatefulActivity<STATE_TYPE> l, STATE_TYPE baseState) {
mUiHandler = new Handler(Looper.getMainLooper());
- mLauncher = l;
-
+ mActivity = l;
+ mBaseState = baseState;
+ mState = mLastStableState = mCurrentStableState = baseState;
mAtomicAnimationFactory = l.createAtomicAnimationFactory();
}
- public LauncherState getState() {
+ public STATE_TYPE getState() {
return mState;
}
- public LauncherState getCurrentStableState() {
+ public STATE_TYPE getCurrentStableState() {
return mCurrentStableState;
}
public void dump(String prefix, PrintWriter writer) {
- writer.println(prefix + "LauncherState:");
+ writer.println(prefix + "StateManager:");
writer.println(prefix + "\tmLastStableState:" + mLastStableState);
writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState);
writer.println(prefix + "\tmState:" + mState);
@@ -124,7 +89,7 @@
public StateHandler[] getStateHandlers() {
if (mStateHandlers == null) {
- mStateHandlers = mLauncher.createStateHandlers();
+ mStateHandlers = mActivity.createStateHandlers();
}
return mStateHandlers;
}
@@ -141,29 +106,29 @@
* Returns true if the state changes should be animated.
*/
public boolean shouldAnimateStateChange() {
- return !mLauncher.isForceInvisible() && mLauncher.isStarted();
+ return !mActivity.isForceInvisible() && mActivity.isStarted();
}
/**
* @return {@code true} if the state matches the current state and there is no active
* transition to different state.
*/
- public boolean isInStableState(LauncherState state) {
+ public boolean isInStableState(STATE_TYPE state) {
return mState == state && mCurrentStableState == state
&& (mConfig.targetState == null || mConfig.targetState == state);
}
/**
- * @see #goToState(LauncherState, boolean, Runnable)
+ * @see #goToState(STATE_TYPE, boolean, Runnable)
*/
- public void goToState(LauncherState state) {
+ public void goToState(STATE_TYPE state) {
goToState(state, shouldAnimateStateChange());
}
/**
- * @see #goToState(LauncherState, boolean, Runnable)
+ * @see #goToState(STATE_TYPE, boolean, Runnable)
*/
- public void goToState(LauncherState state, boolean animated) {
+ public void goToState(STATE_TYPE state, boolean animated) {
goToState(state, animated, 0, null);
}
@@ -174,21 +139,21 @@
* true otherwise
* @paras onCompleteRunnable any action to perform at the end of the transition, of null.
*/
- public void goToState(LauncherState state, boolean animated, Runnable onCompleteRunnable) {
+ public void goToState(STATE_TYPE state, boolean animated, Runnable onCompleteRunnable) {
goToState(state, animated, 0, onCompleteRunnable);
}
/**
* Changes the Launcher state to the provided state after the given delay.
*/
- public void goToState(LauncherState state, long delay, Runnable onCompleteRunnable) {
+ public void goToState(STATE_TYPE state, long delay, Runnable onCompleteRunnable) {
goToState(state, true, delay, onCompleteRunnable);
}
/**
* Changes the Launcher state to the provided state after the given delay.
*/
- public void goToState(LauncherState state, long delay) {
+ public void goToState(STATE_TYPE state, long delay) {
goToState(state, true, delay, null);
}
@@ -212,10 +177,10 @@
}
}
- private void goToState(LauncherState state, boolean animated, long delay,
+ private void goToState(STATE_TYPE state, boolean animated, long delay,
final Runnable onCompleteRunnable) {
- animated &= Utilities.areAnimationsEnabled(mLauncher);
- if (mLauncher.isInState(state)) {
+ animated &= Utilities.areAnimationsEnabled(mActivity);
+ if (mActivity.isInState(state)) {
if (mConfig.currentAnimation == null) {
// Run any queued runnable
if (onCompleteRunnable != null) {
@@ -233,7 +198,7 @@
}
// Cancel the current animation. This will reset mState to mCurrentStableState, so store it.
- LauncherState fromState = mState;
+ STATE_TYPE fromState = mState;
mConfig.reset();
if (!animated) {
@@ -266,13 +231,13 @@
}
}
- private void goToStateAnimated(LauncherState state, LauncherState fromState,
+ private void goToStateAnimated(STATE_TYPE state, STATE_TYPE fromState,
Runnable onCompleteRunnable) {
- // Since state NORMAL can be reached from multiple states, just assume that the
+ // Since state mBaseState can be reached from multiple states, just assume that the
// transition plays in reverse and use the same duration as previous state.
- mConfig.duration = state == NORMAL
- ? fromState.getTransitionDuration(mLauncher)
- : state.getTransitionDuration(mLauncher);
+ mConfig.duration = state == mBaseState
+ ? fromState.getTransitionDuration(mActivity)
+ : state.getTransitionDuration(mActivity);
prepareForAtomicAnimation(fromState, state, mConfig);
AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).getAnim();
if (onCompleteRunnable != null) {
@@ -286,7 +251,7 @@
* - Setting interpolators for various animations included in the state transition.
* - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
*/
- public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
+ public void prepareForAtomicAnimation(STATE_TYPE fromState, STATE_TYPE toState,
StateAnimationConfig config) {
mAtomicAnimationFactory.prepareForAtomicAnimation(fromState, toState, config);
}
@@ -295,11 +260,11 @@
* Creates an animation representing atomic transitions between the provided states
*/
public AnimatorSet createAtomicAnimation(
- LauncherState fromState, LauncherState toState, StateAnimationConfig config) {
+ STATE_TYPE fromState, STATE_TYPE toState, StateAnimationConfig config) {
PendingAnimation builder = new PendingAnimation(config.duration);
prepareForAtomicAnimation(fromState, toState, config);
- for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
+ for (StateHandler handler : mActivity.getStateManager().getStateHandlers()) {
handler.setStateWithAnimation(toState, config, builder);
}
return builder.getAnim();
@@ -309,23 +274,23 @@
* Creates a {@link AnimatorPlaybackController} that can be used for a controlled
* state transition.
* @param state the final state for the transition.
- * @param duration intended duration for normal playback. Use higher duration for better
+ * @param duration intended duration for state playback. Use higher duration for better
* accuracy.
*/
public AnimatorPlaybackController createAnimationToNewWorkspace(
- LauncherState state, long duration) {
+ STATE_TYPE state, long duration) {
return createAnimationToNewWorkspace(state, duration, ANIM_ALL_COMPONENTS);
}
public AnimatorPlaybackController createAnimationToNewWorkspace(
- LauncherState state, long duration, @AnimationFlags int animComponents) {
+ STATE_TYPE state, long duration, @AnimationFlags int animComponents) {
StateAnimationConfig config = new StateAnimationConfig();
config.duration = duration;
config.animFlags = animComponents;
return createAnimationToNewWorkspace(state, config);
}
- public AnimatorPlaybackController createAnimationToNewWorkspace(LauncherState state,
+ public AnimatorPlaybackController createAnimationToNewWorkspace(STATE_TYPE state,
StateAnimationConfig config) {
config.userControlled = true;
mConfig.reset();
@@ -335,10 +300,10 @@
return mConfig.playbackController;
}
- private PendingAnimation createAnimationToNewWorkspaceInternal(final LauncherState state) {
+ private PendingAnimation createAnimationToNewWorkspaceInternal(final STATE_TYPE state) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "createAnimationToNewWorkspaceInternal: "
- + state.ordinal);
+ + state);
}
PendingAnimation builder = new PendingAnimation(mConfig.duration);
for (StateHandler handler : getStateHandlers()) {
@@ -355,7 +320,7 @@
@Override
public void onAnimationSuccess(Animator animator) {
if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "onAnimationSuccess: " + state.ordinal);
+ Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "onAnimationSuccess: " + state);
}
onStateTransitionEnd(state);
}
@@ -364,24 +329,24 @@
return builder;
}
- private void onStateTransitionStart(LauncherState state) {
+ private void onStateTransitionStart(STATE_TYPE state) {
mState = state;
- mLauncher.onStateSetStart(mState);
+ mActivity.onStateSetStart(mState);
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onStateTransitionStart(state);
}
}
- private void onStateTransitionEnd(LauncherState state) {
+ private void onStateTransitionEnd(STATE_TYPE state) {
// Only change the stable states after the transitions have finished
if (state != mCurrentStableState) {
mLastStableState = state.getHistoryForState(mCurrentStableState);
mCurrentStableState = state;
}
- mLauncher.onStateSetEnd(state);
- if (state == NORMAL) {
+ mActivity.onStateSetEnd(state);
+ if (state == mBaseState) {
setRestState(null);
}
@@ -390,7 +355,7 @@
}
}
- public LauncherState getLastState() {
+ public STATE_TYPE getLastState() {
return mLastStableState;
}
@@ -402,15 +367,15 @@
if (mState.shouldDisableRestore()) {
goToState(getRestState());
// Reset history
- mLastStableState = NORMAL;
+ mLastStableState = mBaseState;
}
}
- public LauncherState getRestState() {
- return mRestState == null ? NORMAL : mRestState;
+ public STATE_TYPE getRestState() {
+ return mRestState == null ? mBaseState : mRestState;
}
- public void setRestState(LauncherState restState) {
+ public void setRestState(STATE_TYPE restState) {
mRestState = restState;
}
@@ -505,13 +470,14 @@
}
}
- private static class AnimationState extends StateAnimationConfig implements AnimatorListener {
+ private static class AnimationState<STATE_TYPE> extends StateAnimationConfig
+ implements AnimatorListener {
private static final StateAnimationConfig DEFAULT = new StateAnimationConfig();
public AnimatorPlaybackController playbackController;
public AnimatorSet currentAnimation;
- public LauncherState targetState;
+ public STATE_TYPE targetState;
// Id to keep track of config changes, to tie an animation with the corresponding request
public int changeId = 0;
@@ -546,7 +512,7 @@
}
}
- public void setAnimation(AnimatorSet animation, LauncherState targetState) {
+ public void setAnimation(AnimatorSet animation, STATE_TYPE targetState) {
currentAnimation = animation;
this.targetState = targetState;
currentAnimation.addListener(this);
@@ -562,31 +528,31 @@
public void onAnimationRepeat(Animator animator) { }
}
- public interface StateHandler {
+ public interface StateHandler<STATE_TYPE> {
/**
* Updates the UI to {@param state} without any animations
*/
- void setState(LauncherState state);
+ void setState(STATE_TYPE state);
/**
* Sets the UI to {@param state} by animating any changes.
*/
void setStateWithAnimation(
- LauncherState toState, StateAnimationConfig config, PendingAnimation animation);
+ STATE_TYPE toState, StateAnimationConfig config, PendingAnimation animation);
}
- public interface StateListener {
+ public interface StateListener<STATE_TYPE> {
- default void onStateTransitionStart(LauncherState toState) { }
+ default void onStateTransitionStart(STATE_TYPE toState) { }
- default void onStateTransitionComplete(LauncherState finalState) { }
+ default void onStateTransitionComplete(STATE_TYPE finalState) { }
}
/**
* Factory class to configure and create atomic animations.
*/
- public static class AtomicAnimationFactory {
+ public static class AtomicAnimationFactory<STATE_TYPE> {
private final Animator[] mStateElementAnimators;
@@ -622,6 +588,6 @@
* - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
*/
public void prepareForAtomicAnimation(
- LauncherState fromState, LauncherState toState, StateAnimationConfig config) { }
+ STATE_TYPE fromState, STATE_TYPE toState, StateAnimationConfig config) { }
}
}
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
new file mode 100644
index 0000000..0a1607c
--- /dev/null
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.statemanager;
+
+import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
+
+import android.os.Handler;
+
+import androidx.annotation.CallSuper;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
+import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.views.BaseDragLayer;
+
+/**
+ * Abstract activity with state management
+ * @param <STATE_TYPE> Type of state object
+ */
+public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
+ extends BaseDraggingActivity {
+
+ public final Handler mHandler = new Handler();
+ private final Runnable mHandleDeferredResume = this::handleDeferredResume;
+ private boolean mDeferredResumePending;
+
+ /**
+ * Create handlers to control the property changes for this activity
+ */
+ protected abstract StateHandler<STATE_TYPE>[] createStateHandlers();
+
+ /**
+ * Returns true if the activity is in the provided state
+ */
+ public boolean isInState(STATE_TYPE state) {
+ return getStateManager().getState() == state;
+ }
+
+ /**
+ * Returns the state manager for this activity
+ */
+ public abstract StateManager<STATE_TYPE> getStateManager();
+
+ /**
+ * Called when transition to the state starts
+ */
+ @CallSuper
+ public void onStateSetStart(STATE_TYPE state) {
+ if (mDeferredResumePending) {
+ handleDeferredResume();
+ }
+ }
+
+ /**
+ * Called when transition to state ends
+ */
+ public void onStateSetEnd(STATE_TYPE state) { }
+
+ /**
+ * Creates a factory for atomic state animations
+ */
+ public AtomicAnimationFactory<STATE_TYPE> createAtomicAnimationFactory() {
+ return new AtomicAnimationFactory(0);
+ }
+
+ @Override
+ public void reapplyUi() {
+ reapplyUi(true /* cancelCurrentAnimation */);
+ }
+
+ /**
+ * Re-applies if any state transition is not running, optionally cancelling
+ * the transition if requested.
+ */
+ public void reapplyUi(boolean cancelCurrentAnimation) {
+ getStateManager().reapplyState(cancelCurrentAnimation);
+ }
+
+ @Override
+ protected void onStop() {
+ BaseDragLayer dragLayer = getDragLayer();
+ final boolean wasActive = isUserActive();
+ final STATE_TYPE origState = getStateManager().getState();
+ final int origDragLayerChildCount = dragLayer.getChildCount();
+ super.onStop();
+
+ getStateManager().moveToRestState();
+
+ // Workaround for b/78520668, explicitly trim memory once UI is hidden
+ onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
+
+ if (wasActive) {
+ // The expected condition is that this activity is stopped because the device goes to
+ // sleep and the UI may have noticeable changes.
+ dragLayer.post(() -> {
+ if ((!getStateManager().isInStableState(origState)
+ // The drag layer may be animating (e.g. dismissing QSB).
+ || dragLayer.getAlpha() < 1
+ // Maybe an ArrowPopup is closed.
+ || dragLayer.getChildCount() != origDragLayerChildCount)) {
+ onUiChangedWhileSleeping();
+ }
+ });
+ }
+ }
+
+ /**
+ * Called if the Activity UI changed while the activity was not visible
+ */
+ protected void onUiChangedWhileSleeping() { }
+
+ private void handleDeferredResume() {
+ if (hasBeenResumed() && !getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE)) {
+ onDeferredResumed();
+ addActivityFlags(ACTIVITY_STATE_DEFERRED_RESUMED);
+
+ mDeferredResumePending = false;
+ } else {
+ mDeferredResumePending = true;
+ }
+ }
+
+ /**
+ * Called want the activity has stayed resumed for 1 frame.
+ */
+ protected void onDeferredResumed() { }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ mHandler.removeCallbacks(mHandleDeferredResume);
+ Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
+ }
+}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index dd9c3fa..52e2ab8 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -164,6 +164,9 @@
@Override
public final boolean onControllerTouchEvent(MotionEvent ev) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "onControllerTouchEvent");
+ }
return mDetector.onTouchEvent(ev);
}
diff --git a/src/com/android/launcher3/touch/BaseSwipeDetector.java b/src/com/android/launcher3/touch/BaseSwipeDetector.java
index 1276ece..01b33d8 100644
--- a/src/com/android/launcher3/touch/BaseSwipeDetector.java
+++ b/src/com/android/launcher3/touch/BaseSwipeDetector.java
@@ -26,6 +26,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.testing.TestProtocol;
+
import java.util.LinkedList;
import java.util.Queue;
@@ -173,6 +175,9 @@
if (mState != ScrollState.DRAGGING && shouldScrollStart(mDisplacement)) {
setState(ScrollState.DRAGGING);
}
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "before report dragging");
+ }
if (mState == ScrollState.DRAGGING) {
reportDragging(ev);
}
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 5007ca0..d02c731 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -33,7 +33,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.OverScroller;
@@ -74,8 +73,8 @@
}
@Override
- public boolean isGoingUp(float displacement) {
- return displacement > 0;
+ public boolean isGoingUp(float displacement, boolean isRtl) {
+ return isRtl ? displacement < 0 : displacement > 0;
}
@Override
@@ -226,13 +225,13 @@
}
@Override
- public int getShortEdgeLength(DeviceProfile dp) {
- return dp.heightPx;
+ public int getTaskDismissDirectionFactor() {
+ return 1;
}
@Override
- public int getTaskDismissDirectionFactor() {
- return 1;
+ public int getTaskDragDisplacementFactor(boolean isRtl) {
+ return isRtl ? 1 : -1;
}
@Override
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index cdfe6d5..2e0268d 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -75,8 +75,8 @@
int getScrollOffsetStart(View view, Rect insets);
int getScrollOffsetEnd(View view, Rect insets);
SingleAxisSwipeDetector.Direction getOppositeSwipeDirection();
- int getShortEdgeLength(DeviceProfile dp);
int getTaskDismissDirectionFactor();
+ int getTaskDragDisplacementFactor(boolean isRtl);
ChildBounds getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild);
void setMaxScroll(AccessibilityEvent event, int maxScroll);
boolean getRecentsRtlSetting(Resources resources);
@@ -91,7 +91,7 @@
void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y);
void scrollerStartScroll(OverScroller scroller, int newPosition);
void getCurveProperties(PagedView view, Rect insets, CurveProperties out);
- boolean isGoingUp(float displacement);
+ boolean isGoingUp(float displacement, boolean isRtl);
boolean isLayoutNaturalToLauncher();
float getTaskMenuX(float x, View thumbnailView);
float getTaskMenuY(float y, View thumbnailView);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 25dc1f6..2fc7a9f 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -32,7 +32,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.OverScroller;
@@ -73,7 +72,8 @@
}
@Override
- public boolean isGoingUp(float displacement) {
+ public boolean isGoingUp(float displacement, boolean isRtl) {
+ // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
return displacement < 0;
}
@@ -223,13 +223,14 @@
}
@Override
- public int getShortEdgeLength(DeviceProfile dp) {
- return dp.widthPx;
+ public int getTaskDismissDirectionFactor() {
+ return -1;
}
@Override
- public int getTaskDismissDirectionFactor() {
- return -1;
+ public int getTaskDragDisplacementFactor(boolean isRtl) {
+ // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+ return 1;
}
@Override
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index dde2829..4c1700e 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -32,6 +32,11 @@
}
@Override
+ public int getTaskDragDisplacementFactor(boolean isRtl) {
+ return isRtl ? -1 : 1;
+ }
+
+ @Override
public boolean getRecentsRtlSetting(Resources resources) {
return Utilities.isRtl(resources);
}
@@ -60,8 +65,8 @@
}
@Override
- public boolean isGoingUp(float displacement) {
- return displacement < 0;
+ public boolean isGoingUp(float displacement, boolean isRtl) {
+ return isRtl ? displacement > 0 : displacement < 0;
}
@Override
@@ -82,13 +87,8 @@
}
@Override
- public int getClearAllScrollOffset(View view, boolean isRtl) {
- return (isRtl ? view.getPaddingTop() : - view.getPaddingBottom()) / 2;
- }
-
- @Override
public void setPrimaryAndResetSecondaryTranslate(View view, float translation) {
view.setTranslationX(0);
- view.setTranslationY(-translation);
+ view.setTranslationY(translation);
}
}
diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
index d725486..875eefb 100644
--- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
+++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.graphics.PointF;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -24,6 +25,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestProtocol;
/**
* One dimensional scroll/drag/swipe gesture detector (either HORIZONTAL or VERTICAL).
@@ -103,6 +105,11 @@
super(config, isRtl);
mListener = l;
mDir = dir;
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "SingleAxisSwipeDetector.ctor "
+ + l.getClass().getSimpleName()
+ + " @ " + android.util.Log.getStackTraceString(new Throwable()));
+ }
}
public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
@@ -154,6 +161,10 @@
@Override
protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "SingleAxisSwipeDetector "
+ + mListener.getClass().getSimpleName());
+ }
mListener.onDrag(mDir.extractDirection(displacement),
mDir.extractOrthogonalDirection(displacement), event);
}
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index da631bd..e6de06d 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -167,8 +167,8 @@
@Override
public void onLongPress(MotionEvent event) {
- TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Workspace.longPress");
if (mLongPressState == STATE_REQUESTED) {
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Workspace.longPress");
if (canHandleLongPress()) {
mLongPressState = STATE_PENDING_PARENT_INFORM;
mWorkspace.getParent().requestDisallowInterceptTouchEvent(true);
diff --git a/src/com/android/launcher3/util/LogConfig.java b/src/com/android/launcher3/util/LogConfig.java
index b54074e..528a6e9 100644
--- a/src/com/android/launcher3/util/LogConfig.java
+++ b/src/com/android/launcher3/util/LogConfig.java
@@ -12,6 +12,8 @@
public class LogConfig {
// These are list of strings that can be used to replace TAGNAME.
+ public static final String STATSLOG = "StatsLog";
+
/**
* After this tag is turned on, whenever there is n user event, debug information is
* printed out to logcat.
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index baa1eee..1620289 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -21,7 +21,6 @@
import androidx.annotation.StringDef;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherStateManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -71,13 +70,10 @@
protected final T mLauncher;
protected final SharedPreferences mSharedPrefs;
- protected final LauncherStateManager mStateManager;
- public OnboardingPrefs(T launcher, SharedPreferences sharedPrefs,
- LauncherStateManager stateManager) {
+ public OnboardingPrefs(T launcher, SharedPreferences sharedPrefs) {
mLauncher = launcher;
mSharedPrefs = sharedPrefs;
- mStateManager = stateManager;
}
/** @return The number of times we have seen the given event. */
diff --git a/src/com/android/launcher3/util/WindowBounds.java b/src/com/android/launcher3/util/WindowBounds.java
new file mode 100644
index 0000000..3c2fb62
--- /dev/null
+++ b/src/com/android/launcher3/util/WindowBounds.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Utility class to hold information about window position and layout
+ */
+public class WindowBounds {
+
+ public final Rect bounds;
+ public final Rect insets;
+ public final Point availableSize;
+
+ public WindowBounds(Rect bounds, Rect insets) {
+ this.bounds = bounds;
+ this.insets = insets;
+ availableSize = new Point(bounds.width() - insets.left - insets.right,
+ bounds.height() - insets.top - insets.bottom);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof WindowBounds)) {
+ return false;
+ }
+ WindowBounds other = (WindowBounds) obj;
+ return other.bounds.equals(bounds) && other.insets.equals(insets);
+ }
+}
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index da874cf..a2c7d14 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -62,10 +62,10 @@
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.uioverrides.WallpaperColorInfo.OnChangeListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -116,7 +116,8 @@
private final AccessibilityManager mAM;
protected final int mEndScrim;
- private final StateListener mAccessibilityLauncherStateListener = new StateListener() {
+ private final StateListener<LauncherState> mAccessibilityLauncherStateListener =
+ new StateListener<LauncherState>() {
@Override
public void onStateTransitionComplete(LauncherState finalState) {
setImportantForAccessibility(finalState == ALL_APPS
@@ -383,7 +384,7 @@
@Override
public void onAccessibilityStateChanged(boolean enabled) {
- LauncherStateManager stateManager = mLauncher.getStateManager();
+ StateManager<LauncherState> stateManager = mLauncher.getStateManager();
stateManager.removeStateListener(mAccessibilityLauncherStateListener);
if (enabled) {
diff --git a/src/com/android/launcher3/views/WorkEduView.java b/src/com/android/launcher3/views/WorkEduView.java
index 859b9d0..d35a38f 100644
--- a/src/com/android/launcher3/views/WorkEduView.java
+++ b/src/com/android/launcher3/views/WorkEduView.java
@@ -32,19 +32,19 @@
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsPagedView;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.userevent.nano.LauncherLogProto;
/**
* On boarding flow for users right after setting up work profile
*/
-public class WorkEduView extends AbstractSlideInView implements Insettable, StateListener {
+public class WorkEduView extends AbstractSlideInView
+ implements Insettable, StateListener<LauncherState> {
private static final int DEFAULT_CLOSE_DURATION = 200;
public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu";
@@ -185,8 +185,8 @@
/**
* Checks if user has not seen onboarding UI yet and shows it when user navigates to all apps
*/
- public static LauncherStateManager.StateListener showEduFlowIfNeeded(Launcher launcher,
- @Nullable LauncherStateManager.StateListener oldListener) {
+ public static StateListener<LauncherState> showEduFlowIfNeeded(Launcher launcher,
+ @Nullable StateListener<LauncherState> oldListener) {
if (oldListener != null) {
launcher.getStateManager().removeStateListener(oldListener);
}
@@ -195,7 +195,7 @@
return null;
}
- LauncherStateManager.StateListener listener = new LauncherStateManager.StateListener() {
+ StateListener<LauncherState> listener = new StateListener<LauncherState>() {
@Override
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState != LauncherState.ALL_APPS) return;
diff --git a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
index a4e7daa..ed42bc4 100644
--- a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
@@ -218,7 +218,7 @@
}
@Override
- public void getVisualDragBounds(Rect bounds) {
+ public void getWorkspaceVisualDragBounds(Rect bounds) {
int width = (int) (getMeasuredWidth() * mScaleToFit);
int height = (int) (getMeasuredHeight() * mScaleToFit);
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 4a0b4ef..bef91d2 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -55,7 +55,7 @@
private static final int FADE_IN_DURATION_MS = 90;
/** Widget cell width is calculated by multiplying this factor to grid cell width. */
- private static final float WIDTH_SCALE = 2.6f;
+ private static final float WIDTH_SCALE = 3f;
/** Widget preview width is calculated by multiplying this factor to the widget cell width. */
private static final float PREVIEW_SCALE = 0.8f;
@@ -104,7 +104,7 @@
}
private void setContainerWidth() {
- mCellSize = (int) (mDeviceProfile.cellWidthPx * WIDTH_SCALE);
+ mCellSize = (int) (mDeviceProfile.allAppsIconSizePx * WIDTH_SCALE);
mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE);
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 160daef..7cce044 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -37,6 +37,8 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.StrictMode;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -49,11 +51,11 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.Utilities;
import com.android.launcher3.common.WidgetUtils;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
import com.android.launcher3.tapl.TestHelpers;
@@ -276,6 +278,15 @@
mTargetContext = InstrumentationRegistry.getTargetContext();
mTargetPackage = mTargetContext.getPackageName();
mLauncherPid = mLauncher.getPid();
+
+ UserManager userManager = mTargetContext.getSystemService(UserManager.class);
+ if (userManager != null) {
+ for (UserHandle userHandle : userManager.getUserProfiles()) {
+ if (!userHandle.isSystem()) {
+ mDevice.executeShellCommand("pm remove-user " + userHandle.getIdentifier());
+ }
+ }
+ }
}
@After
@@ -297,6 +308,7 @@
clearPackageData(mDevice.getLauncherPackageName());
mLauncher.enableDebugTracing();
mLauncherPid = mLauncher.getPid();
+ mLauncher.waitForLauncherInitialized();
}
}
@@ -525,7 +537,7 @@
private static void checkLauncherIntegrity(
Launcher launcher, ContainerType expectedContainerType) {
if (launcher != null) {
- final LauncherStateManager stateManager = launcher.getStateManager();
+ final StateManager<LauncherState> stateManager = launcher.getStateManager();
final LauncherState stableState = stateManager.getCurrentStableState();
assertTrue("Stable state != state: " + stableState.getClass().getSimpleName() + ", "
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 94ab780..ce94a3e 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -86,6 +86,11 @@
zeroButtonToOverviewGestureStartsInLauncher()
? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
: LauncherInstrumentation.GestureScope.OUTSIDE;
+
+ // b/156044202
+ mLauncher.log("Hierarchy before swiping up to overview:");
+ mLauncher.dumpViewHierarchy();
+
mLauncher.sendPointer(
downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
mLauncher.executeAndWaitForEvent(
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 7e344c2..14212be 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -428,7 +428,7 @@
if (sCheckingEvents) {
sCheckingEvents = false;
if (checkEvents) {
- final String eventMismatch = sEventChecker.verify(0);
+ final String eventMismatch = sEventChecker.verify(0, false);
if (eventMismatch != null) {
message = message + ", having produced " + eventMismatch;
}
@@ -436,6 +436,13 @@
sEventChecker.finishNoWait();
}
}
+ // b/156287114
+ try {
+ log("Input: " + mDevice.executeShellCommand("dumpsys input"));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
log("Hierarchy dump for: " + message);
dumpViewHierarchy();
@@ -625,6 +632,7 @@
mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(
e -> Log.d("b/155926212", e.toString()));
try (LauncherInstrumentation.Closable e = eventsCheck()) {
+ waitForLauncherInitialized();
// Click home, then wait for any accessibility event, then wait until accessibility
// events stop.
// We need waiting for any accessibility event generated after pressing Home because
@@ -1310,7 +1318,7 @@
if (sCheckingEvents) {
sCheckingEvents = false;
if (mCheckEventsForSuccessfulGestures) {
- final String message = sEventChecker.verify(WAIT_TIME_MS);
+ final String message = sEventChecker.verify(WAIT_TIME_MS, true);
if (message != null) {
checkForAnomaly();
Assert.fail(formatSystemHealthMessage(
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index bbba4fb..79d20ac 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -168,7 +168,7 @@
Log.d(TestProtocol.TAPL_EVENTS_TAG, mFinishCommand);
}
- String verify(long waitForExpectedCountMs) {
+ String verify(long waitForExpectedCountMs, boolean successfulGesture) {
finishSync(waitForExpectedCountMs);
final StringBuilder sb = new StringBuilder();
@@ -179,7 +179,8 @@
List<String> actual = new ArrayList<>(mEvents.getNonNull(sequence));
Log.d(SKIP_EVENTS_TAG, "Verifying events");
final int mismatchPosition = getMismatchPosition(expectedEvents.getValue(), actual);
- hasMismatches = hasMismatches || mismatchPosition != -1;
+ hasMismatches = hasMismatches
+ || mismatchPosition != -1 && !ignoreMistatch(successfulGesture, sequence);
formatSequenceWithMismatch(
sb,
sequence,
@@ -190,7 +191,8 @@
// Check for unexpected event sequences in the actual data.
for (String actualNamedSequence : mEvents.keySet()) {
if (!mExpectedEvents.containsKey(actualNamedSequence)) {
- hasMismatches = true;
+ hasMismatches = hasMismatches
+ || !ignoreMistatch(successfulGesture, actualNamedSequence);
formatSequenceWithMismatch(
sb,
actualNamedSequence,
@@ -203,6 +205,13 @@
return hasMismatches ? "mismatching events: " + sb.toString() : null;
}
+ // Workaround for b/154157191
+ private static boolean ignoreMistatch(boolean successfulGesture, String sequence) {
+ // b/156287114
+ return false;
+// return TestProtocol.SEQUENCE_TIS.equals(sequence) && successfulGesture;
+ }
+
// If the list of actual events matches the list of expected events, returns -1, otherwise
// the position of the mismatch.
private static int getMismatchPosition(List<Pattern> expected, List<String> actual) {