diff --git a/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml b/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml index bf76f29dec..c02fe2cdce 100644 --- a/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml +++ b/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml @@ -21,5 +21,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "無法啟動活動" "新增應用程式捷徑" - "設定桌布" + "套用桌布" diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java index fe159b5f02..92900f2168 100644 --- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -25,6 +25,7 @@ import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MOD import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.app.ActivityOptions; +import android.content.Context; import android.os.Handler; import android.util.Log; @@ -151,7 +152,7 @@ final class AppToOverviewAnimationProvider imple } @Override - public ActivityOptions toActivityOptions(Handler handler, long duration) { + public ActivityOptions toActivityOptions(Handler handler, long duration, Context context) { LauncherAnimationRunner runner = new LauncherAnimationRunner(handler, false /* startAtFrontOfQueue */) { @@ -165,7 +166,7 @@ final class AppToOverviewAnimationProvider imple ); return; } - result.setAnimation(createWindowAnimation(targetCompats)); + result.setAnimation(createWindowAnimation(targetCompats), context); } }; return ActivityOptionsCompat.makeRemoteAnimation( diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java index 900b94e186..577b175661 100644 --- a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -34,6 +34,8 @@ import android.view.MotionEvent; import com.android.launcher3.Utilities; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.LooperExecutor; +import com.android.launcher3.util.UiThreadHelper; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; @@ -137,6 +139,9 @@ public class TouchInteractionService extends Service { return sConnected; } + public static final LooperExecutor BACKGROUND_EXECUTOR = + new LooperExecutor(UiThreadHelper.getBackgroundLooper()); + private RecentsModel mRecentsModel; private OverviewComponentObserver mOverviewComponentObserver; private OverviewCommandHelper mOverviewCommandHelper; @@ -180,4 +185,8 @@ public class TouchInteractionService extends Service { } return mMyBinder; } + + public static boolean isInitialized() { + return true; + } } diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java index 60320d63be..e1b71a0b5f 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java +++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java @@ -22,6 +22,7 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Process; import android.os.UserHandle; +import androidx.annotation.NonNull; /** * This class will be moved to androidx library. There shouldn't be any dependency outside @@ -154,7 +155,7 @@ public class BaseIconFactory implements AutoCloseable { * @param scale returns the scale result from normalization * @return a bitmap suitable for disaplaying as an icon at various system UIs. */ - public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, + public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon, UserHandle user, boolean shrinkNonAdaptiveIcons, boolean isInstantApp, float[] scale) { if (scale == null) { scale = new float[1]; @@ -204,8 +205,11 @@ public class BaseIconFactory implements AutoCloseable { mDisableColorExtractor = true; } - private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, boolean shrinkNonAdaptiveIcons, - RectF outIconBounds, float[] outScale) { + private Drawable normalizeAndWrapToAdaptiveIcon(@NonNull Drawable icon, + boolean shrinkNonAdaptiveIcons, RectF outIconBounds, float[] outScale) { + if (icon == null) { + return null; + } float scale = 1f; if (shrinkNonAdaptiveIcons && ATLEAST_OREO) { @@ -261,7 +265,7 @@ public class BaseIconFactory implements AutoCloseable { * @param icon drawable that should be flattened to a bitmap * @param scale the scale to apply before drawing {@param icon} on the canvas */ - public Bitmap createIconBitmap(Drawable icon, float scale, int size) { + public Bitmap createIconBitmap(@NonNull Drawable icon, float scale, int size) { Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); if (icon == null) { return bitmap; diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index 332e0fa360..546548036e 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -23,6 +23,7 @@ package="com.android.launcher3" > + 0) { + // If the entire header is translucent, make sure the text is at full opacity so it's + // not double-translucent. However, we support keeping the text invisible (alpha == 0). + textAlpha = mIconFullTextAlpha; + } + mIconCurrentTextAlpha = textAlpha; int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha); for (int i = 0; i < getChildCount(); i++) { ((BubbleTextView) getChildAt(i)).setTextColor(iconColor); } } + @Override + public void setAlpha(float alpha) { + super.setAlpha(alpha); + // Reapply text alpha so that we update it to be full alpha if the row is now translucent. + setTextAlpha(mIconLastSetTextAlpha); + } + @Override public boolean hasOverlappingRendering() { return false; @@ -351,23 +366,15 @@ public class PredictionRowView extends LinearLayout implements } @Override - public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent, - PropertySetter setter, Interpolator fadeInterpolator) { - boolean isDrawn = getAlpha() > 0; - int textAlpha = hasHeaderExtra - ? (hasContent ? mIconFullTextAlpha : 0) // Text follows the content visibility - : mIconCurrentTextAlpha; // Leave as before - if (!isDrawn) { - // If the header is not drawn, no need to animate the text alpha - setTextAlpha(textAlpha); - } else { - setter.setInt(this, TEXT_ALPHA, textAlpha, fadeInterpolator); - } - + public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent, + PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) { + // Text follows all apps visibility + int textAlpha = hasHeaderExtra && hasAllAppsContent ? mIconFullTextAlpha : 0; + setter.setInt(this, TEXT_ALPHA, textAlpha, allAppsFade); setter.setFloat(mOverviewScrollFactor, AnimatedFloat.VALUE, - (hasHeaderExtra && !hasContent) ? 1 : 0, LINEAR); + (hasHeaderExtra && !hasAllAppsContent) ? 1 : 0, LINEAR); setter.setFloat(mContentAlphaFactor, AnimatedFloat.VALUE, hasHeaderExtra ? 1 : 0, - fadeInterpolator); + headerFade); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index a3c2e3cc81..596bc4f44c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -164,6 +164,11 @@ public abstract class RecentsUiFactory { } } + if (FeatureFlags.PULL_DOWN_STATUS_BAR + && !launcher.getDeviceProfile().isMultiWindowMode) { + list.add(new StatusBarTouchController(launcher)); + } + list.add(new LauncherTaskViewController(launcher)); return list.toArray(new TouchController[list.size()]); } 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 5ee08c12d2..50cfac8e43 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 @@ -19,6 +19,7 @@ import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; +import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.util.LayoutUtils; @@ -68,8 +69,8 @@ public class BackgroundAppState extends OverviewState { if (taskCount == 0) { return super.getOverviewScaleAndTranslation(launcher); } - TaskView dummyTask = recentsView.getTaskViewAt(Math.max(taskCount - 1, - recentsView.getCurrentPage())); + TaskView dummyTask = recentsView.getTaskViewAt(Utilities.boundToRange( + recentsView.getCurrentPage(), 0, taskCount - 1)); return recentsView.getTempClipAnimationHelper().updateForFullscreenOverview(dummyTask) .getScaleAndTranslation(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java index c954762837..427206a65b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java @@ -15,7 +15,9 @@ */ package com.android.launcher3.uioverrides.states; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCRIM_FADE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X; +import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; @@ -43,6 +45,7 @@ public class OverviewPeekState extends OverviewState { if (this == OVERVIEW_PEEK && fromState == NORMAL) { builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7); + builder.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN); } } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java index 5543860eec..151ceb8347 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -43,7 +43,6 @@ import com.android.launcher3.R; import com.android.launcher3.Workspace; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorSetBuilder; -import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.quickstep.SysUINavigationMode; @@ -128,14 +127,15 @@ public class OverviewState extends LauncherState { if (launcher.getDeviceProfile().isVerticalBarLayout()) { return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON; } else { + boolean hasAllAppsHeaderExtra = launcher.getAppsView() != null + && launcher.getAppsView().getFloatingHeaderView().hasVisibleContent(); return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON | - (launcher.getAppsView().getFloatingHeaderView().hasVisibleContent() - ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS); + (hasAllAppsHeaderExtra ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS); } } @Override - public float getWorkspaceScrimAlpha(Launcher launcher) { + public float getOverviewScrimAlpha(Launcher launcher) { return 0.5f; } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java index 3fe4bcfd9c..ab346c0599 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java @@ -23,13 +23,17 @@ import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_PEEK; import static com.android.launcher3.LauncherStateManager.ANIM_ALL; import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE; +import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_3; +import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; @@ -43,6 +47,7 @@ import android.view.ViewConfiguration; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.quickstep.OverviewInteractionState; import com.android.quickstep.util.MotionPauseDetector; @@ -102,6 +107,9 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { mPeekAnim.start(); recentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + + mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1, + peekDuration, 0); }); } } @@ -120,6 +128,13 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { LauncherState toState) { if (fromState == NORMAL && toState == ALL_APPS) { AnimatorSetBuilder builder = new AnimatorSetBuilder(); + // Fade in prediction icons quickly, then rest of all apps after reaching overview. + float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher) + - OVERVIEW.getVerticalProgress(mLauncher); + builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(ACCEL, + 0, ALL_APPS_CONTENT_FADE_THRESHOLD)); + builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(LINEAR, + progressToReachOverview, 1)); // Get workspace out of the way quickly, to prepare for potential pause. builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3); @@ -167,6 +182,21 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { mMotionPauseDetector.clear(); } + @Override + protected void goToTargetState(LauncherState targetState, int logAction) { + if (mPeekAnim != null && mPeekAnim.isStarted()) { + // Don't jump to the target state until overview is no longer peeking. + mPeekAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + FlingAndHoldTouchController.super.goToTargetState(targetState, logAction); + } + }); + } else { + super.goToTargetState(targetState, logAction); + } + } + @Override protected void updateAnimatorBuilderOnReinit(AnimatorSetBuilder builder) { if (handlingOverviewAnim()) { 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 8e32bb370e..00e4f58e92 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 @@ -16,10 +16,10 @@ package com.android.launcher3.uioverrides.touchcontrollers; import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE; -import static com.android.launcher3.Utilities.SINGLE_FRAME_MS; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; +import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -266,8 +266,8 @@ public abstract class TaskViewTouchController animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity); } - float nextFrameProgress = Utilities.boundToRange( - progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f); + float nextFrameProgress = Utilities.boundToRange(progress + + velocity * getSingleFrameMs(mActivity) / Math.abs(mEndDisplacement), 0f, 1f); mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction)); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java new file mode 100644 index 0000000000..d627a7f14f --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -0,0 +1,509 @@ +/* + * 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.os.VibrationEffect.EFFECT_CLICK; +import static android.os.VibrationEffect.createPredefined; + +import static com.android.launcher3.Utilities.postAsyncCallback; +import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; +import static com.android.launcher3.anim.Interpolators.DEACCEL; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; +import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; +import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; +import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; +import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; + +import android.animation.Animator; +import android.annotation.TargetApi; +import android.app.ActivityManager.RunningTaskInfo; +import android.content.Context; +import android.content.Intent; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.provider.Settings; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowManager; +import android.view.animation.Interpolator; + +import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.graphics.RotationMode; +import com.android.launcher3.views.FloatingIconView; +import com.android.quickstep.ActivityControlHelper.ActivityInitListener; +import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory; +import com.android.quickstep.SysUINavigationMode.Mode; +import com.android.quickstep.inputconsumers.InputConsumer; +import com.android.quickstep.util.ClipAnimationHelper; +import com.android.quickstep.util.ClipAnimationHelper.TransformParams; +import com.android.quickstep.util.RectFSpringAnim; +import com.android.quickstep.util.RemoteAnimationTargetSet; +import com.android.quickstep.util.SwipeAnimationTargetSet; +import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; +import com.android.quickstep.views.RecentsView; +import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.system.InputConsumerController; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; + +import java.util.function.Consumer; + +import androidx.annotation.UiThread; + +/** + * Base class for swipe up handler with some utility methods + */ +@TargetApi(Build.VERSION_CODES.Q) +public abstract class BaseSwipeUpHandler + implements SwipeAnimationListener { + + private static final String TAG = "BaseSwipeUpHandler"; + protected static final Rect TEMP_RECT = new Rect(); + + // Start resisting when swiping past this factor of mTransitionDragLength. + private static final float DRAG_LENGTH_FACTOR_START_PULLBACK = 1.4f; + // This is how far down we can scale down, where 0f is full screen and 1f is recents. + private static final float DRAG_LENGTH_FACTOR_MAX_PULLBACK = 1.8f; + private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL; + + // The distance needed to drag to reach the task size in recents. + protected int mTransitionDragLength; + // How much further we can drag past recents, as a factor of mTransitionDragLength. + protected float mDragLengthFactor = 1; + + protected final Context mContext; + protected final OverviewComponentObserver mOverviewComponentObserver; + protected final ActivityControlHelper mActivityControlHelper; + protected final RecentsModel mRecentsModel; + protected final int mRunningTaskId; + + protected final ClipAnimationHelper mClipAnimationHelper; + protected final TransformParams mTransformParams = new TransformParams(); + + private final Vibrator mVibrator; + protected final Mode mMode; + + // Shift in the range of [0, 1]. + // 0 => preview snapShot is completely visible, and hotseat is completely translated down + // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely + // visible. + protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift); + + protected final ActivityInitListener mActivityInitListener; + protected final RecentsAnimationWrapper mRecentsAnimationWrapper; + + protected T mActivity; + protected Q mRecentsView; + protected DeviceProfile mDp; + private final int mPageSpacing; + + protected Runnable mGestureEndCallback; + + protected final Handler mMainThreadHandler = MAIN_THREAD_EXECUTOR.getHandler(); + protected MultiStateCallback mStateCallback; + + protected boolean mCanceled; + protected int mFinishingRecentsAnimationForNewTaskId = -1; + + protected BaseSwipeUpHandler(Context context, + OverviewComponentObserver overviewComponentObserver, + RecentsModel recentsModel, InputConsumerController inputConsumer, int runningTaskId) { + mContext = context; + mOverviewComponentObserver = overviewComponentObserver; + mActivityControlHelper = overviewComponentObserver.getActivityControlHelper(); + mRecentsModel = recentsModel; + mActivityInitListener = + mActivityControlHelper.createActivityInitListener(this::onActivityInit); + mRunningTaskId = runningTaskId; + mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer, + this::createNewInputProxyHandler); + mMode = SysUINavigationMode.getMode(context); + + mClipAnimationHelper = new ClipAnimationHelper(context); + mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing); + mVibrator = context.getSystemService(Vibrator.class); + initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext) + .getDeviceProfile(mContext)); + } + + protected void setStateOnUiThread(int stateFlag) { + if (Looper.myLooper() == mMainThreadHandler.getLooper()) { + mStateCallback.setState(stateFlag); + } else { + postAsyncCallback(mMainThreadHandler, () -> mStateCallback.setState(stateFlag)); + } + } + + protected void performHapticFeedback() { + if (!mVibrator.hasVibrator()) { + return; + } + if (Settings.System.getInt( + mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) == 0) { + return; + } + + VibrationEffect effect = createPredefined(EFFECT_CLICK); + if (effect == null) { + return; + } + BACKGROUND_EXECUTOR.execute(() -> mVibrator.vibrate(effect)); + } + + public Consumer getRecentsViewDispatcher(RotationMode rotationMode) { + return mRecentsView != null ? mRecentsView.getEventDispatcher(rotationMode) : null; + } + + @UiThread + public void updateDisplacement(float displacement) { + // We are moving in the negative x/y direction + displacement = -displacement; + float shift; + if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) { + shift = mDragLengthFactor; + } else { + float translation = Math.max(displacement, 0); + shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength; + if (shift > DRAG_LENGTH_FACTOR_START_PULLBACK) { + float pullbackProgress = Utilities.getProgress(shift, + DRAG_LENGTH_FACTOR_START_PULLBACK, mDragLengthFactor); + pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress); + shift = DRAG_LENGTH_FACTOR_START_PULLBACK + pullbackProgress + * (DRAG_LENGTH_FACTOR_MAX_PULLBACK - DRAG_LENGTH_FACTOR_START_PULLBACK); + } + } + + mCurrentShift.updateValue(shift); + } + + public void setGestureEndCallback(Runnable gestureEndCallback) { + mGestureEndCallback = gestureEndCallback; + } + + public abstract Intent getLaunchIntent(); + + protected void linkRecentsViewScroll() { + SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, applier -> { + mTransformParams.setSyncTransactionApplier(applier); + mRecentsAnimationWrapper.runOnInit(() -> + mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier)); + }); + + mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { + if (moveWindowWithRecentsScroll()) { + updateFinalShift(); + } + }); + mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper); + mRecentsView.setClipAnimationHelper(mClipAnimationHelper); + } + + protected void startNewTask(int successStateFlag, Consumer resultCallback) { + // Launch the task user scrolled to (mRecentsView.getNextPage()). + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + // We finish recents animation inside launchTask() when live tile is enabled. + mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */, + true /* freezeTaskList */); + } else { + int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id; + mFinishingRecentsAnimationForNewTaskId = taskId; + mRecentsAnimationWrapper.finish(true /* toRecents */, () -> { + if (!mCanceled) { + TaskView nextTask = mRecentsView.getTaskView(taskId); + if (nextTask != null) { + nextTask.launchTask(false /* animate */, true /* freezeTaskList */, + success -> { + resultCallback.accept(success); + if (!success) { + mActivityControlHelper.onLaunchTaskFailed(mActivity); + nextTask.notifyTaskLaunchFailed(TAG); + } else { + mActivityControlHelper.onLaunchTaskSuccess(mActivity); + } + }, mMainThreadHandler); + } + setStateOnUiThread(successStateFlag); + } + mCanceled = false; + mFinishingRecentsAnimationForNewTaskId = -1; + }); + } + TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true); + } + + @Override + public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) { + DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext); + final Rect overviewStackBounds; + RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId); + + if (targetSet.minimizedHomeBounds != null && runningTaskTarget != null) { + overviewStackBounds = mActivityControlHelper + .getOverviewWindowBounds(targetSet.minimizedHomeBounds, runningTaskTarget); + dp = dp.getMultiWindowProfile(mContext, new Point( + overviewStackBounds.width(), overviewStackBounds.height())); + } else { + // If we are not in multi-window mode, home insets should be same as system insets. + dp = dp.copy(mContext); + overviewStackBounds = getStackBounds(dp); + } + dp.updateInsets(targetSet.homeContentInsets); + dp.updateIsSeascape(mContext.getSystemService(WindowManager.class)); + if (runningTaskTarget != null) { + mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget); + } + + mClipAnimationHelper.prepareAnimation(dp, false /* isOpening */); + initTransitionEndpoints(dp); + + mRecentsAnimationWrapper.setController(targetSet); + } + + private Rect getStackBounds(DeviceProfile dp) { + if (mActivity != null) { + int loc[] = new int[2]; + View rootView = mActivity.getRootView(); + rootView.getLocationOnScreen(loc); + return new Rect(loc[0], loc[1], loc[0] + rootView.getWidth(), + loc[1] + rootView.getHeight()); + } else { + return new Rect(0, 0, dp.widthPx, dp.heightPx); + } + } + + protected void initTransitionEndpoints(DeviceProfile dp) { + mDp = dp; + + mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength( + dp, mContext, TEMP_RECT); + if (!dp.isMultiWindowMode) { + // When updating the target rect, also update the home bounds since the location on + // screen of the launcher window may be stale (position is not updated until first + // traversal after the window is resized). We only do this for non-multiwindow because + // we otherwise use the minimized home bounds provided by the system. + mClipAnimationHelper.updateHomeBounds(getStackBounds(dp)); + } + mClipAnimationHelper.updateTargetRect(TEMP_RECT); + if (mMode == Mode.NO_BUTTON) { + // We can drag all the way to the top of the screen. + mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength; + } + } + + /** + * Return true if the window should be translated horizontally if the recents view scrolls + */ + protected abstract boolean moveWindowWithRecentsScroll(); + + protected abstract boolean onActivityInit(final T activity, Boolean alreadyOnHome); + + /** + * Called to create a input proxy for the running task + */ + @UiThread + protected abstract InputConsumer createNewInputProxyHandler(); + + /** + * Called when the value of {@link #mCurrentShift} changes + */ + @UiThread + public abstract void updateFinalShift(); + + /** + * Called when motion pause is detected + */ + public abstract void onMotionPauseChanged(boolean isPaused); + + @UiThread + public void onGestureStarted() { } + + @UiThread + public abstract void onGestureCancelled(); + + @UiThread + public abstract void onGestureEnded(float endVelocity, PointF velocity, PointF downPos); + + public abstract void onConsumerAboutToBeSwitched(SwipeSharedState sharedState); + + public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { } + + public void initWhenReady() { + // Preload the plan + mRecentsModel.getTasks(null); + + mActivityInitListener.register(); + } + + /** + * Applies the transform on the recents animation without any additional null checks + */ + protected void applyTransformUnchecked() { + float shift = mCurrentShift.value; + float offsetX = mRecentsView == null ? 0 : mRecentsView.getScrollOffset(); + float offsetScale = getTaskCurveScaleForOffsetX(offsetX, + mClipAnimationHelper.getTargetRect().width()); + mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale); + mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, + mTransformParams); + } + + private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) { + float distanceToReachEdge = mDp.widthPx / 2 + taskWidth / 2 + mPageSpacing; + float interpolation = Math.min(1, offsetX / distanceToReachEdge); + return TaskView.getCurveScaleForInterpolation(interpolation); + } + + /** + * Creates an animation that transforms the current app window into the home app. + * @param startProgress The progress of {@link #mCurrentShift} to start the window from. + * @param homeAnimationFactory The home animation factory. + */ + protected RectFSpringAnim createWindowAnimationToHome(float startProgress, + HomeAnimationFactory homeAnimationFactory) { + final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet; + final RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet, + mTransformParams.setProgress(startProgress), false /* launcherOnTop */)); + final RectF targetRect = homeAnimationFactory.getWindowTargetRect(); + + final View floatingView = homeAnimationFactory.getFloatingView(); + final boolean isFloatingIconView = floatingView instanceof FloatingIconView; + RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext.getResources()); + if (isFloatingIconView) { + FloatingIconView fiv = (FloatingIconView) floatingView; + anim.addAnimatorListener(fiv); + fiv.setOnTargetChangeListener(anim::onTargetPositionChanged); + } + + AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome(); + + // End on a "round-enough" radius so that the shape reveal doesn't have to do too much + // rounding at the end of the animation. + float startRadius = mClipAnimationHelper.getCurrentCornerRadius(); + float endRadius = startRect.width() / 6f; + // We want the window alpha to be 0 once this threshold is met, so that the + // FolderIconView can be seen morphing into the icon shape. + final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f; + anim.addOnUpdateListener(new RectFSpringAnim.OnUpdateListener() { + + // Alpha interpolates between [1, 0] between progress values [start, end] + final float start = 0f; + final float end = 0.85f; + + private float getWindowAlpha(float progress) { + if (progress <= start) { + return 1f; + } + if (progress >= end) { + return 0f; + } + return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5); + } + + @Override + public void onUpdate(RectF currentRect, float progress) { + homeAnim.setPlayFraction(progress); + + mTransformParams.setProgress(progress) + .setCurrentRectAndTargetAlpha(currentRect, getWindowAlpha(progress)); + if (isFloatingIconView) { + mTransformParams.setCornerRadius(endRadius * progress + startRadius + * (1f - progress)); + } + mClipAnimationHelper.applyTransform(targetSet, mTransformParams, + false /* launcherOnTop */); + + if (isFloatingIconView) { + ((FloatingIconView) floatingView).update(currentRect, 1f, progress, + windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), + false); + } + } + + @Override + public void onCancel() { + if (isFloatingIconView) { + ((FloatingIconView) floatingView).fastFinish(); + } + } + }); + anim.addAnimatorListener(new AnimationSuccessListener() { + @Override + public void onAnimationStart(Animator animation) { + homeAnim.dispatchOnStart(); + } + + @Override + public void onAnimationSuccess(Animator animator) { + homeAnim.getAnimationPlayer().end(); + } + }); + return anim; + } + + public interface Factory { + + BaseSwipeUpHandler newHandler(RunningTaskInfo runningTask, + long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask); + } + + protected interface RunningWindowAnim { + void end(); + + void cancel(); + + static RunningWindowAnim wrap(Animator animator) { + return new RunningWindowAnim() { + @Override + public void end() { + animator.end(); + } + + @Override + public void cancel() { + animator.cancel(); + } + }; + } + + static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) { + return new RunningWindowAnim() { + @Override + public void end() { + rectFSpringAnim.end(); + } + + @Override + public void cancel() { + rectFSpringAnim.cancel(); + } + }; + } + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java index c43155b73a..8c5a78823a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java @@ -80,7 +80,9 @@ public final class FallbackActivityControllerHelper implements @Override public void onAssistantVisibilityChanged(float visibility) { - // TODO: + // This class becomes active when the screen is locked. + // Rather than having it handle assistant visibility changes, the assistant visibility is + // set to zero prior to this class becoming active. } @NonNull diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index b2a71a4882..295585e002 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -151,16 +151,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe @NonNull @Override public RectF getWindowTargetRect() { - final int halfIconSize = dp.iconSizePx / 2; - final float targetCenterX = dp.availableWidthPx / 2f; - final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx; - if (canUseWorkspaceView) { return iconLocation; } else { - // Fallback to animate to center of screen. - return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize, - targetCenterX + halfIconSize, targetCenterY + halfIconSize); + return HomeAnimationFactory.getDefaultWindowTargetRect(dp); } } @@ -194,9 +188,6 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe @Override public AnimationFactory prepareRecentsUI(Launcher activity, boolean activityVisible, boolean animateActivity, Consumer callback) { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "prepareRecentsUI"); - } final LauncherState startState = activity.getStateManager().getState(); LauncherState resetState = startState; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java new file mode 100644 index 0000000000..4eb9df2cbd --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -0,0 +1,83 @@ +package com.android.quickstep; + +import android.content.Context; +import android.os.Bundle; + +import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.testing.TestInformationHandler; +import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.uioverrides.states.OverviewState; +import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController; +import com.android.quickstep.util.LayoutUtils; +import com.android.quickstep.views.RecentsView; + +import java.util.concurrent.ExecutionException; + +public class QuickstepTestInformationHandler extends TestInformationHandler { + + public QuickstepTestInformationHandler(Context context) { + } + + @Override + public Bundle call(String method) { + final Bundle response = new Bundle(); + switch (method) { + case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: { + final float swipeHeight = + OverviewState.getDefaultSwipeHeight(mDeviceProfile); + response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight); + return response; + } + + case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: { + final float swipeHeight = + LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile); + response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight); + return response; + } + + case TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED: { + response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, + TouchInteractionService.isInitialized()); + return response; + } + + case TestProtocol.REQUEST_HOTSEAT_TOP: { + if (mLauncher == null) return null; + + response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, + PortraitStatesTouchController.getHotseatTop(mLauncher)); + return response; + } + + case TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN: { + try { + final int leftMargin = new MainThreadExecutor().submit(() -> + mLauncher.getOverviewPanel().getLeftGestureMargin()).get(); + response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, leftMargin); + } catch (ExecutionException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return response; + } + + case TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN: { + try { + final int rightMargin = new MainThreadExecutor().submit(() -> + mLauncher.getOverviewPanel().getRightGestureMargin()). + get(); + response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, rightMargin); + } catch (ExecutionException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return response; + } + } + + return super.call(method); + } +} 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 fc29a5663b..f08ae4a827 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java @@ -28,8 +28,10 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.app.ActivityOptions; +import android.content.Intent; import android.content.res.Configuration; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.view.View; @@ -42,7 +44,9 @@ import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.fallback.FallbackRecentsView; import com.android.quickstep.fallback.RecentsRootView; import com.android.quickstep.util.ClipAnimationHelper; +import com.android.quickstep.util.ObjectWrapper; import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; @@ -54,6 +58,9 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat; */ public final class RecentsActivity extends BaseRecentsActivity { + public static final String EXTRA_THUMBNAIL = "thumbnailData"; + public static final String EXTRA_TASK_ID = "taskID"; + private Handler mUiHandler = new Handler(Looper.getMainLooper()); private RecentsRootView mRecentsRootView; private FallbackRecentsView mFallbackRecentsView; @@ -78,6 +85,22 @@ public final class RecentsActivity extends BaseRecentsActivity { } } + @Override + protected void onNewIntent(Intent intent) { + if (intent.getExtras() != null) { + int taskID = intent.getIntExtra(EXTRA_TASK_ID, 0); + IBinder thumbnail = intent.getExtras().getBinder(EXTRA_THUMBNAIL); + if (taskID != 0 && thumbnail instanceof ObjectWrapper) { + ThumbnailData thumbnailData = ((ObjectWrapper) thumbnail).get(); + mFallbackRecentsView.showCurrentTask(taskID); + mFallbackRecentsView.updateThumbnail(taskID, thumbnailData); + } + } + intent.removeExtra(EXTRA_TASK_ID); + intent.removeExtra(EXTRA_THUMBNAIL); + super.onNewIntent(intent); + } + @Override protected void onHandleConfigChanged() { super.onHandleConfigChanged(); @@ -132,7 +155,7 @@ public final class RecentsActivity extends BaseRecentsActivity { mFallbackRecentsView.resetViewUI(); } }); - result.setAnimation(anim); + result.setAnimation(anim, RecentsActivity.this); } }; return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat( @@ -178,6 +201,12 @@ public final class RecentsActivity extends BaseRecentsActivity { mFallbackRecentsView.resetTaskVisuals(); } + @Override + protected void onStop() { + super.onStop(); + mFallbackRecentsView.reset(); + } + public void onTaskLaunched() { mFallbackRecentsView.resetTaskVisuals(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java index ddd28a3500..e51ba631bf 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java @@ -19,12 +19,12 @@ import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; -import static com.android.launcher3.Utilities.FLAG_NO_GESTURES; - import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; +import androidx.annotation.UiThread; + import com.android.launcher3.util.Preconditions; import com.android.quickstep.inputconsumers.InputConsumer; import com.android.quickstep.util.SwipeAnimationTargetSet; @@ -33,8 +33,6 @@ import com.android.systemui.shared.system.InputConsumerController; import java.util.ArrayList; import java.util.function.Supplier; -import androidx.annotation.UiThread; - /** * Wrapper around RecentsAnimationController to help with some synchronization */ @@ -184,18 +182,15 @@ public class RecentsAnimationWrapper { } } if (mInputConsumer != null) { - int flags = ev.getEdgeFlags(); - ev.setEdgeFlags(flags | FLAG_NO_GESTURES); mInputConsumer.onMotionEvent(ev); - ev.setEdgeFlags(flags); } return true; } - public void setCancelWithDeferredScreenshot(boolean deferredWithScreenshot) { + public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { if (targetSet != null) { - targetSet.controller.setCancelWithDeferredScreenshot(deferredWithScreenshot); + targetSet.controller.setDeferCancelUntilNextTransition(defer, screenshot); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java index 213c5d3244..fd45923070 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java @@ -207,8 +207,7 @@ public class TaskSystemShortcut extends SystemShortcut } }; WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture( - future, animStartedListener, mHandler, true /* scaleUp */, - v.getDisplay().getDisplayId()); + future, animStartedListener, mHandler, true /* scaleUp */, displayId); } }); } 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 53da0f92dd..86ba855784 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -31,18 +31,22 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_H import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT; import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.Service; +import android.app.TaskInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Configuration; import android.graphics.Point; import android.graphics.RectF; import android.graphics.Region; @@ -65,6 +69,7 @@ import android.view.WindowManager; import androidx.annotation.BinderThread; import androidx.annotation.UiThread; +import androidx.annotation.WorkerThread; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.MainThreadExecutor; @@ -74,6 +79,9 @@ import com.android.launcher3.Utilities; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.logging.EventLogArray; import com.android.launcher3.logging.UserEventDispatcher; +import com.android.launcher3.model.AppLaunchTracker; +import com.android.launcher3.provider.RestoreDbTask; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.UiThreadHelper; import com.android.quickstep.SysUINavigationMode.Mode; @@ -96,8 +104,10 @@ import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; +import com.android.systemui.shared.system.RecentsAnimationListener; import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat; +import com.android.systemui.shared.system.TaskInfoCompat; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; @@ -119,10 +129,6 @@ class ArgList extends LinkedList { public String nextArg() { return pollFirst().toLowerCase(); } - - public String nextArgExact() { - return pollFirst(); - } } /** @@ -141,6 +147,11 @@ public class TouchInteractionService extends Service implements private static final String TAG = "TouchInteractionService"; + private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount"; + private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE"; + private static final int MAX_BACK_NOTIFICATION_COUNT = 3; + private int mBackGestureNotificationCounter = -1; + private final IBinder mMyBinder = new IOverviewProxy.Stub() { public void onActiveNavBarRegionChanges(Region region) { @@ -152,6 +163,8 @@ public class TouchInteractionService extends Service implements .asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY)); MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor); MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiProxySet); + MAIN_THREAD_EXECUTOR.execute(() -> preloadOverview(true /* fromInit */)); + sIsInitialized = true; } @Override @@ -199,6 +212,10 @@ public class TouchInteractionService extends Service implements mOverviewComponentObserver.getActivityControlHelper(); UserEventDispatcher.newInstance(getBaseContext()).logActionBack(completed, downX, downY, isButton, gestureSwipeLeft, activityControl.getContainerType()); + + if (completed && !isButton && shouldNotifyBackGesture()) { + BACKGROUND_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture); + } } public void onSystemUiStateChanged(int stateFlags) { @@ -225,12 +242,17 @@ public class TouchInteractionService extends Service implements }; private static boolean sConnected = false; + private static boolean sIsInitialized = false; private static final SwipeSharedState sSwipeSharedState = new SwipeSharedState(); public static boolean isConnected() { return sConnected; } + public static boolean isInitialized() { + return sIsInitialized; + } + public static SwipeSharedState getSwipeSharedState() { return sSwipeSharedState; } @@ -238,6 +260,11 @@ public class TouchInteractionService extends Service implements private final InputConsumer mResetGestureInputConsumer = new ResetGestureInputConsumer(sSwipeSharedState); + private final BaseSwipeUpHandler.Factory mWindowTreansformFactory = + this::createWindowTransformSwipeHandler; + private final BaseSwipeUpHandler.Factory mFallbackNoButtonFactory = + this::createFallbackNoButtonSwipeHandler; + private ActivityManagerWrapper mAM; private RecentsModel mRecentsModel; private ISystemUiProxy mISystemUiProxy; @@ -319,6 +346,9 @@ public class TouchInteractionService extends Service implements if (mInputEventReceiver != null) { mInputEventReceiver.dispose(); mInputEventReceiver = null; + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "disposeEventHandlers"); + } } if (mInputMonitorCompat != null) { mInputMonitorCompat.dispose(); @@ -327,16 +357,25 @@ public class TouchInteractionService extends Service implements } private void initInputMonitor() { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 1"); + } if (!mMode.hasGestures || mISystemUiProxy == null) { return; } disposeEventHandlers(); + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 2"); + } try { mInputMonitorCompat = InputMonitorCompat.fromBundle(mISystemUiProxy .monitorGestureInput("swipe-up", mDefaultDisplayId), KEY_EXTRA_INPUT_MONITOR); mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(), mMainChoreographer, this::onInputEvent); + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 3"); + } } catch (RemoteException e) { Log.e(TAG, "Unable to create input monitor", e); } @@ -394,6 +433,9 @@ public class TouchInteractionService extends Service implements @Override public void onNavigationModeChanged(Mode newMode) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onNavigationModeChanged " + newMode); + } if (mMode.hasGestures != newMode.hasGestures) { if (newMode.hasGestures) { getSystemService(DisplayManager.class).registerDisplayListener( @@ -448,6 +490,8 @@ public class TouchInteractionService extends Service implements // Temporarily disable model preload // new ModelPreload().start(this); + mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this) + .getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT)); Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver); } @@ -478,6 +522,7 @@ public class TouchInteractionService extends Service implements @Override public void onDestroy() { + sIsInitialized = false; if (mIsUserUnlocked) { mInputConsumer.unregisterInputConsumer(); mOverviewComponentObserver.onDestroy(); @@ -502,6 +547,9 @@ public class TouchInteractionService extends Service implements } private void onInputEvent(InputEvent ev) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onInputEvent " + ev); + } if (!(ev instanceof MotionEvent)) { Log.e(TAG, "Unknown event " + ev); return; @@ -534,6 +582,7 @@ public class TouchInteractionService extends Service implements private boolean validSystemUiFlags() { return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0 && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0 + && (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0 && ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0); } @@ -553,7 +602,7 @@ public class TouchInteractionService extends Service implements if (isInValidSystemUiState) { // This handles apps launched in direct boot mode (e.g. dialer) as well as apps // launched while device is locked even after exiting direct boot mode (e.g. camera). - return createDeviceLockedInputConsumer(mAM.getRunningTask(0)); + return createDeviceLockedInputConsumer(mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT)); } else { return mResetGestureInputConsumer; } @@ -591,7 +640,7 @@ public class TouchInteractionService extends Service implements } private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) { - final RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); + RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); if (!useSharedState) { sSwipeSharedState.clearAllState(false /* finishAnimation */); } @@ -603,6 +652,19 @@ public class TouchInteractionService extends Service implements final ActivityControlHelper activityControl = mOverviewComponentObserver.getActivityControlHelper(); + boolean forceOverviewInputConsumer = false; + if (isExcludedAssistant(runningTaskInfo)) { + // In the case where we are in the excluded assistant state, ignore it and treat the + // running activity as the task behind the assistant + runningTaskInfo = mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT); + if (!ActivityManagerWrapper.isHomeTask(runningTaskInfo)) { + final ComponentName homeComponent = + mOverviewComponentObserver.getHomeIntent().getComponent(); + forceOverviewInputConsumer = + runningTaskInfo.baseIntent.getComponent().equals(homeComponent); + } + } + if (runningTaskInfo == null && !sSwipeSharedState.goingToLauncher && !sSwipeSharedState.recentsAnimationFinishInterrupted) { return mResetGestureInputConsumer; @@ -612,22 +674,25 @@ public class TouchInteractionService extends Service implements RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); info.id = sSwipeSharedState.nextRunningTaskId; return createOtherActivityInputConsumer(event, info); - } else if (sSwipeSharedState.goingToLauncher || activityControl.isResumed()) { + } else if (sSwipeSharedState.goingToLauncher || activityControl.isResumed() + || forceOverviewInputConsumer) { return createOverviewInputConsumer(event); } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityControl.isInLiveTileMode()) { return createOverviewInputConsumer(event); } else if (mGestureBlockingActivity != null && runningTaskInfo != null && mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) { return mResetGestureInputConsumer; - } else if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) { - return new FallbackNoButtonInputConsumer(this, activityControl, - mInputMonitorCompat, sSwipeSharedState, mSwipeTouchRegion, - mOverviewComponentObserver, disableHorizontalSwipe(event), runningTaskInfo); } else { return createOtherActivityInputConsumer(event, runningTaskInfo); } } + private boolean isExcludedAssistant(TaskInfo info) { + return info != null + && TaskInfoCompat.getActivityType(info) == ACTIVITY_TYPE_ASSISTANT + && (info.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0; + } + private boolean disableHorizontalSwipe(MotionEvent event) { // mExclusionRegion can change on binder thread, use a local instance here. Region exclusionRegion = mExclusionRegion; @@ -635,17 +700,25 @@ public class TouchInteractionService extends Service implements && exclusionRegion.contains((int) event.getX(), (int) event.getY()); } - private OtherActivityInputConsumer createOtherActivityInputConsumer(MotionEvent event, + private InputConsumer createOtherActivityInputConsumer(MotionEvent event, RunningTaskInfo runningTaskInfo) { - final ActivityControlHelper activityControl = - mOverviewComponentObserver.getActivityControlHelper(); - boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event); - return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel, - mOverviewComponentObserver.getOverviewIntent(), activityControl, - shouldDefer, mOverviewCallbacks, mInputConsumer, this::onConsumerInactive, + final boolean shouldDefer; + final BaseSwipeUpHandler.Factory factory; + + if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) { + shouldDefer = !sSwipeSharedState.recentsAnimationFinishInterrupted; + factory = mFallbackNoButtonFactory; + } else { + shouldDefer = mOverviewComponentObserver.getActivityControlHelper() + .deferStartingActivity(mActiveNavBarRegion, event); + factory = mWindowTreansformFactory; + } + + return new OtherActivityInputConsumer(this, runningTaskInfo, + shouldDefer, mOverviewCallbacks, this::onConsumerInactive, sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, - disableHorizontalSwipe(event)); + disableHorizontalSwipe(event), factory); } private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) { @@ -669,7 +742,7 @@ public class TouchInteractionService extends Service implements return new OverviewInputConsumer(activity, mInputMonitorCompat, false /* startingInActivityBounds */); } else { - return new OverviewWithoutFocusInputConsumer(this, mInputMonitorCompat, + return new OverviewWithoutFocusInputConsumer(activity, mInputMonitorCompat, disableHorizontalSwipe(event)); } } @@ -684,6 +757,60 @@ public class TouchInteractionService extends Service implements } } + private void preloadOverview(boolean fromInit) { + if (!mIsUserUnlocked) { + return; + } + if (!mMode.hasGestures && !mOverviewComponentObserver.isHomeAndOverviewSame()) { + // Prevent the overview from being started before the real home on first boot. + return; + } + + if (RestoreDbTask.isPending(this)) { + // Preloading while a restore is pending may cause launcher to start the restore + // too early. + return; + } + + final ActivityControlHelper activityControl = + mOverviewComponentObserver.getActivityControlHelper(); + if (activityControl.getCreatedActivity() == null) { + // Make sure that UI states will be initialized. + activityControl.createActivityInitListener((activity, wasVisible) -> { + AppLaunchTracker.INSTANCE.get(activity); + return false; + }).register(); + } else if (fromInit) { + // The activity has been created before the initialization of overview service. It is + // usually happens when booting or launcher is the top activity, so we should already + // have the latest state. + return; + } + + // Pass null animation handler to indicate this start is preload. + startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(), null); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + if (!mIsUserUnlocked) { + return; + } + final ActivityControlHelper activityControl = + mOverviewComponentObserver.getActivityControlHelper(); + final BaseDraggingActivity activity = activityControl.getCreatedActivity(); + if (activity == null || activity.isStarted()) { + // We only care about the existing background activity. + return; + } + if (mOverviewComponentObserver.canHandleConfigChanges(activity.getComponentName(), + activity.getResources().getConfiguration().diff(newConfig))) { + return; + } + + preloadOverview(false /* fromInit */); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) { if (rawArgs.length > 0 && Utilities.IS_DEBUG_DEVICE) { @@ -708,8 +835,9 @@ public class TouchInteractionService extends Service implements pw.println(" assistantAvailable=" + mAssistantAvailable); pw.println(" assistantDisabled=" + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)); - pw.println(" resumed=" - + mOverviewComponentObserver.getActivityControlHelper().isResumed()); + boolean resumed = mOverviewComponentObserver != null + && mOverviewComponentObserver.getActivityControlHelper().isResumed(); + pw.println(" resumed=" + resumed); pw.println(" useSharedState=" + mConsumer.useSharedSwipeState()); if (mConsumer.useSharedSwipeState()) { sSwipeSharedState.dump(" ", pw); @@ -739,4 +867,37 @@ public class TouchInteractionService extends Service implements break; } } + + private BaseSwipeUpHandler createWindowTransformSwipeHandler(RunningTaskInfo runningTask, + long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) { + return new WindowTransformSwipeHandler(runningTask, this, touchTimeMs, + mOverviewComponentObserver, continuingLastGesture, mInputConsumer, mRecentsModel); + } + + private BaseSwipeUpHandler createFallbackNoButtonSwipeHandler(RunningTaskInfo runningTask, + long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) { + return new FallbackNoButtonInputConsumer(this, mOverviewComponentObserver, runningTask, + mRecentsModel, mInputConsumer, isLikelyToStartNewTask, continuingLastGesture); + } + + protected boolean shouldNotifyBackGesture() { + return mBackGestureNotificationCounter > 0 && + mGestureBlockingActivity != null; + } + + @WorkerThread + protected void tryNotifyBackGesture() { + if (shouldNotifyBackGesture()) { + mBackGestureNotificationCounter--; + Utilities.getDevicePrefs(this).edit() + .putInt(KEY_BACK_NOTIFICATION_COUNT, mBackGestureNotificationCounter).apply(); + sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage( + mGestureBlockingActivity.getPackageName())); + } + } + + public static void startRecentsActivityAsync(Intent intent, RecentsAnimationListener listener) { + BACKGROUND_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance() + .startRecentsActivity(intent, null, listener, null, null)); + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index 476bb8f935..0d29e5df84 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -17,22 +17,18 @@ package com.android.quickstep; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; -import static com.android.launcher3.Utilities.SINGLE_FRAME_MS; -import static com.android.launcher3.Utilities.postAsyncCallback; -import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; +import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import static com.android.launcher3.util.RaceConditionTracker.ENTER; import static com.android.launcher3.util.RaceConditionTracker.EXIT; import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; -import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE; import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; -import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.HOME; import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.LAST_TASK; @@ -43,65 +39,49 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_O import android.animation.Animator; import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; +import android.content.Intent; import android.graphics.Canvas; -import android.graphics.Point; import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; -import android.os.Handler; -import android.os.Looper; import android.os.SystemClock; -import android.util.Log; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; import android.view.View; import android.view.View.OnApplyWindowInsetsListener; import android.view.ViewTreeObserver.OnDrawListener; import android.view.WindowInsets; -import android.view.WindowManager; import android.view.animation.Interpolator; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.UiThread; 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.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.logging.UserEventDispatcher; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.RaceConditionTracker; import com.android.launcher3.util.TraceHelper; -import com.android.launcher3.views.FloatingIconView; -import com.android.quickstep.ActivityControlHelper.ActivityInitListener; import com.android.quickstep.ActivityControlHelper.AnimationFactory; import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState; import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.inputconsumers.InputConsumer; import com.android.quickstep.inputconsumers.OverviewInputConsumer; -import com.android.quickstep.util.ClipAnimationHelper; +import com.android.quickstep.util.ClipAnimationHelper.TargetAlphaProvider; import com.android.quickstep.util.RectFSpringAnim; -import com.android.quickstep.util.RemoteAnimationTargetSet; import com.android.quickstep.util.SwipeAnimationTargetSet; -import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; import com.android.quickstep.views.LiveTileOverlay; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -109,19 +89,14 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.LatencyTrackerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; import com.android.systemui.shared.system.WindowCallbacksCompat; -import java.util.function.BiFunction; -import java.util.function.Consumer; - @TargetApi(Build.VERSION_CODES.O) public class WindowTransformSwipeHandler - implements SwipeAnimationListener, OnApplyWindowInsetsListener { + extends BaseSwipeUpHandler + implements OnApplyWindowInsetsListener { private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName(); - private static final Rect TEMP_RECT = new Rect(); - private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null; private static int getFlagForIndex(int index, String name) { @@ -220,64 +195,30 @@ public class WindowTransformSwipeHandler private static final long SHELF_ANIM_DURATION = 240; public static final long RECENTS_ATTACH_DURATION = 300; - // Start resisting when swiping past this factor of mTransitionDragLength. - private static final float DRAG_LENGTH_FACTOR_START_PULLBACK = 1.4f; - // This is how far down we can scale down, where 0f is full screen and 1f is recents. - private static final float DRAG_LENGTH_FACTOR_MAX_PULLBACK = 1.8f; - private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL; - /** * Used as the page index for logging when we return to the last task at the end of the gesture. */ private static final int LOG_NO_OP_PAGE_INDEX = -1; - private final ClipAnimationHelper mClipAnimationHelper; - private final ClipAnimationHelper.TransformParams mTransformParams; - - private Runnable mGestureEndCallback; private GestureEndTarget mGestureEndTarget; // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise private RunningWindowAnim mRunningWindowAnim; private boolean mIsShelfPeeking; - private DeviceProfile mDp; - // The distance needed to drag to reach the task size in recents. - private int mTransitionDragLength; - // How much further we can drag past recents, as a factor of mTransitionDragLength. - private float mDragLengthFactor = 1; - // Shift in the range of [0, 1]. - // 0 => preview snapShot is completely visible, and hotseat is completely translated down - // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely - // visible. - private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift); private boolean mContinuingLastGesture; // To avoid UI jump when gesture is started, we offset the animation by the threshold. private float mShiftAtGestureStart = 0; - private final Handler mMainThreadHandler = MAIN_THREAD_EXECUTOR.getHandler(); - - private final Context mContext; - private final ActivityControlHelper mActivityControlHelper; - private final ActivityInitListener mActivityInitListener; - - private final SysUINavigationMode.Mode mMode; - - private final int mRunningTaskId; private ThumbnailData mTaskSnapshot; - private MultiStateCallback mStateCallback; // Used to control launcher components throughout the swipe gesture. private AnimatorPlaybackController mLauncherTransitionController; private boolean mHasLauncherTransitionControllerStarted; - private T mActivity; - private RecentsView mRecentsView; private AnimationFactory mAnimationFactory = (t) -> { }; private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay(); - private boolean mCanceled; private boolean mWasLauncherAlreadyVisible; - private int mFinishingRecentsAnimationForNewTaskId = -1; private boolean mPassedOverviewThreshold; private boolean mGestureStarted; @@ -286,31 +227,17 @@ public class WindowTransformSwipeHandler private PointF mDownPos; private boolean mIsLikelyToStartNewTask; - private final RecentsAnimationWrapper mRecentsAnimationWrapper; - private final long mTouchTimeMs; private long mLauncherFrameDrawnTime; public WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, - long touchTimeMs, ActivityControlHelper controller, boolean continuingLastGesture, - InputConsumerController inputConsumer) { - mContext = context; - mRunningTaskId = runningTaskInfo.id; + long touchTimeMs, OverviewComponentObserver overviewComponentObserver, + boolean continuingLastGesture, + InputConsumerController inputConsumer, RecentsModel recentsModel) { + super(context, overviewComponentObserver, recentsModel, inputConsumer, runningTaskInfo.id); mTouchTimeMs = touchTimeMs; - mActivityControlHelper = controller; - mActivityInitListener = mActivityControlHelper - .createActivityInitListener(this::onActivityInit); mContinuingLastGesture = continuingLastGesture; - mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer, - this::createNewInputProxyHandler); - mClipAnimationHelper = new ClipAnimationHelper(context); - mTransformParams = new ClipAnimationHelper.TransformParams(); - - mMode = SysUINavigationMode.getMode(context); initStateCallbacks(); - - DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext); - initTransitionEndpoints(dp); } private void initStateCallbacks() { @@ -373,44 +300,8 @@ public class WindowTransformSwipeHandler } } - private void setStateOnUiThread(int stateFlag) { - if (Looper.myLooper() == mMainThreadHandler.getLooper()) { - mStateCallback.setState(stateFlag); - } else { - postAsyncCallback(mMainThreadHandler, () -> mStateCallback.setState(stateFlag)); - } - } - - private void initTransitionEndpoints(DeviceProfile dp) { - mDp = dp; - - Rect tempRect = new Rect(); - mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength( - dp, mContext, tempRect); - mClipAnimationHelper.updateTargetRect(tempRect); - if (mMode == Mode.NO_BUTTON) { - // We can drag all the way to the top of the screen. - mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength; - } - } - - private long getFadeInDuration() { - if (mCurrentShift.getCurrentAnimation() != null) { - ObjectAnimator anim = mCurrentShift.getCurrentAnimation(); - long theirDuration = anim.getDuration() - anim.getCurrentPlayTime(); - - // TODO: Find a better heuristic - return Math.min(MAX_SWIPE_DURATION, Math.max(theirDuration, MIN_SWIPE_DURATION)); - } else { - return MAX_SWIPE_DURATION; - } - } - - public void initWhenReady() { - mActivityInitListener.register(); - } - - private boolean onActivityInit(final T activity, Boolean alreadyOnHome) { + @Override + protected boolean onActivityInit(final T activity, Boolean alreadyOnHome) { if (mActivity == activity) { return true; } @@ -431,19 +322,7 @@ public class WindowTransformSwipeHandler } mRecentsView = activity.getOverviewPanel(); - SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, applier -> { - mTransformParams.setSyncTransactionApplier(applier); - mRecentsAnimationWrapper.runOnInit(() -> - mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier)); - }); - - mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { - if (mGestureEndTarget != HOME) { - updateFinalShift(); - } - }); - mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper); - mRecentsView.setClipAnimationHelper(mClipAnimationHelper); + linkRecentsViewScroll(); mRecentsView.setLiveTileOverlay(mLiveTileOverlay); mActivity.getRootView().getOverlay().add(mLiveTileOverlay); @@ -458,33 +337,23 @@ public class WindowTransformSwipeHandler return true; } + @Override + protected boolean moveWindowWithRecentsScroll() { + return mGestureEndTarget != HOME; + } + private void onLauncherStart(final T activity) { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart"); - } if (mActivity != activity) { return; } - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 1"); - } if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) { return; } - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 2"); - } // If we've already ended the gesture and are going home, don't prepare recents UI, // as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL. if (mGestureEndTarget != HOME) { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 3"); - } Runnable initAnimFactory = () -> { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 4"); - } mAnimationFactory = mActivityControlHelper.prepareRecentsUI(mActivity, mWasLauncherAlreadyVisible, true, this::onAnimatorPlaybackControllerCreated); @@ -494,14 +363,8 @@ public class WindowTransformSwipeHandler // Launcher is visible, but might be about to stop. Thus, if we prepare recents // now, it might get overridden by moveToRestState() in onStop(). To avoid this, // wait until the next gesture (and possibly launcher) starts. - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 5"); - } mStateCallback.addCallback(STATE_GESTURE_STARTED, initAnimFactory); } else { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 6"); - } initAnimFactory.run(); } } @@ -577,30 +440,7 @@ public class WindowTransformSwipeHandler return TaskView.getCurveScaleForInterpolation(interpolation); } - public Consumer getRecentsViewDispatcher(RotationMode rotationMode) { - return mRecentsView != null ? mRecentsView.getEventDispatcher(rotationMode) : null; - } - - @UiThread - public void updateDisplacement(float displacement) { - // We are moving in the negative x/y direction - displacement = -displacement; - if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) { - mCurrentShift.updateValue(mDragLengthFactor); - } else { - float translation = Math.max(displacement, 0); - float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength; - if (shift > DRAG_LENGTH_FACTOR_START_PULLBACK) { - float pullbackProgress = Utilities.getProgress(shift, - DRAG_LENGTH_FACTOR_START_PULLBACK, mDragLengthFactor); - pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress); - shift = DRAG_LENGTH_FACTOR_START_PULLBACK + pullbackProgress - * (DRAG_LENGTH_FACTOR_MAX_PULLBACK - DRAG_LENGTH_FACTOR_START_PULLBACK); - } - mCurrentShift.updateValue(shift); - } - } - + @Override public void onMotionPauseChanged(boolean isPaused) { setShelfState(isPaused ? PEEK : HIDE, OVERSHOOT_1_2, SHELF_ANIM_DURATION); } @@ -651,6 +491,7 @@ public class WindowTransformSwipeHandler mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate); } + @Override public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) { mIsLikelyToStartNewTask = isLikelyToStartNewTask; @@ -666,9 +507,8 @@ public class WindowTransformSwipeHandler if (mIsShelfPeeking != wasShelfPeeking) { maybeUpdateRecentsAttachedState(); } - if (mRecentsView != null && shelfState.shouldPreformHaptic) { - mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, - HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + if (shelfState.shouldPreformHaptic) { + performHapticFeedback(); } } @@ -697,19 +537,18 @@ public class WindowTransformSwipeHandler updateLauncherTransitionProgress(); } - @UiThread - private void updateFinalShift() { - float shift = mCurrentShift.value; + @Override + public Intent getLaunchIntent() { + return mOverviewComponentObserver.getOverviewIntent(); + } + + @Override + public void updateFinalShift() { SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController(); if (controller != null) { - float offsetX = mRecentsView == null ? 0 : mRecentsView.getScrollOffset(); - float offsetScale = getTaskCurveScaleForOffsetX(offsetX, - mClipAnimationHelper.getTargetRect().width()); - mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale); - mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, - mTransformParams); - updateSysUiFlags(shift); + applyTransformUnchecked(); + updateSysUiFlags(mCurrentShift.value); } if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { @@ -722,9 +561,8 @@ public class WindowTransformSwipeHandler final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW; if (passed != mPassedOverviewThreshold) { mPassedOverviewThreshold = passed; - if (mRecentsView != null && mMode != Mode.NO_BUTTON) { - mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, - HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + if (mMode != Mode.NO_BUTTON) { + performHapticFeedback(); } } @@ -767,39 +605,7 @@ public class WindowTransformSwipeHandler @Override public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) { - DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext); - final Rect overviewStackBounds; - RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId); - - if (targetSet.minimizedHomeBounds != null && runningTaskTarget != null) { - overviewStackBounds = mActivityControlHelper - .getOverviewWindowBounds(targetSet.minimizedHomeBounds, runningTaskTarget); - dp = dp.getMultiWindowProfile(mContext, new Point( - targetSet.minimizedHomeBounds.width(), targetSet.minimizedHomeBounds.height())); - dp.updateInsets(targetSet.homeContentInsets); - } else { - if (mActivity != null) { - int loc[] = new int[2]; - View rootView = mActivity.getRootView(); - rootView.getLocationOnScreen(loc); - overviewStackBounds = new Rect(loc[0], loc[1], loc[0] + rootView.getWidth(), - loc[1] + rootView.getHeight()); - } else { - overviewStackBounds = new Rect(0, 0, dp.widthPx, dp.heightPx); - } - // If we are not in multi-window mode, home insets should be same as system insets. - dp = dp.copy(mContext); - dp.updateInsets(targetSet.homeContentInsets); - } - dp.updateIsSeascape(mContext.getSystemService(WindowManager.class)); - - if (runningTaskTarget != null) { - mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget); - } - mClipAnimationHelper.prepareAnimation(dp, false /* isOpening */); - initTransitionEndpoints(dp); - - mRecentsAnimationWrapper.setController(targetSet); + super.onRecentsAnimationStart(targetSet); TOUCH_INTERACTION_LOG.addLog("startRecentsAnimationCallback", targetSet.apps.length); setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED); @@ -814,7 +620,7 @@ public class WindowTransformSwipeHandler TOUCH_INTERACTION_LOG.addLog("cancelRecentsAnimation"); } - @UiThread + @Override public void onGestureStarted() { notifyGestureStartedAsync(); mShiftAtGestureStart = mCurrentShift.value; @@ -838,7 +644,7 @@ public class WindowTransformSwipeHandler /** * Called as a result on ACTION_CANCEL to return the UI to the start state. */ - @UiThread + @Override public void onGestureCancelled() { updateDisplacement(0); setStateOnUiThread(STATE_GESTURE_COMPLETED); @@ -851,7 +657,7 @@ public class WindowTransformSwipeHandler * @param velocity The x and y components of the velocity when the gesture ends. * @param downPos The x and y value of where the gesture started. */ - @UiThread + @Override public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) { float flingThreshold = mContext.getResources() .getDimension(R.dimen.quickstep_fling_threshold_velocity); @@ -869,9 +675,9 @@ public class WindowTransformSwipeHandler handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */); } - @UiThread - private InputConsumer createNewInputProxyHandler() { - endRunningWindowAnim(); + @Override + protected InputConsumer createNewInputProxyHandler() { + endRunningWindowAnim(mGestureEndTarget == HOME /* cancel */); endLauncherTransitionController(); if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) { // Hide the task view, if not already hidden @@ -883,9 +689,13 @@ public class WindowTransformSwipeHandler ? InputConsumer.NO_OP : new OverviewInputConsumer(activity, null, true); } - private void endRunningWindowAnim() { + private void endRunningWindowAnim(boolean cancel) { if (mRunningWindowAnim != null) { - mRunningWindowAnim.end(); + if (cancel) { + mRunningWindowAnim.cancel(); + } else { + mRunningWindowAnim.end(); + } } } @@ -926,18 +736,19 @@ public class WindowTransformSwipeHandler : LAST_TASK; } } else { - if (mMode == Mode.NO_BUTTON && endVelocity < 0 && !mIsShelfPeeking) { + // If swiping at a diagonal, base end target on the faster velocity. + boolean isSwipeUp = endVelocity < 0; + boolean willGoToNewTaskOnSwipeUp = + goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity); + + if (mMode == Mode.NO_BUTTON && isSwipeUp && !willGoToNewTaskOnSwipeUp) { + endTarget = HOME; + } else if (mMode == Mode.NO_BUTTON && isSwipeUp && !mIsShelfPeeking) { // If swiping at a diagonal, base end target on the faster velocity. - endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity) - ? NEW_TASK : HOME; - } else if (endVelocity < 0) { - if (reachedOverviewThreshold) { - endTarget = RECENTS; - } else { - // If swiping at a diagonal, base end target on the faster velocity. - endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity) - ? NEW_TASK : RECENTS; - } + endTarget = NEW_TASK; + } else if (isSwipeUp) { + endTarget = !reachedOverviewThreshold && willGoToNewTaskOnSwipeUp + ? NEW_TASK : RECENTS; } else { endTarget = goingToNewTask ? NEW_TASK : LAST_TASK; } @@ -970,14 +781,14 @@ public class WindowTransformSwipeHandler interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL; } else { startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y - * SINGLE_FRAME_MS / mTransitionDragLength, 0, mDragLengthFactor); + * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); float minFlingVelocity = mContext.getResources() .getDimension(R.dimen.quickstep_fling_min_velocity); if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) { if (endTarget == RECENTS && mMode != Mode.NO_BUTTON) { Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams( - startShift, endShift, endShift, velocityPxPerMs.y, - mTransitionDragLength); + startShift, endShift, endShift, endVelocity / 1000, + mTransitionDragLength, mContext); endShift = overshoot.end; interpolator = overshoot.interpolator; duration = Utilities.boundToRange(overshoot.duration, MIN_OVERSHOOT_DURATION, @@ -1151,57 +962,15 @@ public class WindowTransformSwipeHandler * @param startProgress The progress of {@link #mCurrentShift} to start the window from. * @param homeAnimationFactory The home animation factory. */ - private RectFSpringAnim createWindowAnimationToHome(float startProgress, + @Override + protected RectFSpringAnim createWindowAnimationToHome(float startProgress, HomeAnimationFactory homeAnimationFactory) { - final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet; - final RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet, - mTransformParams.setProgress(startProgress), false /* launcherOnTop */)); - final RectF targetRect = homeAnimationFactory.getWindowTargetRect(); - - final View floatingView = homeAnimationFactory.getFloatingView(); - final boolean isFloatingIconView = floatingView instanceof FloatingIconView; - RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mActivity.getResources()); - if (isFloatingIconView) { - FloatingIconView fiv = (FloatingIconView) floatingView; - anim.addAnimatorListener(fiv); - fiv.setOnTargetChangeListener(anim::onTargetPositionChanged); - } - - AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome(); - - // End on a "round-enough" radius so that the shape reveal doesn't have to do too much - // rounding at the end of the animation. - float startRadius = mClipAnimationHelper.getCurrentCornerRadius(); - float endRadius = startRect.width() / 6f; - // We want the window alpha to be 0 once this threshold is met, so that the - // FolderIconView can be seen morphing into the icon shape. - final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f; - anim.addOnUpdateListener((currentRect, progress) -> { - homeAnim.setPlayFraction(progress); - - float alphaProgress = ACCEL_1_5.getInterpolation(progress); - float windowAlpha = Utilities.boundToRange(Utilities.mapToRange(alphaProgress, 0, - windowAlphaThreshold, 1.5f, 0f, Interpolators.LINEAR), 0, 1); - mTransformParams.setProgress(progress) - .setCurrentRectAndTargetAlpha(currentRect, windowAlpha); - if (isFloatingIconView) { - mTransformParams.setCornerRadius(endRadius * progress + startRadius - * (1f - progress)); - } - mClipAnimationHelper.applyTransform(targetSet, mTransformParams, - false /* launcherOnTop */); - - if (isFloatingIconView) { - ((FloatingIconView) floatingView).update(currentRect, 1f, progress, - windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false); - } - - updateSysUiFlags(Math.max(progress, mCurrentShift.value)); - }); + RectFSpringAnim anim = + super.createWindowAnimationToHome(startProgress, homeAnimationFactory); + anim.addOnUpdateListener((r, p) -> updateSysUiFlags(Math.max(p, mCurrentShift.value))); anim.addAnimatorListener(new AnimationSuccessListener() { @Override public void onAnimationStart(Animator animation) { - homeAnim.dispatchOnStart(); if (mActivity != null) { mActivity.getRootView().getOverlay().remove(mLiveTileOverlay); } @@ -1209,7 +978,6 @@ public class WindowTransformSwipeHandler @Override public void onAnimationSuccess(Animator animator) { - homeAnim.getAnimationPlayer().end(); if (mRecentsView != null) { mRecentsView.post(mRecentsView::resetTaskVisuals); } @@ -1221,11 +989,18 @@ public class WindowTransformSwipeHandler return anim; } - /** - * @return The GestureEndTarget if the gesture has ended, else null. - */ - public @Nullable GestureEndTarget getGestureEndTarget() { - return mGestureEndTarget; + @Override + public void onConsumerAboutToBeSwitched(SwipeSharedState sharedState) { + if (mGestureEndTarget != null) { + sharedState.canGestureBeContinued = mGestureEndTarget.canBeContinued; + sharedState.goingToLauncher = mGestureEndTarget.isLauncher; + } + + if (sharedState.canGestureBeContinued) { + cancelCurrentAnimation(sharedState); + } else { + reset(); + } } @UiThread @@ -1238,43 +1013,18 @@ public class WindowTransformSwipeHandler @UiThread private void startNewTask() { - // Launch the task user scrolled to (mRecentsView.getNextPage()). - if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { - // We finish recents animation inside launchTask() when live tile is enabled. - mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */, - true /* freezeTaskList */); - } else { - int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id; - mFinishingRecentsAnimationForNewTaskId = taskId; - mRecentsAnimationWrapper.finish(true /* toRecents */, () -> { - if (!mCanceled) { - TaskView nextTask = mRecentsView.getTaskView(taskId); - if (nextTask != null) { - nextTask.launchTask(false /* animate */, true /* freezeTaskList */, - success -> { - if (!success) { - // We couldn't launch the task, so take user to overview so they can - // decide what to do instead of staying in this broken state. - endLauncherTransitionController(); - mActivityControlHelper.onLaunchTaskFailed(mActivity); - nextTask.notifyTaskLaunchFailed(TAG); - updateSysUiFlags(1 /* windowProgress == overview */); - } else { - mActivityControlHelper.onLaunchTaskSuccess(mActivity); - } - }, mMainThreadHandler); - doLogGesture(NEW_TASK); - } - reset(); - } - mCanceled = false; - mFinishingRecentsAnimationForNewTaskId = -1; - }); - } - TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true); + startNewTask(STATE_HANDLER_INVALIDATED, success -> { + if (!success) { + // We couldn't launch the task, so take user to overview so they can + // decide what to do instead of staying in this broken state. + endLauncherTransitionController(); + updateSysUiFlags(1 /* windowProgress == overview */); + } + doLogGesture(NEW_TASK); + }); } - public void reset() { + private void reset() { setStateOnUiThread(STATE_HANDLER_INVALIDATED); } @@ -1282,7 +1032,7 @@ public class WindowTransformSwipeHandler * Cancels any running animation so that the active target can be overriden by a new swipe * handle (in case of quick switch). */ - public void cancelCurrentAnimation(SwipeSharedState sharedState) { + private void cancelCurrentAnimation(SwipeSharedState sharedState) { mCanceled = true; mCurrentShift.cancelAnimation(); if (mLauncherTransitionController != null && mLauncherTransitionController @@ -1305,7 +1055,7 @@ public class WindowTransformSwipeHandler } private void invalidateHandler() { - endRunningWindowAnim(); + endRunningWindowAnim(false /* cancel */); if (mGestureEndCallback != null) { mGestureEndCallback.run(); @@ -1436,7 +1186,8 @@ public class WindowTransformSwipeHandler private void setupLauncherUiAfterSwipeUpToRecentsAnimation() { endLauncherTransitionController(); mActivityControlHelper.onSwipeUpToRecentsComplete(mActivity); - mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true); + mRecentsAnimationWrapper.setDeferCancelUntilNextTransition(true /* defer */, + true /* screenshot */); mRecentsView.onSwipeUpAnimationSuccess(); RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG); @@ -1445,17 +1196,12 @@ public class WindowTransformSwipeHandler reset(); } - public void setGestureEndCallback(Runnable gestureEndCallback) { - mGestureEndCallback = gestureEndCallback; - } - - private void setTargetAlphaProvider( - BiFunction provider) { + private void setTargetAlphaProvider(TargetAlphaProvider provider) { mClipAnimationHelper.setTaskAlphaCallback(provider); updateFinalShift(); } - public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, Float expectedAlpha) { + public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, float expectedAlpha) { if (!isNotInRecents(app)) { return 0; } @@ -1466,16 +1212,4 @@ public class WindowTransformSwipeHandler return app.isNotInRecents || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; } - - private interface RunningWindowAnim { - void end(); - - static RunningWindowAnim wrap(Animator animator) { - return animator::end; - } - - static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) { - return rectFSpringAnim::end; - } - } } 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 c28761804b..2e9c0a3c4d 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 @@ -17,6 +17,7 @@ package com.android.quickstep.fallback; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; @@ -31,6 +32,10 @@ import com.android.quickstep.RecentsActivity; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.Task.TaskKey; + +import java.util.ArrayList; public class FallbackRecentsView extends RecentsView { @@ -54,6 +59,8 @@ public class FallbackRecentsView extends RecentsView { private float mZoomScale = 1f; private float mZoomTranslationY = 0f; + private RunningTaskInfo mRunningTaskInfo; + public FallbackRecentsView(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -87,6 +94,12 @@ public class FallbackRecentsView extends RecentsView { super.draw(canvas); } + @Override + public void reset() { + super.reset(); + resetViewUI(); + } + @Override protected void getTaskSize(DeviceProfile dp, Rect outRect) { LayoutUtils.calculateFallbackTaskSize(getContext(), dp, outRect); @@ -114,6 +127,12 @@ public class FallbackRecentsView extends RecentsView { } } + @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); @@ -139,4 +158,41 @@ public class FallbackRecentsView extends RecentsView { TRANSLATION_Y.set(this, Utilities.mapRange(mZoomInProgress, 0, mZoomTranslationY)); FULLSCREEN_PROGRESS.set(this, mZoomInProgress); } + + public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) { + mRunningTaskInfo = runningTaskInfo; + onGestureAnimationStart(runningTaskInfo == null ? -1 : runningTaskInfo.taskId); + } + + @Override + public void setCurrentTask(int runningTaskId) { + super.setCurrentTask(runningTaskId); + if (mRunningTaskInfo != null && mRunningTaskInfo.taskId != runningTaskId) { + mRunningTaskInfo = null; + } + } + + @Override + protected void applyLoadPlan(ArrayList tasks) { + // When quick-switching on 3p-launcher, we add a "dummy" tile corresponding to Launcher + // as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to + // track the index of the next task appropriately, as it we are switching on any other app. + if (mRunningTaskInfo != null && mRunningTaskInfo.taskId == mRunningTaskId) { + // Check if the task list has running task + boolean found = false; + for (Task t : tasks) { + if (t.key.id == mRunningTaskId) { + found = true; + break; + } + } + if (!found) { + ArrayList newList = new ArrayList<>(tasks.size() + 1); + newList.addAll(tasks); + newList.add(Task.from(new TaskKey(mRunningTaskInfo), mRunningTaskInfo, false)); + tasks = newList; + } + } + super.applyLoadPlan(tasks); + } } 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 db2af59aca..3d763ab52c 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 @@ -23,6 +23,7 @@ import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.Utilities.squaredTouchSlop; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW; +import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync; import android.content.ComponentName; import android.content.Context; @@ -44,8 +45,6 @@ import com.android.quickstep.SwipeSharedState; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.util.RecentsAnimationListenerSet; import com.android.quickstep.util.SwipeAnimationTargetSet; -import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.BackgroundExecutor; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -209,9 +208,7 @@ public class DeviceLockedInputConsumer implements InputConsumer, .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); mInputMonitorCompat.pilferPointers(); - BackgroundExecutor.get().submit( - () -> ActivityManagerWrapper.getInstance().startRecentsActivity( - intent, null, newListenerSet, null, null)); + startRecentsActivityAsync(intent, newListenerSet); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java index d05ca2a161..6ec1da0c48 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java @@ -15,344 +15,443 @@ */ package com.android.quickstep.inputconsumers; -import static android.view.MotionEvent.ACTION_CANCEL; -import static android.view.MotionEvent.ACTION_DOWN; -import static android.view.MotionEvent.ACTION_MOVE; -import static android.view.MotionEvent.ACTION_POINTER_DOWN; -import static android.view.MotionEvent.ACTION_POINTER_UP; -import static android.view.MotionEvent.ACTION_UP; -import static android.view.MotionEvent.INVALID_POINTER_ID; - -import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION; +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.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW; -import static com.android.quickstep.WindowTransformSwipeHandler.MIN_SWIPE_DURATION; -import static com.android.quickstep.inputconsumers.OtherActivityInputConsumer.QUICKSTEP_TOUCH_SLOP_RATIO; -import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; +import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.HOME; +import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.LAST_TASK; +import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.NEW_TASK; +import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.RECENTS; +import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; +import android.animation.AnimatorSet; import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.ViewConfiguration; -import android.view.WindowManager; +import android.os.Bundle; -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.R; -import com.android.quickstep.ActivityControlHelper; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory; +import com.android.quickstep.AnimatedFloat; +import com.android.quickstep.BaseSwipeUpHandler; +import com.android.quickstep.MultiStateCallback; import com.android.quickstep.OverviewComponentObserver; +import com.android.quickstep.RecentsActivity; +import com.android.quickstep.RecentsModel; import com.android.quickstep.SwipeSharedState; -import com.android.quickstep.util.ClipAnimationHelper; -import com.android.quickstep.util.ClipAnimationHelper.TransformParams; -import com.android.quickstep.util.NavBarPosition; -import com.android.quickstep.util.RecentsAnimationListenerSet; +import com.android.quickstep.fallback.FallbackRecentsView; +import com.android.quickstep.util.ObjectWrapper; +import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.SwipeAnimationTargetSet; -import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; +import com.android.quickstep.views.RecentsView; +import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.BackgroundExecutor; -import com.android.systemui.shared.system.InputMonitorCompat; -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import com.android.systemui.shared.system.ActivityOptionsCompat; +import com.android.systemui.shared.system.InputConsumerController; -public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimationListener { +public class FallbackNoButtonInputConsumer extends + BaseSwipeUpHandler { - private static final int STATE_NOT_FINISHED = 0; - private static final int STATE_FINISHED_TO_HOME = 1; - private static final int STATE_FINISHED_TO_APP = 2; + private static final String[] STATE_NAMES = DEBUG_STATES ? new String[5] : null; - private static final float PROGRESS_TO_END_GESTURE = -2; + private static int getFlagForIndex(int index, String name) { + if (DEBUG_STATES) { + STATE_NAMES[index] = name; + } + return 1 << index; + } - private final ActivityControlHelper mActivityControlHelper; - private final InputMonitorCompat mInputMonitor; - private final Context mContext; - private final NavBarPosition mNavBarPosition; - private final SwipeSharedState mSwipeSharedState; - private final OverviewComponentObserver mOverviewComponentObserver; - private final int mRunningTaskId; + private static final int STATE_RECENTS_PRESENT = + getFlagForIndex(0, "STATE_RECENTS_PRESENT"); + private static final int STATE_HANDLER_INVALIDATED = + getFlagForIndex(1, "STATE_HANDLER_INVALIDATED"); - private final ClipAnimationHelper mClipAnimationHelper; - private final TransformParams mTransformParams = new TransformParams(); - private final float mTransitionDragLength; - private final DeviceProfile mDP; + private static final int STATE_GESTURE_CANCELLED = + getFlagForIndex(2, "STATE_GESTURE_CANCELLED"); + private static final int STATE_GESTURE_COMPLETED = + getFlagForIndex(3, "STATE_GESTURE_COMPLETED"); + private static final int STATE_APP_CONTROLLER_RECEIVED = + getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED"); - private final RectF mSwipeTouchRegion; - private final boolean mDisableHorizontalSwipe; + public enum GestureEndTarget { + HOME(3, 100, 1), + RECENTS(1, 300, 0), + LAST_TASK(0, 150, 1), + NEW_TASK(0, 150, 1); - private final PointF mDownPos = new PointF(); - private final PointF mLastPos = new PointF(); + private final float mEndProgress; + private final long mDurationMultiplier; + private final float mLauncherAlpha; - private int mActivePointerId = -1; - // Slop used to determine when we say that the gesture has started. - private boolean mPassedPilferInputSlop; + GestureEndTarget(float endProgress, long durationMultiplier, float launcherAlpha) { + mEndProgress = endProgress; + mDurationMultiplier = durationMultiplier; + mLauncherAlpha = launcherAlpha; + } + } - private VelocityTracker mVelocityTracker; + private final AnimatedFloat mLauncherAlpha = new AnimatedFloat(this::onLauncherAlphaChanged); - // Distance after which we start dragging the window. - private final float mTouchSlop; + private boolean mIsMotionPaused = false; + private GestureEndTarget mEndTarget; - // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar. - private float mStartDisplacement; - private SwipeAnimationTargetSet mSwipeAnimationTargetSet; - private float mProgress; + private final boolean mInQuickSwitchMode; + private final boolean mContinuingLastGesture; + private final boolean mRunningOverHome; + private final boolean mSwipeUpOverHome; - private int mState = STATE_NOT_FINISHED; + private final RunningTaskInfo mRunningTaskInfo; + + private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f); + private RunningWindowAnim mFinishAnimation; public FallbackNoButtonInputConsumer(Context context, - ActivityControlHelper activityControlHelper, InputMonitorCompat inputMonitor, - SwipeSharedState swipeSharedState, RectF swipeTouchRegion, OverviewComponentObserver overviewComponentObserver, - boolean disableHorizontalSwipe, RunningTaskInfo runningTaskInfo) { - mContext = context; - mActivityControlHelper = activityControlHelper; - mInputMonitor = inputMonitor; - mOverviewComponentObserver = overviewComponentObserver; - mRunningTaskId = runningTaskInfo.id; + RunningTaskInfo runningTaskInfo, RecentsModel recentsModel, + InputConsumerController inputConsumer, + boolean isLikelyToStartNewTask, boolean continuingLastGesture) { + super(context, overviewComponentObserver, recentsModel, inputConsumer, runningTaskInfo.id); + mLauncherAlpha.value = 1; - mSwipeSharedState = swipeSharedState; - mSwipeTouchRegion = swipeTouchRegion; - mDisableHorizontalSwipe = disableHorizontalSwipe; + mRunningTaskInfo = runningTaskInfo; + mInQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture; + mContinuingLastGesture = continuingLastGesture; + mRunningOverHome = ActivityManagerWrapper.isHomeTask(runningTaskInfo); + mSwipeUpOverHome = mRunningOverHome && !mInQuickSwitchMode; - mNavBarPosition = new NavBarPosition(context); - mVelocityTracker = VelocityTracker.obtain(); - - mTouchSlop = QUICKSTEP_TOUCH_SLOP_RATIO - * ViewConfiguration.get(context).getScaledTouchSlop(); - - mClipAnimationHelper = new ClipAnimationHelper(context); - - mDP = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context).copy(context); - Rect tempRect = new Rect(); - mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength( - mDP, context, tempRect); - mClipAnimationHelper.updateTargetRect(tempRect); - } - - @Override - public int getType() { - return TYPE_FALLBACK_NO_BUTTON; - } - - @Override - public void onMotionEvent(MotionEvent ev) { - if (mVelocityTracker == null) { - return; - } - - mVelocityTracker.addMovement(ev); - if (ev.getActionMasked() == ACTION_POINTER_UP) { - mVelocityTracker.clear(); - } - - switch (ev.getActionMasked()) { - case ACTION_DOWN: { - mActivePointerId = ev.getPointerId(0); - mDownPos.set(ev.getX(), ev.getY()); - mLastPos.set(mDownPos); - break; - } - case ACTION_POINTER_DOWN: { - if (!mPassedPilferInputSlop) { - // Cancel interaction in case of multi-touch interaction - int ptrIdx = ev.getActionIndex(); - if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) { - forceCancelGesture(ev); - } - } - break; - } - case ACTION_POINTER_UP: { - int ptrIdx = ev.getActionIndex(); - int ptrId = ev.getPointerId(ptrIdx); - if (ptrId == mActivePointerId) { - final int newPointerIdx = ptrIdx == 0 ? 1 : 0; - mDownPos.set( - ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x), - ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y)); - mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx)); - mActivePointerId = ev.getPointerId(newPointerIdx); - } - break; - } - case ACTION_MOVE: { - int pointerIndex = ev.findPointerIndex(mActivePointerId); - if (pointerIndex == INVALID_POINTER_ID) { - break; - } - mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); - float displacement = getDisplacement(ev); - - if (!mPassedPilferInputSlop) { - if (mDisableHorizontalSwipe && Math.abs(mLastPos.x - mDownPos.x) - > Math.abs(mLastPos.y - mDownPos.y)) { - // Horizontal gesture is not allowed in this region - forceCancelGesture(ev); - break; - } - - if (Math.abs(displacement) >= mTouchSlop) { - mPassedPilferInputSlop = true; - - // Deferred gesture, start the animation and gesture tracking once - // we pass the actual touch slop - startTouchTrackingForWindowAnimation(displacement); - } - } else { - updateDisplacement(displacement - mStartDisplacement); - } - break; - } - case ACTION_CANCEL: - case ACTION_UP: { - finishTouchTracking(ev); - break; - } - } - } - - private void startTouchTrackingForWindowAnimation(float displacement) { - mStartDisplacement = Math.min(displacement, -mTouchSlop); - - RecentsAnimationListenerSet listenerSet = - mSwipeSharedState.newRecentsAnimationListenerSet(); - listenerSet.addListener(this); - Intent homeIntent = mOverviewComponentObserver.getHomeIntent(); - BackgroundExecutor.get().submit( - () -> ActivityManagerWrapper.getInstance().startRecentsActivity( - homeIntent, null, listenerSet, null, null)); - - ActivityManagerWrapper.getInstance().closeSystemWindows( - CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); - mInputMonitor.pilferPointers(); - } - - private void updateDisplacement(float displacement) { - mProgress = displacement / mTransitionDragLength; - mTransformParams.setProgress(mProgress); - - if (mSwipeAnimationTargetSet != null) { - mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); - } - } - - private void forceCancelGesture(MotionEvent ev) { - int action = ev.getAction(); - ev.setAction(ACTION_CANCEL); - finishTouchTracking(ev); - ev.setAction(action); - } - - /** - * Called when the gesture has ended. Does not correlate to the completion of the interaction as - * the animation can still be running. - */ - private void finishTouchTracking(MotionEvent ev) { - if (ev.getAction() == ACTION_CANCEL) { - mState = STATE_FINISHED_TO_APP; + if (mSwipeUpOverHome) { + mClipAnimationHelper.setBaseAlphaCallback((t, a) -> 1 - mLauncherAlpha.value); + } else { + mClipAnimationHelper.setBaseAlphaCallback((t, a) -> mLauncherAlpha.value); + } + + initStateCallbacks(); + } + + private void initStateCallbacks() { + mStateCallback = new MultiStateCallback(STATE_NAMES); + + mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, + this::onHandlerInvalidated); + mStateCallback.addCallback(STATE_RECENTS_PRESENT | STATE_HANDLER_INVALIDATED, + this::onHandlerInvalidatedWithRecents); + + mStateCallback.addCallback(STATE_GESTURE_CANCELLED | STATE_APP_CONTROLLER_RECEIVED, + this::finishAnimationTargetSetAnimationComplete); + + if (mInQuickSwitchMode) { + mStateCallback.addCallback(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED + | STATE_RECENTS_PRESENT, + this::finishAnimationTargetSet); + } else { + mStateCallback.addCallback(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED, + this::finishAnimationTargetSet); + } + } + + private void onLauncherAlphaChanged() { + if (mRecentsAnimationWrapper.targetSet != null && mEndTarget == null) { + applyTransformUnchecked(); + } + } + + @Override + protected boolean onActivityInit(final RecentsActivity activity, Boolean alreadyOnHome) { + mActivity = activity; + mRecentsView = activity.getOverviewPanel(); + linkRecentsViewScroll(); + mRecentsView.setDisallowScrollToClearAll(true); + mRecentsView.getClearAllButton().setVisibilityAlpha(0); + + mRecentsView.setZoomProgress(1); + + if (!mContinuingLastGesture) { + if (mRunningOverHome) { + mRecentsView.onGestureAnimationStart(mRunningTaskInfo); + } else { + mRecentsView.onGestureAnimationStart(mRunningTaskId); + } + } + setStateOnUiThread(STATE_RECENTS_PRESENT); + return true; + } + + @Override + protected boolean moveWindowWithRecentsScroll() { + return mInQuickSwitchMode; + } + + @Override + public void initWhenReady() { + if (mInQuickSwitchMode) { + // Only init if we are in quickswitch mode + super.initWhenReady(); + } + } + + @Override + public void updateDisplacement(float displacement) { + if (!mInQuickSwitchMode) { + super.updateDisplacement(displacement); + } + } + + @Override + protected InputConsumer createNewInputProxyHandler() { + // Just consume all input on the active task + return InputConsumer.NO_OP; + } + + @Override + public void onMotionPauseChanged(boolean isPaused) { + if (!mInQuickSwitchMode) { + mIsMotionPaused = isPaused; + mLauncherAlpha.animateToValue(mLauncherAlpha.value, isPaused ? 0 : 1) + .setDuration(150).start(); + performHapticFeedback(); + } + } + + @Override + public Intent getLaunchIntent() { + if (mInQuickSwitchMode || mSwipeUpOverHome) { + return mOverviewComponentObserver.getOverviewIntent(); + } else { + return mOverviewComponentObserver.getHomeIntent(); + } + } + + @Override + public void updateFinalShift() { + mTransformParams.setProgress(mCurrentShift.value); + mRecentsAnimationWrapper.setWindowThresholdCrossed(!mInQuickSwitchMode + && (mCurrentShift.value > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD)); + if (mRecentsAnimationWrapper.targetSet != null) { + applyTransformUnchecked(); + } + } + + @Override + public void onGestureCancelled() { + updateDisplacement(0); + mEndTarget = LAST_TASK; + setStateOnUiThread(STATE_GESTURE_CANCELLED); + } + + @Override + public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) { + mEndVelocityPxPerMs.set(0, velocity.y / 1000); + if (mInQuickSwitchMode) { + // For now set it to non-null, it will be reset before starting the animation + mEndTarget = LAST_TASK; } else { - mVelocityTracker.computeCurrentVelocity(1000, - ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity()); - float velocityX = mVelocityTracker.getXVelocity(mActivePointerId); - float velocityY = mVelocityTracker.getYVelocity(mActivePointerId); - float velocity = mNavBarPosition.isRightEdge() ? velocityX - : mNavBarPosition.isLeftEdge() ? -velocityX - : velocityY; float flingThreshold = mContext.getResources() .getDimension(R.dimen.quickstep_fling_threshold_velocity); - boolean isFling = Math.abs(velocity) > flingThreshold; + boolean isFling = Math.abs(endVelocity) > flingThreshold; - boolean goingHome; - if (!isFling) { - goingHome = -mProgress >= MIN_PROGRESS_FOR_OVERVIEW; + if (isFling) { + mEndTarget = endVelocity < 0 ? HOME : LAST_TASK; + } else if (mIsMotionPaused) { + mEndTarget = RECENTS; } else { - goingHome = velocity < 0; + mEndTarget = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? HOME : LAST_TASK; + } + } + setStateOnUiThread(STATE_GESTURE_COMPLETED); + } + + @Override + public void onConsumerAboutToBeSwitched(SwipeSharedState sharedState) { + if (mInQuickSwitchMode && mEndTarget != null) { + sharedState.canGestureBeContinued = true; + sharedState.goingToLauncher = false; + + mCanceled = true; + mCurrentShift.cancelAnimation(); + if (mFinishAnimation != null) { + mFinishAnimation.cancel(); } - if (goingHome) { - mState = STATE_FINISHED_TO_HOME; - } else { - mState = STATE_FINISHED_TO_APP; + if (mRecentsView != null) { + if (mFinishingRecentsAnimationForNewTaskId != -1) { + TaskView newRunningTaskView = mRecentsView.getTaskView( + mFinishingRecentsAnimationForNewTaskId); + int newRunningTaskId = newRunningTaskView != null + ? newRunningTaskView.getTask().key.id + : -1; + mRecentsView.setCurrentTask(newRunningTaskId); + sharedState.setRecentsAnimationFinishInterrupted(newRunningTaskId); + } + mRecentsView.setOnScrollChangeListener(null); + } + } else { + setStateOnUiThread(STATE_HANDLER_INVALIDATED); + } + } + + private void onHandlerInvalidated() { + mActivityInitListener.unregister(); + if (mGestureEndCallback != null) { + mGestureEndCallback.run(); + } + if (mFinishAnimation != null) { + mFinishAnimation.end(); + } + } + + private void onHandlerInvalidatedWithRecents() { + mRecentsView.onGestureAnimationEnd(); + mRecentsView.setDisallowScrollToClearAll(false); + mRecentsView.getClearAllButton().setVisibilityAlpha(1); + } + + private void finishAnimationTargetSetAnimationComplete() { + switch (mEndTarget) { + case HOME: { + if (mSwipeUpOverHome) { + mRecentsAnimationWrapper.finish(false, null, false); + // Send a home intent to clear the task stack + mContext.startActivity(mOverviewComponentObserver.getHomeIntent()); + } else { + mRecentsAnimationWrapper.finish(true, null, true); + } + break; + } + case LAST_TASK: + mRecentsAnimationWrapper.finish(false, null, false); + break; + case RECENTS: { + if (mSwipeUpOverHome) { + mRecentsAnimationWrapper.finish(true, null, true); + break; + } + + ThumbnailData thumbnail = + mRecentsAnimationWrapper.targetSet.controller.screenshotTask(mRunningTaskId); + mRecentsAnimationWrapper.setDeferCancelUntilNextTransition(true /* defer */, + false /* screenshot */); + + ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); + ActivityOptionsCompat.setFreezeRecentTasksList(options); + + Bundle extras = new Bundle(); + extras.putBinder(EXTRA_THUMBNAIL, new ObjectWrapper<>(thumbnail)); + extras.putInt(EXTRA_TASK_ID, mRunningTaskId); + + Intent intent = new Intent(mOverviewComponentObserver.getOverviewIntent()) + .putExtras(extras); + mContext.startActivity(intent, options.toBundle()); + mRecentsAnimationWrapper.targetSet.controller.cleanupScreenshot(); + break; + } + case NEW_TASK: { + startNewTask(STATE_HANDLER_INVALIDATED, b -> {}); + break; } } - if (mSwipeAnimationTargetSet != null) { - finishAnimationTargetSet(); - } + setStateOnUiThread(STATE_HANDLER_INVALIDATED); } private void finishAnimationTargetSet() { - if (mState == STATE_FINISHED_TO_APP) { - mSwipeAnimationTargetSet.finishController(false, null, false); - } else { - if (mProgress < PROGRESS_TO_END_GESTURE) { - mSwipeAnimationTargetSet.finishController(true, null, true); + if (mInQuickSwitchMode) { + // Recalculate the end target, some views might have been initialized after + // gesture has ended. + if (mRecentsView == null || !mRecentsAnimationWrapper.hasTargets()) { + mEndTarget = LAST_TASK; } else { - long duration = (long) (Math.min(mProgress - PROGRESS_TO_END_GESTURE, 1) - * MAX_SWIPE_DURATION / Math.abs(PROGRESS_TO_END_GESTURE)); - if (duration < 0) { - duration = MIN_SWIPE_DURATION; - } - - ValueAnimator anim = ValueAnimator.ofFloat(mProgress, PROGRESS_TO_END_GESTURE); - anim.addUpdateListener(a -> { - float p = (Float) anim.getAnimatedValue(); - mTransformParams.setProgress(p); - mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); - }); - anim.setDuration(duration); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mSwipeAnimationTargetSet.finishController(true, null, true); - } - }); - anim.start(); + final int runningTaskIndex = mRecentsView.getRunningTaskIndex(); + final int taskToLaunch = mRecentsView.getNextPage(); + mEndTarget = (runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex) + ? NEW_TASK : LAST_TASK; } } + + float endProgress = mEndTarget.mEndProgress; + long duration = (long) (mEndTarget.mDurationMultiplier * + Math.abs(endProgress - mCurrentShift.value)); + if (mRecentsView != null) { + duration = Math.max(duration, mRecentsView.getScroller().getDuration()); + } + if (mCurrentShift.value != endProgress || mInQuickSwitchMode) { + AnimationSuccessListener endListener = new AnimationSuccessListener() { + + @Override + public void onAnimationSuccess(Animator animator) { + finishAnimationTargetSetAnimationComplete(); + mFinishAnimation = null; + } + }; + + if (mEndTarget == HOME && !mRunningOverHome) { + RectFSpringAnim anim = createWindowAnimationToHome(mCurrentShift.value, duration); + anim.addAnimatorListener(endListener); + anim.start(mEndVelocityPxPerMs); + mFinishAnimation = RunningWindowAnim.wrap(anim); + } else { + + AnimatorSet anim = new AnimatorSet(); + anim.play(mLauncherAlpha.animateToValue( + mLauncherAlpha.value, mEndTarget.mLauncherAlpha)); + anim.play(mCurrentShift.animateToValue(mCurrentShift.value, endProgress)); + + anim.setDuration(duration); + anim.addListener(endListener); + anim.start(); + mFinishAnimation = RunningWindowAnim.wrap(anim); + } + + } else { + finishAnimationTargetSetAnimationComplete(); + } } @Override public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) { - mSwipeAnimationTargetSet = targetSet; - Rect overviewStackBounds = new Rect(0, 0, mDP.widthPx, mDP.heightPx); - RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId); + super.onRecentsAnimationStart(targetSet); + mRecentsAnimationWrapper.enableInputConsumer(); - mDP.updateIsSeascape(mContext.getSystemService(WindowManager.class)); - if (runningTaskTarget != null) { - mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget); + if (mRunningOverHome) { + mClipAnimationHelper.prepareAnimation(mDp, true); } - mClipAnimationHelper.prepareAnimation(mDP, false /* isOpening */); + applyTransformUnchecked(); - overviewStackBounds - .inset(-overviewStackBounds.width() / 5, -overviewStackBounds.height() / 5); - mClipAnimationHelper.updateTargetRect(overviewStackBounds); - mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); - - if (mState != STATE_NOT_FINISHED) { - finishAnimationTargetSet(); - } + setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED); } @Override - public void onRecentsAnimationCanceled() { } - - private float getDisplacement(MotionEvent ev) { - if (mNavBarPosition.isRightEdge()) { - return ev.getX() - mDownPos.x; - } else if (mNavBarPosition.isLeftEdge()) { - return mDownPos.x - ev.getX(); - } else { - return ev.getY() - mDownPos.y; - } + public void onRecentsAnimationCanceled() { + mRecentsAnimationWrapper.setController(null); + setStateOnUiThread(STATE_HANDLER_INVALIDATED); } - @Override - public boolean allowInterceptByParent() { - return !mPassedPilferInputSlop; + /** + * Creates an animation that transforms the current app window into the home app. + * @param startProgress The progress of {@link #mCurrentShift} to start the window from. + */ + private RectFSpringAnim createWindowAnimationToHome(float startProgress, long duration) { + HomeAnimationFactory factory = new HomeAnimationFactory() { + @Override + public RectF getWindowTargetRect() { + return HomeAnimationFactory.getDefaultWindowTargetRect(mDp); + } + + @Override + public AnimatorPlaybackController createActivityAnimationToHome() { + AnimatorSet anim = new AnimatorSet(); + anim.play(mLauncherAlpha.animateToValue(mLauncherAlpha.value, 1)); + anim.setDuration(duration); + return AnimatorPlaybackController.wrap(anim, duration); + } + }; + return createWindowAnimationToHome(startProgress, factory); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java index f5cf654b15..a1e5d47a53 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java @@ -33,7 +33,6 @@ public interface InputConsumer { int TYPE_SCREEN_PINNED = 1 << 6; int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7; int TYPE_RESET_GESTURE = 1 << 8; - int TYPE_FALLBACK_NO_BUTTON = 1 << 9; String[] NAMES = new String[] { "TYPE_NO_OP", // 0 @@ -45,7 +44,6 @@ public interface InputConsumer { "TYPE_SCREEN_PINNED", // 6 "TYPE_OVERVIEW_WITHOUT_FOCUS", // 7 "TYPE_RESET_GESTURE", // 8 - "TYPE_FALLBACK_NO_BUTTON", // 9 }; InputConsumer NO_OP = () -> TYPE_NO_OP; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 4c137d3bf9..86766d99fb 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -28,13 +28,13 @@ import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.util.RaceConditionTracker.ENTER; import static com.android.launcher3.util.RaceConditionTracker.EXIT; import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; +import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.content.ContextWrapper; -import android.content.Intent; import android.graphics.PointF; import android.graphics.RectF; import android.os.Build; @@ -48,21 +48,16 @@ import com.android.launcher3.R; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RaceConditionTracker; import com.android.launcher3.util.TraceHelper; -import com.android.quickstep.ActivityControlHelper; +import com.android.quickstep.BaseSwipeUpHandler; import com.android.quickstep.OverviewCallbacks; -import com.android.quickstep.RecentsModel; import com.android.quickstep.SwipeSharedState; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; -import com.android.quickstep.WindowTransformSwipeHandler; -import com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget; import com.android.quickstep.util.CachedEventDispatcher; import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.util.NavBarPosition; import com.android.quickstep.util.RecentsAnimationListenerSet; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.BackgroundExecutor; -import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.InputMonitorCompat; import java.util.function.Consumer; @@ -83,16 +78,14 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher(); private final RunningTaskInfo mRunningTask; - private final RecentsModel mRecentsModel; - private final Intent mHomeIntent; - private final ActivityControlHelper mActivityControlHelper; private final OverviewCallbacks mOverviewCallbacks; - private final InputConsumerController mInputConsumer; private final SwipeSharedState mSwipeSharedState; private final InputMonitorCompat mInputMonitorCompat; private final SysUINavigationMode.Mode mMode; private final RectF mSwipeTouchRegion; + private final BaseSwipeUpHandler.Factory mHandlerFactory; + private final NavBarPosition mNavBarPosition; private final Consumer mOnCompleteCallback; @@ -100,7 +93,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private final float mMotionPauseMinDisplacement; private VelocityTracker mVelocityTracker; - private WindowTransformSwipeHandler mInteractionHandler; + private BaseSwipeUpHandler mInteractionHandler; private final boolean mIsDeferredDownTarget; private final PointF mDownPos = new PointF(); @@ -114,7 +107,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private final boolean mDisableHorizontalSwipe; // Slop used to check when we start moving window. - private boolean mPaddedWindowMoveSlop; + private boolean mPassedWindowMoveSlop; // Slop used to determine when we say that the gesture has started. private boolean mPassedPilferInputSlop; @@ -128,20 +121,18 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC }; public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo, - RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl, boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks, - InputConsumerController inputConsumer, Consumer onCompleteCallback, SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat, - RectF swipeTouchRegion, boolean disableHorizontalSwipe) { + RectF swipeTouchRegion, boolean disableHorizontalSwipe, + BaseSwipeUpHandler.Factory handlerFactory) { super(base); mMainThreadHandler = new Handler(Looper.getMainLooper()); mRunningTask = runningTaskInfo; - mRecentsModel = recentsModel; - mHomeIntent = homeIntent; mMode = SysUINavigationMode.getMode(base); mSwipeTouchRegion = swipeTouchRegion; + mHandlerFactory = handlerFactory; mMotionPauseDetector = new MotionPauseDetector(base); mMotionPauseMinDisplacement = base.getResources().getDimension( @@ -150,11 +141,9 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mVelocityTracker = VelocityTracker.obtain(); mInputMonitorCompat = inputMonitorCompat; - mActivityControlHelper = activityControl; boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null; mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget; mOverviewCallbacks = overviewCallbacks; - mInputConsumer = inputConsumer; mSwipeSharedState = swipeSharedState; mNavBarPosition = new NavBarPosition(base); @@ -163,7 +152,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC float slop = QUICKSTEP_TOUCH_SLOP_RATIO * mTouchSlop; mSquaredTouchSlop = slop * slop; - mPassedPilferInputSlop = mPaddedWindowMoveSlop = continuingPreviousGesture; + mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture; mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe; } @@ -186,7 +175,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } // Proxy events to recents view - if (mPaddedWindowMoveSlop && mInteractionHandler != null + if (mPassedWindowMoveSlop && mInteractionHandler != null && !mRecentsViewDispatcher.hasConsumer()) { mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher( mNavBarPosition.getRotationMode())); @@ -213,7 +202,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // Start the window animation on down to give more time for launcher to draw if the // user didn't start the gesture over the back button if (!mIsDeferredDownTarget) { - startTouchTrackingForWindowAnimation(ev.getEventTime()); + startTouchTrackingForWindowAnimation(ev.getEventTime(), false); } RaceConditionTracker.onEvent(DOWN_EVT, EXIT); @@ -251,17 +240,21 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC float displacement = getDisplacement(ev); float displacementX = mLastPos.x - mDownPos.x; - if (!mPaddedWindowMoveSlop) { + if (!mPassedWindowMoveSlop) { if (!mIsDeferredDownTarget) { // Normal gesture, ensure we pass the drag slop before we start tracking // the gesture if (Math.abs(displacement) > mTouchSlop) { - mPaddedWindowMoveSlop = true; + mPassedWindowMoveSlop = true; mStartDisplacement = Math.min(displacement, -mTouchSlop); } } } + float horizontalDist = Math.abs(displacementX); + float upDist = -displacement; + boolean isLikelyToStartNewTask = horizontalDist > upDist; + if (!mPassedPilferInputSlop) { float displacementY = mLastPos.y - mDownPos.y; if (squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop) { @@ -277,10 +270,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC if (mIsDeferredDownTarget) { // Deferred gesture, start the animation and gesture tracking once // we pass the actual touch slop - startTouchTrackingForWindowAnimation(ev.getEventTime()); + startTouchTrackingForWindowAnimation( + ev.getEventTime(), isLikelyToStartNewTask); } - if (!mPaddedWindowMoveSlop) { - mPaddedWindowMoveSlop = true; + if (!mPassedWindowMoveSlop) { + mPassedWindowMoveSlop = true; mStartDisplacement = Math.min(displacement, -mTouchSlop); } @@ -289,15 +283,12 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } if (mInteractionHandler != null) { - if (mPaddedWindowMoveSlop) { + if (mPassedWindowMoveSlop) { // Move mInteractionHandler.updateDisplacement(displacement - mStartDisplacement); } if (mMode == Mode.NO_BUTTON) { - float horizontalDist = Math.abs(displacementX); - float upDist = -displacement; - boolean isLikelyToStartNewTask = horizontalDist > upDist; mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement || isLikelyToStartNewTask); mMotionPauseDetector.addPosition(displacement, ev.getEventTime()); @@ -329,16 +320,14 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mInteractionHandler.onGestureStarted(); } - private void startTouchTrackingForWindowAnimation(long touchTimeMs) { + private void startTouchTrackingForWindowAnimation( + long touchTimeMs, boolean isLikelyToStartNewTask) { TOUCH_INTERACTION_LOG.addLog("startRecentsAnimation"); RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener(); - final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler( - mRunningTask, this, touchTimeMs, mActivityControlHelper, - listenerSet != null, mInputConsumer); + final BaseSwipeUpHandler handler = mHandlerFactory.newHandler(mRunningTask, touchTimeMs, + listenerSet != null, isLikelyToStartNewTask); - // Preload the plan - mRecentsModel.getTasks(null); mInteractionHandler = handler; handler.setGestureEndCallback(this::onInteractionGestureFinished); mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged); @@ -352,9 +341,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC RecentsAnimationListenerSet newListenerSet = mSwipeSharedState.newRecentsAnimationListenerSet(); newListenerSet.addListener(handler); - BackgroundExecutor.get().submit( - () -> ActivityManagerWrapper.getInstance().startRecentsActivity( - mHomeIntent, null, newListenerSet, null, null)); + startRecentsActivityAsync(handler.getLaunchIntent(), newListenerSet); } } @@ -366,7 +353,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC RaceConditionTracker.onEvent(UP_EVT, ENTER); TraceHelper.endSection("TouchInt"); - if (mPaddedWindowMoveSlop && mInteractionHandler != null) { + if (mPassedWindowMoveSlop && mInteractionHandler != null) { if (ev.getActionMasked() == ACTION_CANCEL) { mInteractionHandler.onGestureCancelled(); } else { @@ -409,14 +396,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // The consumer is being switched while we are active. Set up the shared state to be // used by the next animation removeListener(); - GestureEndTarget endTarget = mInteractionHandler.getGestureEndTarget(); - mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued; - mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher; - if (mSwipeSharedState.canGestureBeContinued) { - mInteractionHandler.cancelCurrentAnimation(mSwipeSharedState); - } else { - mInteractionHandler.reset(); - } + mInteractionHandler.onConsumerAboutToBeSwitched(mSwipeSharedState); } } 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 b021df877e..581ab6d3ea 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 @@ -19,6 +19,7 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TI import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; +import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; @@ -26,6 +27,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.Utilities; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.OverviewCallbacks; import com.android.systemui.shared.system.ActivityManagerWrapper; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java index 425b8b6b0b..05cbb789d9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java @@ -30,7 +30,13 @@ import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; +import com.android.launcher3.BaseActivity; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.Utilities; +import com.android.launcher3.logging.StatsLogUtils; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.quickstep.OverviewCallbacks; import com.android.quickstep.util.NavBarPosition; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -131,12 +137,14 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer { ? -velocityX : (mNavBarPosition.isLeftEdge() ? velocityX : -velocityY); final boolean triggerQuickstep; + int touch = Touch.FLING; if (Math.abs(velocity) >= ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) { triggerQuickstep = velocity > 0; } else { float displacementX = mDisableHorizontalSwipe ? 0 : (ev.getX() - mDownPos.x); float displacementY = ev.getY() - mDownPos.y; triggerQuickstep = squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop; + touch = Touch.SWIPE; } if (triggerQuickstep) { @@ -144,6 +152,13 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer { ActivityManagerWrapper.getInstance() .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); TOUCH_INTERACTION_LOG.addLog("startQuickstep"); + BaseActivity activity = BaseDraggingActivity.fromContext(mContext); + int pageIndex = -1; // This number doesn't reflect workspace page index. + // It only indicates that launcher client screen was shown. + int containerType = StatsLogUtils.getContainerTypeFromState(activity.getCurrentState()); + activity.getUserEventDispatcher().logActionOnContainer( + touch, Direction.UP, containerType, pageIndex); + activity.getUserEventDispatcher().setPreviousHomeGesture(true); } else { // ignore } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java index 6dc672ecfa..90989feb4d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java @@ -99,8 +99,8 @@ public class ClipAnimationHelper { // Whether to boost the opening animation target layers, or the closing private int mBoostModeTargetLayers = -1; - private BiFunction mTaskAlphaCallback = - (t, a1) -> a1; + private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a; + private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1; public ClipAnimationHelper(Context context) { mWindowCornerRadius = getWindowCornerRadius(context.getResources()); @@ -119,8 +119,12 @@ public class ClipAnimationHelper { } public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) { - mHomeStackBounds.set(homeStackBounds); updateSourceStack(target); + updateHomeBounds(homeStackBounds); + } + + public void updateHomeBounds(Rect homeStackBounds) { + mHomeStackBounds.set(homeStackBounds); } public void updateTargetRect(Rect targetRect) { @@ -187,12 +191,12 @@ public class ClipAnimationHelper { Rect crop = mTmpRect; crop.set(app.sourceContainerBounds); crop.offsetTo(0, 0); - float alpha = 1f; + float alpha; int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers); float cornerRadius = 0f; float scale = Math.max(params.currentRect.width(), mTargetRect.width()) / crop.width(); if (app.mode == targetSet.targetMode) { - alpha = mTaskAlphaCallback.apply(app, params.targetAlpha); + alpha = mTaskAlphaCallback.getAlpha(app, params.targetAlpha); if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) { mTmpMatrix.setRectToRect(mSourceRect, params.currentRect, ScaleToFit.FILL); mTmpMatrix.postTranslate(app.position.x, app.position.y); @@ -214,9 +218,12 @@ public class ClipAnimationHelper { // home target. alpha = 1 - (progress * params.targetAlpha); } - } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && launcherOnTop) { - crop = null; - layer = Integer.MAX_VALUE; + } else { + alpha = mBaseAlphaCallback.getAlpha(app, progress); + if (ENABLE_QUICKSTEP_LIVE_TILE.get() && launcherOnTop) { + crop = null; + layer = Integer.MAX_VALUE; + } } // Since radius is in Surface space, but we draw the rounded corners in screen space, we @@ -247,11 +254,14 @@ public class ClipAnimationHelper { } } - public void setTaskAlphaCallback( - BiFunction callback) { + public void setTaskAlphaCallback(TargetAlphaProvider callback) { mTaskAlphaCallback = callback; } + public void setBaseAlphaCallback(TargetAlphaProvider callback) { + mBaseAlphaCallback = callback; + } + public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) { fromTaskThumbnailView(ttv, rv, null); } @@ -357,6 +367,10 @@ public class ClipAnimationHelper { return mCurrentCornerRadius; } + public interface TargetAlphaProvider { + float getAlpha(RemoteAnimationTargetCompat target, float expectedAlpha); + } + public static class TransformParams { float progress; public float offsetX; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java new file mode 100644 index 0000000000..abfe3adbff --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java @@ -0,0 +1,41 @@ +/* + * 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.util; + +import android.os.Binder; +import android.os.IBinder; + +/** + * Utility class to pass non-parcealable objects within same process using parcealable payload. + * + * It wraps the object in a binder as binders are singleton within a process + */ +public class ObjectWrapper extends Binder { + + private final T mObject; + + public ObjectWrapper(T object) { + mObject = object; + } + + public T get() { + return mObject; + } + + public static IBinder wrap(Object obj) { + return new ObjectWrapper<>(obj); + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java index 77dc6f32e7..c6eafe6a64 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java @@ -225,7 +225,18 @@ public class RectFSpringAnim { } } + public void cancel() { + if (mAnimsStarted) { + for (OnUpdateListener onUpdateListener : mOnUpdateListeners) { + onUpdateListener.onCancel(); + } + } + end(); + } + public interface OnUpdateListener { void onUpdate(RectF currentRect, float progress); + + default void onCancel() { } } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java index 07e96869ed..1069bed592 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -15,7 +15,14 @@ */ package com.android.quickstep.util; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; +import static com.android.launcher3.LauncherState.BACKGROUND_APP; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.anim.Interpolators.LINEAR; + import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.view.View; import android.view.ViewGroup; @@ -29,18 +36,15 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; +import com.android.launcher3.Workspace; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.anim.SpringObjectAnimator; +import com.android.launcher3.graphics.OverviewScrim; import java.util.ArrayList; import java.util.List; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; -import static com.android.launcher3.LauncherState.BACKGROUND_APP; -import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.anim.Interpolators.LINEAR; - /** * Creates an animation where all the workspace items are moved into their final location, * staggered row by row from the bottom up. @@ -79,9 +83,19 @@ public class StaggeredWorkspaceAnim { .getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y); DeviceProfile grid = launcher.getDeviceProfile(); - ShortcutAndWidgetContainer currentPage = ((CellLayout) launcher.getWorkspace() - .getChildAt(launcher.getWorkspace().getCurrentPage())) - .getShortcutsAndWidgets(); + Workspace workspace = launcher.getWorkspace(); + CellLayout cellLayout = (CellLayout) workspace.getChildAt(workspace.getCurrentPage()); + ShortcutAndWidgetContainer currentPage = cellLayout.getShortcutsAndWidgets(); + + boolean workspaceClipChildren = workspace.getClipChildren(); + boolean workspaceClipToPadding = workspace.getClipToPadding(); + boolean cellLayoutClipChildren = cellLayout.getClipChildren(); + boolean cellLayoutClipToPadding = cellLayout.getClipToPadding(); + + workspace.setClipChildren(false); + workspace.setClipToPadding(false); + cellLayout.setClipChildren(false); + cellLayout.setClipToPadding(false); // Hotseat and QSB takes up two additional rows. int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2); @@ -109,8 +123,29 @@ public class StaggeredWorkspaceAnim { addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows); } - addWorkspaceScrimAnimationForState(launcher, BACKGROUND_APP, 0); - addWorkspaceScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS); + addScrimAnimationForState(launcher, BACKGROUND_APP, 0); + addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS); + + AnimatorListener resetClipListener = new AnimatorListenerAdapter() { + int numAnimations = mAnimators.size(); + + @Override + public void onAnimationEnd(Animator animation) { + numAnimations--; + if (numAnimations > 0) { + return; + } + + workspace.setClipChildren(workspaceClipChildren); + workspace.setClipToPadding(workspaceClipToPadding); + cellLayout.setClipChildren(cellLayoutClipChildren); + cellLayout.setClipToPadding(cellLayoutClipToPadding); + } + }; + + for (Animator a : mAnimators) { + a.addListener(resetClipListener); + } } /** @@ -134,10 +169,6 @@ public class StaggeredWorkspaceAnim { * @param totalRows Total number of rows. */ private void addStaggeredAnimationForView(View v, int row, int totalRows) { - if (v == mViewToIgnore) { - return; - } - // Invert the rows, because we stagger starting from the bottom of the screen. int invertedRow = totalRows - row; // Add 1 to the inverted row so that the bottom most row has a start delay. @@ -149,6 +180,10 @@ public class StaggeredWorkspaceAnim { springTransY.setStartDelay(startDelay); mAnimators.add(springTransY); + if (v == mViewToIgnore) { + return; + } + v.setAlpha(0); ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f); alpha.setInterpolator(LINEAR); @@ -157,13 +192,17 @@ public class StaggeredWorkspaceAnim { mAnimators.add(alpha); } - private void addWorkspaceScrimAnimationForState(Launcher launcher, LauncherState state, - long duration) { + private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) { AnimatorSetBuilder scrimAnimBuilder = new AnimatorSetBuilder(); AnimationConfig scrimAnimConfig = new AnimationConfig(); scrimAnimConfig.duration = duration; PropertySetter scrimPropertySetter = scrimAnimConfig.getPropertySetter(scrimAnimBuilder); launcher.getWorkspace().getStateTransitionAnimation().setScrim(scrimPropertySetter, state); mAnimators.add(scrimAnimBuilder.build()); + Animator fadeOverviewScrim = ObjectAnimator.ofFloat( + launcher.getDragLayer().getOverviewScrim(), OverviewScrim.SCRIM_PROGRESS, + state.getOverviewScrimAlpha(launcher)); + fadeOverviewScrim.setDuration(duration); + mAnimators.add(fadeOverviewScrim); } } 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 a98df0fa1f..c6a082ad53 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 @@ -37,6 +37,7 @@ import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON; import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; +import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; import android.animation.Animator; import android.animation.AnimatorSet; @@ -72,6 +73,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.WindowInsets; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ListView; @@ -108,7 +110,6 @@ import com.android.quickstep.util.ClipAnimationHelper; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.BackgroundExecutor; import com.android.systemui.shared.system.LauncherEventUtil; import com.android.systemui.shared.system.PackageManagerWrapper; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; @@ -226,7 +227,7 @@ public abstract class RecentsView extends PagedView impl return; } - BackgroundExecutor.get().submit(() -> { + BACKGROUND_EXECUTOR.execute(() -> { TaskView taskView = getTaskView(taskId); if (taskView == null) { return; @@ -269,7 +270,7 @@ public abstract class RecentsView extends PagedView impl private int mTaskListChangeId = -1; // Only valid until the launcher state changes to NORMAL - private int mRunningTaskId = -1; + protected int mRunningTaskId = -1; private boolean mRunningTaskTileHidden; private Task mTmpRunningTask; @@ -289,7 +290,7 @@ public abstract class RecentsView extends PagedView impl @ViewDebug.ExportedProperty(category = "launcher") private float mContentAlpha = 1; @ViewDebug.ExportedProperty(category = "launcher") - private float mFullscreenProgress = 0; + protected float mFullscreenProgress = 0; // Keeps track of task id whose visual state should not be reset private int mIgnoreResetTaskId = -1; @@ -527,7 +528,7 @@ public abstract class RecentsView extends PagedView impl return true; } - private void applyLoadPlan(ArrayList tasks) { + protected void applyLoadPlan(ArrayList tasks) { if (mPendingAnimation != null) { mPendingAnimation.addEndListener((onEndListener) -> applyLoadPlan(tasks)); return; @@ -599,6 +600,7 @@ public abstract class RecentsView extends PagedView impl TaskView taskView = (TaskView) getChildAt(i); if (mIgnoreResetTaskId != taskView.getTask().key.id) { taskView.resetVisualProperties(); + taskView.setStableAlpha(mContentAlpha); } } if (mRunningTaskTileHidden) { @@ -848,12 +850,14 @@ public abstract class RecentsView extends PagedView impl * is called. Also scrolls the view to this task. */ public void showCurrentTask(int runningTaskId) { - if (getChildCount() == 0) { + if (getTaskView(runningTaskId) == null) { + boolean wasEmpty = getChildCount() == 0; // Add an empty view for now until the task plan is loaded and applied final TaskView taskView = mTaskViewPool.getView(); - addView(taskView); - addView(mClearAllButton); - + addView(taskView, 0); + if (wasEmpty) { + addView(mClearAllButton); + } // The temporary running task is only used for the duration between the start of the // gesture and the task list is loaded and applied mTmpRunningTask = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(), @@ -1687,6 +1691,9 @@ public abstract class RecentsView extends PagedView impl * @return How many pixels the running task is offset on the x-axis due to the current scrollX. */ public float getScrollOffset() { + if (getRunningTaskIndex() == -1) { + return 0; + } int startScroll = getScrollForPage(getRunningTaskIndex()); int offsetX = startScroll - getScrollX(); offsetX *= getScaleX(); @@ -1733,4 +1740,14 @@ public abstract class RecentsView extends PagedView impl updateEnabledOverlays(); } } + + public int getLeftGestureMargin() { + final WindowInsets insets = getRootWindowInsets(); + return Math.max(insets.getSystemGestureInsets().left, insets.getSystemWindowInsetLeft()); + } + + public int getRightGestureMargin() { + final WindowInsets insets = getRootWindowInsets(); + return Math.max(insets.getSystemGestureInsets().right, insets.getSystemWindowInsetRight()); + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java index d55a520443..7f1e8980b9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java @@ -410,4 +410,11 @@ public class TaskThumbnailView extends View { return new ColorMatrixColorFilter(COLOR_MATRIX); } + + public Bitmap getThumbnail() { + if (mThumbnailData == null) { + return null; + } + return mThumbnailData.thumbnail; + } } 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 b26fdce92f..2211eb4dd7 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 @@ -192,9 +192,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { super(context, attrs, defStyleAttr); mActivity = BaseDraggingActivity.fromContext(context); setOnClickListener((view) -> { - if (com.android.launcher3.testing.TestProtocol.sDebugTracing) { - android.util.Log.d(TestProtocol.NO_START_TASK_TAG, "TaskView onClick"); - } if (getTask() == null) { return; } @@ -291,9 +288,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { public void launchTask(boolean animate, boolean freezeTaskList, Consumer resultCallback, Handler resultCallbackHandler) { - if (com.android.launcher3.testing.TestProtocol.sDebugTracing) { - android.util.Log.d(TestProtocol.NO_START_TASK_TAG, "launchTask"); - } if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { if (isRunningTask()) { getRecentsView().finishRecentsAnimation(false /* toRecents */, @@ -308,9 +302,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private void launchTaskInternal(boolean animate, boolean freezeTaskList, Consumer resultCallback, Handler resultCallbackHandler) { - if (com.android.launcher3.testing.TestProtocol.sDebugTracing) { - android.util.Log.d(TestProtocol.NO_START_TASK_TAG, "launchTaskInternal"); - } if (mTask != null) { final ActivityOptions opts; if (animate) { diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml index 5394f495e9..01dcff202d 100644 --- a/quickstep/res/values-fr/strings.xml +++ b/quickstep/res/values-fr/strings.xml @@ -33,5 +33,5 @@ "Encore %1$s aujourd\'hui" "Suggestions d\'applications" "Toutes les applications" - "Vos applications prévues" + "Applications prévues pour vous" diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml index 0467af4b2d..387d509587 100644 --- a/quickstep/res/values-hi/strings.xml +++ b/quickstep/res/values-hi/strings.xml @@ -27,7 +27,7 @@ "बंद करें" "ऐप्लिकेशन इस्तेमाल की सेटिंग" "सभी ऐप्लिकेशन बंद करें" - "हाल ही में इस्तेमाल किए गए एेप्लिकेशन" + "हाल ही में इस्तेमाल किए गए ऐप्लिकेशन" "%1$s, %2$s" "<1 मिनट" "आज %1$s और चलेगा" diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml index 1ca558a240..cccece749c 100644 --- a/quickstep/res/values-mr/strings.xml +++ b/quickstep/res/values-mr/strings.xml @@ -27,7 +27,7 @@ "बंद" "अ‍ॅप वापर सेटिंग्ज" "सर्व साफ करा" - "अलीकडील अॅप्स" + "अलीकडील अ‍ॅप्स" "%1$s, %2$s" "१मिहून कमी" "आज %1$sशिल्लक आहे" diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java index 78f6ffa88c..a8e29569b1 100644 --- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java +++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java @@ -15,8 +15,8 @@ */ package com.android.launcher3; -import static com.android.launcher3.Utilities.SINGLE_FRAME_MS; import static com.android.launcher3.Utilities.postAsyncCallback; +import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import static com.android.systemui.shared.recents.utilities.Utilities .postAtFrontOfQueueAsynchronously; @@ -24,6 +24,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.annotation.TargetApi; +import android.content.Context; import android.os.Build; import android.os.Handler; @@ -66,7 +67,7 @@ public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCo /** * Called on the UI thread when the animation targets are received. The implementation must - * call {@link AnimationResult#setAnimation(AnimatorSet)} with the target animation to be run. + * call {@link AnimationResult#setAnimation} with the target animation to be run. */ @UiThread public abstract void onCreateAnimation( @@ -110,7 +111,7 @@ public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCo } @UiThread - public void setAnimation(AnimatorSet animation) { + public void setAnimation(AnimatorSet animation, Context context) { if (mInitialized) { throw new IllegalStateException("Animation already initialized"); } @@ -134,7 +135,7 @@ public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCo // Because t=0 has the app icon in its original spot, we can skip the // first frame and have the same movement one frame earlier. - mAnimator.setCurrentPlayTime(SINGLE_FRAME_MS); + mAnimator.setCurrentPlayTime(getSingleFrameMs(context)); } } } diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java index c5c5323ec5..b9ce1ceee5 100644 --- a/quickstep/src/com/android/launcher3/LauncherInitListener.java +++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java @@ -85,7 +85,7 @@ public class LauncherInitListener extends InternalStateHandler implements Activi register(); - Bundle options = animProvider.toActivityOptions(handler, duration).toBundle(); + Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle(); context.startActivity(addToIntent(new Intent((intent))), options); } } diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index b60a017dd8..991408c649 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -219,7 +219,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans anim.addListener(mForceInvisibleListener); } - result.setAnimation(anim); + result.setAnimation(anim, mLauncher); } }; @@ -822,7 +822,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans } mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL); - result.setAnimation(anim); + result.setAnimation(anim, mLauncher); } } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index f0204b9ba4..174e49b8d2 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -19,16 +19,20 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCRIM_FADE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y; import static com.android.launcher3.anim.AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS; import android.util.FloatProperty; import android.view.View; import android.view.animation.Interpolator; +import androidx.annotation.NonNull; + import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherState.ScaleAndTranslation; @@ -36,8 +40,7 @@ import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PropertySetter; - -import androidx.annotation.NonNull; +import com.android.launcher3.graphics.OverviewScrim; /** * State handler for recents view. Manages UI changes and animations for recents view based off the @@ -67,6 +70,8 @@ public abstract class BaseRecentsViewStateController mRecentsView.setTranslationX(translationX); mRecentsView.setTranslationY(scaleAndTranslation.translationY); getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0); + OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim(); + SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher)); } @Override @@ -110,6 +115,9 @@ public abstract class BaseRecentsViewStateController translateYInterpolator); setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0, builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT)); + OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim(); + setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher), + builder.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR)); } /** diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java index 6030cea938..a55f36b787 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java @@ -51,7 +51,6 @@ import com.android.quickstep.OverviewInteractionState; import com.android.quickstep.RecentsModel; import com.android.quickstep.TouchInteractionService; import com.android.quickstep.util.LayoutUtils; -import com.android.systemui.shared.system.QuickStepContract; /** * Touch controller for handling various state transitions in portrait UI. @@ -63,7 +62,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr /** * The progress at which all apps content will be fully visible when swiping up from overview. */ - private static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f; + protected static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f; /** * The progress at which recents will begin fading out when swiping up from overview. @@ -296,9 +295,13 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr * @return true if the event is over the hotseat */ static boolean isTouchOverHotseat(Launcher launcher, MotionEvent ev) { + return (ev.getY() >= getHotseatTop(launcher)); + } + + public static int getHotseatTop(Launcher launcher) { DeviceProfile dp = launcher.getDeviceProfile(); int hotseatHeight = dp.hotseatBarSizePx + dp.getInsets().bottom; - return (ev.getY() >= (launcher.getDragLayer().getHeight() - hotseatHeight)); + return launcher.getDragLayer().getHeight() - hotseatHeight; } private static class InterpolatorWrapper implements Interpolator { diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java index fee18204ea..11a804356d 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java @@ -17,17 +17,25 @@ package com.android.launcher3.uioverrides.touchcontrollers; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; +import static android.view.MotionEvent.ACTION_UP; +import static android.view.MotionEvent.ACTION_CANCEL; +import android.graphics.PointF; import android.os.RemoteException; import android.util.Log; +import android.util.SparseArray; import android.view.MotionEvent; import android.view.ViewConfiguration; +import android.view.Window; +import android.view.WindowManager; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; -import com.android.launcher3.touch.TouchEventTranslator; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.TouchController; import com.android.quickstep.RecentsModel; import com.android.systemui.shared.recents.ISystemUiProxy; @@ -36,18 +44,29 @@ import java.io.PrintWriter; /** * TouchController for handling touch events that get sent to the StatusBar. Once the - * Once the event delta y passes the touch slop, the events start getting forwarded. + * Once the event delta mDownY passes the touch slop, the events start getting forwarded. * All events are offset by initial Y value of the pointer. */ public class StatusBarTouchController implements TouchController { private static final String TAG = "StatusBarController"; + /** + * Window flag: Enable touches to slide out of a window into neighboring + * windows in mid-gesture instead of being captured for the duration of + * the gesture. + * + * This flag changes the behavior of touch focus for this window only. + * Touches can slide out of the window but they cannot necessarily slide + * back in (unless the other window with touch focus permits it). + */ + private static final int FLAG_SLIPPERY = 0x20000000; + protected final Launcher mLauncher; - protected final TouchEventTranslator mTranslator; private final float mTouchSlop; private ISystemUiProxy mSysUiProxy; private int mLastAction; + private final SparseArray mDownEvents; /* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/ private boolean mCanIntercept; @@ -56,7 +75,7 @@ public class StatusBarTouchController implements TouchController { mLauncher = l; // Guard against TAPs by increasing the touch slop. mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop(); - mTranslator = new TouchEventTranslator((MotionEvent ev)-> dispatchTouchEvent(ev)); + mDownEvents = new SparseArray<>(); } @Override @@ -64,7 +83,6 @@ public class StatusBarTouchController implements TouchController { writer.println(prefix + "mCanIntercept:" + mCanIntercept); writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction)); writer.println(prefix + "mSysUiProxy available:" + (mSysUiProxy != null)); - } private void dispatchTouchEvent(MotionEvent ev) { @@ -81,26 +99,31 @@ public class StatusBarTouchController implements TouchController { @Override public final boolean onControllerInterceptTouchEvent(MotionEvent ev) { int action = ev.getActionMasked(); + int idx = ev.getActionIndex(); + int pid = ev.getPointerId(idx); if (action == ACTION_DOWN) { mCanIntercept = canInterceptTouch(ev); if (!mCanIntercept) { return false; } - mTranslator.reset(); - mTranslator.setDownParameters(0, ev); + mDownEvents.put(pid, new PointF(ev.getX(), ev.getY())); } else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { - // Check!! should only set it only when threshold is not entered. - mTranslator.setDownParameters(ev.getActionIndex(), ev); + // Check!! should only set it only when threshold is not entered. + mDownEvents.put(pid, new PointF(ev.getX(idx), ev.getY(idx))); } if (!mCanIntercept) { return false; } if (action == ACTION_MOVE) { - float dy = ev.getY() - mTranslator.getDownY(); - float dx = ev.getX() - mTranslator.getDownX(); - if (dy > mTouchSlop && dy > Math.abs(dx)) { - mTranslator.dispatchDownEvents(ev); - mTranslator.processMotionEvent(ev); + float dy = ev.getY(idx) - mDownEvents.get(pid).y; + float dx = ev.getX(idx) - mDownEvents.get(pid).x; + // Currently input dispatcher will not do touch transfer if there are more than + // one touch pointer. Hence, even if slope passed, only set the slippery flag + // when there is single touch event. (context: InputDispatcher.cpp line 1445) + if (dy > mTouchSlop && dy > Math.abs(dx) && ev.getPointerCount() == 1) { + ev.setAction(ACTION_DOWN); + dispatchTouchEvent(ev); + setWindowSlippery(true); return true; } if (Math.abs(dx) > mTouchSlop) { @@ -110,13 +133,31 @@ public class StatusBarTouchController implements TouchController { return false; } - @Override public final boolean onControllerTouchEvent(MotionEvent ev) { - mTranslator.processMotionEvent(ev); + int action = ev.getAction(); + if (action == ACTION_UP || action == ACTION_CANCEL) { + dispatchTouchEvent(ev); + mLauncher.getUserEventDispatcher().logActionOnContainer(action == ACTION_UP ? + Touch.FLING : Touch.SWIPE, Direction.DOWN, ContainerType.WORKSPACE, + mLauncher.getWorkspace().getCurrentPage()); + setWindowSlippery(false); + return true; + } return true; } + private void setWindowSlippery(boolean enable) { + Window w = mLauncher.getWindow(); + WindowManager.LayoutParams wlp = w.getAttributes(); + if (enable) { + wlp.flags |= FLAG_SLIPPERY; + } else { + wlp.flags &= ~FLAG_SLIPPERY; + } + w.setAttributes(wlp); + } + private boolean canInterceptTouch(MotionEvent ev) { if (!mLauncher.isInState(LauncherState.NORMAL) || AbstractFloatingView.getTopOpenViewWithType(mLauncher, @@ -132,4 +173,4 @@ public class StatusBarTouchController implements TouchController { mSysUiProxy = RecentsModel.INSTANCE.get(mLauncher).getSystemUiProxy(); return mSysUiProxy != null; } -} +} \ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java index cd2c9cb1bf..5c9c7d4cab 100644 --- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -152,5 +152,15 @@ public interface ActivityControlHelper { default void playAtomicAnimation(float velocity) { // No-op } + + static RectF getDefaultWindowTargetRect(DeviceProfile dp) { + final int halfIconSize = dp.iconSizePx / 2; + final float targetCenterX = dp.availableWidthPx / 2f; + final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx; + // Fallback to animate to center of screen. + return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize, + targetCenterX + halfIconSize, targetCenterY + halfIconSize); + } + } } diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java index 0738affa93..88a4eb6d11 100644 --- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java +++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java @@ -29,11 +29,15 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.util.SparseIntArray; import com.android.systemui.shared.system.PackageManagerWrapper; import java.util.ArrayList; +import java.util.Objects; /** * Class to keep track of the current overview component based off user preferences and app updates @@ -53,22 +57,41 @@ public final class OverviewComponentObserver { } }; private final Context mContext; - private final ComponentName mMyHomeComponent; + private final Intent mCurrentHomeIntent; + private final Intent mMyHomeIntent; + private final Intent mFallbackIntent; + private final SparseIntArray mConfigChangesMap = new SparseIntArray(); private String mUpdateRegisteredPackage; private ActivityControlHelper mActivityControlHelper; private Intent mOverviewIntent; - private Intent mHomeIntent; private int mSystemUiStateFlags; private boolean mIsHomeAndOverviewSame; + private boolean mIsDefaultHome; public OverviewComponentObserver(Context context) { mContext = context; - Intent myHomeIntent = new Intent(Intent.ACTION_MAIN) + mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME) - .setPackage(mContext.getPackageName()); - ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0); - mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name); + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName()); + ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0); + ComponentName myHomeComponent = + new ComponentName(context.getPackageName(), info.activityInfo.name); + mMyHomeIntent.setComponent(myHomeComponent); + mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges); + + ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class); + mFallbackIntent = new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_DEFAULT) + .setComponent(fallbackComponent) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + try { + ActivityInfo fallbackInfo = context.getPackageManager().getActivityInfo( + mFallbackIntent.getComponent(), 0 /* flags */); + mConfigChangesMap.append(fallbackComponent.hashCode(), fallbackInfo.configChanges); + } catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ } mContext.registerReceiver(mUserPreferenceChangeReceiver, new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED)); @@ -92,17 +115,22 @@ public final class OverviewComponentObserver { ComponentName defaultHome = PackageManagerWrapper.getInstance() .getHomeActivities(new ArrayList<>()); - final String overviewIntentCategory; - ComponentName overviewComponent; - mHomeIntent = null; + mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome); - if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 && - (defaultHome == null || mMyHomeComponent.equals(defaultHome))) { + // Set assistant visibility to 0 from launcher's perspective, ensures any elements that + // launcher made invisible become visible again before the new activity control helper + // becomes active. + if (mActivityControlHelper != null) { + mActivityControlHelper.onAssistantVisibilityChanged(0.f); + } + + if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 + && (defaultHome == null || mIsDefaultHome)) { // User default home is same as out home app. Use Overview integrated in Launcher. - overviewComponent = mMyHomeComponent; mActivityControlHelper = new LauncherActivityControllerHelper(); mIsHomeAndOverviewSame = true; - overviewIntentCategory = Intent.CATEGORY_HOME; + mOverviewIntent = mMyHomeIntent; + mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent()); if (mUpdateRegisteredPackage != null) { // Remove any update listener as we don't care about other packages. @@ -111,14 +139,12 @@ public final class OverviewComponentObserver { } } else { // The default home app is a different launcher. Use the fallback Overview instead. - overviewComponent = new ComponentName(mContext, RecentsActivity.class); + mActivityControlHelper = new FallbackActivityControllerHelper(); mIsHomeAndOverviewSame = false; - overviewIntentCategory = Intent.CATEGORY_DEFAULT; + mOverviewIntent = mFallbackIntent; + mCurrentHomeIntent.setComponent(defaultHome); - mHomeIntent = new Intent(Intent.ACTION_MAIN) - .addCategory(Intent.CATEGORY_HOME) - .setComponent(defaultHome); // User's default home app can change as a result of package updates of this app (such // as uninstalling the app or removing the "Launcher" feature in an update). // Listen for package updates of this app (and remove any previously attached @@ -138,14 +164,6 @@ public final class OverviewComponentObserver { ACTION_PACKAGE_REMOVED)); } } - - mOverviewIntent = new Intent(Intent.ACTION_MAIN) - .addCategory(overviewIntentCategory) - .setComponent(overviewComponent) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - if (mHomeIntent == null) { - mHomeIntent = mOverviewIntent; - } } /** @@ -160,6 +178,32 @@ public final class OverviewComponentObserver { } } + /** + * @return {@code true} if the overview component is able to handle the configuration changes. + */ + boolean canHandleConfigChanges(ComponentName component, int changes) { + final int orientationChange = + ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE; + if ((changes & orientationChange) == orientationChange) { + // This is just an approximate guess for simple orientation change because the changes + // may contain non-public bits (e.g. window configuration). + return true; + } + + int configMask = mConfigChangesMap.get(component.hashCode()); + return configMask != 0 && (~configMask & changes) == 0; + } + + /** + * Get the intent for overview activity. It is used when lockscreen is shown and home was died + * in background, we still want to restart the one that will be used after unlock. + * + * @return the overview intent + */ + Intent getOverviewIntentIgnoreSysUiState() { + return mIsDefaultHome ? mMyHomeIntent : mOverviewIntent; + } + /** * Get the current intent for going to the overview activity. * @@ -173,7 +217,7 @@ public final class OverviewComponentObserver { * Get the current intent for going to the home activity. */ public Intent getHomeIntent() { - return mHomeIntent; + return mCurrentHomeIntent; } /** diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java index 7bfa9a0f99..befeee0db9 100644 --- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java +++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java @@ -15,7 +15,6 @@ */ package com.android.quickstep; -import android.app.ActivityManager; import android.content.Context; import android.content.pm.PackageManager; import android.os.UserManager; @@ -23,29 +22,17 @@ import android.util.Log; import com.android.launcher3.BuildConfig; import com.android.launcher3.MainProcessInitializer; -import com.android.launcher3.Utilities; import com.android.systemui.shared.system.ThreadedRendererCompat; @SuppressWarnings("unused") public class QuickstepProcessInitializer extends MainProcessInitializer { private static final String TAG = "QuickstepProcessInitializer"; - private static final int HEAP_LIMIT_MB = 250; public QuickstepProcessInitializer(Context context) { } @Override protected void init(Context context) { - if (Utilities.IS_DEBUG_DEVICE) { - try { - // Trigger a heap dump if the PSS reaches beyond the target heap limit - final ActivityManager am = context.getSystemService(ActivityManager.class); - am.setWatchHeapLimit(HEAP_LIMIT_MB * 1024 * 1024); - } catch (SecurityException e) { - // Do nothing - } - } - // Workaround for b/120550382, an external app can cause the launcher process to start for // a work profile user which we do not support. Disable the application immediately when we // detect this to be the case. diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java deleted file mode 100644 index 89513634fe..0000000000 --- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.android.quickstep; - -import android.content.Context; -import android.os.Bundle; - -import com.android.launcher3.testing.TestInformationHandler; -import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.uioverrides.states.OverviewState; -import com.android.quickstep.util.LayoutUtils; - -public class QuickstepTestInformationHandler extends TestInformationHandler { - - public QuickstepTestInformationHandler(Context context) { } - - @Override - public Bundle call(String method) { - final Bundle response = new Bundle(); - switch (method) { - case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: { - final float swipeHeight = - OverviewState.getDefaultSwipeHeight(mDeviceProfile); - response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight); - return response; - } - - case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: { - final float swipeHeight = - LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile); - response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight); - return response; - } - } - - return super.call(method); - } -} diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index f27ba85388..e41dba94cc 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -16,6 +16,8 @@ package com.android.quickstep; +import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; + import android.annotation.TargetApi; import android.app.ActivityManager; import android.content.Context; @@ -25,10 +27,7 @@ import android.util.SparseBooleanArray; import com.android.launcher3.MainThreadExecutor; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.BackgroundExecutor; import com.android.systemui.shared.system.KeyguardManagerCompat; -import com.android.systemui.shared.system.RecentTaskInfoCompat; -import com.android.systemui.shared.system.TaskDescriptionCompat; import com.android.systemui.shared.system.TaskStackChangeListener; import java.util.ArrayList; import java.util.Collections; @@ -43,7 +42,6 @@ public class RecentTasksList extends TaskStackChangeListener { private final KeyguardManagerCompat mKeyguardManager; private final MainThreadExecutor mMainThreadExecutor; - private final BackgroundExecutor mBgThreadExecutor; // The list change id, increments as the task list changes in the system private int mChangeId; @@ -56,7 +54,6 @@ public class RecentTasksList extends TaskStackChangeListener { public RecentTasksList(Context context) { mMainThreadExecutor = new MainThreadExecutor(); - mBgThreadExecutor = BackgroundExecutor.get(); mKeyguardManager = new KeyguardManagerCompat(context); mChangeId = 1; ActivityManagerWrapper.getInstance().registerTaskStackListener(this); @@ -67,7 +64,7 @@ public class RecentTasksList extends TaskStackChangeListener { */ public void getTaskKeys(int numTasks, Consumer> callback) { // Kick off task loading in the background - mBgThreadExecutor.submit(() -> { + BACKGROUND_EXECUTOR.execute(() -> { ArrayList tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */); mMainThreadExecutor.execute(() -> callback.accept(tasks)); }); @@ -87,13 +84,14 @@ public class RecentTasksList extends TaskStackChangeListener { : () -> callback.accept(copyOf(mTasks)); if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) { - // The list is up to date, callback with the same list - mMainThreadExecutor.execute(resultCallback); + // The list is up to date, send the callback on the next frame, + // so that requestID can be returned first. + mMainThreadExecutor.getHandler().post(resultCallback); return requestLoadId; } // Kick off task loading in the background - mBgThreadExecutor.submit(() -> { + BACKGROUND_EXECUTOR.execute(() -> { ArrayList tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly); mMainThreadExecutor.execute(() -> { @@ -121,12 +119,7 @@ public class RecentTasksList extends TaskStackChangeListener { @Override public void onTaskRemoved(int taskId) { - for (int i = mTasks.size() - 1; i >= 0; i--) { - if (mTasks.get(i).key.id == taskId) { - mTasks.remove(i); - return; - } - } + mTasks = loadTasksInBackground(Integer.MAX_VALUE, false); } @Override @@ -166,15 +159,11 @@ public class RecentTasksList extends TaskStackChangeListener { int taskCount = rawTasks.size(); for (int i = 0; i < taskCount; i++) { ActivityManager.RecentTaskInfo rawTask = rawTasks.get(i); - RecentTaskInfoCompat t = new RecentTaskInfoCompat(rawTask); Task.TaskKey taskKey = new Task.TaskKey(rawTask); Task task; if (!loadKeysOnly) { - ActivityManager.TaskDescription rawTd = t.getTaskDescription(); - TaskDescriptionCompat td = new TaskDescriptionCompat(rawTd); - boolean isLocked = tmpLockedUsers.get(t.getUserId()); - task = new Task(taskKey, td.getPrimaryColor(), td.getBackgroundColor(), - t.supportsSplitScreenMultiWindow(), isLocked, rawTd, t.getTopActivity()); + boolean isLocked = tmpLockedUsers.get(taskKey.userId); + task = Task.from(taskKey, rawTask, isLocked); } else { task = new Task(taskKey); } diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java index 0822e6199b..f9d2f11cba 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java +++ b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java @@ -68,7 +68,7 @@ public class RecentsActivityTracker implements Ac Context context, Handler handler, long duration) { register(); - Bundle options = animProvider.toActivityOptions(handler, duration).toBundle(); + Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle(); context.startActivity(intent, options); } diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java index a7e6d74f02..4503a43542 100644 --- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java +++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java @@ -17,6 +17,7 @@ package com.android.quickstep.util; import android.animation.AnimatorSet; import android.app.ActivityOptions; +import android.content.Context; import android.os.Handler; import com.android.launcher3.LauncherAnimationRunner; @@ -32,14 +33,14 @@ public interface RemoteAnimationProvider { AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targets); - default ActivityOptions toActivityOptions(Handler handler, long duration) { + default ActivityOptions toActivityOptions(Handler handler, long duration, Context context) { LauncherAnimationRunner runner = new LauncherAnimationRunner(handler, false /* startAtFrontOfQueue */) { @Override public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats, AnimationResult result) { - result.setAnimation(createWindowAnimation(targetCompats)); + result.setAnimation(createWindowAnimation(targetCompats), context); } }; return ActivityOptionsCompat.makeRemoteAnimation( diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java index 63c8023f6d..3747f9a8b1 100644 --- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java +++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java @@ -74,6 +74,9 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis private int mMidAlpha; private float mMidProgress; + // The progress at which the drag handle starts moving up with the shelf. + private float mDragHandleProgress; + private Interpolator mBeforeMidProgressColorInterpolator = ACCEL; private Interpolator mAfterMidProgressColorInterpolator = ACCEL; @@ -95,7 +98,7 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis public ShelfScrimView(Context context, AttributeSet attrs) { super(context, attrs); - mMaxScrimAlpha = Math.round(OVERVIEW.getWorkspaceScrimAlpha(mLauncher) * 255); + mMaxScrimAlpha = Math.round(OVERVIEW.getOverviewScrimAlpha(mLauncher) * 255); mEndAlpha = Color.alpha(mEndScrim); mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context); @@ -150,15 +153,16 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) { mMidProgress = 1; + mDragHandleProgress = 1; mMidAlpha = 0; } else { mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha); + mMidProgress = OVERVIEW.getVerticalProgress(mLauncher); Rect hotseatPadding = dp.getHotseatLayoutPadding(); int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom - hotseatPadding.bottom - hotseatPadding.top; - float arrowTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp)); - mMidProgress = 1 - (arrowTop / mShiftRange); - + float dragHandleTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp)); + mDragHandleProgress = 1 - (dragHandleTop / mShiftRange); } mTopOffset = dp.getInsets().top - mShelfOffset; mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset; @@ -199,8 +203,6 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis mProgress, mMidProgress, 1, mMidAlpha, 0, mBeforeMidProgressColorInterpolator)); mShelfColor = setColorAlphaBound(mEndScrim, alpha); } else { - mDragHandleOffset += mShiftRange * (mMidProgress - mProgress); - // Note that these ranges and interpolators are inverted because progress goes 1 to 0. int alpha = Math.round( Utilities.mapToRange(mProgress, (float) 0, mMidProgress, (float) mEndAlpha, @@ -212,6 +214,10 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis (float) 0, LINEAR)); mRemainingScreenColor = setColorAlphaBound(mScrimColor, remainingScrimAlpha); } + + if (mProgress < mDragHandleProgress) { + mDragHandleOffset += mShiftRange * (mDragHandleProgress - mProgress); + } } @Override diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java index d9fcf4d97a..d0956d1f6d 100644 --- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java +++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java @@ -88,7 +88,7 @@ public class AppPredictionsUITests extends AbstractQuickStepTest { */ @Test public void testPredictionExistsInAllApps() { - mActivityMonitor.startLauncher(); + mDevice.pressHome(); mLauncher.pressHome().switchToAllApps(); // Dispatch an update @@ -103,7 +103,7 @@ public class AppPredictionsUITests extends AbstractQuickStepTest { */ @Test public void testPredictionsDeferredUntilHome() { - mActivityMonitor.startLauncher(); + mDevice.pressHome(); sendPredictionUpdate(mSampleApp1, mSampleApp2); mLauncher.pressHome().switchToAllApps(); waitForLauncherCondition("Predictions were not updated in loading state", @@ -120,7 +120,7 @@ public class AppPredictionsUITests extends AbstractQuickStepTest { @Test public void testPredictionsDisabled() { - mActivityMonitor.startLauncher(); + mDevice.pressHome(); sendPredictionUpdate(); mLauncher.pressHome().switchToAllApps(); diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java index 0c5a6f5b6e..ec3d49afad 100644 --- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java +++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java @@ -51,7 +51,7 @@ public class DigitalWellBeingToastTest extends AbstractQuickStepTest { mLauncher.pressHome(); final DigitalWellBeingToast toast = getToast(); - assertTrue("Toast is not visible", toast.hasLimit()); + waitForLauncherCondition("Toast is not visible", launcher -> toast.hasLimit()); assertEquals("Toast text: ", "5 minutes left today", toast.getText()); // Unset time limit for app. diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java index 3b35c86af8..f27f400884 100644 --- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java +++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java @@ -27,6 +27,7 @@ import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_ import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY; import android.content.Context; +import android.content.pm.PackageManager; import android.util.Log; import androidx.test.uiautomator.UiDevice; @@ -80,6 +81,7 @@ public class NavigationModeSwitchRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { + mLauncher.enableDebugTracing(); final Context context = getInstrumentation().getContext(); final int currentInteractionMode = LauncherInstrumentation.getCurrentInteractionMode(context); @@ -101,35 +103,55 @@ public class NavigationModeSwitchRule implements TestRule { if (mode == THREE_BUTTON || mode == ALL) { evaluateWithThreeButtons(); } + } catch (Exception e) { + Log.e(TAG, "Exception", e); + throw e; } finally { - setActiveOverlay(prevOverlayPkg, originalMode); + Assert.assertTrue(setActiveOverlay(prevOverlayPkg, originalMode)); } - } - - public void evaluateWithoutChangingSetting(Statement base) throws Throwable { - base.evaluate(); + mLauncher.disableDebugTracing(); } private void evaluateWithThreeButtons() throws Throwable { - setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, - LauncherInstrumentation.NavigationModel.THREE_BUTTON); - evaluateWithoutChangingSetting(base); + if (setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, + LauncherInstrumentation.NavigationModel.THREE_BUTTON)) { + base.evaluate(); + } } private void evaluateWithTwoButtons() throws Throwable { - setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY, - LauncherInstrumentation.NavigationModel.TWO_BUTTON); - base.evaluate(); + if (setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY, + LauncherInstrumentation.NavigationModel.TWO_BUTTON)) { + base.evaluate(); + } } private void evaluateWithZeroButtons() throws Throwable { - setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, - LauncherInstrumentation.NavigationModel.ZERO_BUTTON); - base.evaluate(); + if (setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, + LauncherInstrumentation.NavigationModel.ZERO_BUTTON)) { + base.evaluate(); + } } - private void setActiveOverlay(String overlayPackage, + private boolean packageExists(String packageName) { + try { + PackageManager pm = getInstrumentation().getContext().getPackageManager(); + if (pm.getApplicationInfo(packageName, 0 /* flags */) == null) { + return false; + } + } catch (PackageManager.NameNotFoundException e) { + return false; + } + return true; + } + + private boolean setActiveOverlay(String overlayPackage, LauncherInstrumentation.NavigationModel expectedMode) throws Exception { + if (!packageExists(overlayPackage)) { + Log.d(TAG, "setActiveOverlay: " + overlayPackage + " pkg does not exist"); + return false; + } + setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY, overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY); setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY, @@ -173,6 +195,7 @@ public class NavigationModeSwitchRule implements TestRule { Assert.assertTrue("Switching nav mode: " + error, error == null); Thread.sleep(5000); + return true; } private void setOverlayPackageEnabled(String overlayPackage, boolean enable) diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java index 2111e2ca27..c5b560c3fc 100644 --- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java +++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java @@ -25,6 +25,7 @@ import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.Launcher; +import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.util.RaceConditionReproducer; import com.android.quickstep.NavigationModeSwitchRule.Mode; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; @@ -79,6 +80,8 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { @Test @NavigationModeSwitch public void testStressPressHome() { + if (LauncherInstrumentation.isAvd()) return; // b/136278866 + for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) { // Destroy Launcher activity. closeLauncherActivity(); @@ -91,6 +94,8 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { @Test @NavigationModeSwitch public void testStressSwipeToOverview() { + if (LauncherInstrumentation.isAvd()) return; // b/136278866 + for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) { // Destroy Launcher activity. closeLauncherActivity(); diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index 9e3bf2f56e..885fdbf423 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -17,7 +17,6 @@ package com.android.quickstep; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -208,7 +207,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @NavigationModeSwitch -// @PortraitLandscape + @PortraitLandscape public void testBackground() throws Exception { startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); final Background background = mLauncher.getBackground(); diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index c0859d3b4d..50f92daf28 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -88,7 +88,7 @@ "Punts de notificació" "Activats" "Desactivats" - "Cal que tingui accés a les notificacions" + "Cal accés a les notificacions" "Per veure els punts de notificació, activa les notificacions de l\'aplicació %1$s" "Canvia la configuració" "Mostra els punts de notificació" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index b796140e5e..3b0432ee4a 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -88,7 +88,7 @@ "नई सूचनाएं बताने वाला गोल निशान" "चालू" "चालू" - "सूचना के एक्सेस की ज़रूरत है" + "सूचना के ऐक्सेस की ज़रूरत है" "सूचना बिंदु दिखाने के लिए, %1$s के ऐप्लिकेशन सूचना चालू करें" "सेटिंग बदलें" "नई सूचनाएं बताने वाला गोल निशान दिखाएं" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 3054214b5e..211f735b49 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -30,10 +30,10 @@ "Layar utama" "Tindakan khusus" "Sentuh lama untuk memilih widget." - "Tap dua kalip & tahan untuk mengambil widget atau menggunakan tindakan khusus." + "Ketuk dua kali & tahan untuk mengambil widget atau menggunakan tindakan khusus." "%1$d × %2$d" "lebar %1$d x tinggi %2$d" - "Tap lama untuk menempatkan secara manual" + "Sentuh lama untuk menempatkan secara manual" "Tambahkan otomatis" "Telusuri aplikasi" "Memuat aplikasi…" @@ -41,8 +41,8 @@ "Telusuri aplikasi lainnya" "Aplikasi" "Notifikasi" - "Tap lama untuk memilih pintasan." - "Tap dua kali & tahan untuk memilih pintasan atau menggunakan tindakan khusus." + "Sentuh lama untuk memilih pintasan." + "Ketuk dua kali & tahan untuk memilih pintasan atau menggunakan tindakan khusus." "Tidak ada ruang lagi pada layar Utama ini." "Tidak ada ruang tersisa di baki Favorit" "Daftar aplikasi" @@ -73,8 +73,8 @@ "Layar utama %1$d dari %2$d" "Halaman layar utama baru" "Folder dibuka, %1$d x %2$d" - "Tap untuk menutup folder" - "Tap untuk menyimpan ganti nama" + "Ketuk untuk menutup folder" + "Ketuk untuk menyimpan ganti nama" "Folder ditutup" "Folder diganti namanya menjadi %1$s" "Folder: %1$s" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 19c06976b0..49e38982cd 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -35,18 +35,18 @@ "%1$d रूंद बाय %2$d उंच" "स्वतः ठेवण्यासाठी स्पर्श करा आणि धरून ठेवा" "आपोआप जोडा" - "अॅप्स शोधा" - "अॅप्स लोड करत आहे…" - "\"%1$s\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत" - "अधिक अॅप्स शोधा" + "अ‍ॅप्स शोधा" + "अ‍ॅप्स लोड करत आहे…" + "\"%1$s\" शी जुळणारे कोणतेही अ‍ॅप्स आढळले नाहीत" + "अधिक अ‍ॅप्स शोधा" "ॲप" "सूचना" "शॉर्टकट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा." "शॉर्टकट निवडण्यासाठी किंवा कस्टम क्रिया वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा." "या मुख्य स्क्रीनवर आणखी जागा नाही." "आवडीच्या ट्रे मध्ये आणखी जागा नाही" - "अॅप्स सूची" - "वैयक्तिक अॅप्स सूची" + "अ‍ॅप्स सूची" + "वैयक्तिक अ‍ॅप्स सूची" "कामाच्या ठिकाणी वापरली जाणाऱ्या अॅप्सची सूची" "होम" "काढा" @@ -136,7 +136,7 @@ "कामाची अ‍ॅप्स येथे मिळवा" "प्रत्येक कार्य अ‍ॅपला एक बॅज असतो आणि तो तुमच्या संस्थेकडून सुरक्षित ठेवला जातो. अधिक सहज अ‍ॅक्सेससाठी अ‍ॅप्स तुमच्या होम स्क्रीनवर हलवा." "तुमच्या संस्थेकडून व्यवस्थापित" - "सूचना आणि अॅप्स बंद आहेत" + "सूचना आणि अ‍ॅप्स बंद आहेत" "बंद करा" "बंद केले" "हे करता आले नाही: %1$s" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 34e267da9b..23b00d0936 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -90,7 +90,7 @@ "Значки уведомлений" "Включены" "Отключены" - "Нет доступа к уведомлениям" + "Нужен доступ к уведомлениям" "Чтобы показывать значки уведомлений, включите уведомления в приложении \"%1$s\"" "Изменить настройки" "Показывать значки уведомлений" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 672665e430..c926bc185b 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -89,7 +89,7 @@ "ஆன்" "ஆஃப்" "அறிவிப்பிற்கான அணுகல் தேவை" - "அறிவிப்புப் புள்ளிகளைக் காட்ட, %1$s இன் பயன்பாட்டு அறிவிப்புகளை இயக்கவும்" + "அறிவிப்புப் புள்ளிகளைக் காட்ட, %1$s இன் ஆப்ஸ் அறிவிப்புகளை இயக்கவும்" "அமைப்புகளை மாற்று" "அறிவிப்புப் புள்ளிகளைக் காட்டு" "முகப்புத் திரையில் ஐகானைச் சேர்" diff --git a/res/values/styles.xml b/res/values/styles.xml index 881f65d276..339aef5b60 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -104,6 +104,7 @@