Loading quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +54 −0 Original line number Diff line number Diff line Loading @@ -36,9 +36,13 @@ 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; Loading Loading @@ -75,9 +79,14 @@ 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.unfold.UnfoldTransitionFactory; import com.android.unfold.UnfoldTransitionProgressProvider; import com.android.unfold.config.UnfoldTransitionConfig; import java.io.FileDescriptor; import java.io.PrintWriter; Loading @@ -97,10 +106,51 @@ 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 Loading Loading @@ -231,6 +281,10 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { public void onDestroy() { super.onDestroy(); mHotseatPredictionController.destroy(); if (mLauncherUnfoldAnimationController != null) { mLauncherUnfoldAnimationController.onDestroy(); } } @Override Loading quickstep/src/com/android/quickstep/TouchInteractionService.java +7 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.AssistantUtilities; import com.android.quickstep.util.ProtoTracer; import com.android.quickstep.util.ProxyScreenStatusProvider; import com.android.quickstep.util.SplitScreenBounds; import com.android.systemui.plugins.OverscrollPlugin; import com.android.systemui.plugins.PluginListener; Loading Loading @@ -268,6 +269,12 @@ public class TouchInteractionService extends Service implements PluginListener<O MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb)); } @BinderThread @Override public void onScreenTurnedOn() { MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn); } @Override public void onRotationProposal(int rotation, boolean isValid) { executeForTaskbarManager(() -> mTaskbarManager.onRotationProposal(rotation, isValid)); Loading quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java 0 → 100644 +122 −0 Original line number Diff line number Diff line /* * 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.view.ViewTreeObserver; import android.view.WindowManager; import com.android.launcher3.Launcher; import com.android.unfold.UnfoldTransitionProgressProvider; import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener; /** * Controls animations that are happening during unfolding foldable devices */ public class LauncherUnfoldAnimationController { private final Launcher mLauncher; private final UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider; private final UnfoldMoveFromCenterWorkspaceAnimator mMoveFromCenterWorkspaceAnimation; private final AnimationListener mAnimationListener = new AnimationListener(); private boolean mIsTransitionRunning = false; private boolean mIsReadyToPlayAnimation = false; public LauncherUnfoldAnimationController( Launcher launcher, WindowManager windowManager, UnfoldTransitionProgressProvider unfoldTransitionProgressProvider) { mLauncher = launcher; mUnfoldTransitionProgressProvider = unfoldTransitionProgressProvider; mMoveFromCenterWorkspaceAnimation = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, windowManager); mUnfoldTransitionProgressProvider.addCallback(mAnimationListener); } /** * Called when launcher is resumed */ public void onResume() { final ViewTreeObserver obs = mLauncher.getWorkspace().getViewTreeObserver(); obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { if (obs.isAlive()) { onPreDrawAfterResume(); obs.removeOnPreDrawListener(this); } return true; } }); } /** * Called when launcher activity is paused */ public void onPause() { mIsReadyToPlayAnimation = false; if (mIsTransitionRunning) { mIsTransitionRunning = false; mMoveFromCenterWorkspaceAnimation.onTransitionFinished(); } } /** * Called when launcher activity is destroyed */ public void onDestroy() { mUnfoldTransitionProgressProvider.removeCallback(mAnimationListener); } /** * Called after performing layouting of the views after configuration change */ private void onPreDrawAfterResume() { mIsReadyToPlayAnimation = true; if (mIsTransitionRunning) { mMoveFromCenterWorkspaceAnimation.onTransitionStarted(); } } private class AnimationListener implements TransitionProgressListener { @Override public void onTransitionStarted() { mIsTransitionRunning = true; if (mIsReadyToPlayAnimation) { mMoveFromCenterWorkspaceAnimation.onTransitionStarted(); } } @Override public void onTransitionFinished() { if (mIsReadyToPlayAnimation) { mMoveFromCenterWorkspaceAnimation.onTransitionFinished(); } mIsTransitionRunning = false; } @Override public void onTransitionProgress(float progress) { mMoveFromCenterWorkspaceAnimation.onTransitionProgress(progress); } } } quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java 0 → 100644 +51 −0 Original line number Diff line number Diff line /* * 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 androidx.annotation.NonNull; import com.android.unfold.updates.screen.ScreenStatusProvider; import java.util.ArrayList; import java.util.List; /** * Screen status provider implementation that exposes methods to provide screen * status updates to listeners. It is used to receive screen turned on event from * SystemUI to Launcher. */ public class ProxyScreenStatusProvider implements ScreenStatusProvider { public static final ProxyScreenStatusProvider INSTANCE = new ProxyScreenStatusProvider(); private final List<ScreenListener> mListeners = new ArrayList<>(); /** * Called when the screen is on and ready (windows are drawn and screen blocker is removed) */ public void onScreenTurnedOn() { mListeners.forEach(ScreenListener::onScreenTurnedOn); } @Override public void addCallback(@NonNull ScreenListener listener) { mListeners.add(listener); } @Override public void removeCallback(@NonNull ScreenListener listener) { mListeners.remove(listener); } } quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java 0 → 100644 +140 −0 Original line number Diff line number Diff line /* * 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 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.unfold.UnfoldTransitionProgressProvider; import java.util.HashMap; import java.util.Map; /** * Animation that moves launcher icons and widgets from center to the sides (final position) */ public class UnfoldMoveFromCenterWorkspaceAnimator implements UnfoldTransitionProgressProvider.TransitionProgressListener { private final Launcher mLauncher; private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation; private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>(); private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>(); public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager) { mLauncher = launcher; mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager, new WorkspaceViewsTranslationApplier()); } @Override public void onTransitionStarted() { mMoveFromCenterAnimation.updateDisplayProperties(); Workspace workspace = mLauncher.getWorkspace(); Hotseat hotseat = mLauncher.getHotseat(); // App icons and widgets workspace .forEachVisiblePage(page -> { final CellLayout cellLayout = (CellLayout) page; ShortcutAndWidgetContainer itemsContainer = cellLayout .getShortcutsAndWidgets(); disableClipping(cellLayout); for (int i = 0; i < itemsContainer.getChildCount(); i++) { View child = itemsContainer.getChildAt(i); mMoveFromCenterAnimation.registerViewForAnimation(child); } }); disableClipping(workspace); // Hotseat icons ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets(); disableClipping(hotseat); for (int i = 0; i < hotseatIcons.getChildCount(); i++) { View child = hotseatIcons.getChildAt(i); mMoveFromCenterAnimation.registerViewForAnimation(child); } onTransitionProgress(0f); } @Override public void onTransitionProgress(float progress) { mMoveFromCenterAnimation.onTransitionProgress(progress); } @Override public void onTransitionFinished() { mMoveFromCenterAnimation.onTransitionFinished(); mMoveFromCenterAnimation.clearRegisteredViews(); restoreClipping(mLauncher.getWorkspace()); mLauncher.getWorkspace().forEachVisiblePage(page -> restoreClipping((CellLayout) page)); restoreClipping(mLauncher.getHotseat()); mOriginalClipChildren.clear(); mOriginalClipToPadding.clear(); } private void disableClipping(ViewGroup view) { mOriginalClipToPadding.put(view, view.getClipToPadding()); mOriginalClipChildren.put(view, view.getClipChildren()); view.setClipToPadding(false); view.setClipChildren(false); } private void restoreClipping(ViewGroup view) { final Boolean originalClipToPadding = mOriginalClipToPadding.get(view); if (originalClipToPadding != null) { view.setClipToPadding(originalClipToPadding); } final Boolean originalClipChildren = mOriginalClipChildren.get(view); if (originalClipChildren != null) { 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); } } } } Loading
quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +54 −0 Original line number Diff line number Diff line Loading @@ -36,9 +36,13 @@ 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; Loading Loading @@ -75,9 +79,14 @@ 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.unfold.UnfoldTransitionFactory; import com.android.unfold.UnfoldTransitionProgressProvider; import com.android.unfold.config.UnfoldTransitionConfig; import java.io.FileDescriptor; import java.io.PrintWriter; Loading @@ -97,10 +106,51 @@ 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 Loading Loading @@ -231,6 +281,10 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { public void onDestroy() { super.onDestroy(); mHotseatPredictionController.destroy(); if (mLauncherUnfoldAnimationController != null) { mLauncherUnfoldAnimationController.onDestroy(); } } @Override Loading
quickstep/src/com/android/quickstep/TouchInteractionService.java +7 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.AssistantUtilities; import com.android.quickstep.util.ProtoTracer; import com.android.quickstep.util.ProxyScreenStatusProvider; import com.android.quickstep.util.SplitScreenBounds; import com.android.systemui.plugins.OverscrollPlugin; import com.android.systemui.plugins.PluginListener; Loading Loading @@ -268,6 +269,12 @@ public class TouchInteractionService extends Service implements PluginListener<O MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb)); } @BinderThread @Override public void onScreenTurnedOn() { MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn); } @Override public void onRotationProposal(int rotation, boolean isValid) { executeForTaskbarManager(() -> mTaskbarManager.onRotationProposal(rotation, isValid)); Loading
quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java 0 → 100644 +122 −0 Original line number Diff line number Diff line /* * 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.view.ViewTreeObserver; import android.view.WindowManager; import com.android.launcher3.Launcher; import com.android.unfold.UnfoldTransitionProgressProvider; import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener; /** * Controls animations that are happening during unfolding foldable devices */ public class LauncherUnfoldAnimationController { private final Launcher mLauncher; private final UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider; private final UnfoldMoveFromCenterWorkspaceAnimator mMoveFromCenterWorkspaceAnimation; private final AnimationListener mAnimationListener = new AnimationListener(); private boolean mIsTransitionRunning = false; private boolean mIsReadyToPlayAnimation = false; public LauncherUnfoldAnimationController( Launcher launcher, WindowManager windowManager, UnfoldTransitionProgressProvider unfoldTransitionProgressProvider) { mLauncher = launcher; mUnfoldTransitionProgressProvider = unfoldTransitionProgressProvider; mMoveFromCenterWorkspaceAnimation = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, windowManager); mUnfoldTransitionProgressProvider.addCallback(mAnimationListener); } /** * Called when launcher is resumed */ public void onResume() { final ViewTreeObserver obs = mLauncher.getWorkspace().getViewTreeObserver(); obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { if (obs.isAlive()) { onPreDrawAfterResume(); obs.removeOnPreDrawListener(this); } return true; } }); } /** * Called when launcher activity is paused */ public void onPause() { mIsReadyToPlayAnimation = false; if (mIsTransitionRunning) { mIsTransitionRunning = false; mMoveFromCenterWorkspaceAnimation.onTransitionFinished(); } } /** * Called when launcher activity is destroyed */ public void onDestroy() { mUnfoldTransitionProgressProvider.removeCallback(mAnimationListener); } /** * Called after performing layouting of the views after configuration change */ private void onPreDrawAfterResume() { mIsReadyToPlayAnimation = true; if (mIsTransitionRunning) { mMoveFromCenterWorkspaceAnimation.onTransitionStarted(); } } private class AnimationListener implements TransitionProgressListener { @Override public void onTransitionStarted() { mIsTransitionRunning = true; if (mIsReadyToPlayAnimation) { mMoveFromCenterWorkspaceAnimation.onTransitionStarted(); } } @Override public void onTransitionFinished() { if (mIsReadyToPlayAnimation) { mMoveFromCenterWorkspaceAnimation.onTransitionFinished(); } mIsTransitionRunning = false; } @Override public void onTransitionProgress(float progress) { mMoveFromCenterWorkspaceAnimation.onTransitionProgress(progress); } } }
quickstep/src/com/android/quickstep/util/ProxyScreenStatusProvider.java 0 → 100644 +51 −0 Original line number Diff line number Diff line /* * 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 androidx.annotation.NonNull; import com.android.unfold.updates.screen.ScreenStatusProvider; import java.util.ArrayList; import java.util.List; /** * Screen status provider implementation that exposes methods to provide screen * status updates to listeners. It is used to receive screen turned on event from * SystemUI to Launcher. */ public class ProxyScreenStatusProvider implements ScreenStatusProvider { public static final ProxyScreenStatusProvider INSTANCE = new ProxyScreenStatusProvider(); private final List<ScreenListener> mListeners = new ArrayList<>(); /** * Called when the screen is on and ready (windows are drawn and screen blocker is removed) */ public void onScreenTurnedOn() { mListeners.forEach(ScreenListener::onScreenTurnedOn); } @Override public void addCallback(@NonNull ScreenListener listener) { mListeners.add(listener); } @Override public void removeCallback(@NonNull ScreenListener listener) { mListeners.remove(listener); } }
quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java 0 → 100644 +140 −0 Original line number Diff line number Diff line /* * 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 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.unfold.UnfoldTransitionProgressProvider; import java.util.HashMap; import java.util.Map; /** * Animation that moves launcher icons and widgets from center to the sides (final position) */ public class UnfoldMoveFromCenterWorkspaceAnimator implements UnfoldTransitionProgressProvider.TransitionProgressListener { private final Launcher mLauncher; private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation; private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>(); private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>(); public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager) { mLauncher = launcher; mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager, new WorkspaceViewsTranslationApplier()); } @Override public void onTransitionStarted() { mMoveFromCenterAnimation.updateDisplayProperties(); Workspace workspace = mLauncher.getWorkspace(); Hotseat hotseat = mLauncher.getHotseat(); // App icons and widgets workspace .forEachVisiblePage(page -> { final CellLayout cellLayout = (CellLayout) page; ShortcutAndWidgetContainer itemsContainer = cellLayout .getShortcutsAndWidgets(); disableClipping(cellLayout); for (int i = 0; i < itemsContainer.getChildCount(); i++) { View child = itemsContainer.getChildAt(i); mMoveFromCenterAnimation.registerViewForAnimation(child); } }); disableClipping(workspace); // Hotseat icons ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets(); disableClipping(hotseat); for (int i = 0; i < hotseatIcons.getChildCount(); i++) { View child = hotseatIcons.getChildAt(i); mMoveFromCenterAnimation.registerViewForAnimation(child); } onTransitionProgress(0f); } @Override public void onTransitionProgress(float progress) { mMoveFromCenterAnimation.onTransitionProgress(progress); } @Override public void onTransitionFinished() { mMoveFromCenterAnimation.onTransitionFinished(); mMoveFromCenterAnimation.clearRegisteredViews(); restoreClipping(mLauncher.getWorkspace()); mLauncher.getWorkspace().forEachVisiblePage(page -> restoreClipping((CellLayout) page)); restoreClipping(mLauncher.getHotseat()); mOriginalClipChildren.clear(); mOriginalClipToPadding.clear(); } private void disableClipping(ViewGroup view) { mOriginalClipToPadding.put(view, view.getClipToPadding()); mOriginalClipChildren.put(view, view.getClipChildren()); view.setClipToPadding(false); view.setClipChildren(false); } private void restoreClipping(ViewGroup view) { final Boolean originalClipToPadding = mOriginalClipToPadding.get(view); if (originalClipToPadding != null) { view.setClipToPadding(originalClipToPadding); } final Boolean originalClipChildren = mOriginalClipChildren.get(view); if (originalClipChildren != null) { 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); } } } }