diff --git a/protos/launcher_trace.proto b/protos/launcher_trace.proto index 65fcfe512b..e5a86a0108 100644 --- a/protos/launcher_trace.proto +++ b/protos/launcher_trace.proto @@ -63,5 +63,6 @@ message GestureStateProto { RECENTS = 2; NEW_TASK = 3; LAST_TASK = 4; + ALL_APPS = 5; } } diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index d64347f977..b7a29e0034 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -39,6 +39,7 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs; +import static com.android.quickstep.GestureState.GestureEndTarget.ALL_APPS; import static com.android.quickstep.GestureState.GestureEndTarget.HOME; import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK; import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK; @@ -161,6 +162,9 @@ public abstract class AbsSwipeUpHandler, private static final ArrayList STATE_NAMES = new ArrayList<>(); + /** Shift distance to transition to All Apps if ENABLE_ALL_APPS_FROM_OVERVIEW. */ + public static final float ALL_APPS_SHIFT_THRESHOLD = 2f; + protected final BaseActivityInterface mActivityInterface; protected final InputConsumerProxy mInputConsumerProxy; protected final ActivityInitListener mActivityInitListener; @@ -247,6 +251,8 @@ public abstract class AbsSwipeUpHandler, getNextStateFlag("STATE_CURRENT_TASK_FINISHED"); private static final int STATE_FINISH_WITH_NO_END = getNextStateFlag("STATE_FINISH_WITH_NO_END"); + private static final int STATE_SETTLED_ON_ALL_APPS = + getNextStateFlag("STATE_SETTLED_ON_ALL_APPS"); private static final int LAUNCHER_UI_STATES = STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED | @@ -299,6 +305,7 @@ public abstract class AbsSwipeUpHandler, private boolean mGestureStarted; private boolean mLogDirectionUpOrLeft = true; private boolean mIsLikelyToStartNewTask; + private boolean mIsInAllAppsRegion; private final long mTouchTimeMs; private long mLauncherFrameDrawnTime; @@ -432,6 +439,9 @@ public abstract class AbsSwipeUpHandler, this::finishCurrentTransitionToHome); mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED, this::reset); + mStateCallback.runOnceAtState(STATE_SETTLED_ON_ALL_APPS | STATE_SCREENSHOT_CAPTURED + | STATE_GESTURE_COMPLETED, + this::finishCurrentTransitionToAllApps); mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS @@ -681,7 +691,9 @@ public abstract class AbsSwipeUpHandler, maybeUpdateRecentsAttachedState(true/* animate */, true/* moveRunningTask */); Optional.ofNullable(mActivityInterface.getTaskbarController()) .ifPresent(TaskbarUIController::startTranslationSpring); - performHapticFeedback(); + if (!mIsInAllAppsRegion) { + performHapticFeedback(); + } } @Override @@ -695,7 +707,7 @@ public abstract class AbsSwipeUpHandler, maybeUpdateRecentsAttachedState(true /* animate */); } - private void maybeUpdateRecentsAttachedState(boolean animate) { + protected void maybeUpdateRecentsAttachedState(boolean animate) { maybeUpdateRecentsAttachedState(animate, false /* moveRunningTask */); } @@ -716,7 +728,9 @@ public abstract class AbsSwipeUpHandler, ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId()) : null; final boolean recentsAttachedToAppWindow; - if (mGestureState.getEndTarget() != null) { + if (mIsInAllAppsRegion) { + recentsAttachedToAppWindow = false; + } else if (mGestureState.getEndTarget() != null) { recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow; } else if (mContinuingLastGesture && mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) { @@ -772,6 +786,26 @@ public abstract class AbsSwipeUpHandler, } } + /** + * Update whether user is currently dragging in a region that will trigger all apps. + */ + private void setIsInAllAppsRegion(boolean isInAllAppsRegion) { + if (mIsInAllAppsRegion == isInAllAppsRegion + || !mActivityInterface.allowAllAppsFromOverview()) { + return; + } + mIsInAllAppsRegion = isInAllAppsRegion; + + // Newly entering or exiting the zone - do haptic and animate recent tasks. + VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC); + maybeUpdateRecentsAttachedState(true); + + // Draw active task below Launcher so that All Apps can appear over it. + runActionOnRemoteHandles(remoteTargetHandle -> + remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(isInAllAppsRegion)); + } + + private void buildAnimationController() { if (!canCreateNewOrUpdateExistingLauncherTransitionController()) { return; @@ -792,10 +826,15 @@ public abstract class AbsSwipeUpHandler, @Override public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { WindowInsets result = view.onApplyWindowInsets(windowInsets); + // Don't rebuild animation when we are animating the IME, because it will cause a loop + // where the insets change -> animation changes (updating ime) -> insets change -> ... + if (windowInsets.isVisible(WindowInsets.Type.ime())) { + return result; + } buildAnimationController(); // Reapply the current shift to ensure it takes new insets into account, e.g. when long // pressing to stash taskbar without moving the finger. - updateFinalShift(); + onCurrentShiftUpdated(); return result; } @@ -822,7 +861,8 @@ public abstract class AbsSwipeUpHandler, */ @UiThread @Override - public void updateFinalShift() { + public void onCurrentShiftUpdated() { + setIsInAllAppsRegion(mCurrentShift.value >= ALL_APPS_SHIFT_THRESHOLD); updateSysUiFlags(mCurrentShift.value); applyScrollAndTransform(); @@ -1085,6 +1125,9 @@ public abstract class AbsSwipeUpHandler, } switch (endTarget) { + case ALL_APPS: + mStateCallback.setState(STATE_SETTLED_ON_ALL_APPS | STATE_CAPTURE_SCREENSHOT); + break; case HOME: mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT); // Notify the SysUI to use fade-in animation when entering PiP @@ -1173,6 +1216,9 @@ public abstract class AbsSwipeUpHandler, final boolean willGoToNewTask = isScrollingToNewTask() && Math.abs(velocity.x) > Math.abs(endVelocity); final boolean isSwipeUp = endVelocity < 0; + if (mIsInAllAppsRegion) { + return isSwipeUp ? ALL_APPS : LAST_TASK; + } if (!isSwipeUp) { final boolean isCenteredOnNewTask = mRecentsView.getDestinationPage() != mRecentsView.getRunningTaskIndex(); @@ -1188,7 +1234,9 @@ public abstract class AbsSwipeUpHandler, // Fully gestural mode. final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources() .getDimension(R.dimen.quickstep_fling_threshold_speed); - if (isScrollingToNewTask && isFlingX) { + if (mIsInAllAppsRegion) { + return ALL_APPS; + } else if (isScrollingToNewTask && isFlingX) { // Flinging towards new task takes precedence over mIsMotionPaused (which only // checks y-velocity). return NEW_TASK; @@ -1236,7 +1284,8 @@ public abstract class AbsSwipeUpHandler, mGestureState.setEndTarget(endTarget, false /* isAtomic */); mAnimationFactory.setEndTarget(endTarget); - float endShift = endTarget.isLauncher ? 1 : 0; + float endShift = endTarget == ALL_APPS ? mDragLengthFactor + : endTarget.isLauncher ? 1 : 0; final float startShift; if (!isFling) { long expectedDuration = Math.abs(Math.round((endShift - currentShift) @@ -1793,6 +1842,12 @@ public abstract class AbsSwipeUpHandler, reset(); } + @UiThread + private void finishCurrentTransitionToAllApps() { + finishCurrentTransitionToHome(); + reset(); + } + private void reset() { mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED); if (mActivity != null) { @@ -1926,7 +1981,8 @@ public abstract class AbsSwipeUpHandler, private boolean updateThumbnail(int runningTaskId, boolean refreshView) { boolean finishTransitionPosted = false; final TaskView taskView; - if (mGestureState.getEndTarget() == HOME || mGestureState.getEndTarget() == NEW_TASK) { + if (mGestureState.getEndTarget() == HOME || mGestureState.getEndTarget() == NEW_TASK + || mGestureState.getEndTarget() == ALL_APPS) { // Capture the screenshot before finishing the transition to home or quickswitching to // ensure it's taken in the correct orientation, but no need to update the thumbnail. taskView = null; @@ -2072,7 +2128,7 @@ public abstract class AbsSwipeUpHandler, private void onRecentsViewScroll() { if (moveWindowWithRecentsScroll()) { - updateFinalShift(); + onCurrentShiftUpdated(); } } diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index fd7aa581b7..60083c67e7 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -187,6 +187,9 @@ public abstract class BaseActivityInterface 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 AnimatedFloat mCurrentShift = new AnimatedFloat(this::onCurrentShiftUpdated); protected float mCurrentDisplacement; // The distance needed to drag to reach the task size in recents. @@ -148,7 +148,7 @@ public abstract class SwipeUpAnimationLogic implements * Called when the value of {@link #mCurrentShift} changes */ @UiThread - public abstract void updateFinalShift(); + public abstract void onCurrentShiftUpdated(); protected PagedOrientationHandler getOrientationHandler() { // OrientationHandler should be independent of remote target, can directly take one diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java index b3243ff31f..a8af05e7d8 100644 --- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java @@ -309,7 +309,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { } @Override - public void updateFinalShift() { + public void onCurrentShiftUpdated() { mRemoteTargetHandles[0].getPlaybackController() .setProgress(mCurrentShift.value, mDragLengthFactor); mRemoteTargetHandles[0].getTaskViewSimulator().apply( diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java index 6eadd2bf15..8335523758 100644 --- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java +++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java @@ -33,10 +33,11 @@ public class ActiveGestureErrorDetector { */ public enum GestureEvent { MOTION_DOWN, MOTION_UP, MOTION_MOVE, SET_END_TARGET, SET_END_TARGET_HOME, - SET_END_TARGET_NEW_TASK, ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, - FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, - CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, - EXPECTING_TASK_APPEARED, FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED, + SET_END_TARGET_NEW_TASK, SET_END_TARGET_ALL_APPS, ON_SETTLED_ON_END_TARGET, + START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, + SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, + SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED, + FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED, /** * These GestureEvents are specifically associated to state flags that get set in @@ -220,6 +221,7 @@ public class ActiveGestureErrorDetector { case MOTION_DOWN: case SET_END_TARGET: case SET_END_TARGET_HOME: + case SET_END_TARGET_ALL_APPS: case SET_END_TARGET_NEW_TASK: case START_RECENTS_ANIMATION: case SET_ON_PAGE_TRANSITION_END_CALLBACK: diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java index baca76cfba..a92ab2a417 100644 --- a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java +++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java @@ -17,9 +17,11 @@ package com.android.quickstep.util; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.quickstep.AbsSwipeUpHandler.ALL_APPS_SHIFT_THRESHOLD; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; +import android.animation.AnimatorSet; import android.animation.TimeInterpolator; import android.content.Context; import android.graphics.Matrix; @@ -32,11 +34,15 @@ import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.statemanager.StateManager; +import com.android.launcher3.statemanager.StatefulActivity; +import com.android.launcher3.states.StateAnimationConfig; +import com.android.launcher3.touch.AllAppsSwipeController; import com.android.launcher3.touch.PagedOrientationHandler; -import com.android.quickstep.LauncherActivityInterface; import com.android.quickstep.views.RecentsView; /** @@ -49,7 +55,9 @@ public class AnimatorControllerWithResistance { private enum RecentsResistanceParams { FROM_APP(0.75f, 0.5f, 1f, false), + FROM_APP_TO_ALL_APPS(0.75f, 0.5f, 0.8f, false), FROM_APP_TABLET(1f, 0.7f, 1f, true), + FROM_APP_TO_ALL_APPS_TABLET(1f, 0.5f, 0.5f, false), FROM_OVERVIEW(1f, 0.75f, 0.5f, false); RecentsResistanceParams(float scaleStartResist, float scaleMaxResist, @@ -86,6 +94,8 @@ public class AnimatorControllerWithResistance { private static final TimeInterpolator RECENTS_SCALE_RESIST_INTERPOLATOR = DEACCEL; private static final TimeInterpolator RECENTS_TRANSLATE_RESIST_INTERPOLATOR = LINEAR; + private static final Rect TEMP_RECT = new Rect(); + private final AnimatorPlaybackController mNormalController; private final AnimatorPlaybackController mResistanceController; @@ -145,10 +155,42 @@ public class AnimatorControllerWithResistance { scaleProperty, translationTarget, translationProperty); PendingAnimation resistAnim = createRecentsResistanceAnim(params); + // Apply All Apps animation during the resistance animation. + if (recentsOrientedState.getActivityInterface().allowAllAppsFromOverview()) { + StatefulActivity activity = + recentsOrientedState.getActivityInterface().getCreatedActivity(); + if (activity != null) { + StateManager stateManager = activity.getStateManager(); + if (stateManager.isInStableState(LauncherState.BACKGROUND_APP) + && stateManager.isInTransition()) { + + // Calculate the resistance progress threshold where All Apps will trigger. + float threshold = getAllAppsThreshold(context, recentsOrientedState, dp); + + StateAnimationConfig config = new StateAnimationConfig(); + AllAppsSwipeController.applyOverviewToAllAppsAnimConfig(dp, config, threshold); + AnimatorSet allAppsAnimator = stateManager.createAnimationToNewWorkspace( + LauncherState.ALL_APPS, config).getTarget(); + resistAnim.add(allAppsAnimator); + } + } + } + AnimatorPlaybackController resistanceController = resistAnim.createPlaybackController(); return new AnimatorControllerWithResistance(normalController, resistanceController); } + private static float getAllAppsThreshold(Context context, + RecentsOrientedState recentsOrientedState, DeviceProfile dp) { + int transitionDragLength = + recentsOrientedState.getActivityInterface().getSwipeUpDestinationAndLength( + dp, context, TEMP_RECT, + recentsOrientedState.getOrientationHandler()); + float dragLengthFactor = (float) dp.heightPx / transitionDragLength; + // -1s are because 0-1 is reserved for the normal transition. + return (ALL_APPS_SHIFT_THRESHOLD - 1) / (dragLengthFactor - 1); + } + /** * Creates the resistance animation for {@link #createForRecents}, or can be used separately * when starting from recents, i.e. {@link #createRecentsResistanceFromOverviewAnim}. @@ -158,8 +200,8 @@ public class AnimatorControllerWithResistance { Rect startRect = new Rect(); PagedOrientationHandler orientationHandler = params.recentsOrientedState .getOrientationHandler(); - LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect, - orientationHandler); + params.recentsOrientedState.getActivityInterface() + .calculateTaskSize(params.context, params.dp, startRect, orientationHandler); long distanceToCover = startRect.bottom; PendingAnimation resistAnim = params.resistAnim != null ? params.resistAnim @@ -257,9 +299,15 @@ public class AnimatorControllerWithResistance { this.translationTarget = translationTarget; this.translationProperty = translationProperty; if (dp.isTablet) { - resistanceParams = RecentsResistanceParams.FROM_APP_TABLET; + resistanceParams = + recentsOrientedState.getActivityInterface().allowAllAppsFromOverview() + ? RecentsResistanceParams.FROM_APP_TO_ALL_APPS_TABLET + : RecentsResistanceParams.FROM_APP_TABLET; } else { - resistanceParams = RecentsResistanceParams.FROM_APP; + resistanceParams = + recentsOrientedState.getActivityInterface().allowAllAppsFromOverview() + ? RecentsResistanceParams.FROM_APP_TO_ALL_APPS + : RecentsResistanceParams.FROM_APP; } } diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java index c4ba39a76e..f6ad692fcb 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -116,6 +116,7 @@ public class RecentsOrientedState implements | FLAG_SWIPE_UP_NOT_RUNNING; private final Context mContext; + private final BaseActivityInterface mActivityInterface; private final OrientationEventListener mOrientationListener; private final SettingsCache mSettingsCache; private final SettingsCache.OnChangeListener mRotationChangeListener = @@ -135,9 +136,10 @@ public class RecentsOrientedState implements * is enabled * @see #setRotationWatcherEnabled(boolean) */ - public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy, + public RecentsOrientedState(Context context, BaseActivityInterface activityInterface, IntConsumer rotationChangeListener) { mContext = context; + mActivityInterface = activityInterface; mOrientationListener = new OrientationEventListener(context) { @Override public void onOrientationChanged(int degrees) { @@ -149,7 +151,7 @@ public class RecentsOrientedState implements } }; - mFlags = sizeStrategy.rotationSupportedByActivity + mFlags = mActivityInterface.rotationSupportedByActivity ? FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY : 0; mFlags |= FLAG_SWIPE_UP_NOT_RUNNING; @@ -157,6 +159,10 @@ public class RecentsOrientedState implements initFlags(); } + public BaseActivityInterface getActivityInterface() { + return mActivityInterface; + } + /** * Sets the device profile for the current state. */ diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index c165accc52..697a1c16b7 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -17,6 +17,7 @@ package com.android.quickstep.views; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; @@ -129,7 +130,7 @@ public class LauncherRecentsView extends RecentsView + + - - diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index 470a75c611..01a4876b49 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -40,6 +40,7 @@ import android.os.Parcelable; import android.os.Process; import android.os.UserManager; import android.util.AttributeSet; +import android.util.FloatProperty; import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; @@ -101,6 +102,20 @@ public class ActivityAllAppsContainerView OnDeviceProfileChangeListener, PersonalWorkSlidingTabStrip.OnActivePageChangedListener, ScrimView.ScrimDrawingController { + + public static final FloatProperty> BOTTOM_SHEET_ALPHA = + new FloatProperty<>("bottomSheetAlpha") { + @Override + public Float get(ActivityAllAppsContainerView containerView) { + return containerView.mBottomSheetAlpha; + } + + @Override + public void setValue(ActivityAllAppsContainerView containerView, float v) { + containerView.setBottomSheetAlpha(v); + } + }; + public static final float PULL_MULTIPLIER = .02f; public static final float FLING_VELOCITY_MULTIPLIER = 1200f; protected static final String BUNDLE_KEY_CURRENT_PAGE = "launcher.allapps.current_page"; @@ -159,6 +174,8 @@ public class ActivityAllAppsContainerView private ScrimView mScrimView; private int mHeaderColor; private int mBottomSheetBackgroundColor; + private float mBottomSheetAlpha = 1f; + private boolean mForceBottomSheetVisible; private int mTabsProtectionAlpha; @Nullable private AllAppsTransitionController mAllAppsTransitionController; @@ -258,7 +275,7 @@ public class ActivityAllAppsContainerView final TypedValue value = new TypedValue(); getContext().getTheme().resolveAttribute(android.R.attr.colorBackground, value, true); mBottomSheetBackgroundColor = value.data; - updateBackground(mActivityContext.getDeviceProfile()); + updateBackgroundVisibility(mActivityContext.getDeviceProfile()); mSearchUiManager.initializeSearch(this); } @@ -282,6 +299,16 @@ public class ActivityAllAppsContainerView return mBottomSheetBackground; } + /** + * Temporarily force the bottom sheet to be visible on non-tablets. + * + * @param force {@code true} means bottom sheet will be visible on phones until {@code reset()}. + **/ + public void forceBottomSheetVisible(boolean force) { + mForceBottomSheetVisible = force; + updateBackgroundVisibility(mActivityContext.getDeviceProfile()); + } + public View getSearchView() { return mSearchContainer; } @@ -408,6 +435,7 @@ public class ActivityAllAppsContainerView if (mHeader != null && mHeader.getVisibility() == VISIBLE) { mHeader.reset(animate); } + forceBottomSheetVisible(false); // Reset the base recycler view after transitioning home. updateHeaderScroll(0); if (exitSearch) { @@ -830,7 +858,7 @@ public class ActivityAllAppsContainerView holder.mRecyclerView.getRecycledViewPool().clear(); } } - updateBackground(dp); + updateBackgroundVisibility(dp); int navBarScrimColor = Themes.getNavBarScrimColor(mActivityContext); if (mNavBarScrimPaint.getColor() != navBarScrimColor) { @@ -839,13 +867,19 @@ public class ActivityAllAppsContainerView } } - protected void updateBackground(DeviceProfile deviceProfile) { - mBottomSheetBackground.setVisibility(deviceProfile.isTablet ? View.VISIBLE : View.GONE); + protected void updateBackgroundVisibility(DeviceProfile deviceProfile) { + boolean visible = deviceProfile.isTablet || mForceBottomSheetVisible; + mBottomSheetBackground.setVisibility(visible ? View.VISIBLE : View.GONE); // Note: For tablets, the opaque background and header protection are added in drawOnScrim. // For the taskbar entrypoint, the scrim is drawn differently, so a static background is // added in TaskbarAllAppsContainerView and header protection is not yet supported. } + private void setBottomSheetAlpha(float alpha) { + // Bottom sheet alpha is always 1 for tablets. + mBottomSheetAlpha = mActivityContext.getDeviceProfile().isTablet ? 1f : alpha; + } + private void onAppsUpdated() { mHasWorkApps = Stream.of(mAllAppsStore.getApps()).anyMatch(mWorkManager.getMatcher()); if (TestProtocol.sDebugTracing) { @@ -1148,8 +1182,8 @@ public class ActivityAllAppsContainerView @Override public void drawOnScrimWithScale(Canvas canvas, float scale) { - final boolean isTablet = mActivityContext.getDeviceProfile().isTablet; final View panel = mBottomSheetBackground; + final boolean hasBottomSheet = panel.getVisibility() == VISIBLE; final float translationY = ((View) panel.getParent()).getTranslationY(); final float horizontalScaleOffset = (1 - scale) * panel.getWidth() / 2; @@ -1160,8 +1194,9 @@ public class ActivityAllAppsContainerView final float leftWithScale = panel.getLeft() + horizontalScaleOffset; final float rightWithScale = panel.getRight() - horizontalScaleOffset; // Draw full background panel for tablets. - if (isTablet) { + if (hasBottomSheet) { mHeaderPaint.setColor(mBottomSheetBackgroundColor); + mHeaderPaint.setAlpha((int) (255 * mBottomSheetAlpha)); mTmpRectF.set( leftWithScale, @@ -1192,7 +1227,7 @@ public class ActivityAllAppsContainerView final float headerBottomOffset = (getVisibleContainerView().getHeight() * (1 - scale) / 2); final float headerBottomWithScaleOnPhone = headerBottomNoScale * scale + headerBottomOffset; final FloatingHeaderView headerView = getFloatingHeaderView(); - if (isTablet) { + if (hasBottomSheet) { // Start adding header protection if search bar or tabs will attach to the top. if (!FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() || mUsingTabs) { mTmpRectF.set( @@ -1219,12 +1254,12 @@ public class ActivityAllAppsContainerView } float left = 0f; float right = canvas.getWidth(); - if (isTablet) { + if (hasBottomSheet) { left = mBottomSheetBackground.getLeft() + horizontalScaleOffset; right = mBottomSheetBackground.getRight() - horizontalScaleOffset; } - final float tabTopWithScale = isTablet + final float tabTopWithScale = hasBottomSheet ? headerBottomWithScaleOnTablet : headerBottomWithScaleOnPhone; final float tabBottomWithScale = tabTopWithScale + tabsHeight * scale; @@ -1263,7 +1298,7 @@ public class ActivityAllAppsContainerView * Returns a view that denotes the visible part of all apps container view. */ public View getVisibleContainerView() { - return mActivityContext.getDeviceProfile().isTablet ? mBottomSheetBackground : this; + return mBottomSheetBackground.getVisibility() == VISIBLE ? mBottomSheetBackground : this; } protected void onInitializeRecyclerView(RecyclerView rv) { diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 4d1006adba..d4f152aa0a 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -21,8 +21,10 @@ import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; +import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS; import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV; @@ -410,8 +412,12 @@ public class AllAppsTransitionController setter.setFloat(getAppsViewPullbackAlpha(), MultiPropertyFactory.MULTI_PROPERTY_VALUE, hasAllAppsContent ? 1 : 0, allAppsFade); - boolean shouldProtectHeader = - ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS; + setter.setFloat(mLauncher.getAppsView(), + ActivityAllAppsContainerView.BOTTOM_SHEET_ALPHA, hasAllAppsContent ? 1 : 0, + config.getInterpolator(ANIM_ALL_APPS_BOTTOM_SHEET_FADE, INSTANT)); + + boolean shouldProtectHeader = !config.hasAnimationFlag(StateAnimationConfig.SKIP_SCRIM) + && (ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS); mScrimView.setDrawingController(shouldProtectHeader ? mAppsView : null); } diff --git a/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java index 684e98ed96..d7f54635e5 100644 --- a/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/SecondaryLauncherAllAppsContainerView.java @@ -41,7 +41,7 @@ public class SecondaryLauncherAllAppsContainerView extends } @Override - protected void updateBackground(DeviceProfile deviceProfile) {} + protected void updateBackgroundVisibility(DeviceProfile deviceProfile) {} @Override public boolean isInAllApps() { diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index cdebe440a1..0ba4dee546 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -115,9 +115,13 @@ public final class FeatureFlags { // TODO(Block 4): Cleanup flags public static final BooleanFlag ENABLE_FLOATING_SEARCH_BAR = - getReleaseFlag(270390286, "ENABLE_FLOATING_SEARCH_BAR", DISABLED, + getReleaseFlag(268388460, "ENABLE_FLOATING_SEARCH_BAR", DISABLED, "Keep All Apps search bar at the bottom (but above keyboard if open)"); + public static final BooleanFlag ENABLE_ALL_APPS_FROM_OVERVIEW = + getDebugFlag(275132633, "ENABLE_ALL_APPS_FROM_OVERVIEW", DISABLED, + "Allow entering All Apps from Overview (e.g. long swipe up from app)"); + public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag( 270394468, "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", ENABLED, "Enable option to show keyboard when going to all-apps"); diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java index 54735f0094..d1e816bec6 100644 --- a/src/com/android/launcher3/states/StateAnimationConfig.java +++ b/src/com/android/launcher3/states/StateAnimationConfig.java @@ -65,7 +65,8 @@ public class StateAnimationConfig { ANIM_OVERVIEW_ACTIONS_FADE, ANIM_WORKSPACE_PAGE_TRANSLATE_X, ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN, - ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE + ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE, + ANIM_ALL_APPS_BOTTOM_SHEET_FADE }) @Retention(RetentionPolicy.SOURCE) public @interface AnimType {} @@ -88,8 +89,9 @@ public class StateAnimationConfig { public static final int ANIM_WORKSPACE_PAGE_TRANSLATE_X = 15; public static final int ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN = 17; public static final int ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE = 18; + public static final int ANIM_ALL_APPS_BOTTOM_SHEET_FADE = 19; - private static final int ANIM_TYPES_COUNT = 19; + private static final int ANIM_TYPES_COUNT = 20; protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT]; diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java index a53751fcb8..d028f24b0a 100644 --- a/src/com/android/launcher3/touch/AllAppsSwipeController.java +++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java @@ -23,6 +23,8 @@ import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE; import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.Interpolators.clampToProgress; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE; @@ -32,11 +34,14 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS; import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE; +import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import android.view.MotionEvent; import android.view.animation.Interpolator; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.anim.Interpolators; @@ -269,4 +274,44 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController { : ALL_APPS_VERTICAL_PROGRESS_ATOMIC); } } + + /** + * Applies Animation config values for transition from overview to all apps. + * + * @param threshold progress at which all apps will open upon release + */ + public static void applyOverviewToAllAppsAnimConfig( + DeviceProfile deviceProfile, StateAnimationConfig config, float threshold) { + config.userControlled = true; + config.animFlags = SKIP_OVERVIEW; + if (deviceProfile.isTablet) { + config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT); + config.setInterpolator(ANIM_SCRIM_FADE, ALL_APPS_SCRIM_RESPONDER); + // The fact that we end on Workspace is not very ideal, but since we do, fade it in at + // the end of the transition. Don't scale/translate it. + config.setInterpolator(ANIM_WORKSPACE_FADE, clampToProgress(LINEAR, 0.8f, 1)); + config.setInterpolator(ANIM_WORKSPACE_SCALE, INSTANT); + config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, INSTANT); + } else { + // Remove scrim for this transition. + config.setInterpolator(ANIM_SCRIM_FADE, progress -> 0); + + // For now, pop the background panel in at full opacity at the threshold. + config.setInterpolator(ANIM_ALL_APPS_BOTTOM_SHEET_FADE, + thresholdInterpolator(threshold, INSTANT)); + + // Fade the apps in when the scrim normally does, so it's apparent sooner what is + // happening (in this case we are fading them on top of the background panel). + config.setInterpolator(ANIM_ALL_APPS_FADE, + thresholdInterpolator(threshold, SCRIM_FADE_MANUAL)); + + config.setInterpolator(ANIM_VERTICAL_PROGRESS, + thresholdInterpolator(threshold, ALL_APPS_VERTICAL_PROGRESS_MANUAL)); + } + } + + /** Creates an interpolator that is 0 until the threshold, then follows given interpolator. */ + private static Interpolator thresholdInterpolator(float threshold, Interpolator interpolator) { + return progress -> progress <= threshold ? 0 : interpolator.getInterpolation(progress); + } }