diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 033cf8e17b..2534699624 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -35,6 +35,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.ServiceConnection; +import android.hardware.SensorManager; +import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Handler; @@ -73,6 +75,8 @@ import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskUtils; import com.android.quickstep.TouchInteractionService; import com.android.quickstep.TouchInteractionService.TISBinder; +import com.android.quickstep.util.LauncherUnfoldAnimationController; +import com.android.quickstep.util.ProxyScreenStatusProvider; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.util.SplitSelectStateController; @@ -82,6 +86,9 @@ import com.android.quickstep.views.SplitPlaceholderView; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import com.android.systemui.unfold.UnfoldTransitionFactory; +import com.android.systemui.unfold.UnfoldTransitionProgressProvider; +import com.android.systemui.unfold.config.UnfoldTransitionConfig; import java.util.List; import java.util.stream.Stream; @@ -117,6 +124,7 @@ public abstract class BaseQuickstepLauncher extends Launcher public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mTaskbarManager = ((TISBinder) iBinder).getTaskbarManager(); mTaskbarManager.setLauncher(BaseQuickstepLauncher.this); + Log.d(TAG, "TIS service connected"); resetServiceBindRetryState(); @@ -142,16 +150,41 @@ public abstract class BaseQuickstepLauncher extends Launcher private @Nullable DragOptions mNextWorkspaceDragOptions = null; private SplitPlaceholderView mSplitPlaceholderView; + private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider; + private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this); addMultiWindowModeChangedListener(mDepthController); + initUnfoldTransitionProgressProvider(); + } + + @Override + protected void onResume() { + super.onResume(); + + if (mLauncherUnfoldAnimationController != null) { + mLauncherUnfoldAnimationController.onResume(); + } + } + + @Override + protected void onPause() { + if (mLauncherUnfoldAnimationController != null) { + mLauncherUnfoldAnimationController.onPause(); + } + + super.onPause(); } @Override public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); + if (mUnfoldTransitionProgressProvider != null) { + mUnfoldTransitionProgressProvider.destroy(); + } SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this); @@ -160,6 +193,11 @@ public abstract class BaseQuickstepLauncher extends Launcher mTaskbarManager.clearLauncher(this); } resetServiceBindRetryState(); + + if (mLauncherUnfoldAnimationController != null) { + mLauncherUnfoldAnimationController.onDestroy(); + } + super.onDestroy(); } @@ -334,6 +372,28 @@ public abstract class BaseQuickstepLauncher extends Launcher mConnectionAttempts = 0; } + private void initUnfoldTransitionProgressProvider() { + final UnfoldTransitionConfig config = UnfoldTransitionFactory.createConfig(this); + if (config.isEnabled()) { + mUnfoldTransitionProgressProvider = + UnfoldTransitionFactory.createUnfoldTransitionProgressProvider( + this, + config, + ProxyScreenStatusProvider.INSTANCE, + getSystemService(DeviceStateManager.class), + getSystemService(SensorManager.class), + getMainThreadHandler(), + getMainExecutor() + ); + + mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController( + this, + getWindowManager(), + mUnfoldTransitionProgressProvider + ); + } + } + public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) { mTaskbarUIController = taskbarUIController; } @@ -373,6 +433,11 @@ public abstract class BaseQuickstepLauncher extends Launcher return mTaskbarStateHandler; } + @Nullable + public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() { + return mUnfoldTransitionProgressProvider; + } + @Override public boolean supportsAdaptiveIconAnimation(View clickedView) { return mAppTransitionManager.hasControlRemoteAppTransitionPermission() diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 8c12567ccf..51d031b3f2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -62,6 +62,7 @@ import com.android.launcher3.util.ViewCache; import com.android.launcher3.views.ActivityContext; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; +import com.android.quickstep.util.ScopedUnfoldTransitionProgressProvider; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -98,7 +99,8 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ private boolean mIsDestroyed = false; public TaskbarActivityContext(Context windowContext, DeviceProfile dp, - TaskbarNavButtonController buttonController) { + TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider + unfoldTransitionProgressProvider) { super(windowContext, Themes.getActivityThemeRes(windowContext)); mDeviceProfile = dp; @@ -120,6 +122,14 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view); StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle); + Display display = windowContext.getDisplay(); + Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY + ? windowContext.getApplicationContext() + : windowContext.getApplicationContext().createDisplayContext(display); + mWindowManager = c.getSystemService(WindowManager.class); + mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT); + mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT); + // Construct controllers. mControllers = new TaskbarControllers(this, new TaskbarDragController(this), @@ -129,18 +139,12 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ R.color.popup_color_primary_light), new TaskbarDragLayerController(this, mDragLayer), new TaskbarViewController(this, taskbarView), + new TaskbarUnfoldAnimationController(unfoldTransitionProgressProvider, + mWindowManager), new TaskbarKeyguardController(this), new StashedHandleViewController(this, stashedHandleView), new TaskbarStashController(this), new TaskbarEduController(this)); - - Display display = windowContext.getDisplay(); - Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY - ? windowContext.getApplicationContext() - : windowContext.getApplicationContext().createDisplayContext(display); - mWindowManager = c.getSystemService(WindowManager.class); - mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT); - mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT); } public void init() { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java index 11975430b1..6144881902 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java @@ -31,6 +31,7 @@ public class TaskbarControllers { public final RotationButtonController rotationButtonController; public final TaskbarDragLayerController taskbarDragLayerController; public final TaskbarViewController taskbarViewController; + public final TaskbarUnfoldAnimationController taskbarUnfoldAnimationController; public final TaskbarKeyguardController taskbarKeyguardController; public final StashedHandleViewController stashedHandleViewController; public final TaskbarStashController taskbarStashController; @@ -46,6 +47,7 @@ public class TaskbarControllers { RotationButtonController rotationButtonController, TaskbarDragLayerController taskbarDragLayerController, TaskbarViewController taskbarViewController, + TaskbarUnfoldAnimationController taskbarUnfoldAnimationController, TaskbarKeyguardController taskbarKeyguardController, StashedHandleViewController stashedHandleViewController, TaskbarStashController taskbarStashController, @@ -57,6 +59,7 @@ public class TaskbarControllers { this.rotationButtonController = rotationButtonController; this.taskbarDragLayerController = taskbarDragLayerController; this.taskbarViewController = taskbarViewController; + this.taskbarUnfoldAnimationController = taskbarUnfoldAnimationController; this.taskbarKeyguardController = taskbarKeyguardController; this.stashedHandleViewController = stashedHandleViewController; this.taskbarStashController = taskbarStashController; @@ -75,6 +78,7 @@ public class TaskbarControllers { } taskbarDragLayerController.init(this); taskbarViewController.init(this); + taskbarUnfoldAnimationController.init(this); taskbarKeyguardController.init(navbarButtonsViewController); stashedHandleViewController.init(this); taskbarStashController.init(this); @@ -89,6 +93,7 @@ public class TaskbarControllers { rotationButtonController.onDestroy(); taskbarDragLayerController.onDestroy(); taskbarKeyguardController.onDestroy(); + taskbarUnfoldAnimationController.onDestroy(); taskbarViewController.onDestroy(); stashedHandleViewController.onDestroy(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index 4ed83f295e..ec98bbf05a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -42,6 +42,7 @@ import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TouchInteractionService; +import com.android.quickstep.util.ScopedUnfoldTransitionProgressProvider; /** * Class to manage taskbar lifecycle @@ -58,6 +59,10 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen private final TaskbarNavButtonController mNavButtonController; private final SettingsCache.OnChangeListener mUserSetupCompleteListener; + // The source for this provider is set when Launcher is available + private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider = + new ScopedUnfoldTransitionProgressProvider(); + private TaskbarActivityContext mTaskbarActivityContext; private BaseQuickstepLauncher mLauncher; /** @@ -120,6 +125,9 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen */ public void setLauncher(@NonNull BaseQuickstepLauncher launcher) { mLauncher = launcher; + mUnfoldProgressProvider.setSourceProvider(launcher + .getUnfoldTransitionProgressProvider()); + if (mTaskbarActivityContext != null) { mTaskbarActivityContext.setUIController( new LauncherTaskbarUIController(launcher, mTaskbarActivityContext)); @@ -135,6 +143,7 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen if (mTaskbarActivityContext != null) { mTaskbarActivityContext.setUIController(TaskbarUIController.DEFAULT); } + mUnfoldProgressProvider.setSourceProvider(null); } } @@ -153,8 +162,8 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen return; } - mTaskbarActivityContext = new TaskbarActivityContext( - mContext, dp.copy(mContext), mNavButtonController); + mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp.copy(mContext), + mNavButtonController, mUnfoldProgressProvider); mTaskbarActivityContext.init(); if (mLauncher != null) { mTaskbarActivityContext.setUIController( diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java new file mode 100644 index 0000000000..43f015cd2d --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 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.taskbar; + +import android.view.View; +import android.view.WindowManager; + +import com.android.quickstep.util.LauncherViewsMoveFromCenterTranslationApplier; +import com.android.quickstep.util.ScopedUnfoldTransitionProgressProvider; +import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator; +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener; + +/** + * Controls animation of taskbar icons when unfolding foldable devices + */ +public class TaskbarUnfoldAnimationController { + + private final ScopedUnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider; + private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimator; + private final TransitionListener mTransitionListener = new TransitionListener(); + private TaskbarViewController mTaskbarViewController; + + public TaskbarUnfoldAnimationController(ScopedUnfoldTransitionProgressProvider + unfoldTransitionProgressProvider, WindowManager windowManager) { + mUnfoldTransitionProgressProvider = unfoldTransitionProgressProvider; + mMoveFromCenterAnimator = new UnfoldMoveFromCenterAnimator(windowManager, + new LauncherViewsMoveFromCenterTranslationApplier()); + } + + /** + * Initializes the controller + * @param taskbarControllers references to all other taskbar controllers + */ + public void init(TaskbarControllers taskbarControllers) { + mTaskbarViewController = taskbarControllers.taskbarViewController; + mTaskbarViewController.addOneTimePreDrawListener(() -> + mUnfoldTransitionProgressProvider.setReadyToHandleTransition(true)); + mUnfoldTransitionProgressProvider.addCallback(mTransitionListener); + } + + /** + * Destroys the controller + */ + public void onDestroy() { + mUnfoldTransitionProgressProvider.setReadyToHandleTransition(false); + mUnfoldTransitionProgressProvider.removeCallback(mTransitionListener); + } + + private class TransitionListener implements TransitionProgressListener { + + @Override + public void onTransitionStarted() { + mMoveFromCenterAnimator.updateDisplayProperties(); + View[] icons = mTaskbarViewController.getIconViews(); + for (View icon : icons) { + // TODO(b/193794563) we should re-register views if they are re-bound/re-inflated + // during the animation + mMoveFromCenterAnimator.registerViewForAnimation(icon); + } + + mMoveFromCenterAnimator.onTransitionStarted(); + } + + @Override + public void onTransitionFinished() { + mMoveFromCenterAnimator.onTransitionFinished(); + } + + @Override + public void onTransitionProgress(float progress) { + mMoveFromCenterAnimator.onTransitionProgress(progress); + } + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index f1748afce3..f359a3dbaa 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -16,20 +16,24 @@ package com.android.launcher3.taskbar; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.AnimatedFloat.VALUE; import android.graphics.Rect; +import android.util.FloatProperty; import android.view.MotionEvent; import android.view.View; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnPreDrawListener; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.MultiValueAlpha; import com.android.quickstep.AnimatedFloat; @@ -117,6 +121,25 @@ public class TaskbarViewController { mTaskbarView.setClickAndLongClickListenersForIcon(icon); } + /** + * Adds one time pre draw listener to the taskbar view, it is called before + * drawing a frame and invoked only once + * @param listener callback that will be invoked before drawing the next frame + */ + public void addOneTimePreDrawListener(Runnable listener) { + mTaskbarView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { + @Override + public boolean onPreDraw() { + final ViewTreeObserver viewTreeObserver = mTaskbarView.getViewTreeObserver(); + if (viewTreeObserver.isAlive()) { + listener.run(); + viewTreeObserver.removeOnPreDrawListener(this); + } + return true; + } + }); + } + public Rect getIconLayoutBounds() { return mTaskbarView.getIconLayoutBounds(); } @@ -194,7 +217,7 @@ public class TaskbarViewController { float childCenter = (child.getLeft() + child.getRight()) / 2; float hotseatIconCenter = hotseatPadding.left + hotseatCellSize * info.screenId + hotseatCellSize / 2; - setter.setFloat(child, VIEW_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR); + setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR); } AnimatorPlaybackController controller = setter.createPlaybackController(); @@ -257,4 +280,30 @@ public class TaskbarViewController { return false; } } + + public static final FloatProperty ICON_TRANSLATE_X = + new FloatProperty("taskbarAligmentTranslateX") { + + @Override + public void setValue(View view, float v) { + if (view instanceof BubbleTextView) { + ((BubbleTextView) view).setTranslationXForTaskbarAlignmentAnimation(v); + } else if (view instanceof FolderIcon) { + ((FolderIcon) view).setTranslationForTaskbarAlignmentAnimation(v); + } else { + view.setTranslationX(v); + } + } + + @Override + public Float get(View view) { + if (view instanceof BubbleTextView) { + return ((BubbleTextView) view) + .getTranslationXForTaskbarAlignmentAnimation(); + } else if (view instanceof FolderIcon) { + return ((FolderIcon) view).getTranslationXForTaskbarAlignmentAnimation(); + } + return view.getTranslationX(); + } + }; } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 14bc380b8b..2009cd75d9 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -36,13 +36,9 @@ import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SY import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; -import android.hardware.SensorManager; -import android.hardware.devicestate.DeviceStateManager; import android.view.HapticFeedbackConstants; import android.view.View; -import androidx.annotation.Nullable; - import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; @@ -79,14 +75,9 @@ import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskUtils; -import com.android.quickstep.util.LauncherUnfoldAnimationController; -import com.android.quickstep.util.ProxyScreenStatusProvider; import com.android.quickstep.util.QuickstepOnboardingPrefs; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; -import com.android.systemui.unfold.UnfoldTransitionFactory; -import com.android.systemui.unfold.UnfoldTransitionProgressProvider; -import com.android.systemui.unfold.config.UnfoldTransitionConfig; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -106,51 +97,10 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { private FixedContainerItems mAllAppsPredictions; private HotseatPredictionController mHotseatPredictionController; - @Nullable - private LauncherUnfoldAnimationController mLauncherUnfoldAnimationController; - @Override protected void setupViews() { super.setupViews(); mHotseatPredictionController = new HotseatPredictionController(this); - - final UnfoldTransitionConfig config = UnfoldTransitionFactory.createConfig(this); - if (config.isEnabled()) { - final UnfoldTransitionProgressProvider unfoldTransitionProgressProvider = - UnfoldTransitionFactory.createUnfoldTransitionProgressProvider( - this, - config, - ProxyScreenStatusProvider.INSTANCE, - getSystemService(DeviceStateManager.class), - getSystemService(SensorManager.class), - getMainThreadHandler(), - getMainExecutor() - ); - - mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController( - this, - getWindowManager(), - unfoldTransitionProgressProvider - ); - } - } - - @Override - protected void onResume() { - super.onResume(); - - if (mLauncherUnfoldAnimationController != null) { - mLauncherUnfoldAnimationController.onResume(); - } - } - - @Override - protected void onPause() { - if (mLauncherUnfoldAnimationController != null) { - mLauncherUnfoldAnimationController.onPause(); - } - - super.onPause(); } @Override @@ -281,10 +231,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { public void onDestroy() { super.onDestroy(); mHotseatPredictionController.destroy(); - - if (mLauncherUnfoldAnimationController != null) { - mLauncherUnfoldAnimationController.onDestroy(); - } } @Override diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index c5ab84daf7..47d35808d6 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -37,26 +37,23 @@ public class LauncherUnfoldAnimationController { private static final float MAX_WIDTH_INSET_FRACTION = 0.15f; private final Launcher mLauncher; - private final UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider; - private final UnfoldMoveFromCenterWorkspaceAnimator mMoveFromCenterWorkspaceAnimation; @Nullable private HorizontalInsettableView mQsbInsettable; - private final AnimationListener mAnimationListener = new AnimationListener(); - - private boolean mIsTransitionRunning = false; - private boolean mIsReadyToPlayAnimation = false; + private final ScopedUnfoldTransitionProgressProvider mProgressProvider; public LauncherUnfoldAnimationController( Launcher launcher, WindowManager windowManager, UnfoldTransitionProgressProvider unfoldTransitionProgressProvider) { mLauncher = launcher; - mUnfoldTransitionProgressProvider = unfoldTransitionProgressProvider; - mMoveFromCenterWorkspaceAnimation = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, - windowManager); - mUnfoldTransitionProgressProvider.addCallback(mAnimationListener); + mProgressProvider = new ScopedUnfoldTransitionProgressProvider( + unfoldTransitionProgressProvider); + + mProgressProvider.addCallback(new UnfoldMoveFromCenterWorkspaceAnimator(launcher, + windowManager)); + mProgressProvider.addCallback(new QsbAnimationListener()); } /** @@ -73,7 +70,7 @@ public class LauncherUnfoldAnimationController { @Override public boolean onPreDraw() { if (obs.isAlive()) { - onPreDrawAfterResume(); + mProgressProvider.setReadyToHandleTransition(true); obs.removeOnPreDrawListener(this); } return true; @@ -85,12 +82,7 @@ public class LauncherUnfoldAnimationController { * Called when launcher activity is paused */ public void onPause() { - if (mIsTransitionRunning) { - mIsTransitionRunning = false; - mAnimationListener.onTransitionFinished(); - } - - mIsReadyToPlayAnimation = false; + mProgressProvider.setReadyToHandleTransition(false); mQsbInsettable = null; } @@ -98,48 +90,24 @@ public class LauncherUnfoldAnimationController { * Called when launcher activity is destroyed */ public void onDestroy() { - mUnfoldTransitionProgressProvider.removeCallback(mAnimationListener); + mProgressProvider.destroy(); } - /** - * Called after performing layouting of the views after configuration change - */ - private void onPreDrawAfterResume() { - mIsReadyToPlayAnimation = true; - - if (mIsTransitionRunning) { - mMoveFromCenterWorkspaceAnimation.onTransitionStarted(); - } - } - - private class AnimationListener implements TransitionProgressListener { + private class QsbAnimationListener implements TransitionProgressListener { @Override public void onTransitionStarted() { - mIsTransitionRunning = true; - - if (mIsReadyToPlayAnimation) { - mMoveFromCenterWorkspaceAnimation.onTransitionStarted(); - } } @Override public void onTransitionFinished() { - if (mIsReadyToPlayAnimation) { - mMoveFromCenterWorkspaceAnimation.onTransitionFinished(); - - if (mQsbInsettable != null) { - mQsbInsettable.setHorizontalInsets(0); - } + if (mQsbInsettable != null) { + mQsbInsettable.setHorizontalInsets(0); } - - mIsTransitionRunning = false; } @Override public void onTransitionProgress(float progress) { - mMoveFromCenterWorkspaceAnimation.onTransitionProgress(progress); - if (mQsbInsettable != null) { float insetPercentage = comp(progress) * MAX_WIDTH_INSET_FRACTION; mQsbInsettable.setHorizontalInsets(insetPercentage); diff --git a/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java b/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java new file mode 100644 index 0000000000..effdfdd97b --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/LauncherViewsMoveFromCenterTranslationApplier.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import android.annotation.NonNull; +import android.view.View; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.widget.NavigableAppWidgetHostView; +import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.TranslationApplier; + +/** + * Class that allows to set translations for move from center animation independently + * from other translations for certain launcher views + */ +public class LauncherViewsMoveFromCenterTranslationApplier implements TranslationApplier { + + @Override + public void apply(@NonNull View view, float x, float y) { + if (view instanceof NavigableAppWidgetHostView) { + ((NavigableAppWidgetHostView) view).setTranslationForMoveFromCenterAnimation(x, y); + } else if (view instanceof BubbleTextView) { + ((BubbleTextView) view).setTranslationForMoveFromCenterAnimation(x, y); + } else if (view instanceof FolderIcon) { + ((FolderIcon) view).setTranslationForMoveFromCenterAnimation(x, y); + } else { + view.setTranslationX(x); + view.setTranslationY(y); + } + } +} diff --git a/quickstep/src/com/android/quickstep/util/ScopedUnfoldTransitionProgressProvider.java b/quickstep/src/com/android/quickstep/util/ScopedUnfoldTransitionProgressProvider.java new file mode 100644 index 0000000000..2ef311ff3e --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/ScopedUnfoldTransitionProgressProvider.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.systemui.unfold.UnfoldTransitionProgressProvider; +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener; + +import java.util.ArrayList; +import java.util.List; + +/** + * Manages progress listeners that can have smaller lifespan than the unfold animation. + * Allows to limit getting transition updates to only when + * {@link ScopedUnfoldTransitionProgressProvider#setReadyToHandleTransition} is called + * with readyToHandleTransition = true + * + * If the transition has already started by the moment when the clients are ready to play + * the transition then it will report transition started callback and current animation progress. + */ +public final class ScopedUnfoldTransitionProgressProvider implements + UnfoldTransitionProgressProvider, TransitionProgressListener { + + private static final float PROGRESS_UNSET = -1f; + + @Nullable + private UnfoldTransitionProgressProvider mSource; + + private final List mListeners = new ArrayList<>(); + + private boolean mIsReadyToHandleTransition; + private boolean mIsTransitionRunning; + private float mLastTransitionProgress = PROGRESS_UNSET; + + public ScopedUnfoldTransitionProgressProvider() { + this(null); + } + + public ScopedUnfoldTransitionProgressProvider(@Nullable UnfoldTransitionProgressProvider + source) { + setSourceProvider(source); + } + + /** + * Sets the source for the unfold transition progress updates, + * it replaces current provider if it is already set + * @param provider transition provider that emits transition progress updates + */ + public void setSourceProvider(@Nullable UnfoldTransitionProgressProvider provider) { + if (mSource != null) { + mSource.removeCallback(this); + } + + if (provider != null) { + mSource = provider; + mSource.addCallback(this); + } + } + + /** + * Allows to notify this provide whether the listeners can play the transition or not. + * Call this method with readyToHandleTransition = true when all listeners + * are ready to consume the transition progress events. + * Call it with readyToHandleTransition = false when listeners can't process the events. + */ + public void setReadyToHandleTransition(boolean isReadyToHandleTransition) { + if (mIsTransitionRunning) { + if (mIsReadyToHandleTransition) { + mListeners.forEach(TransitionProgressListener::onTransitionStarted); + + if (mLastTransitionProgress != PROGRESS_UNSET) { + mListeners.forEach(listener -> + listener.onTransitionProgress(mLastTransitionProgress)); + } + } else { + mIsTransitionRunning = false; + mListeners.forEach(TransitionProgressListener::onTransitionFinished); + } + } + + mIsReadyToHandleTransition = isReadyToHandleTransition; + } + + @Override + public void addCallback(@NonNull TransitionProgressListener listener) { + mListeners.add(listener); + } + + @Override + public void removeCallback(@NonNull TransitionProgressListener listener) { + mListeners.remove(listener); + } + + @Override + public void destroy() { + mSource.removeCallback(this); + } + + @Override + public void onTransitionStarted() { + this.mIsTransitionRunning = true; + if (mIsReadyToHandleTransition) { + mListeners.forEach(TransitionProgressListener::onTransitionStarted); + } + } + + @Override + public void onTransitionProgress(float progress) { + if (mIsReadyToHandleTransition) { + mListeners.forEach(listener -> listener.onTransitionProgress(progress)); + } + + mLastTransitionProgress = progress; + } + + @Override + public void onTransitionFinished() { + if (mIsReadyToHandleTransition) { + mListeners.forEach(TransitionProgressListener::onTransitionFinished); + } + + mIsTransitionRunning = false; + mLastTransitionProgress = PROGRESS_UNSET; + } +} diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java index 482092d367..95403b2d0c 100644 --- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java +++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java @@ -15,20 +15,16 @@ */ package com.android.quickstep.util; -import android.annotation.NonNull; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; -import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Workspace; -import com.android.launcher3.widget.NavigableAppWidgetHostView; import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator; -import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.TranslationApplier; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; import java.util.HashMap; @@ -49,7 +45,7 @@ public class UnfoldMoveFromCenterWorkspaceAnimator public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager) { mLauncher = launcher; mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager, - new WorkspaceViewsTranslationApplier()); + new LauncherViewsMoveFromCenterTranslationApplier()); } @Override @@ -122,19 +118,4 @@ public class UnfoldMoveFromCenterWorkspaceAnimator view.setClipChildren(originalClipChildren); } } - - private static class WorkspaceViewsTranslationApplier implements TranslationApplier { - - @Override - public void apply(@NonNull View view, float x, float y) { - if (view instanceof NavigableAppWidgetHostView) { - ((NavigableAppWidgetHostView) view).setTranslationForMoveFromCenterAnimation(x, y); - } else if (view instanceof BubbleTextView) { - ((BubbleTextView) view).setTranslationForMoveFromCenterAnimation(x, y); - } else { - view.setTranslationX(x); - view.setTranslationY(y); - } - } - } } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index e52d1be18c..54920e1e17 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -90,6 +90,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private final PointF mTranslationForReorderBounce = new PointF(0, 0); private final PointF mTranslationForReorderPreview = new PointF(0, 0); + private float mTranslationXForTaskbarAlignmentAnimation = 0f; + private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0); private float mScaleForReorderBounce = 1f; @@ -825,7 +827,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private void updateTranslation() { super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x - + mTranslationForMoveFromCenterAnimation.x); + + mTranslationForMoveFromCenterAnimation.x + + mTranslationXForTaskbarAlignmentAnimation); super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y + mTranslationForMoveFromCenterAnimation.y); } @@ -860,11 +863,29 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, return mScaleForReorderBounce; } + /** + * Sets translation values for move from center animation + */ public void setTranslationForMoveFromCenterAnimation(float x, float y) { mTranslationForMoveFromCenterAnimation.set(x, y); updateTranslation(); } + /** + * Sets translationX for taskbar to launcher alignment animation + */ + public void setTranslationXForTaskbarAlignmentAnimation(float translationX) { + mTranslationXForTaskbarAlignmentAnimation = translationX; + updateTranslation(); + } + + /** + * Returns translationX value for taskbar to launcher alignment animation + */ + public float getTranslationXForTaskbarAlignmentAnimation() { + return mTranslationXForTaskbarAlignmentAnimation; + } + public View getView() { return this; } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 60d8cdbd7c..439df80284 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -132,6 +132,9 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel private Rect mTouchArea = new Rect(); + private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0); + private float mTranslationXForTaskbarAlignmentAnimation = 0f; + private final PointF mTranslationForReorderBounce = new PointF(0, 0); private final PointF mTranslationForReorderPreview = new PointF(0, 0); private float mScaleForReorderBounce = 1f; @@ -765,8 +768,11 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } private void updateTranslation() { - super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x); - super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y); + super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x + + mTranslationForMoveFromCenterAnimation.x + + mTranslationXForTaskbarAlignmentAnimation); + super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y + + mTranslationForMoveFromCenterAnimation.y); } public void setReorderBounceOffset(float x, float y) { @@ -778,6 +784,29 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel offset.set(mTranslationForReorderBounce); } + /** + * Sets translationX value for taskbar to launcher alignment animation + */ + public void setTranslationForTaskbarAlignmentAnimation(float translationX) { + mTranslationXForTaskbarAlignmentAnimation = translationX; + updateTranslation(); + } + + /** + * Returns translation values for taskbar to launcher alignment animation + */ + public float getTranslationXForTaskbarAlignmentAnimation() { + return mTranslationXForTaskbarAlignmentAnimation; + } + + /** + * Sets translation values for move from center animation + */ + public void setTranslationForMoveFromCenterAnimation(float x, float y) { + mTranslationForMoveFromCenterAnimation.set(x, y); + updateTranslation(); + } + @Override public void setReorderPreviewOffset(float x, float y) { mTranslationForReorderPreview.set(x, y);