Merge "Only set spring end value if animation wasn't canceled" into ub-launcher3-rvc-dev
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/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/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 a7e7d3a..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.overview_actions_top_margin);
- 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/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 1dd5fb7..f38ff10 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -31,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;
@@ -47,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;
}
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 76c6060..bbee67c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -44,12 +44,12 @@
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.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;
@@ -60,7 +60,6 @@
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
-import com.android.quickstep.util.WindowSizeStrategy;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -76,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";
@@ -97,7 +96,7 @@
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 final TaskViewSimulator mTaskViewSimulator;
@@ -132,15 +131,14 @@
private boolean mRecentsViewScrollLinked = false;
protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState, InputConsumerController inputConsumer,
- WindowSizeStrategy windowSizeStrategy) {
+ GestureState gestureState, InputConsumerController inputConsumer) {
mContext = context;
mDeviceState = deviceState;
mGestureState = gestureState;
mActivityInterface = gestureState.getActivityInterface();
mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
mInputConsumer = inputConsumer;
- mTaskViewSimulator = new TaskViewSimulator(context, windowSizeStrategy);
+ mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
}
/**
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 324c390..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,23 +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.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.Rect;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
+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;
@@ -44,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();
@@ -68,17 +71,6 @@
}
@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
@@ -89,15 +81,13 @@
public AnimationFactory prepareRecentsUI(
boolean activityVisible, 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);
- rv.setInOverviewState(false);
return new AnimationFactory() {
@@ -115,23 +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(() ->
- rv.setInOverviewState(controller.getInterpolatedProgress() > 0.5));
+ controller.setEndAction(() -> activity.getStateManager().goToState(
+ controller.getInterpolatedProgress() > 0.5 ? DEFAULT : BACKGROUND_APP));
callback.accept(controller);
}
};
@@ -147,7 +133,7 @@
@Nullable
@Override
public RecentsActivity getCreatedActivity() {
- return BaseRecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
+ return RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
@@ -202,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 8ce6bbc..db41bd6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -24,7 +24,6 @@
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
-import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
@@ -37,10 +36,12 @@
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.util.ObjectWrapper;
+import com.android.quickstep.BaseActivityInterface.AnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.util.RectFSpringAnim;
@@ -103,10 +104,16 @@
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) {
- super(context, deviceState, gestureState, inputConsumer, FALLBACK_RECENTS_SIZE_STRATEGY);
+ super(context, deviceState, gestureState, inputConsumer);
mInQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture;
mContinuingLastGesture = continuingLastGesture;
@@ -165,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());
@@ -178,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;
}
@@ -260,6 +302,7 @@
}
applyWindowTransform();
+ updateLauncherTransitionProgress();
}
@Override
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 9ff5d942..d466296 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -21,16 +21,21 @@
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.content.Context;
+import android.content.res.Resources;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.util.Log;
import android.view.MotionEvent;
@@ -44,6 +49,7 @@
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;
@@ -60,7 +66,6 @@
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
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;
@@ -69,11 +74,18 @@
/**
* {@link BaseActivityInterface} for the in-launcher recents.
*/
-public final class LauncherActivityInterface implements BaseActivityInterface<Launcher> {
+public final class LauncherActivityInterface extends
+ BaseActivityInterface<LauncherState, Launcher> {
+
+ 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;
@@ -84,24 +96,12 @@
}
@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
@@ -113,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
@@ -337,15 +329,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) {
@@ -360,23 +343,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) {
@@ -404,4 +370,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 e7fe142..1830ccb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -35,7 +35,6 @@
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;
@@ -60,7 +59,6 @@
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;
@@ -69,6 +67,7 @@
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;
@@ -198,7 +197,7 @@
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
InputConsumerController inputConsumer) {
- super(context, deviceState, gestureState, inputConsumer, LAUNCHER_ACTIVITY_SIZE_STRATEGY);
+ super(context, deviceState, gestureState, inputConsumer);
mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
@@ -684,7 +683,7 @@
setTargetAlphaProvider(LauncherSwipeHandler::getHiddenTargetAlpha);
}
- BaseDraggingActivity activity = mActivityInterface.getCreatedActivity();
+ StatefulActivity activity = mActivityInterface.getCreatedActivity();
return activity == null ? InputConsumer.NO_OP
: new OverviewInputConsumer(mGestureState, activity, null, true);
}
@@ -724,7 +723,7 @@
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return false;
}
- if (mGestureState.getEndTarget() == NEW_TASK
+ if (mStateCallback.hasStates(STATE_START_NEW_TASK)
&& appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) {
reset();
return true;
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/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 4b2fc75..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) { }
@@ -697,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;
}
@@ -746,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 c82d4b5..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
@@ -24,8 +24,8 @@
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;
@@ -41,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;
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 b8f0f4d..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,6 +31,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.AnimatedFloat;
+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;
@@ -52,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();
@@ -81,7 +80,7 @@
private boolean mLayoutValid = false;
private boolean mScrollValid = false;
- public TaskViewSimulator(Context context, WindowSizeStrategy sizeStrategy) {
+ public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
mContext = context;
mSizeStrategy = sizeStrategy;
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 9d7efc4..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,17 +25,14 @@
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;
@@ -49,6 +46,7 @@
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;
@@ -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 7201b02..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
@@ -145,8 +145,6 @@
/** Updates vertical margins for different navigation mode. */
public void updateVerticalMarginForNavModeChange(Mode mode) {
- int topMargin = getResources()
- .getDimensionPixelSize(R.dimen.overview_actions_top_margin);
int bottomMargin = 0;
if (mode == Mode.THREE_BUTTONS) {
bottomMargin = getResources()
@@ -157,6 +155,6 @@
}
LayoutParams params = (LayoutParams) getLayoutParams();
params.setMargins(
- params.leftMargin, topMargin, params.rightMargin, bottomMargin);
+ 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 700af97..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
@@ -115,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;
@@ -126,8 +127,8 @@
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;
@@ -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);
@@ -510,6 +512,7 @@
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
mIPinnedStackAnimationListener);
mOrientationState.initListeners();
+ SplitScreenBounds.INSTANCE.addOnChangeListener(this);
}
@Override
@@ -523,6 +526,7 @@
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
mIdp.removeOnChangeListener(this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
+ SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
mIPinnedStackAnimationListener.setActivity(null);
mOrientationState.destroyListeners();
}
@@ -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) {
@@ -762,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--) {
@@ -829,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();
@@ -1589,7 +1608,12 @@
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,
@@ -2145,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);
}
}
@@ -2165,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 da9468e..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
@@ -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/dimens.xml b/quickstep/res/values/dimens.xml
index 5c30651..ee55e61 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -24,7 +24,6 @@
<!-- Overrideable in overlay that provides the Overview Actions. -->
<dimen name="overview_actions_height">66dp</dimen>
- <dimen name="overview_actions_top_margin">44dp</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>
@@ -63,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:
@@ -79,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>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 8368817..c841170 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -129,12 +129,12 @@
<!-- 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] -->
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 43328b6..2699a91 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -15,23 +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.os.Build;
import android.view.MotionEvent;
-import android.view.View;
import android.view.animation.Interpolator;
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.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;
@@ -42,84 +53,232 @@
* 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);
+ 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);
- AnimationFactory prepareRecentsUI(
+ 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();
+ }
+
+ public void onSwipeUpToHomeComplete() { }
+
+ public abstract void onAssistantVisibilityChanged(float visibility);
+
+ public abstract AnimationFactory prepareRecentsUI(
boolean activityVisible, Consumer<AnimatorPlaybackController> callback);
- ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener);
+ 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 {
+ /**
+ * 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) { }
@@ -137,4 +296,8 @@
*/
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
}
+
+ 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 9b515ae..295b465 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -17,10 +17,12 @@
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;
@@ -32,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 {
/**
@@ -189,7 +192,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;
}
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 f3cefb9..58870ed 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -68,13 +68,16 @@
@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;
}
@Override
Integer getActionTextButtonStringId() {
+ if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
+ return R.string.gesture_tutorial_action_button_label_settings;
+ }
return null;
}
@@ -86,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 a98aad1..8889560 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -64,24 +64,46 @@
}
/**
- * Logs an event and accompanying {@link ItemInfo}
+ * Logs a {@link LauncherEvent}.
*/
+ @Override
+ 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 LauncherAtom.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, String.format("\n%s\n%s", event.name(), itemInfo));
+ 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.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 */,
@@ -118,6 +140,7 @@
}
private class SnapshotWorker extends BaseModelUpdateTask {
+
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
IntSparseArrayMap<FolderInfo> folders = dataModel.folders.clone();
@@ -140,6 +163,7 @@
}
}
}
+
private static void writeSnapshot(LauncherAtom.ItemInfo itemInfo) {
if (IS_VERBOSE) {
Log.d(TAG, "\nwriteSnapshot:" + itemInfo);
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/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index fffbb34..e03f4b8 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -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;
@@ -120,7 +122,7 @@
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();
@@ -132,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();
@@ -361,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);
}
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 81a1924..0000000
--- a/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
+++ /dev/null
@@ -1,183 +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.getMode;
-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;
-import com.android.quickstep.SysUINavigationMode.Mode;
-
-/**
- * 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.
- 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 actionsTopMargin = res.getDimensionPixelSize(
- R.dimen.overview_actions_top_margin);
- float actionsHeight = actionsTopMargin + 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);
- }
- }
- }
- };
-
- 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/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 79ed2b8..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;
@@ -744,11 +745,12 @@
}
@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/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/dragndrop/DraggableView.java b/src/com/android/launcher3/dragndrop/DraggableView.java
index 287c781..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)
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 93208d4..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;
@@ -62,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;
@@ -410,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);
@@ -421,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);
}
@@ -434,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;
}
@@ -445,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);
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 634d07e..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.getSourceVisualDragBounds(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);
}
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/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index fdd32b8..b240f0b 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -18,13 +18,13 @@
import android.content.Context;
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 id is defined here.
+ * 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.
*/
@@ -37,25 +37,58 @@
public enum LauncherEvent implements EventEnum {
@LauncherUiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
LAUNCHER_APP_LAUNCH_TAP(338),
+
@LauncherUiEvent(doc = "Task launched from overview using TAP")
LAUNCHER_TASK_LAUNCH_TAP(339),
+
@LauncherUiEvent(doc = "Task launched from overview using SWIPE DOWN")
LAUNCHER_TASK_LAUNCH_SWIPE_DOWN(340),
+
@LauncherUiEvent(doc = "TASK dismissed from overview using SWIPE UP")
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;
}
@@ -78,14 +111,32 @@
}
/**
- * Logs an event and accompanying {@link LauncherAtom.ItemInfo}
+ * Logs a {@link LauncherEvent}.
*/
- public void log(LauncherEvent event, InstanceId instanceId, LauncherAtom.ItemInfo itemInfo) { }
- public void log(LauncherEvent event, LauncherAtom.ItemInfo itemInfo) { }
+ public void log(LauncherEvent event) {
+ }
+ /**
+ * Logs an event and accompanying {@link InstanceId}.
+ */
+ 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 logSnapshot() {
+ }
}
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/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/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/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/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/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 6e9c5a0..14212be 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -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();
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index ac90b1b..79d20ac 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -207,7 +207,9 @@
// Workaround for b/154157191
private static boolean ignoreMistatch(boolean successfulGesture, String sequence) {
- return TestProtocol.SEQUENCE_TIS.equals(sequence) && successfulGesture;
+ // 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