From d60ae092bb5f1267ce6784e6121222f7cd2fdff0 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 23 Sep 2022 14:36:18 -0700 Subject: Updating icon loader method calls as per signature change Bug: 248308987 Test: Presumit Change-Id: I1177659132f929fdc60923ce7ae327d029bafd7a --- .../Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java | 2 +- .../Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java | 2 +- .../android/wm/shell/startingsurface/SplashscreenContentDrawer.java | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java index d6803e8052c6..d3a9a672ec76 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java @@ -52,7 +52,7 @@ public class BubbleBadgeIconFactory extends BaseIconFactory { userBadgedAppIcon = new CircularRingDrawable(userBadgedAppIcon); } Bitmap userBadgedBitmap = createIconBitmap( - userBadgedAppIcon, 1, BITMAP_GENERATION_MODE_WITH_SHADOW); + userBadgedAppIcon, 1, MODE_WITH_SHADOW); return createIconBitmap(userBadgedBitmap); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java index 5dab8a071f76..4ded3ea951e5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java @@ -79,6 +79,6 @@ public class BubbleIconFactory extends BaseIconFactory { true /* shrinkNonAdaptiveIcons */, null /* outscale */, outScale); - return createIconBitmap(icon, outScale[0], BITMAP_GENERATION_MODE_WITH_SHADOW); + return createIconBitmap(icon, outScale[0], MODE_WITH_SHADOW); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index 8cee4f1dc8fb..6ce981e25f5e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -432,7 +432,8 @@ public class SplashscreenContentDrawer { final ShapeIconFactory factory = new ShapeIconFactory( SplashscreenContentDrawer.this.mContext, scaledIconDpi, mFinalIconSize); - final Bitmap bitmap = factory.createScaledBitmapWithoutShadow(iconDrawable); + final Bitmap bitmap = factory.createScaledBitmap(iconDrawable, + BaseIconFactory.MODE_DEFAULT); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); createIconDrawable(new BitmapDrawable(bitmap), true, mHighResIconProvider.mLoadInDetail); -- cgit v1.2.3-59-g8ed1b From 7e2eeb1395f3753414048132bb2e2a747a24775d Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Sat, 24 Sep 2022 00:20:57 +0800 Subject: Make startTransition one-way If shell is starting an existing transition, it doesn't need the returned transition token so it can be an async call then it won't block shell's thread to execute other operations. If shell is starting a new transition, then use the new added 2-way startNewTransition which is the same as the original path. Bug: 248550757 Test: atest ShellTransitionTests Test: CtsWindowManagerDeviceTestCases with shell transition Change-Id: I5f64d19475d5b857a461775dd6f3002567e93ad8 --- .../android/window/IWindowOrganizerController.aidl | 17 ++++++++++------- core/java/android/window/WindowOrganizer.java | 21 +++++++++++++++++---- .../android/wm/shell/transition/Transitions.java | 6 +++--- .../wm/shell/transition/ShellTransitionTests.java | 21 ++++++++++++--------- .../server/wm/WindowOrganizerController.java | 17 ++++++++++++----- .../src/com/android/server/wm/WindowTestsBase.java | 2 +- 6 files changed, 55 insertions(+), 29 deletions(-) (limited to 'libs') diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl index 3c7cd0254e78..36eaf4966165 100644 --- a/core/java/android/window/IWindowOrganizerController.aidl +++ b/core/java/android/window/IWindowOrganizerController.aidl @@ -51,16 +51,19 @@ interface IWindowOrganizerController { in IWindowContainerTransactionCallback callback); /** - * Starts a transition. + * Starts a new transition. * @param type The transition type. - * @param transitionToken A token associated with the transition to start. If null, a new - * transition will be created of the provided type. * @param t Operations that are part of the transition. - * @return a token representing the transition. This will just be transitionToken if it was - * non-null. + * @return a token representing the transition. */ - IBinder startTransition(int type, in @nullable IBinder transitionToken, - in @nullable WindowContainerTransaction t); + IBinder startNewTransition(int type, in @nullable WindowContainerTransaction t); + + /** + * Starts the given transition. + * @param transitionToken A token associated with the transition to start. + * @param t Operations that are part of the transition. + */ + oneway void startTransition(IBinder transitionToken, in @nullable WindowContainerTransaction t); /** * Starts a legacy transition. diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java index 4ea5ea5694fa..2a80d021abd6 100644 --- a/core/java/android/window/WindowOrganizer.java +++ b/core/java/android/window/WindowOrganizer.java @@ -84,9 +84,8 @@ public class WindowOrganizer { } /** - * Start a transition. + * Starts a new transition, don't use this to start an already created one. * @param type The type of the transition. This is ignored if a transitionToken is provided. - * @param transitionToken An existing transition to start. If null, a new transition is created. * @param t The set of window operations that are part of this transition. * @return A token identifying the transition. This will be the same as transitionToken if it * was provided. @@ -94,10 +93,24 @@ public class WindowOrganizer { */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) @NonNull - public IBinder startTransition(int type, @Nullable IBinder transitionToken, + public IBinder startNewTransition(int type, @Nullable WindowContainerTransaction t) { + try { + return getWindowOrganizerController().startNewTransition(type, t); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Starts an already created transition. + * @param transitionToken An existing transition to start. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) + public void startTransition(@NonNull IBinder transitionToken, @Nullable WindowContainerTransaction t) { try { - return getWindowOrganizerController().startTransition(type, transitionToken, t); + getWindowOrganizerController().startTransition(transitionToken, t); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 29d25bc39223..d65ac80621df 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -716,8 +716,8 @@ public class Transitions implements RemoteCallable { null /* newDisplayAreaInfo */); } } - active.mToken = mOrganizer.startTransition( - request.getType(), transitionToken, wct); + mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct); + active.mToken = transitionToken; mActiveTransitions.add(active); } @@ -726,7 +726,7 @@ public class Transitions implements RemoteCallable { @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) { final ActiveTransition active = new ActiveTransition(); active.mHandler = handler; - active.mToken = mOrganizer.startTransition(type, null /* token */, wct); + active.mToken = mOrganizer.startNewTransition(type, wct); mActiveTransitions.add(active); return active.mToken; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index c6492bee040e..db9136d8eacb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -45,7 +45,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.clearInvocations; @@ -67,10 +66,12 @@ import android.view.SurfaceControl; import android.view.WindowManager; import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; +import android.window.IWindowContainerToken; import android.window.RemoteTransition; import android.window.TransitionFilter; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import android.window.WindowOrganizer; @@ -117,7 +118,7 @@ public class ShellTransitionTests extends ShellTestCase { @Before public void setUp() { doAnswer(invocation -> invocation.getArguments()[1]) - .when(mOrganizer).startTransition(anyInt(), any(), any()); + .when(mOrganizer).startTransition(any(), any()); } @Test @@ -136,7 +137,7 @@ public class ShellTransitionTests extends ShellTestCase { IBinder transitToken = new Binder(); transitions.requestStartTransition(transitToken, new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); - verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any()); + verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class), @@ -188,7 +189,7 @@ public class ShellTransitionTests extends ShellTestCase { // Make a request that will be rejected by the testhandler. transitions.requestStartTransition(transitToken, new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); - verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), isNull()); + verify(mOrganizer, times(1)).startTransition(eq(transitToken), isNull()); transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class)); assertEquals(1, mDefaultHandler.activeCount()); @@ -199,10 +200,12 @@ public class ShellTransitionTests extends ShellTestCase { // Make a request that will be handled by testhandler but not animated by it. RunningTaskInfo mwTaskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + // Make the wct non-empty. + handlerWCT.setFocusable(new WindowContainerToken(mock(IWindowContainerToken.class)), true); transitions.requestStartTransition(transitToken, new TransitionRequestInfo(TRANSIT_OPEN, mwTaskInfo, null /* remote */)); verify(mOrganizer, times(1)).startTransition( - eq(TRANSIT_OPEN), eq(transitToken), eq(handlerWCT)); + eq(transitToken), eq(handlerWCT)); transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class)); assertEquals(1, mDefaultHandler.activeCount()); @@ -217,8 +220,8 @@ public class ShellTransitionTests extends ShellTestCase { transitions.addHandler(topHandler); transitions.requestStartTransition(transitToken, new TransitionRequestInfo(TRANSIT_CHANGE, mwTaskInfo, null /* remote */)); - verify(mOrganizer, times(1)).startTransition( - eq(TRANSIT_CHANGE), eq(transitToken), eq(handlerWCT)); + verify(mOrganizer, times(2)).startTransition( + eq(transitToken), eq(handlerWCT)); TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE) .addChange(TRANSIT_CHANGE).build(); transitions.onTransitionReady(transitToken, change, mock(SurfaceControl.Transaction.class), @@ -256,7 +259,7 @@ public class ShellTransitionTests extends ShellTestCase { transitions.requestStartTransition(transitToken, new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, new RemoteTransition(testRemote))); - verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any()); + verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class), @@ -406,7 +409,7 @@ public class ShellTransitionTests extends ShellTestCase { IBinder transitToken = new Binder(); transitions.requestStartTransition(transitToken, new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); - verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any()); + verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class), diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 2e1477ddf0f1..949fa9662258 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -46,6 +46,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED; +import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; @@ -242,8 +243,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } @Override - public IBinder startTransition(int type, @Nullable IBinder transitionToken, + public IBinder startNewTransition(int type, @Nullable WindowContainerTransaction t) { + return startTransition(type, null /* transitionToken */, t); + } + + @Override + public void startTransition(@NonNull IBinder transitionToken, @Nullable WindowContainerTransaction t) { + startTransition(-1 /* unused type */, transitionToken, t); + } + + private IBinder startTransition(@WindowManager.TransitionType int type, + @Nullable IBinder transitionToken, @Nullable WindowContainerTransaction t) { enforceTaskPermission("startTransition()"); final CallerInfo caller = new CallerInfo(); final long ident = Binder.clearCallingIdentity(); @@ -1557,10 +1568,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return (cfgChanges & CONTROLLABLE_CONFIGS) == 0; } - private void enforceTaskPermission(String func) { - mService.enforceTaskPermission(func); - } - private boolean isValidTransaction(@NonNull WindowContainerTransaction t) { if (t.getTaskFragmentOrganizer() != null && !mTaskFragmentOrganizerController .isOrganizerRegistered(t.getTaskFragmentOrganizer())) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index ef532f5732eb..139e44099f42 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1727,7 +1727,7 @@ class WindowTestsBase extends SystemServiceTestsBase { } void startTransition() { - mOrganizer.startTransition(mLastRequest.getType(), mLastTransit, null); + mOrganizer.startTransition(mLastTransit, null); } void onTransactionReady(SurfaceControl.Transaction t) { -- cgit v1.2.3-59-g8ed1b From 66e601fb135ce681a88655d6306e503ebad661ff Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Fri, 16 Sep 2022 16:53:53 +0800 Subject: [Shell Transition] Schedule another transition to update visibility. Activity could call setTranslucent during a transition playing, if the task of an activity was in transition and that activity should become invisible, it would be defer until transition finish. However, since the activity wasn't participant the running transition, there won't do commitVisibility for it after transition finish. To correct the visibility status, trigger another transition so the activities which visibility changed can be collect and commit. Bug: 246518648 Test: atest testConvertTranslucentOnTranslucentActivity Test: atest testConvertTranslucentOnNonTopTranslucentActivity Change-Id: Ic1cda79da37162cca2a1a3fbc73311cc325b3874 --- core/java/android/window/TransitionInfo.java | 9 +++- .../android/wm/shell/transition/Transitions.java | 50 ++++++++++++---------- .../server/wm/ActivityClientController.java | 43 ++++++++++++++++++- .../java/com/android/server/wm/ActivityRecord.java | 16 ++++--- .../java/com/android/server/wm/Transition.java | 5 +-- 5 files changed, 88 insertions(+), 35 deletions(-) (limited to 'libs') diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 641d1a189711..fbdd325e0814 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -132,8 +132,11 @@ public final class TransitionInfo implements Parcelable { */ public static final int FLAG_IS_BEHIND_STARTING_WINDOW = 1 << 14; + /** This change happened underneath something else. */ + public static final int FLAG_IS_OCCLUDED = 1 << 15; + /** The first unused bit. This can be used by remotes to attach custom flags to this change. */ - public static final int FLAG_FIRST_CUSTOM = 1 << 15; + public static final int FLAG_FIRST_CUSTOM = 1 << 16; /** @hide */ @IntDef(prefix = { "FLAG_" }, value = { @@ -153,6 +156,7 @@ public final class TransitionInfo implements Parcelable { FLAG_CROSS_PROFILE_OWNER_THUMBNAIL, FLAG_CROSS_PROFILE_WORK_THUMBNAIL, FLAG_IS_BEHIND_STARTING_WINDOW, + FLAG_IS_OCCLUDED, FLAG_FIRST_CUSTOM }) public @interface ChangeFlags {} @@ -362,6 +366,9 @@ public final class TransitionInfo implements Parcelable { if ((flags & FLAG_IS_BEHIND_STARTING_WINDOW) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("IS_BEHIND_STARTING_WINDOW"); } + if ((flags & FLAG_IS_OCCLUDED) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("IS_OCCLUDED"); + } if ((flags & FLAG_FIRST_CUSTOM) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM"); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 29d25bc39223..43ea695299a9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -25,6 +25,7 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.fixScale; import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD; +import static android.window.TransitionInfo.FLAG_IS_OCCLUDED; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; @@ -441,32 +442,35 @@ public class Transitions implements RemoteCallable { return; } - // apply transfer starting window directly if there is no other task change. Since this - // is an activity->activity situation, we can detect it by selecting transitions with only - // 2 changes where neither are tasks and one is a starting-window recipient. final int changeSize = info.getChanges().size(); - if (changeSize == 2) { - boolean nonTaskChange = true; - boolean transferStartingWindow = false; - for (int i = changeSize - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getTaskInfo() != null) { - nonTaskChange = false; - break; - } - if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { - transferStartingWindow = true; - } - } - if (nonTaskChange && transferStartingWindow) { - t.apply(); - finishT.apply(); - // Treat this as an abort since we are bypassing any merge logic and effectively - // finishing immediately. - onAbort(transitionToken); - return; + boolean taskChange = false; + boolean transferStartingWindow = false; + boolean allOccluded = changeSize > 0; + for (int i = changeSize - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + taskChange |= change.getTaskInfo() != null; + transferStartingWindow |= change.hasFlags(FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT); + if (!change.hasFlags(FLAG_IS_OCCLUDED)) { + allOccluded = false; } } + // There does not need animation when: + // A. Transfer starting window. Apply transfer starting window directly if there is no other + // task change. Since this is an activity->activity situation, we can detect it by selecting + // transitions with only 2 changes where neither are tasks and one is a starting-window + // recipient. + if (!taskChange && transferStartingWindow && changeSize == 2 + // B. It's visibility change if the TRANSIT_TO_BACK/TO_FRONT happened when all + // changes are underneath another change. + || ((info.getType() == TRANSIT_TO_BACK || info.getType() == TRANSIT_TO_FRONT) + && allOccluded)) { + t.apply(); + finishT.apply(); + // Treat this as an abort since we are bypassing any merge logic and effectively + // finishing immediately. + onAbort(transitionToken); + return; + } final ActiveTransition active = mActiveTransitions.get(activeIdx); active.mInfo = info; diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index d2a00af245f6..b64b9f393d28 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -27,6 +27,8 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE; @@ -707,7 +709,26 @@ class ActivityClientController extends IActivityClientController.Stub { try { synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); - return r != null && r.setOccludesParent(true); + // Create a transition if the activity is playing in case the below activity didn't + // commit invisible. That's because if any activity below this one has changed its + // visibility while playing transition, there won't able to commit visibility until + // the running transition finish. + final Transition transition = r != null + && r.mTransitionController.inPlayingTransition(r) + ? r.mTransitionController.createTransition(TRANSIT_TO_BACK) : null; + if (transition != null) { + r.mTransitionController.requestStartTransition(transition, null /*startTask */, + null /* remoteTransition */, null /* displayChange */); + } + final boolean changed = r != null && r.setOccludesParent(true); + if (transition != null) { + if (changed) { + r.mTransitionController.setReady(r.getDisplayContent()); + } else { + transition.abort(); + } + } + return changed; } } finally { Binder.restoreCallingIdentity(origId); @@ -728,7 +749,25 @@ class ActivityClientController extends IActivityClientController.Stub { if (under != null) { under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null; } - return r.setOccludesParent(false); + // Create a transition if the activity is playing in case the current activity + // didn't commit invisible. That's because if this activity has changed its + // visibility while playing transition, there won't able to commit visibility until + // the running transition finish. + final Transition transition = r.mTransitionController.inPlayingTransition(r) + ? r.mTransitionController.createTransition(TRANSIT_TO_FRONT) : null; + if (transition != null) { + r.mTransitionController.requestStartTransition(transition, null /*startTask */, + null /* remoteTransition */, null /* displayChange */); + } + final boolean changed = r.setOccludesParent(false); + if (transition != null) { + if (changed) { + r.mTransitionController.setReady(r.getDisplayContent()); + } else { + transition.abort(); + } + } + return changed; } } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b8486e7aa2b4..fab168369470 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -120,6 +120,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_OLD_UNSET; +import static android.window.TransitionInfo.FLAG_IS_OCCLUDED; +import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM; @@ -658,7 +660,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults = new WindowState.UpdateReportedVisibilityResults(); - boolean mUseTransferredAnimation; + int mTransitionChangeFlags; /** Whether we need to setup the animation to animate only within the letterbox. */ private boolean mNeedsLetterboxedAnimation; @@ -4395,10 +4397,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // When transferring an animation, we no longer need to apply an animation to // the token we transfer the animation over. Thus, set this flag to indicate // we've transferred the animation. - mUseTransferredAnimation = true; + mTransitionChangeFlags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; } else if (mTransitionController.getTransitionPlayer() != null) { // In the new transit system, just set this every time we transfer the window - mUseTransferredAnimation = true; + mTransitionChangeFlags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; } // Post cleanup after the visibility and animation are transferred. fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow); @@ -5247,6 +5249,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // If in a transition, defer commits for activities that are going invisible if (!visible && inTransition()) { + if (mTransitionController.inPlayingTransition(this) + && mTransitionController.isCollecting(this)) { + mTransitionChangeFlags |= FLAG_IS_OCCLUDED; + } return; } // If we are preparing an app transition, then delay changing @@ -5297,7 +5303,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override boolean applyAnimation(LayoutParams lp, @TransitionOldType int transit, boolean enter, boolean isVoiceInteraction, @Nullable ArrayList sources) { - if (mUseTransferredAnimation) { + if ((mTransitionChangeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { return false; } // If it was set to true, reset the last request to force the transition. @@ -5370,7 +5376,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mWmService.mWindowPlacerLocked.performSurfacePlacement(); } displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); - mUseTransferredAnimation = false; + mTransitionChangeFlags = 0; postApplyAnimation(visible, fromTransition); } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 147c9cb18315..4a1bc6a71f91 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -55,7 +55,6 @@ import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; -import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static android.window.TransitionInfo.FLAG_WILL_IME_SHOWN; @@ -1879,12 +1878,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe final ActivityRecord record = wc.asActivityRecord(); if (record != null) { parentTask = record.getTask(); - if (record.mUseTransferredAnimation) { - flags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; - } if (record.mVoiceInteraction) { flags |= FLAG_IS_VOICE_INTERACTION; } + flags |= record.mTransitionChangeFlags; } final TaskFragment taskFragment = wc.asTaskFragment(); if (taskFragment != null && task == null) { -- cgit v1.2.3-59-g8ed1b From 1c48aa129001921f2c3992c77958a71e33945fdf Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 30 Sep 2022 23:47:33 +0000 Subject: Move kids task organizer to handheld package Bug: 238217847 Test: mp sysuig Change-Id: Idee19d16fdea9811044bd47bf6e63c6674cbb470 --- .../android/wm/shell/dagger/WMShellBaseModule.java | 21 ------------------- .../com/android/wm/shell/dagger/WMShellModule.java | 24 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 21 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index c25bbbf06dda..353014264990 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -69,7 +69,6 @@ import com.android.wm.shell.floating.FloatingTasksController; import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.fullscreen.FullscreenTaskListener; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; -import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; @@ -190,25 +189,6 @@ public abstract class WMShellBaseModule { unfoldAnimationController, recentTasksOptional, mainExecutor); } - @WMSingleton - @Provides - static KidsModeTaskOrganizer provideKidsModeTaskOrganizer( - Context context, - ShellInit shellInit, - ShellCommandHandler shellCommandHandler, - SyncTransactionQueue syncTransactionQueue, - DisplayController displayController, - DisplayInsetsController displayInsetsController, - Optional unfoldAnimationController, - Optional recentTasksOptional, - @ShellMainThread ShellExecutor mainExecutor, - @ShellMainThread Handler mainHandler - ) { - return new KidsModeTaskOrganizer(context, shellInit, shellCommandHandler, - syncTransactionQueue, displayController, displayInsetsController, - unfoldAnimationController, recentTasksOptional, mainExecutor, mainHandler); - } - @WMSingleton @Provides static CompatUIController provideCompatUIController(Context context, @@ -781,7 +761,6 @@ public abstract class WMShellBaseModule { DisplayInsetsController displayInsetsController, DragAndDropController dragAndDropController, ShellTaskOrganizer shellTaskOrganizer, - KidsModeTaskOrganizer kidsModeTaskOrganizer, Optional bubblesOptional, Optional splitScreenOptional, Optional pipOptional, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 37a50b611039..47b665970d28 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -56,6 +56,7 @@ import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.freeform.FreeformTaskTransitionHandler; import com.android.wm.shell.freeform.FreeformTaskTransitionObserver; import com.android.wm.shell.fullscreen.FullscreenTaskListener; +import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; @@ -619,6 +620,28 @@ public abstract class WMShellModule { return new DesktopModeTaskRepository(); } + // + // Kids mode + // + @WMSingleton + @Provides + static KidsModeTaskOrganizer provideKidsModeTaskOrganizer( + Context context, + ShellInit shellInit, + ShellCommandHandler shellCommandHandler, + SyncTransactionQueue syncTransactionQueue, + DisplayController displayController, + DisplayInsetsController displayInsetsController, + Optional unfoldAnimationController, + Optional recentTasksOptional, + @ShellMainThread ShellExecutor mainExecutor, + @ShellMainThread Handler mainHandler + ) { + return new KidsModeTaskOrganizer(context, shellInit, shellCommandHandler, + syncTransactionQueue, displayController, displayInsetsController, + unfoldAnimationController, recentTasksOptional, mainExecutor, mainHandler); + } + // // Misc // @@ -630,6 +653,7 @@ public abstract class WMShellModule { @Provides static Object provideIndependentShellComponentsToCreate( DefaultMixedHandler defaultMixedHandler, + KidsModeTaskOrganizer kidsModeTaskOrganizer, Optional desktopModeController) { return new Object(); } -- cgit v1.2.3-59-g8ed1b From 5414dab01eda3d6ef6e3bbe4baeb5680ec1171ef Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 3 Oct 2022 22:12:08 +0000 Subject: Clean up some methods on the Pip interface - Remove some pip methods that aren't used from SysUI - Migrate an old ISystemUIProxy pip call to IPip Bug: 238217847 Test: atest WMShellUnitTests Change-Id: Iab57329a9d79704b0e35fdb22a36594aa4368141 --- .../Shell/src/com/android/wm/shell/pip/IPip.aidl | 7 +++++- .../Shell/src/com/android/wm/shell/pip/Pip.java | 17 -------------- .../android/wm/shell/pip/phone/PipController.java | 27 +++++++++------------- .../systemui/shared/recents/ISystemUiProxy.aidl | 5 ---- .../systemui/recents/OverviewProxyService.java | 8 ------- 5 files changed, 17 insertions(+), 47 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl index 4def15db2f52..2624ee536b58 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl @@ -59,10 +59,15 @@ interface IPip { /** * Sets listener to get pinned stack animation callbacks. */ - oneway void setPinnedStackAnimationListener(IPipAnimationListener listener) = 3; + oneway void setPipAnimationListener(IPipAnimationListener listener) = 3; /** * Sets the shelf height and visibility. */ oneway void setShelfHeight(boolean visible, int shelfHeight) = 4; + + /** + * Sets the next pip animation type to be the alpha animation. + */ + oneway void setPipAnimationTypeToAlpha() = 5; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index c06881ae6ad7..72b9dd37ac7d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -50,15 +50,6 @@ public interface Pip { default void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) { } - /** - * Sets both shelf visibility and its height. - * - * @param visible visibility of shelf. - * @param height to specify the height for shelf. - */ - default void setShelfHeight(boolean visible, int height) { - } - /** * Set the callback when {@link PipTaskOrganizer#isInPip()} state is changed. * @@ -67,14 +58,6 @@ public interface Pip { */ default void setOnIsInPipStateChangedListener(Consumer callback) {} - /** - * Set the pinned stack with {@link PipAnimationController.AnimationType} - * - * @param animationType The pre-defined {@link PipAnimationController.AnimationType} - */ - default void setPinnedStackAnimationType(int animationType) { - } - /** * Called when showing Pip menu. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index af47666efa5a..3345b1b2d0e8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -23,6 +23,7 @@ import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static com.android.internal.jank.InteractionJankMonitor.CUJ_PIP_TRANSITION; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; +import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN; @@ -1064,13 +1065,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb }); } - @Override - public void setShelfHeight(boolean visible, int height) { - mMainExecutor.execute(() -> { - PipController.this.setShelfHeight(visible, height); - }); - } - @Override public void setOnIsInPipStateChangedListener(Consumer callback) { mMainExecutor.execute(() -> { @@ -1078,13 +1072,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb }); } - @Override - public void setPinnedStackAnimationType(int animationType) { - mMainExecutor.execute(() -> { - PipController.this.setPinnedStackAnimationType(animationType); - }); - } - @Override public void addPipExclusionBoundsChangeListener(Consumer listener) { mMainExecutor.execute(() -> { @@ -1178,8 +1165,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override - public void setPinnedStackAnimationListener(IPipAnimationListener listener) { - executeRemoteCallWithTaskPermission(mController, "setPinnedStackAnimationListener", + public void setPipAnimationListener(IPipAnimationListener listener) { + executeRemoteCallWithTaskPermission(mController, "setPipAnimationListener", (controller) -> { if (listener != null) { mListener.register(listener); @@ -1188,5 +1175,13 @@ public class PipController implements PipTransitionController.PipTransitionCallb } }); } + + @Override + public void setPipAnimationTypeToAlpha() { + executeRemoteCallWithTaskPermission(mController, "setPipAnimationTypeToAlpha", + (controller) -> { + controller.setPinnedStackAnimationType(ANIM_TYPE_ALPHA); + }); + } } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index e77c65079456..2b2b05ce2fbf 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -81,11 +81,6 @@ interface ISystemUiProxy { */ void stopScreenPinning() = 17; - /* - * Notifies that the swipe-to-home (recents animation) is finished. - */ - void notifySwipeToHomeFinished() = 23; - /** * Notifies that quickstep will switch to a new task * @param rotation indicates which Surface.Rotation the gesture was started in diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 7e2a5c51786d..899e57d7d0ae 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -341,14 +341,6 @@ public class OverviewProxyService extends CurrentUserTracker implements }); } - @Override - public void notifySwipeToHomeFinished() { - verifyCallerAndClearCallingIdentity("notifySwipeToHomeFinished", () -> - mPipOptional.ifPresent( - pip -> pip.setPinnedStackAnimationType( - PipAnimationController.ANIM_TYPE_ALPHA))); - } - @Override public void notifySwipeUpGestureStarted() { verifyCallerAndClearCallingIdentityPostMain("notifySwipeUpGestureStarted", () -> -- cgit v1.2.3-59-g8ed1b From 797d6ab9da2a1b1c1ef015844c351655ece39144 Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Tue, 4 Oct 2022 10:26:55 +0000 Subject: Remove stage concept from split screen APIs Since split screen has transferred from stage-based model to app-pair model. Update to remove the stage concept from split screen APIs, so the caller won't need to deal with stage concept. Bug: 250875492 Test: atest WMShellFlickerTests Test: manual check start tasks or start intent/shortcut with task flow Change-Id: I42a491132052ed87f57ccba9e9ca99b8eddb13de --- .../android/wm/shell/splitscreen/ISplitScreen.aidl | 20 +++++----- .../shell/splitscreen/SplitScreenController.java | 29 +++++++------- .../wm/shell/splitscreen/StageCoordinator.java | 45 +++++++++++----------- 3 files changed, 47 insertions(+), 47 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl index ecdafa9a63f4..03688b747f76 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl @@ -79,23 +79,23 @@ interface ISplitScreen { /** * Starts tasks simultaneously in one transition. */ - oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId, - in Bundle sideOptions, int sidePosition, float splitRatio, - in RemoteTransition remoteTransition, in InstanceId instanceId) = 10; + oneway void startTasks(int taskId1, in Bundle options1, int taskId2, in Bundle options2, + int splitPosition, float splitRatio, in RemoteTransition remoteTransition, + in InstanceId instanceId) = 10; /** * Version of startTasks using legacy transition system. */ - oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions, - int sideTaskId, in Bundle sideOptions, int sidePosition, - float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 11; + oneway void startTasksWithLegacyTransition(int taskId1, in Bundle options1, int taskId2, + in Bundle options2, int splitPosition, float splitRatio, + in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 11; /** * Starts a pair of intent and task using legacy transition system. */ oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent, - in Intent fillInIntent, int taskId, in Bundle mainOptions,in Bundle sideOptions, - int sidePosition, float splitRatio, in RemoteAnimationAdapter adapter, + in Intent fillInIntent, in Bundle options1, int taskId, in Bundle options2, + int splitPosition, float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 12; /** @@ -115,7 +115,7 @@ interface ISplitScreen { /** * Starts a pair of shortcut and task using legacy transition system. */ - oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo, int taskId, - in Bundle mainOptions, in Bundle sideOptions, int sidePosition, float splitRatio, + oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo, + in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 15; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 07a6895e2720..8043b97164ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -828,47 +828,46 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } @Override - public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions, - int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, + public void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1, + int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startTasks", (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition( - mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition, + taskId1, options1, taskId2, options2, splitPosition, splitRatio, adapter, instanceId)); } @Override public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, - Intent fillInIntent, int taskId, Bundle mainOptions, Bundle sideOptions, - int sidePosition, float splitRatio, RemoteAnimationAdapter adapter, + Intent fillInIntent, Bundle options1, int taskId, Bundle options2, + int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startIntentAndTaskWithLegacyTransition", (controller) -> controller.mStageCoordinator.startIntentAndTaskWithLegacyTransition( - pendingIntent, fillInIntent, taskId, mainOptions, sideOptions, - sidePosition, splitRatio, adapter, instanceId)); + pendingIntent, fillInIntent, options1, taskId, options2, + splitPosition, splitRatio, adapter, instanceId)); } @Override public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, - int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions, - @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter, + @Nullable Bundle options1, int taskId, @Nullable Bundle options2, + @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startShortcutAndTaskWithLegacyTransition", (controller) -> controller.mStageCoordinator.startShortcutAndTaskWithLegacyTransition( - shortcutInfo, taskId, mainOptions, sideOptions, sidePosition, + shortcutInfo, options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId)); } @Override - public void startTasks(int mainTaskId, @Nullable Bundle mainOptions, - int sideTaskId, @Nullable Bundle sideOptions, - @SplitPosition int sidePosition, float splitRatio, + public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, + @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { executeRemoteCallWithTaskPermission(mController, "startTasks", - (controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions, - sideTaskId, sideOptions, sidePosition, splitRatio, remoteTransition, + (controller) -> controller.mStageCoordinator.startTasks(taskId1, options1, + taskId2, options2, splitPosition, splitRatio, remoteTransition, instanceId)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index c17f8226c925..d79e6a1f11d3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -516,8 +516,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } /** Starts 2 tasks in one transition. */ - void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId, - @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio, + void startTasks(int sideTaskId, @Nullable Bundle sideOptions, int mainTaskId, + @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); mainOptions = mainOptions != null ? mainOptions : new Bundle(); @@ -550,44 +550,45 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setEnterInstanceId(instanceId); } - /** Starts 2 tasks in one legacy transition. */ - void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions, - int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, + /** Starts a pair of tasks using legacy transition. */ + void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1, + int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); - if (sideOptions == null) sideOptions = new Bundle(); - addActivityOptions(sideOptions, mSideStage); - wct.startTask(sideTaskId, sideOptions); + if (options1 == null) options1 = new Bundle(); + addActivityOptions(options1, mSideStage); + wct.startTask(taskId1, options1); - startWithLegacyTransition(wct, mainTaskId, mainOptions, sidePosition, splitRatio, adapter, + startWithLegacyTransition(wct, taskId2, options2, splitPosition, splitRatio, adapter, instanceId); } - /** Start an intent and a task ordered by {@code intentFirst}. */ + /** Starts a pair of intent and task using legacy transition. */ void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent, - int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions, - @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter, + @Nullable Bundle options1, int taskId, @Nullable Bundle options2, + @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); - if (sideOptions == null) sideOptions = new Bundle(); - addActivityOptions(sideOptions, mSideStage); - wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions); + if (options1 == null) options1 = new Bundle(); + addActivityOptions(options1, mSideStage); + wct.sendPendingIntent(pendingIntent, fillInIntent, options1); - startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter, + startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter, instanceId); } + /** Starts a pair of shortcut and task using legacy transition. */ void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, - int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions, - @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter, + @Nullable Bundle options1, int taskId, @Nullable Bundle options2, + @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); - if (sideOptions == null) sideOptions = new Bundle(); - addActivityOptions(sideOptions, mSideStage); - wct.startShortcut(mContext.getPackageName(), shortcutInfo, sideOptions); + if (options1 == null) options1 = new Bundle(); + addActivityOptions(options1, mSideStage); + wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1); - startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter, + startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter, instanceId); } -- cgit v1.2.3-59-g8ed1b From 778001641a3e19e0847275a35151e61c93b62eb6 Mon Sep 17 00:00:00 2001 From: Massimo Carli Date: Mon, 26 Sep 2022 23:24:34 +0000 Subject: LetterboxEdu not visible when device is docked If the device is docked the letterbox edu should not be visible. Fix: 215098637 Test: Run `atest WMShellUnitTests:LetterboxEduWindowManagerTest WMShellUnitTests:CompatUIControllerTest` Change-Id: I9ad5274654ed827fc31800d9d6dae5be1fc4baa1 --- .../android/wm/shell/common/DockStateReader.java | 57 ++++++++++++++++++++++ .../wm/shell/compatui/CompatUIController.java | 9 +++- .../letterboxedu/LetterboxEduWindowManager.java | 16 ++++-- .../android/wm/shell/dagger/WMShellBaseModule.java | 7 ++- .../wm/shell/compatui/CompatUIControllerTest.java | 4 +- .../LetterboxEduWindowManagerTest.java | 26 +++++++++- 6 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/common/DockStateReader.java (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DockStateReader.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DockStateReader.java new file mode 100644 index 000000000000..e029358cb3a2 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DockStateReader.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 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.wm.shell.common; + +import static android.content.Intent.EXTRA_DOCK_STATE; + +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import com.android.wm.shell.dagger.WMSingleton; + +import javax.inject.Inject; + +/** + * Provides information about the docked state of the device. + */ +@WMSingleton +public class DockStateReader { + + private static final IntentFilter DOCK_INTENT_FILTER = new IntentFilter( + Intent.ACTION_DOCK_EVENT); + + private final Context mContext; + + @Inject + public DockStateReader(Context context) { + mContext = context; + } + + /** + * @return True if the device is docked and false otherwise. + */ + public boolean isDocked() { + Intent dockStatus = mContext.registerReceiver(/* receiver */ null, DOCK_INTENT_FILTER); + if (dockStatus != null) { + int dockState = dockStatus.getIntExtra(EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + return dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED; + } + return false; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index 235fd9c469ea..6627de58cce3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -37,6 +37,7 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.DockStateReader; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState; @@ -109,6 +110,7 @@ public class CompatUIController implements OnDisplaysChangedListener, private final SyncTransactionQueue mSyncQueue; private final ShellExecutor mMainExecutor; private final Lazy mTransitionsLazy; + private final DockStateReader mDockStateReader; private CompatUICallback mCallback; @@ -127,7 +129,8 @@ public class CompatUIController implements OnDisplaysChangedListener, DisplayImeController imeController, SyncTransactionQueue syncQueue, ShellExecutor mainExecutor, - Lazy transitionsLazy) { + Lazy transitionsLazy, + DockStateReader dockStateReader) { mContext = context; mShellController = shellController; mDisplayController = displayController; @@ -138,6 +141,7 @@ public class CompatUIController implements OnDisplaysChangedListener, mTransitionsLazy = transitionsLazy; mCompatUIHintsState = new CompatUIHintsState(); shellInit.addInitCallback(this::onInit, this); + mDockStateReader = dockStateReader; } private void onInit() { @@ -315,7 +319,8 @@ public class CompatUIController implements OnDisplaysChangedListener, return new LetterboxEduWindowManager(context, taskInfo, mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId), mTransitionsLazy.get(), - this::onLetterboxEduDismissed); + this::onLetterboxEduDismissed, + mDockStateReader); } private void onLetterboxEduDismissed() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java index 35f1038a6853..867d0ef732ac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java @@ -34,6 +34,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.DockStateReader; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.compatui.CompatUIWindowManagerAbstract; import com.android.wm.shell.transition.Transitions; @@ -88,19 +89,21 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { */ private final int mDialogVerticalMargin; + private final DockStateReader mDockStateReader; + public LetterboxEduWindowManager(Context context, TaskInfo taskInfo, SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout, Transitions transitions, - Runnable onDismissCallback) { + Runnable onDismissCallback, DockStateReader dockStateReader) { this(context, taskInfo, syncQueue, taskListener, displayLayout, transitions, - onDismissCallback, new LetterboxEduAnimationController(context)); + onDismissCallback, new LetterboxEduAnimationController(context), dockStateReader); } @VisibleForTesting LetterboxEduWindowManager(Context context, TaskInfo taskInfo, SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout, Transitions transitions, Runnable onDismissCallback, - LetterboxEduAnimationController animationController) { + LetterboxEduAnimationController animationController, DockStateReader dockStateReader) { super(context, taskInfo, syncQueue, taskListener, displayLayout); mTransitions = transitions; mOnDismissCallback = onDismissCallback; @@ -111,6 +114,7 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { Context.MODE_PRIVATE); mDialogVerticalMargin = (int) mContext.getResources().getDimension( R.dimen.letterbox_education_dialog_margin); + mDockStateReader = dockStateReader; } @Override @@ -130,13 +134,15 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract { @Override protected boolean eligibleToShowLayout() { + // - The letterbox education should not be visible if the device is docked. // - If taskbar education is showing, the letterbox education shouldn't be shown for the // given task until the taskbar education is dismissed and the compat info changes (then // the controller will create a new instance of this class since this one isn't eligible). // - If the layout isn't null then it was previously showing, and we shouldn't check if the // user has seen the letterbox education before. - return mEligibleForLetterboxEducation && !isTaskbarEduShowing() && (mLayout != null - || !getHasSeenLetterboxEducation()); + return mEligibleForLetterboxEducation && !isTaskbarEduShowing() + && (mLayout != null || !getHasSeenLetterboxEducation()) + && !mDockStateReader.isDocked(); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index c25bbbf06dda..49ee2aaf971a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -46,6 +46,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.DockStateReader; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -216,9 +217,11 @@ public abstract class WMShellBaseModule { ShellController shellController, DisplayController displayController, DisplayInsetsController displayInsetsController, DisplayImeController imeController, SyncTransactionQueue syncQueue, - @ShellMainThread ShellExecutor mainExecutor, Lazy transitionsLazy) { + @ShellMainThread ShellExecutor mainExecutor, Lazy transitionsLazy, + DockStateReader dockStateReader) { return new CompatUIController(context, shellInit, shellController, displayController, - displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy); + displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy, + dockStateReader); } @WMSingleton diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index 6292130ddec9..2fc0914acbd4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -51,6 +51,7 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.DockStateReader; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager; @@ -93,6 +94,7 @@ public class CompatUIControllerTest extends ShellTestCase { private @Mock Lazy mMockTransitionsLazy; private @Mock CompatUIWindowManager mMockCompatLayout; private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout; + private @Mock DockStateReader mDockStateReader; @Captor ArgumentCaptor mOnInsetsChangedListenerCaptor; @@ -113,7 +115,7 @@ public class CompatUIControllerTest extends ShellTestCase { mShellInit = spy(new ShellInit(mMockExecutor)); mController = new CompatUIController(mContext, mShellInit, mMockShellController, mMockDisplayController, mMockDisplayInsetsController, mMockImeController, - mMockSyncQueue, mMockExecutor, mMockTransitionsLazy) { + mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader) { @Override CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java index f3a8cf45b7f8..16517c0a0010 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java @@ -54,6 +54,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.DockStateReader; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.transition.Transitions; @@ -103,6 +104,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { @Mock private SurfaceControlViewHost mViewHost; @Mock private Transitions mTransitions; @Mock private Runnable mOnDismissCallback; + @Mock private DockStateReader mDockStateReader; private SharedPreferences mSharedPreferences; @Nullable @@ -152,6 +154,16 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { assertNull(windowManager.mLayout); } + @Test + public void testCreateLayout_eligibleAndDocked_doesNotCreateLayout() { + LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ + true, /* isDocked */ true); + + assertFalse(windowManager.createLayout(/* canShow= */ true)); + + assertNull(windowManager.mLayout); + } + @Test public void testCreateLayout_taskBarEducationIsShowing_doesNotCreateLayout() { LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ @@ -382,17 +394,27 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase { return createWindowManager(eligible, USER_ID_1, /* isTaskbarEduShowing= */ false); } + private LetterboxEduWindowManager createWindowManager(boolean eligible, boolean isDocked) { + return createWindowManager(eligible, USER_ID_1, /* isTaskbarEduShowing= */ + false, isDocked); + } + private LetterboxEduWindowManager createWindowManager(boolean eligible, int userId, boolean isTaskbarEduShowing) { + return createWindowManager(eligible, userId, isTaskbarEduShowing, /* isDocked */false); + } + + private LetterboxEduWindowManager createWindowManager(boolean eligible, + int userId, boolean isTaskbarEduShowing, boolean isDocked) { + doReturn(isDocked).when(mDockStateReader).isDocked(); LetterboxEduWindowManager windowManager = new LetterboxEduWindowManager(mContext, createTaskInfo(eligible, userId), mSyncTransactionQueue, mTaskListener, createDisplayLayout(), mTransitions, mOnDismissCallback, - mAnimationController); + mAnimationController, mDockStateReader); spyOn(windowManager); doReturn(mViewHost).when(windowManager).createSurfaceViewHost(); doReturn(isTaskbarEduShowing).when(windowManager).isTaskbarEduShowing(); - return windowManager; } -- cgit v1.2.3-59-g8ed1b From 9ee3fbb5a00eeb1caf93b1ce83950a0aa4e1307f Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Tue, 4 Oct 2022 11:26:45 +0000 Subject: Support starts a pair of intent/shortcut and task to split Add startIntentAndTask and startShortcutAndTask APIs to support starts a pair of intent/shortcut and task to split with shell-transition. Fix: 250875492 Test: atest WMShellFlickerTests Test: manual check start tasks or start intent/shortcut with task flow with shell-transition enabled Change-Id: Ia79cf5e1990089f2a92895fd09b8f813d9381093 --- .../android/wm/shell/splitscreen/ISplitScreen.aidl | 29 ++++++++--- .../shell/splitscreen/SplitScreenController.java | 22 ++++++++ .../wm/shell/splitscreen/StageCoordinator.java | 60 ++++++++++++++++++---- 3 files changed, 95 insertions(+), 16 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl index 03688b747f76..eb08d0ecbd06 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl @@ -83,6 +83,20 @@ interface ISplitScreen { int splitPosition, float splitRatio, in RemoteTransition remoteTransition, in InstanceId instanceId) = 10; + /** + * Starts a pair of intent and task in one transition. + */ + oneway void startIntentAndTask(in PendingIntent pendingIntent, in Intent fillInIntent, + in Bundle options1, int taskId, in Bundle options2, int sidePosition, float splitRatio, + in RemoteTransition remoteTransition, in InstanceId instanceId) = 16; + + /** + * Starts a pair of shortcut and task in one transition. + */ + oneway void startShortcutAndTask(in ShortcutInfo shortcutInfo, in Bundle options1, int taskId, + in Bundle options2, int splitPosition, float splitRatio, + in RemoteTransition remoteTransition, in InstanceId instanceId) = 17; + /** * Version of startTasks using legacy transition system. */ @@ -98,6 +112,13 @@ interface ISplitScreen { int splitPosition, float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 12; + /** + * Starts a pair of shortcut and task using legacy transition system. + */ + oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo, + in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio, + in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 15; + /** * Blocking call that notifies and gets additional split-screen targets when entering * recents (for example: the dividerBar). @@ -111,11 +132,5 @@ interface ISplitScreen { * does not expect split to currently be running. */ RemoteAnimationTarget[] onStartingSplitLegacy(in RemoteAnimationTarget[] appTargets) = 14; - - /** - * Starts a pair of shortcut and task using legacy transition system. - */ - oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo, - in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio, - in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 15; } +// Last id = 17 diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 8043b97164ce..d9c2871a1654 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -871,6 +871,28 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, instanceId)); } + @Override + public void startIntentAndTask(PendingIntent pendingIntent, Intent fillInIntent, + @Nullable Bundle options1, int taskId, @Nullable Bundle options2, + @SplitPosition int splitPosition, float splitRatio, + @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + executeRemoteCallWithTaskPermission(mController, "startIntentAndTask", + (controller) -> controller.mStageCoordinator.startIntentAndTask(pendingIntent, + fillInIntent, options1, taskId, options2, splitPosition, splitRatio, + remoteTransition, instanceId)); + } + + @Override + public void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1, + int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, + float splitRatio, @Nullable RemoteTransition remoteTransition, + InstanceId instanceId) { + executeRemoteCallWithTaskPermission(mController, "startShortcutAndTask", + (controller) -> controller.mStageCoordinator.startShortcutAndTask(shortcutInfo, + options1, taskId, options2, splitPosition, splitRatio, remoteTransition, + instanceId)); + } + @Override public void startShortcut(String packageName, String shortcutId, int position, @Nullable Bundle options, UserHandle user, InstanceId instanceId) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index d79e6a1f11d3..9102bd3a9b6a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -516,14 +516,55 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } /** Starts 2 tasks in one transition. */ - void startTasks(int sideTaskId, @Nullable Bundle sideOptions, int mainTaskId, - @Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio, + void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, + @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { final WindowContainerTransaction wct = new WindowContainerTransaction(); - mainOptions = mainOptions != null ? mainOptions : new Bundle(); - sideOptions = sideOptions != null ? sideOptions : new Bundle(); - setSideStagePosition(sidePosition, wct); + setSideStagePosition(splitPosition, wct); + options1 = options1 != null ? options1 : new Bundle(); + addActivityOptions(options1, mSideStage); + wct.startTask(taskId1, options1); + + startWithTask(wct, taskId2, options2, splitRatio, remoteTransition, instanceId); + } + + /** Start an intent and a task to a split pair in one transition. */ + void startIntentAndTask(PendingIntent pendingIntent, Intent fillInIntent, + @Nullable Bundle options1, int taskId, @Nullable Bundle options2, + @SplitPosition int splitPosition, float splitRatio, + @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + setSideStagePosition(splitPosition, wct); + options1 = options1 != null ? options1 : new Bundle(); + addActivityOptions(options1, mSideStage); + wct.sendPendingIntent(pendingIntent, fillInIntent, options1); + startWithTask(wct, taskId, options2, splitRatio, remoteTransition, instanceId); + } + + /** Starts a shortcut and a task to a split pair in one transition. */ + void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1, + int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, + float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + setSideStagePosition(splitPosition, wct); + options1 = options1 != null ? options1 : new Bundle(); + addActivityOptions(options1, mSideStage); + wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1); + + startWithTask(wct, taskId, options2, splitRatio, remoteTransition, instanceId); + } + + /** + * Starts with the second task to a split pair in one transition. + * + * @param wct transaction to start the first task + * @param instanceId if {@code null}, will not log. Otherwise it will be used in + * {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)} + */ + private void startWithTask(WindowContainerTransaction wct, int mainTaskId, + @Nullable Bundle mainOptions, float splitRatio, + @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { if (mMainStage.isActive()) { mMainStage.evictAllChildren(wct); mSideStage.evictAllChildren(wct); @@ -538,12 +579,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.setForceTranslucent(mRootTaskInfo.token, false); // Make sure the launch options will put tasks in the corresponding split roots + mainOptions = mainOptions != null ? mainOptions : new Bundle(); addActivityOptions(mainOptions, mMainStage); - addActivityOptions(sideOptions, mSideStage); // Add task launch requests wct.startTask(mainTaskId, mainOptions); - wct.startTask(sideTaskId, sideOptions); mSplitTransitions.startEnterTransition( TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null); @@ -593,6 +633,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } /** + * @param wct transaction to start the first task * @param instanceId if {@code null}, will not log. Otherwise it will be used in * {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)} */ @@ -1728,6 +1769,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @StageType private int getStageType(StageTaskListener stage) { + if (stage == null) return STAGE_TYPE_UNDEFINED; return stage == mMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; } @@ -1982,8 +2024,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - // TODO: fallback logic. Probably start a new transition to exit split before applying - // anything here. Ideally consolidate with transition-merging. + // TODO(b/250853925): fallback logic. Probably start a new transition to exit split before + // applying anything here. Ideally consolidate with transition-merging. if (info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) { if (mainChild == null && sideChild == null) { throw new IllegalStateException("Launched a task in split, but didn't receive any" -- cgit v1.2.3-59-g8ed1b From 2b227ee11da397003b1d67a97edc1817214f5f35 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Fri, 19 Aug 2022 09:38:18 -0700 Subject: Save ProtoLog viewer configuration in WMShell In this changeset topic - Build WMShell with protolog viewer config - it's built with SystemUI{,Google} packages and installed as /system_ext/etc/wmshell.protolog.json.gz - Deprecated the viewer configuration as a bundled resource - The `required` keyword in Android.bp does not propagate, need to add it in individual Android.bp file such as SystemUIGoogle/Android.bp - Push the generated viewer config file onto device in makepush script (in a separate CL) Bug: 244642224 Test: mp sysuig Change-Id: Ib7f37ec55d12493d6beec3aba66883415966b521 --- libs/WindowManager/Shell/Android.bp | 18 ++++++++++--- .../wm/shell/protolog/ShellProtoLogImpl.java | 30 +++++----------------- packages/SystemUI/Android.bp | 1 + 3 files changed, 23 insertions(+), 26 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 7960dec5080b..9ee52ccaf357 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -100,6 +100,21 @@ genrule { out: ["wm_shell_protolog.json"], } +genrule { + name: "protolog.json.gz", + srcs: [":generate-wm_shell_protolog.json"], + out: ["wmshell.protolog.json.gz"], + cmd: "$(location minigzip) -c < $(in) > $(out)", + tools: ["minigzip"], +} + +prebuilt_etc { + name: "wmshell.protolog.json.gz", + system_ext_specific: true, + src: ":protolog.json.gz", + filename_from_src: true, +} + // End ProtoLog java_library { @@ -123,9 +138,6 @@ android_library { resource_dirs: [ "res", ], - java_resources: [ - ":generate-wm_shell_protolog.json", - ], static_libs: [ "androidx.appcompat_appcompat", "androidx.arch.core_core-runtime", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java index 552ebde05274..93ffb3dc8115 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java @@ -17,22 +17,14 @@ package com.android.wm.shell.protolog; import android.annotation.Nullable; -import android.content.Context; -import android.util.Log; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.BaseProtoLogImpl; import com.android.internal.protolog.ProtoLogViewerConfigReader; import com.android.internal.protolog.common.IProtoLogGroup; -import com.android.wm.shell.R; import java.io.File; -import java.io.IOException; -import java.io.InputStream; import java.io.PrintWriter; -import org.json.JSONException; - /** * A service for the ProtoLog logging system. @@ -40,8 +32,9 @@ import org.json.JSONException; public class ShellProtoLogImpl extends BaseProtoLogImpl { private static final String TAG = "ProtoLogImpl"; private static final int BUFFER_CAPACITY = 1024 * 1024; - // TODO: Get the right path for the proto log file when we initialize the shell components - private static final String LOG_FILENAME = new File("wm_shell_log.pb").getAbsolutePath(); + // TODO: find a proper location to save the protolog message file + private static final String LOG_FILENAME = "/data/misc/wmtrace/shell_log.winscope"; + private static final String VIEWER_CONFIG_FILENAME = "/system_ext/etc/wmshell.protolog.json.gz"; private static ShellProtoLogImpl sServiceInstance = null; @@ -111,18 +104,8 @@ public class ShellProtoLogImpl extends BaseProtoLogImpl { } public int startTextLogging(String[] groups, PrintWriter pw) { - try (InputStream is = - getClass().getClassLoader().getResourceAsStream("wm_shell_protolog.json")){ - mViewerConfig.loadViewerConfig(is); - return setLogging(true /* setTextLogging */, true, pw, groups); - } catch (IOException e) { - Log.i(TAG, "Unable to load log definitions: IOException while reading " - + "wm_shell_protolog. " + e); - } catch (JSONException e) { - Log.i(TAG, "Unable to load log definitions: JSON parsing exception while reading " - + "wm_shell_protolog. " + e); - } - return -1; + mViewerConfig.loadViewerConfig(pw, VIEWER_CONFIG_FILENAME); + return setLogging(true /* setTextLogging */, true, pw, groups); } public int stopTextLogging(String[] groups, PrintWriter pw) { @@ -130,7 +113,8 @@ public class ShellProtoLogImpl extends BaseProtoLogImpl { } private ShellProtoLogImpl() { - super(new File(LOG_FILENAME), null, BUFFER_CAPACITY, new ProtoLogViewerConfigReader()); + super(new File(LOG_FILENAME), VIEWER_CONFIG_FILENAME, BUFFER_CAPACITY, + new ProtoLogViewerConfigReader()); } } diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index df6f08df7c56..2f5b5f456d0b 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -294,5 +294,6 @@ android_app { dxflags: ["--multi-dex"], required: [ "privapp_whitelist_com.android.systemui", + "wmshell.protolog.json.gz", ], } -- cgit v1.2.3-59-g8ed1b From 990c149544e2fa5d7c38e83e3eb4fed23854adae Mon Sep 17 00:00:00 2001 From: Ben Lin Date: Tue, 27 Sep 2022 15:29:39 -0700 Subject: Bring other Desktop apps to front when opening a new one. This enables the launcher to bring all desktop apps to the front when launching a new app. This is only enabled when Desktop Mode feature flag is enabled and Shell transition is enabled. Bug: None Test: Manual, launch new apps from launcher Change-Id: I5ae3941c7d27db3180ea6725a37868416b6649fb --- .../shell/desktopmode/DesktopModeController.java | 48 ++++++++++++++++++++-- .../android/wm/shell/transition/Transitions.java | 16 ++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index 99739c457aa6..dc7ed15dbe3a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -19,6 +19,7 @@ package com.android.wm.shell.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.TRANSIT_CHANGE; +import static android.view.WindowManager.TRANSIT_OPEN; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; @@ -29,13 +30,18 @@ import android.content.Context; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; +import android.os.IBinder; import android.os.UserHandle; import android.provider.Settings; import android.util.ArraySet; +import android.view.SurfaceControl; import android.window.DisplayAreaInfo; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; import androidx.annotation.BinderThread; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; @@ -55,7 +61,8 @@ import java.util.Comparator; /** * Handles windowing changes when desktop mode system setting changes */ -public class DesktopModeController implements RemoteCallable { +public class DesktopModeController implements RemoteCallable, + Transitions.TransitionHandler { private final Context mContext; private final ShellTaskOrganizer mShellTaskOrganizer; @@ -89,6 +96,7 @@ public class DesktopModeController implements RemoteCallable activeTasks = mDesktopModeTaskRepository.getActiveTasks(); ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size()); ArrayList taskInfos = new ArrayList<>(); @@ -173,7 +181,12 @@ public class DesktopModeController implements RemoteCallable { "This shouldn't happen, maybe the default handler is broken."); } + /** + * Gives every handler (in order) a chance to handle request until one consumes the transition. + * @return the WindowContainerTransaction given by the handler which consumed the transition. + */ + public WindowContainerTransaction dispatchRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request, @Nullable TransitionHandler skip) { + for (int i = mHandlers.size() - 1; i >= 0; --i) { + if (mHandlers.get(i) == skip) continue; + WindowContainerTransaction wct = mHandlers.get(i).handleRequest(transition, request); + if (wct != null) { + return wct; + } + } + return null; + } + /** Special version of finish just for dealing with no-op/invalid transitions. */ private void onAbort(IBinder transition) { onFinish(transition, null /* wct */, null /* wctCB */, true /* abort */); -- cgit v1.2.3-59-g8ed1b From 73166e71d0c9e681cc03b13febd67928da292003 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Mon, 3 Oct 2022 09:48:41 -0700 Subject: Put window decors at one place There are some juggling between task listeners and the transition observer of window decors because they are hosts together with its corresponding task states. This change can remove those juggling by putting all of them at one place. Bug: 250622579 Test: atest WMShellUnitTests Change-Id: Ic1db1633a5e79e2bcea5885b8fc2f345fa05fb1e --- .../android/wm/shell/dagger/WMShellBaseModule.java | 12 +- .../com/android/wm/shell/dagger/WMShellModule.java | 18 +- .../wm/shell/freeform/FreeformTaskListener.java | 174 ++----------------- .../freeform/FreeformTaskTransitionHandler.java | 4 +- .../freeform/FreeformTaskTransitionObserver.java | 133 ++++----------- .../shell/fullscreen/FullscreenTaskListener.java | 188 ++------------------- .../windowdecor/CaptionWindowDecorViewModel.java | 44 +++-- .../wm/shell/windowdecor/WindowDecorViewModel.java | 31 ++-- .../FreeformTaskTransitionObserverTest.java | 91 ++-------- 9 files changed, 131 insertions(+), 564 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 353014264990..6645e7828aea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -289,17 +289,17 @@ public abstract class WMShellBaseModule { // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride} @BindsOptionalOf @DynamicOverride - abstract FullscreenTaskListener optionalFullscreenTaskListener(); + abstract FullscreenTaskListener optionalFullscreenTaskListener(); @WMSingleton @Provides - static FullscreenTaskListener provideFullscreenTaskListener( - @DynamicOverride Optional> fullscreenTaskListener, + static FullscreenTaskListener provideFullscreenTaskListener( + @DynamicOverride Optional fullscreenTaskListener, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Optional recentTasksOptional, - Optional> windowDecorViewModelOptional) { + Optional windowDecorViewModelOptional) { if (fullscreenTaskListener.isPresent()) { return fullscreenTaskListener.get(); } else { @@ -313,7 +313,7 @@ public abstract class WMShellBaseModule { // @BindsOptionalOf - abstract WindowDecorViewModel optionalWindowDecorViewModel(); + abstract WindowDecorViewModel optionalWindowDecorViewModel(); // // Unfold transition @@ -765,7 +765,7 @@ public abstract class WMShellBaseModule { Optional splitScreenOptional, Optional pipOptional, Optional pipTouchHandlerOptional, - FullscreenTaskListener fullscreenTaskListener, + FullscreenTaskListener fullscreenTaskListener, Optional unfoldAnimationController, Optional unfoldTransitionHandler, Optional freeformComponents, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 47b665970d28..461d7dcf30ad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -55,7 +55,6 @@ import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.freeform.FreeformTaskTransitionHandler; import com.android.wm.shell.freeform.FreeformTaskTransitionObserver; -import com.android.wm.shell.fullscreen.FullscreenTaskListener; import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; @@ -183,7 +182,7 @@ public abstract class WMShellModule { @WMSingleton @Provides - static WindowDecorViewModel provideWindowDecorViewModel( + static WindowDecorViewModel provideWindowDecorViewModel( Context context, @ShellMainThread Handler mainHandler, @ShellMainThread Choreographer mainChoreographer, @@ -209,7 +208,7 @@ public abstract class WMShellModule { @Provides @DynamicOverride static FreeformComponents provideFreeformComponents( - FreeformTaskListener taskListener, + FreeformTaskListener taskListener, FreeformTaskTransitionHandler transitionHandler, FreeformTaskTransitionObserver transitionObserver) { return new FreeformComponents( @@ -218,18 +217,18 @@ public abstract class WMShellModule { @WMSingleton @Provides - static FreeformTaskListener provideFreeformTaskListener( + static FreeformTaskListener provideFreeformTaskListener( Context context, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Optional desktopModeTaskRepository, - WindowDecorViewModel windowDecorViewModel) { + WindowDecorViewModel windowDecorViewModel) { // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic // override for this controller from the base module ShellInit init = FreeformComponents.isFreeformEnabled(context) ? shellInit : null; - return new FreeformTaskListener<>(init, shellTaskOrganizer, desktopModeTaskRepository, + return new FreeformTaskListener(init, shellTaskOrganizer, desktopModeTaskRepository, windowDecorViewModel); } @@ -238,7 +237,7 @@ public abstract class WMShellModule { static FreeformTaskTransitionHandler provideFreeformTaskTransitionHandler( ShellInit shellInit, Transitions transitions, - WindowDecorViewModel windowDecorViewModel) { + WindowDecorViewModel windowDecorViewModel) { return new FreeformTaskTransitionHandler(shellInit, transitions, windowDecorViewModel); } @@ -248,10 +247,9 @@ public abstract class WMShellModule { Context context, ShellInit shellInit, Transitions transitions, - FullscreenTaskListener fullscreenTaskListener, - FreeformTaskListener freeformTaskListener) { + WindowDecorViewModel windowDecorViewModel) { return new FreeformTaskTransitionObserver( - context, shellInit, transitions, fullscreenTaskListener, freeformTaskListener); + context, shellInit, transitions, windowDecorViewModel); } // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java index e2d5a499d1e1..f82a34621262 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -19,12 +19,8 @@ package com.android.wm.shell.freeform; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM; import android.app.ActivityManager.RunningTaskInfo; -import android.util.Log; import android.util.SparseArray; import android.view.SurfaceControl; -import android.window.TransitionInfo; - -import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; @@ -41,31 +37,26 @@ import java.util.Optional; /** * {@link ShellTaskOrganizer.TaskListener} for {@link * ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}. - * - * @param the type of window decoration instance */ -public class FreeformTaskListener - implements ShellTaskOrganizer.TaskListener { +public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener { private static final String TAG = "FreeformTaskListener"; private final ShellTaskOrganizer mShellTaskOrganizer; private final Optional mDesktopModeTaskRepository; - private final WindowDecorViewModel mWindowDecorationViewModel; + private final WindowDecorViewModel mWindowDecorationViewModel; - private final SparseArray> mTasks = new SparseArray<>(); - private final SparseArray mWindowDecorOfVanishedTasks = new SparseArray<>(); + private final SparseArray mTasks = new SparseArray<>(); - private static class State { + private static class State { RunningTaskInfo mTaskInfo; SurfaceControl mLeash; - T mWindowDecoration; } public FreeformTaskListener( ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Optional desktopModeTaskRepository, - WindowDecorViewModel windowDecorationViewModel) { + WindowDecorViewModel windowDecorationViewModel) { mShellTaskOrganizer = shellTaskOrganizer; mWindowDecorationViewModel = windowDecorationViewModel; mDesktopModeTaskRepository = desktopModeTaskRepository; @@ -80,13 +71,18 @@ public class FreeformTaskListener @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { + if (mTasks.get(taskInfo.taskId) != null) { + throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId); + } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d", taskInfo.taskId); - final State state = createOrUpdateTaskState(taskInfo, leash); + final State state = new State(); + state.mTaskInfo = taskInfo; + state.mLeash = leash; + mTasks.put(taskInfo.taskId, state); if (!Transitions.ENABLE_SHELL_TRANSITIONS) { SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - state.mWindowDecoration = - mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t); + mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t); t.apply(); } @@ -97,28 +93,8 @@ public class FreeformTaskListener } } - private State createOrUpdateTaskState(RunningTaskInfo taskInfo, SurfaceControl leash) { - State state = mTasks.get(taskInfo.taskId); - if (state != null) { - updateTaskInfo(taskInfo); - return state; - } - - state = new State<>(); - state.mTaskInfo = taskInfo; - state.mLeash = leash; - mTasks.put(taskInfo.taskId, state); - - return state; - } - @Override public void onTaskVanished(RunningTaskInfo taskInfo) { - final State state = mTasks.get(taskInfo.taskId); - if (state == null) { - // This is possible if the transition happens before this method. - return; - } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d", taskInfo.taskId); mTasks.remove(taskInfo.taskId); @@ -129,26 +105,18 @@ public class FreeformTaskListener mDesktopModeTaskRepository.ifPresent(it -> it.removeActiveTask(taskInfo.taskId)); } - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - // Save window decorations of closing tasks so that we can hand them over to the - // transition system if this method happens before the transition. In case where the - // transition didn't happen, it'd be cleared when the next transition finished. - if (state.mWindowDecoration != null) { - mWindowDecorOfVanishedTasks.put(taskInfo.taskId, state.mWindowDecoration); - } - return; + if (!Transitions.ENABLE_SHELL_TRANSITIONS) { + mWindowDecorationViewModel.destroyWindowDecoration(taskInfo); } - releaseWindowDecor(state.mWindowDecoration); } @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { - final State state = updateTaskInfo(taskInfo); + final State state = mTasks.get(taskInfo.taskId); + state.mTaskInfo = taskInfo; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d", taskInfo.taskId); - if (state.mWindowDecoration != null) { - mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration); - } + mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo); if (DesktopModeStatus.IS_SUPPORTED) { if (taskInfo.isVisible) { @@ -159,15 +127,6 @@ public class FreeformTaskListener } } - private State updateTaskInfo(RunningTaskInfo taskInfo) { - final State state = mTasks.get(taskInfo.taskId); - if (state == null) { - throw new RuntimeException("Task info changed before appearing: #" + taskInfo.taskId); - } - state.mTaskInfo = taskInfo; - return state; - } - @Override public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { b.setParent(findTaskSurface(taskId)); @@ -186,103 +145,6 @@ public class FreeformTaskListener return mTasks.get(taskId).mLeash; } - /** - * Creates a window decoration for a transition. - * - * @param change the change of this task transition that needs to have the task layer as the - * leash - * @return {@code true} if it creates the window decoration; {@code false} otherwise - */ - boolean createWindowDecoration( - TransitionInfo.Change change, - SurfaceControl.Transaction startT, - SurfaceControl.Transaction finishT) { - final State state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash()); - if (state.mWindowDecoration != null) { - return false; - } - state.mWindowDecoration = mWindowDecorationViewModel.createWindowDecoration( - state.mTaskInfo, state.mLeash, startT, finishT); - return true; - } - - /** - * Gives out the ownership of the task's window decoration. The given task is leaving (of has - * left) this task listener. This is the transition system asking for the ownership. - * - * @param taskInfo the maximizing task - * @return the window decor of the maximizing task if any - */ - T giveWindowDecoration( - RunningTaskInfo taskInfo, - SurfaceControl.Transaction startT, - SurfaceControl.Transaction finishT) { - T windowDecor; - final State state = mTasks.get(taskInfo.taskId); - if (state != null) { - windowDecor = state.mWindowDecoration; - state.mWindowDecoration = null; - } else { - windowDecor = - mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId); - } - if (windowDecor == null) { - return null; - } - mWindowDecorationViewModel.setupWindowDecorationForTransition( - taskInfo, startT, finishT, windowDecor); - return windowDecor; - } - - /** - * Adopt the incoming window decoration and lets the window decoration prepare for a transition. - * - * @param change the change of this task transition that needs to have the task layer as the - * leash - * @param startT the start transaction of this transition - * @param finishT the finish transaction of this transition - * @param windowDecor the window decoration to adopt - * @return {@code true} if it adopts the window decoration; {@code false} otherwise - */ - boolean adoptWindowDecoration( - TransitionInfo.Change change, - SurfaceControl.Transaction startT, - SurfaceControl.Transaction finishT, - @Nullable AutoCloseable windowDecor) { - final State state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash()); - state.mWindowDecoration = mWindowDecorationViewModel.adoptWindowDecoration(windowDecor); - if (state.mWindowDecoration != null) { - mWindowDecorationViewModel.setupWindowDecorationForTransition( - state.mTaskInfo, startT, finishT, state.mWindowDecoration); - return true; - } else { - state.mWindowDecoration = mWindowDecorationViewModel.createWindowDecoration( - state.mTaskInfo, state.mLeash, startT, finishT); - return false; - } - } - - void onTaskTransitionFinished() { - if (mWindowDecorOfVanishedTasks.size() == 0) { - return; - } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, - "Clearing window decors of vanished tasks. There could be visual defects " - + "if any of them is used later in transitions."); - for (int i = 0; i < mWindowDecorOfVanishedTasks.size(); ++i) { - releaseWindowDecor(mWindowDecorOfVanishedTasks.valueAt(i)); - } - mWindowDecorOfVanishedTasks.clear(); - } - - private void releaseWindowDecor(T windowDecor) { - try { - windowDecor.close(); - } catch (Exception e) { - Log.e(TAG, "Failed to release window decoration.", e); - } - } - @Override public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java index fd4c85fad77f..04fc79acadbd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java @@ -46,14 +46,14 @@ public class FreeformTaskTransitionHandler implements Transitions.TransitionHandler, FreeformTaskTransitionStarter { private final Transitions mTransitions; - private final WindowDecorViewModel mWindowDecorViewModel; + private final WindowDecorViewModel mWindowDecorViewModel; private final List mPendingTransitionTokens = new ArrayList<>(); public FreeformTaskTransitionHandler( ShellInit shellInit, Transitions transitions, - WindowDecorViewModel windowDecorViewModel) { + WindowDecorViewModel windowDecorViewModel) { mTransitions = transitions; mWindowDecorViewModel = windowDecorViewModel; if (Transitions.ENABLE_SHELL_TRANSITIONS) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java index 17d60671e964..f4888fbb2bb9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java @@ -16,13 +16,9 @@ package com.android.wm.shell.freeform; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; - import android.app.ActivityManager; import android.content.Context; import android.os.IBinder; -import android.util.Log; import android.view.SurfaceControl; import android.view.WindowManager; import android.window.TransitionInfo; @@ -31,9 +27,9 @@ import android.window.WindowContainerToken; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import com.android.wm.shell.fullscreen.FullscreenTaskListener; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.util.ArrayList; import java.util.Collections; @@ -47,23 +43,19 @@ import java.util.Map; * be a part of transitions. */ public class FreeformTaskTransitionObserver implements Transitions.TransitionObserver { - private static final String TAG = "FreeformTO"; - private final Transitions mTransitions; - private final FreeformTaskListener mFreeformTaskListener; - private final FullscreenTaskListener mFullscreenTaskListener; + private final WindowDecorViewModel mWindowDecorViewModel; - private final Map> mTransitionToWindowDecors = new HashMap<>(); + private final Map> mTransitionToTaskInfo = + new HashMap<>(); public FreeformTaskTransitionObserver( Context context, ShellInit shellInit, Transitions transitions, - FullscreenTaskListener fullscreenTaskListener, - FreeformTaskListener freeformTaskListener) { + WindowDecorViewModel windowDecorViewModel) { mTransitions = transitions; - mFreeformTaskListener = freeformTaskListener; - mFullscreenTaskListener = fullscreenTaskListener; + mWindowDecorViewModel = windowDecorViewModel; if (Transitions.ENABLE_SHELL_TRANSITIONS && FreeformComponents.isFreeformEnabled(context)) { shellInit.addInitCallback(this::onInit, this); } @@ -80,7 +72,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT) { - final ArrayList windowDecors = new ArrayList<>(); + final ArrayList taskInfoList = new ArrayList<>(); final ArrayList taskParents = new ArrayList<>(); for (TransitionInfo.Change change : info.getChanges()) { if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) { @@ -110,92 +102,40 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs onOpenTransitionReady(change, startT, finishT); break; case WindowManager.TRANSIT_CLOSE: { - onCloseTransitionReady(change, windowDecors, startT, finishT); + taskInfoList.add(change.getTaskInfo()); + onCloseTransitionReady(change, startT, finishT); break; } case WindowManager.TRANSIT_CHANGE: - onChangeTransitionReady(info.getType(), change, startT, finishT); + onChangeTransitionReady(change, startT, finishT); break; } } - if (!windowDecors.isEmpty()) { - mTransitionToWindowDecors.put(transition, windowDecors); - } + mTransitionToTaskInfo.put(transition, taskInfoList); } private void onOpenTransitionReady( TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { - switch (change.getTaskInfo().getWindowingMode()){ - case WINDOWING_MODE_FREEFORM: - mFreeformTaskListener.createWindowDecoration(change, startT, finishT); - break; - case WINDOWING_MODE_FULLSCREEN: - mFullscreenTaskListener.createWindowDecoration(change, startT, finishT); - break; - } + mWindowDecorViewModel.createWindowDecoration( + change.getTaskInfo(), change.getLeash(), startT, finishT); } private void onCloseTransitionReady( TransitionInfo.Change change, - ArrayList windowDecors, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { - final AutoCloseable windowDecor; - switch (change.getTaskInfo().getWindowingMode()) { - case WINDOWING_MODE_FREEFORM: - windowDecor = mFreeformTaskListener.giveWindowDecoration(change.getTaskInfo(), - startT, finishT); - break; - case WINDOWING_MODE_FULLSCREEN: - windowDecor = mFullscreenTaskListener.giveWindowDecoration(change.getTaskInfo(), - startT, finishT); - break; - default: - windowDecor = null; - } - if (windowDecor != null) { - windowDecors.add(windowDecor); - } + mWindowDecorViewModel.setupWindowDecorationForTransition( + change.getTaskInfo(), startT, finishT); } private void onChangeTransitionReady( - int type, TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { - AutoCloseable windowDecor = null; - - boolean adopted = false; - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { - windowDecor = mFreeformTaskListener.giveWindowDecoration( - change.getTaskInfo(), startT, finishT); - if (windowDecor != null) { - adopted = mFullscreenTaskListener.adoptWindowDecoration( - change, startT, finishT, windowDecor); - } else { - // will return false if it already has the window decor. - adopted = mFullscreenTaskListener.createWindowDecoration(change, startT, finishT); - } - } - - if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { - windowDecor = mFullscreenTaskListener.giveWindowDecoration( - change.getTaskInfo(), startT, finishT); - if (windowDecor != null) { - adopted = mFreeformTaskListener.adoptWindowDecoration( - change, startT, finishT, windowDecor); - } else { - // will return false if it already has the window decor. - adopted = mFreeformTaskListener.createWindowDecoration(change, startT, finishT); - } - } - - if (!adopted) { - releaseWindowDecor(windowDecor); - } + mWindowDecorViewModel.setupWindowDecorationForTransition( + change.getTaskInfo(), startT, finishT); } @Override @@ -203,43 +143,32 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs @Override public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) { - final List windowDecorsOfMerged = mTransitionToWindowDecors.get(merged); - if (windowDecorsOfMerged == null) { + final List infoOfMerged = + mTransitionToTaskInfo.get(merged); + if (infoOfMerged == null) { // We are adding window decorations of the merged transition to them of the playing // transition so if there is none of them there is nothing to do. return; } - mTransitionToWindowDecors.remove(merged); + mTransitionToTaskInfo.remove(merged); - final List windowDecorsOfPlaying = mTransitionToWindowDecors.get(playing); - if (windowDecorsOfPlaying != null) { - windowDecorsOfPlaying.addAll(windowDecorsOfMerged); + final List infoOfPlaying = + mTransitionToTaskInfo.get(playing); + if (infoOfPlaying != null) { + infoOfPlaying.addAll(infoOfMerged); } else { - mTransitionToWindowDecors.put(playing, windowDecorsOfMerged); + mTransitionToTaskInfo.put(playing, infoOfMerged); } } @Override public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) { - final List windowDecors = mTransitionToWindowDecors.getOrDefault( - transition, Collections.emptyList()); - mTransitionToWindowDecors.remove(transition); + final List taskInfo = + mTransitionToTaskInfo.getOrDefault(transition, Collections.emptyList()); + mTransitionToTaskInfo.remove(transition); - for (AutoCloseable windowDecor : windowDecors) { - releaseWindowDecor(windowDecor); - } - mFullscreenTaskListener.onTaskTransitionFinished(); - mFreeformTaskListener.onTaskTransitionFinished(); - } - - private static void releaseWindowDecor(AutoCloseable windowDecor) { - if (windowDecor == null) { - return; - } - try { - windowDecor.close(); - } catch (Exception e) { - Log.e(TAG, "Failed to release window decoration.", e); + for (int i = 0; i < taskInfo.size(); ++i) { + mWindowDecorViewModel.destroyWindowDecoration(taskInfo.get(i)); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java index 76e296bb8c61..75a4091c7d78 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java @@ -22,13 +22,10 @@ import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.graphics.Point; -import android.util.Log; import android.util.SparseArray; import android.view.SurfaceControl; -import android.window.TransitionInfo; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; @@ -46,23 +43,20 @@ import java.util.Optional; * Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}. * @param the type of window decoration instance */ -public class FullscreenTaskListener - implements ShellTaskOrganizer.TaskListener { +public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { private static final String TAG = "FullscreenTaskListener"; private final ShellTaskOrganizer mShellTaskOrganizer; - private final SparseArray> mTasks = new SparseArray<>(); - private final SparseArray mWindowDecorOfVanishedTasks = new SparseArray<>(); + private final SparseArray mTasks = new SparseArray<>(); - private static class State { + private static class State { RunningTaskInfo mTaskInfo; SurfaceControl mLeash; - T mWindowDecoration; } private final SyncTransactionQueue mSyncQueue; private final Optional mRecentTasksOptional; - private final Optional> mWindowDecorViewModelOptional; + private final Optional mWindowDecorViewModelOptional; /** * This constructor is used by downstream products. */ @@ -75,7 +69,7 @@ public class FullscreenTaskListener ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Optional recentTasksOptional, - Optional> windowDecorViewModelOptional) { + Optional windowDecorViewModelOptional) { mShellTaskOrganizer = shellTaskOrganizer; mSyncQueue = syncQueue; mRecentTasksOptional = recentTasksOptional; @@ -98,21 +92,21 @@ public class FullscreenTaskListener ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d", taskInfo.taskId); final Point positionInParent = taskInfo.positionInParent; - final State state = new State(); + final State state = new State(); state.mLeash = leash; state.mTaskInfo = taskInfo; mTasks.put(taskInfo.taskId, state); if (Transitions.ENABLE_SHELL_TRANSITIONS) return; updateRecentsForVisibleFullscreenTask(taskInfo); + boolean createdWindowDecor = false; if (mWindowDecorViewModelOptional.isPresent()) { SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - state.mWindowDecoration = - mWindowDecorViewModelOptional.get().createWindowDecoration(taskInfo, - leash, t, t); + createdWindowDecor = mWindowDecorViewModelOptional.get() + .createWindowDecoration(taskInfo, leash, t, t); t.apply(); } - if (state.mWindowDecoration == null) { + if (!createdWindowDecor) { mSyncQueue.runInSync(t -> { // Reset several properties back to fullscreen (PiP, for example, leaves all these // properties in a bad state). @@ -127,12 +121,11 @@ public class FullscreenTaskListener @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { - final State state = mTasks.get(taskInfo.taskId); + final State state = mTasks.get(taskInfo.taskId); final Point oldPositionInParent = state.mTaskInfo.positionInParent; state.mTaskInfo = taskInfo; - if (state.mWindowDecoration != null) { - mWindowDecorViewModelOptional.get().onTaskInfoChanged( - state.mTaskInfo, state.mWindowDecoration); + if (mWindowDecorViewModelOptional.isPresent()) { + mWindowDecorViewModelOptional.get().onTaskInfoChanged(state.mTaskInfo); } if (Transitions.ENABLE_SHELL_TRANSITIONS) return; updateRecentsForVisibleFullscreenTask(taskInfo); @@ -147,160 +140,13 @@ public class FullscreenTaskListener @Override public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - final State state = mTasks.get(taskInfo.taskId); - if (state == null) { - // This is possible if the transition happens before this method. - return; - } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d", taskInfo.taskId); mTasks.remove(taskInfo.taskId); - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - // Save window decorations of closing tasks so that we can hand them over to the - // transition system if this method happens before the transition. In case where the - // transition didn't happen, it'd be cleared when the next transition finished. - if (state.mWindowDecoration != null) { - mWindowDecorOfVanishedTasks.put(taskInfo.taskId, state.mWindowDecoration); - } - return; - } - releaseWindowDecor(state.mWindowDecoration); - } - - /** - * Creates a window decoration for a transition. - * - * @param change the change of this task transition that needs to have the task layer as the - * leash - * @return {@code true} if a decoration was actually created. - */ - public boolean createWindowDecoration(TransitionInfo.Change change, - SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { - final State state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash()); - if (!mWindowDecorViewModelOptional.isPresent()) return false; - if (state.mWindowDecoration != null) { - // Already has a decoration. - return false; - } - T newWindowDecor = mWindowDecorViewModelOptional.get().createWindowDecoration( - state.mTaskInfo, state.mLeash, startT, finishT); - if (newWindowDecor != null) { - state.mWindowDecoration = newWindowDecor; - return true; - } - return false; - } - - /** - * Adopt the incoming window decoration and lets the window decoration prepare for a transition. - * - * @param change the change of this task transition that needs to have the task layer as the - * leash - * @param startT the start transaction of this transition - * @param finishT the finish transaction of this transition - * @param windowDecor the window decoration to adopt - * @return {@code true} if it adopts the window decoration; {@code false} otherwise - */ - public boolean adoptWindowDecoration( - TransitionInfo.Change change, - SurfaceControl.Transaction startT, - SurfaceControl.Transaction finishT, - @Nullable AutoCloseable windowDecor) { - if (!mWindowDecorViewModelOptional.isPresent()) { - return false; - } - final State state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash()); - state.mWindowDecoration = mWindowDecorViewModelOptional.get().adoptWindowDecoration( - windowDecor); - if (state.mWindowDecoration != null) { - mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition( - state.mTaskInfo, startT, finishT, state.mWindowDecoration); - return true; - } else { - T newWindowDecor = mWindowDecorViewModelOptional.get().createWindowDecoration( - state.mTaskInfo, state.mLeash, startT, finishT); - if (newWindowDecor != null) { - state.mWindowDecoration = newWindowDecor; - } - return false; - } - } - - /** - * Clear window decors of vanished tasks. - */ - public void onTaskTransitionFinished() { - if (mWindowDecorOfVanishedTasks.size() == 0) { - return; - } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, - "Clearing window decors of vanished tasks. There could be visual defects " - + "if any of them is used later in transitions."); - for (int i = 0; i < mWindowDecorOfVanishedTasks.size(); ++i) { - releaseWindowDecor(mWindowDecorOfVanishedTasks.valueAt(i)); - } - mWindowDecorOfVanishedTasks.clear(); - } - - /** - * Gives out the ownership of the task's window decoration. The given task is leaving (of has - * left) this task listener. This is the transition system asking for the ownership. - * - * @param taskInfo the maximizing task - * @return the window decor of the maximizing task if any - */ - public T giveWindowDecoration( - ActivityManager.RunningTaskInfo taskInfo, - SurfaceControl.Transaction startT, - SurfaceControl.Transaction finishT) { - T windowDecor; - final State state = mTasks.get(taskInfo.taskId); - if (state != null) { - windowDecor = state.mWindowDecoration; - state.mWindowDecoration = null; - } else { - windowDecor = - mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId); - } - if (mWindowDecorViewModelOptional.isPresent() && windowDecor != null) { - mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition( - taskInfo, startT, finishT, windowDecor); - } - - return windowDecor; - } - - private State createOrUpdateTaskState(ActivityManager.RunningTaskInfo taskInfo, - SurfaceControl leash) { - State state = mTasks.get(taskInfo.taskId); - if (state != null) { - updateTaskInfo(taskInfo); - return state; - } - - state = new State(); - state.mTaskInfo = taskInfo; - state.mLeash = leash; - mTasks.put(taskInfo.taskId, state); - - return state; - } - - private State updateTaskInfo(ActivityManager.RunningTaskInfo taskInfo) { - final State state = mTasks.get(taskInfo.taskId); - state.mTaskInfo = taskInfo; - return state; - } - - private void releaseWindowDecor(T windowDecor) { - if (windowDecor == null) { - return; - } - try { - windowDecor.close(); - } catch (Exception e) { - Log.e(TAG, "Failed to release window decoration.", e); + if (Transitions.ENABLE_SHELL_TRANSITIONS) return; + if (mWindowDecorViewModelOptional.isPresent()) { + mWindowDecorViewModelOptional.get().destroyWindowDecoration(taskInfo); } } @@ -342,6 +188,4 @@ public class FullscreenTaskListener public String toString() { return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN); } - - } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 9e49b51e1504..3df33f346e84 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -26,6 +26,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.content.Context; import android.os.Handler; +import android.util.SparseArray; import android.view.Choreographer; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -46,7 +47,7 @@ import com.android.wm.shell.transition.Transitions; * View model for the window decoration with a caption and shadows. Works with * {@link CaptionWindowDecoration}. */ -public class CaptionWindowDecorViewModel implements WindowDecorViewModel { +public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private final ActivityTaskManager mActivityTaskManager; private final ShellTaskOrganizer mTaskOrganizer; private final Context mContext; @@ -57,6 +58,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel mWindowDecorByTaskId = new SparseArray<>(); + public CaptionWindowDecorViewModel( Context context, Handler mainHandler, @@ -81,12 +84,12 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel The actual decoration type */ -public interface WindowDecorViewModel { +public interface WindowDecorViewModel { /** * Sets the transition starter that starts freeform task transitions. @@ -50,29 +46,19 @@ public interface WindowDecorViewModel { * @param finishT the finish transaction to restore states after the transition * @return the window decoration object */ - @Nullable T createWindowDecoration( + boolean createWindowDecoration( ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT); - /** - * Adopts the window decoration if possible. - * May be {@code null} if a window decor is not needed or the given one is incompatible. - * - * @param windowDecor the potential window decoration to adopt - * @return the window decoration if it can be adopted, or {@code null} otherwise. - */ - @Nullable T adoptWindowDecoration(@Nullable AutoCloseable windowDecor); - /** * Notifies a task info update on the given task, with the window decoration created previously * for this task by {@link #createWindowDecoration}. * * @param taskInfo the new task info of the task - * @param windowDecoration the window decoration created for the task */ - void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo, T windowDecoration); + void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo); /** * Notifies a transition is about to start about the given task to give the window decoration a @@ -80,11 +66,16 @@ public interface WindowDecorViewModel { * * @param startT the start transaction to be applied before the transition * @param finishT the finish transaction to restore states after the transition - * @param windowDecoration the window decoration created for the task */ void setupWindowDecorationForTransition( ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, - SurfaceControl.Transaction finishT, - T windowDecoration); + SurfaceControl.Transaction finishT); + + /** + * Destroys the window decoration of the give task. + * + * @param taskInfo the info of the task + */ + void destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java index 0fd5cb081ea9..7068a84c3056 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java @@ -17,17 +17,10 @@ package com.android.wm.shell.freeform; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; -import static com.android.wm.shell.transition.Transitions.TRANSIT_MAXIMIZE; -import static com.android.wm.shell.transition.Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE; - -import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.same; @@ -44,9 +37,9 @@ import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; -import com.android.wm.shell.fullscreen.FullscreenTaskListener; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.windowdecor.WindowDecorViewModel; import org.junit.Before; import org.junit.Test; @@ -65,9 +58,7 @@ public class FreeformTaskTransitionObserverTest { @Mock private Transitions mTransitions; @Mock - private FullscreenTaskListener mFullscreenTaskListener; - @Mock - private FreeformTaskListener mFreeformTaskListener; + private WindowDecorViewModel mWindowDecorViewModel; private FreeformTaskTransitionObserver mTransitionObserver; @@ -82,7 +73,7 @@ public class FreeformTaskTransitionObserverTest { doReturn(pm).when(context).getPackageManager(); mTransitionObserver = new FreeformTaskTransitionObserver( - context, mShellInit, mTransitions, mFullscreenTaskListener, mFreeformTaskListener); + context, mShellInit, mTransitions, mWindowDecorViewModel); if (Transitions.ENABLE_SHELL_TRANSITIONS) { final ArgumentCaptor initRunnableCaptor = ArgumentCaptor.forClass( Runnable.class); @@ -112,11 +103,12 @@ public class FreeformTaskTransitionObserverTest { mTransitionObserver.onTransitionReady(transition, info, startT, finishT); mTransitionObserver.onTransitionStarting(transition); - verify(mFreeformTaskListener).createWindowDecoration(change, startT, finishT); + verify(mWindowDecorViewModel).createWindowDecoration( + change.getTaskInfo(), change.getLeash(), startT, finishT); } @Test - public void testObtainsWindowDecorOnCloseTransition_freeform() { + public void testPreparesWindowDecorOnCloseTransition_freeform() { final TransitionInfo.Change change = createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM); final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 0); @@ -128,7 +120,8 @@ public class FreeformTaskTransitionObserverTest { mTransitionObserver.onTransitionReady(transition, info, startT, finishT); mTransitionObserver.onTransitionStarting(transition); - verify(mFreeformTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT); + verify(mWindowDecorViewModel).setupWindowDecorationForTransition( + change.getTaskInfo(), startT, finishT); } @Test @@ -138,17 +131,13 @@ public class FreeformTaskTransitionObserverTest { final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 0); info.addChange(change); - final AutoCloseable windowDecor = mock(AutoCloseable.class); - doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration( - eq(change.getTaskInfo()), any(), any()); - final IBinder transition = mock(IBinder.class); final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); mTransitionObserver.onTransitionReady(transition, info, startT, finishT); mTransitionObserver.onTransitionStarting(transition); - verify(windowDecor, never()).close(); + verify(mWindowDecorViewModel, never()).destroyWindowDecoration(change.getTaskInfo()); } @Test @@ -159,8 +148,6 @@ public class FreeformTaskTransitionObserverTest { info.addChange(change); final AutoCloseable windowDecor = mock(AutoCloseable.class); - doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration( - eq(change.getTaskInfo()), any(), any()); final IBinder transition = mock(IBinder.class); final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); @@ -169,7 +156,7 @@ public class FreeformTaskTransitionObserverTest { mTransitionObserver.onTransitionStarting(transition); mTransitionObserver.onTransitionFinished(transition, false); - verify(windowDecor).close(); + verify(mWindowDecorViewModel).destroyWindowDecoration(change.getTaskInfo()); } @Test @@ -192,10 +179,6 @@ public class FreeformTaskTransitionObserverTest { final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0); info2.addChange(change2); - final AutoCloseable windowDecor2 = mock(AutoCloseable.class); - doReturn(windowDecor2).when(mFreeformTaskListener).giveWindowDecoration( - eq(change2.getTaskInfo()), any(), any()); - final IBinder transition2 = mock(IBinder.class); final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class); @@ -204,7 +187,7 @@ public class FreeformTaskTransitionObserverTest { mTransitionObserver.onTransitionFinished(transition1, false); - verify(windowDecor2).close(); + verify(mWindowDecorViewModel).destroyWindowDecoration(change2.getTaskInfo()); } @Test @@ -215,10 +198,6 @@ public class FreeformTaskTransitionObserverTest { final TransitionInfo info1 = new TransitionInfo(TRANSIT_CLOSE, 0); info1.addChange(change1); - final AutoCloseable windowDecor1 = mock(AutoCloseable.class); - doReturn(windowDecor1).when(mFreeformTaskListener).giveWindowDecoration( - eq(change1.getTaskInfo()), any(), any()); - final IBinder transition1 = mock(IBinder.class); final SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class); @@ -231,10 +210,6 @@ public class FreeformTaskTransitionObserverTest { final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0); info2.addChange(change2); - final AutoCloseable windowDecor2 = mock(AutoCloseable.class); - doReturn(windowDecor2).when(mFreeformTaskListener).giveWindowDecoration( - eq(change2.getTaskInfo()), any(), any()); - final IBinder transition2 = mock(IBinder.class); final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class); @@ -243,48 +218,8 @@ public class FreeformTaskTransitionObserverTest { mTransitionObserver.onTransitionFinished(transition1, false); - verify(windowDecor1).close(); - verify(windowDecor2).close(); - } - - @Test - public void testTransfersWindowDecorOnMaximize() { - final TransitionInfo.Change change = - createChange(TRANSIT_CHANGE, 1, WINDOWING_MODE_FULLSCREEN); - final TransitionInfo info = new TransitionInfo(TRANSIT_MAXIMIZE, 0); - info.addChange(change); - - final AutoCloseable windowDecor = mock(AutoCloseable.class); - doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration( - eq(change.getTaskInfo()), any(), any()); - - final IBinder transition = mock(IBinder.class); - final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); - final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); - mTransitionObserver.onTransitionReady(transition, info, startT, finishT); - mTransitionObserver.onTransitionStarting(transition); - - verify(mFreeformTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT); - verify(mFullscreenTaskListener).adoptWindowDecoration( - eq(change), same(startT), same(finishT), any()); - } - - @Test - public void testTransfersWindowDecorOnRestoreFromMaximize() { - final TransitionInfo.Change change = - createChange(TRANSIT_CHANGE, 1, WINDOWING_MODE_FREEFORM); - final TransitionInfo info = new TransitionInfo(TRANSIT_RESTORE_FROM_MAXIMIZE, 0); - info.addChange(change); - - final IBinder transition = mock(IBinder.class); - final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); - final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); - mTransitionObserver.onTransitionReady(transition, info, startT, finishT); - mTransitionObserver.onTransitionStarting(transition); - - verify(mFullscreenTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT); - verify(mFreeformTaskListener).adoptWindowDecoration( - eq(change), same(startT), same(finishT), any()); + verify(mWindowDecorViewModel).destroyWindowDecoration(change1.getTaskInfo()); + verify(mWindowDecorViewModel).destroyWindowDecoration(change2.getTaskInfo()); } private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) { -- cgit v1.2.3-59-g8ed1b From 6e0b27a126f3b0adc9b6db078073451945e871b8 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Thu, 29 Sep 2022 17:18:12 +0800 Subject: Fix SplitContainer not updated when folded. The root cause is that TaskFragmentParentInfo wasn't be dispatched when there's a display or visibility change because we didn't implement getTaskFragment() in TaskFragment and it led to the TskFragment can't return itself if the predicate function returns true. This CL fixes WC#getTaskFragment and changes to dispatch Task#shouldBeVisible instead of Task#isVisibleRequested. The reason is that the visibleRequested change is not early enough for the scenario that device is folded from unfolded state, and lead to Settings flickering. Test: manual - open Settings and fold the device Test: atest TaskTests#testGetTaskFragment Test: atest TaskFragmentOrganizerControllerTest ActivityRecordTests fixes: 249055633 Change-Id: Ie1c56758697d14b426c9ed713da84e49c9f880d8 --- .../android/window/TaskFragmentParentInfo.java | 26 +++++++++---------- .../window/extensions/embedding/TaskContainer.java | 2 +- services/core/java/com/android/server/wm/Task.java | 8 ++++-- .../java/com/android/server/wm/TaskFragment.java | 16 +++++++++++- .../server/wm/TaskFragmentOrganizerController.java | 1 + .../wm/TaskFragmentOrganizerControllerTest.java | 29 +++++++++++++++------- .../src/com/android/server/wm/TaskTests.java | 15 +++++++++++ .../src/com/android/server/wm/WindowTestsBase.java | 4 +++ 8 files changed, 75 insertions(+), 26 deletions(-) (limited to 'libs') diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java index 64b2638407df..841354a92192 100644 --- a/core/java/android/window/TaskFragmentParentInfo.java +++ b/core/java/android/window/TaskFragmentParentInfo.java @@ -33,19 +33,19 @@ public class TaskFragmentParentInfo implements Parcelable { private final int mDisplayId; - private final boolean mVisibleRequested; + private final boolean mVisible; public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId, - boolean visibleRequested) { + boolean visible) { mConfiguration.setTo(configuration); mDisplayId = displayId; - mVisibleRequested = visibleRequested; + mVisible = visible; } public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) { mConfiguration.setTo(info.getConfiguration()); mDisplayId = info.mDisplayId; - mVisibleRequested = info.mVisibleRequested; + mVisible = info.mVisible; } /** The {@link Configuration} of the parent Task */ @@ -62,9 +62,9 @@ public class TaskFragmentParentInfo implements Parcelable { return mDisplayId; } - /** Whether the parent Task is requested to be visible or not */ - public boolean isVisibleRequested() { - return mVisibleRequested; + /** Whether the parent Task is visible or not */ + public boolean isVisible() { + return mVisible; } /** @@ -80,7 +80,7 @@ public class TaskFragmentParentInfo implements Parcelable { return false; } return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId - && mVisibleRequested == that.mVisibleRequested; + && mVisible == that.mVisible; } @WindowConfiguration.WindowingMode @@ -93,7 +93,7 @@ public class TaskFragmentParentInfo implements Parcelable { return TaskFragmentParentInfo.class.getSimpleName() + ":{" + "config=" + mConfiguration + ", displayId=" + mDisplayId - + ", visibleRequested=" + mVisibleRequested + + ", visible=" + mVisible + "}"; } @@ -114,14 +114,14 @@ public class TaskFragmentParentInfo implements Parcelable { final TaskFragmentParentInfo that = (TaskFragmentParentInfo) obj; return mConfiguration.equals(that.mConfiguration) && mDisplayId == that.mDisplayId - && mVisibleRequested == that.mVisibleRequested; + && mVisible == that.mVisible; } @Override public int hashCode() { int result = mConfiguration.hashCode(); result = 31 * result + mDisplayId; - result = 31 * result + (mVisibleRequested ? 1 : 0); + result = 31 * result + (mVisible ? 1 : 0); return result; } @@ -129,13 +129,13 @@ public class TaskFragmentParentInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { mConfiguration.writeToParcel(dest, flags); dest.writeInt(mDisplayId); - dest.writeBoolean(mVisibleRequested); + dest.writeBoolean(mVisible); } private TaskFragmentParentInfo(Parcel in) { mConfiguration.readFromParcel(in); mDisplayId = in.readInt(); - mVisibleRequested = in.readBoolean(); + mVisible = in.readBoolean(); } public static final Creator CREATOR = diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 91573ffef568..00943f2d53e1 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -140,7 +140,7 @@ class TaskContainer { void updateTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) { mConfiguration.setTo(info.getConfiguration()); mDisplayId = info.getDisplayId(); - mIsVisible = info.isVisibleRequested(); + mIsVisible = info.isVisible(); } /** diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index aede32c453f2..6001a87828d2 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3525,12 +3525,16 @@ class Task extends TaskFragment { * {@link android.window.TaskFragmentOrganizer} */ TaskFragmentParentInfo getTaskFragmentParentInfo() { - return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(), isVisibleRequested()); + return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(), + shouldBeVisible(null /* starting */)); } @Override void onActivityVisibleRequestedChanged() { - if (mVisibleRequested != isVisibleRequested()) { + final boolean prevVisibleRequested = mVisibleRequested; + // mVisibleRequested is updated in super method. + super.onActivityVisibleRequestedChanged(); + if (prevVisibleRequested != mVisibleRequested) { sendTaskFragmentParentInfoChangedIfNeeded(); } } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 058a066fffa4..d17867673474 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -2690,12 +2690,26 @@ class TaskFragment extends WindowContainer { return; } mVisibleRequested = isVisibleRequested; - final TaskFragment parentTf = getParent().asTaskFragment(); + final WindowContainer parent = getParent(); + if (parent == null) { + return; + } + final TaskFragment parentTf = parent.asTaskFragment(); if (parentTf != null) { parentTf.onActivityVisibleRequestedChanged(); } } + @Nullable + @Override + TaskFragment getTaskFragment(Predicate callback) { + final TaskFragment taskFragment = super.getTaskFragment(callback); + if (taskFragment != null) { + return taskFragment; + } + return callback.test(this) ? this : null; + } + String toFullString() { final StringBuilder sb = new StringBuilder(128); sb.append(this); diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index 2d5c9897a82c..867833a3271a 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -920,6 +920,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr for (int i = 0, n = pendingEvents.size(); i < n; i++) { final PendingTaskFragmentEvent event = pendingEvents.get(i); final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null; + // TODO(b/251132298): move visibility check to the client side. if (task != null && (task.lastActiveTime <= event.mDeferTime || !(isTaskVisible(task, visibleTasks, invisibleTasks) || shouldSendEventWhenTaskInvisible(event)))) { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 1404de253476..0b23359627fb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -195,6 +195,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mController.dispatchPendingEvents(); + assertTaskFragmentParentInfoChangedTransaction(mTask); assertTaskFragmentAppearedTransaction(); } @@ -365,6 +366,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mController.onActivityReparentedToTask(activity); mController.dispatchPendingEvents(); + assertTaskFragmentParentInfoChangedTransaction(task); assertActivityReparentedToTaskTransaction(task.mTaskId, activity.intent, activity.token); } @@ -1205,7 +1207,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { /** * Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls - * {@link WindowOrganizerController#applyTransaction} to apply the transaction, + * {@link WindowOrganizerController#applyTransaction(WindowContainerTransaction)} to apply the + * transaction, */ private void createTaskFragmentFromOrganizer(WindowContainerTransaction wct, ActivityRecord ownerActivity, IBinder fragmentToken) { @@ -1239,8 +1242,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final List changes = transaction.getChanges(); assertFalse(changes.isEmpty()); - // Appeared will come with parent info changed. - final TaskFragmentTransaction.Change change = changes.get(changes.size() - 1); + // Use remove to verify multiple transaction changes. + final TaskFragmentTransaction.Change change = changes.remove(0); assertEquals(TYPE_TASK_FRAGMENT_APPEARED, change.getType()); assertEquals(mTaskFragmentInfo, change.getTaskFragmentInfo()); assertEquals(mFragmentToken, change.getTaskFragmentToken()); @@ -1253,8 +1256,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final List changes = transaction.getChanges(); assertFalse(changes.isEmpty()); - // InfoChanged may come with parent info changed. - final TaskFragmentTransaction.Change change = changes.get(changes.size() - 1); + // Use remove to verify multiple transaction changes. + final TaskFragmentTransaction.Change change = changes.remove(0); assertEquals(TYPE_TASK_FRAGMENT_INFO_CHANGED, change.getType()); assertEquals(mTaskFragmentInfo, change.getTaskFragmentInfo()); assertEquals(mFragmentToken, change.getTaskFragmentToken()); @@ -1266,7 +1269,9 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); final List changes = transaction.getChanges(); assertFalse(changes.isEmpty()); - final TaskFragmentTransaction.Change change = changes.get(0); + + // Use remove to verify multiple transaction changes. + final TaskFragmentTransaction.Change change = changes.remove(0); assertEquals(TYPE_TASK_FRAGMENT_VANISHED, change.getType()); assertEquals(mTaskFragmentInfo, change.getTaskFragmentInfo()); assertEquals(mFragmentToken, change.getTaskFragmentToken()); @@ -1278,7 +1283,9 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); final List changes = transaction.getChanges(); assertFalse(changes.isEmpty()); - final TaskFragmentTransaction.Change change = changes.get(0); + + // Use remove to verify multiple transaction changes. + final TaskFragmentTransaction.Change change = changes.remove(0); assertEquals(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED, change.getType()); assertEquals(task.mTaskId, change.getTaskId()); assertEquals(task.getTaskFragmentParentInfo(), change.getTaskFragmentParentInfo()); @@ -1290,7 +1297,9 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); final List changes = transaction.getChanges(); assertFalse(changes.isEmpty()); - final TaskFragmentTransaction.Change change = changes.get(0); + + // Use remove to verify multiple transaction changes. + final TaskFragmentTransaction.Change change = changes.remove(0); assertEquals(TYPE_TASK_FRAGMENT_ERROR, change.getType()); assertEquals(mErrorToken, change.getErrorCallbackToken()); final Bundle errorBundle = change.getErrorBundle(); @@ -1306,7 +1315,9 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); final List changes = transaction.getChanges(); assertFalse(changes.isEmpty()); - final TaskFragmentTransaction.Change change = changes.get(0); + + // Use remove to verify multiple transaction changes. + final TaskFragmentTransaction.Change change = changes.remove(0); assertEquals(TYPE_ACTIVITY_REPARENTED_TO_TASK, change.getType()); assertEquals(taskId, change.getTaskId()); assertEquals(intent, change.getActivityIntent()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 76cd19be19b0..68ac1d6f2e01 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -1454,6 +1454,21 @@ public class TaskTests extends WindowTestsBase { verify(tfBehind, never()).resumeTopActivity(any(), any(), anyBoolean()); } + @Test + public void testGetTaskFragment() { + final Task parentTask = createTask(mDisplayContent); + final TaskFragment tf0 = createTaskFragmentWithParentTask(parentTask); + final TaskFragment tf1 = createTaskFragmentWithParentTask(parentTask); + + assertNull("Could not find it because there's no organized TaskFragment", + parentTask.getTaskFragment(TaskFragment::isOrganizedTaskFragment)); + + doReturn(true).when(tf0).isOrganizedTaskFragment(); + + assertEquals("tf0 must be return because it's the organized TaskFragment.", + tf0, parentTask.getTaskFragment(TaskFragment::isOrganizedTaskFragment)); + } + private Task getTestTask() { final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); return task.getBottomMostTask(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index ef532f5732eb..34f5463c03b1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -718,6 +718,10 @@ class WindowTestsBase extends SystemServiceTestsBase { activity.mVisibleRequested = true; } + static TaskFragment createTaskFragmentWithParentTask(@NonNull Task parentTask) { + return createTaskFragmentWithParentTask(parentTask, false /* createEmbeddedTask */); + } + /** * Creates a {@link TaskFragment} and attach it to the {@code parentTask}. * -- cgit v1.2.3-59-g8ed1b From b3d7ef6d859b914cc40e45c1245c56aff63bfd3a Mon Sep 17 00:00:00 2001 From: Tony Huang Date: Thu, 6 Oct 2022 16:59:26 +0800 Subject: Update split context when display size changed Because SnapAlgorithm use many resources value and it will be affected by density, we should update context and mDividerSnapAlgorithm too when configuration density changed. Fix: 250483334 Test: manaul Test: atest SplitLayoutTests Change-Id: I207a025503c032318ede707eed786ae83b60e658 --- .../Shell/src/com/android/wm/shell/common/split/SplitLayout.java | 4 ++++ .../src/com/android/wm/shell/common/split/SplitLayoutTests.java | 9 +++++++++ 2 files changed, 13 insertions(+) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 419e62daf586..c2ad1a98d167 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -118,6 +118,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private boolean mFreezeDividerWindow = false; private int mOrientation; private int mRotation; + private int mDensity; private final boolean mDimNonImeSide; @@ -290,9 +291,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange final int rotation = configuration.windowConfiguration.getRotation(); final Rect rootBounds = configuration.windowConfiguration.getBounds(); final int orientation = configuration.orientation; + final int density = configuration.densityDpi; if (mOrientation == orientation && mRotation == rotation + && mDensity == density && mRootBounds.equals(rootBounds)) { return false; } @@ -303,6 +306,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mTempRect.set(mRootBounds); mRootBounds.set(rootBounds); mRotation = rotation; + mDensity = density; mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null); initDividerPosition(mTempRect); updateInvisibleRect(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index 695550dd8fa5..f6d6c03bc2ee 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -16,6 +16,7 @@ package com.android.wm.shell.common.split; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.google.common.truth.Truth.assertThat; @@ -91,6 +92,14 @@ public class SplitLayoutTests extends ShellTestCase { // Verify updateConfiguration returns true if the root bounds changed. config.windowConfiguration.setBounds(new Rect(0, 0, 2160, 1080)); assertThat(mSplitLayout.updateConfiguration(config)).isTrue(); + + // Verify updateConfiguration returns true if the orientation changed. + config.orientation = ORIENTATION_LANDSCAPE; + assertThat(mSplitLayout.updateConfiguration(config)).isTrue(); + + // Verify updateConfiguration returns true if the density changed. + config.densityDpi = 123; + assertThat(mSplitLayout.updateConfiguration(config)).isTrue(); } @Test -- cgit v1.2.3-59-g8ed1b From 430d40eea63a908a96460726c4625a54edc7e907 Mon Sep 17 00:00:00 2001 From: An An Yu Date: Tue, 30 Aug 2022 15:49:46 -0700 Subject: add @Override for addWindowLayoutInfoListener(context) and update prebuilts. Also throw exception when there's re-registration for Context/Consumer callback. At the Extension level we maintain a 1-1 mapping between Context and Consumer, while Jetpack handles the multicasting. Test: CTS in same topic. Change-Id: If0fccd5e52c799fd4aaa9b3637b2f1c78986fc75 Bug: 204073440 --- .../layout/WindowLayoutComponentImpl.java | 15 ++++++++++++--- .../Jetpack/window-extensions-release.aar | Bin 30713 -> 30816 bytes 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index c76f568e117f..0fb6ff878451 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -103,14 +103,23 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { /** * Similar to {@link #addWindowLayoutInfoListener(Activity, Consumer)}, but takes a UI Context * as a parameter. + * + * Jetpack {@link androidx.window.layout.ExtensionWindowLayoutInfoBackend} makes sure all + * consumers related to the same {@link Context} gets updated {@link WindowLayoutInfo} + * together. However only the first registered consumer of a {@link Context} will actually + * invoke {@link #addWindowLayoutInfoListener(Context, Consumer)}. + * Here we enforce that {@link #addWindowLayoutInfoListener(Context, Consumer)} can only be + * called once for each {@link Context}. */ - // TODO(b/204073440): Add @Override to hook the API in WM extensions library. + @Override public void addWindowLayoutInfoListener(@NonNull @UiContext Context context, @NonNull Consumer consumer) { if (mWindowLayoutChangeListeners.containsKey(context) + // In theory this method can be called on the same consumer with different context. || mWindowLayoutChangeListeners.containsValue(consumer)) { - // Early return if the listener or consumer has been registered. - return; + throw new IllegalArgumentException( + "Context or Consumer has already been registered for WindowLayoutInfo" + + " callback."); } if (!context.isUiContext()) { throw new IllegalArgumentException("Context must be a UI Context, which should be" diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar index 2c766d81d611..b0b95f9cc871 100644 Binary files a/libs/WindowManager/Jetpack/window-extensions-release.aar and b/libs/WindowManager/Jetpack/window-extensions-release.aar differ -- cgit v1.2.3-59-g8ed1b From c03010f1502834fc16cc3903d9fabb2f2a6be81a Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 26 Sep 2022 21:46:25 +0000 Subject: Simplify exposing external interfaces to Launcher - Currently, the collection of binders to send to Launcher is inverted and managed by OverviewProxyService which has to know about each component directly (and a SysUI interface has to be created to support this even if there are no other calls in SysUI to that component). Instead, we should have each component register itself with the ShellController for controllers which can be exposed to an external process, and each product can customize which set of controllers it needs to support. - Add missing dump registration for ShellController - Updating docs for creating remote callable controllers Bug: 238217847 Test: atest WMShellUnitTests Change-Id: Icd0ed33bdbd68b1dd9d782b9ba1f5b4a604479ba Merged-In: Icd0ed33bdbd68b1dd9d782b9ba1f5b4a604479ba --- libs/WindowManager/Shell/Android.bp | 1 + .../com/android/wm/shell/back/BackAnimation.java | 7 -- .../wm/shell/back/BackAnimationController.java | 37 ++++--- .../wm/shell/common/ExternalInterfaceBinder.java | 34 ++++++ .../android/wm/shell/dagger/WMShellBaseModule.java | 23 ++-- .../com/android/wm/shell/dagger/WMShellModule.java | 6 +- .../android/wm/shell/desktopmode/DesktopMode.java | 5 - .../shell/desktopmode/DesktopModeController.java | 38 ++++--- .../Shell/src/com/android/wm/shell/docs/changes.md | 25 ++++- .../android/wm/shell/floating/FloatingTasks.java | 5 - .../wm/shell/floating/FloatingTasksController.java | 27 +++-- .../com/android/wm/shell/onehanded/OneHanded.java | 7 -- .../wm/shell/onehanded/OneHandedController.java | 24 ++--- .../Shell/src/com/android/wm/shell/pip/Pip.java | 8 -- .../android/wm/shell/pip/phone/PipController.java | 24 ++--- .../com/android/wm/shell/recents/RecentTasks.java | 7 -- .../wm/shell/recents/RecentTasksController.java | 36 ++++--- .../android/wm/shell/splitscreen/SplitScreen.java | 7 -- .../shell/splitscreen/SplitScreenController.java | 24 ++--- .../wm/shell/startingsurface/StartingSurface.java | 8 -- .../startingsurface/StartingWindowController.java | 29 ++--- .../android/wm/shell/sysui/ShellController.java | 120 +++++++++++++++++---- .../com/android/wm/shell/sysui/ShellInterface.java | 30 +++--- .../wm/shell/sysui/ShellSharedConstants.java | 43 ++++++++ .../wm/shell/transition/ShellTransitions.java | 8 -- .../android/wm/shell/transition/Transitions.java | 30 +++--- .../wm/shell/back/BackAnimationControllerTest.java | 15 ++- .../desktopmode/DesktopModeControllerTest.java | 7 +- .../floating/FloatingTasksControllerTest.java | 13 +++ .../shell/onehanded/OneHandedControllerTest.java | 7 ++ .../wm/shell/pip/phone/PipControllerTest.java | 8 ++ .../shell/recents/RecentTasksControllerTest.java | 13 ++- .../splitscreen/SplitScreenControllerTests.java | 10 ++ .../StartingWindowControllerTests.java | 20 +++- .../wm/shell/sysui/ShellControllerTest.java | 47 ++++++++ .../wm/shell/transition/ShellTransitionTests.java | 24 ++++- .../systemui/shared/system/QuickStepContract.java | 19 ---- .../systemui/recents/OverviewProxyService.java | 86 ++------------- 38 files changed, 537 insertions(+), 345 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java (limited to 'libs') diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 7960dec5080b..6c0ae5b0db57 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -44,6 +44,7 @@ filegroup { srcs: [ "src/com/android/wm/shell/util/**/*.java", "src/com/android/wm/shell/common/split/SplitScreenConstants.java", + "src/com/android/wm/shell/sysui/ShellSharedConstants.java", ], path: "src", } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java index 86f9d5b534f4..8cbe44b15e42 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java @@ -28,13 +28,6 @@ import com.android.wm.shell.common.annotations.ExternalThread; @ExternalThread public interface BackAnimation { - /** - * Returns a binder that can be passed to an external process to update back animations. - */ - default IBackAnimation createExternalInterface() { - return null; - } - /** * Called when a {@link MotionEvent} is generated by a back gesture. * diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 33ecdd88fad3..938189fc8a88 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -21,6 +21,7 @@ import static android.view.RemoteAnimationTarget.MODE_OPENING; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; +import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION; import android.annotation.NonNull; import android.annotation.Nullable; @@ -57,10 +58,12 @@ import android.window.IOnBackInvokedCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import java.util.concurrent.atomic.AtomicBoolean; @@ -105,6 +108,7 @@ public class BackAnimationController implements RemoteCallable controller.onBackToLauncherAnimationFinished()); } - void invalidate() { + @Override + public void invalidate() { mController = null; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java new file mode 100644 index 000000000000..aa5b0cb628e1 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 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.wm.shell.common; + +import android.os.IBinder; + +/** + * An interface for binders which can be registered to be sent to other processes. + */ +public interface ExternalInterfaceBinder { + /** + * Invalidates this binder (detaches it from the controller it would call). + */ + void invalidate(); + + /** + * Returns the IBinder to send. + */ + IBinder asBinder(); +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 353014264990..cae9d155b27f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -258,13 +258,14 @@ public abstract class WMShellBaseModule { static Optional provideBackAnimationController( Context context, ShellInit shellInit, + ShellController shellController, @ShellMainThread ShellExecutor shellExecutor, @ShellBackgroundThread Handler backgroundHandler ) { if (BackAnimationController.IS_ENABLED) { return Optional.of( - new BackAnimationController(shellInit, shellExecutor, backgroundHandler, - context)); + new BackAnimationController(shellInit, shellController, shellExecutor, + backgroundHandler, context)); } return Optional.empty(); } @@ -468,6 +469,7 @@ public abstract class WMShellBaseModule { static Optional provideRecentTasksController( Context context, ShellInit shellInit, + ShellController shellController, ShellCommandHandler shellCommandHandler, TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, @@ -475,9 +477,9 @@ public abstract class WMShellBaseModule { @ShellMainThread ShellExecutor mainExecutor ) { return Optional.ofNullable( - RecentTasksController.create(context, shellInit, shellCommandHandler, - taskStackListener, activityTaskManager, desktopModeTaskRepository, - mainExecutor)); + RecentTasksController.create(context, shellInit, shellController, + shellCommandHandler, taskStackListener, activityTaskManager, + desktopModeTaskRepository, mainExecutor)); } // @@ -494,14 +496,15 @@ public abstract class WMShellBaseModule { @Provides static Transitions provideTransitions(Context context, ShellInit shellInit, + ShellController shellController, ShellTaskOrganizer organizer, TransactionPool pool, DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler, @ShellAnimationThread ShellExecutor animExecutor) { - return new Transitions(context, shellInit, organizer, pool, displayController, mainExecutor, - mainHandler, animExecutor); + return new Transitions(context, shellInit, shellController, organizer, pool, + displayController, mainExecutor, mainHandler, animExecutor); } @WMSingleton @@ -618,13 +621,15 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static StartingWindowController provideStartingWindowController(Context context, + static StartingWindowController provideStartingWindowController( + Context context, ShellInit shellInit, + ShellController shellController, ShellTaskOrganizer shellTaskOrganizer, @ShellSplashscreenThread ShellExecutor splashScreenExecutor, StartingWindowTypeAlgorithm startingWindowTypeAlgorithm, IconProvider iconProvider, TransactionPool pool) { - return new StartingWindowController(context, shellInit, shellTaskOrganizer, + return new StartingWindowController(context, shellInit, shellController, shellTaskOrganizer, splashScreenExecutor, startingWindowTypeAlgorithm, iconProvider, pool); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 47b665970d28..1c3b2db347e5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -600,7 +600,9 @@ public abstract class WMShellModule { @WMSingleton @Provides @DynamicOverride - static DesktopModeController provideDesktopModeController(Context context, ShellInit shellInit, + static DesktopModeController provideDesktopModeController(Context context, + ShellInit shellInit, + ShellController shellController, ShellTaskOrganizer shellTaskOrganizer, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, Transitions transitions, @@ -608,7 +610,7 @@ public abstract class WMShellModule { @ShellMainThread Handler mainHandler, @ShellMainThread ShellExecutor mainExecutor ) { - return new DesktopModeController(context, shellInit, shellTaskOrganizer, + return new DesktopModeController(context, shellInit, shellController, shellTaskOrganizer, rootTaskDisplayAreaOrganizer, transitions, desktopModeTaskRepository, mainHandler, mainExecutor); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java index ff3be38d09e1..44a467ffcf3d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java @@ -23,9 +23,4 @@ import com.android.wm.shell.common.annotations.ExternalThread; */ @ExternalThread public interface DesktopMode { - - /** Returns a binder that can be passed to an external process to manipulate DesktopMode. */ - default IDesktopMode createExternalInterface() { - return null; - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index 99739c457aa6..e34c2975d431 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -22,6 +22,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; +import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE; import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; @@ -42,10 +43,12 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; @@ -58,15 +61,18 @@ import java.util.Comparator; public class DesktopModeController implements RemoteCallable { private final Context mContext; + private final ShellController mShellController; private final ShellTaskOrganizer mShellTaskOrganizer; private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; private final Transitions mTransitions; private final DesktopModeTaskRepository mDesktopModeTaskRepository; private final ShellExecutor mMainExecutor; - private final DesktopMode mDesktopModeImpl = new DesktopModeImpl(); + private final DesktopModeImpl mDesktopModeImpl = new DesktopModeImpl(); private final SettingsObserver mSettingsObserver; - public DesktopModeController(Context context, ShellInit shellInit, + public DesktopModeController(Context context, + ShellInit shellInit, + ShellController shellController, ShellTaskOrganizer shellTaskOrganizer, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, Transitions transitions, @@ -74,6 +80,7 @@ public class DesktopModeController implements RemoteCallable` -interface that the `Controller` implements and handles on the main Shell thread. +interface that the `Controller` implements and handles on the main Shell thread +(see [SysUI/Shell threading](threading.md)). In addition, because components accessible to SysUI injection are explicitly listed, you'll have to add an appropriate method in `WMComponent` to get the interface and update the `Builder` in `SysUIComponent` to take the interface so it can be injected in SysUI code. The binding between the two is done in `SystemUIFactory#init()` which will need to be updated as well. +Specifically, to support calling into a controller from an external process (like Launcher): +- Create an implementation of the external interface within the controller +- Have all incoming calls post to the main shell thread (inject @ShellMainThread Executor into the + controller if needed) +- Note that callbacks into SysUI should take an associated executor to call back on + ### Launcher accessible components Because Launcher is not a part of SystemUI and is a separate process, exposing controllers to Launcher requires a new AIDL interface to be created and implemented by the controller. The implementation of the stub interface in the controller otherwise behaves similar to the interface to SysUI where it posts the work to the main Shell thread. +Specifically, to support calling into a controller from an external process (like Launcher): +- Create an implementation of the interface binder's `Stub` class within the controller, have it + extend `ExternalInterfaceBinder` and implement `invalidate()` to ensure it doesn't hold long + references to the outer controller +- Make the controller implement `RemoteCallable`, and have all incoming calls use one of + the `ExecutorUtils.executeRemoteCallWithTaskPermission()` calls to verify the caller's identity + and ensure the call happens on the main shell thread and not the binder thread +- Inject `ShellController` and add the instance of the implementation as external interface +- In Launcher, update `TouchInteractionService` to pass the interface to `SystemUIProxy`, and then + call the SystemUIProxy method as needed in that code + ### Component initialization To initialize the component: - On the Shell side, you potentially need to do two things to initialize the component: @@ -64,8 +82,9 @@ adb logcat *:S WindowManagerShell ### General Do's & Dont's Do: -- Do add unit tests for all new components -- Do keep controllers simple and break them down as needed +- Add unit tests for all new components +- Keep controllers simple and break them down as needed +- Any SysUI callbacks should also take an associated executor to run the callback on Don't: - **Don't** do initialization in the constructor, only do initialization in the init callbacks. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java index 935666026bf4..f86d467360f9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java @@ -33,9 +33,4 @@ public interface FloatingTasks { * - If there is a floating task for this intent, and it's not stashed, this stashes it. */ void showOrSetStashed(Intent intent); - - /** Returns a binder that can be passed to an external process to manipulate FloatingTasks. */ - default IFloatingTasks createExternalInterface() { - return null; - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java index 67552991869b..b3c09d32055b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java @@ -21,6 +21,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS; +import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_FLOATING_TASKS; import android.annotation.Nullable; import android.content.Context; @@ -40,6 +41,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.TaskViewTransitions; import com.android.wm.shell.bubbles.BubbleController; +import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -136,11 +138,13 @@ public class FloatingTasksController implements RemoteCallable FloatingTasksController.this.showOrSetStashed(intent)); } - - @Override - public IFloatingTasks createExternalInterface() { - if (mIFloatingTasks != null) { - mIFloatingTasks.invalidate(); - } - mIFloatingTasks = new IFloatingTasksImpl(FloatingTasksController.this); - return mIFloatingTasks; - } } /** * The interface for calls from outside the host process. */ @BinderThread - private static class IFloatingTasksImpl extends IFloatingTasks.Stub { + private static class IFloatingTasksImpl extends IFloatingTasks.Stub + implements ExternalInterfaceBinder { private FloatingTasksController mController; IFloatingTasksImpl(FloatingTasksController controller) { @@ -443,7 +441,8 @@ public class FloatingTasksController implements RemoteCallable, mShellController.addConfigurationChangeListener(this); mShellController.addKeyguardChangeListener(this); mShellController.addUserChangeListener(this); + mShellController.addExternalInterface(KEY_EXTRA_SHELL_ONE_HANDED, + this::createExternalInterface, this); } public OneHanded asOneHanded() { return mImpl; } + private ExternalInterfaceBinder createExternalInterface() { + return new IOneHandedImpl(this); + } + @Override public Context getContext() { return mContext; @@ -709,17 +717,6 @@ public class OneHandedController implements RemoteCallable, */ @ExternalThread private class OneHandedImpl implements OneHanded { - private IOneHandedImpl mIOneHanded; - - @Override - public IOneHanded createExternalInterface() { - if (mIOneHanded != null) { - mIOneHanded.invalidate(); - } - mIOneHanded = new IOneHandedImpl(OneHandedController.this); - return mIOneHanded; - } - @Override public void startOneHanded() { mMainExecutor.execute(() -> { @@ -767,7 +764,7 @@ public class OneHandedController implements RemoteCallable, * The interface for calls from outside the host process. */ @BinderThread - private static class IOneHandedImpl extends IOneHanded.Stub { + private static class IOneHandedImpl extends IOneHanded.Stub implements ExternalInterfaceBinder { private OneHandedController mController; IOneHandedImpl(OneHandedController controller) { @@ -777,7 +774,8 @@ public class OneHandedController implements RemoteCallable, /** * Invalidates this instance, preventing future calls from updating the controller. */ - void invalidate() { + @Override + public void invalidate() { mController = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index 72b9dd37ac7d..f34d2a827e69 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -27,14 +27,6 @@ import java.util.function.Consumer; */ @ExternalThread public interface Pip { - - /** - * Returns a binder that can be passed to an external process to manipulate PIP. - */ - default IPip createExternalInterface() { - return null; - } - /** * Expand PIP, it's possible that specific request to activate the window via Alt-tab. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 3345b1b2d0e8..a918559d897e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -33,6 +33,7 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; +import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP; import android.app.ActivityManager; import android.app.ActivityTaskManager; @@ -68,6 +69,7 @@ import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SingleInstanceRemoteListener; @@ -632,6 +634,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb mShellController.addConfigurationChangeListener(this); mShellController.addKeyguardChangeListener(this); mShellController.addUserChangeListener(this); + mShellController.addExternalInterface(KEY_EXTRA_SHELL_PIP, + this::createExternalInterface, this); + } + + private ExternalInterfaceBinder createExternalInterface() { + return new IPipImpl(this); } @Override @@ -1040,17 +1048,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb * The interface for calls from outside the Shell, within the host process. */ private class PipImpl implements Pip { - private IPipImpl mIPip; - - @Override - public IPip createExternalInterface() { - if (mIPip != null) { - mIPip.invalidate(); - } - mIPip = new IPipImpl(PipController.this); - return mIPip; - } - @Override public void expandPip() { mMainExecutor.execute(() -> { @@ -1098,7 +1095,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb * The interface for calls from outside the host process. */ @BinderThread - private static class IPipImpl extends IPip.Stub { + private static class IPipImpl extends IPip.Stub implements ExternalInterfaceBinder { private PipController mController; private final SingleInstanceRemoteListener mListener; @@ -1129,7 +1126,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb /** * Invalidates this instance, preventing future calls from updating the controller. */ - void invalidate() { + @Override + public void invalidate() { mController = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java index 2a625524b48b..069066e4bd49 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java @@ -28,13 +28,6 @@ import java.util.function.Consumer; */ @ExternalThread public interface RecentTasks { - /** - * Returns a binder that can be passed to an external process to fetch recent tasks. - */ - default IRecentTasks createExternalInterface() { - return null; - } - /** * Gets the set of recent tasks. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index 02b5a35f653b..08f3db65e62f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.content.pm.PackageManager.FEATURE_PC; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; +import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS; import android.app.ActivityManager; import android.app.ActivityTaskManager; @@ -37,6 +38,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SingleInstanceRemoteListener; @@ -48,6 +50,7 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.sysui.ShellCommandHandler; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.util.GroupedRecentTaskInfo; import com.android.wm.shell.util.SplitBounds; @@ -69,11 +72,12 @@ public class RecentTasksController implements TaskStackListenerCallback, private static final String TAG = RecentTasksController.class.getSimpleName(); private final Context mContext; + private final ShellController mShellController; private final ShellCommandHandler mShellCommandHandler; private final Optional mDesktopModeTaskRepository; private final ShellExecutor mMainExecutor; private final TaskStackListenerImpl mTaskStackListener; - private final RecentTasks mImpl = new RecentTasksImpl(); + private final RecentTasksImpl mImpl = new RecentTasksImpl(); private final ActivityTaskManager mActivityTaskManager; private IRecentTasksListener mListener; private final boolean mIsDesktopMode; @@ -97,6 +101,7 @@ public class RecentTasksController implements TaskStackListenerCallback, public static RecentTasksController create( Context context, ShellInit shellInit, + ShellController shellController, ShellCommandHandler shellCommandHandler, TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, @@ -106,18 +111,20 @@ public class RecentTasksController implements TaskStackListenerCallback, if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) { return null; } - return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener, - activityTaskManager, desktopModeTaskRepository, mainExecutor); + return new RecentTasksController(context, shellInit, shellController, shellCommandHandler, + taskStackListener, activityTaskManager, desktopModeTaskRepository, mainExecutor); } RecentTasksController(Context context, ShellInit shellInit, + ShellController shellController, ShellCommandHandler shellCommandHandler, TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, Optional desktopModeTaskRepository, ShellExecutor mainExecutor) { mContext = context; + mShellController = shellController; mShellCommandHandler = shellCommandHandler; mActivityTaskManager = activityTaskManager; mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC); @@ -131,7 +138,13 @@ public class RecentTasksController implements TaskStackListenerCallback, return mImpl; } + private ExternalInterfaceBinder createExternalInterface() { + return new IRecentTasksImpl(this); + } + private void onInit() { + mShellController.addExternalInterface(KEY_EXTRA_SHELL_RECENT_TASKS, + this::createExternalInterface, this); mShellCommandHandler.addDumpCallback(this::dump, this); mTaskStackListener.addListener(this); mDesktopModeTaskRepository.ifPresent(it -> it.addListener(this)); @@ -366,17 +379,6 @@ public class RecentTasksController implements TaskStackListenerCallback, */ @ExternalThread private class RecentTasksImpl implements RecentTasks { - private IRecentTasksImpl mIRecentTasks; - - @Override - public IRecentTasks createExternalInterface() { - if (mIRecentTasks != null) { - mIRecentTasks.invalidate(); - } - mIRecentTasks = new IRecentTasksImpl(RecentTasksController.this); - return mIRecentTasks; - } - @Override public void getRecentTasks(int maxNum, int flags, int userId, Executor executor, Consumer> callback) { @@ -393,7 +395,8 @@ public class RecentTasksController implements TaskStackListenerCallback, * The interface for calls from outside the host process. */ @BinderThread - private static class IRecentTasksImpl extends IRecentTasks.Stub { + private static class IRecentTasksImpl extends IRecentTasks.Stub + implements ExternalInterfaceBinder { private RecentTasksController mController; private final SingleInstanceRemoteListener mListener; @@ -424,7 +427,8 @@ public class RecentTasksController implements TaskStackListenerCallback, /** * Invalidates this instance, preventing future calls from updating the controller. */ - void invalidate() { + @Override + public void invalidate() { mController = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index e73b799b7a3d..d86aadc996e3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -70,13 +70,6 @@ public interface SplitScreen { /** Unregisters listener that gets split screen callback. */ void unregisterSplitScreenListener(@NonNull SplitScreenListener listener); - /** - * Returns a binder that can be passed to an external process to manipulate SplitScreen. - */ - default ISplitScreen createExternalInterface() { - return null; - } - /** Called when device waking up finished. */ void onFinishedWakingUp(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 07a6895e2720..eeb2c0f1be6c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -29,6 +29,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; +import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import android.app.ActivityManager; @@ -71,6 +72,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SingleInstanceRemoteListener; @@ -214,6 +216,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mImpl; } + private ExternalInterfaceBinder createExternalInterface() { + return new ISplitScreenImpl(this); + } + /** * This will be called after ShellTaskOrganizer has initialized/registered because of the * dependency order. @@ -224,6 +230,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mShellCommandHandler.addCommandCallback("splitscreen", mSplitScreenShellCommandHandler, this); mShellController.addKeyguardChangeListener(this); + mShellController.addExternalInterface(KEY_EXTRA_SHELL_SPLIT_SCREEN, + this::createExternalInterface, this); if (mStageCoordinator == null) { // TODO: Multi-display mStageCoordinator = createStageCoordinator(); @@ -658,7 +666,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, */ @ExternalThread private class SplitScreenImpl implements SplitScreen { - private ISplitScreenImpl mISplitScreen; private final ArrayMap mExecutors = new ArrayMap<>(); private final SplitScreen.SplitScreenListener mListener = new SplitScreenListener() { @Override @@ -703,15 +710,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } }; - @Override - public ISplitScreen createExternalInterface() { - if (mISplitScreen != null) { - mISplitScreen.invalidate(); - } - mISplitScreen = new ISplitScreenImpl(SplitScreenController.this); - return mISplitScreen; - } - @Override public void registerSplitScreenListener(SplitScreenListener listener, Executor executor) { if (mExecutors.containsKey(listener)) return; @@ -752,7 +750,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, * The interface for calls from outside the host process. */ @BinderThread - private static class ISplitScreenImpl extends ISplitScreen.Stub { + private static class ISplitScreenImpl extends ISplitScreen.Stub + implements ExternalInterfaceBinder { private SplitScreenController mController; private final SingleInstanceRemoteListener mListener; @@ -779,7 +778,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, /** * Invalidates this instance, preventing future calls from updating the controller. */ - void invalidate() { + @Override + public void invalidate() { mController = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java index 76105a39189b..538bbec2aa2b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java @@ -22,14 +22,6 @@ import android.graphics.Color; * Interface to engage starting window feature. */ public interface StartingSurface { - - /** - * Returns a binder that can be passed to an external process to manipulate starting windows. - */ - default IStartingWindow createExternalInterface() { - return null; - } - /** * Returns the background color for a starting window if existing. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index 379af21ac956..0c23f109feaf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -23,6 +23,7 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; +import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW; import android.app.ActivityManager.RunningTaskInfo; import android.app.TaskInfo; @@ -43,10 +44,12 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.function.TriConsumer; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SingleInstanceRemoteListener; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; /** @@ -76,6 +79,7 @@ public class StartingWindowController implements RemoteCallable mTaskLaunchingCallback; private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl(); private final Context mContext; + private final ShellController mShellController; private final ShellTaskOrganizer mShellTaskOrganizer; private final ShellExecutor mSplashScreenExecutor; /** @@ -86,12 +90,14 @@ public class StartingWindowController implements RemoteCallable mListener; @@ -276,7 +278,8 @@ public class StartingWindowController implements RemoteCallable mUserChangeListeners = new CopyOnWriteArrayList<>(); + private ArrayMap> mExternalInterfaceSuppliers = + new ArrayMap<>(); + // References to the existing interfaces, to be invalidated when they are recreated + private ArrayMap mExternalInterfaces = new ArrayMap<>(); + private Configuration mLastConfiguration; @@ -67,6 +77,11 @@ public class ShellController { mShellInit = shellInit; mShellCommandHandler = shellCommandHandler; mMainExecutor = mainExecutor; + shellInit.addInitCallback(this::onInit, this); + } + + private void onInit() { + mShellCommandHandler.addDumpCallback(this::dump, this); } /** @@ -124,6 +139,47 @@ public class ShellController { mUserChangeListeners.remove(listener); } + /** + * Adds an interface that can be called from a remote process. This method takes a supplier + * because each binder reference is valid for a single process, and in multi-user mode, SysUI + * will request new binder instances for each instance of Launcher that it provides binders + * to. + * + * @param extra the key for the interface, {@see ShellSharedConstants} + * @param binderSupplier the supplier of the binder to pass to the external process + * @param callerInstance the instance of the caller, purely for logging + */ + public void addExternalInterface(String extra, Supplier binderSupplier, + Object callerInstance) { + ProtoLog.v(WM_SHELL_INIT, "Adding external interface from %s with key %s", + callerInstance.getClass().getSimpleName(), extra); + if (mExternalInterfaceSuppliers.containsKey(extra)) { + throw new IllegalArgumentException("Supplier with same key already exists: " + + extra); + } + mExternalInterfaceSuppliers.put(extra, binderSupplier); + } + + /** + * Updates the given bundle with the set of external interfaces, invalidating the old set of + * binders. + */ + private void createExternalInterfaces(Bundle output) { + // Invalidate the old binders + for (int i = 0; i < mExternalInterfaces.size(); i++) { + mExternalInterfaces.valueAt(i).invalidate(); + } + mExternalInterfaces.clear(); + + // Create new binders for each key + for (int i = 0; i < mExternalInterfaceSuppliers.size(); i++) { + final String key = mExternalInterfaceSuppliers.keyAt(i); + final ExternalInterfaceBinder b = mExternalInterfaceSuppliers.valueAt(i).get(); + mExternalInterfaces.put(key, b); + output.putBinder(key, b.asBinder()); + } + } + @VisibleForTesting void onConfigurationChanged(Configuration newConfig) { // The initial config is send on startup and doesn't trigger listener callbacks @@ -204,6 +260,14 @@ public class ShellController { pw.println(innerPrefix + "mLastConfiguration=" + mLastConfiguration); pw.println(innerPrefix + "mKeyguardChangeListeners=" + mKeyguardChangeListeners.size()); pw.println(innerPrefix + "mUserChangeListeners=" + mUserChangeListeners.size()); + + if (!mExternalInterfaces.isEmpty()) { + pw.println(innerPrefix + "mExternalInterfaces={"); + for (String key : mExternalInterfaces.keySet()) { + pw.println(innerPrefix + "\t" + key + ": " + mExternalInterfaces.get(key)); + } + pw.println(innerPrefix + "}"); + } } /** @@ -211,7 +275,6 @@ public class ShellController { */ @ExternalThread private class ShellInterfaceImpl implements ShellInterface { - @Override public void onInit() { try { @@ -221,28 +284,6 @@ public class ShellController { } } - @Override - public void dump(PrintWriter pw) { - try { - mMainExecutor.executeBlocking(() -> mShellCommandHandler.dump(pw)); - } catch (InterruptedException e) { - throw new RuntimeException("Failed to dump the Shell in 2s", e); - } - } - - @Override - public boolean handleCommand(String[] args, PrintWriter pw) { - try { - boolean[] result = new boolean[1]; - mMainExecutor.executeBlocking(() -> { - result[0] = mShellCommandHandler.handleCommand(args, pw); - }); - return result[0]; - } catch (InterruptedException e) { - throw new RuntimeException("Failed to handle Shell command in 2s", e); - } - } - @Override public void onConfigurationChanged(Configuration newConfiguration) { mMainExecutor.execute(() -> @@ -274,5 +315,38 @@ public class ShellController { mMainExecutor.execute(() -> ShellController.this.onUserProfilesChanged(profiles)); } + + @Override + public boolean handleCommand(String[] args, PrintWriter pw) { + try { + boolean[] result = new boolean[1]; + mMainExecutor.executeBlocking(() -> { + result[0] = mShellCommandHandler.handleCommand(args, pw); + }); + return result[0]; + } catch (InterruptedException e) { + throw new RuntimeException("Failed to handle Shell command in 2s", e); + } + } + + @Override + public void createExternalInterfaces(Bundle bundle) { + try { + mMainExecutor.executeBlocking(() -> { + ShellController.this.createExternalInterfaces(bundle); + }); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to get Shell command in 2s", e); + } + } + + @Override + public void dump(PrintWriter pw) { + try { + mMainExecutor.executeBlocking(() -> mShellCommandHandler.dump(pw)); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to dump the Shell in 2s", e); + } + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java index 2108c824ac6f..bc5dd11ef54e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java @@ -19,6 +19,7 @@ package com.android.wm.shell.sysui; import android.content.Context; import android.content.pm.UserInfo; import android.content.res.Configuration; +import android.os.Bundle; import androidx.annotation.NonNull; @@ -36,18 +37,6 @@ public interface ShellInterface { */ default void onInit() {} - /** - * Dumps the shell state. - */ - default void dump(PrintWriter pw) {} - - /** - * Handles a shell command. - */ - default boolean handleCommand(final String[] args, PrintWriter pw) { - return false; - } - /** * Notifies the Shell that the configuration has changed. */ @@ -74,4 +63,21 @@ public interface ShellInterface { * Notifies the Shell when a profile belonging to the user changes. */ default void onUserProfilesChanged(@NonNull List profiles) {} + + /** + * Handles a shell command. + */ + default boolean handleCommand(final String[] args, PrintWriter pw) { + return false; + } + + /** + * Updates the given {@param bundle} with the set of exposed interfaces. + */ + default void createExternalInterfaces(Bundle bundle) {} + + /** + * Dumps the shell state. + */ + default void dump(PrintWriter pw) {} } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java new file mode 100644 index 000000000000..bdda6a8e926b --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 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.wm.shell.sysui; + +/** + * General shell-related constants that are shared with users of the library. + */ +public class ShellSharedConstants { + // See IPip.aidl + public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip"; + // See ISplitScreen.aidl + public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen"; + // See IOneHanded.aidl + public static final String KEY_EXTRA_SHELL_ONE_HANDED = "extra_shell_one_handed"; + // See IShellTransitions.aidl + public static final String KEY_EXTRA_SHELL_SHELL_TRANSITIONS = + "extra_shell_shell_transitions"; + // See IStartingWindow.aidl + public static final String KEY_EXTRA_SHELL_STARTING_WINDOW = + "extra_shell_starting_window"; + // See IRecentTasks.aidl + public static final String KEY_EXTRA_SHELL_RECENT_TASKS = "extra_shell_recent_tasks"; + // See IBackAnimation.aidl + public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation"; + // See IFloatingTasks.aidl + public static final String KEY_EXTRA_SHELL_FLOATING_TASKS = "extra_shell_floating_tasks"; + // See IDesktopMode.aidl + public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode"; +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java index b34049d4ec42..da39017a0313 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java @@ -27,14 +27,6 @@ import com.android.wm.shell.common.annotations.ExternalThread; */ @ExternalThread public interface ShellTransitions { - - /** - * Returns a binder that can be passed to an external process to manipulate remote transitions. - */ - default IShellTransitions createExternalInterface() { - return null; - } - /** * Registers a remote transition. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 29d25bc39223..e26f1be881cc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -29,6 +29,7 @@ import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; +import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; import android.annotation.NonNull; import android.annotation.Nullable; @@ -61,11 +62,13 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import java.util.ArrayList; @@ -115,6 +118,7 @@ public class Transitions implements RemoteCallable { private final DefaultTransitionHandler mDefaultTransitionHandler; private final RemoteTransitionHandler mRemoteTransitionHandler; private final DisplayController mDisplayController; + private final ShellController mShellController; private final ShellTransitionImpl mImpl = new ShellTransitionImpl(); /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ @@ -142,6 +146,7 @@ public class Transitions implements RemoteCallable { public Transitions(@NonNull Context context, @NonNull ShellInit shellInit, + @NonNull ShellController shellController, @NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @NonNull DisplayController displayController, @@ -156,10 +161,14 @@ public class Transitions implements RemoteCallable { mDefaultTransitionHandler = new DefaultTransitionHandler(context, shellInit, displayController, pool, mainExecutor, mainHandler, animExecutor); mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor); + mShellController = shellController; shellInit.addInitCallback(this::onInit, this); } private void onInit() { + mShellController.addExternalInterface(KEY_EXTRA_SHELL_SHELL_TRANSITIONS, + this::createExternalInterface, this); + // The very last handler (0 in the list) should be the default one. mHandlers.add(mDefaultTransitionHandler); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default"); @@ -193,6 +202,10 @@ public class Transitions implements RemoteCallable { return mImpl; } + private ExternalInterfaceBinder createExternalInterface() { + return new IShellTransitionsImpl(this); + } + @Override public Context getContext() { return mContext; @@ -897,17 +910,6 @@ public class Transitions implements RemoteCallable { */ @ExternalThread private class ShellTransitionImpl implements ShellTransitions { - private IShellTransitionsImpl mIShellTransitions; - - @Override - public IShellTransitions createExternalInterface() { - if (mIShellTransitions != null) { - mIShellTransitions.invalidate(); - } - mIShellTransitions = new IShellTransitionsImpl(Transitions.this); - return mIShellTransitions; - } - @Override public void registerRemote(@NonNull TransitionFilter filter, @NonNull RemoteTransition remoteTransition) { @@ -928,7 +930,8 @@ public class Transitions implements RemoteCallable { * The interface for calls from outside the host process. */ @BinderThread - private static class IShellTransitionsImpl extends IShellTransitions.Stub { + private static class IShellTransitionsImpl extends IShellTransitions.Stub + implements ExternalInterfaceBinder { private Transitions mTransitions; IShellTransitionsImpl(Transitions transitions) { @@ -938,7 +941,8 @@ public class Transitions implements RemoteCallable { /** * Invalidates this instance, preventing future calls from updating the controller. */ - void invalidate() { + @Override + public void invalidate() { mTransitions = null; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index 90a377309edd..077e9ca2e88c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -63,7 +63,9 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.util.test.FakeSettingsProvider; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.sysui.ShellSharedConstants; import org.junit.Before; import org.junit.Ignore; @@ -102,6 +104,9 @@ public class BackAnimationControllerTest extends ShellTestCase { @Mock private IBackNaviAnimationController mIBackNaviAnimationController; + @Mock + private ShellController mShellController; + private BackAnimationController mController; private int mEventTime = 0; @@ -118,7 +123,7 @@ public class BackAnimationControllerTest extends ShellTestCase { ANIMATION_ENABLED); mTestableLooper = TestableLooper.get(this); mShellInit = spy(new ShellInit(mShellExecutor)); - mController = new BackAnimationController(mShellInit, + mController = new BackAnimationController(mShellInit, mShellController, mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction, mActivityTaskManager, mContext, mContentResolver); @@ -174,6 +179,12 @@ public class BackAnimationControllerTest extends ShellTestCase { verify(mShellInit, times(1)).addInitCallback(any(), any()); } + @Test + public void instantiateController_addExternalInterface() { + verify(mShellController, times(1)).addExternalInterface( + eq(ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION), any(), any()); + } + @Test @Ignore("b/207481538") public void crossActivity_screenshotAttachedAndVisible() { @@ -250,7 +261,7 @@ public class BackAnimationControllerTest extends ShellTestCase { // Toggle the setting off Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0"); ShellInit shellInit = new ShellInit(mShellExecutor); - mController = new BackAnimationController(shellInit, + mController = new BackAnimationController(shellInit, mShellController, mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction, mActivityTaskManager, mContext, mContentResolver); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index dd23d97d9199..c850a3b3b780 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -52,6 +52,7 @@ import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; @@ -67,6 +68,8 @@ import org.mockito.Mockito; @RunWith(AndroidTestingRunner.class) public class DesktopModeControllerTest extends ShellTestCase { + @Mock + private ShellController mShellController; @Mock private ShellTaskOrganizer mShellTaskOrganizer; @Mock @@ -94,8 +97,8 @@ public class DesktopModeControllerTest extends ShellTestCase { mDesktopModeTaskRepository = new DesktopModeTaskRepository(); - mController = new DesktopModeController(mContext, mShellInit, mShellTaskOrganizer, - mRootTaskDisplayAreaOrganizer, mMockTransitions, + mController = new DesktopModeController(mContext, mShellInit, mShellController, + mShellTaskOrganizer, mRootTaskDisplayAreaOrganizer, mMockTransitions, mDesktopModeTaskRepository, mMockHandler, mExecutor); when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(anyInt())).thenReturn( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java index a88c83779f25..d378a177650a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java @@ -52,6 +52,7 @@ import com.android.wm.shell.floating.views.FloatingTaskLayer; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.sysui.ShellSharedConstants; import org.junit.After; import org.junit.Before; @@ -168,6 +169,18 @@ public class FloatingTasksControllerTest extends ShellTestCase { } } + @Test + public void onInit_addExternalInterface() { + if (FLOATING_TASKS_ACTUALLY_ENABLED) { + createController(); + setUpTabletConfig(); + mController.onInit(); + + verify(mShellController, times(1)).addExternalInterface( + ShellSharedConstants.KEY_EXTRA_SHELL_FLOATING_TASKS, any(), any()); + } + } + // // Tests for floating layer, which is only available for tablets. // diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index cf8297eec061..8ad3d2a72617 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -51,6 +51,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.sysui.ShellSharedConstants; import org.junit.Before; import org.junit.Test; @@ -175,6 +176,12 @@ public class OneHandedControllerTest extends OneHandedTestCase { verify(mMockShellController, times(1)).addUserChangeListener(any()); } + @Test + public void testControllerRegisteresExternalInterface() { + verify(mMockShellController, times(1)).addExternalInterface( + eq(ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED), any(), any()); + } + @Test public void testDefaultShouldNotInOneHanded() { // Assert default transition state is STATE_NONE diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index 1e08f1e55797..d06fb55a5769 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -61,6 +62,7 @@ import com.android.wm.shell.pip.PipTransitionState; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.sysui.ShellSharedConstants; import org.junit.Before; import org.junit.Test; @@ -151,6 +153,12 @@ public class PipControllerTest extends ShellTestCase { verify(mShellController, times(1)).addKeyguardChangeListener(any()); } + @Test + public void instantiatePipController_registerExternalInterface() { + verify(mShellController, times(1)).addExternalInterface( + eq(ShellSharedConstants.KEY_EXTRA_SHELL_PIP), any(), any()); + } + @Test public void instantiatePipController_registerUserChangeListener() { verify(mShellController, times(1)).addUserChangeListener(any()); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index b8aaaa76e3c7..f6ac3ee0a8e4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -28,6 +28,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -57,7 +58,9 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.sysui.ShellCommandHandler; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.sysui.ShellSharedConstants; import com.android.wm.shell.util.GroupedRecentTaskInfo; import com.android.wm.shell.util.SplitBounds; @@ -84,6 +87,8 @@ public class RecentTasksControllerTest extends ShellTestCase { @Mock private TaskStackListenerImpl mTaskStackListener; @Mock + private ShellController mShellController; + @Mock private ShellCommandHandler mShellCommandHandler; @Mock private DesktopModeTaskRepository mDesktopModeTaskRepository; @@ -101,7 +106,7 @@ public class RecentTasksControllerTest extends ShellTestCase { when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); mShellInit = spy(new ShellInit(mMainExecutor)); mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit, - mShellCommandHandler, mTaskStackListener, mActivityTaskManager, + mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager, Optional.of(mDesktopModeTaskRepository), mMainExecutor)); mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler, null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController), @@ -120,6 +125,12 @@ public class RecentTasksControllerTest extends ShellTestCase { isA(RecentTasksController.class)); } + @Test + public void instantiateController_addExternalInterface() { + verify(mShellController, times(1)).addExternalInterface( + eq(ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS), any(), any()); + } + @Test public void testAddRemoveSplitNotifyChange() { ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java index 5a68361c595c..55883ab2ef70 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java @@ -58,6 +58,7 @@ import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.sysui.ShellSharedConstants; import com.android.wm.shell.transition.Transitions; import org.junit.Before; @@ -132,6 +133,15 @@ public class SplitScreenControllerTests extends ShellTestCase { verify(mShellController, times(1)).addKeyguardChangeListener(any()); } + @Test + public void instantiateController_addExternalInterface() { + doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor(); + when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout()); + mSplitScreenController.onInit(); + verify(mShellController, times(1)).addExternalInterface( + eq(ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN), any(), any()); + } + @Test public void testShouldAddMultipleTaskFlag_notInSplitScreen() { doReturn(false).when(mSplitScreenController).isSplitScreenVisible(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java index 35515e3bb6e8..90165d1cd1b2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java @@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -36,7 +37,9 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.sysui.ShellSharedConstants; import org.junit.Before; import org.junit.Test; @@ -56,25 +59,34 @@ public class StartingWindowControllerTests extends ShellTestCase { private @Mock Context mContext; private @Mock DisplayManager mDisplayManager; - private @Mock ShellInit mShellInit; + private @Mock ShellController mShellController; private @Mock ShellTaskOrganizer mTaskOrganizer; private @Mock ShellExecutor mMainExecutor; private @Mock StartingWindowTypeAlgorithm mTypeAlgorithm; private @Mock IconProvider mIconProvider; private @Mock TransactionPool mTransactionPool; private StartingWindowController mController; + private ShellInit mShellInit; @Before public void setUp() { MockitoAnnotations.initMocks(this); doReturn(mock(Display.class)).when(mDisplayManager).getDisplay(anyInt()); doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class)); - mController = new StartingWindowController(mContext, mShellInit, mTaskOrganizer, - mMainExecutor, mTypeAlgorithm, mIconProvider, mTransactionPool); + mShellInit = spy(new ShellInit(mMainExecutor)); + mController = new StartingWindowController(mContext, mShellInit, mShellController, + mTaskOrganizer, mMainExecutor, mTypeAlgorithm, mIconProvider, mTransactionPool); + mShellInit.init(); } @Test - public void instantiate_addInitCallback() { + public void instantiateController_addInitCallback() { verify(mShellInit, times(1)).addInitCallback(any(), any()); } + + @Test + public void instantiateController_addExternalInterface() { + verify(mShellController, times(1)).addExternalInterface( + eq(ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW), any(), any()); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java index d6ddba9e927d..fbc50c68eff9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java @@ -16,12 +16,16 @@ package com.android.wm.shell.sysui; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import android.content.Context; import android.content.pm.UserInfo; import android.content.res.Configuration; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -30,6 +34,7 @@ import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.ShellExecutor; import org.junit.After; @@ -49,6 +54,7 @@ import java.util.Locale; public class ShellControllerTest extends ShellTestCase { private static final int TEST_USER_ID = 100; + private static final String EXTRA_TEST_BINDER = "test_binder"; @Mock private ShellInit mShellInit; @@ -80,6 +86,47 @@ public class ShellControllerTest extends ShellTestCase { // Do nothing } + @Test + public void testAddExternalInterface_ensureCallback() { + Binder callback = new Binder(); + ExternalInterfaceBinder wrapper = new ExternalInterfaceBinder() { + @Override + public void invalidate() { + // Do nothing + } + + @Override + public IBinder asBinder() { + return callback; + } + }; + mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this); + + Bundle b = new Bundle(); + mController.asShell().createExternalInterfaces(b); + assertTrue(b.getIBinder(EXTRA_TEST_BINDER) == callback); + } + + @Test + public void testAddExternalInterface_disallowDuplicateKeys() { + Binder callback = new Binder(); + ExternalInterfaceBinder wrapper = new ExternalInterfaceBinder() { + @Override + public void invalidate() { + // Do nothing + } + + @Override + public IBinder asBinder() { + return callback; + } + }; + mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this); + assertThrows(IllegalArgumentException.class, () -> { + mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this); + }); + } + @Test public void testAddUserChangeListener_ensureCallback() { mController.addUserChangeListener(mUserChangeListener); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index c6492bee040e..d888b793146c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -86,7 +86,9 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.sysui.ShellSharedConstants; import org.junit.Before; import org.junit.Test; @@ -123,11 +125,24 @@ public class ShellTransitionTests extends ShellTestCase { @Test public void instantiate_addInitCallback() { ShellInit shellInit = mock(ShellInit.class); - final Transitions t = new Transitions(mContext, shellInit, mOrganizer, mTransactionPool, - createTestDisplayController(), mMainExecutor, mMainHandler, mAnimExecutor); + final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class), + mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor, + mMainHandler, mAnimExecutor); verify(shellInit, times(1)).addInitCallback(any(), eq(t)); } + @Test + public void instantiateController_addExternalInterface() { + ShellInit shellInit = new ShellInit(mMainExecutor); + ShellController shellController = mock(ShellController.class); + final Transitions t = new Transitions(mContext, shellInit, shellController, + mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor, + mMainHandler, mAnimExecutor); + shellInit.init(); + verify(shellController, times(1)).addExternalInterface( + eq(ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS), any(), any()); + } + @Test public void testBasicTransitionFlow() { Transitions transitions = createTestTransitions(); @@ -1060,8 +1075,9 @@ public class ShellTransitionTests extends ShellTestCase { private Transitions createTestTransitions() { ShellInit shellInit = new ShellInit(mMainExecutor); - final Transitions t = new Transitions(mContext, shellInit, mOrganizer, mTransactionPool, - createTestDisplayController(), mMainExecutor, mMainHandler, mAnimExecutor); + final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class), + mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor, + mMainHandler, mAnimExecutor); shellInit.init(); return t; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 85278dd4b883..f2742b7889b1 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -43,27 +43,8 @@ public class QuickStepContract { public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy"; public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius"; public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners"; - // See IPip.aidl - public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip"; - // See ISplitScreen.aidl - public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen"; - // See IFloatingTasks.aidl - public static final String KEY_EXTRA_SHELL_FLOATING_TASKS = "extra_shell_floating_tasks"; - // See IOneHanded.aidl - public static final String KEY_EXTRA_SHELL_ONE_HANDED = "extra_shell_one_handed"; - // See IShellTransitions.aidl - public static final String KEY_EXTRA_SHELL_SHELL_TRANSITIONS = - "extra_shell_shell_transitions"; - // See IStartingWindow.aidl - public static final String KEY_EXTRA_SHELL_STARTING_WINDOW = - "extra_shell_starting_window"; // See ISysuiUnlockAnimationController.aidl public static final String KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER = "unlock_animation"; - // See IRecentTasks.aidl - public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks"; - public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation"; - // See IDesktopMode.aidl - public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode"; public static final String NAV_BAR_MODE_3BUTTON_OVERLAY = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 899e57d7d0ae..66be00d8de66 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -25,15 +25,6 @@ import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_DESKTOP_MODE; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_FLOATING_TASKS; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; @@ -110,16 +101,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; -import com.android.wm.shell.back.BackAnimation; -import com.android.wm.shell.desktopmode.DesktopMode; -import com.android.wm.shell.floating.FloatingTasks; -import com.android.wm.shell.onehanded.OneHanded; -import com.android.wm.shell.pip.Pip; -import com.android.wm.shell.pip.PipAnimationController; -import com.android.wm.shell.recents.RecentTasks; -import com.android.wm.shell.splitscreen.SplitScreen; -import com.android.wm.shell.startingsurface.StartingSurface; -import com.android.wm.shell.transition.ShellTransitions; +import com.android.wm.shell.sysui.ShellInterface; import java.io.PrintWriter; import java.util.ArrayList; @@ -151,10 +133,8 @@ public class OverviewProxyService extends CurrentUserTracker implements private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000; private final Context mContext; - private final Optional mPipOptional; + private final ShellInterface mShellInterface; private final Lazy> mCentralSurfacesOptionalLazy; - private final Optional mSplitScreenOptional; - private final Optional mFloatingTasksOptional; private SysUiState mSysUiState; private final Handler mHandler; private final Lazy mNavBarControllerLazy; @@ -164,14 +144,8 @@ public class OverviewProxyService extends CurrentUserTracker implements private final List mConnectionCallbacks = new ArrayList<>(); private final Intent mQuickStepIntent; private final ScreenshotHelper mScreenshotHelper; - private final Optional mOneHandedOptional; private final CommandQueue mCommandQueue; - private final ShellTransitions mShellTransitions; - private final Optional mStartingSurface; private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController; - private final Optional mRecentTasks; - private final Optional mBackAnimation; - private final Optional mDesktopModeOptional; private final UiEventLogger mUiEventLogger; private Region mActiveNavBarRegion; @@ -456,36 +430,10 @@ public class OverviewProxyService extends CurrentUserTracker implements params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder()); params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius); params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows); - - mPipOptional.ifPresent((pip) -> params.putBinder( - KEY_EXTRA_SHELL_PIP, - pip.createExternalInterface().asBinder())); - mSplitScreenOptional.ifPresent((splitscreen) -> params.putBinder( - KEY_EXTRA_SHELL_SPLIT_SCREEN, - splitscreen.createExternalInterface().asBinder())); - mFloatingTasksOptional.ifPresent(floatingTasks -> params.putBinder( - KEY_EXTRA_SHELL_FLOATING_TASKS, - floatingTasks.createExternalInterface().asBinder())); - mOneHandedOptional.ifPresent((onehanded) -> params.putBinder( - KEY_EXTRA_SHELL_ONE_HANDED, - onehanded.createExternalInterface().asBinder())); - params.putBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS, - mShellTransitions.createExternalInterface().asBinder()); - mStartingSurface.ifPresent((startingwindow) -> params.putBinder( - KEY_EXTRA_SHELL_STARTING_WINDOW, - startingwindow.createExternalInterface().asBinder())); - params.putBinder( - KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER, + params.putBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER, mSysuiUnlockAnimationController.asBinder()); - mRecentTasks.ifPresent(recentTasks -> params.putBinder( - KEY_EXTRA_RECENT_TASKS, - recentTasks.createExternalInterface().asBinder())); - mBackAnimation.ifPresent((backAnimation) -> params.putBinder( - KEY_EXTRA_SHELL_BACK_ANIMATION, - backAnimation.createExternalInterface().asBinder())); - mDesktopModeOptional.ifPresent((desktopMode -> params.putBinder( - KEY_EXTRA_SHELL_DESKTOP_MODE, - desktopMode.createExternalInterface().asBinder()))); + // Add all the interfaces exposed by the shell + mShellInterface.createExternalInterfaces(params); try { Log.d(TAG_OPS, "OverviewProxyService connected, initializing overview proxy"); @@ -559,21 +507,14 @@ public class OverviewProxyService extends CurrentUserTracker implements @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Inject - public OverviewProxyService(Context context, CommandQueue commandQueue, + public OverviewProxyService(Context context, + CommandQueue commandQueue, + ShellInterface shellInterface, Lazy navBarControllerLazy, Lazy> centralSurfacesOptionalLazy, NavigationModeController navModeController, NotificationShadeWindowController statusBarWinController, SysUiState sysUiState, - Optional pipOptional, - Optional splitScreenOptional, - Optional floatingTasksOptional, - Optional oneHandedOptional, - Optional recentTasks, - Optional backAnimation, - Optional startingSurface, - Optional desktopModeOptional, BroadcastDispatcher broadcastDispatcher, - ShellTransitions shellTransitions, ScreenLifecycle screenLifecycle, UiEventLogger uiEventLogger, KeyguardUnlockAnimationController sysuiUnlockAnimationController, @@ -587,7 +528,7 @@ public class OverviewProxyService extends CurrentUserTracker implements } mContext = context; - mPipOptional = pipOptional; + mShellInterface = shellInterface; mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy; mHandler = new Handler(); mNavBarControllerLazy = navBarControllerLazy; @@ -602,11 +543,6 @@ public class OverviewProxyService extends CurrentUserTracker implements .supportsRoundedCornersOnWindows(mContext.getResources()); mSysUiState = sysUiState; mSysUiState.addCallback(this::notifySystemUiStateFlags); - mOneHandedOptional = oneHandedOptional; - mShellTransitions = shellTransitions; - mRecentTasks = recentTasks; - mBackAnimation = backAnimation; - mDesktopModeOptional = desktopModeOptional; mUiEventLogger = uiEventLogger; dumpManager.registerDumpable(getClass().getSimpleName(), this); @@ -636,9 +572,6 @@ public class OverviewProxyService extends CurrentUserTracker implements }); mCommandQueue = commandQueue; - mSplitScreenOptional = splitScreenOptional; - mFloatingTasksOptional = floatingTasksOptional; - // Listen for user setup startTracking(); @@ -647,7 +580,6 @@ public class OverviewProxyService extends CurrentUserTracker implements // Connect to the service updateEnabledState(); startConnectionToCurrentUser(); - mStartingSurface = startingSurface; mSysuiUnlockAnimationController = sysuiUnlockAnimationController; // Listen for assistant changes -- cgit v1.2.3-59-g8ed1b From 8eaecacd3b557166c8fca5d0cc57cccd0de827b5 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Fri, 7 Oct 2022 21:02:54 +0800 Subject: Re-increase the Extensions API version change To make the dependent features available in T_QPR2. Fix: 248644827 Test: N/A Change-Id: Iec064ca8ca4e90ce5620e1b5fb9ab18c1b7d5ad8 --- .../Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index fb0a9db6a20b..7e9c4189dabb 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -41,7 +41,7 @@ public class WindowExtensionsImpl implements WindowExtensions { // TODO(b/241126279) Introduce constants to better version functionality @Override public int getVendorApiLevel() { - return 1; + return 2; } /** -- cgit v1.2.3-59-g8ed1b From 6a113515eca600f478c562b857da54a560333c79 Mon Sep 17 00:00:00 2001 From: Mateusz Cicheński Date: Fri, 7 Oct 2022 19:15:33 +0000 Subject: Don't move stashed PiP in response to keep clear areas changes. Test: manually, enter pip, stash it, open an app -> pip doesn't move Bug: 183746978 Change-Id: Iaffb4e1683fe3fb1238fa3a7ae12f04055e40704 --- .../Shell/src/com/android/wm/shell/pip/phone/PipController.java | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index a918559d897e..30124a5363a4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -161,6 +161,10 @@ public class PipController implements PipTransitionController.PipTransitionCallb // early bail out if the keep clear areas feature is disabled return; } + if (mPipBoundsState.isStashed()) { + // don't move when stashed + return; + } // if there is another animation ongoing, wait for it to finish and try again if (mPipAnimationController.isAnimating()) { mMainExecutor.removeCallbacks( -- cgit v1.2.3-59-g8ed1b From a7457301f532c48baf23822570d126d503351901 Mon Sep 17 00:00:00 2001 From: Diego Vela Date: Tue, 30 Aug 2022 22:12:07 +0000 Subject: Update SplitController to use updated method names. Add implementation of clearSplitListenerCallback. Bug: 240265856 Test: atest CtsWindowManagerJetpackTestCases Change-Id: I5cc141faf9b29426f00a60794dc7bb86894cb61f --- .../extensions/embedding/SplitController.java | 10 ++++++++++ .../Jetpack/window-extensions-release.aar | Bin 30816 -> 30820 bytes 2 files changed, 10 insertions(+) (limited to 'libs') diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 203ece091e46..1174b685e92c 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -239,6 +239,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } + /** + * Clears the listener set in {@link SplitController#setSplitInfoListener}. + */ + @Override + public void clearSplitInfoCallback() { + synchronized (mLock) { + mEmbeddingCallback = null; + } + } + /** * Called when the transaction is ready so that the organizer can update the TaskFragments based * on the changes in transaction. diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar index b0b95f9cc871..4978e04e0115 100644 Binary files a/libs/WindowManager/Jetpack/window-extensions-release.aar and b/libs/WindowManager/Jetpack/window-extensions-release.aar differ -- cgit v1.2.3-59-g8ed1b From d55e3bc286d80e2171bc552f2afb7d0ff9b52f0e Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Fri, 7 Oct 2022 14:30:25 -0700 Subject: Import ActivityManager inner classes directly Test: studiow Bug: 251871740 Bug: 236188940 Change-Id: I91cb52184a896529fbe0a26a09df0686dccac7de --- .../Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl | 4 ++-- .../src/com/android/wm/shell/recents/IRecentTasksListener.aidl | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl index b71cc32a0347..1a6c1d65db03 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl @@ -16,7 +16,7 @@ package com.android.wm.shell.recents; -import android.app.ActivityManager; +import android.app.ActivityManager.RunningTaskInfo; import com.android.wm.shell.recents.IRecentTasksListener; import com.android.wm.shell.util.GroupedRecentTaskInfo; @@ -44,5 +44,5 @@ interface IRecentTasks { /** * Gets the set of running tasks. */ - ActivityManager.RunningTaskInfo[] getRunningTasks(int maxNum) = 4; + RunningTaskInfo[] getRunningTasks(int maxNum) = 4; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl index 59f72335678e..e8f58fe2bfad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl @@ -16,7 +16,7 @@ package com.android.wm.shell.recents; -import android.app.ActivityManager; +import android.app.ActivityManager.RunningTaskInfo; /** * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks. @@ -31,10 +31,10 @@ oneway interface IRecentTasksListener { /** * Called when a running task appears. */ - void onRunningTaskAppeared(in ActivityManager.RunningTaskInfo taskInfo); + void onRunningTaskAppeared(in RunningTaskInfo taskInfo); /** * Called when a running task vanishes. */ - void onRunningTaskVanished(in ActivityManager.RunningTaskInfo taskInfo); -} \ No newline at end of file + void onRunningTaskVanished(in RunningTaskInfo taskInfo); +} -- cgit v1.2.3-59-g8ed1b From b30627adad066229781c422cba562d6ca2eb8d25 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Tue, 4 Oct 2022 16:44:53 -0700 Subject: Resize tasks from corners when in touch mode Update task resizing logic for desktop mode. Add a new 44x44dp touch region in each corner of the task that is centered around the corner. This is only used when resizing via touch. This simplifies task resizing with touches and ensures that task resize and back gesture do not conflict. New task resize regions: - with touch: use 44x44dp rect in each corner - with mouse: use 30dp wide task edges TODO: - investigate how to pass taps in touch corners through to app Bug: 251270585 Test: using touches to resize task for corners, check that resizing from edges no longer possible Test: use mouse to resize task from edges Change-Id: Ib1aaf69d2971c29a35200b0f3807871343aa1bf8 --- .../shell/windowdecor/CaptionWindowDecoration.java | 19 ++- .../shell/windowdecor/DragResizeInputListener.java | 165 +++++++++++++++++++-- .../wm/shell/windowdecor/TaskPositioner.java | 3 +- 3 files changed, 164 insertions(+), 23 deletions(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 733f6b7d5dbf..9a9dca05eb33 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -28,6 +28,7 @@ import android.os.Handler; import android.view.Choreographer; import android.view.SurfaceControl; import android.view.View; +import android.view.ViewConfiguration; import android.window.WindowContainerTransaction; import com.android.wm.shell.R; @@ -55,6 +56,7 @@ public class CaptionWindowDecoration extends WindowDecoration mTouchSlop) { + mDragging = true; + } + } else { + // For all other types allow immediate dragging. + mDragging = true; + } + if (mDragging) { + mCallback.onDragResizeMove(rawX, rawY); + result = true; + } break; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { - int dragPointerIndex = e.findPointerIndex(mDragPointerId); - mCallback.onDragResizeEnd( - e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex)); + if (mDragging) { + int dragPointerIndex = e.findPointerIndex(mDragPointerId); + mCallback.onDragResizeEnd( + e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex)); + } + mDragging = false; + mShouldHandleEvents = false; + mActionDownPoint.set(0, 0); mDragPointerId = -1; + result = true; break; } case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_MOVE: { updateCursorType(e.getXCursorPosition(), e.getYCursorPosition()); + result = true; break; } case MotionEvent.ACTION_HOVER_EXIT: mInputManager.setPointerIconType(PointerIcon.TYPE_DEFAULT); + result = true; break; } - return true; + return result; + } + + private boolean isInCornerBounds(float xf, float yf) { + return calculateCornersCtrlType(xf, yf) != 0; + } + + private boolean isInResizeHandleBounds(float x, float y) { + return calculateResizeHandlesCtrlType(x, y) != 0; + } + + @TaskPositioner.CtrlType + private int calculateCtrlType(boolean isTouch, float x, float y) { + if (isTouch) { + return calculateCornersCtrlType(x, y); + } + return calculateResizeHandlesCtrlType(x, y); } @TaskPositioner.CtrlType - private int calculateCtrlType(float x, float y) { + private int calculateResizeHandlesCtrlType(float x, float y) { int ctrlType = 0; if (x < mResizeHandleThickness) { ctrlType |= TaskPositioner.CTRL_TYPE_LEFT; @@ -267,8 +383,27 @@ class DragResizeInputListener implements AutoCloseable { return ctrlType; } + @TaskPositioner.CtrlType + private int calculateCornersCtrlType(float x, float y) { + int xi = (int) x; + int yi = (int) y; + if (mLeftTopCornerBounds.contains(xi, yi)) { + return TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_TOP; + } + if (mLeftBottomCornerBounds.contains(xi, yi)) { + return TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_BOTTOM; + } + if (mRightTopCornerBounds.contains(xi, yi)) { + return TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_TOP; + } + if (mRightBottomCornerBounds.contains(xi, yi)) { + return TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_BOTTOM; + } + return 0; + } + private void updateCursorType(float x, float y) { - @TaskPositioner.CtrlType int ctrlType = calculateCtrlType(x, y); + @TaskPositioner.CtrlType int ctrlType = calculateResizeHandlesCtrlType(x, y); int cursorType = PointerIcon.TYPE_DEFAULT; switch (ctrlType) { @@ -292,4 +427,4 @@ class DragResizeInputListener implements AutoCloseable { mInputManager.setPointerIconType(cursorType); } } -} +} \ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java index 280569b05d87..27c10114ac0e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java @@ -25,9 +25,10 @@ import com.android.wm.shell.ShellTaskOrganizer; class TaskPositioner implements DragResizeCallback { - @IntDef({CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM}) + @IntDef({CTRL_TYPE_UNDEFINED, CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM}) @interface CtrlType {} + static final int CTRL_TYPE_UNDEFINED = 0; static final int CTRL_TYPE_LEFT = 1; static final int CTRL_TYPE_RIGHT = 2; static final int CTRL_TYPE_TOP = 4; -- cgit v1.2.3-59-g8ed1b From 1e8069e8da3fe480a43dc34d280d01fa0f4fd568 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 9 Oct 2022 14:59:59 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I602bdeb57f7f57b0708efa89fe72ffc9a377e528 --- libs/WindowManager/Shell/res/values-te/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libs') diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index 88bb13004657..a3e21f745707 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -28,7 +28,7 @@ "పాజ్ చేయి" "దాటవేసి తర్వాత దానికి వెళ్లు" "దాటవేసి మునుపటి దానికి వెళ్లు" - "పరిమాణం మార్చు" + "సైజ్‌ మార్చు" "స్టాచ్" "ఆన్‌స్టాచ్" "స్క్రీన్ విభజనతో యాప్‌ పని చేయకపోవచ్చు." -- cgit v1.2.3-59-g8ed1b From f7d61d7dda9039c34c1e751efe9fb1b78477b9b0 Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Wed, 7 Sep 2022 17:33:41 +0100 Subject: Refactor SplashScreenAnimation so it can be reused in other parts of the code. Bug: 202826469 Test: manual Change-Id: Ib2ce8b8b69bc3f9e4b3003978bac8b3884563678 --- libs/WindowManager/Shell/Android.bp | 3 + .../startingsurface/SplashScreenExitAnimation.java | 251 +-------------- .../SplashScreenExitAnimationUtils.java | 358 +++++++++++++++++++++ 3 files changed, 365 insertions(+), 247 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java (limited to 'libs') diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 82573b2b9acc..f615ad6e671b 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -45,6 +45,9 @@ filegroup { "src/com/android/wm/shell/util/**/*.java", "src/com/android/wm/shell/common/split/SplitScreenConstants.java", "src/com/android/wm/shell/sysui/ShellSharedConstants.java", + "src/com/android/wm/shell/common/TransactionPool.java", + "src/com/android/wm/shell/animation/Interpolators.java", + "src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java", ], path: "src", } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java index 014f02bcf8b7..8bba44049c88 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java @@ -15,38 +15,20 @@ */ package com.android.wm.shell.startingsurface; -import static android.view.Choreographer.CALLBACK_COMMIT; import static android.view.View.GONE; import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLASHSCREEN_EXIT_ANIM; import android.animation.Animator; -import android.animation.ValueAnimator; import android.content.Context; -import android.graphics.BlendMode; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Point; -import android.graphics.RadialGradient; import android.graphics.Rect; -import android.graphics.Shader; -import android.util.MathUtils; import android.util.Slog; -import android.view.Choreographer; import android.view.SurfaceControl; -import android.view.SyncRtSurfaceTransactionApplier; import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.animation.Interpolator; -import android.view.animation.PathInterpolator; import android.window.SplashScreenView; import com.android.internal.jank.InteractionJankMonitor; import com.android.wm.shell.R; -import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.TransactionPool; /** @@ -55,14 +37,8 @@ import com.android.wm.shell.common.TransactionPool; */ public class SplashScreenExitAnimation implements Animator.AnimatorListener { private static final boolean DEBUG_EXIT_ANIMATION = false; - private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false; private static final String TAG = StartingWindowController.TAG; - private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f); - private static final Interpolator MASK_RADIUS_INTERPOLATOR = - new PathInterpolator(0f, 0f, 0.4f, 1f); - private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f); - private final SurfaceControl mFirstWindowSurface; private final Rect mFirstWindowFrame = new Rect(); private final SplashScreenView mSplashScreenView; @@ -75,9 +51,6 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { private final float mBrandingStartAlpha; private final TransactionPool mTransactionPool; - private ValueAnimator mMainAnimator; - private ShiftUpAnimation mShiftUpAnimation; - private RadialVanishAnimation mRadialVanishAnimation; private Runnable mFinishCallback; SplashScreenExitAnimation(Context context, SplashScreenView view, SurfaceControl leash, @@ -121,187 +94,10 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { } void startAnimations() { - mMainAnimator = createAnimator(); - mMainAnimator.start(); - } - - // fade out icon, reveal app, shift up main window - private ValueAnimator createAnimator() { - // reveal app - final float transparentRatio = 0.8f; - final int globalHeight = mSplashScreenView.getHeight(); - final int verticalCircleCenter = 0; - final int finalVerticalLength = globalHeight - verticalCircleCenter; - final int halfWidth = mSplashScreenView.getWidth() / 2; - final int endRadius = (int) (0.5 + (1f / transparentRatio * (int) - Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth))); - final int[] colors = {Color.WHITE, Color.WHITE, Color.TRANSPARENT}; - final float[] stops = {0f, transparentRatio, 1f}; - - mRadialVanishAnimation = new RadialVanishAnimation(mSplashScreenView); - mRadialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter); - mRadialVanishAnimation.setRadius(0 /* initRadius */, endRadius); - mRadialVanishAnimation.setRadialPaintParam(colors, stops); - - if (mFirstWindowSurface != null && mFirstWindowSurface.isValid()) { - // shift up main window - View occludeHoleView = new View(mSplashScreenView.getContext()); - if (DEBUG_EXIT_ANIMATION_BLEND) { - occludeHoleView.setBackgroundColor(Color.BLUE); - } else { - occludeHoleView.setBackgroundColor(mSplashScreenView.getInitBackgroundColor()); - } - final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( - WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength); - mSplashScreenView.addView(occludeHoleView, params); - - mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView); - } - - ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); - animator.setDuration(mAnimationDuration); - animator.setInterpolator(Interpolators.LINEAR); - animator.addListener(this); - animator.addUpdateListener(a -> onAnimationProgress((float) a.getAnimatedValue())); - return animator; - } - - private static class RadialVanishAnimation extends View { - private final SplashScreenView mView; - private int mInitRadius; - private int mFinishRadius; - - private final Point mCircleCenter = new Point(); - private final Matrix mVanishMatrix = new Matrix(); - private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - - RadialVanishAnimation(SplashScreenView target) { - super(target.getContext()); - mView = target; - mView.addView(this); - mVanishPaint.setAlpha(0); - } - - void onAnimationProgress(float linearProgress) { - if (mVanishPaint.getShader() == null) { - return; - } - - final float radiusProgress = MASK_RADIUS_INTERPOLATOR.getInterpolation(linearProgress); - final float alphaProgress = Interpolators.ALPHA_OUT.getInterpolation(linearProgress); - final float scale = mInitRadius + (mFinishRadius - mInitRadius) * radiusProgress; - - mVanishMatrix.setScale(scale, scale); - mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y); - mVanishPaint.getShader().setLocalMatrix(mVanishMatrix); - mVanishPaint.setAlpha(Math.round(0xFF * alphaProgress)); - - postInvalidate(); - } - - void setRadius(int initRadius, int finishRadius) { - if (DEBUG_EXIT_ANIMATION) { - Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius - + " final " + finishRadius); - } - mInitRadius = initRadius; - mFinishRadius = finishRadius; - } - - void setCircleCenter(int x, int y) { - if (DEBUG_EXIT_ANIMATION) { - Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y); - } - mCircleCenter.set(x, y); - } - - void setRadialPaintParam(int[] colors, float[] stops) { - // setup gradient shader - final RadialGradient rShader = - new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP); - mVanishPaint.setShader(rShader); - if (!DEBUG_EXIT_ANIMATION_BLEND) { - // We blend the reveal gradient with the splash screen using DST_OUT so that the - // splash screen is fully visible when radius = 0 (or gradient opacity is 0) and - // fully invisible when radius = finishRadius AND gradient opacity is 1. - mVanishPaint.setBlendMode(BlendMode.DST_OUT); - } - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint); - } - } - - private final class ShiftUpAnimation { - private final float mFromYDelta; - private final float mToYDelta; - private final View mOccludeHoleView; - private final SyncRtSurfaceTransactionApplier mApplier; - private final Matrix mTmpTransform = new Matrix(); - - ShiftUpAnimation(float fromYDelta, float toYDelta, View occludeHoleView) { - mFromYDelta = fromYDelta; - mToYDelta = toYDelta; - mOccludeHoleView = occludeHoleView; - mApplier = new SyncRtSurfaceTransactionApplier(occludeHoleView); - } - - void onAnimationProgress(float linearProgress) { - if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid() - || !mSplashScreenView.isAttachedToWindow()) { - return; - } - - final float progress = SHIFT_UP_INTERPOLATOR.getInterpolation(linearProgress); - final float dy = mFromYDelta + (mToYDelta - mFromYDelta) * progress; - - mOccludeHoleView.setTranslationY(dy); - mTmpTransform.setTranslate(0 /* dx */, dy); - - // set the vsyncId to ensure the transaction doesn't get applied too early. - final SurfaceControl.Transaction tx = mTransactionPool.acquire(); - tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId()); - mTmpTransform.postTranslate(mFirstWindowFrame.left, - mFirstWindowFrame.top + mMainWindowShiftLength); - - SyncRtSurfaceTransactionApplier.SurfaceParams - params = new SyncRtSurfaceTransactionApplier.SurfaceParams - .Builder(mFirstWindowSurface) - .withMatrix(mTmpTransform) - .withMergeTransaction(tx) - .build(); - mApplier.scheduleApply(params); - - mTransactionPool.release(tx); - } - - void finish() { - if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) { - return; - } - final SurfaceControl.Transaction tx = mTransactionPool.acquire(); - if (mSplashScreenView.isAttachedToWindow()) { - tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId()); - - SyncRtSurfaceTransactionApplier.SurfaceParams - params = new SyncRtSurfaceTransactionApplier.SurfaceParams - .Builder(mFirstWindowSurface) - .withWindowCrop(null) - .withMergeTransaction(tx) - .build(); - mApplier.scheduleApply(params); - } else { - tx.setWindowCrop(mFirstWindowSurface, null); - tx.apply(); - } - mTransactionPool.release(tx); - - Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT, - mFirstWindowSurface::release, null); - } + SplashScreenExitAnimationUtils.startAnimations(mSplashScreenView, mFirstWindowSurface, + mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame, mAnimationDuration, + mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha, mAppRevealDelay, + mAppRevealDuration, this); } private void reset() { @@ -316,9 +112,6 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { mFinishCallback = null; } } - if (mShiftUpAnimation != null) { - mShiftUpAnimation.finish(); - } } @Override @@ -342,40 +135,4 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { public void onAnimationRepeat(Animator animation) { // ignore } - - private void onFadeOutProgress(float linearProgress) { - final float iconProgress = ICON_INTERPOLATOR.getInterpolation( - getProgress(linearProgress, 0 /* delay */, mIconFadeOutDuration)); - final View iconView = mSplashScreenView.getIconView(); - final View brandingView = mSplashScreenView.getBrandingView(); - if (iconView != null) { - iconView.setAlpha(mIconStartAlpha * (1 - iconProgress)); - } - if (brandingView != null) { - brandingView.setAlpha(mBrandingStartAlpha * (1 - iconProgress)); - } - } - - private void onAnimationProgress(float linearProgress) { - onFadeOutProgress(linearProgress); - - final float revealLinearProgress = getProgress(linearProgress, mAppRevealDelay, - mAppRevealDuration); - - if (mRadialVanishAnimation != null) { - mRadialVanishAnimation.onAnimationProgress(revealLinearProgress); - } - - if (mShiftUpAnimation != null) { - mShiftUpAnimation.onAnimationProgress(revealLinearProgress); - } - } - - private float getProgress(float linearProgress, long delay, long duration) { - return MathUtils.constrain( - (linearProgress * (mAnimationDuration) - delay) / duration, - 0.0f, - 1.0f - ); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java new file mode 100644 index 000000000000..3098e55ec78b --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2022 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.wm.shell.startingsurface; + +import static android.view.Choreographer.CALLBACK_COMMIT; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.BlendMode; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.RadialGradient; +import android.graphics.Rect; +import android.graphics.Shader; +import android.util.MathUtils; +import android.util.Slog; +import android.view.Choreographer; +import android.view.SurfaceControl; +import android.view.SyncRtSurfaceTransactionApplier; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; +import android.window.SplashScreenView; + +import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.TransactionPool; + +/** + * Utilities for creating the splash screen window animations. + * @hide + */ +public class SplashScreenExitAnimationUtils { + private static final boolean DEBUG_EXIT_ANIMATION = false; + private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false; + private static final String TAG = "SplashScreenExitAnimationUtils"; + + private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f); + private static final Interpolator MASK_RADIUS_INTERPOLATOR = + new PathInterpolator(0f, 0f, 0.4f, 1f); + private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f); + + /** + * Creates and starts the animator to fade out the icon, reveal the app, and shift up main + * window. + * @hide + */ + public static void startAnimations(ViewGroup splashScreenView, + SurfaceControl firstWindowSurface, int mainWindowShiftLength, + TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration, + int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha, + int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) { + ValueAnimator animator = + createAnimator(splashScreenView, firstWindowSurface, mainWindowShiftLength, + transactionPool, firstWindowFrame, animationDuration, iconFadeOutDuration, + iconStartAlpha, brandingStartAlpha, appRevealDelay, appRevealDuration, + animatorListener); + animator.start(); + } + + /** + * Creates the animator to fade out the icon, reveal the app, and shift up main window. + * @hide + */ + private static ValueAnimator createAnimator(ViewGroup splashScreenView, + SurfaceControl firstWindowSurface, int mMainWindowShiftLength, + TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration, + int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha, + int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) { + // reveal app + final float transparentRatio = 0.8f; + final int globalHeight = splashScreenView.getHeight(); + final int verticalCircleCenter = 0; + final int finalVerticalLength = globalHeight - verticalCircleCenter; + final int halfWidth = splashScreenView.getWidth() / 2; + final int endRadius = (int) (0.5 + (1f / transparentRatio * (int) + Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth))); + final int[] colors = {Color.WHITE, Color.WHITE, Color.TRANSPARENT}; + final float[] stops = {0f, transparentRatio, 1f}; + + RadialVanishAnimation radialVanishAnimation = new RadialVanishAnimation(splashScreenView); + radialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter); + radialVanishAnimation.setRadius(0 /* initRadius */, endRadius); + radialVanishAnimation.setRadialPaintParam(colors, stops); + + View occludeHoleView = null; + ShiftUpAnimation shiftUpAnimation = null; + if (firstWindowSurface != null && firstWindowSurface.isValid()) { + // shift up main window + occludeHoleView = new View(splashScreenView.getContext()); + if (DEBUG_EXIT_ANIMATION_BLEND) { + occludeHoleView.setBackgroundColor(Color.BLUE); + } else if (splashScreenView instanceof SplashScreenView) { + occludeHoleView.setBackgroundColor( + ((SplashScreenView) splashScreenView).getInitBackgroundColor()); + } else { + occludeHoleView.setBackgroundColor( + isDarkTheme(splashScreenView.getContext()) ? Color.BLACK : Color.WHITE); + } + final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( + WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength); + splashScreenView.addView(occludeHoleView, params); + + shiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView, + firstWindowSurface, splashScreenView, transactionPool, firstWindowFrame, + mMainWindowShiftLength); + } + + ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); + animator.setDuration(animationDuration); + animator.setInterpolator(Interpolators.LINEAR); + if (animatorListener != null) { + animator.addListener(animatorListener); + } + View finalOccludeHoleView = occludeHoleView; + ShiftUpAnimation finalShiftUpAnimation = shiftUpAnimation; + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (finalShiftUpAnimation != null) { + finalShiftUpAnimation.finish(); + } + splashScreenView.removeView(radialVanishAnimation); + splashScreenView.removeView(finalOccludeHoleView); + } + }); + animator.addUpdateListener(animation -> { + float linearProgress = (float) animation.getAnimatedValue(); + + // Fade out progress + final float iconProgress = + ICON_INTERPOLATOR.getInterpolation(getProgress( + linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration)); + View iconView = null; + View brandingView = null; + if (splashScreenView instanceof SplashScreenView) { + iconView = ((SplashScreenView) splashScreenView).getIconView(); + brandingView = ((SplashScreenView) splashScreenView).getBrandingView(); + } + if (iconView != null) { + iconView.setAlpha(iconStartAlpha * (1 - iconProgress)); + } + if (brandingView != null) { + brandingView.setAlpha(brandingStartAlpha * (1 - iconProgress)); + } + + final float revealLinearProgress = getProgress(linearProgress, appRevealDelay, + appRevealDuration, animationDuration); + + radialVanishAnimation.onAnimationProgress(revealLinearProgress); + + if (finalShiftUpAnimation != null) { + finalShiftUpAnimation.onAnimationProgress(revealLinearProgress); + } + }); + return animator; + } + + private static float getProgress(float linearProgress, long delay, long duration, + int animationDuration) { + return MathUtils.constrain( + (linearProgress * (animationDuration) - delay) / duration, + 0.0f, + 1.0f + ); + } + + private static boolean isDarkTheme(Context context) { + Configuration configuration = context.getResources().getConfiguration(); + int nightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK; + return nightMode == Configuration.UI_MODE_NIGHT_YES; + } + + /** + * View which creates a circular reveal of the underlying view. + * @hide + */ + @SuppressLint("ViewConstructor") + public static class RadialVanishAnimation extends View { + private final ViewGroup mView; + private int mInitRadius; + private int mFinishRadius; + + private final Point mCircleCenter = new Point(); + private final Matrix mVanishMatrix = new Matrix(); + private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public RadialVanishAnimation(ViewGroup target) { + super(target.getContext()); + mView = target; + mView.addView(this); + if (getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + ((ViewGroup.MarginLayoutParams) getLayoutParams()).setMargins(0, 0, 0, 0); + } + mVanishPaint.setAlpha(0); + } + + void onAnimationProgress(float linearProgress) { + if (mVanishPaint.getShader() == null) { + return; + } + + final float radiusProgress = MASK_RADIUS_INTERPOLATOR.getInterpolation(linearProgress); + final float alphaProgress = Interpolators.ALPHA_OUT.getInterpolation(linearProgress); + final float scale = mInitRadius + (mFinishRadius - mInitRadius) * radiusProgress; + + mVanishMatrix.setScale(scale, scale); + mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y); + mVanishPaint.getShader().setLocalMatrix(mVanishMatrix); + mVanishPaint.setAlpha(Math.round(0xFF * alphaProgress)); + + postInvalidate(); + } + + void setRadius(int initRadius, int finishRadius) { + if (DEBUG_EXIT_ANIMATION) { + Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius + + " final " + finishRadius); + } + mInitRadius = initRadius; + mFinishRadius = finishRadius; + } + + void setCircleCenter(int x, int y) { + if (DEBUG_EXIT_ANIMATION) { + Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y); + } + mCircleCenter.set(x, y); + } + + void setRadialPaintParam(int[] colors, float[] stops) { + // setup gradient shader + final RadialGradient rShader = + new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP); + mVanishPaint.setShader(rShader); + if (!DEBUG_EXIT_ANIMATION_BLEND) { + // We blend the reveal gradient with the splash screen using DST_OUT so that the + // splash screen is fully visible when radius = 0 (or gradient opacity is 0) and + // fully invisible when radius = finishRadius AND gradient opacity is 1. + mVanishPaint.setBlendMode(BlendMode.DST_OUT); + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint); + } + } + + /** + * Shifts up the main window. + * @hide + */ + public static final class ShiftUpAnimation { + private final float mFromYDelta; + private final float mToYDelta; + private final View mOccludeHoleView; + private final SyncRtSurfaceTransactionApplier mApplier; + private final Matrix mTmpTransform = new Matrix(); + private final SurfaceControl mFirstWindowSurface; + private final ViewGroup mSplashScreenView; + private final TransactionPool mTransactionPool; + private final Rect mFirstWindowFrame; + private final int mMainWindowShiftLength; + + public ShiftUpAnimation(float fromYDelta, float toYDelta, View occludeHoleView, + SurfaceControl firstWindowSurface, ViewGroup splashScreenView, + TransactionPool transactionPool, Rect firstWindowFrame, + int mainWindowShiftLength) { + mFromYDelta = fromYDelta; + mToYDelta = toYDelta; + mOccludeHoleView = occludeHoleView; + mApplier = new SyncRtSurfaceTransactionApplier(occludeHoleView); + mFirstWindowSurface = firstWindowSurface; + mSplashScreenView = splashScreenView; + mTransactionPool = transactionPool; + mFirstWindowFrame = firstWindowFrame; + mMainWindowShiftLength = mainWindowShiftLength; + } + + void onAnimationProgress(float linearProgress) { + if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid() + || !mSplashScreenView.isAttachedToWindow()) { + return; + } + + final float progress = SHIFT_UP_INTERPOLATOR.getInterpolation(linearProgress); + final float dy = mFromYDelta + (mToYDelta - mFromYDelta) * progress; + + mOccludeHoleView.setTranslationY(dy); + mTmpTransform.setTranslate(0 /* dx */, dy); + + // set the vsyncId to ensure the transaction doesn't get applied too early. + final SurfaceControl.Transaction tx = mTransactionPool.acquire(); + tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId()); + mTmpTransform.postTranslate(mFirstWindowFrame.left, + mFirstWindowFrame.top + mMainWindowShiftLength); + + SyncRtSurfaceTransactionApplier.SurfaceParams + params = new SyncRtSurfaceTransactionApplier.SurfaceParams + .Builder(mFirstWindowSurface) + .withMatrix(mTmpTransform) + .withMergeTransaction(tx) + .build(); + mApplier.scheduleApply(params); + + mTransactionPool.release(tx); + } + + void finish() { + if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) { + return; + } + final SurfaceControl.Transaction tx = mTransactionPool.acquire(); + if (mSplashScreenView.isAttachedToWindow()) { + tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId()); + + SyncRtSurfaceTransactionApplier.SurfaceParams + params = new SyncRtSurfaceTransactionApplier.SurfaceParams + .Builder(mFirstWindowSurface) + .withWindowCrop(null) + .withMergeTransaction(tx) + .build(); + mApplier.scheduleApply(params); + } else { + tx.setWindowCrop(mFirstWindowSurface, null); + tx.apply(); + } + mTransactionPool.release(tx); + + Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT, + mFirstWindowSurface::release, null); + } + } +} -- cgit v1.2.3-59-g8ed1b From bd839dbdd1287f290a7dad0895c685368ebdea33 Mon Sep 17 00:00:00 2001 From: mattsziklay Date: Wed, 28 Sep 2022 13:44:18 -0700 Subject: Add bar type window caption. This is the first CL towards implementing the bar type caption which will have a back button, a handle, and a close button. The caption will also disappear when the attached task is not in focus. Still to be implemented: 1: Windowing options appearing on handle click 2: Drag down in fullscreen to enter desktop mode 3: Caption display via hovering when cursor available Bug: 239960071 Test: Manual: confirm new caption displays with the correct buttons/layout. Confirm buttons work and focus change works with multiple tasks. Test: Atest WindowDecorationTests Change-Id: I2ebed76b803d1f337edc3e7340966909f63eaf40 --- .../Shell/res/drawable/decor_back_button_dark.xml | 32 ++++++++ .../Shell/res/drawable/decor_caption_title.xml | 4 +- .../Shell/res/drawable/decor_close_button_dark.xml | 8 +- .../Shell/res/drawable/decor_handle_dark.xml | 23 ++++++ .../Shell/res/layout/caption_window_decoration.xml | 30 +++----- libs/WindowManager/Shell/res/values/strings.xml | 4 + .../com/android/wm/shell/dagger/WMShellModule.java | 14 ++-- .../windowdecor/CaptionWindowDecorViewModel.java | 73 +++++++++---------- .../shell/windowdecor/CaptionWindowDecoration.java | 85 +++++++++++++--------- .../wm/shell/windowdecor/WindowDecoration.java | 21 ++++-- .../shell/windowdecor/WindowDecorationTests.java | 5 +- 11 files changed, 185 insertions(+), 114 deletions(-) create mode 100644 libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml create mode 100644 libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml (limited to 'libs') diff --git a/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml new file mode 100644 index 000000000000..66e5b43d76af --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml @@ -0,0 +1,32 @@ + + + + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml b/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml index 8207365a737d..53a8bb18537c 100644 --- a/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml +++ b/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml @@ -15,8 +15,6 @@ ~ limitations under the License. --> - + diff --git a/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml index f2f1a1d55dee..851cbf26afc3 100644 --- a/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml +++ b/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml @@ -18,15 +18,13 @@ android:width="32.0dp" android:height="32.0dp" android:viewportWidth="32.0" - android:viewportHeight="32.0" - android:tint="@color/decor_button_dark_color" - > + android:viewportHeight="32.0"> + android:fillColor="@android:color/black" + android:pathData="M12.45,38.35 L9.65,35.55 21.2,24 9.65,12.45 12.45,9.65 24,21.2 35.55,9.65 38.35,12.45 26.8,24 38.35,35.55 35.55,38.35 24,26.8Z"/> diff --git a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml new file mode 100644 index 000000000000..ee0f4663c940 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml @@ -0,0 +1,23 @@ + + + + diff --git a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml index d183e42c173b..38cd5702f134 100644 --- a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml +++ b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml @@ -17,39 +17,33 @@