Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +35 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ import android.graphics.drawable.Icon; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; Loading Loading @@ -390,6 +391,7 @@ public class BubbleController implements ConfigurationChangeListener, mSyncQueue = syncQueue; mWmService = wmService; mBubbleTransitions = bubbleTransitions; mBubbleTransitions.setBubbleController(this); mBubbleTaskViewFactory = new BubbleTaskViewFactory() { @Override public BubbleTaskView create() { Loading Loading @@ -1639,6 +1641,39 @@ public class BubbleController implements ConfigurationChangeListener, } } /** * Expands and selects a bubble created from a running task in a different mode. * * @param taskInfo the task. */ @Nullable public Transitions.TransitionHandler expandStackAndSelectBubbleForExistingTransition( @NonNull ActivityManager.RunningTaskInfo taskInfo, @NonNull IBinder transition, Consumer<Transitions.TransitionHandler> onInflatedCallback) { if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) return null; // If there is an existing bubble then just show it final String taskKey = Bubble.getAppBubbleKeyForTask(taskInfo); if (mBubbleData.hasAnyBubbleWithKey(taskKey)) { ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubbleForExistingTransition(): " + "skipping due to existing bubbled task=%d", taskInfo.taskId); return null; } // Otherwise, create a new bubble and show it Bubble b = mBubbleData.getOrCreateBubble(taskInfo); // Removes from overflow ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubbleForExistingTransition() taskId=%s", taskInfo.taskId); b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE); // Lazy init stack view when a bubble is created ensureBubbleViewsAndWindowCreated(); return mBubbleTransitions.startLaunchNewTaskBubbleForExistingTransition(b, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, mStackView, mLayerView, mBubbleIconFactory, mInflateSynchronously, transition, onInflatedCallback); } /** * Expands and selects a bubble based on the provided {@link BubbleEntry}. If no bubble * exists for this entry, and it is able to bubble, a new bubble will be created. Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskStackListener.kt +9 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.internal.protolog.ProtoLog import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.TaskStackListenerCallback import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES_NOISY import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper import com.android.wm.shell.taskview.TaskViewTaskController Loading @@ -50,6 +51,10 @@ class BubbleTaskStackListener( clearedTask: Boolean, wasVisible: Boolean, ) { ProtoLog.d( WM_SHELL_BUBBLES_NOISY, "BubbleTaskStackListener.onActivityRestartAttempt(): taskId=%d", task.taskId) val taskId = task.taskId bubbleData.getBubbleInStackWithTaskId(taskId)?.let { bubble -> if (isBubbleToFullscreen(task)) { Loading Loading @@ -133,6 +138,10 @@ class BubbleTaskStackListener( bubble: Bubble, task: ActivityManager.RunningTaskInfo, ) { ProtoLog.d( WM_SHELL_BUBBLES_NOISY, "BubbleTaskStackListener.collapsedBubbleToFullscreenInternal(): taskId=%d", task.taskId) val taskViewTaskController: TaskViewTaskController = bubble.taskView.controller val taskOrganizer: ShellTaskOrganizer = taskViewTaskController.taskOrganizer Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java +301 −12 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ import com.android.wm.shell.transition.Transitions.TransitionHandler; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executor; import java.util.function.Consumer; /** * Implements transition coordination for bubble operations. Loading Loading @@ -107,6 +108,8 @@ public class BubbleTransitions { private final Map<IBinder, TransitionHandler> mEnterTransitions = new HashMap<>(); private BubbleController mBubbleController; public BubbleTransitions(Context context, @NonNull Transitions transitions, @NonNull ShellTaskOrganizer organizer, @NonNull TaskViewRepository repository, @NonNull BubbleData bubbleData, Loading @@ -120,6 +123,10 @@ public class BubbleTransitions { mContext = context; } void setBubbleController(BubbleController controller) { mBubbleController = controller; } /** * Returns whether there is a pending transition for the given request. */ Loading Loading @@ -181,6 +188,37 @@ public class BubbleTransitions { stackView, layerView, iconFactory, inflateSync); } /** * Called to initiate axed bubble-to-bubble launch/convert for the given transition. * * @return whether a new transition was started for the launch */ public boolean startBubbleToBubbleLaunch(@NonNull IBinder transition, @NonNull ActivityManager.RunningTaskInfo launchingTask, @NonNull Consumer<TransitionHandler> onInflatedCallback) { TransitionHandler handler = mBubbleController.expandStackAndSelectBubbleForExistingTransition( launchingTask, transition, onInflatedCallback); if (handler != null) { mEnterTransitions.put(transition, handler); } return handler != null; } /** * Starts a new launch or convert transition to show the given bubble. */ public TransitionHandler startLaunchNewTaskBubbleForExistingTransition(Bubble bubble, BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory, BubblePositioner positioner, BubbleStackView stackView, BubbleBarLayerView layerView, BubbleIconFactory iconFactory, boolean inflateSync, IBinder transition, Consumer<TransitionHandler> onInflatedCallback) { return new LaunchNewTaskBubbleForExistingTransition(bubble, mContext, expandedViewManager, factory, positioner, stackView, layerView, iconFactory, inflateSync, transition, onInflatedCallback); } /** * Starts a convert-to-bubble transition. * Loading Loading @@ -252,7 +290,7 @@ public class BubbleTransitions { } /** * Information about the task when it is being dragged to a bubble * Information about the task when it is being dragged to a bubble. */ public static class DragData { private final boolean mReleasedOnLeft; Loading Loading @@ -304,12 +342,14 @@ public class BubbleTransitions { } /** * Keeps track of internal state of different steps of a BubbleTransition. * Keeps track of internal state of different steps of a BubbleTransition. Serves as a gating * mechanism to block animations or updates until necessary states are set. */ private static class TransitionProgress { private final Bubble mBubble; private boolean mTransitionReady; private boolean mInflated; private boolean mReadyToExpand; private boolean mSurfaceReady; Loading @@ -317,34 +357,287 @@ public class BubbleTransitions { mBubble = bubble; } void setInflated() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setInflated()"); mInflated = true; onUpdate(); } void setTransitionReady() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setTransitionReady()"); mTransitionReady = true; onUpdate(); } void setReadyToExpand() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setReadyToExpand()"); mReadyToExpand = true; onUpdate(); } void setSurfaceReady() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setSurfaceReady()"); mSurfaceReady = true; onUpdate(); } boolean isReadyToAnimate() { // Animation only depends on transition and surface state return mTransitionReady && mSurfaceReady; return mTransitionReady && mSurfaceReady && mInflated; } private void onUpdate() { if (mTransitionReady && mReadyToExpand && mSurfaceReady) { if (mTransitionReady && mReadyToExpand && mSurfaceReady && mInflated) { // Clear the transition from bubble when all the steps are ready mBubble.setPreparingTransition(null); } } } /** * Starts a new bubble for an existing playing transition. * TODO(b/408328557): To be consolidated with LaunchOrConvertToBubble and ConvertToBubble */ @VisibleForTesting class LaunchNewTaskBubbleForExistingTransition implements TransitionHandler, BubbleTransition { final BubbleBarLayerView mLayerView; private final TransitionProgress mTransitionProgress; Bubble mBubble; IBinder mTransition; Transitions.TransitionFinishCallback mFinishCb; WindowContainerTransaction mFinishWct = null; final Rect mStartBounds = new Rect(); SurfaceControl mSnapshot = null; // The task info is resolved once we find the task from the transition info using the // pending launch cookie otherwise @Nullable TaskInfo mTaskInfo; BubbleViewProvider mPriorBubble = null; // Whether we should play the convert-task animation, or the launch-task animation private boolean mPlayConvertTaskAnimation; private SurfaceControl.Transaction mFinishT; private SurfaceControl mTaskLeash; LaunchNewTaskBubbleForExistingTransition(Bubble bubble, Context context, BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory, BubblePositioner positioner, BubbleStackView stackView, BubbleBarLayerView layerView, BubbleIconFactory iconFactory, boolean inflateSync, IBinder transition, Consumer<TransitionHandler> onInflatedCallback) { mBubble = bubble; mTransition = transition; mTransitionProgress = new TransitionProgress(bubble); mLayerView = layerView; mBubble.setInflateSynchronously(inflateSync); mBubble.setPreparingTransition(this); mBubble.inflate( b -> { onInflated(b); onInflatedCallback.accept(LaunchNewTaskBubbleForExistingTransition.this); }, context, expandedViewManager, factory, positioner, stackView, layerView, iconFactory, false /* skipInflation */); } @VisibleForTesting void onInflated(Bubble b) { if (b != mBubble) { throw new IllegalArgumentException("inflate callback doesn't match bubble"); } if (!mBubble.isShortcut() && !mBubble.isApp()) { throw new IllegalArgumentException("Unsupported bubble type"); } final Rect launchBounds = new Rect(); mLayerView.getExpandedViewRestBounds(launchBounds); final TaskView tv = b.getTaskView(); tv.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT); final TaskViewRepository.TaskViewState state = mRepository.byTaskView( tv.getController()); if (state != null) { state.mVisible = true; } mTransitionProgress.setInflated(); mTaskViewTransitions.enqueueExternal(tv.getController(), () -> { return mTransition; }); } @Override public void skip() { mBubble.setPreparingTransition(null); cleanup(); } @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @Nullable TransitionRequestInfo request) { return null; } @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback) { } @Override public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, @NonNull SurfaceControl.Transaction finishTransaction) { if (!aborted) return; mTaskViewTransitions.onExternalDone(mTransition); mTransition = null; } @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { // Identify the task that we are converting or launching. Note, we iterate back to front // so that we can adjust alpha for revealed surfaces as needed. boolean found = false; mPlayConvertTaskAnimation = false; for (int i = info.getChanges().size() - 1; i >= 0; i--) { final TransitionInfo.Change chg = info.getChanges().get(i); final boolean isTaskToConvertToBubble = (chg.getTaskInfo() != null) && (chg.getMode() == TRANSIT_CHANGE || isOpeningMode(chg.getMode())); if (isTaskToConvertToBubble) { mStartBounds.set(chg.getStartAbsBounds()); // Converting a task into taskview, so treat as "new" mFinishWct = new WindowContainerTransaction(); mTaskInfo = chg.getTaskInfo(); mFinishT = finishTransaction; mTaskLeash = chg.getLeash(); mSnapshot = chg.getSnapshot(); // TODO: This should be set for the CHANGE transition, but for some reason there // is no snapshot, so fallback to the open transition for now mPlayConvertTaskAnimation = false; found = true; } else { // In core-initiated launches, the transition is of an OPEN type, and we need to // manually show the surfaces behind the newly bubbled task if (info.getType() == TRANSIT_OPEN && isOpeningMode(chg.getMode())) { startTransaction.setAlpha(chg.getLeash(), 1f); } } } if (!found) { Slog.w(TAG, "Expected a TaskView conversion in this transition but didn't get " + "one, cleaning up the task view"); mBubble.getTaskView().getController().setTaskNotFound(); mTaskViewTransitions.onExternalDone(mTransition); return false; } mFinishCb = finishCallback; // Now update state (and talk to launcher) in parallel with snapshot stuff mBubbleData.notificationEntryUpdated(mBubble, /* suppressFlyout= */ true, /* showInShade= */ false); if (mPlayConvertTaskAnimation) { final int left = mStartBounds.left - info.getRoot(0).getOffset().x; final int top = mStartBounds.top - info.getRoot(0).getOffset().y; startTransaction.setPosition(mTaskLeash, left, top); startTransaction.show(mSnapshot); // Move snapshot to root so that it remains visible while task is moved to taskview startTransaction.reparent(mSnapshot, info.getRoot(0).getLeash()); startTransaction.setPosition(mSnapshot, left, top); startTransaction.setLayer(mSnapshot, Integer.MAX_VALUE); } else { final int left = mStartBounds.left - info.getRoot(0).getOffset().x; final int top = mStartBounds.top - info.getRoot(0).getOffset().y; startTransaction.setPosition(mTaskLeash, left, top); } startTransaction.apply(); mTaskViewTransitions.onExternalDone(mTransition); mTransitionProgress.setTransitionReady(); startExpandAnim(); return true; } private void startExpandAnim() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.startExpandAnim(): " + "readyToAnimate=%b", mTransitionProgress.isReadyToAnimate()); if (mLayerView.canExpandView(mBubble)) { mPriorBubble = mLayerView.prepareConvertedView(mBubble); } else if (mLayerView.isExpanded()) { mTransitionProgress.setReadyToExpand(); } if (mTransitionProgress.isReadyToAnimate()) { playAnimation(); } } @Override public void continueExpand() { mTransitionProgress.setReadyToExpand(); } @Override public void surfaceCreated() { mTransitionProgress.setSurfaceReady(); mMainExecutor.execute(() -> { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.surfaceCreated(): " + "mTaskLeash=%s", mTaskLeash); final TaskViewTaskController tvc = mBubble.getTaskView().getController(); final TaskViewRepository.TaskViewState state = mRepository.byTaskView(tvc); if (state == null) return; state.mVisible = true; if (mTransitionProgress.isReadyToAnimate()) { playAnimation(); } }); } private void playAnimation() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation()"); final TaskViewTaskController tv = mBubble.getTaskView().getController(); final SurfaceControl.Transaction startT = new SurfaceControl.Transaction(); // Set task position to 0,0 as it will be placed inside the TaskView startT.setPosition(mTaskLeash, 0, 0) .reparent(mTaskLeash, mBubble.getTaskView().getSurfaceControl()) .setAlpha(mTaskLeash, 1f) .show(mTaskLeash); mTaskViewTransitions.prepareOpenAnimation(tv, true /* new */, startT, mFinishT, (ActivityManager.RunningTaskInfo) mTaskInfo, mTaskLeash, mFinishWct); // Add the task view task listener manually since we aren't going through // TaskViewTransitions (which normally sets up the listener via a pending launch cookie mTaskOrganizer.addListenerForTaskId(tv, mTaskInfo.taskId); if (mFinishWct.isEmpty()) { mFinishWct = null; } float startScale = 1f; if (mPlayConvertTaskAnimation) { mLayerView.animateConvert(startT, mStartBounds, startScale, mSnapshot, mTaskLeash, this::cleanup); } else { startT.apply(); mLayerView.animateExpand(null, this::cleanup); } } private void cleanup() { mFinishCb.onTransitionFinished(mFinishWct); mFinishCb = null; } } /** * Starts a new transition into a bubble, which will either play a launch animation (if the task * was not previously visible) or a convert animation (if the task is currently visible). Loading @@ -369,7 +662,6 @@ public class BubbleTransitions { BubbleViewProvider mPriorBubble = null; // Whether we should play the convert-task animation, or the launch-task animation private boolean mPlayConvertTaskAnimation; private boolean mPlayingAnimation; private SurfaceControl.Transaction mFinishT; private SurfaceControl mTaskLeash; Loading Loading @@ -414,6 +706,7 @@ public class BubbleTransitions { if (state != null) { state.mVisible = true; } mTransitionProgress.setInflated(); mTaskViewTransitions.enqueueExternal(tv.getController(), () -> { // We need to convert the next launch into a bubble mLaunchCookie = new ActivityOptions.LaunchCookie(); Loading Loading @@ -596,11 +889,11 @@ public class BubbleTransitions { public void surfaceCreated() { mTransitionProgress.setSurfaceReady(); mMainExecutor.execute(() -> { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.surfaceCreated(): " + "mTaskLeash=%s", mTaskLeash); final TaskViewTaskController tvc = mBubble.getTaskView().getController(); final TaskViewRepository.TaskViewState state = mRepository.byTaskView(tvc); if (state == null) return; ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.surfaceCreated(): " + "mTaskLeash=%s", mTaskLeash); state.mVisible = true; if (mTransitionProgress.isReadyToAnimate()) { playAnimation(true /* animate */); Loading @@ -609,10 +902,6 @@ public class BubbleTransitions { } private void playAnimation(boolean animate) { if (mPlayingAnimation) { // Already playing return; } ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation(): animate=%b", animate); final TaskViewTaskController tv = mBubble.getTaskView().getController(); Loading Loading @@ -648,7 +937,6 @@ public class BubbleTransitions { startT.apply(); cleanup(); } mPlayingAnimation = true; } private void cleanup() { Loading Loading @@ -758,6 +1046,7 @@ public class BubbleTransitions { if (state != null) { state.mVisible = true; } mTransitionProgress.setInflated(); mTaskViewTransitions.enqueueExternal(tv.getController(), () -> { mTransition = mTransitions.startTransition(TRANSIT_CONVERT_TO_BUBBLE, wct, this); return mTransition; Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +1 −3 Original line number Diff line number Diff line Loading @@ -631,7 +631,6 @@ public abstract class WMShellModule { Optional<UnfoldTransitionHandler> unfoldHandler, Optional<ActivityEmbeddingController> activityEmbeddingController, BubbleTransitions bubbleTransitions, TaskViewTransitions taskViewTransitions, Transitions transitions) { return new DefaultMixedHandler( shellInit, Loading @@ -643,8 +642,7 @@ public abstract class WMShellModule { desktopTasksController, unfoldHandler, activityEmbeddingController, bubbleTransitions, taskViewTransitions); bubbleTransitions); } @WMSingleton Loading libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +36 −12 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +35 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ import android.graphics.drawable.Icon; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; Loading Loading @@ -390,6 +391,7 @@ public class BubbleController implements ConfigurationChangeListener, mSyncQueue = syncQueue; mWmService = wmService; mBubbleTransitions = bubbleTransitions; mBubbleTransitions.setBubbleController(this); mBubbleTaskViewFactory = new BubbleTaskViewFactory() { @Override public BubbleTaskView create() { Loading Loading @@ -1639,6 +1641,39 @@ public class BubbleController implements ConfigurationChangeListener, } } /** * Expands and selects a bubble created from a running task in a different mode. * * @param taskInfo the task. */ @Nullable public Transitions.TransitionHandler expandStackAndSelectBubbleForExistingTransition( @NonNull ActivityManager.RunningTaskInfo taskInfo, @NonNull IBinder transition, Consumer<Transitions.TransitionHandler> onInflatedCallback) { if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) return null; // If there is an existing bubble then just show it final String taskKey = Bubble.getAppBubbleKeyForTask(taskInfo); if (mBubbleData.hasAnyBubbleWithKey(taskKey)) { ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubbleForExistingTransition(): " + "skipping due to existing bubbled task=%d", taskInfo.taskId); return null; } // Otherwise, create a new bubble and show it Bubble b = mBubbleData.getOrCreateBubble(taskInfo); // Removes from overflow ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubbleForExistingTransition() taskId=%s", taskInfo.taskId); b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE); // Lazy init stack view when a bubble is created ensureBubbleViewsAndWindowCreated(); return mBubbleTransitions.startLaunchNewTaskBubbleForExistingTransition(b, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, mStackView, mLayerView, mBubbleIconFactory, mInflateSynchronously, transition, onInflatedCallback); } /** * Expands and selects a bubble based on the provided {@link BubbleEntry}. If no bubble * exists for this entry, and it is able to bubble, a new bubble will be created. Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskStackListener.kt +9 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.internal.protolog.ProtoLog import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.TaskStackListenerCallback import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES_NOISY import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper import com.android.wm.shell.taskview.TaskViewTaskController Loading @@ -50,6 +51,10 @@ class BubbleTaskStackListener( clearedTask: Boolean, wasVisible: Boolean, ) { ProtoLog.d( WM_SHELL_BUBBLES_NOISY, "BubbleTaskStackListener.onActivityRestartAttempt(): taskId=%d", task.taskId) val taskId = task.taskId bubbleData.getBubbleInStackWithTaskId(taskId)?.let { bubble -> if (isBubbleToFullscreen(task)) { Loading Loading @@ -133,6 +138,10 @@ class BubbleTaskStackListener( bubble: Bubble, task: ActivityManager.RunningTaskInfo, ) { ProtoLog.d( WM_SHELL_BUBBLES_NOISY, "BubbleTaskStackListener.collapsedBubbleToFullscreenInternal(): taskId=%d", task.taskId) val taskViewTaskController: TaskViewTaskController = bubble.taskView.controller val taskOrganizer: ShellTaskOrganizer = taskViewTaskController.taskOrganizer Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java +301 −12 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ import com.android.wm.shell.transition.Transitions.TransitionHandler; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executor; import java.util.function.Consumer; /** * Implements transition coordination for bubble operations. Loading Loading @@ -107,6 +108,8 @@ public class BubbleTransitions { private final Map<IBinder, TransitionHandler> mEnterTransitions = new HashMap<>(); private BubbleController mBubbleController; public BubbleTransitions(Context context, @NonNull Transitions transitions, @NonNull ShellTaskOrganizer organizer, @NonNull TaskViewRepository repository, @NonNull BubbleData bubbleData, Loading @@ -120,6 +123,10 @@ public class BubbleTransitions { mContext = context; } void setBubbleController(BubbleController controller) { mBubbleController = controller; } /** * Returns whether there is a pending transition for the given request. */ Loading Loading @@ -181,6 +188,37 @@ public class BubbleTransitions { stackView, layerView, iconFactory, inflateSync); } /** * Called to initiate axed bubble-to-bubble launch/convert for the given transition. * * @return whether a new transition was started for the launch */ public boolean startBubbleToBubbleLaunch(@NonNull IBinder transition, @NonNull ActivityManager.RunningTaskInfo launchingTask, @NonNull Consumer<TransitionHandler> onInflatedCallback) { TransitionHandler handler = mBubbleController.expandStackAndSelectBubbleForExistingTransition( launchingTask, transition, onInflatedCallback); if (handler != null) { mEnterTransitions.put(transition, handler); } return handler != null; } /** * Starts a new launch or convert transition to show the given bubble. */ public TransitionHandler startLaunchNewTaskBubbleForExistingTransition(Bubble bubble, BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory, BubblePositioner positioner, BubbleStackView stackView, BubbleBarLayerView layerView, BubbleIconFactory iconFactory, boolean inflateSync, IBinder transition, Consumer<TransitionHandler> onInflatedCallback) { return new LaunchNewTaskBubbleForExistingTransition(bubble, mContext, expandedViewManager, factory, positioner, stackView, layerView, iconFactory, inflateSync, transition, onInflatedCallback); } /** * Starts a convert-to-bubble transition. * Loading Loading @@ -252,7 +290,7 @@ public class BubbleTransitions { } /** * Information about the task when it is being dragged to a bubble * Information about the task when it is being dragged to a bubble. */ public static class DragData { private final boolean mReleasedOnLeft; Loading Loading @@ -304,12 +342,14 @@ public class BubbleTransitions { } /** * Keeps track of internal state of different steps of a BubbleTransition. * Keeps track of internal state of different steps of a BubbleTransition. Serves as a gating * mechanism to block animations or updates until necessary states are set. */ private static class TransitionProgress { private final Bubble mBubble; private boolean mTransitionReady; private boolean mInflated; private boolean mReadyToExpand; private boolean mSurfaceReady; Loading @@ -317,34 +357,287 @@ public class BubbleTransitions { mBubble = bubble; } void setInflated() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setInflated()"); mInflated = true; onUpdate(); } void setTransitionReady() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setTransitionReady()"); mTransitionReady = true; onUpdate(); } void setReadyToExpand() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setReadyToExpand()"); mReadyToExpand = true; onUpdate(); } void setSurfaceReady() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "TransitionProgress.setSurfaceReady()"); mSurfaceReady = true; onUpdate(); } boolean isReadyToAnimate() { // Animation only depends on transition and surface state return mTransitionReady && mSurfaceReady; return mTransitionReady && mSurfaceReady && mInflated; } private void onUpdate() { if (mTransitionReady && mReadyToExpand && mSurfaceReady) { if (mTransitionReady && mReadyToExpand && mSurfaceReady && mInflated) { // Clear the transition from bubble when all the steps are ready mBubble.setPreparingTransition(null); } } } /** * Starts a new bubble for an existing playing transition. * TODO(b/408328557): To be consolidated with LaunchOrConvertToBubble and ConvertToBubble */ @VisibleForTesting class LaunchNewTaskBubbleForExistingTransition implements TransitionHandler, BubbleTransition { final BubbleBarLayerView mLayerView; private final TransitionProgress mTransitionProgress; Bubble mBubble; IBinder mTransition; Transitions.TransitionFinishCallback mFinishCb; WindowContainerTransaction mFinishWct = null; final Rect mStartBounds = new Rect(); SurfaceControl mSnapshot = null; // The task info is resolved once we find the task from the transition info using the // pending launch cookie otherwise @Nullable TaskInfo mTaskInfo; BubbleViewProvider mPriorBubble = null; // Whether we should play the convert-task animation, or the launch-task animation private boolean mPlayConvertTaskAnimation; private SurfaceControl.Transaction mFinishT; private SurfaceControl mTaskLeash; LaunchNewTaskBubbleForExistingTransition(Bubble bubble, Context context, BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory, BubblePositioner positioner, BubbleStackView stackView, BubbleBarLayerView layerView, BubbleIconFactory iconFactory, boolean inflateSync, IBinder transition, Consumer<TransitionHandler> onInflatedCallback) { mBubble = bubble; mTransition = transition; mTransitionProgress = new TransitionProgress(bubble); mLayerView = layerView; mBubble.setInflateSynchronously(inflateSync); mBubble.setPreparingTransition(this); mBubble.inflate( b -> { onInflated(b); onInflatedCallback.accept(LaunchNewTaskBubbleForExistingTransition.this); }, context, expandedViewManager, factory, positioner, stackView, layerView, iconFactory, false /* skipInflation */); } @VisibleForTesting void onInflated(Bubble b) { if (b != mBubble) { throw new IllegalArgumentException("inflate callback doesn't match bubble"); } if (!mBubble.isShortcut() && !mBubble.isApp()) { throw new IllegalArgumentException("Unsupported bubble type"); } final Rect launchBounds = new Rect(); mLayerView.getExpandedViewRestBounds(launchBounds); final TaskView tv = b.getTaskView(); tv.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT); final TaskViewRepository.TaskViewState state = mRepository.byTaskView( tv.getController()); if (state != null) { state.mVisible = true; } mTransitionProgress.setInflated(); mTaskViewTransitions.enqueueExternal(tv.getController(), () -> { return mTransition; }); } @Override public void skip() { mBubble.setPreparingTransition(null); cleanup(); } @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @Nullable TransitionRequestInfo request) { return null; } @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback) { } @Override public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, @NonNull SurfaceControl.Transaction finishTransaction) { if (!aborted) return; mTaskViewTransitions.onExternalDone(mTransition); mTransition = null; } @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { // Identify the task that we are converting or launching. Note, we iterate back to front // so that we can adjust alpha for revealed surfaces as needed. boolean found = false; mPlayConvertTaskAnimation = false; for (int i = info.getChanges().size() - 1; i >= 0; i--) { final TransitionInfo.Change chg = info.getChanges().get(i); final boolean isTaskToConvertToBubble = (chg.getTaskInfo() != null) && (chg.getMode() == TRANSIT_CHANGE || isOpeningMode(chg.getMode())); if (isTaskToConvertToBubble) { mStartBounds.set(chg.getStartAbsBounds()); // Converting a task into taskview, so treat as "new" mFinishWct = new WindowContainerTransaction(); mTaskInfo = chg.getTaskInfo(); mFinishT = finishTransaction; mTaskLeash = chg.getLeash(); mSnapshot = chg.getSnapshot(); // TODO: This should be set for the CHANGE transition, but for some reason there // is no snapshot, so fallback to the open transition for now mPlayConvertTaskAnimation = false; found = true; } else { // In core-initiated launches, the transition is of an OPEN type, and we need to // manually show the surfaces behind the newly bubbled task if (info.getType() == TRANSIT_OPEN && isOpeningMode(chg.getMode())) { startTransaction.setAlpha(chg.getLeash(), 1f); } } } if (!found) { Slog.w(TAG, "Expected a TaskView conversion in this transition but didn't get " + "one, cleaning up the task view"); mBubble.getTaskView().getController().setTaskNotFound(); mTaskViewTransitions.onExternalDone(mTransition); return false; } mFinishCb = finishCallback; // Now update state (and talk to launcher) in parallel with snapshot stuff mBubbleData.notificationEntryUpdated(mBubble, /* suppressFlyout= */ true, /* showInShade= */ false); if (mPlayConvertTaskAnimation) { final int left = mStartBounds.left - info.getRoot(0).getOffset().x; final int top = mStartBounds.top - info.getRoot(0).getOffset().y; startTransaction.setPosition(mTaskLeash, left, top); startTransaction.show(mSnapshot); // Move snapshot to root so that it remains visible while task is moved to taskview startTransaction.reparent(mSnapshot, info.getRoot(0).getLeash()); startTransaction.setPosition(mSnapshot, left, top); startTransaction.setLayer(mSnapshot, Integer.MAX_VALUE); } else { final int left = mStartBounds.left - info.getRoot(0).getOffset().x; final int top = mStartBounds.top - info.getRoot(0).getOffset().y; startTransaction.setPosition(mTaskLeash, left, top); } startTransaction.apply(); mTaskViewTransitions.onExternalDone(mTransition); mTransitionProgress.setTransitionReady(); startExpandAnim(); return true; } private void startExpandAnim() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.startExpandAnim(): " + "readyToAnimate=%b", mTransitionProgress.isReadyToAnimate()); if (mLayerView.canExpandView(mBubble)) { mPriorBubble = mLayerView.prepareConvertedView(mBubble); } else if (mLayerView.isExpanded()) { mTransitionProgress.setReadyToExpand(); } if (mTransitionProgress.isReadyToAnimate()) { playAnimation(); } } @Override public void continueExpand() { mTransitionProgress.setReadyToExpand(); } @Override public void surfaceCreated() { mTransitionProgress.setSurfaceReady(); mMainExecutor.execute(() -> { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.surfaceCreated(): " + "mTaskLeash=%s", mTaskLeash); final TaskViewTaskController tvc = mBubble.getTaskView().getController(); final TaskViewRepository.TaskViewState state = mRepository.byTaskView(tvc); if (state == null) return; state.mVisible = true; if (mTransitionProgress.isReadyToAnimate()) { playAnimation(); } }); } private void playAnimation() { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation()"); final TaskViewTaskController tv = mBubble.getTaskView().getController(); final SurfaceControl.Transaction startT = new SurfaceControl.Transaction(); // Set task position to 0,0 as it will be placed inside the TaskView startT.setPosition(mTaskLeash, 0, 0) .reparent(mTaskLeash, mBubble.getTaskView().getSurfaceControl()) .setAlpha(mTaskLeash, 1f) .show(mTaskLeash); mTaskViewTransitions.prepareOpenAnimation(tv, true /* new */, startT, mFinishT, (ActivityManager.RunningTaskInfo) mTaskInfo, mTaskLeash, mFinishWct); // Add the task view task listener manually since we aren't going through // TaskViewTransitions (which normally sets up the listener via a pending launch cookie mTaskOrganizer.addListenerForTaskId(tv, mTaskInfo.taskId); if (mFinishWct.isEmpty()) { mFinishWct = null; } float startScale = 1f; if (mPlayConvertTaskAnimation) { mLayerView.animateConvert(startT, mStartBounds, startScale, mSnapshot, mTaskLeash, this::cleanup); } else { startT.apply(); mLayerView.animateExpand(null, this::cleanup); } } private void cleanup() { mFinishCb.onTransitionFinished(mFinishWct); mFinishCb = null; } } /** * Starts a new transition into a bubble, which will either play a launch animation (if the task * was not previously visible) or a convert animation (if the task is currently visible). Loading @@ -369,7 +662,6 @@ public class BubbleTransitions { BubbleViewProvider mPriorBubble = null; // Whether we should play the convert-task animation, or the launch-task animation private boolean mPlayConvertTaskAnimation; private boolean mPlayingAnimation; private SurfaceControl.Transaction mFinishT; private SurfaceControl mTaskLeash; Loading Loading @@ -414,6 +706,7 @@ public class BubbleTransitions { if (state != null) { state.mVisible = true; } mTransitionProgress.setInflated(); mTaskViewTransitions.enqueueExternal(tv.getController(), () -> { // We need to convert the next launch into a bubble mLaunchCookie = new ActivityOptions.LaunchCookie(); Loading Loading @@ -596,11 +889,11 @@ public class BubbleTransitions { public void surfaceCreated() { mTransitionProgress.setSurfaceReady(); mMainExecutor.execute(() -> { ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.surfaceCreated(): " + "mTaskLeash=%s", mTaskLeash); final TaskViewTaskController tvc = mBubble.getTaskView().getController(); final TaskViewRepository.TaskViewState state = mRepository.byTaskView(tvc); if (state == null) return; ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.surfaceCreated(): " + "mTaskLeash=%s", mTaskLeash); state.mVisible = true; if (mTransitionProgress.isReadyToAnimate()) { playAnimation(true /* animate */); Loading @@ -609,10 +902,6 @@ public class BubbleTransitions { } private void playAnimation(boolean animate) { if (mPlayingAnimation) { // Already playing return; } ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubbleTransitions.playAnimation(): animate=%b", animate); final TaskViewTaskController tv = mBubble.getTaskView().getController(); Loading Loading @@ -648,7 +937,6 @@ public class BubbleTransitions { startT.apply(); cleanup(); } mPlayingAnimation = true; } private void cleanup() { Loading Loading @@ -758,6 +1046,7 @@ public class BubbleTransitions { if (state != null) { state.mVisible = true; } mTransitionProgress.setInflated(); mTaskViewTransitions.enqueueExternal(tv.getController(), () -> { mTransition = mTransitions.startTransition(TRANSIT_CONVERT_TO_BUBBLE, wct, this); return mTransition; Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +1 −3 Original line number Diff line number Diff line Loading @@ -631,7 +631,6 @@ public abstract class WMShellModule { Optional<UnfoldTransitionHandler> unfoldHandler, Optional<ActivityEmbeddingController> activityEmbeddingController, BubbleTransitions bubbleTransitions, TaskViewTransitions taskViewTransitions, Transitions transitions) { return new DefaultMixedHandler( shellInit, Loading @@ -643,8 +642,7 @@ public abstract class WMShellModule { desktopTasksController, unfoldHandler, activityEmbeddingController, bubbleTransitions, taskViewTransitions); bubbleTransitions); } @WMSingleton Loading
libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +36 −12 File changed.Preview size limit exceeded, changes collapsed. Show changes