diff options
author | Hyunyoung Song <hyunyoungs@google.com> | 2019-12-04 00:54:30 -0800 |
---|---|---|
committer | Hyunyoung Song <hyunyoungs@google.com> | 2019-12-06 06:18:48 +0000 |
commit | 60fce03ac48b5548151b499562a341fab954e796 (patch) | |
tree | b9b14c1d16edaedef65c541f6465fc4d0b025851 | |
parent | 793f5c518656f94732be695eac4d5b01fa50ae01 (diff) | |
parent | 491bb699c841cfb9ce065230d88f00ee51675bad (diff) | |
download | android_packages_apps_Trebuchet-60fce03ac48b5548151b499562a341fab954e796.tar.gz android_packages_apps_Trebuchet-60fce03ac48b5548151b499562a341fab954e796.tar.bz2 android_packages_apps_Trebuchet-60fce03ac48b5548151b499562a341fab954e796.zip |
Merging from ub-launcher3-qt-future-dev @ build 6048032
Test: manual, presubmit on the source branch
http://x20/teams/android-launcher/merge/ub-launcher3-qt-future-dev_6048032.html
Change-Id: I74059dbc75a8530884f8b4f67917b83c75f32d14
Merged-In: Ieee38cc301364f96cf252b2387142b0aa1b75317
23 files changed, 819 insertions, 178 deletions
diff --git a/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java b/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java new file mode 100644 index 000000000..fb8901388 --- /dev/null +++ b/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import com.android.launcher3.Launcher; + +/** Empty class, only exists so that l3goWithQuickstepIconRecentsDebug compiles. */ +public class ShelfPeekAnim { + public ShelfPeekAnim(Launcher launcher) { + } + + public enum ShelfAnimState { + } + + public boolean isPeeking() { + return false; + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index cdff33bf9..114fd8e10 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -21,8 +21,15 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.LauncherStateManager.ANIM_ALL; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; +import static com.android.launcher3.anim.Interpolators.DEACCEL_3; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator; @@ -40,6 +47,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.quickstep.util.ClipAnimationHelper; @@ -56,6 +64,9 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti public static final int INDEX_SHELF_ANIM = 0; public static final int INDEX_RECENTS_FADE_ANIM = 1; public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = 2; + public static final int INDEX_PAUSE_TO_OVERVIEW_ANIM = 3; + + public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300; public LauncherAppTransitionManagerImpl(Context context) { super(context); @@ -144,7 +155,7 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti @Override public int getStateElementAnimationsCount() { - return 3; + return 4; } @Override @@ -190,6 +201,20 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti .setStiffness(250) .setValues(values) .build(mLauncher); + case INDEX_PAUSE_TO_OVERVIEW_ANIM: { + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2); + builder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3); + if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { + builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2); + builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2); + } + LauncherStateManager stateManager = mLauncher.getStateManager(); + return stateManager.createAtomicAnimation( + stateManager.getCurrentStableState(), OVERVIEW, builder, + ANIM_ALL, ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW); + } + default: return super.createStateElementAnimation(index, values); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java index 23db5df2e..f82af62aa 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -276,7 +276,7 @@ public class PredictionRowView extends LinearLayout implements boolean predictionsEnabled = predictionCount > 0; if (predictionsEnabled != mPredictionsEnabled) { mPredictionsEnabled = predictionsEnabled; - mLauncher.reapplyUi(); + mLauncher.reapplyUi(false /* cancelCurrentAnimation */); updateVisibility(); } mParent.onHeightUpdated(); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index 596bc4f44..cac170c68 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -28,17 +28,17 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.StateHandler; -import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController; import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController; import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController; +import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController; import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController; import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController; -import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController; import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController; +import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController; import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController; import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController; import com.android.launcher3.util.TouchController; @@ -145,7 +145,7 @@ public abstract class RecentsUiFactory { ArrayList<TouchController> list = new ArrayList<>(); list.add(launcher.getDragController()); if (mode == NO_BUTTON) { - list.add(new QuickSwitchTouchController(launcher)); + list.add(new NoButtonQuickSwitchTouchController(launcher)); list.add(new NavBarToHomeTouchController(launcher)); list.add(new FlingAndHoldTouchController(launcher)); } else { diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java index 25eaab187..ed5dba1fd 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -22,6 +22,7 @@ import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE; @@ -205,6 +206,7 @@ public class OverviewState extends LauncherState { builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2); builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2); builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7); + builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_7); builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java index 38a0b6673..3231f378d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java @@ -16,25 +16,20 @@ package com.android.launcher3.uioverrides.touchcontrollers; +import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_PAUSE_TO_OVERVIEW_ANIM; import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_PEEK; -import static com.android.launcher3.LauncherStateManager.ANIM_ALL; import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE; -import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE; -import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE; -import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_3; -import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; @@ -46,6 +41,7 @@ import android.view.View; import android.view.ViewConfiguration; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppTransitionManagerImpl; import com.android.launcher3.LauncherState; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; @@ -79,7 +75,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { @Override protected long getAtomicDuration() { - return 300; + return LauncherAppTransitionManagerImpl.ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW; } @Override @@ -179,15 +175,8 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { mPeekAnim.cancel(); } - AnimatorSetBuilder builder = new AnimatorSetBuilder(); - builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2); - builder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3); - if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { - builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2); - builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2); - } - AnimatorSet overviewAnim = mLauncher.getStateManager().createAtomicAnimation( - NORMAL, OVERVIEW, builder, ANIM_ALL, ATOMIC_DURATION); + Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation( + INDEX_PAUSE_TO_OVERVIEW_ANIM); overviewAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { 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 new file mode 100644 index 000000000..76374af6a --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.uioverrides.touchcontrollers; + +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_PAUSE_TO_OVERVIEW_ANIM; +import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.LauncherState.QUICK_SWITCH; +import static com.android.launcher3.LauncherStateManager.ANIM_ALL; +import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE; +import static com.android.launcher3.anim.AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW; +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; +import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT; +import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP; +import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; +import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; +import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.CANCEL; +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.FULLSCREEN_PROGRESS; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.graphics.PointF; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.Interpolator; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager; +import com.android.launcher3.LauncherStateManager.AnimationConfig; +import com.android.launcher3.QuickstepAppTransitionManagerImpl; +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.AnimatorSetBuilder; +import com.android.launcher3.graphics.OverviewScrim; +import com.android.launcher3.touch.BaseSwipeDetector; +import com.android.launcher3.touch.BothAxesSwipeDetector; +import com.android.launcher3.userevent.nano.LauncherLogProto; +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.OverviewInteractionState; +import com.android.quickstep.util.LayoutUtils; +import com.android.quickstep.util.MotionPauseDetector; +import com.android.quickstep.util.ShelfPeekAnim; +import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState; +import com.android.quickstep.util.StaggeredWorkspaceAnim; +import com.android.quickstep.views.LauncherRecentsView; + +/** + * Handles quick switching to a recent task from the home screen. To give as much flexibility to + * the user as possible, also handles swipe up and hold to go to overview and swiping back home. + */ +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 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 final Launcher mLauncher; + private final BothAxesSwipeDetector mSwipeDetector; + private final float mXRange; + private final float mYRange; + private final MotionPauseDetector mMotionPauseDetector; + private final float mMotionPauseMinDisplacement; + + private boolean mNoIntercept; + private LauncherState mStartState; + + private ShelfPeekAnim mShelfPeekAnim; + private boolean mIsHomeScreenVisible = true; + + // As we drag, we control 3 animations: one to get non-overview components out of the way, + // and the other two to set overview properties based on x and y progress. + private AnimatorPlaybackController mNonOverviewAnim; + private AnimatorPlaybackController mXOverviewAnim; + private AnimatorPlaybackController mYOverviewAnim; + + public NoButtonQuickSwitchTouchController(Launcher launcher) { + mLauncher = launcher; + mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this); + mXRange = mLauncher.getDeviceProfile().widthPx / 2f; + mYRange = LayoutUtils.getShelfTrackingDistance(mLauncher, mLauncher.getDeviceProfile()); + mMotionPauseDetector = new MotionPauseDetector(mLauncher); + mMotionPauseMinDisplacement = mLauncher.getResources().getDimension( + R.dimen.motion_pause_detector_min_displacement_from_app); + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mNoIntercept = !canInterceptTouch(ev); + if (mNoIntercept) { + return false; + } + + // Only detect horizontal swipe for intercept, then we will allow swipe up as well. + mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT, + false /* ignoreSlopWhenSettling */); + } + + if (mNoIntercept) { + return false; + } + + onControllerTouchEvent(ev); + return mSwipeDetector.isDraggingOrSettling(); + } + + @Override + public boolean onControllerTouchEvent(MotionEvent ev) { + return mSwipeDetector.onTouchEvent(ev); + } + + private boolean canInterceptTouch(MotionEvent ev) { + if (!mLauncher.isInState(LauncherState.NORMAL)) { + return false; + } + if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) { + return false; + } + int stateFlags = OverviewInteractionState.INSTANCE.get(mLauncher).getSystemUiStateFlags(); + if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) { + return false; + } + return true; + } + + @Override + public void onDragStart(boolean start) { + mMotionPauseDetector.clear(); + if (start) { + mShelfPeekAnim = ((QuickstepAppTransitionManagerImpl) mLauncher + .getAppTransitionManager()).getShelfPeekAnim(); + + mStartState = mLauncher.getStateManager().getState(); + + mMotionPauseDetector.setOnMotionPauseListener(this); + + // We have detected horizontal drag start, now allow swipe up as well. + mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT | DIRECTION_UP, + false /* ignoreSlopWhenSettling */); + + setupAnimators(); + } + } + + @Override + public void onMotionPauseChanged(boolean isPaused) { + ShelfAnimState shelfState = isPaused ? PEEK : HIDE; + if (shelfState == PEEK) { + // Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking. + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + AllAppsTransitionController allAppsController = mLauncher.getAllAppsController(); + allAppsController.setAlphas(NORMAL.getVisibleElements(mLauncher), + new AnimationConfig(), builder); + builder.build().setDuration(0).start(); + + if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { + // Hotseat was hidden, but we need it visible when peeking. + mLauncher.getHotseat().setAlpha(1); + } + } + mShelfPeekAnim.setShelfState(shelfState, ShelfPeekAnim.INTERPOLATOR, + ShelfPeekAnim.DURATION); + VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC); + } + + private void setupAnimators() { + // Animate the non-overview components (e.g. workspace, shelf) out of the way. + AnimatorSetBuilder nonOverviewBuilder = new AnimatorSetBuilder(); + nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_FADE, FADE_OUT_INTERPOLATOR); + nonOverviewBuilder.setInterpolator(ANIM_ALL_APPS_FADE, FADE_OUT_INTERPOLATOR); + nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, TRANSLATE_OUT_INTERPOLATOR); + nonOverviewBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, TRANSLATE_OUT_INTERPOLATOR); + updateNonOverviewAnim(QUICK_SWITCH, nonOverviewBuilder, ANIM_ALL); + mNonOverviewAnim.dispatchOnStart(); + + setupOverviewAnimators(); + } + + /** Create state animation to control non-overview components. */ + private void updateNonOverviewAnim(LauncherState toState, AnimatorSetBuilder builder, + @LauncherStateManager.AnimationComponents int animComponents) { + builder.addFlag(FLAG_DONT_ANIMATE_OVERVIEW); + long accuracy = (long) (Math.max(mXRange, mYRange) * 2); + mNonOverviewAnim = mLauncher.getStateManager().createAnimationToNewWorkspace(toState, + builder, accuracy, this::clearState, animComponents); + } + + private void setupOverviewAnimators() { + final LauncherState fromState = QUICK_SWITCH; + final LauncherState toState = OVERVIEW; + LauncherState.ScaleAndTranslation fromScaleAndTranslation = fromState + .getOverviewScaleAndTranslation(mLauncher); + LauncherState.ScaleAndTranslation toScaleAndTranslation = toState + .getOverviewScaleAndTranslation(mLauncher); + // Update RecentView's translationX to have it start offscreen. + LauncherRecentsView recentsView = mLauncher.getOverviewPanel(); + float startScale = Utilities.mapRange( + SCALE_DOWN_INTERPOLATOR.getInterpolation(Y_ANIM_MIN_PROGRESS), + fromScaleAndTranslation.scale, + toScaleAndTranslation.scale); + fromScaleAndTranslation.translationX = recentsView.getOffscreenTranslationX(startScale); + + // Set RecentView's initial properties. + recentsView.setScaleX(fromScaleAndTranslation.scale); + recentsView.setScaleY(fromScaleAndTranslation.scale); + recentsView.setTranslationX(fromScaleAndTranslation.translationX); + recentsView.setTranslationY(fromScaleAndTranslation.translationY); + recentsView.setContentAlpha(1); + + // As we drag right, animate the following properties: + // - RecentsView translationX + // - OverviewScrim + AnimatorSet xOverviewAnim = new AnimatorSet(); + xOverviewAnim.play(ObjectAnimator.ofFloat(recentsView, View.TRANSLATION_X, + toScaleAndTranslation.translationX)); + xOverviewAnim.play(ObjectAnimator.ofFloat( + mLauncher.getDragLayer().getOverviewScrim(), OverviewScrim.SCRIM_PROGRESS, + toState.getOverviewScrimAlpha(mLauncher))); + long xAccuracy = (long) (mXRange * 2); + xOverviewAnim.setDuration(xAccuracy); + mXOverviewAnim = AnimatorPlaybackController.wrap(xOverviewAnim, xAccuracy); + mXOverviewAnim.dispatchOnStart(); + + // As we drag up, animate the following properties: + // - RecentsView translationY + // - RecentsView scale + // - RecentsView fullscreenProgress + AnimatorSet yAnimation = new AnimatorSet(); + Animator translateYAnim = ObjectAnimator.ofFloat(recentsView, View.TRANSLATION_Y, + toScaleAndTranslation.translationY); + Animator scaleAnim = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, + toScaleAndTranslation.scale); + Animator fullscreenProgressAnim = ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS, + fromState.getOverviewFullscreenProgress(), toState.getOverviewFullscreenProgress()); + scaleAnim.setInterpolator(SCALE_DOWN_INTERPOLATOR); + fullscreenProgressAnim.setInterpolator(SCALE_DOWN_INTERPOLATOR); + yAnimation.play(translateYAnim); + yAnimation.play(scaleAnim); + yAnimation.play(fullscreenProgressAnim); + long yAccuracy = (long) (mYRange * 2); + yAnimation.setDuration(yAccuracy); + mYOverviewAnim = AnimatorPlaybackController.wrap(yAnimation, yAccuracy); + mYOverviewAnim.dispatchOnStart(); + } + + @Override + public boolean onDrag(PointF displacement, MotionEvent ev) { + float xProgress = Math.max(0, displacement.x) / mXRange; + float yProgress = Math.max(0, -displacement.y) / mYRange; + yProgress = Utilities.mapRange(yProgress, Y_ANIM_MIN_PROGRESS, 1f); + + boolean wasHomeScreenVisible = mIsHomeScreenVisible; + if (wasHomeScreenVisible && mNonOverviewAnim != null) { + mNonOverviewAnim.setPlayFraction(xProgress); + } + mIsHomeScreenVisible = FADE_OUT_INTERPOLATOR.getInterpolation(xProgress) + <= 1 - ALPHA_CUTOFF_THRESHOLD; + + if (wasHomeScreenVisible && !mIsHomeScreenVisible) { + // Get the shelf all the way offscreen so it pops up when we decide to peek it. + mShelfPeekAnim.setShelfState(HIDE, LINEAR, 0); + } + + // Only allow motion pause if the home screen is invisible, since some + // home screen elements will appear in the shelf on motion pause. + mMotionPauseDetector.setDisallowPause(mIsHomeScreenVisible + || -displacement.y < mMotionPauseMinDisplacement); + mMotionPauseDetector.addPosition(displacement.y, ev.getEventTime()); + + if (mIsHomeScreenVisible) { + // Cancel the shelf anim so it doesn't clobber mNonOverviewAnim. + mShelfPeekAnim.setShelfState(CANCEL, LINEAR, 0); + } + + if (mXOverviewAnim != null) { + mXOverviewAnim.setPlayFraction(xProgress); + } + if (mYOverviewAnim != null) { + mYOverviewAnim.setPlayFraction(yProgress); + } + return true; + } + + @Override + public void onDragEnd(PointF velocity) { + boolean horizontalFling = mSwipeDetector.isFling(velocity.x); + boolean verticalFling = mSwipeDetector.isFling(velocity.y); + boolean noFling = !horizontalFling && !verticalFling; + int logAction = noFling ? Touch.SWIPE : Touch.FLING; + if (mMotionPauseDetector.isPaused() && noFling) { + cancelAnimations(); + + Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation( + INDEX_PAUSE_TO_OVERVIEW_ANIM); + overviewAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + onAnimationToStateCompleted(OVERVIEW, logAction); + } + }); + overviewAnim.start(); + return; + } + + final LauncherState targetState; + if (horizontalFling && verticalFling) { + if (velocity.x < 0) { + // Flinging left and up or down both go back home. + targetState = NORMAL; + } else { + if (velocity.y > 0) { + // Flinging right and down goes to quick switch. + targetState = QUICK_SWITCH; + } else { + // Flinging up and right could go either home or to quick switch. + // Determine the target based on the higher velocity. + targetState = Math.abs(velocity.x) > Math.abs(velocity.y) + ? QUICK_SWITCH : NORMAL; + } + } + } else if (horizontalFling) { + targetState = velocity.x > 0 ? QUICK_SWITCH : NORMAL; + } 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. + boolean passedHorizontalThreshold = mXOverviewAnim.getInterpolatedProgress() > 0.5f; + targetState = passedHorizontalThreshold ? QUICK_SWITCH : NORMAL; + } + + // Animate the various components to the target state. + + float xProgress = mXOverviewAnim.getProgressFraction(); + float startXProgress = Utilities.boundToRange(xProgress + + velocity.x * getSingleFrameMs(mLauncher) / mXRange, 0f, 1f); + final float endXProgress = targetState == NORMAL ? 0 : 1; + long xDuration = BaseSwipeDetector.calculateDuration(velocity.x, + Math.abs(endXProgress - startXProgress)); + ValueAnimator xOverviewAnim = mXOverviewAnim.getAnimationPlayer(); + xOverviewAnim.setFloatValues(startXProgress, endXProgress); + xOverviewAnim.setDuration(xDuration) + .setInterpolator(scrollInterpolatorForVelocity(velocity.x)); + mXOverviewAnim.dispatchOnStartWithVelocity(endXProgress, velocity.x); + + boolean flingUpToNormal = verticalFling && velocity.y < 0 && targetState == NORMAL; + + float yProgress = mYOverviewAnim.getProgressFraction(); + float startYProgress = Utilities.boundToRange(yProgress + - velocity.y * getSingleFrameMs(mLauncher) / mYRange, 0f, 1f); + final float endYProgress; + if (flingUpToNormal) { + endYProgress = 1; + } else if (targetState == NORMAL) { + // Keep overview at its current scale/translationY as it slides off the screen. + endYProgress = startYProgress; + } else { + endYProgress = 0; + } + long yDuration = BaseSwipeDetector.calculateDuration(velocity.y, + Math.abs(endYProgress - startYProgress)); + ValueAnimator yOverviewAnim = mYOverviewAnim.getAnimationPlayer(); + yOverviewAnim.setFloatValues(startYProgress, endYProgress); + yOverviewAnim.setDuration(yDuration); + mYOverviewAnim.dispatchOnStartWithVelocity(endYProgress, velocity.y); + + ValueAnimator nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer(); + if (flingUpToNormal && !mIsHomeScreenVisible) { + // We are flinging to home while workspace is invisible, run the same staggered + // animation as from an app. + // Update mNonOverviewAnim to do nothing so it doesn't interfere. + updateNonOverviewAnim(targetState, new AnimatorSetBuilder(), 0 /* animComponents */); + nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer(); + + new StaggeredWorkspaceAnim(mLauncher, velocity.y, false /* animateOverviewScrim */) + .start(); + } else { + boolean canceled = targetState == NORMAL; + if (canceled) { + // Let the state manager know that the animation didn't go to the target state, + // but don't clean up yet (we already clean up when the animation completes). + mNonOverviewAnim.dispatchOnCancelWithoutCancelRunnable(); + } + float startProgress = mNonOverviewAnim.getProgressFraction(); + float endProgress = canceled ? 0 : 1; + nonOverviewAnim.setFloatValues(startProgress, endProgress); + mNonOverviewAnim.dispatchOnStartWithVelocity(endProgress, + horizontalFling ? velocity.x : velocity.y); + } + + nonOverviewAnim.setDuration(Math.max(xDuration, yDuration)); + mNonOverviewAnim.setEndAction(() -> onAnimationToStateCompleted(targetState, logAction)); + + cancelAnimations(); + xOverviewAnim.start(); + yOverviewAnim.start(); + nonOverviewAnim.start(); + } + + private void onAnimationToStateCompleted(LauncherState targetState, int logAction) { + mLauncher.getUserEventDispatcher().logStateChangeAction(logAction, + getDirectionForLog(), mSwipeDetector.getDownX(), mSwipeDetector.getDownY(), + LauncherLogProto.ContainerType.NAVBAR, + mStartState.containerType, + targetState.containerType, + mLauncher.getWorkspace().getCurrentPage()); + mLauncher.getStateManager().goToState(targetState, false, this::clearState); + } + + private int getDirectionForLog() { + return Utilities.isRtl(mLauncher.getResources()) ? Direction.LEFT : Direction.RIGHT; + } + + private void cancelAnimations() { + if (mNonOverviewAnim != null) { + mNonOverviewAnim.getAnimationPlayer().cancel(); + } + if (mXOverviewAnim != null) { + mXOverviewAnim.getAnimationPlayer().cancel(); + } + if (mYOverviewAnim != null) { + mYOverviewAnim.getAnimationPlayer().cancel(); + } + mShelfPeekAnim.setShelfState(ShelfAnimState.CANCEL, LINEAR, 0); + mMotionPauseDetector.clear(); + } + + private void clearState() { + cancelAnimations(); + mNonOverviewAnim = null; + mXOverviewAnim = null; + mYOverviewAnim = null; + mIsHomeScreenVisible = true; + mSwipeDetector.finishedScrolling(); + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index 40e13154d..54a366d8a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -24,7 +24,6 @@ import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_SHELF import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.LauncherStateManager.ANIM_ALL; import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.INSTANT; @@ -53,15 +52,15 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherInitListenerEx; import com.android.launcher3.LauncherState; -import com.android.launcher3.LauncherStateManager; +import com.android.launcher3.QuickstepAppTransitionManagerImpl; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.AnimatorSetBuilder; -import com.android.launcher3.uioverrides.states.OverviewState; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.util.LayoutUtils; +import com.android.quickstep.util.ShelfPeekAnim; +import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState; import com.android.quickstep.util.StaggeredWorkspaceAnim; import com.android.quickstep.views.LauncherRecentsView; import com.android.quickstep.views.RecentsView; @@ -167,18 +166,8 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe @Override public void playAtomicAnimation(float velocity) { - // Setup workspace with 0 duration to prepare for our staggered animation. - LauncherStateManager stateManager = activity.getStateManager(); - AnimatorSetBuilder builder = new AnimatorSetBuilder(); - // setRecentsAttachedToAppWindow() will animate recents out. - builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW); - stateManager.createAtomicAnimation(BACKGROUND_APP, NORMAL, builder, ANIM_ALL, 0); - builder.build().start(); - - // Stop scrolling so that it doesn't interfere with the translation offscreen. - recentsView.getScroller().forceFinished(true); - - new StaggeredWorkspaceAnim(activity, workspaceView, velocity).start(); + new StaggeredWorkspaceAnim(activity, velocity, true /* animateOverviewScrim */) + .start(); } }; } @@ -201,7 +190,9 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe activity.getAppsView().reset(false /* animate */); return new AnimationFactory() { - private ShelfAnimState mShelfState; + private final ShelfPeekAnim mShelfAnim = + ((QuickstepAppTransitionManagerImpl) activity.getAppTransitionManager()) + .getShelfPeekAnim(); private boolean mIsAttachedToWindow; @Override @@ -230,30 +221,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe @Override public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) { - if (mShelfState == shelfState) { - return; - } - mShelfState = shelfState; - activity.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM); - if (mShelfState == ShelfAnimState.CANCEL) { - return; - } - float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(activity); - float shelfOverviewProgress = OVERVIEW.getVerticalProgress(activity); - // Peek based on default overview progress so we can see hotseat if we're showing - // that instead of predictions in overview. - float defaultOverviewProgress = OverviewState.getDefaultVerticalProgress(activity); - float shelfPeekingProgress = shelfHiddenProgress - - (shelfHiddenProgress - defaultOverviewProgress) * 0.25f; - float toProgress = mShelfState == ShelfAnimState.HIDE - ? shelfHiddenProgress - : mShelfState == ShelfAnimState.PEEK - ? shelfPeekingProgress - : shelfOverviewProgress; - Animator shelfAnim = activity.getStateManager() - .createStateElementAnimation(INDEX_SHELF_ANIM, toProgress); - shelfAnim.setInterpolator(interpolator); - shelfAnim.setDuration(duration).start(); + mShelfAnim.setShelfState(shelfState, interpolator, duration); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index c80dede53..2fa4feb33 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -26,14 +26,14 @@ import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import static com.android.launcher3.util.RaceConditionTracker.ENTER; import static com.android.launcher3.util.RaceConditionTracker.EXIT; import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; -import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE; -import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.HOME; import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.LAST_TASK; import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.NEW_TASK; import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.RECENTS; +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.UPDATE_SYSUI_FLAGS_THRESHOLD; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; @@ -74,13 +74,14 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.RaceConditionTracker; import com.android.launcher3.util.TraceHelper; import com.android.quickstep.ActivityControlHelper.AnimationFactory; -import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState; import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.inputconsumers.InputConsumer; import com.android.quickstep.inputconsumers.OverviewInputConsumer; import com.android.quickstep.util.ClipAnimationHelper.TargetAlphaProvider; import com.android.quickstep.util.RectFSpringAnim; +import com.android.quickstep.util.ShelfPeekAnim; +import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState; import com.android.quickstep.util.SwipeAnimationTargetSet; import com.android.quickstep.views.LiveTileOverlay; import com.android.quickstep.views.RecentsView; @@ -192,7 +193,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW)); private static final String SCREENSHOT_CAPTURED_EVT = "ScreenshotCaptured"; - private static final long SHELF_ANIM_DURATION = 240; public static final long RECENTS_ATTACH_DURATION = 300; /** @@ -206,8 +206,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> private boolean mIsShelfPeeking; private boolean mContinuingLastGesture; - // To avoid UI jump when gesture is started, we offset the animation by the threshold. - private float mShiftAtGestureStart = 0; private ThumbnailData mTaskSnapshot; @@ -442,7 +440,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> @Override public void onMotionPauseChanged(boolean isPaused) { - setShelfState(isPaused ? PEEK : HIDE, OVERSHOOT_1_2, SHELF_ANIM_DURATION); + setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION); } public void maybeUpdateRecentsAttachedState() { @@ -580,9 +578,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> // 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 <= mShiftAtGestureStart || mShiftAtGestureStart >= 1 - ? 0 : (progress - mShiftAtGestureStart) / (1 - mShiftAtGestureStart)); + mLauncherTransitionController.setPlayFraction(progress); } /** @@ -622,7 +618,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> @Override public void onGestureStarted() { notifyGestureStartedAsync(); - mShiftAtGestureStart = mCurrentShift.value; setStateOnUiThread(STATE_GESTURE_STARTED); mGestureStarted = true; } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java new file mode 100644 index 000000000..83bc41623 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_SHELF_ANIM; +import static com.android.launcher3.LauncherState.BACKGROUND_APP; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.view.animation.Interpolator; + +import com.android.launcher3.Launcher; +import com.android.launcher3.uioverrides.states.OverviewState; + +/** + * Animates the shelf between states HIDE, PEEK, and OVERVIEW. + */ + +public class ShelfPeekAnim { + + public static final Interpolator INTERPOLATOR = OVERSHOOT_1_2; + public static final long DURATION = 240; + + private final Launcher mLauncher; + + private ShelfAnimState mShelfState; + private boolean mIsPeeking; + + public ShelfPeekAnim(Launcher launcher) { + mLauncher = launcher; + } + + /** + * Animates to the given state, canceling the previous animation if it was still running. + */ + public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) { + if (mShelfState == shelfState) { + return; + } + mLauncher.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM); + mShelfState = shelfState; + mIsPeeking = mShelfState == ShelfAnimState.PEEK || mShelfState == ShelfAnimState.HIDE; + if (mShelfState == ShelfAnimState.CANCEL) { + return; + } + float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(mLauncher); + float shelfOverviewProgress = OVERVIEW.getVerticalProgress(mLauncher); + // Peek based on default overview progress so we can see hotseat if we're showing + // that instead of predictions in overview. + float defaultOverviewProgress = OverviewState.getDefaultVerticalProgress(mLauncher); + float shelfPeekingProgress = shelfHiddenProgress + - (shelfHiddenProgress - defaultOverviewProgress) * 0.25f; + float toProgress = mShelfState == ShelfAnimState.HIDE + ? shelfHiddenProgress + : mShelfState == ShelfAnimState.PEEK + ? shelfPeekingProgress + : shelfOverviewProgress; + Animator shelfAnim = mLauncher.getStateManager() + .createStateElementAnimation(INDEX_SHELF_ANIM, toProgress); + shelfAnim.setInterpolator(interpolator); + shelfAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + mShelfState = ShelfAnimState.CANCEL; + } + + @Override + public void onAnimationEnd(Animator animator) { + mIsPeeking = mShelfState == ShelfAnimState.PEEK; + } + }); + shelfAnim.setDuration(duration).start(); + } + + /** @return Whether the shelf is currently peeking or animating to or from peeking. */ + public boolean isPeeking() { + return mIsPeeking; + } + + /** The various shelf states we can animate to. */ + public enum ShelfAnimState { + HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false); + + ShelfAnimState(boolean shouldPreformHaptic) { + this.shouldPreformHaptic = shouldPreformHaptic; + } + + public final boolean shouldPreformHaptic; + } +} 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 1aa5365fd..958ef7d4f 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 @@ -18,6 +18,7 @@ package com.android.quickstep.util; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherStateManager.ANIM_ALL; import static com.android.launcher3.anim.Interpolators.LINEAR; import android.animation.Animator; @@ -27,13 +28,11 @@ import android.animation.ObjectAnimator; import android.view.View; import android.view.ViewGroup; -import androidx.annotation.Nullable; - -import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; @@ -41,9 +40,8 @@ import com.android.launcher3.Workspace; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.anim.SpringObjectAnimator; -import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.graphics.OverviewScrim; -import com.android.launcher3.views.IconLabelDotView; +import com.android.quickstep.views.RecentsView; import java.util.ArrayList; import java.util.List; @@ -66,18 +64,12 @@ public class StaggeredWorkspaceAnim { private final float mVelocity; private final float mSpringTransY; - // The original view of the {@link FloatingIconView}. - private final View mOriginalView; - private final List<Animator> mAnimators = new ArrayList<>(); - /** - * @param floatingViewOriginalView The FloatingIconView's original view. - */ - public StaggeredWorkspaceAnim(Launcher launcher, @Nullable View floatingViewOriginalView, - float velocity) { + public StaggeredWorkspaceAnim(Launcher launcher, float velocity, boolean animateOverviewScrim) { + prepareToAnimate(launcher); + mVelocity = velocity; - mOriginalView = floatingViewOriginalView; // Scale the translationY based on the initial velocity to better sync the workspace items // with the floating view. @@ -133,8 +125,10 @@ public class StaggeredWorkspaceAnim { addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows); } - addScrimAnimationForState(launcher, BACKGROUND_APP, 0); - addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS); + if (animateOverviewScrim) { + addScrimAnimationForState(launcher, BACKGROUND_APP, 0); + addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS); + } AnimatorListener resetClipListener = new AnimatorListenerAdapter() { int numAnimations = mAnimators.size(); @@ -161,6 +155,21 @@ public class StaggeredWorkspaceAnim { } /** + * Setup workspace with 0 duration to prepare for our staggered animation. + */ + private void prepareToAnimate(Launcher launcher) { + LauncherStateManager stateManager = launcher.getStateManager(); + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + // setRecentsAttachedToAppWindow() will animate recents out. + builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW); + stateManager.createAtomicAnimation(BACKGROUND_APP, NORMAL, builder, ANIM_ALL, 0); + builder.build().start(); + + // Stop scrolling so that it doesn't interfere with the translation offscreen. + launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true); + } + + /** * Starts the animation. */ public void start() { @@ -192,35 +201,12 @@ public class StaggeredWorkspaceAnim { springTransY.setStartDelay(startDelay); mAnimators.add(springTransY); - ObjectAnimator alpha = getAlphaAnimator(v, startDelay); - if (v == mOriginalView) { - // For IconLabelDotViews, we just want the label to fade in. - // Icon, badge, and dots will animate in separately (controlled via FloatingIconView) - if (v instanceof IconLabelDotView) { - alpha.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - IconLabelDotView view = (IconLabelDotView) v; - view.setIconVisible(false); - view.setForceHideDot(true); - } - }); - } else { - return; - } - } - v.setAlpha(0); - mAnimators.add(alpha); - } - - private ObjectAnimator getAlphaAnimator(View v, long startDelay) { ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f); alpha.setInterpolator(LINEAR); alpha.setDuration(ALPHA_DURATION_MS); alpha.setStartDelay(startDelay); - return alpha; - + mAnimators.add(alpha); } private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) { diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index 991408c64..a91410cd9 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -57,6 +57,9 @@ import android.os.Looper; import android.util.Pair; import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.Interpolators; @@ -69,6 +72,7 @@ import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.util.MultiValueUpdateListener; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteAnimationTargetSet; +import com.android.quickstep.util.ShelfPeekAnim; import com.android.systemui.shared.system.ActivityCompat; import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.QuickStepContract; @@ -80,9 +84,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; import com.android.systemui.shared.system.WindowManagerWrapper; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - /** * {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from * home and/or all-apps. @@ -150,6 +151,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans private RemoteAnimationProvider mRemoteAnimationProvider; + private final ShelfPeekAnim mShelfPeekAnim; + private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { @@ -177,6 +180,12 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans mLauncher.addOnDeviceProfileChangeListener(this); registerRemoteAnimations(); + + mShelfPeekAnim = new ShelfPeekAnim(mLauncher); + } + + public ShelfPeekAnim getShelfPeekAnim() { + return mShelfPeekAnim; } @Override diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java index 5c9c7d4ca..110cc23ec 100644 --- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -36,6 +36,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteAnimationTargetSet; +import com.android.quickstep.util.ShelfPeekAnim; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import java.util.function.BiPredicate; @@ -109,16 +110,6 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { interface AnimationFactory { - enum ShelfAnimState { - HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false); - - ShelfAnimState(boolean shouldPreformHaptic) { - this.shouldPreformHaptic = shouldPreformHaptic; - } - - public final boolean shouldPreformHaptic; - } - default void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) { } void createActivityController(long transitionLength); @@ -127,8 +118,8 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { default void onTransitionCancelled() { } - default void setShelfState(ShelfAnimState animState, Interpolator interpolator, - long duration) { } + default void setShelfState(ShelfPeekAnim.ShelfAnimState animState, + Interpolator interpolator, long duration) { } /** * @param attached Whether to show RecentsView alongside the app window. If false, recents diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java index 0e591cac7..3320dae73 100644 --- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java +++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java @@ -18,6 +18,7 @@ package com.android.quickstep.views; import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.LauncherState.QUICK_SWITCH; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.LINEAR; @@ -35,6 +36,8 @@ import android.util.AttributeSet; import android.view.animation.Interpolator; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherState; +import com.android.launcher3.QuickstepAppTransitionManagerImpl; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.Interpolators; @@ -44,6 +47,7 @@ import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.util.LayoutUtils; +import com.android.quickstep.util.ShelfPeekAnim; /** * Scrim used for all-apps and shelf in Overview @@ -193,8 +197,12 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis if (mProgress >= 1) { mRemainingScreenColor = 0; mShelfColor = 0; + ShelfPeekAnim shelfPeekAnim = ((QuickstepAppTransitionManagerImpl) + mLauncher.getAppTransitionManager()).getShelfPeekAnim(); + LauncherState state = mLauncher.getStateManager().getState(); if (mSysUINavigationMode == Mode.NO_BUTTON - && mLauncher.getStateManager().getState() == BACKGROUND_APP) { + && (state == BACKGROUND_APP || state == QUICK_SWITCH) + && shelfPeekAnim.isPeeking()) { // Show the shelf background when peeking during swipe up. mShelfColor = setColorAlphaBound(mEndScrim, mMidAlpha); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 491e5de85..80860455c 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -446,12 +446,16 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override public void reapplyUi() { + reapplyUi(true /* cancelCurrentAnimation */); + } + + public void reapplyUi(boolean cancelCurrentAnimation) { if (supportsFakeLandscapeUI()) { mRotationMode = mStableDeviceProfile == null ? RotationMode.NORMAL : UiFactory.getRotationMode(mDeviceProfile); } getRootView().dispatchInsets(); - getStateManager().reapplyState(true /* cancelCurrentAnimation */); + getStateManager().reapplyState(cancelCurrentAnimation); } @Override diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index 848e19fb5..f67350884 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -24,7 +24,8 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.os.Handler; import android.os.Looper; -import android.util.Log; + +import androidx.annotation.IntDef; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; @@ -32,7 +33,6 @@ import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter; import com.android.launcher3.compat.AccessibilityManagerCompat; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.uioverrides.UiFactory; import java.io.PrintWriter; @@ -40,8 +40,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import androidx.annotation.IntDef; - /** * TODO: figure out what kind of tests we can write for this * diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java index 8ac9d662c..eabd28369 100644 --- a/src/com/android/launcher3/anim/AlphaUpdateListener.java +++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java @@ -27,7 +27,7 @@ import android.view.ViewGroup; */ public class AlphaUpdateListener extends AnimationSuccessListener implements AnimatorUpdateListener { - private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; + public static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; private View mView; diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java index 2c440bba1..4a52795f8 100644 --- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java +++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java @@ -26,15 +26,15 @@ import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.util.Log; +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.dynamicanimation.animation.SpringAnimation; + import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import androidx.dynamicanimation.animation.DynamicAnimation; -import androidx.dynamicanimation.animation.SpringAnimation; - /** * Helper class to control the playback of an {@link AnimatorSet}, with custom interpolators * and durations. @@ -250,6 +250,17 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat } } + /** + * Sets mOnCancelRunnable = null before dispatching the cancel and restoring the runnable. This + * is intended to be used only if you need to cancel but want to defer cleaning up yourself. + */ + public void dispatchOnCancelWithoutCancelRunnable() { + Runnable onCancel = mOnCancelRunnable; + setOnCancelRunnable(null); + dispatchOnCancel(); + setOnCancelRunnable(onCancel); + } + public void dispatchOnCancel() { dispatchOnCancelRecursively(mAnim); } @@ -283,10 +294,6 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat mOnCancelRunnable = runnable; } - public Runnable getOnCancelRunnable() { - return mOnCancelRunnable; - } - public void skipToEnd() { mSkipToEnd = true; for (SpringAnimation spring : mSprings) { diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java index c45cd85aa..fccc12090 100644 --- a/src/com/android/launcher3/anim/Interpolators.java +++ b/src/com/android/launcher3/anim/Interpolators.java @@ -39,6 +39,7 @@ public class Interpolators { public static final Interpolator LINEAR = new LinearInterpolator(); public static final Interpolator ACCEL = new AccelerateInterpolator(); + public static final Interpolator ACCEL_0_75 = new AccelerateInterpolator(0.75f); public static final Interpolator ACCEL_1_5 = new AccelerateInterpolator(1.5f); public static final Interpolator ACCEL_2 = new AccelerateInterpolator(2); @@ -48,6 +49,7 @@ public class Interpolators { public static final Interpolator DEACCEL_2 = new DecelerateInterpolator(2); public static final Interpolator DEACCEL_2_5 = new DecelerateInterpolator(2.5f); public static final Interpolator DEACCEL_3 = new DecelerateInterpolator(3f); + public static final Interpolator DEACCEL_5 = new DecelerateInterpolator(5f); public static final Interpolator ACCEL_DEACCEL = new AccelerateDecelerateInterpolator(); diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 6b9e20525..0bd2c9af7 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -148,7 +148,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo public ExtendedEditText mFolderName; private PageIndicatorDots mPageIndicator; - private View mFooter; + protected View mFooter; private int mFooterHeight; // Cell ranks used for drag and drop @@ -991,7 +991,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo lp.y = top; } - private int getContentAreaHeight() { + protected int getContentAreaHeight() { DeviceProfile grid = mLauncher.getDeviceProfile(); int maxContentAreaHeight = grid.availableHeightPx - grid.getTotalWorkspacePadding().y - mFooterHeight; diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index 9eb06938a..1310d374e 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -59,6 +59,8 @@ import java.util.List; */ public class FolderAnimationManager { + private static final int FOLDER_NAME_ALPHA_DURATION = 32; + private Folder mFolder; private FolderPagedView mContent; private GradientDrawable mFolderBackground; @@ -130,11 +132,19 @@ public class FolderAnimationManager { * scaleRelativeToDragLayer; final float finalScale = 1f; float scale = mIsOpening ? initialScale : finalScale; - mFolder.setScaleX(scale); - mFolder.setScaleY(scale); mFolder.setPivotX(0); mFolder.setPivotY(0); + // Scale the contents of the folder. + mFolder.mContent.setScaleX(scale); + mFolder.mContent.setScaleY(scale); + mFolder.mContent.setPivotX(0); + mFolder.mContent.setPivotY(0); + mFolder.mFooter.setScaleX(scale); + mFolder.mFooter.setScaleY(scale); + mFolder.mFooter.setPivotX(0); + mFolder.mFooter.setPivotY(0); + // We want to create a small X offset for the preview items, so that they follow their // expected path to their final locations. ie. an icon should not move right, if it's final // location is to its left. This value is arbitrarily defined. @@ -143,14 +153,13 @@ public class FolderAnimationManager { previewItemOffsetX = (int) (lp.width * initialScale - initialSize - previewItemOffsetX); } - final int paddingOffsetX = (int) ((mFolder.getPaddingLeft() + mContent.getPaddingLeft()) - * initialScale); - final int paddingOffsetY = (int) ((mFolder.getPaddingTop() + mContent.getPaddingTop()) - * initialScale); + final int paddingOffsetX = (int) (mContent.getPaddingLeft() * initialScale); + final int paddingOffsetY = (int) (mContent.getPaddingTop() * initialScale); - int initialX = folderIconPos.left + mPreviewBackground.getOffsetX() - paddingOffsetX - - previewItemOffsetX; - int initialY = folderIconPos.top + mPreviewBackground.getOffsetY() - paddingOffsetY; + int initialX = folderIconPos.left + mFolder.getPaddingLeft() + + mPreviewBackground.getOffsetX() - paddingOffsetX - previewItemOffsetX; + int initialY = folderIconPos.top + mFolder.getPaddingTop() + + mPreviewBackground.getOffsetY() - paddingOffsetY; final float xDistance = initialX - lp.x; final float yDistance = initialY - lp.y; @@ -164,11 +173,10 @@ public class FolderAnimationManager { // Set up the reveal animation that clips the Folder. int totalOffsetX = paddingOffsetX + previewItemOffsetX; - Rect startRect = new Rect( - Math.round(totalOffsetX / initialScale), - Math.round(paddingOffsetY / initialScale), - Math.round((totalOffsetX + initialSize) / initialScale), - Math.round((paddingOffsetY + initialSize) / initialScale)); + Rect startRect = new Rect(totalOffsetX, + paddingOffsetY, + Math.round((totalOffsetX + initialSize)), + Math.round((paddingOffsetY + initialSize))); Rect endRect = new Rect(0, 0, lp.width, lp.height); float finalRadius = ResourceUtils.pxFromDp(2, mContext.getResources().getDisplayMetrics()); @@ -189,17 +197,46 @@ public class FolderAnimationManager { play(a, getAnimator(mFolder, View.TRANSLATION_X, xDistance, 0f)); play(a, getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f)); - play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale)); + play(a, getAnimator(mFolder.mContent, SCALE_PROPERTY, initialScale, finalScale)); + play(a, getAnimator(mFolder.mFooter, SCALE_PROPERTY, initialScale, finalScale)); play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor)); play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening)); play(a, getShape().createRevealAnimator( mFolder, startRect, endRect, finalRadius, !mIsOpening)); + // Fade in the folder name, as the text can overlap the icons when grid size is small. + mFolder.mFolderName.setAlpha(mIsOpening ? 0f : 1f); + play(a, getAnimator(mFolder.mFolderName, View.ALPHA, 0, 1), + mIsOpening ? FOLDER_NAME_ALPHA_DURATION : 0, + mIsOpening ? mDuration - FOLDER_NAME_ALPHA_DURATION : FOLDER_NAME_ALPHA_DURATION); + + // Translate the footer so that it tracks the bottom of the content. + float normalHeight = mFolder.getContentAreaHeight(); + float scaledHeight = normalHeight * initialScale; + float diff = normalHeight - scaledHeight; + play(a, getAnimator(mFolder.mFooter, View.TRANSLATION_Y, -diff, 0f)); // Animate the elevation midway so that the shadow is not noticeable in the background. int midDuration = mDuration / 2; Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0); play(a, z, mIsOpening ? midDuration : 0, midDuration); + + // Store clip variables + CellLayout cellLayout = mContent.getCurrentCellLayout(); + boolean folderClipChildren = mFolder.getClipChildren(); + boolean folderClipToPadding = mFolder.getClipToPadding(); + boolean contentClipChildren = mContent.getClipChildren(); + boolean contentClipToPadding = mContent.getClipToPadding(); + boolean cellLayoutClipChildren = cellLayout.getClipChildren(); + boolean cellLayoutClipPadding = cellLayout.getClipToPadding(); + + mFolder.setClipChildren(false); + mFolder.setClipToPadding(false); + mContent.setClipChildren(false); + mContent.setClipToPadding(false); + cellLayout.setClipChildren(false); + cellLayout.setClipToPadding(false); + a.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -207,8 +244,20 @@ public class FolderAnimationManager { mFolder.setTranslationX(0.0f); mFolder.setTranslationY(0.0f); mFolder.setTranslationZ(0.0f); - mFolder.setScaleX(1f); - mFolder.setScaleY(1f); + mFolder.mContent.setScaleX(1f); + mFolder.mContent.setScaleY(1f); + mFolder.mFooter.setScaleX(1f); + mFolder.mFooter.setScaleY(1f); + mFolder.mFooter.setTranslationX(0f); + mFolder.mFolderName.setAlpha(1f); + + mFolder.setClipChildren(folderClipChildren); + mFolder.setClipToPadding(folderClipToPadding); + mContent.setClipChildren(contentClipChildren); + mContent.setClipToPadding(contentClipToPadding); + cellLayout.setClipChildren(cellLayoutClipChildren); + cellLayout.setClipToPadding(cellLayoutClipPadding); + } }); diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index 60f6ee9c5..f40f9762d 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -413,10 +413,7 @@ public abstract class AbstractStateChangeTouchController } else { // Let the state manager know that the animation didn't go to the target state, // but don't cancel ourselves (we already clean up when the animation completes). - Runnable onCancel = mCurrentAnimation.getOnCancelRunnable(); - mCurrentAnimation.setOnCancelRunnable(null); - mCurrentAnimation.dispatchOnCancel(); - mCurrentAnimation.setOnCancelRunnable(onCancel); + mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(); endProgress = 0; if (progress <= 0) { diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 45c0d9097..49d94f06e 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -560,7 +560,7 @@ public class FloatingIconView extends View implements * Checks if the icon result is loaded. If true, we set the icon immediately. Else, we add a * callback to set the icon once the icon result is loaded. */ - private void checkIconResult(View originalView, boolean isOpening) { + private void checkIconResult(View originalView) { CancellationSignal cancellationSignal = new CancellationSignal(); if (mIconLoadResult == null) { @@ -572,9 +572,7 @@ public class FloatingIconView extends View implements if (mIconLoadResult.isIconLoaded) { setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge, mIconLoadResult.iconOffset); - if (isOpening) { - hideOriginalView(originalView); - } + hideOriginalView(originalView); } else { mIconLoadResult.onIconLoaded = () -> { if (cancellationSignal.isCanceled()) { @@ -583,12 +581,8 @@ public class FloatingIconView extends View implements setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge, mIconLoadResult.iconOffset); - setVisibility(VISIBLE); - if (isOpening) { - // Delay swapping views until the icon is loaded to prevent a flash. - hideOriginalView(originalView); - } + hideOriginalView(originalView); }; mLoadIconSignal = cancellationSignal; } @@ -596,9 +590,9 @@ public class FloatingIconView extends View implements } private void hideOriginalView(View originalView) { - if (originalView instanceof BubbleTextView) { - ((BubbleTextView) originalView).setIconVisible(false); - ((BubbleTextView) originalView).setForceHideDot(true); + if (originalView instanceof IconLabelDotView) { + ((IconLabelDotView) originalView).setIconVisible(false); + ((IconLabelDotView) originalView).setForceHideDot(true); } else { originalView.setVisibility(INVISIBLE); } @@ -674,6 +668,9 @@ public class FloatingIconView extends View implements } public void fastFinish() { + if (mLoadIconSignal != null) { + mLoadIconSignal.cancel(); + } if (mEndRunnable != null) { mEndRunnable.run(); mEndRunnable = null; @@ -689,6 +686,10 @@ public class FloatingIconView extends View implements if (mIconLoadResult != null && mIconLoadResult.isIconLoaded) { setVisibility(View.VISIBLE); } + if (!mIsOpening) { + // When closing an app, we want the item on the workspace to be invisible immediately + hideOriginalView(mOriginalIcon); + } } @Override @@ -798,7 +799,7 @@ public class FloatingIconView extends View implements // Must be called after the fastFinish listener and end runnable is created so that // the icon is not left in a hidden state. if (shouldLoadIcon) { - view.checkIconResult(originalView, isOpening); + view.checkIconResult(originalView); } return view; @@ -842,6 +843,7 @@ public class FloatingIconView extends View implements @Override public void onAnimationStart(Animator animation) { btv.setIconVisible(true); + btv.setForceHideDot(true); } }); fade.play(ObjectAnimator.ofInt(btv.getIcon(), DRAWABLE_ALPHA, 0, 255)); |