diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b031ffbee4..bc0cd97685 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -49,7 +49,7 @@ android:stateNotNeeded="true" android:windowSoftInputMode="adjustPan" android:screenOrientation="unspecified" - android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize" + android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode" android:resizeableActivity="true" android:resumeWhilePausing="true" android:taskAffinity="" diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml index 60afddb0a8..d680507e3b 100644 --- a/quickstep/AndroidManifest-launcher.xml +++ b/quickstep/AndroidManifest-launcher.xml @@ -49,7 +49,7 @@ android:stateNotNeeded="true" android:windowSoftInputMode="adjustPan" android:screenOrientation="unspecified" - android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize" + android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode" android:resizeableActivity="true" android:resumeWhilePausing="true" android:taskAffinity="" diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index e49f2ecdc0..bb83b761e7 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -60,7 +60,7 @@ android:stateNotNeeded="true" android:theme="@style/LauncherTheme" android:screenOrientation="unspecified" - android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize" + android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode" android:resizeableActivity="true" android:resumeWhilePausing="true" android:taskAffinity="" /> diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java index c15a5963f6..20e1edc05f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java @@ -71,10 +71,9 @@ public class HotseatFileLog { } private PrintWriter getWriter() { - String fName = FILE_NAME_PREFIX + (LOG_DAYS % 10); - if (fName.equals(mFileName)) return mCurrentWriter; - Calendar cal = Calendar.getInstance(); + String fName = FILE_NAME_PREFIX + (cal.get(Calendar.DAY_OF_YEAR) % 10); + if (fName.equals(mFileName)) return mCurrentWriter; boolean append = false; File logFile = new File(mLogsDir, fName); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java index 8c1db4e2e7..9e7c9fba6d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java @@ -30,7 +30,6 @@ import com.android.launcher3.provider.LauncherDbUtils; */ public class HotseatRestoreHelper { private final Launcher mLauncher; - private boolean mBackupRestored = false; HotseatRestoreHelper(Launcher context) { mLauncher = context; @@ -62,7 +61,6 @@ public class HotseatRestoreHelper { * Finds and restores a previously saved snapshow of Favorites table */ public void restoreBackup() { - if (mBackupRestored) return; MODEL_EXECUTOR.execute(() -> { try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) LauncherSettings.Settings.call( @@ -78,7 +76,6 @@ public class HotseatRestoreHelper { idp.numRows); backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true); transaction.commit(); - mBackupRestored = true; mLauncher.getModel().forceReload(); } }); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index 085b9b3af9..5ccc1e8681 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -17,6 +17,7 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE; import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; import static com.android.quickstep.views.RecentsView.TASK_MODALNESS; @@ -57,7 +58,7 @@ public final class RecentsViewStateController extends mRecentsView.updateEmptyMessage(); mRecentsView.resetTaskVisuals(); } - setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, state); + setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, new StateAnimationConfig(), state); mRecentsView.setFullscreenProgress(state.getOverviewFullscreenProgress()); } @@ -75,17 +76,19 @@ public final class RecentsViewStateController extends AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals)); } - setAlphas(builder, toState); + setAlphas(builder, config, toState); builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS, toState.getOverviewFullscreenProgress(), LINEAR); } - private void setAlphas(PropertySetter propertySetter, LauncherState state) { + private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config, + LauncherState state) { float buttonAlpha = (state.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1 : 0; propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA, buttonAlpha, LINEAR); propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(), - MultiValueAlpha.VALUE, buttonAlpha, LINEAR); + MultiValueAlpha.VALUE, buttonAlpha, config.getInterpolator( + ANIM_OVERVIEW_ACTIONS_FADE, LINEAR)); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index a0af79743b..daa1aadbae 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -17,7 +17,6 @@ package com.android.launcher3.uioverrides.states; import static android.view.View.VISIBLE; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; @@ -26,6 +25,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_PEEK; import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator; import static com.android.launcher3.anim.Interpolators.ACCEL; +import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; import static com.android.launcher3.anim.Interpolators.DEACCEL_3; @@ -52,6 +52,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_S import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import android.animation.Animator; import android.animation.AnimatorSet; @@ -163,10 +164,15 @@ public class QuickstepAtomicAnimationFactory extends config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL); config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL); config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f)); - config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL); - config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7); - Workspace workspace = mActivity.getWorkspace(); + config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL); + if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) { + config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME); + } else { + config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7); + } + + Workspace workspace = mActivity.getWorkspace(); // Start from a higher workspace scale, but only if we're invisible so we don't jump. boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE; if (isWorkspaceVisible) { @@ -206,13 +212,15 @@ public class QuickstepAtomicAnimationFactory extends config.setInterpolator(ANIM_WORKSPACE_SCALE, fromState == NORMAL ? ACCEL : OVERSHOOT_1_2); config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL); + config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); } else { config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2); + config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2); // Scale up the recents, if it is not coming from the side RecentsView overview = mActivity.getOverviewPanel(); if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) { - SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); + RECENTS_SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); } } config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2); @@ -225,7 +233,6 @@ public class QuickstepAtomicAnimationFactory extends : OVERSHOOT_1_7; config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator); config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator); - config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2); } else if (fromState == HINT_STATE && toState == NORMAL) { config.setInterpolator(ANIM_DEPTH, DEACCEL_3); if (mHintToNormalDuration == -1) { diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index c1a585ea9d..c6aaa438ca 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -25,7 +25,6 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS; -import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import android.animation.ValueAnimator; @@ -45,6 +44,7 @@ import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.graphics.OverviewScrim; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.testing.TestProtocol; @@ -52,7 +52,9 @@ import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.util.TouchController; +import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.AssistantUtilities; +import com.android.quickstep.util.OverviewToHomeAnim; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -63,6 +65,8 @@ public class NavBarToHomeTouchController implements TouchController, SingleAxisSwipeDetector.Listener { private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3; + // The min amount of overview scrim we keep during the transition. + private static final float OVERVIEW_TO_HOME_SCRIM_MULTIPLIER = 0.5f; private final Launcher mLauncher; private final SingleAxisSwipeDetector mSwipeDetector; @@ -156,8 +160,13 @@ public class NavBarToHomeTouchController implements TouchController, final PendingAnimation builder = new PendingAnimation(accuracy); if (mStartState.overviewUi) { RecentsView recentsView = mLauncher.getOverviewPanel(); - builder.setFloat(recentsView, ADJACENT_PAGE_OFFSET, - -mPullbackDistance / recentsView.getPageOffsetScale(), PULLBACK_INTERPOLATOR); + AnimatorControllerWithResistance.createRecentsResistanceFromOverviewAnim(mLauncher, + builder); + + builder.setFloat(mLauncher.getDragLayer().getOverviewScrim(), + OverviewScrim.SCRIM_MULTIPLIER, OVERVIEW_TO_HOME_SCRIM_MULTIPLIER, + PULLBACK_INTERPOLATOR); + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { builder.addOnFrameCallback( () -> recentsView.redrawLiveTile(false /* mightNeedToRefill */)); @@ -212,8 +221,13 @@ public class NavBarToHomeTouchController implements TouchController, recentsView.switchToScreenshot(null, () -> recentsView.finishRecentsAnimation(true /* toRecents */, null)); } - mLauncher.getStateManager().goToState(mEndState, true, - () -> onSwipeInteractionCompleted(mEndState)); + if (mStartState.overviewUi) { + new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState)) + .animateWithVelocity(velocity); + } else { + mLauncher.getStateManager().goToState(mEndState, true, + () -> onSwipeInteractionCompleted(mEndState)); + } if (mStartState != mEndState) { logStateChange(mStartState.containerType, logAction); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index 9316938c45..8f0f683d18 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -21,11 +21,8 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; -import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; -import android.animation.Animator; -import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.graphics.PointF; @@ -35,14 +32,15 @@ import android.view.MotionEvent; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.graphics.OverviewScrim; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.util.VibratorWrapper; -import com.android.quickstep.util.StaggeredWorkspaceAnim; +import com.android.quickstep.util.AnimatorControllerWithResistance; +import com.android.quickstep.util.OverviewToHomeAnim; import com.android.quickstep.views.RecentsView; /** @@ -62,10 +60,10 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo private boolean mDidTouchStartInNavBar; private boolean mReachedOverview; - private boolean mIsOverviewRehidden; - private boolean mIsHomeStaggeredAnimFinished; // The last recorded displacement before we reached overview. private PointF mStartDisplacement = new PointF(); + private float mStartY; + private AnimatorPlaybackController mOverviewResistYAnim; // Normal to Hint animation has flag SKIP_OVERVIEW, so we update this scrim with this animator. private ObjectAnimator mNormalToHintOverviewScrimAnimator; @@ -123,6 +121,7 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo mToState.getOverviewScrimAlpha(mLauncher)); } mReachedOverview = false; + mOverviewResistYAnim = null; } @Override @@ -137,6 +136,11 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo public void onDragEnd(float velocity) { super.onDragEnd(velocity); mNormalToHintOverviewScrimAnimator = null; + if (mLauncher.isInState(OVERVIEW)) { + // Normally we would cleanup the state based on mCurrentAnimation, but since we stop + // using that when we pause to go to Overview, we need to clean up ourselves. + clearState(); + } } @Override @@ -160,6 +164,9 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo mNormalToHintOverviewScrimAnimator = null; mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> { mLauncher.getStateManager().goToState(OVERVIEW, true, () -> { + mOverviewResistYAnim = AnimatorControllerWithResistance + .createRecentsResistanceFromOverviewAnim(mLauncher, null) + .createPlaybackController(); mReachedOverview = true; maybeSwipeInteractionToOverviewComplete(); }); @@ -173,13 +180,6 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo } } - // Used if flinging back to home after reaching overview - private void maybeSwipeInteractionToHomeComplete() { - if (mIsHomeStaggeredAnimFinished && mIsOverviewRehidden) { - onSwipeInteractionCompleted(NORMAL, Touch.FLING); - } - } - @Override protected boolean handlingOverviewAnim() { return mDidTouchStartInNavBar && super.handlingOverviewAnim(); @@ -193,11 +193,17 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo if (mMotionPauseDetector.isPaused()) { if (!mReachedOverview) { mStartDisplacement.set(xDisplacement, yDisplacement); + mStartY = event.getY(); } else { mRecentsView.setTranslationX((xDisplacement - mStartDisplacement.x) * OVERVIEW_MOVEMENT_FACTOR); - mRecentsView.setTranslationY((yDisplacement - mStartDisplacement.y) - * OVERVIEW_MOVEMENT_FACTOR); + float yProgress = (mStartDisplacement.y - yDisplacement) / mStartY; + if (yProgress > 0 && mOverviewResistYAnim != null) { + mOverviewResistYAnim.setPlayFraction(yProgress); + } else { + mRecentsView.setTranslationY((yDisplacement - mStartDisplacement.y) + * OVERVIEW_MOVEMENT_FACTOR); + } } // Stay in Overview. return true; @@ -212,35 +218,8 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo StateManager stateManager = mLauncher.getStateManager(); boolean goToHomeInsteadOfOverview = isFling; if (goToHomeInsteadOfOverview) { - if (velocity > 0) { - stateManager.goToState(NORMAL, true, - () -> onSwipeInteractionCompleted(NORMAL, Touch.FLING)); - } else { - mIsHomeStaggeredAnimFinished = mIsOverviewRehidden = false; - - StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim( - mLauncher, velocity, false /* animateOverviewScrim */); - staggeredWorkspaceAnim.addAnimatorListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - mIsHomeStaggeredAnimFinished = true; - maybeSwipeInteractionToHomeComplete(); - } - }).start(); - - // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here. - stateManager.cancelAnimation(); - StateAnimationConfig config = new StateAnimationConfig(); - config.duration = OVERVIEW.getTransitionDuration(mLauncher); - config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK; - AnimatorSet anim = stateManager.createAtomicAnimation( - stateManager.getState(), NORMAL, config); - anim.addListener(AnimationSuccessListener.forRunnable(() -> { - mIsOverviewRehidden = true; - maybeSwipeInteractionToHomeComplete(); - })); - anim.start(); - } + new OverviewToHomeAnim(mLauncher, ()-> onSwipeInteractionCompleted(NORMAL, Touch.FLING)) + .animateWithVelocity(velocity); } if (mReachedOverview) { float distanceDp = dpiFromPx(Math.max( @@ -256,6 +235,13 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo .withEndAction(goToHomeInsteadOfOverview ? null : this::maybeSwipeInteractionToOverviewComplete); + if (!goToHomeInsteadOfOverview) { + // Return to normal properties for the overview state. + StateAnimationConfig config = new StateAnimationConfig(); + config.duration = duration; + LauncherState state = mLauncher.getStateManager().getState(); + mLauncher.getStateManager().createAtomicAnimation(state, state, config).start(); + } } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index 1b439d1d31..5c9fd4705b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -15,7 +15,6 @@ */ package com.android.launcher3.uioverrides.touchcontrollers; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; @@ -23,7 +22,6 @@ import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS; import static com.android.launcher3.LauncherState.QUICK_SWITCH; import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD; import static com.android.launcher3.anim.Interpolators.ACCEL_0_75; -import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_5; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; @@ -47,6 +45,8 @@ import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE; import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; +import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import android.animation.Animator; @@ -74,7 +74,9 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.util.TouchController; import com.android.launcher3.util.VibratorWrapper; +import com.android.quickstep.AnimatedFloat; import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.util.ShelfPeekAnim; @@ -90,16 +92,17 @@ public class NoButtonQuickSwitchTouchController implements TouchController, BothAxesSwipeDetector.Listener, MotionPauseDetector.OnMotionPauseListener { /** The minimum progress of the scale/translationY animation until drag end. */ - private static final float Y_ANIM_MIN_PROGRESS = 0.15f; + private static final float Y_ANIM_MIN_PROGRESS = 0.25f; private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_5; private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCEL_0_75; - private static final Interpolator SCALE_DOWN_INTERPOLATOR = DEACCEL; + private static final Interpolator SCALE_DOWN_INTERPOLATOR = LINEAR; private final BaseQuickstepLauncher mLauncher; private final BothAxesSwipeDetector mSwipeDetector; private final ShelfPeekAnim mShelfPeekAnim; private final float mXRange; private final float mYRange; + private final float mMaxYProgress; private final MotionPauseDetector mMotionPauseDetector; private final float mMotionPauseMinDisplacement; private final LauncherRecentsView mRecentsView; @@ -113,7 +116,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, // and the other two to set overview properties based on x and y progress. private AnimatorPlaybackController mNonOverviewAnim; private AnimatorPlaybackController mXOverviewAnim; - private AnimatorPlaybackController mYOverviewAnim; + private AnimatedFloat mYOverviewAnim; public NoButtonQuickSwitchTouchController(BaseQuickstepLauncher launcher) { mLauncher = launcher; @@ -123,6 +126,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, mXRange = mLauncher.getDeviceProfile().widthPx / 2f; mYRange = LayoutUtils.getShelfTrackingDistance( mLauncher, mLauncher.getDeviceProfile(), mRecentsView.getPagedOrientationHandler()); + mMaxYProgress = mLauncher.getDeviceProfile().heightPx / mYRange; mMotionPauseDetector = new MotionPauseDetector(mLauncher); mMotionPauseMinDisplacement = mLauncher.getResources().getDimension( R.dimen.motion_pause_detector_min_displacement_from_app); @@ -244,7 +248,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, final LauncherState toState = OVERVIEW; // Set RecentView's initial properties. - SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]); + RECENTS_SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]); ADJACENT_PAGE_OFFSET.set(mRecentsView, 1f); mRecentsView.setContentAlpha(1); mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress()); @@ -266,11 +270,22 @@ public class NoButtonQuickSwitchTouchController implements TouchController, // - RecentsView scale // - RecentsView fullscreenProgress PendingAnimation yAnim = new PendingAnimation((long) (mYRange * 2)); - yAnim.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndOffset[0], SCALE_DOWN_INTERPOLATOR); + yAnim.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0], + SCALE_DOWN_INTERPOLATOR); yAnim.setFloat(mRecentsView, FULLSCREEN_PROGRESS, toState.getOverviewFullscreenProgress(), SCALE_DOWN_INTERPOLATOR); - mYOverviewAnim = yAnim.createPlaybackController(); - mYOverviewAnim.dispatchOnStart(); + AnimatorPlaybackController yNormalController = yAnim.createPlaybackController(); + AnimatorControllerWithResistance yAnimWithResistance = AnimatorControllerWithResistance + .createForRecents(yNormalController, mLauncher, + mRecentsView.getPagedViewOrientedState(), mLauncher.getDeviceProfile(), + mRecentsView, RECENTS_SCALE_PROPERTY, mRecentsView, + TASK_SECONDARY_TRANSLATION); + mYOverviewAnim = new AnimatedFloat(() -> { + if (mYOverviewAnim != null) { + yAnimWithResistance.setProgress(mYOverviewAnim.value, mMaxYProgress); + } + }); + yNormalController.dispatchOnStart(); } @Override @@ -306,7 +321,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, mXOverviewAnim.setPlayFraction(xProgress); } if (mYOverviewAnim != null) { - mYOverviewAnim.setPlayFraction(yProgress); + mYOverviewAnim.updateValue(yProgress); } return true; } @@ -353,9 +368,11 @@ public class NoButtonQuickSwitchTouchController implements TouchController, } else if (verticalFling) { targetState = velocity.y > 0 ? QUICK_SWITCH : NORMAL; } else { - // If user isn't flinging, just snap to the closest state based on x progress. + // If user isn't flinging, just snap to the closest state. boolean passedHorizontalThreshold = mXOverviewAnim.getInterpolatedProgress() > 0.5f; - targetState = passedHorizontalThreshold ? QUICK_SWITCH : NORMAL; + boolean passedVerticalThreshold = mYOverviewAnim.value > 1f; + targetState = passedHorizontalThreshold && !passedVerticalThreshold + ? QUICK_SWITCH : NORMAL; } // Animate the various components to the target state. @@ -374,9 +391,9 @@ public class NoButtonQuickSwitchTouchController implements TouchController, boolean flingUpToNormal = verticalFling && velocity.y < 0 && targetState == NORMAL; - float yProgress = mYOverviewAnim.getProgressFraction(); + float yProgress = mYOverviewAnim.value; float startYProgress = Utilities.boundToRange(yProgress - - velocity.y * getSingleFrameMs(mLauncher) / mYRange, 0f, 1f); + - velocity.y * getSingleFrameMs(mLauncher) / mYRange, 0f, mMaxYProgress); final float endYProgress; if (flingUpToNormal) { endYProgress = 1; @@ -386,12 +403,11 @@ public class NoButtonQuickSwitchTouchController implements TouchController, } else { endYProgress = 0; } - long yDuration = BaseSwipeDetector.calculateDuration(velocity.y, - Math.abs(endYProgress - startYProgress)); - ValueAnimator yOverviewAnim = mYOverviewAnim.getAnimationPlayer(); - yOverviewAnim.setFloatValues(startYProgress, endYProgress); + float yDistanceToCover = Math.abs(endYProgress - startYProgress) * mYRange; + long yDuration = (long) (yDistanceToCover / Math.max(1f, Math.abs(velocity.y))); + ValueAnimator yOverviewAnim = mYOverviewAnim.animateToValue(startYProgress, endYProgress); yOverviewAnim.setDuration(yDuration); - mYOverviewAnim.dispatchOnStart(); + mYOverviewAnim.updateValue(startYProgress); ValueAnimator nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer(); if (flingUpToNormal && !mIsHomeScreenVisible) { @@ -456,7 +472,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, mXOverviewAnim.getAnimationPlayer().cancel(); } if (mYOverviewAnim != null) { - mYOverviewAnim.getAnimationPlayer().cancel(); + mYOverviewAnim.cancelAnimation(); } mShelfPeekAnim.setShelfState(ShelfAnimState.CANCEL, LINEAR, 0); mMotionPauseDetector.clear(); 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 0ee5d047c6..2cfbc2bec6 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 @@ -25,6 +25,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.view.MotionEvent; import android.view.View; +import android.view.animation.Interpolator; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseDraggingActivity; @@ -82,7 +83,15 @@ public abstract class TaskViewTouchController mDetector = new SingleAxisSwipeDetector(activity, this, dir); } - private boolean canInterceptTouch() { + private boolean canInterceptTouch(MotionEvent ev) { + if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0) { + // Don't intercept swipes on the nav bar, as user might be trying to go home + // during a task dismiss animation. + if (mCurrentAnimation != null) { + mCurrentAnimation.getAnimationPlayer().end(); + } + return false; + } if (mCurrentAnimation != null) { mCurrentAnimation.forceFinishIfCloseToEnd(); } @@ -118,7 +127,7 @@ public abstract class TaskViewTouchController clearState(); } if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mNoIntercept = !canInterceptTouch(); + mNoIntercept = !canInterceptTouch(ev); if (mNoIntercept) { return false; } @@ -206,14 +215,19 @@ public abstract class TaskViewTouchController long maxDuration = 2 * secondaryLayerDimension; int verticalFactor = orientationHandler.getTaskDragDisplacementFactor(mIsRtl); int secondaryTaskDimension = orientationHandler.getSecondaryDimension(mTaskBeingDragged); + // The interpolator controlling the most prominent visual movement. We use this to determine + // whether we passed SUCCESS_TRANSITION_PROGRESS. + final Interpolator currentInterpolator; if (goingUp) { + currentInterpolator = Interpolators.LINEAR; mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged, true /* animateTaskView */, true /* removeTask */, maxDuration); mEndDisplacement = -secondaryTaskDimension; } else { + currentInterpolator = Interpolators.ZOOM_IN; mPendingAnimation = mRecentsView.createTaskLaunchAnimation( - mTaskBeingDragged, maxDuration, Interpolators.ZOOM_IN); + mTaskBeingDragged, maxDuration, currentInterpolator); // Since the thumbnail is what is filling the screen, based the end displacement on it. View thumbnailView = mTaskBeingDragged.getThumbnail(); @@ -228,6 +242,9 @@ public abstract class TaskViewTouchController } mCurrentAnimation = mPendingAnimation.createPlaybackController() .setOnCancelRunnable(this::clearState); + // Setting this interpolator doesn't affect the visual motion, but is used to determine + // whether we successfully reached the target state in onDragEnd(). + mCurrentAnimation.getTarget().setInterpolator(currentInterpolator); onUserControlledAnimationCreated(mCurrentAnimation); mCurrentAnimation.getTarget().addListener(this); mCurrentAnimation.dispatchOnStart(); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java index de83caf162..55f542461c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -24,6 +24,7 @@ import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MOD import android.animation.Animator; import android.animation.AnimatorSet; +import android.app.ActivityManager.RunningTaskInfo; import android.util.Log; import android.view.animation.Interpolator; @@ -52,17 +53,17 @@ final class AppToOverviewAnimationProvider> extend private final BaseActivityInterface mActivityInterface; // The id of the currently running task that is transitioning to overview. - private final int mTargetTaskId; + private final RunningTaskInfo mTargetTask; private final RecentsAnimationDeviceState mDeviceState; private T mActivity; private RecentsView mRecentsView; AppToOverviewAnimationProvider( - BaseActivityInterface activityInterface, int targetTaskId, + BaseActivityInterface activityInterface, RunningTaskInfo targetTask, RecentsAnimationDeviceState deviceState) { mActivityInterface = activityInterface; - mTargetTaskId = targetTaskId; + mTargetTask = targetTask; mDeviceState = deviceState; } @@ -73,13 +74,13 @@ final class AppToOverviewAnimationProvider> extend * @param wasVisible true if it was visible before */ boolean onActivityReady(T activity, Boolean wasVisible) { - activity.getOverviewPanel().showCurrentTask(mTargetTaskId); + activity.getOverviewPanel().showCurrentTask(mTargetTask); AbstractFloatingView.closeAllOpenViews(activity, wasVisible); BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI( mDeviceState, wasVisible, (controller) -> { - controller.dispatchOnStart(); - controller.getAnimationPlayer().end(); + controller.getNormalController().dispatchOnStart(); + controller.getNormalController().getAnimationPlayer().end(); }); factory.createActivityInterface(RECENTS_LAUNCH_DURATION); factory.setRecentsAttachedToAppWindow(true, false); @@ -122,7 +123,8 @@ final class AppToOverviewAnimationProvider> extend wallpaperTargets, MODE_CLOSING); // Use the top closing app to determine the insets for the animation - RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId); + RemoteAnimationTargetCompat runningTaskTarget = mTargetTask == null ? null + : targets.findTask(mTargetTask.taskId); if (runningTaskTarget == null) { Log.e(TAG, "No closing app"); return pa.buildAnim(); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index aba5ab63e1..c90f70125a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -15,6 +15,8 @@ */ package com.android.quickstep; +import static android.widget.Toast.LENGTH_SHORT; + import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; @@ -27,11 +29,13 @@ import android.graphics.Rect; import android.os.Build; import android.util.Log; import android.view.MotionEvent; +import android.widget.Toast; import androidx.annotation.CallSuper; import androidx.annotation.UiThread; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.R; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.VibratorWrapper; @@ -140,10 +144,10 @@ public abstract class BaseSwipeUpHandler, Q extend mRecentsView.getNextPageTaskView().launchTask(false /* animate */, true /* freezeTaskList */); } else { - int taskId = mRecentsView.getNextPageTaskView().getTask().key.id; if (!mCanceled) { - TaskView nextTask = mRecentsView.getTaskView(taskId); + TaskView nextTask = mRecentsView.getNextPageTaskView(); if (nextTask != null) { + int taskId = nextTask.getTask().key.id; mGestureState.updateLastStartedTaskId(taskId); boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds() .contains(taskId); @@ -160,6 +164,10 @@ public abstract class BaseSwipeUpHandler, Q extend mRecentsAnimationController.finish(true /* toRecents */, null); } }, MAIN_EXECUTOR.getHandler()); + } else { + mActivityInterface.onLaunchTaskFailed(); + Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show(); + mRecentsAnimationController.finish(true /* toRecents */, null); } } mCanceled = false; @@ -357,8 +365,7 @@ public abstract class BaseSwipeUpHandler, Q extend */ protected void applyWindowTransform() { if (mWindowTransitionController != null) { - float progress = mCurrentShift.value / mDragLengthFactor; - mWindowTransitionController.setPlayFraction(progress); + mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor); } if (mRecentsAnimationTargets != null) { if (mRecentsViewScrollLinked) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java index f0f3d0f1e3..0aa14862a7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java @@ -21,7 +21,6 @@ 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.UNSTABLE_SPRINGS; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; @@ -45,7 +44,6 @@ import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHO import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; import android.animation.Animator; -import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.ActivityManager; @@ -67,7 +65,6 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.UserEventDispatcher; @@ -80,6 +77,7 @@ import com.android.quickstep.BaseActivityInterface.AnimationFactory; import com.android.quickstep.GestureState.GestureEndTarget; import com.android.quickstep.inputconsumers.OverviewInputConsumer; import com.android.quickstep.util.ActiveGestureLog; +import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.ShelfPeekAnim; import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState; @@ -181,8 +179,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte private ThumbnailData mTaskSnapshot; // Used to control launcher components throughout the swipe gesture. - private AnimatorPlaybackController mLauncherTransitionController; - private boolean mHasLauncherTransitionControllerStarted; + private AnimatorControllerWithResistance mLauncherTransitionController; private AnimationFactory mAnimationFactory = (t) -> { }; @@ -266,10 +263,6 @@ public abstract class BaseSwipeUpHandlerV2, Q exte mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK, this::notifyTransitionCancelled); - mGestureState.runOnceAtState(STATE_END_TARGET_SET, - () -> mDeviceState.onEndTargetCalculated(mGestureState.getEndTarget(), - mActivityInterface)); - if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) { mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT, @@ -337,7 +330,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) { return; } - mTaskViewSimulator.setRecentsConfiguration(mActivity.getResources().getConfiguration()); + mTaskViewSimulator.setRecentsRotation(mActivity.getDisplay().getRotation()); // 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. @@ -400,6 +393,11 @@ public abstract class BaseSwipeUpHandlerV2, Q exte mGestureState.getActivityInterface().setOnDeferredActivityLaunchCallback( mOnDeferredActivityLaunch); + mGestureState.runOnceAtState(STATE_END_TARGET_SET, + () -> mDeviceState.getRotationTouchHelper(). + onEndTargetCalculated(mGestureState.getEndTarget(), + mActivityInterface)); + notifyGestureStartedAsync(); } @@ -423,7 +421,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte } protected void notifyGestureAnimationStartToRecents() { - mRecentsView.onGestureAnimationStart(mGestureState.getRunningTaskId()); + mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask()); } private void launcherFrameDrawn() { @@ -490,6 +488,20 @@ public abstract class BaseSwipeUpHandlerV2, Q exte recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask; } mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate); + + // Reapply window transform throughout the attach animation, as the animation affects how + // much the window is bound by overscroll (vs moving freely). + if (animate) { + ValueAnimator reapplyWindowTransformAnim = ValueAnimator.ofFloat(0, 1); + reapplyWindowTransformAnim.addUpdateListener(anim -> { + if (mRunningWindowAnim == null) { + applyWindowTransform(); + } + }); + reapplyWindowTransformAnim.setDuration(RECENTS_ATTACH_DURATION).start(); + } else { + applyWindowTransform(); + } } @Override @@ -527,11 +539,11 @@ public abstract class BaseSwipeUpHandlerV2, Q exte /** * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME - * (it has its own animation) or if we're already animating the current controller. + * (it has its own animation). * @return Whether we can create the launcher controller or update its progress. */ private boolean canCreateNewOrUpdateExistingLauncherTransitionController() { - return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted; + return mGestureState.getEndTarget() != HOME; } @Override @@ -541,10 +553,9 @@ public abstract class BaseSwipeUpHandlerV2, Q exte return result; } - private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) { + private void onAnimatorPlaybackControllerCreated(AnimatorControllerWithResistance anim) { mLauncherTransitionController = anim; - mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor); - mLauncherTransitionController.dispatchOnStart(); + mLauncherTransitionController.getNormalController().dispatchOnStart(); updateLauncherTransitionProgress(); } @@ -581,10 +592,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte || !canCreateNewOrUpdateExistingLauncherTransitionController()) { return; } - // Normalize the progress to 0 to 1, as the animation controller will clamp it to that - // anyway. The controller mimics the drag length factor by applying it to its interpolators. - float progress = mCurrentShift.value / mDragLengthFactor; - mLauncherTransitionController.setPlayFraction(progress); + mLauncherTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor); } /** @@ -1027,31 +1035,6 @@ public abstract class BaseSwipeUpHandlerV2, Q exte windowAnim.start(); mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); } - // Always play the entire launcher animation when going home, since it is separate from - // the animation that has been controlled thus far. - if (mGestureState.getEndTarget() == HOME) { - start = 0; - } - - // We want to use the same interpolator as the window, but need to adjust it to - // interpolate over the remaining progress (end - start). - TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress( - interpolator, start, end); - if (mLauncherTransitionController == null) { - return; - } - if (start == end || duration <= 0) { - mLauncherTransitionController.dispatchSetInterpolator(t -> end); - } else { - mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator); - } - mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration)); - - if (UNSTABLE_SPRINGS.get()) { - mLauncherTransitionController.dispatchOnStart(); - } - mLauncherTransitionController.getAnimationPlayer().start(); - mHasLauncherTransitionControllerStarted = true; } private void computeRecentsScrollIfInvisible() { @@ -1172,10 +1155,6 @@ public abstract class BaseSwipeUpHandlerV2, Q exte private void cancelCurrentAnimation() { mCanceled = true; mCurrentShift.cancelAnimation(); - if (mLauncherTransitionController != null && mLauncherTransitionController - .getAnimationPlayer().isStarted()) { - mLauncherTransitionController.getAnimationPlayer().cancel(); - } } private void invalidateHandler() { @@ -1201,7 +1180,10 @@ public abstract class BaseSwipeUpHandlerV2, Q exte private void endLauncherTransitionController() { setShelfState(ShelfAnimState.CANCEL, LINEAR, 0); if (mLauncherTransitionController != null) { - mLauncherTransitionController.getAnimationPlayer().end(); + // End the animation, but stay at the same visual progress. + mLauncherTransitionController.getNormalController().dispatchSetInterpolator( + t -> Utilities.boundToRange(mCurrentShift.value, 0, 1)); + mLauncherTransitionController.getNormalController().getAnimationPlayer().end(); mLauncherTransitionController = null; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java index 33b9cdee08..3898f0b0ca 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java @@ -27,11 +27,11 @@ import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; -import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.fallback.RecentsState; import com.android.quickstep.util.ActivityInitListener; +import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -84,7 +84,7 @@ public final class FallbackActivityInterface extends /** 6 */ @Override public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState, - boolean activityVisible, Consumer callback) { + boolean activityVisible, Consumer callback) { DefaultAnimationFactory factory = new DefaultAnimationFactory(callback); factory.initUI(); return factory; @@ -140,7 +140,7 @@ public final class FallbackActivityInterface extends } @Override - public void onExitOverview(RecentsAnimationDeviceState deviceState, Runnable exitRunnable) { + public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) { // no-op, fake landscape not supported for 3P } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java index fc7a119f21..f60a50bb43 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java @@ -15,14 +15,35 @@ */ package com.android.quickstep; +import static android.content.Intent.EXTRA_COMPONENT_NAME; +import static android.content.Intent.EXTRA_USER; + +import static com.android.launcher3.GestureNavContract.EXTRA_GESTURE_CONTRACT; +import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION; +import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE; +import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; import android.animation.ObjectAnimator; +import android.annotation.TargetApi; import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.ParcelUuid; +import android.os.UserHandle; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; import androidx.annotation.NonNull; @@ -32,19 +53,33 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.quickstep.fallback.FallbackRecentsView; +import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.TransformParams; import com.android.quickstep.util.TransformParams.BuilderProxy; +import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; +import java.lang.ref.WeakReference; +import java.util.UUID; +import java.util.function.Consumer; + /** * Handles the navigation gestures when a 3rd party launcher is the default home activity. */ +@TargetApi(Build.VERSION_CODES.R) public class FallbackSwipeHandler extends BaseSwipeUpHandlerV2 { + /** + * Message used for receiving gesture nav contract information. We use a static messenger to + * avoid leaking too make binders in case the receiving launcher does not handle the contract + * properly. + */ + private static StaticMessageReceiver sMessageReceiver = null; + private FallbackHomeAnimationFactory mActiveAnimationFactory; private final boolean mRunningOverHome; @@ -89,7 +124,9 @@ public class FallbackSwipeHandler extends protected HomeAnimationFactory createHomeAnimationFactory(long duration) { mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration); ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); - mContext.startActivity(new Intent(mGestureState.getHomeIntent()), options.toBundle()); + Intent intent = new Intent(mGestureState.getHomeIntent()); + mActiveAnimationFactory.addGestureContract(intent); + mContext.startActivity(intent, options.toBundle()); return mActiveAnimationFactory; } @@ -130,17 +167,20 @@ public class FallbackSwipeHandler extends } private class FallbackHomeAnimationFactory extends HomeAnimationFactory { - + private final Rect mTempRect = new Rect(); private final TransformParams mHomeAlphaParams = new TransformParams(); private final AnimatedFloat mHomeAlpha; private final AnimatedFloat mVerticalShiftForScale = new AnimatedFloat(); - private final AnimatedFloat mRecentsAlpha = new AnimatedFloat(); + private final RectF mTargetRect = new RectF(); + private SurfaceControl mSurfaceControl; + private final long mDuration; + + private RectFSpringAnim mSpringAnim; FallbackHomeAnimationFactory(long duration) { - super(null); mDuration = duration; if (mRunningOverHome) { @@ -162,6 +202,15 @@ public class FallbackSwipeHandler extends this::updateRecentsActivityTransformDuringHomeAnim); } + @NonNull + @Override + public RectF getWindowTargetRect() { + if (mTargetRect.isEmpty()) { + mTargetRect.set(super.getWindowTargetRect()); + } + return mTargetRect; + } + private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder, RemoteAnimationTargetCompat app, TransformParams params) { builder.withAlpha(mRecentsAlpha.value); @@ -218,5 +267,87 @@ public class FallbackSwipeHandler extends .start(); } } + + @Override + public void setAnimation(RectFSpringAnim anim) { + mSpringAnim = anim; + } + + private void onMessageReceived(Message msg) { + try { + Bundle data = msg.getData(); + RectF position = data.getParcelable(EXTRA_ICON_POSITION); + if (!position.isEmpty()) { + mSurfaceControl = data.getParcelable(EXTRA_ICON_SURFACE); + mTargetRect.set(position); + if (mSpringAnim != null) { + mSpringAnim.onTargetPositionChanged(); + } + } + } catch (Exception e) { + // Ignore + } + } + + @Override + public void update(RectF currentRect, float progress, float radius) { + if (mSurfaceControl != null) { + currentRect.roundOut(mTempRect); + Transaction t = new Transaction(); + t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0); + t.apply(); + } + } + + private void addGestureContract(Intent intent) { + if (mRunningOverHome || mGestureState.getRunningTask() == null) { + return; + } + + TaskKey key = new TaskKey(mGestureState.getRunningTask()); + if (key.getComponent() != null) { + if (sMessageReceiver == null) { + sMessageReceiver = new StaticMessageReceiver(); + } + + Bundle gestureNavContract = new Bundle(); + gestureNavContract.putParcelable(EXTRA_COMPONENT_NAME, key.getComponent()); + gestureNavContract.putParcelable(EXTRA_USER, UserHandle.of(key.userId)); + gestureNavContract.putParcelable(EXTRA_REMOTE_CALLBACK, + sMessageReceiver.newCallback(this::onMessageReceived)); + intent.putExtra(EXTRA_GESTURE_CONTRACT, gestureNavContract); + } + } + } + + private static class StaticMessageReceiver implements Handler.Callback { + + private final Messenger mMessenger = + new Messenger(new Handler(Looper.getMainLooper(), this)); + + private ParcelUuid mCurrentUID = new ParcelUuid(UUID.randomUUID()); + private WeakReference> mCurrentCallback = new WeakReference<>(null); + + public Message newCallback(Consumer callback) { + mCurrentUID = new ParcelUuid(UUID.randomUUID()); + mCurrentCallback = new WeakReference<>(callback); + + Message msg = Message.obtain(); + msg.replyTo = mMessenger; + msg.obj = mCurrentUID; + return msg; + } + + @Override + public boolean handleMessage(@NonNull Message message) { + if (mCurrentUID.equals(message.obj)) { + Consumer consumer = mCurrentCallback.get(); + if (consumer != null) { + consumer.accept(message); + return true; + } + } + return false; + } } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java index edefbe1252..cd4be1895b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java @@ -39,7 +39,6 @@ import com.android.launcher3.LauncherInitListener; import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.allapps.DiscoveryBounce; -import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.appprediction.PredictionUiStateManager; import com.android.launcher3.statehandlers.DepthController; @@ -50,6 +49,7 @@ import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.util.ActivityInitListener; +import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState; import com.android.quickstep.views.RecentsView; @@ -105,7 +105,7 @@ public final class LauncherActivityInterface extends // recents, we assume the first task is invisible, making translation off by one task. launcher.getStateManager().reapplyState(); launcher.getRootView().setForceHideBackArrow(false); - notifyRecentsOfOrientation(deviceState); + notifyRecentsOfOrientation(deviceState.getRotationTouchHelper()); } @Override @@ -119,8 +119,8 @@ public final class LauncherActivityInterface extends @Override public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState, - boolean activityVisible, Consumer callback) { - notifyRecentsOfOrientation(deviceState); + boolean activityVisible, Consumer callback) { + notifyRecentsOfOrientation(deviceState.getRotationTouchHelper()); DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) { @Override public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, @@ -228,7 +228,7 @@ public final class LauncherActivityInterface extends @Override - public void onExitOverview(RecentsAnimationDeviceState deviceState, Runnable exitRunnable) { + public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) { final StateManager stateManager = getCreatedActivity().getStateManager(); stateManager.addStateListener( new StateManager.StateListener() { @@ -244,11 +244,11 @@ public final class LauncherActivityInterface extends }); } - private void notifyRecentsOfOrientation(RecentsAnimationDeviceState deviceState) { + private void notifyRecentsOfOrientation(RotationTouchHelper rotationTouchHelper) { // reset layout on swipe to home RecentsView recentsView = getCreatedActivity().getOverviewPanel(); - recentsView.setLayoutRotation(deviceState.getCurrentActiveRotation(), - deviceState.getDisplayRotation()); + recentsView.setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(), + rotationTouchHelper.getDisplayRotation()); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java index fa7d2689f3..052d0a68a6 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -16,6 +16,7 @@ package com.android.quickstep; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; import android.animation.AnimatorSet; import android.content.Context; @@ -28,6 +29,7 @@ import androidx.annotation.NonNull; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.views.FloatingIconView; +import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.StaggeredWorkspaceAnim; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -72,36 +74,39 @@ public class LauncherSwipeHandlerV2 extends mActivity.getRootView().setForceHideBackArrow(true); mActivity.setHintUserWillBeActive(); - homeAnimFactory = new HomeAnimationFactory(floatingIconView) { - - @Override - public RectF getWindowTargetRect() { - if (canUseWorkspaceView) { + if (canUseWorkspaceView) { + // 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. + float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION; + homeAnimFactory = new LauncherHomeAnimationFactory() { + @Override + public RectF getWindowTargetRect() { return iconLocation; - } else { - return super.getWindowTargetRect(); } - } - @NonNull - @Override - public AnimatorPlaybackController createActivityAnimationToHome() { - // Return an empty APC here since we have an non-user controlled animation - // to home. - long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx); - return mActivity.getStateManager().createAnimationToNewWorkspace( - NORMAL, accuracy, 0 /* animComponents */); - } + @Override + public void setAnimation(RectFSpringAnim anim) { + anim.addAnimatorListener(floatingIconView); + floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged); + floatingIconView.setFastFinishRunnable(anim::end); + } - @Override - public void playAtomicAnimation(float velocity) { - new StaggeredWorkspaceAnim(mActivity, velocity, - true /* animateOverviewScrim */).start(); - } - }; + @Override + public void update(RectF currentRect, float progress, float radius) { + floatingIconView.update(currentRect, 1f, progress, windowAlphaThreshold, + radius, false); + } + @Override + public void onCancel() { + floatingIconView.fastFinish(); + } + }; + } else { + homeAnimFactory = new LauncherHomeAnimationFactory(); + } } else { - homeAnimFactory = new HomeAnimationFactory(null) { + homeAnimFactory = new HomeAnimationFactory() { @Override public AnimatorPlaybackController createActivityAnimationToHome() { return AnimatorPlaybackController.wrap(new AnimatorSet(), duration); @@ -118,4 +123,22 @@ public class LauncherSwipeHandlerV2 extends mRecentsAnimationController.finish( true /* toRecents */, callback, true /* sendUserLeaveHint */); } + + private class LauncherHomeAnimationFactory extends HomeAnimationFactory { + @NonNull + @Override + public AnimatorPlaybackController createActivityAnimationToHome() { + // Return an empty APC here since we have an non-user controlled animation + // to home. + long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx); + return mActivity.getStateManager().createAnimationToNewWorkspace( + NORMAL, accuracy, 0 /* animComponents */); + } + + @Override + public void playAtomicAnimation(float velocity) { + new StaggeredWorkspaceAnim(mActivity, velocity, + true /* animateOverviewScrim */).start(); + } + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverscrollPluginFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverscrollPluginFactory.java new file mode 100644 index 0000000000..4c261abc7a --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverscrollPluginFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import static com.android.launcher3.util.MainThreadInitializedObject.forOverride; + +import com.android.launcher3.R; +import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.launcher3.util.ResourceBasedOverride; +import com.android.systemui.plugins.OverscrollPlugin; + +/** + * Resource overrideable factory for forcing a local overscroll plugin. + * Override {@link R.string#overscroll_plugin_factory_class} to set a different class. + */ +public class OverscrollPluginFactory implements ResourceBasedOverride { + public static final MainThreadInitializedObject INSTANCE = forOverride( + OverscrollPluginFactory.class, + R.string.overscroll_plugin_factory_class); + + /** + * Get the plugin that is defined locally in launcher, as opposed to a dynamic side loaded one. + */ + public OverscrollPlugin getLocalOverscrollPlugin() { + return null; + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java index 434a929b5c..4879db7897 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java @@ -165,7 +165,7 @@ public class OverviewCommandHelper { mActivityInterface = mOverviewComponentObserver.getActivityInterface(); mCreateTime = SystemClock.elapsedRealtime(); mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface, - RecentsModel.getRunningTaskId(), mDeviceState); + ActivityManagerWrapper.getInstance().getRunningTask(), mDeviceState); // Preload the plan mRecentsModel.getTasks(null); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java index 6f4d34c8c7..5026f36e08 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -58,6 +58,12 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()); return response; } + + case TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED: { + response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, + FeatureFlags.ENABLE_OVERVIEW_SHARE.get()); + return response; + } } return super.call(method); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java index dc8f1c54c8..2963b6ce18 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -16,8 +16,7 @@ package com.android.quickstep; import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; -import static com.android.launcher3.anim.Interpolators.DEACCEL; -import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; +import static com.android.launcher3.anim.Interpolators.LINEAR; import android.animation.Animator; import android.content.Context; @@ -25,10 +24,8 @@ import android.graphics.Matrix; import android.graphics.Matrix.ScaleToFit; import android.graphics.Rect; import android.graphics.RectF; -import android.view.animation.Interpolator; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.android.launcher3.DeviceProfile; @@ -37,7 +34,7 @@ import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.touch.PagedOrientationHandler; -import com.android.launcher3.views.FloatingIconView; +import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; @@ -48,7 +45,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat. public abstract class SwipeUpAnimationLogic { protected static final Rect TEMP_RECT = new Rect(); - private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL; protected DeviceProfile mDp; @@ -69,12 +65,8 @@ public abstract class SwipeUpAnimationLogic { protected int mTransitionDragLength; // How much further we can drag past recents, as a factor of mTransitionDragLength. protected float mDragLengthFactor = 1; - // Start resisting when swiping past this factor of mTransitionDragLength. - private float mDragLengthFactorStartPullback = 1f; - // This is how far down we can scale down, where 0f is full screen and 1f is recents. - private float mDragLengthFactorMaxPullback = 1f; - protected AnimatorPlaybackController mWindowTransitionController; + protected AnimatorControllerWithResistance mWindowTransitionController; public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState, GestureState gestureState, TransformParams transformParams) { @@ -85,7 +77,8 @@ public abstract class SwipeUpAnimationLogic { mTransformParams = transformParams; mTaskViewSimulator.setLayoutRotation( - mDeviceState.getCurrentActiveRotation(), mDeviceState.getDisplayRotation()); + mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(), + mDeviceState.getRotationTouchHelper().getDisplayRotation()); } protected void initTransitionEndpoints(DeviceProfile dp) { @@ -99,19 +92,17 @@ public abstract class SwipeUpAnimationLogic { if (mDeviceState.isFullyGesturalNavMode()) { // We can drag all the way to the top of the screen. mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength; - - float startScale = mTaskViewSimulator.getFullScreenScale(); - // Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f. - mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale); - mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale); } else { - mDragLengthFactor = 1; - mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1; + mDragLengthFactor = 1 + AnimatorControllerWithResistance.TWO_BUTTON_EXTRA_DRAG_FACTOR; } PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2); - mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor); - mWindowTransitionController = pa.createPlaybackController(); + mTaskViewSimulator.addAppToOverviewAnim(pa, LINEAR); + AnimatorPlaybackController normalController = pa.createPlaybackController(); + mWindowTransitionController = AnimatorControllerWithResistance.createForRecents( + normalController, mContext, mTaskViewSimulator.getOrientationState(), + mDp, mTaskViewSimulator.recentsViewScale, AnimatedFloat.VALUE, + mTaskViewSimulator.recentsViewSecondaryTranslation, AnimatedFloat.VALUE); } @UiThread @@ -124,13 +115,6 @@ public abstract class SwipeUpAnimationLogic { } else { float translation = Math.max(displacement, 0); shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength; - if (shift > mDragLengthFactorStartPullback) { - float pullbackProgress = Utilities.getProgress(shift, - mDragLengthFactorStartPullback, mDragLengthFactor); - pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress); - shift = mDragLengthFactorStartPullback + pullbackProgress - * (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback); - } } mCurrentShift.updateValue(shift); @@ -148,12 +132,6 @@ public abstract class SwipeUpAnimationLogic { protected abstract class HomeAnimationFactory { - public FloatingIconView mIconView; - - public HomeAnimationFactory(@Nullable FloatingIconView iconView) { - mIconView = iconView; - } - public @NonNull RectF getWindowTargetRect() { PagedOrientationHandler orientationHandler = getOrientationHandler(); DeviceProfile dp = mDp; @@ -174,6 +152,12 @@ public abstract class SwipeUpAnimationLogic { public void playAtomicAnimation(float velocity) { // No-op } + + public void setAnimation(RectFSpringAnim anim) { } + + public void update(RectF currentRect, float progress, float radius) { } + + public void onCancel() { } } /** @@ -184,10 +168,8 @@ public abstract class SwipeUpAnimationLogic { protected RectFSpringAnim createWindowAnimationToHome(float startProgress, HomeAnimationFactory homeAnimationFactory) { final RectF targetRect = homeAnimationFactory.getWindowTargetRect(); - final FloatingIconView fiv = homeAnimationFactory.mIconView; - final boolean isFloatingIconView = fiv != null; - mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor); + mCurrentShift.updateValue(startProgress); mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress)); RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect()); @@ -203,11 +185,7 @@ public abstract class SwipeUpAnimationLogic { windowToHomePositionMap.mapRect(startRect); RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext); - if (isFloatingIconView) { - anim.addAnimatorListener(fiv); - fiv.setOnTargetChangeListener(anim::onTargetPositionChanged); - fiv.setFastFinishRunnable(anim::end); - } + homeAnimationFactory.setAnimation(anim); SpringAnimationRunner runner = new SpringAnimationRunner( homeAnimationFactory, cropRectF, homeToWindowPositionMap); @@ -242,32 +220,27 @@ public abstract class SwipeUpAnimationLogic { final RectF mWindowCurrentRect = new RectF(); final Matrix mHomeToWindowPositionMap; + final HomeAnimationFactory mAnimationFactory; - final FloatingIconView mFIV; final AnimatorPlaybackController mHomeAnim; final RectF mCropRectF; final float mStartRadius; final float mEndRadius; - final float mWindowAlphaThreshold; SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF, Matrix homeToWindowPositionMap) { + mAnimationFactory = factory; mHomeAnim = factory.createActivityAnimationToHome(); mCropRectF = cropRectF; mHomeToWindowPositionMap = homeToWindowPositionMap; cropRectF.roundOut(mCropRect); - mFIV = factory.mIconView; // End on a "round-enough" radius so that the shape reveal doesn't have to do too much // rounding at the end of the animation. mStartRadius = mTaskViewSimulator.getCurrentCornerRadius(); mEndRadius = cropRectF.width() / 2f; - - // We want the window alpha to be 0 once this threshold is met, so that the - // FolderIconView can be seen morphing into the icon shape. - mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f; } @Override @@ -282,10 +255,7 @@ public abstract class SwipeUpAnimationLogic { .setCornerRadius(cornerRadius); mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this)); - if (mFIV != null) { - mFIV.update(currentRect, 1f, progress, - mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false); - } + mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius)); } @Override @@ -298,9 +268,7 @@ public abstract class SwipeUpAnimationLogic { @Override public void onCancel() { - if (mFIV != null) { - mFIV.fastFinish(); - } + mAnimationFactory.onCancel(); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java index e9614d1351..36579ec736 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java @@ -18,7 +18,7 @@ package com.android.quickstep; import static android.view.Surface.ROTATION_0; -import static com.android.launcher3.util.MainThreadInitializedObject.forOverride; +import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL; import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED; import android.annotation.SuppressLint; @@ -38,13 +38,11 @@ import com.android.launcher3.R; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; -import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.ResourceBasedOverride; import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.TaskThumbnailView; import com.android.quickstep.views.TaskView; -import com.android.systemui.plugins.OverscrollPlugin; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -90,20 +88,22 @@ public class TaskOverlayFactory implements ResourceBasedOverride { return shortcuts; } - public static final MainThreadInitializedObject INSTANCE = - forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class); - - /** - * @return a launcher-provided OverscrollPlugin if available, otherwise null - */ - public OverscrollPlugin getLocalOverscrollPlugin() { - return null; - } - public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) { return new TaskOverlay(thumbnailView); } + /** + * Subclasses can attach any system listeners in this method, must be paired with + * {@link #removeListeners()} + */ + public void initListeners() { } + + /** + * Subclasses should remove any system listeners in this method, must be paired with + * {@link #initListeners()} + */ + public void removeListeners() { } + /** Note that these will be shown in order from top to bottom, if available for the task. */ private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{ TaskShortcutFactory.APP_INFO, @@ -146,26 +146,29 @@ public class TaskOverlayFactory implements ResourceBasedOverride { */ public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix, boolean rotated) { - final boolean isAllowedByPolicy = thumbnail.isRealSnapshot; + getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null); - getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated); + if (thumbnail != null) { + getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated); + final boolean isAllowedByPolicy = thumbnail.isRealSnapshot; - getActionsView().setCallbacks(new OverlayUICallbacks() { - @Override - public void onShare() { - if (isAllowedByPolicy) { - mImageApi.startShareActivity(); - } else { - showBlockedByPolicyMessage(); + getActionsView().setCallbacks(new OverlayUICallbacks() { + @Override + public void onShare() { + if (isAllowedByPolicy) { + mImageApi.startShareActivity(); + } else { + showBlockedByPolicyMessage(); + } } - } - @SuppressLint("NewApi") - @Override - public void onScreenshot() { - saveScreenshot(task); - } - }); + @SuppressLint("NewApi") + @Override + public void onScreenshot() { + saveScreenshot(task); + } + }); + } } /** 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 6e0b517b25..7b91001c5f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -258,6 +258,7 @@ public class TouchInteractionService extends Service implements PluginListener mAM.getRunningTask(false /* filterOnlyVisibleRecents */))); } return gestureState; @@ -595,9 +597,8 @@ public class TouchInteractionService extends Service implements PluginListener mAM.getRunningTask(true /* filterOnlyVisibleRecents */))); ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent(); ComponentName runningComponent = diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java index 3f1e7bad9b..24a761066a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java @@ -15,17 +15,19 @@ */ package com.android.quickstep.fallback; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y; import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK; import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE; import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_MODALNESS; +import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PropertySetter; @@ -82,10 +84,12 @@ public class FallbackRecentsStateController implements StateHandler */ public void onGestureAnimationStartOnHome(RunningTaskInfo homeTaskInfo) { mHomeTaskInfo = homeTaskInfo; - onGestureAnimationStart(homeTaskInfo == null ? -1 : homeTaskInfo.taskId); + onGestureAnimationStart(homeTaskInfo); } /** @@ -107,14 +107,15 @@ public class FallbackRecentsView extends RecentsView } @Override - protected boolean shouldAddDummyTaskView(int runningTaskId) { - if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId + protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) { + if (mHomeTaskInfo != null && runningTaskInfo != null && + mHomeTaskInfo.taskId == runningTaskInfo.taskId && getTaskViewCount() == 0) { // Do not add a dummy task if we are running over home with empty recents, so that we // show the empty recents message instead of showing a dummy task and later removing it. return false; } - return super.shouldAddDummyTaskView(runningTaskId); + return super.shouldAddDummyTaskView(runningTaskInfo); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java index 211a30c28d..f15a9de6f8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java @@ -90,7 +90,6 @@ public class RecentsState implements BaseState { return new float[] { NO_SCALE, NO_OFFSET }; } - private static class ModalState extends RecentsState { public ModalState(int id, int flags) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java index 5ad48ebabc..0c2c92cfb0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java @@ -99,7 +99,8 @@ public class AccessibilityInputConsumer extends DelegateInputConsumer { case ACTION_POINTER_DOWN: { if (mState == STATE_INACTIVE) { int pointerIndex = ev.getActionIndex(); - if (mDeviceState.isInSwipeUpTouchRegion(ev, pointerIndex) + if (mDeviceState.getRotationTouchHelper() + .isInSwipeUpTouchRegion(ev, pointerIndex) && mDelegate.allowInterceptByParent()) { setActive(ev); 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 3a97216035..a676390ce3 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 @@ -147,7 +147,7 @@ public class DeviceLockedInputConsumer implements InputConsumer, if (!mThresholdCrossed) { // Cancel interaction in case of multi-touch interaction int ptrIdx = ev.getActionIndex(); - if (!mDeviceState.isInSwipeUpTouchRegion(ev, ptrIdx)) { + if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx)) { int action = ev.getAction(); ev.setAction(ACTION_CANCEL); finishTouchTracking(ev); 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 26df9c7b93..6259f1f9fe 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 @@ -59,6 +59,7 @@ import com.android.quickstep.GestureState; import com.android.quickstep.InputConsumer; import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.RecentsAnimationDeviceState; +import com.android.quickstep.RotationTouchHelper; import com.android.quickstep.TaskAnimationManager; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.CachedEventDispatcher; @@ -86,6 +87,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private final NavBarPosition mNavBarPosition; private final TaskAnimationManager mTaskAnimationManager; private final GestureState mGestureState; + private final RotationTouchHelper mRotationTouchHelper; private RecentsAnimationCallbacks mActiveCallbacks; private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher(); private final InputMonitorCompat mInputMonitorCompat; @@ -163,6 +165,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture; mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe; + mRotationTouchHelper = mDeviceState.getRotationTouchHelper(); } @Override @@ -230,7 +233,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC if (!mPassedPilferInputSlop) { // Cancel interaction in case of multi-touch interaction int ptrIdx = ev.getActionIndex(); - if (!mDeviceState.isInSwipeUpTouchRegion(ev, ptrIdx)) { + if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx)) { forceCancelGesture(ev); } } @@ -424,7 +427,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC @Override public void notifyOrientationSetup() { - mDeviceState.onStartGesture(); + mRotationTouchHelper.onStartGesture(); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/OverviewToHomeAnim.java new file mode 100644 index 0000000000..1031c5b080 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/OverviewToHomeAnim.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.anim.Interpolators.DEACCEL; +import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; +import static com.android.launcher3.anim.Interpolators.INSTANT; +import static com.android.launcher3.anim.Interpolators.clampToProgress; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y; +import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.util.Log; +import android.view.animation.Interpolator; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.statemanager.StateManager; +import com.android.launcher3.states.StateAnimationConfig; +import com.android.quickstep.views.RecentsView; + +/** + * Runs an animation from overview to home. Currently, this animation is just a wrapper around the + * normal state transition, in order to keep RecentsView at the same scale and translationY that + * it started out at as it translates offscreen. It also scrolls RecentsView to page 0 and may play + * a {@link StaggeredWorkspaceAnim} if we're starting from an upward fling. + */ +public class OverviewToHomeAnim { + + private static final String TAG = "OverviewToHomeAnim"; + + // Constants to specify how to scroll RecentsView to the default page if it's not already there. + private static final int DEFAULT_PAGE = 0; + private static final int PER_PAGE_SCROLL_DURATION = 150; + private static final int MAX_PAGE_SCROLL_DURATION = 750; + + private final Launcher mLauncher; + private final Runnable mOnReachedHome; + + // Only run mOnReachedHome when both of these are true. + private boolean mIsHomeStaggeredAnimFinished; + private boolean mIsOverviewHidden; + + public OverviewToHomeAnim(Launcher launcher, Runnable onReachedHome) { + mLauncher = launcher; + mOnReachedHome = onReachedHome; + } + + /** + * Starts the animation. If velocity < 0 (i.e. upwards), also plays a + * {@link StaggeredWorkspaceAnim}. + */ + public void animateWithVelocity(float velocity) { + StateManager stateManager = mLauncher.getStateManager(); + LauncherState startState = stateManager.getState(); + if (startState != OVERVIEW) { + Log.e(TAG, "animateFromOverviewToHome: unexpected start state " + startState); + } + AnimatorSet anim = new AnimatorSet(); + + boolean playStaggeredWorkspaceAnim = velocity < 0; + if (playStaggeredWorkspaceAnim) { + StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim( + mLauncher, velocity, false /* animateOverviewScrim */); + staggeredWorkspaceAnim.addAnimatorListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + mIsHomeStaggeredAnimFinished = true; + maybeOverviewToHomeAnimComplete(); + } + }); + anim.play(staggeredWorkspaceAnim.getAnimators()); + } else { + mIsHomeStaggeredAnimFinished = true; + } + + RecentsView recentsView = mLauncher.getOverviewPanel(); + int numPagesToScroll = recentsView.getNextPage() - DEFAULT_PAGE; + int scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION, + numPagesToScroll * PER_PAGE_SCROLL_DURATION); + int duration = Math.max(scrollDuration, startState.getTransitionDuration(mLauncher)); + + StateAnimationConfig config = new UseFirstInterpolatorStateAnimConfig(); + config.duration = duration; + config.animFlags = playStaggeredWorkspaceAnim + // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here. + ? PLAY_ATOMIC_OVERVIEW_PEEK + : ANIM_ALL_COMPONENTS; + boolean isLayoutNaturalToLauncher = recentsView.getPagedOrientationHandler() + .isLayoutNaturalToLauncher(); + config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, isLayoutNaturalToLauncher + ? clampToProgress(FAST_OUT_SLOW_IN, 0, 0.75f) : FINAL_FRAME); + config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME); + config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME); + config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, INSTANT); + if (!isLayoutNaturalToLauncher) { + config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL); + } + AnimatorSet stateAnim = stateManager.createAtomicAnimation( + startState, NORMAL, config); + stateAnim.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + mIsOverviewHidden = true; + maybeOverviewToHomeAnimComplete(); + } + }); + anim.play(stateAnim); + stateManager.setCurrentAnimation(anim, NORMAL); + anim.start(); + recentsView.snapToPage(DEFAULT_PAGE, duration); + } + + private void maybeOverviewToHomeAnimComplete() { + if (mIsHomeStaggeredAnimFinished && mIsOverviewHidden) { + mOnReachedHome.run(); + } + } + + /** + * Wrapper around StateAnimationConfig that doesn't allow interpolators to be set if they are + * already set. This ensures they aren't overridden before being used. + */ + private static class UseFirstInterpolatorStateAnimConfig extends StateAnimationConfig { + @Override + public void setInterpolator(int animId, Interpolator interpolator) { + if (mInterpolators[animId] == null || interpolator == null) { + super.setInterpolator(animId, interpolator); + } + } + } +} 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 3cafd423ca..41203319b1 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 @@ -205,13 +205,15 @@ public class StaggeredWorkspaceAnim { ResourceProvider rp = DynamicResource.provider(v.getContext()); float stiffness = rp.getFloat(R.dimen.staggered_stiffness); float damping = rp.getFloat(R.dimen.staggered_damping_ratio); + float endTransY = 0; + float springVelocity = Math.abs(mVelocity) * Math.signum(endTransY - mSpringTransY); ValueAnimator springTransY = new SpringAnimationBuilder(v.getContext()) .setStiffness(stiffness) .setDampingRatio(damping) .setMinimumVisibleChange(1f) .setStartValue(mSpringTransY) - .setEndValue(0) - .setStartVelocity(mVelocity) + .setEndValue(endTransY) + .setStartVelocity(springVelocity) .build(v, VIEW_TRANSLATE_Y); springTransY.setStartDelay(startDelay); springTransY.addListener(new AnimatorListenerAdapter() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java index c9ed498af1..c0601456fc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java @@ -31,7 +31,6 @@ import android.graphics.RectF; import android.util.IntProperty; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.touch.PagedOrientationHandler; @@ -91,8 +90,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { // RecentsView properties public final AnimatedFloat recentsViewScale = new AnimatedFloat(); public final AnimatedFloat fullScreenProgress = new AnimatedFloat(); + public final AnimatedFloat recentsViewSecondaryTranslation = new AnimatedFloat(); private final ScrollState mScrollState = new ScrollState(); - private final int mPageSpacing; // Cached calculations private boolean mLayoutValid = false; @@ -106,7 +105,6 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mOrientationState.setGestureActive(true); mCurrentFullscreenParams = new FullscreenDrawParams(context); - mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing); } /** @@ -129,8 +127,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { /** * @see com.android.quickstep.views.RecentsView#onConfigurationChanged(Configuration) */ - public void setRecentsConfiguration(Configuration configuration) { - mOrientationState.setActivityConfiguration(configuration); + public void setRecentsRotation(int recentsRotation) { + mOrientationState.setRecentsRotation(recentsRotation); mLayoutValid = false; } @@ -252,7 +250,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { int start = mOrientationState.getOrientationHandler() .getPrimaryValue(mTaskRect.left, mTaskRect.top); mScrollState.screenCenter = start + mScrollState.scroll + mScrollState.halfPageSize; - mScrollState.updateInterpolation(start, mPageSpacing); + mScrollState.updateInterpolation(start); mCurveScale = TaskView.getCurveScaleForInterpolation(mScrollState.linearInterpolation); } @@ -276,8 +274,10 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mOrientationState.getOrientationHandler().set( mMatrix, MATRIX_POST_TRANSLATE, mScrollState.scroll); - // Apply recensView matrix + // Apply RecentsView matrix mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y); + mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE, + recentsViewSecondaryTranslation.value); applyWindowToHomeRotation(mMatrix); // Crop rect is the inverse of thumbnail matrix diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java index 0979c071bb..c06dd9c0bd 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java @@ -16,6 +16,7 @@ package com.android.quickstep.views; import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; @@ -42,8 +43,10 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.util.Themes; @@ -51,6 +54,7 @@ import com.android.quickstep.util.MultiValueUpdateListener; /** * View used to educate the user on how to access All Apps when in No Nav Button navigation mode. + * Consumes all touches until after the animation is completed and the view is removed. */ public class AllAppsEduView extends AbstractFloatingView { @@ -110,9 +114,19 @@ public class AllAppsEduView extends AbstractFloatingView { return (type & TYPE_ALL_APPS_EDU) != 0; } + @Override + public boolean onBackPressed() { + return true; + } + + @Override + public boolean canInterceptEventsInSystemGestureRegion() { + return true; + } + @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { - return mAnimation != null && mAnimation.isRunning(); + return true; } private void playAnimation() { @@ -139,7 +153,12 @@ public class AllAppsEduView extends AbstractFloatingView { config.userControlled = false; AnimatorPlaybackController stateAnimationController = mLauncher.getStateManager().createAnimationToNewWorkspace(ALL_APPS, config); - float maxAllAppsProgress = 0.15f; + float maxAllAppsProgress = mLauncher.getDeviceProfile().isLandscape ? 0.35f : 0.15f; + + AllAppsTransitionController allAppsController = mLauncher.getAllAppsController(); + PendingAnimation allAppsAlpha = new PendingAnimation(config.duration); + allAppsController.setAlphas(ALL_APPS, config, allAppsAlpha); + mAnimation.play(allAppsAlpha.buildAnim()); ValueAnimator intro = ValueAnimator.ofFloat(0, 1f); intro.setInterpolator(LINEAR); @@ -191,7 +210,8 @@ public class AllAppsEduView extends AbstractFloatingView { @Override public void onAnimationEnd(Animator animation) { mAnimation = null; - stateAnimationController.dispatchOnCancel(); + // Handles cancelling the animation used to hint towards All Apps. + mLauncher.getStateManager().goToState(NORMAL, false); handleClose(false); } }); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index 846b94463f..b934c29d2f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -51,6 +51,7 @@ import com.android.launcher3.util.TraceHelper; import com.android.launcher3.views.ScrimView; import com.android.quickstep.LauncherActivityInterface; import com.android.quickstep.SysUINavigationMode; +import com.android.quickstep.util.OverviewToHomeAnim; import com.android.quickstep.util.TransformParams; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.RecentsExtraCard; @@ -105,12 +106,14 @@ public class LauncherRecentsView extends RecentsView @Override public void startHome() { + Runnable onReachedHome = () -> mActivity.getStateManager().goToState(NORMAL, false); + OverviewToHomeAnim overviewToHomeAnim = new OverviewToHomeAnim(mActivity, onReachedHome); if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { switchToScreenshot(null, () -> finishRecentsAnimation(true /* toRecents */, - () -> mActivity.getStateManager().goToState(NORMAL))); + () -> overviewToHomeAnim.animateWithVelocity(0))); } else { - mActivity.getStateManager().goToState(NORMAL); + overviewToHomeAnim.animateWithVelocity(0); } } @@ -189,7 +192,7 @@ public class LauncherRecentsView extends RecentsView @Override public boolean shouldUseMultiWindowTaskSizeStrategy() { - return TraceHelper.whitelistIpcs("isInMultiWindowMode", mActivity::isInMultiWindowMode); + return TraceHelper.allowIpcs("isInMultiWindowMode", mActivity::isInMultiWindowMode); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java index a2da39855c..1bf2fbf81f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java @@ -70,12 +70,14 @@ public class OverviewActionsView extends FrameLayo @IntDef(flag = true, value = { DISABLED_SCROLLING, - DISABLED_ROTATED}) + DISABLED_ROTATED, + DISABLED_NO_THUMBNAIL}) @Retention(RetentionPolicy.SOURCE) public @interface ActionsDisabledFlags { } public static final int DISABLED_SCROLLING = 1 << 0; public static final int DISABLED_ROTATED = 1 << 1; + public static final int DISABLED_NO_THUMBNAIL = 1 << 2; private static final int INDEX_CONTENT_ALPHA = 0; private static final int INDEX_VISIBILITY_ALPHA = 1; @@ -103,6 +105,7 @@ public class OverviewActionsView extends FrameLayo public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr, 0); mMultiValueAlpha = new MultiValueAlpha(this, 4); + mMultiValueAlpha.setUpdateVisibility(true); } @Override @@ -166,7 +169,6 @@ public class OverviewActionsView extends FrameLayo } boolean isHidden = mHiddenFlags != 0; mMultiValueAlpha.getProperty(INDEX_HIDDEN_FLAGS_ALPHA).setValue(isHidden ? 0 : 1); - setVisibility(isHidden ? INVISIBLE : VISIBLE); } /** 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 7b24b03f2b..0130cae47b 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 @@ -22,9 +22,9 @@ import static android.view.View.MeasureSpec.makeMeasureSpec; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherState.BACKGROUND_APP; +import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.mapToRange; import static com.android.launcher3.Utilities.squaredHypot; @@ -55,15 +55,14 @@ import android.animation.LayoutTransition.TransitionListener; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.TargetApi; -import android.app.ActivityManager; -import android.content.ComponentName; +import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; -import android.content.Intent; import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Build; @@ -94,6 +93,7 @@ import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.LauncherState; import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -116,6 +116,7 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.DynamicResource; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.OverScroller; +import com.android.launcher3.util.ResourceBasedOverride.Overrides; import com.android.launcher3.util.Themes; import com.android.launcher3.util.ViewPool; import com.android.quickstep.BaseActivityInterface; @@ -124,6 +125,7 @@ import com.android.quickstep.RecentsAnimationTargets; import com.android.quickstep.RecentsModel; import com.android.quickstep.RecentsModel.TaskVisualsChangeListener; import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.TaskOverlayFactory; import com.android.quickstep.TaskThumbnailCache; import com.android.quickstep.TaskUtils; import com.android.quickstep.ViewUtils; @@ -135,6 +137,7 @@ import com.android.quickstep.util.TransformParams; import com.android.systemui.plugins.ResourceProvider; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.LauncherEventUtil; @@ -147,7 +150,7 @@ import java.util.function.Consumer; /** * A list of recent tasks. */ -@TargetApi(Build.VERSION_CODES.P) +@TargetApi(Build.VERSION_CODES.R) public abstract class RecentsView extends PagedView implements Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback, InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener, @@ -210,6 +213,37 @@ public abstract class RecentsView extends PagedView } }; + public static final FloatProperty TASK_SECONDARY_TRANSLATION = + new FloatProperty("taskSecondaryTranslation") { + @Override + public void setValue(RecentsView recentsView, float v) { + recentsView.setTaskViewsSecondaryTranslation(v); + } + + @Override + public Float get(RecentsView recentsView) { + return recentsView.mTaskViewsSecondaryTranslation; + } + }; + + /** Same as normal SCALE_PROPERTY, but also updates page offsets that depend on this scale. */ + public static final FloatProperty RECENTS_SCALE_PROPERTY = + new FloatProperty("recentsScale") { + @Override + public void setValue(RecentsView view, float scale) { + view.setScaleX(scale); + view.setScaleY(scale); + view.mLastComputedTaskPushOutDistance = null; + view.updatePageOffsets(); + view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation); + } + + @Override + public Float get(RecentsView view) { + return view.getScaleX(); + } + }; + protected RecentsOrientedState mOrientationState; protected final BaseActivityInterface mSizeStrategy; protected RecentsAnimationController mRecentsAnimationController; @@ -217,8 +251,12 @@ public abstract class RecentsView extends PagedView protected SurfaceTransactionApplier mSyncTransactionApplier; protected int mTaskWidth; protected int mTaskHeight; + protected final Rect mLastComputedTaskSize = new Rect(); + // How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot. + protected Float mLastComputedTaskPushOutDistance = null; protected boolean mEnableDrawingLiveTile = false; protected final Rect mTempRect = new Rect(); + protected final RectF mTempRectF = new RectF(); private final PointF mTempPointF = new PointF(); private static final int DISMISS_TASK_DURATION = 300; @@ -242,12 +280,15 @@ public abstract class RecentsView extends PagedView private final ViewPool mTaskViewPool; + private final TaskOverlayFactory mTaskOverlayFactory; + private boolean mDwbToastShown; protected boolean mDisallowScrollToClearAll; private boolean mOverlayEnabled; protected boolean mFreezeViewVisibility; private float mAdjacentPageOffset = 0; + private float mTaskViewsSecondaryTranslation = 0; /** * TODO: Call reloadIdNeeded in onTaskStackChanged. @@ -377,7 +418,7 @@ public abstract class RecentsView extends PagedView mOrientationState.setMultiWindowMode(inMultiWindowMode); setLayoutRotation(mOrientationState.getTouchRotation(), mOrientationState.getDisplayRotation()); - rotateAllChildTasks(); + updateChildTaskOrientations(); } if (!inMultiWindowMode && mOverviewStateEnabled) { // TODO: Re-enable layout transitions for addition of the unpinned task @@ -394,7 +435,7 @@ public abstract class RecentsView extends PagedView mActivity = BaseActivity.fromContext(context); mOrientationState = new RecentsOrientedState( context, mSizeStrategy, this::animateRecentsRotationInPlace); - mOrientationState.setActivityConfiguration(context.getResources().getConfiguration()); + mOrientationState.setRecentsRotation(mActivity.getDisplay().getRotation()); mFastFlingVelocity = getResources() .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity); @@ -428,6 +469,11 @@ public abstract class RecentsView extends PagedView updateEmptyMessage(); mOrientationHandler = mOrientationState.getOrientationHandler(); + mTaskOverlayFactory = Overrides.getObject( + TaskOverlayFactory.class, + context.getApplicationContext(), + R.string.task_overlay_factory_class); + // Initialize quickstep specific cache params here, as this is constructed only once mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5); } @@ -521,6 +567,7 @@ public abstract class RecentsView extends PagedView mIPinnedStackAnimationListener); mOrientationState.initListeners(); SplitScreenBounds.INSTANCE.addOnChangeListener(this); + mTaskOverlayFactory.initListeners(); } @Override @@ -537,6 +584,7 @@ public abstract class RecentsView extends PagedView SplitScreenBounds.INSTANCE.removeOnChangeListener(this); mIPinnedStackAnimationListener.setActivity(null); mOrientationState.destroyListeners(); + mTaskOverlayFactory.removeListeners(); } @Override @@ -649,6 +697,13 @@ public abstract class RecentsView extends PagedView @Override public boolean onTouchEvent(MotionEvent ev) { super.onTouchEvent(ev); + + TaskView taskView = getCurrentPageTaskView(); + if (taskView != null && taskView.offerTouchToChildren(ev)) { + // Keep consuming events to pass to delegate + return true; + } + final int x = (int) ev.getX(); final int y = (int) ev.getY(); switch (ev.getAction()) { @@ -865,6 +920,7 @@ public abstract class RecentsView extends PagedView public void getTaskSize(Rect outRect) { mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect, mOrientationHandler); + mLastComputedTaskSize.set(outRect); } /** Gets the task size for modal state. */ @@ -906,8 +962,8 @@ public abstract class RecentsView extends PagedView final int pageCount = getPageCount(); for (int i = 0; i < pageCount; i++) { View page = getPageAt(i); - mScrollState.updateInterpolation(mOrientationHandler.getChildStartWithTranslation(page), - mPageSpacing); + mScrollState.updateInterpolation( + mOrientationHandler.getChildStartWithTranslation(page)); ((PageCallbacks) page).onPageScroll(mScrollState); } } @@ -1041,13 +1097,13 @@ public abstract class RecentsView extends PagedView /** * Called when a gesture from an app is starting. */ - public void onGestureAnimationStart(int runningTaskId) { + public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) { // This needs to be called before the other states are set since it can create the task view if (mOrientationState.setGestureActive(true)) { updateOrientationHandler(); } - showCurrentTask(runningTaskId); + showCurrentTask(runningTaskInfo); setEnableFreeScroll(false); setEnableDrawingLiveTile(false); setRunningTaskHidden(true); @@ -1078,7 +1134,7 @@ public abstract class RecentsView extends PagedView pa.addListener(AnimationSuccessListener.forRunnable(() -> { setLayoutRotation(newRotation, mOrientationState.getDisplayRotation()); mActivity.getDragLayer().recreateControllers(); - rotateAllChildTasks(); + updateChildTaskOrientations(); setRecentsChangedOrientation(false).start(); })); pa.start(); @@ -1099,7 +1155,7 @@ public abstract class RecentsView extends PagedView } - private void rotateAllChildTasks() { + private void updateChildTaskOrientations() { for (int i = 0; i < getTaskViewCount(); i++) { getTaskViewAt(i).setOrientationState(mOrientationState); } @@ -1127,8 +1183,8 @@ public abstract class RecentsView extends PagedView /** * Returns true if we should add a dummy taskView for the running task id */ - protected boolean shouldAddDummyTaskView(int runningTaskId) { - return getTaskView(runningTaskId) == null; + protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) { + return runningTaskInfo != null && getTaskView(runningTaskInfo.taskId) == null; } /** @@ -1137,8 +1193,8 @@ public abstract class RecentsView extends PagedView * All subsequent calls to reload will keep the task as the first item until {@link #reset()} * is called. Also scrolls the view to this task. */ - public void showCurrentTask(int runningTaskId) { - if (shouldAddDummyTaskView(runningTaskId)) { + public void showCurrentTask(RunningTaskInfo runningTaskInfo) { + if (shouldAddDummyTaskView(runningTaskInfo)) { boolean wasEmpty = getChildCount() == 0; // Add an empty view for now until the task plan is loaded and applied final TaskView taskView = mTaskViewPool.getView(); @@ -1148,10 +1204,7 @@ public abstract class RecentsView extends PagedView } // 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(), - new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0, - false, true, false, false, new ActivityManager.TaskDescription(), 0, - new ComponentName("", ""), false); + mTmpRunningTask = Task.from(new TaskKey(runningTaskInfo), runningTaskInfo, false); taskView.bind(mTmpRunningTask, mOrientationState); // Measure and layout immediately so that the scroll values is updated instantly @@ -1162,7 +1215,7 @@ public abstract class RecentsView extends PagedView } boolean runningTaskTileHidden = mRunningTaskTileHidden; - setCurrentTask(runningTaskId); + setCurrentTask(runningTaskInfo == null ? -1 : runningTaskInfo.taskId); setCurrentPage(getRunningTaskIndex()); setRunningTaskViewShowScreenshot(false); setRunningTaskHidden(runningTaskTileHidden); @@ -1332,10 +1385,13 @@ public abstract class RecentsView extends PagedView /** * Updates linearInterpolation for the provided child position */ - public void updateInterpolation(float childStart, int pageSpacing) { + public void updateInterpolation(float childStart) { float pageCenter = childStart + halfPageSize; float distanceFromScreenCenter = screenCenter - pageCenter; - float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing; + // How far the page has to move from the center to be offscreen, taking into account + // the EDGE_SCALE_DOWN_FACTOR that will be applied at that position. + float distanceToReachEdge = halfScreenSize + + halfPageSize * (1 - TaskView.EDGE_SCALE_DOWN_FACTOR); linearInterpolation = Math.min(1, Math.abs(distanceFromScreenCenter) / distanceToReachEdge); } @@ -1358,7 +1414,7 @@ public abstract class RecentsView extends PagedView FloatProperty secondaryViewTranslate = mOrientationHandler.getSecondaryViewTranslate(); int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView); - int verticalFactor = mOrientationHandler.getTaskDismissDirectionFactor(); + int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor(); ResourceProvider rp = DynamicResource.provider(mActivity); SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_START) @@ -1652,15 +1708,24 @@ public abstract class RecentsView extends PagedView super.setVisibility(visibility); if (mActionsView != null) { mActionsView.updateHiddenFlags(HIDDEN_NO_RECENTS, visibility != VISIBLE); + if (visibility != VISIBLE) { + mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false); + } } } @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - if (mOrientationState.setActivityConfiguration(newConfig)) { + if (mOrientationState.setRecentsRotation(mActivity.getDisplay().getRotation())) { updateOrientationHandler(); } + // If overview is in modal state when rotate, reset it to overview state without running + // animation. + if (mActivity.isInState(OVERVIEW_MODAL_TASK)) { + mActivity.getStateManager().goToState(LauncherState.OVERVIEW, false); + resetModalVisuals(); + } } public void setLayoutRotation(int touchRotation, int displayRotation) { @@ -1680,10 +1745,11 @@ public abstract class RecentsView extends PagedView : View.LAYOUT_DIRECTION_RTL); mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated()); mActivity.getDragLayer().recreateControllers(); - boolean isInLandscape = mOrientationState.getTouchRotation() != 0 + boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0 || mOrientationState.getRecentsActivityRotation() != ROTATION_0; mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, !mOrientationState.canRecentsActivityRotate() && isInLandscape); + updateChildTaskOrientations(); resetPaddingFromTaskSize(); requestLayout(); // Reapply the current page to update page scrolls. @@ -1769,14 +1835,15 @@ public abstract class RecentsView extends PagedView setPivotX(mTempPointF.x); setPivotY(mTempPointF.y); setTaskModalness(mTaskModalness); + mLastComputedTaskPushOutDistance = null; updatePageOffsets(); setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO : IMPORTANT_FOR_ACCESSIBILITY_AUTO); } private void updatePageOffsets() { - float offset = mAdjacentPageOffset * getWidth(); - float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness) * getWidth(); + float offset = mAdjacentPageOffset; + float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness); if (mIsRtl) { offset = -offset; modalOffset = -modalOffset; @@ -1785,18 +1852,99 @@ public abstract class RecentsView extends PagedView TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden ? null : getTaskView(mRunningTaskId); - int midPoint = runningTask == null ? -1 : indexOfChild(runningTask); - int currentPage = getCurrentPage(); + int midpoint = runningTask == null ? -1 : indexOfChild(runningTask); + int modalMidpoint = getCurrentPage(); + + float midpointOffsetSize = 0; + float leftOffsetSize = midpoint - 1 >= 0 + ? -getOffsetSize(midpoint - 1, midpoint, offset) + : 0; + float rightOffsetSize = midpoint + 1 < count + ? getOffsetSize(midpoint + 1, midpoint, offset) + : 0; + + float modalMidpointOffsetSize = 0; + float modalLeftOffsetSize = modalMidpoint - 1 >= 0 + ? -getOffsetSize(modalMidpoint - 1, modalMidpoint, modalOffset) + : 0; + float modalRightOffsetSize = modalMidpoint + 1 < count + ? getOffsetSize(modalMidpoint + 1, modalMidpoint, modalOffset) + : 0; for (int i = 0; i < count; i++) { - float translation = i == midPoint ? 0 : (i < midPoint ? -offset : offset); - float modalTranslation = - i == currentPage ? 0 : (i < currentPage ? -modalOffset : modalOffset); - getChildAt(i).setTranslationX(translation + modalTranslation); + float translation = i == midpoint + ? midpointOffsetSize + : i < midpoint + ? leftOffsetSize + : rightOffsetSize; + float modalTranslation = i == modalMidpoint + ? modalMidpointOffsetSize + : i < modalMidpoint + ? modalLeftOffsetSize + : modalRightOffsetSize; + float totalTranslation = translation + modalTranslation; + mOrientationHandler.getPrimaryViewTranslate().set(getChildAt(i), + totalTranslation * mOrientationHandler.getPrimaryTranslationDirectionFactor()); } updateCurveProperties(); } + /** + * Computes the distance to offset the given child such that it is completely offscreen when + * translating away from the given midpoint. + * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen. + */ + private float getOffsetSize(int childIndex, int midpointIndex, float offsetProgress) { + if (offsetProgress == 0) { + // Don't bother calculating everything below if we won't offset anyway. + return 0; + } + // First, get the position of the task relative to the midpoint. If there is no midpoint + // then we just use the normal (centered) task position. + mTempRectF.set(mLastComputedTaskSize); + RectF taskPosition = mTempRectF; + float desiredLeft = getWidth(); + float distanceToOffscreen = desiredLeft - taskPosition.left; + // Used to calculate the scale of the task view based on its new offset. + float centerToOffscreenProgress = Math.abs(offsetProgress); + if (midpointIndex > -1) { + // When there is a midpoint reference task, adjacent tasks have less distance to travel + // to reach offscreen. Offset the task position to the task's starting point. + View child = getChildAt(childIndex); + View midpointChild = getChildAt(midpointIndex); + int distanceFromMidpoint = Math.abs(mOrientationHandler.getChildStart(child) + - mOrientationHandler.getChildStart(midpointChild) + + getDisplacementFromScreenCenter(midpointIndex)); + taskPosition.offset(distanceFromMidpoint, 0); + centerToOffscreenProgress = Utilities.mapRange(centerToOffscreenProgress, + distanceFromMidpoint / distanceToOffscreen, 1); + } + // Find the task's scale based on its offscreen progress, then see how far it still needs to + // move to be completely offscreen. + Utilities.scaleRectFAboutCenter(taskPosition, + TaskView.getCurveScaleForInterpolation(centerToOffscreenProgress)); + distanceToOffscreen = desiredLeft - taskPosition.left; + // Finally, we need to account for RecentsView scale, because it moves tasks based on its + // pivot. To do this, we move the task position to where it would be offscreen at scale = 1 + // (computed above), then we apply the scale via getMatrix() to determine how much that + // moves the task from its desired position, and adjust the computed distance accordingly. + if (mLastComputedTaskPushOutDistance == null) { + taskPosition.offsetTo(desiredLeft, 0); + getMatrix().mapRect(taskPosition); + mLastComputedTaskPushOutDistance = (taskPosition.left - desiredLeft) / getScaleX(); + } + distanceToOffscreen -= mLastComputedTaskPushOutDistance; + return distanceToOffscreen * offsetProgress; + } + + private void setTaskViewsSecondaryTranslation(float translation) { + mTaskViewsSecondaryTranslation = translation; + for (int i = 0; i < getTaskViewCount(); i++) { + TaskView task = getTaskViewAt(i); + mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY()); + } + } + /** * TODO: Do not assume motion across X axis for adjacent page */ @@ -1895,7 +2043,7 @@ public abstract class RecentsView extends PagedView float toScale = getMaxScaleForFullScreen(); if (launchingCenterTask) { RecentsView recentsView = tv.getRecentsView(); - anim.play(ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, toScale)); + anim.play(ObjectAnimator.ofFloat(recentsView, RECENTS_SCALE_PROPERTY, toScale)); anim.play(ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS, 1)); } else { // We are launching an adjacent task, so parallax the center and other adjacent task. @@ -2181,7 +2329,14 @@ public abstract class RecentsView extends PagedView if (pageIndex == -1) { return 0; } - return getScrollForPage(pageIndex) - mOrientationHandler.getPrimaryScroll(this); + // Unbound the scroll (due to overscroll) if the adjacent tasks are offset away from it. + // This allows the page to move freely, given there's no visual indication why it shouldn't. + int boundedScroll = mOrientationHandler.getPrimaryScroll(this); + int unboundedScroll = getUnboundedScroll(); + float unboundedProgress = mAdjacentPageOffset; + int scroll = Math.round(unboundedScroll * unboundedProgress + + boundedScroll * (1 - unboundedProgress)); + return getScrollForPage(pageIndex) - scroll; } public Consumer getEventDispatcher(float navbarRotation) { @@ -2283,6 +2438,10 @@ public abstract class RecentsView extends PagedView */ public void setModalStateEnabled(boolean isModalState) { } + public TaskOverlayFactory getTaskOverlayFactory() { + return mTaskOverlayFactory; + } + public BaseActivityInterface getSizeStrategy() { return mSizeStrategy; } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java index ef66b7a396..fb799a800c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java @@ -169,7 +169,9 @@ public class TaskMenuView extends AbstractFloatingView { } if (mIsOpen) { mOptionLayout.removeAllViews(); - populateAndLayoutMenu(); + if (!populateAndLayoutMenu()) { + close(false); + } } } @@ -186,14 +188,22 @@ public class TaskMenuView extends AbstractFloatingView { } mActivity.getDragLayer().addView(this); mTaskView = taskView; - populateAndLayoutMenu(); + if (!populateAndLayoutMenu()) { + return false; + } post(this::animateOpen); return true; } - private void populateAndLayoutMenu() { + /** @return true if successfully able to populate task view menu, false otherwise */ + private boolean populateAndLayoutMenu() { + if (mTaskView.getTask().icon == null) { + // Icon may not be loaded + return false; + } addMenuOptions(mTaskView); orientAroundTaskView(mTaskView); + return true; } private void addMenuOptions(TaskView taskView) { @@ -240,8 +250,10 @@ public class TaskMenuView extends AbstractFloatingView { setLayoutParams(params); setScaleX(taskView.getScaleX()); setScaleY(taskView.getScaleY()); + boolean canActivityRotate = taskView.getRecentsView() + .mOrientationState.isRecentsActivityRotationAllowed(); mOptionLayout.setOrientation(orientationHandler - .getTaskMenuLayoutOrientation(mOptionLayout)); + .getTaskMenuLayoutOrientation(canActivityRotate, mOptionLayout)); setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top, taskView.getPagedOrientationHandler()); } 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 b2f937f8c2..607672a751 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 @@ -52,14 +52,12 @@ import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; -import com.android.quickstep.TaskOverlayFactory; import com.android.quickstep.TaskOverlayFactory.TaskOverlay; import com.android.quickstep.views.TaskView.FullscreenDrawParams; import com.android.systemui.plugins.OverviewScreenshotActions; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; -import com.android.systemui.shared.system.ConfigurationCompat; /** * A task in the Recents view. @@ -86,7 +84,7 @@ public class TaskThumbnailView extends View implements PluginListener SYSTEM_GESTURE_EXCLUSION_RECT = Collections.singletonList(new Rect()); @@ -186,6 +200,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private int mStackHeight; private View mContextualChipWrapper; private View mContextualChip; + private final float[] mIconCenterCoords = new float[2]; + private final float[] mChipCenterCoords = new float[2]; public TaskView(Context context) { this(context, null); @@ -246,6 +262,54 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { super.onFinishInflate(); mSnapshotView = findViewById(R.id.snapshot); mIconView = findViewById(R.id.icon); + mIconTouchDelegate = new TransformingTouchDelegate(mIconView); + } + + /** + * Whether the taskview should take the touch event from parent. Events passed to children + * that might require special handling. + */ + public boolean offerTouchToChildren(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + computeAndSetIconTouchDelegate(); + computeAndSetChipTouchDelegate(); + } + if (mIconTouchDelegate != null && mIconTouchDelegate.onTouchEvent(event)) { + return true; + } + if (mChipTouchDelegate != null && mChipTouchDelegate.onTouchEvent(event)) { + return true; + } + return false; + } + + private void computeAndSetIconTouchDelegate() { + float iconHalfSize = mIconView.getWidth() / 2f; + mIconCenterCoords[0] = mIconCenterCoords[1] = iconHalfSize; + getDescendantCoordRelativeToAncestor(mIconView, mActivity.getDragLayer(), mIconCenterCoords, + false); + mIconTouchDelegate.setBounds( + (int) (mIconCenterCoords[0] - iconHalfSize), + (int) (mIconCenterCoords[1] - iconHalfSize), + (int) (mIconCenterCoords[0] + iconHalfSize), + (int) (mIconCenterCoords[1] + iconHalfSize)); + } + + private void computeAndSetChipTouchDelegate() { + if (mContextualChipWrapper != null) { + float chipHalfWidth = mContextualChipWrapper.getWidth() / 2f; + float chipHalfHeight = mContextualChipWrapper.getHeight() / 2f; + mChipCenterCoords[0] = chipHalfWidth; + mChipCenterCoords[1] = chipHalfHeight; + getDescendantCoordRelativeToAncestor(mContextualChipWrapper, mActivity.getDragLayer(), + mChipCenterCoords, + false); + mChipTouchDelegate.setBounds( + (int) (mChipCenterCoords[0] - chipHalfWidth), + (int) (mChipCenterCoords[1] - chipHalfHeight), + (int) (mChipCenterCoords[0] + chipHalfWidth), + (int) (mChipCenterCoords[1] + chipHalfHeight)); + } } /** @@ -255,6 +319,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { * @param modalness [0, 1] 0 being in context with other tasks, 1 being shown on its own. */ public void setModalness(float modalness) { + if (mModalness == modalness) { + return; + } mModalness = modalness; mIconView.setAlpha(comp(modalness)); if (mContextualChip != null) { @@ -264,7 +331,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { if (mContextualChipWrapper != null) { mContextualChipWrapper.setAlpha(comp(modalness)); } - updateFooterVerticalOffset(mFooterVerticalOffset); } @@ -468,18 +534,18 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin); LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams(); switch (orientationHandler.getRotation()) { - case Surface.ROTATION_90: + case ROTATION_90: iconParams.gravity = (isRtl ? START : END) | CENTER_VERTICAL; iconParams.rightMargin = -thumbnailPadding; iconParams.leftMargin = 0; iconParams.topMargin = snapshotParams.topMargin / 2; break; - case Surface.ROTATION_180: + case ROTATION_180: iconParams.gravity = BOTTOM | CENTER_HORIZONTAL; iconParams.bottomMargin = -thumbnailPadding; iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = 0; break; - case Surface.ROTATION_270: + case ROTATION_270: iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL; iconParams.leftMargin = -thumbnailPadding; iconParams.rightMargin = 0; @@ -512,7 +578,11 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { .getInterpolation(progress); mIconView.setScaleX(scale); mIconView.setScaleY(scale); - + if (mContextualChip != null && mContextualChipWrapper != null) { + mContextualChipWrapper.setAlpha(scale); + mContextualChip.setScaleX(scale); + mContextualChip.setScaleY(scale); + } updateFooterVerticalOffset(1.0f - scale); } @@ -691,6 +761,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mContextualChip.animate().scaleX(1f).scaleY(1f).setDuration(50); } if (mContextualChipWrapper != null) { + mChipTouchDelegate = new TransformingTouchDelegate(mContextualChipWrapper); mContextualChipWrapper.animate().alpha(1f).setDuration(50); } } @@ -712,6 +783,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { View oldContextualChipWrapper = mContextualChipWrapper; mContextualChipWrapper = null; mContextualChip = null; + mChipTouchDelegate = null; return oldContextualChipWrapper; } diff --git a/quickstep/res/drawable/bg_circle.xml b/quickstep/res/drawable/bg_circle.xml new file mode 100644 index 0000000000..506177b6a1 --- /dev/null +++ b/quickstep/res/drawable/bg_circle.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml index 459d65faf7..43bf0ea966 100644 --- a/quickstep/res/layout/gesture_tutorial_fragment.xml +++ b/quickstep/res/layout/gesture_tutorial_fragment.xml @@ -24,6 +24,14 @@ android:layout_height="match_parent" android:background="@drawable/gesture_tutorial_ripple"/> + + + android:padding="18dp" + android:src="@drawable/gesture_tutorial_close_button" + android:tint="?android:attr/textColorPrimary"/> + android:layout_marginEnd="@dimen/gesture_tutorial_title_margin_start_end"/> + android:layout_marginTop="10dp" + android:layout_marginEnd="@dimen/gesture_tutorial_subtitle_margin_start_end"/> + android:layout_marginBottom="10dp"/>