Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +4 −3 Original line number Diff line number Diff line Loading @@ -542,7 +542,7 @@ public class Bubble implements BubbleViewProvider { return (mMetadataShortcutId != null && !mMetadataShortcutId.isEmpty()); } BubbleTransitions.BubbleTransition getPreparingTransition() { public BubbleTransitions.BubbleTransition getPreparingTransition() { return mPreparingTransition; } Loading Loading @@ -572,7 +572,8 @@ public class Bubble implements BubbleViewProvider { mIntentActive = false; } private void cleanupTaskView() { /** Cleans-up the taskview associated with this bubble (possibly removing the task from wm) */ public void cleanupTaskView() { if (mBubbleTaskView != null) { mBubbleTaskView.cleanup(); mBubbleTaskView = null; Loading @@ -593,7 +594,7 @@ public class Bubble implements BubbleViewProvider { * <p>If we're switching between bar and floating modes, pass {@code false} on * {@code cleanupTaskView} to avoid recreating it in the new mode. */ void cleanupViews(boolean cleanupTaskView) { public void cleanupViews(boolean cleanupTaskView) { cleanupExpandedView(cleanupTaskView); mIconView = null; } Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +102 −2 Original line number Diff line number Diff line Loading @@ -40,9 +40,11 @@ import android.annotation.BinderThread; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.Notification; import android.app.NotificationChannel; import android.app.PendingIntent; import android.app.TaskInfo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; Loading Loading @@ -78,6 +80,8 @@ import android.view.WindowInsets; import android.view.WindowManager; import android.window.ScreenCapture; import android.window.ScreenCapture.SynchronousScreenCaptureListener; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.annotation.MainThread; import androidx.annotation.Nullable; Loading Loading @@ -360,7 +364,7 @@ public class BubbleController implements ConfigurationChangeListener, } else { tvTransitions = taskViewTransitions; } mTaskViewController = tvTransitions; mTaskViewController = new BubbleTaskViewController(tvTransitions); mBubbleTransitions = new BubbleTransitions(transitions, organizer, taskViewRepository, data, tvTransitions, context); mTransitions = transitions; Loading Loading @@ -2076,7 +2080,12 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void removeBubble(Bubble removedBubble) { if (mLayerView != null) { final BubbleTransitions.BubbleTransition bubbleTransit = removedBubble.getPreparingTransition(); mLayerView.removeBubble(removedBubble, () -> { if (bubbleTransit != null) { bubbleTransit.continueCollapse(); } if (!mBubbleData.hasBubbles() && !isStackExpanded()) { mLayerView.setVisibility(INVISIBLE); removeFromWindowManagerMaybe(); Loading Loading @@ -2702,7 +2711,18 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void collapseBubbles() { mMainExecutor.execute(() -> mController.collapseStack()); mMainExecutor.execute(() -> { if (mBubbleData.getSelectedBubble() instanceof Bubble) { if (((Bubble) mBubbleData.getSelectedBubble()).getPreparingTransition() != null) { // Currently preparing a transition which will, itself, collapse the bubble. // For transition preparation, the timing of bubble-collapse must be in // sync with the rest of the set-up. return; } } mController.collapseStack(); }); } @Override Loading Loading @@ -3094,4 +3114,84 @@ public class BubbleController implements ConfigurationChangeListener, return mKeyToShownInShadeMap.get(key); } } private class BubbleTaskViewController implements TaskViewController { private final TaskViewTransitions mBaseTransitions; BubbleTaskViewController(TaskViewTransitions baseTransitions) { mBaseTransitions = baseTransitions; } @Override public void registerTaskView(TaskViewTaskController tv) { mBaseTransitions.registerTaskView(tv); } @Override public void unregisterTaskView(TaskViewTaskController tv) { mBaseTransitions.unregisterTaskView(tv); } @Override public void startShortcutActivity(@NonNull TaskViewTaskController destination, @NonNull ShortcutInfo shortcut, @NonNull ActivityOptions options, @Nullable Rect launchBounds) { mBaseTransitions.startShortcutActivity(destination, shortcut, options, launchBounds); } @Override public void startActivity(@NonNull TaskViewTaskController destination, @NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent, @NonNull ActivityOptions options, @Nullable Rect launchBounds) { mBaseTransitions.startActivity(destination, pendingIntent, fillInIntent, options, launchBounds); } @Override public void startRootTask(@NonNull TaskViewTaskController destination, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash, @Nullable WindowContainerTransaction wct) { mBaseTransitions.startRootTask(destination, taskInfo, leash, wct); } @Override public void removeTaskView(@NonNull TaskViewTaskController taskView, @Nullable WindowContainerToken taskToken) { mBaseTransitions.removeTaskView(taskView, taskToken); } @Override public void moveTaskViewToFullscreen(@NonNull TaskViewTaskController taskView) { final TaskInfo tinfo = taskView.getTaskInfo(); if (tinfo == null) { return; } Bubble bub = null; for (Bubble b : mBubbleData.getBubbles()) { if (b.getTaskId() == tinfo.taskId) { bub = b; break; } } if (bub == null) { return; } mBubbleTransitions.startConvertFromBubble(bub, tinfo); } @Override public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) { mBaseTransitions.setTaskViewVisible(taskView, visible); } @Override public void setTaskBounds(TaskViewTaskController taskView, Rect boundsOnScreen) { mBaseTransitions.setTaskBounds(taskView, boundsOnScreen); } @Override public boolean isUsingShellTransitions() { return mBaseTransitions.isUsingShellTransitions(); } } } libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java +199 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.wm.shell.bubbles; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.View.INVISIBLE; import static android.view.WindowManager.TRANSIT_CHANGE; import android.annotation.NonNull; Loading @@ -30,8 +32,10 @@ import android.os.IBinder; import android.util.Slog; import android.view.SurfaceControl; import android.view.SurfaceView; import android.view.View; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; Loading @@ -53,6 +57,12 @@ import java.util.concurrent.Executor; public class BubbleTransitions { private static final String TAG = "BubbleTransitions"; /** * Multiplier used to convert a view elevation to an "equivalent" shadow-radius. This is the * same multiple used by skia and surface-outsets in WMS. */ private static final float ELEVATION_TO_RADIUS = 2; @NonNull final Transitions mTransitions; @NonNull final ShellTaskOrganizer mTaskOrganizer; @NonNull final TaskViewRepository mRepository; Loading Loading @@ -89,6 +99,44 @@ public class BubbleTransitions { return convert; } /** * Starts a convert-from-bubble transition. * * @see ConvertFromBubble */ public BubbleTransition startConvertFromBubble(Bubble bubble, TaskInfo taskInfo) { ConvertFromBubble convert = new ConvertFromBubble(bubble, taskInfo); return convert; } /** * Plucks the task-surface out of an ancestor view while making the view invisible. This helper * attempts to do this seamlessly (ie. view becomes invisible in sync with task reparent). */ private void pluck(SurfaceControl taskLeash, View fromView, SurfaceControl dest, float destX, float destY, float cornerRadius, SurfaceControl.Transaction t, Runnable onPlucked) { SurfaceControl.Transaction pluckT = new SurfaceControl.Transaction(); pluckT.reparent(taskLeash, dest); t.reparent(taskLeash, dest); pluckT.setPosition(taskLeash, destX, destY); t.setPosition(taskLeash, destX, destY); pluckT.show(taskLeash); pluckT.setAlpha(taskLeash, 1.f); float shadowRadius = fromView.getElevation() * ELEVATION_TO_RADIUS; pluckT.setShadowRadius(taskLeash, shadowRadius); pluckT.setCornerRadius(taskLeash, cornerRadius); t.setShadowRadius(taskLeash, shadowRadius); t.setCornerRadius(taskLeash, cornerRadius); // Need to remove the taskview AFTER applying the startTransaction because it isn't // synchronized. pluckT.addTransactionCommittedListener(mMainExecutor, onPlucked::run); fromView.getViewRootImpl().applyTransactionOnDraw(pluckT); fromView.setVisibility(INVISIBLE); } /** * Interface to a bubble-specific transition. Bubble transitions have a multi-step lifecycle * in order to coordinate with the bubble view logic. These steps are communicated on this Loading @@ -98,6 +146,7 @@ public class BubbleTransitions { default void surfaceCreated() {} default void continueExpand() {} void skip(); default void continueCollapse() {} } /** Loading Loading @@ -316,4 +365,154 @@ public class BubbleTransitions { } } } /** * BubbleTransition that coordinates the setup for moving a task out of a bubble. The actual * animation is owned by the "receiver" of the task; however, because Bubbles uses TaskView, * we need to do some extra coordination work to get the task surface out of the view * "seamlessly". * * The process here looks like: * 1. Send transition to WM for leaving bubbles mode * 2. in startAnimation, set-up a "pluck" operation to pull the task surface out of taskview * 3. Once "plucked", remove the view (calls continueCollapse when surfaces can be cleaned-up) * 4. Then re-dispatch the transition animation so that the "receiver" can animate it. * * So, constructor -> startAnimation -> continueCollapse -> re-dispatch. */ @VisibleForTesting class ConvertFromBubble implements Transitions.TransitionHandler, BubbleTransition { @NonNull final Bubble mBubble; IBinder mTransition; TaskInfo mTaskInfo; SurfaceControl mTaskLeash; SurfaceControl mRootLeash; ConvertFromBubble(@NonNull Bubble bubble, TaskInfo taskInfo) { mBubble = bubble; mTaskInfo = taskInfo; mBubble.setPreparingTransition(this); WindowContainerTransaction wct = new WindowContainerTransaction(); WindowContainerToken token = mTaskInfo.getToken(); wct.setWindowingMode(token, WINDOWING_MODE_UNDEFINED); wct.setAlwaysOnTop(token, false); mTaskOrganizer.setInterceptBackPressedOnTaskRoot(token, false); mTaskViewTransitions.enqueueExternal( mBubble.getTaskView().getController(), () -> { mTransition = mTransitions.startTransition(TRANSIT_CHANGE, wct, this); return mTransition; }); } @Override public void skip() { mBubble.setPreparingTransition(null); final TaskViewTaskController tv = mBubble.getTaskView().getController(); tv.notifyTaskRemovalStarted(tv.getTaskInfo()); mTaskLeash = null; } @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @android.annotation.Nullable TransitionRequestInfo request) { return null; } @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback) { } @Override public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, @NonNull SurfaceControl.Transaction finishTransaction) { if (!aborted) return; mTransition = null; skip(); mTaskViewTransitions.onExternalDone(transition); } @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { if (mTransition != transition) return false; final TaskViewTaskController tv = mBubble.getTaskView().getController(); if (tv == null) { mTaskViewTransitions.onExternalDone(transition); return false; } TransitionInfo.Change taskChg = null; boolean found = false; for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change chg = info.getChanges().get(i); if (chg.getTaskInfo() == null) continue; if (chg.getMode() != TRANSIT_CHANGE) continue; if (!mTaskInfo.token.equals(chg.getTaskInfo().token)) continue; found = true; mRepository.remove(tv); taskChg = chg; break; } if (!found) { Slog.w(TAG, "Expected a TaskView conversion in this transition but didn't get " + "one, cleaning up the task view"); tv.setTaskNotFound(); skip(); mTaskViewTransitions.onExternalDone(transition); return false; } mTaskLeash = taskChg.getLeash(); mRootLeash = info.getRoot(0).getLeash(); SurfaceControl dest = mBubble.getBubbleBarExpandedView().getViewRootImpl().getSurfaceControl(); final Runnable onPlucked = () -> { // Need to remove the taskview AFTER applying the startTransaction because // it isn't synchronized. tv.notifyTaskRemovalStarted(tv.getTaskInfo()); // Unset after removeView so it can be used to pick a different animation. mBubble.setPreparingTransition(null); mBubbleData.setExpanded(false /* expanded */); }; if (dest != null) { pluck(mTaskLeash, mBubble.getBubbleBarExpandedView(), dest, taskChg.getStartAbsBounds().left - info.getRoot(0).getOffset().x, taskChg.getStartAbsBounds().top - info.getRoot(0).getOffset().y, mBubble.getBubbleBarExpandedView().getCornerRadius(), startTransaction, onPlucked); mBubble.getBubbleBarExpandedView().post(() -> mTransitions.dispatchTransition( mTransition, info, startTransaction, finishTransaction, finishCallback, null)); } else { onPlucked.run(); mTransitions.dispatchTransition(mTransition, info, startTransaction, finishTransaction, finishCallback, null); } mTaskViewTransitions.onExternalDone(transition); return true; } @Override public void continueCollapse() { mBubble.cleanupTaskView(); if (mTaskLeash == null) return; SurfaceControl.Transaction t = new SurfaceControl.Transaction(); t.reparent(mTaskLeash, mRootLeash); t.apply(); } } } libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +3 −1 Original line number Diff line number Diff line Loading @@ -355,8 +355,10 @@ public class BubbleBarLayerView extends FrameLayout /** Removes the given {@code bubble}. */ public void removeBubble(Bubble bubble, Runnable endAction) { final boolean inTransition = bubble.getPreparingTransition() != null; Runnable cleanUp = () -> { bubble.cleanupViews(); // The transition is already managing the task/wm state. bubble.cleanupViews(!inTransition); endAction.run(); }; if (mBubbleData.getBubbles().isEmpty()) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +2 −1 Original line number Diff line number Diff line Loading @@ -417,7 +417,8 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { } } void notifyTaskRemovalStarted(@NonNull ActivityManager.RunningTaskInfo taskInfo) { /** Notifies listeners of a task being removed. */ public void notifyTaskRemovalStarted(@NonNull ActivityManager.RunningTaskInfo taskInfo) { if (mListener == null) return; final int taskId = taskInfo.taskId; mListenerExecutor.execute(() -> mListener.onTaskRemovalStarted(taskId)); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +4 −3 Original line number Diff line number Diff line Loading @@ -542,7 +542,7 @@ public class Bubble implements BubbleViewProvider { return (mMetadataShortcutId != null && !mMetadataShortcutId.isEmpty()); } BubbleTransitions.BubbleTransition getPreparingTransition() { public BubbleTransitions.BubbleTransition getPreparingTransition() { return mPreparingTransition; } Loading Loading @@ -572,7 +572,8 @@ public class Bubble implements BubbleViewProvider { mIntentActive = false; } private void cleanupTaskView() { /** Cleans-up the taskview associated with this bubble (possibly removing the task from wm) */ public void cleanupTaskView() { if (mBubbleTaskView != null) { mBubbleTaskView.cleanup(); mBubbleTaskView = null; Loading @@ -593,7 +594,7 @@ public class Bubble implements BubbleViewProvider { * <p>If we're switching between bar and floating modes, pass {@code false} on * {@code cleanupTaskView} to avoid recreating it in the new mode. */ void cleanupViews(boolean cleanupTaskView) { public void cleanupViews(boolean cleanupTaskView) { cleanupExpandedView(cleanupTaskView); mIconView = null; } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +102 −2 Original line number Diff line number Diff line Loading @@ -40,9 +40,11 @@ import android.annotation.BinderThread; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.Notification; import android.app.NotificationChannel; import android.app.PendingIntent; import android.app.TaskInfo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; Loading Loading @@ -78,6 +80,8 @@ import android.view.WindowInsets; import android.view.WindowManager; import android.window.ScreenCapture; import android.window.ScreenCapture.SynchronousScreenCaptureListener; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.annotation.MainThread; import androidx.annotation.Nullable; Loading Loading @@ -360,7 +364,7 @@ public class BubbleController implements ConfigurationChangeListener, } else { tvTransitions = taskViewTransitions; } mTaskViewController = tvTransitions; mTaskViewController = new BubbleTaskViewController(tvTransitions); mBubbleTransitions = new BubbleTransitions(transitions, organizer, taskViewRepository, data, tvTransitions, context); mTransitions = transitions; Loading Loading @@ -2076,7 +2080,12 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void removeBubble(Bubble removedBubble) { if (mLayerView != null) { final BubbleTransitions.BubbleTransition bubbleTransit = removedBubble.getPreparingTransition(); mLayerView.removeBubble(removedBubble, () -> { if (bubbleTransit != null) { bubbleTransit.continueCollapse(); } if (!mBubbleData.hasBubbles() && !isStackExpanded()) { mLayerView.setVisibility(INVISIBLE); removeFromWindowManagerMaybe(); Loading Loading @@ -2702,7 +2711,18 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void collapseBubbles() { mMainExecutor.execute(() -> mController.collapseStack()); mMainExecutor.execute(() -> { if (mBubbleData.getSelectedBubble() instanceof Bubble) { if (((Bubble) mBubbleData.getSelectedBubble()).getPreparingTransition() != null) { // Currently preparing a transition which will, itself, collapse the bubble. // For transition preparation, the timing of bubble-collapse must be in // sync with the rest of the set-up. return; } } mController.collapseStack(); }); } @Override Loading Loading @@ -3094,4 +3114,84 @@ public class BubbleController implements ConfigurationChangeListener, return mKeyToShownInShadeMap.get(key); } } private class BubbleTaskViewController implements TaskViewController { private final TaskViewTransitions mBaseTransitions; BubbleTaskViewController(TaskViewTransitions baseTransitions) { mBaseTransitions = baseTransitions; } @Override public void registerTaskView(TaskViewTaskController tv) { mBaseTransitions.registerTaskView(tv); } @Override public void unregisterTaskView(TaskViewTaskController tv) { mBaseTransitions.unregisterTaskView(tv); } @Override public void startShortcutActivity(@NonNull TaskViewTaskController destination, @NonNull ShortcutInfo shortcut, @NonNull ActivityOptions options, @Nullable Rect launchBounds) { mBaseTransitions.startShortcutActivity(destination, shortcut, options, launchBounds); } @Override public void startActivity(@NonNull TaskViewTaskController destination, @NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent, @NonNull ActivityOptions options, @Nullable Rect launchBounds) { mBaseTransitions.startActivity(destination, pendingIntent, fillInIntent, options, launchBounds); } @Override public void startRootTask(@NonNull TaskViewTaskController destination, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash, @Nullable WindowContainerTransaction wct) { mBaseTransitions.startRootTask(destination, taskInfo, leash, wct); } @Override public void removeTaskView(@NonNull TaskViewTaskController taskView, @Nullable WindowContainerToken taskToken) { mBaseTransitions.removeTaskView(taskView, taskToken); } @Override public void moveTaskViewToFullscreen(@NonNull TaskViewTaskController taskView) { final TaskInfo tinfo = taskView.getTaskInfo(); if (tinfo == null) { return; } Bubble bub = null; for (Bubble b : mBubbleData.getBubbles()) { if (b.getTaskId() == tinfo.taskId) { bub = b; break; } } if (bub == null) { return; } mBubbleTransitions.startConvertFromBubble(bub, tinfo); } @Override public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) { mBaseTransitions.setTaskViewVisible(taskView, visible); } @Override public void setTaskBounds(TaskViewTaskController taskView, Rect boundsOnScreen) { mBaseTransitions.setTaskBounds(taskView, boundsOnScreen); } @Override public boolean isUsingShellTransitions() { return mBaseTransitions.isUsingShellTransitions(); } } }
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java +199 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.wm.shell.bubbles; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.View.INVISIBLE; import static android.view.WindowManager.TRANSIT_CHANGE; import android.annotation.NonNull; Loading @@ -30,8 +32,10 @@ import android.os.IBinder; import android.util.Slog; import android.view.SurfaceControl; import android.view.SurfaceView; import android.view.View; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; Loading @@ -53,6 +57,12 @@ import java.util.concurrent.Executor; public class BubbleTransitions { private static final String TAG = "BubbleTransitions"; /** * Multiplier used to convert a view elevation to an "equivalent" shadow-radius. This is the * same multiple used by skia and surface-outsets in WMS. */ private static final float ELEVATION_TO_RADIUS = 2; @NonNull final Transitions mTransitions; @NonNull final ShellTaskOrganizer mTaskOrganizer; @NonNull final TaskViewRepository mRepository; Loading Loading @@ -89,6 +99,44 @@ public class BubbleTransitions { return convert; } /** * Starts a convert-from-bubble transition. * * @see ConvertFromBubble */ public BubbleTransition startConvertFromBubble(Bubble bubble, TaskInfo taskInfo) { ConvertFromBubble convert = new ConvertFromBubble(bubble, taskInfo); return convert; } /** * Plucks the task-surface out of an ancestor view while making the view invisible. This helper * attempts to do this seamlessly (ie. view becomes invisible in sync with task reparent). */ private void pluck(SurfaceControl taskLeash, View fromView, SurfaceControl dest, float destX, float destY, float cornerRadius, SurfaceControl.Transaction t, Runnable onPlucked) { SurfaceControl.Transaction pluckT = new SurfaceControl.Transaction(); pluckT.reparent(taskLeash, dest); t.reparent(taskLeash, dest); pluckT.setPosition(taskLeash, destX, destY); t.setPosition(taskLeash, destX, destY); pluckT.show(taskLeash); pluckT.setAlpha(taskLeash, 1.f); float shadowRadius = fromView.getElevation() * ELEVATION_TO_RADIUS; pluckT.setShadowRadius(taskLeash, shadowRadius); pluckT.setCornerRadius(taskLeash, cornerRadius); t.setShadowRadius(taskLeash, shadowRadius); t.setCornerRadius(taskLeash, cornerRadius); // Need to remove the taskview AFTER applying the startTransaction because it isn't // synchronized. pluckT.addTransactionCommittedListener(mMainExecutor, onPlucked::run); fromView.getViewRootImpl().applyTransactionOnDraw(pluckT); fromView.setVisibility(INVISIBLE); } /** * Interface to a bubble-specific transition. Bubble transitions have a multi-step lifecycle * in order to coordinate with the bubble view logic. These steps are communicated on this Loading @@ -98,6 +146,7 @@ public class BubbleTransitions { default void surfaceCreated() {} default void continueExpand() {} void skip(); default void continueCollapse() {} } /** Loading Loading @@ -316,4 +365,154 @@ public class BubbleTransitions { } } } /** * BubbleTransition that coordinates the setup for moving a task out of a bubble. The actual * animation is owned by the "receiver" of the task; however, because Bubbles uses TaskView, * we need to do some extra coordination work to get the task surface out of the view * "seamlessly". * * The process here looks like: * 1. Send transition to WM for leaving bubbles mode * 2. in startAnimation, set-up a "pluck" operation to pull the task surface out of taskview * 3. Once "plucked", remove the view (calls continueCollapse when surfaces can be cleaned-up) * 4. Then re-dispatch the transition animation so that the "receiver" can animate it. * * So, constructor -> startAnimation -> continueCollapse -> re-dispatch. */ @VisibleForTesting class ConvertFromBubble implements Transitions.TransitionHandler, BubbleTransition { @NonNull final Bubble mBubble; IBinder mTransition; TaskInfo mTaskInfo; SurfaceControl mTaskLeash; SurfaceControl mRootLeash; ConvertFromBubble(@NonNull Bubble bubble, TaskInfo taskInfo) { mBubble = bubble; mTaskInfo = taskInfo; mBubble.setPreparingTransition(this); WindowContainerTransaction wct = new WindowContainerTransaction(); WindowContainerToken token = mTaskInfo.getToken(); wct.setWindowingMode(token, WINDOWING_MODE_UNDEFINED); wct.setAlwaysOnTop(token, false); mTaskOrganizer.setInterceptBackPressedOnTaskRoot(token, false); mTaskViewTransitions.enqueueExternal( mBubble.getTaskView().getController(), () -> { mTransition = mTransitions.startTransition(TRANSIT_CHANGE, wct, this); return mTransition; }); } @Override public void skip() { mBubble.setPreparingTransition(null); final TaskViewTaskController tv = mBubble.getTaskView().getController(); tv.notifyTaskRemovalStarted(tv.getTaskInfo()); mTaskLeash = null; } @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @android.annotation.Nullable TransitionRequestInfo request) { return null; } @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback) { } @Override public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, @NonNull SurfaceControl.Transaction finishTransaction) { if (!aborted) return; mTransition = null; skip(); mTaskViewTransitions.onExternalDone(transition); } @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { if (mTransition != transition) return false; final TaskViewTaskController tv = mBubble.getTaskView().getController(); if (tv == null) { mTaskViewTransitions.onExternalDone(transition); return false; } TransitionInfo.Change taskChg = null; boolean found = false; for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change chg = info.getChanges().get(i); if (chg.getTaskInfo() == null) continue; if (chg.getMode() != TRANSIT_CHANGE) continue; if (!mTaskInfo.token.equals(chg.getTaskInfo().token)) continue; found = true; mRepository.remove(tv); taskChg = chg; break; } if (!found) { Slog.w(TAG, "Expected a TaskView conversion in this transition but didn't get " + "one, cleaning up the task view"); tv.setTaskNotFound(); skip(); mTaskViewTransitions.onExternalDone(transition); return false; } mTaskLeash = taskChg.getLeash(); mRootLeash = info.getRoot(0).getLeash(); SurfaceControl dest = mBubble.getBubbleBarExpandedView().getViewRootImpl().getSurfaceControl(); final Runnable onPlucked = () -> { // Need to remove the taskview AFTER applying the startTransaction because // it isn't synchronized. tv.notifyTaskRemovalStarted(tv.getTaskInfo()); // Unset after removeView so it can be used to pick a different animation. mBubble.setPreparingTransition(null); mBubbleData.setExpanded(false /* expanded */); }; if (dest != null) { pluck(mTaskLeash, mBubble.getBubbleBarExpandedView(), dest, taskChg.getStartAbsBounds().left - info.getRoot(0).getOffset().x, taskChg.getStartAbsBounds().top - info.getRoot(0).getOffset().y, mBubble.getBubbleBarExpandedView().getCornerRadius(), startTransaction, onPlucked); mBubble.getBubbleBarExpandedView().post(() -> mTransitions.dispatchTransition( mTransition, info, startTransaction, finishTransaction, finishCallback, null)); } else { onPlucked.run(); mTransitions.dispatchTransition(mTransition, info, startTransaction, finishTransaction, finishCallback, null); } mTaskViewTransitions.onExternalDone(transition); return true; } @Override public void continueCollapse() { mBubble.cleanupTaskView(); if (mTaskLeash == null) return; SurfaceControl.Transaction t = new SurfaceControl.Transaction(); t.reparent(mTaskLeash, mRootLeash); t.apply(); } } }
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +3 −1 Original line number Diff line number Diff line Loading @@ -355,8 +355,10 @@ public class BubbleBarLayerView extends FrameLayout /** Removes the given {@code bubble}. */ public void removeBubble(Bubble bubble, Runnable endAction) { final boolean inTransition = bubble.getPreparingTransition() != null; Runnable cleanUp = () -> { bubble.cleanupViews(); // The transition is already managing the task/wm state. bubble.cleanupViews(!inTransition); endAction.run(); }; if (mBubbleData.getBubbles().isEmpty()) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +2 −1 Original line number Diff line number Diff line Loading @@ -417,7 +417,8 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { } } void notifyTaskRemovalStarted(@NonNull ActivityManager.RunningTaskInfo taskInfo) { /** Notifies listeners of a task being removed. */ public void notifyTaskRemovalStarted(@NonNull ActivityManager.RunningTaskInfo taskInfo) { if (mListener == null) return; final int taskId = taskInfo.taskId; mListenerExecutor.execute(() -> mListener.onTaskRemovalStarted(taskId)); Loading