diff options
Diffstat (limited to 'libs')
63 files changed, 1415 insertions, 458 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 08b7bb89d10c..39cfacec8447 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -201,7 +201,7 @@ class SplitContainer { return null; } return new SplitInfo(primaryActivityStack, secondaryActivityStack, - mCurrentSplitAttributes, mToken); + mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mToken)); } static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) { 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 ae3a854baf9f..1abda4287800 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -35,6 +35,7 @@ import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHA import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED; import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED; +import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_ACTIVITY_STACK_TOKEN; import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG; import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior; import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior; @@ -55,6 +56,7 @@ import android.app.ActivityOptions; import android.app.ActivityThread; import android.app.Application; import android.app.Instrumentation; +import android.app.servertransaction.ClientTransactionListenerController; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -102,6 +104,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; +import java.util.function.BiConsumer; /** * Main controller class that manages split states and presentation. @@ -112,10 +115,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen static final boolean ENABLE_SHELL_TRANSITIONS = SystemProperties.getBoolean("persist.wm.debug.shell_transit", true); - // TODO(b/295993745): remove after prebuilt library is updated. - private static final String KEY_ACTIVITY_STACK_TOKEN = - "androidx.window.extensions.embedding.ActivityStackToken"; - @VisibleForTesting @GuardedBy("mLock") final SplitPresenter mPresenter; @@ -181,6 +180,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private final List<ActivityStack> mLastReportedActivityStacks = new ArrayList<>(); + /** WM Jetpack set callback for {@link EmbeddedActivityWindowInfo}. */ + @GuardedBy("mLock") + @Nullable + private Pair<Executor, Consumer<EmbeddedActivityWindowInfo>> + mEmbeddedActivityWindowInfoCallback; + + /** Listener registered to {@link ClientTransactionListenerController}. */ + @GuardedBy("mLock") + @Nullable + private final BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener = + Flags.activityWindowInfoFlag() + ? this::onActivityWindowInfoChanged + : null; + private final Handler mHandler; final Object mLock = new Object(); private final ActivityStartMonitor mActivityStartMonitor; @@ -554,7 +567,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } @Override - public void updateActivityStackAttributes(@NonNull IBinder activityStackToken, + public void updateActivityStackAttributes(@NonNull ActivityStack.Token activityStackToken, @NonNull ActivityStackAttributes attributes) { if (!Flags.activityEmbeddingOverlayPresentationFlag()) { return; @@ -563,7 +576,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen Objects.requireNonNull(attributes); synchronized (mLock) { - final TaskFragmentContainer container = getContainer(activityStackToken); + final TaskFragmentContainer container = getContainer(activityStackToken.getRawToken()); if (container == null) { Log.w(TAG, "Cannot find TaskFragmentContainer for token:" + activityStackToken); return; @@ -583,13 +596,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @Override @Nullable - public ParentContainerInfo getParentContainerInfo(@NonNull IBinder activityStackToken) { + public ParentContainerInfo getParentContainerInfo( + @NonNull ActivityStack.Token activityStackToken) { if (!Flags.activityEmbeddingOverlayPresentationFlag()) { return null; } Objects.requireNonNull(activityStackToken); synchronized (mLock) { - final TaskFragmentContainer container = getContainer(activityStackToken); + final TaskFragmentContainer container = getContainer(activityStackToken.getRawToken()); if (container == null) { return null; } @@ -601,7 +615,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @Override @Nullable - public IBinder getActivityStackToken(@NonNull String tag) { + public ActivityStack.Token getActivityStackToken(@NonNull String tag) { if (!Flags.activityEmbeddingOverlayPresentationFlag()) { return null; } @@ -612,7 +626,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (taskFragmentContainer == null) { return null; } - return taskFragmentContainer.getTaskFragmentToken(); + return ActivityStack.Token.createFromBinder(taskFragmentContainer + .getTaskFragmentToken()); } } @@ -2457,6 +2472,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } @VisibleForTesting + @Nullable + ActivityThread.ActivityClientRecord getActivityClientRecord(@NonNull Activity activity) { + return ActivityThread.currentActivityThread() + .getActivityClient(activity.getActivityToken()); + } + + @VisibleForTesting ActivityStartMonitor getActivityStartMonitor() { return mActivityStartMonitor; } @@ -2469,8 +2491,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @VisibleForTesting @Nullable IBinder getTaskFragmentTokenFromActivityClientRecord(@NonNull Activity activity) { - final ActivityThread.ActivityClientRecord record = ActivityThread.currentActivityThread() - .getActivityClient(activity.getActivityToken()); + final ActivityThread.ActivityClientRecord record = getActivityClientRecord(activity); return record != null ? record.mTaskFragmentToken : null; } @@ -2761,8 +2782,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // TODO(b/232042367): Consolidate the activity create handling so that we can handle // cross-process the same as normal. - IBinder activityStackToken = options.getBinder(KEY_ACTIVITY_STACK_TOKEN); - if (activityStackToken != null) { + final Bundle bundle = options.getBundle(KEY_ACTIVITY_STACK_TOKEN); + if (bundle != null) { + final IBinder activityStackToken = ActivityStack.Token.readFromBundle(bundle) + .getRawToken(); // Put activityStack token to #KEY_LAUNCH_TASK_FRAGMENT_TOKEN to launch the activity // into the taskFragment associated with the token. options.putBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN, activityStackToken); @@ -2875,17 +2898,102 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } + @Override + public void setEmbeddedActivityWindowInfoCallback(@NonNull Executor executor, + @NonNull Consumer<EmbeddedActivityWindowInfo> callback) { + if (!Flags.activityWindowInfoFlag()) { + return; + } + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + synchronized (mLock) { + if (mEmbeddedActivityWindowInfoCallback == null) { + ClientTransactionListenerController.getInstance() + .registerActivityWindowInfoChangedListener(getActivityWindowInfoListener()); + } + mEmbeddedActivityWindowInfoCallback = new Pair<>(executor, callback); + } + } + + @Override + public void clearEmbeddedActivityWindowInfoCallback() { + if (!Flags.activityWindowInfoFlag()) { + return; + } + synchronized (mLock) { + if (mEmbeddedActivityWindowInfoCallback == null) { + return; + } + mEmbeddedActivityWindowInfoCallback = null; + ClientTransactionListenerController.getInstance() + .unregisterActivityWindowInfoChangedListener(getActivityWindowInfoListener()); + } + } + + @VisibleForTesting + @GuardedBy("mLock") + @Nullable + BiConsumer<IBinder, ActivityWindowInfo> getActivityWindowInfoListener() { + return mActivityWindowInfoListener; + } + + @Nullable + @Override + public EmbeddedActivityWindowInfo getEmbeddedActivityWindowInfo(@NonNull Activity activity) { + if (!Flags.activityWindowInfoFlag()) { + return null; + } + synchronized (mLock) { + final ActivityWindowInfo activityWindowInfo = getActivityWindowInfo(activity); + return activityWindowInfo != null + ? translateActivityWindowInfo(activity, activityWindowInfo) + : null; + } + } + + @VisibleForTesting + void onActivityWindowInfoChanged(@NonNull IBinder activityToken, + @NonNull ActivityWindowInfo activityWindowInfo) { + synchronized (mLock) { + if (mEmbeddedActivityWindowInfoCallback == null) { + return; + } + final Executor executor = mEmbeddedActivityWindowInfoCallback.first; + final Consumer<EmbeddedActivityWindowInfo> callback = + mEmbeddedActivityWindowInfoCallback.second; + + final Activity activity = getActivity(activityToken); + if (activity == null) { + return; + } + final EmbeddedActivityWindowInfo info = translateActivityWindowInfo( + activity, activityWindowInfo); + + executor.execute(() -> callback.accept(info)); + } + } + @Nullable - private static ActivityWindowInfo getActivityWindowInfo(@NonNull Activity activity) { + private ActivityWindowInfo getActivityWindowInfo(@NonNull Activity activity) { if (activity.isFinishing()) { return null; } - final ActivityThread.ActivityClientRecord record = - ActivityThread.currentActivityThread() - .getActivityClient(activity.getActivityToken()); + final ActivityThread.ActivityClientRecord record = getActivityClientRecord(activity); return record != null ? record.getActivityWindowInfo() : null; } + @NonNull + private static EmbeddedActivityWindowInfo translateActivityWindowInfo( + @NonNull Activity activity, @NonNull ActivityWindowInfo activityWindowInfo) { + final boolean isEmbedded = activityWindowInfo.isEmbedded(); + final Rect activityBounds = new Rect(activity.getResources().getConfiguration() + .windowConfiguration.getBounds()); + final Rect taskBounds = new Rect(activityWindowInfo.getTaskBounds()); + final Rect activityStackBounds = new Rect(activityWindowInfo.getTaskFragmentBounds()); + return new EmbeddedActivityWindowInfo(activity, isEmbedded, activityBounds, taskBounds, + activityStackBounds); + } + /** * If the two rules have the same presentation, and the calculated {@link SplitAttributes} * matches the {@link SplitAttributes} of {@link SplitContainer}, we can reuse the same diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 2f2da8c53db0..b53b9c519cb6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -387,7 +387,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { // Sets the dim area when the two TaskFragments are adjacent. final boolean dimOnTask = !isStacked - && splitAttributes.getWindowAttributes().getDimArea() == DIM_AREA_ON_TASK + && splitAttributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK && Flags.fullscreenDimFlag(); setTaskFragmentDimOnTask(wct, primaryContainer.getTaskFragmentToken(), dimOnTask); setTaskFragmentDimOnTask(wct, secondaryContainer.getTaskFragmentToken(), dimOnTask); @@ -590,7 +590,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { final boolean isFillParent = relativeBounds.isEmpty(); final boolean isIsolatedNavigated = !isFillParent && container.isOverlay(); final boolean dimOnTask = !isFillParent - && attributes.getWindowAttributes().getDimArea() == DIM_AREA_ON_TASK + && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK && Flags.fullscreenDimFlag(); final IBinder fragmentToken = container.getTaskFragmentToken(); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 6fe8e50f105f..a6bf99d4add5 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -367,7 +367,8 @@ class TaskFragmentContainer { if (activities == null) { return null; } - return new ActivityStack(activities, isEmpty(), mToken, mOverlayTag); + return new ActivityStack(activities, isEmpty(), + ActivityStack.Token.createFromBinder(mToken), mOverlayTag); } /** Adds the activity that will be reparented to this container. */ diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java index 34d43ad56bb4..28fbadbebe7f 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java @@ -399,7 +399,8 @@ public class OverlayPresentationTest { new ActivityStackAttributes.Builder().build())); assertThrows(NullPointerException.class, () -> - mSplitController.updateActivityStackAttributes(new Binder(), null)); + mSplitController.updateActivityStackAttributes( + ActivityStack.Token.createFromBinder(new Binder()), null)); verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any()); } @@ -408,7 +409,8 @@ public class OverlayPresentationTest { public void testUpdateActivityStackAttributes_nullContainer_earlyReturn() { final TaskFragmentContainer container = mSplitController.newContainer(mActivity, mActivity.getTaskId()); - mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(), + mSplitController.updateActivityStackAttributes( + ActivityStack.Token.createFromBinder(container.getTaskFragmentToken()), new ActivityStackAttributes.Builder().build()); verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any()); @@ -418,7 +420,8 @@ public class OverlayPresentationTest { public void testUpdateActivityStackAttributes_notOverlay_earlyReturn() { final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity); - mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(), + mSplitController.updateActivityStackAttributes( + ActivityStack.Token.createFromBinder(container.getTaskFragmentToken()), new ActivityStackAttributes.Builder().build()); verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any()); @@ -431,7 +434,8 @@ public class OverlayPresentationTest { final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder().build(); final IBinder token = container.getTaskFragmentToken(); - mSplitController.updateActivityStackAttributes(token, attrs); + mSplitController.updateActivityStackAttributes(ActivityStack.Token.createFromBinder(token), + attrs); verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs), any()); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index b60943a60076..bdeeb7304b12 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -72,6 +72,8 @@ import static org.mockito.Mockito.times; import android.annotation.NonNull; import android.app.Activity; import android.app.ActivityOptions; +import android.app.ActivityThread; +import android.app.servertransaction.ClientTransactionListenerController; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -83,9 +85,11 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArraySet; import android.view.WindowInsets; import android.view.WindowMetrics; +import android.window.ActivityWindowInfo; import android.window.TaskFragmentInfo; import android.window.TaskFragmentOrganizer; import android.window.TaskFragmentParentInfo; @@ -99,7 +103,10 @@ import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.extensions.layout.WindowLayoutComponentImpl; import androidx.window.extensions.layout.WindowLayoutInfo; +import com.android.window.flags.Flags; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -110,6 +117,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.Executor; +import java.util.function.BiConsumer; import java.util.function.Consumer; /** @@ -127,6 +136,9 @@ public class SplitControllerTest { private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent( new ComponentName("test", "placeholder")); + @Rule + public final SetFlagsRule mSetFlagRule = new SetFlagsRule(); + private Activity mActivity; @Mock private Resources mActivityResources; @@ -138,6 +150,13 @@ public class SplitControllerTest { private Handler mHandler; @Mock private WindowLayoutComponentImpl mWindowLayoutComponent; + @Mock + private ActivityWindowInfo mActivityWindowInfo; + @Mock + private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener; + @Mock + private androidx.window.extensions.core.util.function.Consumer<EmbeddedActivityWindowInfo> + mEmbeddedActivityWindowInfoCallback; private SplitController mSplitController; private SplitPresenter mSplitPresenter; @@ -1437,7 +1456,7 @@ public class SplitControllerTest { @Test public void testUpdateSplitAttributes_nullParams_throwException() { assertThrows(NullPointerException.class, - () -> mSplitController.updateSplitAttributes(null, SPLIT_ATTRIBUTES)); + () -> mSplitController.updateSplitAttributes((IBinder) null, SPLIT_ATTRIBUTES)); final SplitContainer splitContainer = mock(SplitContainer.class); final IBinder token = new Binder(); @@ -1529,6 +1548,73 @@ public class SplitControllerTest { .getTopNonFinishingActivity(), secondaryActivity); } + @Test + public void testIsActivityEmbedded() { + mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG); + + assertFalse(mSplitController.isActivityEmbedded(mActivity)); + + doReturn(true).when(mActivityWindowInfo).isEmbedded(); + + assertTrue(mSplitController.isActivityEmbedded(mActivity)); + } + + @Test + public void testGetEmbeddedActivityWindowInfo() { + mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG); + + final boolean isEmbedded = true; + final Rect activityBounds = mActivity.getResources().getConfiguration().windowConfiguration + .getBounds(); + final Rect taskBounds = new Rect(0, 0, 1000, 2000); + final Rect activityStackBounds = new Rect(0, 0, 500, 2000); + doReturn(isEmbedded).when(mActivityWindowInfo).isEmbedded(); + doReturn(taskBounds).when(mActivityWindowInfo).getTaskBounds(); + doReturn(activityStackBounds).when(mActivityWindowInfo).getTaskFragmentBounds(); + + final EmbeddedActivityWindowInfo expected = new EmbeddedActivityWindowInfo(mActivity, + isEmbedded, activityBounds, taskBounds, activityStackBounds); + assertEquals(expected, mSplitController.getEmbeddedActivityWindowInfo(mActivity)); + } + + @Test + public void testSetEmbeddedActivityWindowInfoCallback() { + mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG); + + final ClientTransactionListenerController controller = ClientTransactionListenerController + .getInstance(); + spyOn(controller); + doNothing().when(controller).registerActivityWindowInfoChangedListener(any()); + doReturn(mActivityWindowInfoListener).when(mSplitController) + .getActivityWindowInfoListener(); + final Executor executor = Runnable::run; + + // Register to ClientTransactionListenerController + mSplitController.setEmbeddedActivityWindowInfoCallback(executor, + mEmbeddedActivityWindowInfoCallback); + + verify(controller).registerActivityWindowInfoChangedListener(mActivityWindowInfoListener); + verify(mEmbeddedActivityWindowInfoCallback, never()).accept(any()); + + // Test onActivityWindowInfoChanged triggered. + mSplitController.onActivityWindowInfoChanged(mActivity.getActivityToken(), + mActivityWindowInfo); + + verify(mEmbeddedActivityWindowInfoCallback).accept(any()); + + // Unregister to ClientTransactionListenerController + mSplitController.clearEmbeddedActivityWindowInfoCallback(); + + verify(controller).unregisterActivityWindowInfoChangedListener(mActivityWindowInfoListener); + + // Test onActivityWindowInfoChanged triggered as no-op after clear callback. + clearInvocations(mEmbeddedActivityWindowInfoCallback); + mSplitController.onActivityWindowInfoChanged(mActivity.getActivityToken(), + mActivityWindowInfo); + + verify(mEmbeddedActivityWindowInfoCallback, never()).accept(any()); + } + /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { return createMockActivity(TASK_ID); @@ -1537,13 +1623,17 @@ public class SplitControllerTest { /** Creates a mock activity in the organizer process. */ private Activity createMockActivity(int taskId) { final Activity activity = mock(Activity.class); + final ActivityThread.ActivityClientRecord activityClientRecord = + mock(ActivityThread.ActivityClientRecord.class); doReturn(mActivityResources).when(activity).getResources(); final IBinder activityToken = new Binder(); doReturn(activityToken).when(activity).getActivityToken(); doReturn(activity).when(mSplitController).getActivity(activityToken); + doReturn(activityClientRecord).when(mSplitController).getActivityClientRecord(activity); doReturn(taskId).when(activity).getTaskId(); doReturn(new ActivityInfo()).when(activity).getActivityInfo(); doReturn(DEFAULT_DISPLAY).when(activity).getDisplayId(); + doReturn(mActivityWindowInfo).when(activityClientRecord).getActivityWindowInfo(); return activity; } diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 0ecf1f8f1feb..8829d1b9e0e1 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -212,76 +212,3 @@ android_library { plugins: ["dagger2-compiler"], use_resource_processor: true, } - -android_app { - name: "WindowManagerShellRobolectric", - platform_apis: true, - static_libs: [ - "WindowManager-Shell", - ], - manifest: "multivalentTests/AndroidManifestRobolectric.xml", - use_resource_processor: true, -} - -android_robolectric_test { - name: "WMShellRobolectricTests", - instrumentation_for: "WindowManagerShellRobolectric", - upstream: true, - java_resource_dirs: [ - "multivalentTests/robolectric/config", - ], - srcs: [ - "multivalentTests/src/**/*.kt", - ], - // TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed. - exclude_srcs: ["multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"], - static_libs: [ - "junit", - "androidx.test.runner", - "androidx.test.rules", - "androidx.test.ext.junit", - "mockito-robolectric-prebuilt", - "mockito-kotlin2", - "truth", - ], -} - -android_test { - name: "WMShellMultivalentTestsOnDevice", - srcs: [ - "multivalentTests/src/**/*.kt", - ], - static_libs: [ - "WindowManager-Shell", - "junit", - "androidx.test.runner", - "androidx.test.rules", - "androidx.test.ext.junit", - "frameworks-base-testutils", - "mockito-kotlin2", - "mockito-target-extended-minus-junit4", - "truth", - "platform-test-annotations", - "platform-test-rules", - ], - libs: [ - "android.test.base", - "android.test.runner", - ], - jni_libs: [ - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], - kotlincflags: ["-Xjvm-default=all"], - optimize: { - enabled: false, - }, - test_suites: ["device-tests"], - platform_apis: true, - certificate: "platform", - aaptflags: [ - "--extra-packages", - "com.android.wm.shell", - ], - manifest: "multivalentTests/AndroidManifest.xml", -} diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp new file mode 100644 index 000000000000..1686d0d54dc4 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/Android.bp @@ -0,0 +1,97 @@ +// Copyright (C) 2019 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], + default_team: "trendy_team_multitasking_windowing", +} + +android_app { + name: "WindowManagerShellRobolectric", + platform_apis: true, + static_libs: [ + "WindowManager-Shell", + ], + manifest: "AndroidManifestRobolectric.xml", + use_resource_processor: true, +} + +android_robolectric_test { + name: "WMShellRobolectricTests", + instrumentation_for: "WindowManagerShellRobolectric", + upstream: true, + java_resource_dirs: [ + "robolectric/config", + ], + srcs: [ + "src/**/*.kt", + ], + // TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed. + exclude_srcs: ["src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"], + static_libs: [ + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-robolectric-prebuilt", + "mockito-kotlin2", + "truth", + ], + auto_gen_config: true, +} + +android_test { + name: "WMShellMultivalentTestsOnDevice", + srcs: [ + "src/**/*.kt", + ], + static_libs: [ + "WindowManager-Shell", + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "frameworks-base-testutils", + "mockito-kotlin2", + "mockito-target-extended-minus-junit4", + "truth", + "platform-test-annotations", + "platform-test-rules", + ], + libs: [ + "android.test.base", + "android.test.runner", + ], + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + kotlincflags: ["-Xjvm-default=all"], + optimize: { + enabled: false, + }, + test_suites: ["device-tests"], + platform_apis: true, + certificate: "platform", + aaptflags: [ + "--extra-packages", + "com.android.wm.shell", + ], + manifest: "AndroidManifest.xml", +} diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_close.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_close.xml new file mode 100644 index 000000000000..ff49edb7a699 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_close.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 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. + --> +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#FF000000" + android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/> +</vector> diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml index 490f0883fbfb..a5605a7ff50a 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml @@ -33,14 +33,15 @@ android:orientation="horizontal" android:clickable="true" android:focusable="true" - android:paddingStart="6dp" - android:paddingEnd="8dp"> + android:paddingStart="12dp"> <ImageView android:id="@+id/application_icon" android:layout_width="@dimen/desktop_mode_caption_icon_radius" android:layout_height="@dimen/desktop_mode_caption_icon_radius" android:layout_gravity="center_vertical" - android:contentDescription="@string/app_icon_text" /> + android:contentDescription="@string/app_icon_text" + android:layout_marginStart="6dp" + android:scaleType="centerCrop"/> <TextView android:id="@+id/application_name" @@ -53,8 +54,7 @@ android:lineHeight="20dp" android:layout_gravity="center_vertical" android:layout_weight="1" - android:paddingStart="8dp" - android:paddingEnd="8dp" + android:layout_marginStart="8dp" tools:text="Gmail"/> <ImageButton @@ -67,6 +67,7 @@ android:scaleType="fitCenter" android:clickable="false" android:focusable="false" + android:layout_marginHorizontal="8dp" android:layout_gravity="center_vertical"/> </LinearLayout> @@ -87,14 +88,15 @@ <ImageButton android:id="@+id/close_window" - android:layout_width="40dp" + android:layout_width="44dp" android:layout_height="40dp" - android:padding="4dp" + android:paddingHorizontal="10dp" + android:paddingVertical="8dp" android:layout_marginEnd="8dp" android:tint="?androidprv:attr/materialColorOnSurface" android:background="?android:selectableItemBackgroundBorderless" android:contentDescription="@string/close_button_text" - android:src="@drawable/decor_close_button_dark" - android:scaleType="fitCenter" + android:src="@drawable/desktop_mode_header_ic_close" + android:scaleType="centerCrop" android:gravity="end"/> </com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 8baaf2f155af..a541c590575f 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -145,4 +145,7 @@ <!-- Whether CompatUIController is enabled --> <bool name="config_enableCompatUIController">true</bool> + + <!-- Whether pointer pilfer is required to start back animation. --> + <bool name="config_backAnimationRequiresPointerPilfer">true</bool> </resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 48e6428524ae..7dd39613b438 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -434,15 +434,22 @@ <!-- (32 dp buttons + 10dp margins) * 3 buttons--> <dimen name="caption_right_buttons_width">126dp</dimen> - <!-- 2 buttons * 48dp button size. --> - <dimen name="desktop_mode_right_edge_buttons_width">96dp</dimen> + <!-- 2 buttons * 44dp button size + 16dp total margins. --> + <dimen name="desktop_mode_right_edge_buttons_width">104dp</dimen> <!-- 22dp padding + 24dp app icon + 16dp expand button. Text varies in size, we will calculate that width separately. --> <dimen name="desktop_mode_app_details_width_minus_text">62dp</dimen> - <!-- 22dp padding + 24dp app icon + 16dp expand button + 86dp text (max) --> - <dimen name="desktop_mode_app_details_max_width">148dp</dimen> + <!-- When custom headers are requested, this is the width of the left-aligned region that is + taken up by caption elements and extra margins. The customizable region starts at the + end of this area. --> + <dimen name="desktop_mode_customizable_caption_margin_start">84dp</dimen> + + <!-- When custom headers are requested, this is the width of the right-aligned region that is + taken up by caption elements and extra margins. The customizable region ends at the + start of this area. --> + <dimen name="desktop_mode_customizable_caption_margin_end">152dp</dimen> <!-- The width of the maximize menu in desktop mode. --> <dimen name="desktop_mode_maximize_menu_width">287dp</dimen> @@ -490,7 +497,7 @@ <dimen name="desktop_mode_handle_menu_corner_radius">26dp</dimen> <!-- The radius of the caption menu icon. --> - <dimen name="desktop_mode_caption_icon_radius">28dp</dimen> + <dimen name="desktop_mode_caption_icon_radius">24dp</dimen> <!-- The radius of the caption menu shadow. --> <dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen> @@ -503,10 +510,6 @@ split select if dragged until the touch input is within the range. --> <dimen name="desktop_mode_transition_area_width">32dp</dimen> - <!-- The height of the area at the top of the screen where a freeform task will transition to - fullscreen if dragged until the top bound of the task is within the area. --> - <dimen name="desktop_mode_transition_area_height">16dp</dimen> - <!-- The width of the area where a desktop task will transition to fullscreen. --> <dimen name="desktop_mode_fullscreen_from_desktop_width">80dp</dimen> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java index 93893e33d2d5..ef9bf008b294 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java @@ -51,7 +51,7 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio final ILogger logger = pw::println; switch (args[0]) { case "status": { - if (android.tracing.Flags.perfettoProtolog()) { + if (android.tracing.Flags.perfettoProtologTracing()) { pw.println("(Deprecated) legacy command. Use Perfetto commands instead."); return false; } @@ -59,7 +59,7 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio return true; } case "start": { - if (android.tracing.Flags.perfettoProtolog()) { + if (android.tracing.Flags.perfettoProtologTracing()) { pw.println("(Deprecated) legacy command. Use Perfetto commands instead."); return false; } @@ -67,7 +67,7 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio return true; } case "stop": { - if (android.tracing.Flags.perfettoProtolog()) { + if (android.tracing.Flags.perfettoProtologTracing()) { pw.println("(Deprecated) legacy command. Use Perfetto commands instead."); return false; } @@ -101,7 +101,7 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio return mShellProtoLog.stopLoggingToLogcat(groups, logger) == 0; } case "save-for-bugreport": { - if (android.tracing.Flags.perfettoProtolog()) { + if (android.tracing.Flags.perfettoProtologTracing()) { pw.println("(Deprecated) legacy command"); return false; } 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 2606fb661e80..9bd8531d33dc 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 @@ -64,6 +64,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.LatencyTracker; import com.android.internal.view.AppearanceRegion; +import com.android.wm.shell.R; import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; @@ -115,6 +116,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean mShouldStartOnNextMoveEvent = false; private boolean mOnBackStartDispatched = false; private boolean mPointerPilfered = false; + private final boolean mRequirePointerPilfer; private final FlingAnimationUtils mFlingAnimationUtils; @@ -220,6 +222,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mActivityTaskManager = activityTaskManager; mContext = context; mContentResolver = contentResolver; + mRequirePointerPilfer = + context.getResources().getBoolean(R.bool.config_backAnimationRequiresPointerPilfer); mBgHandler = bgHandler; shellInit.addInitCallback(this::onInit, this); mAnimationBackground = backAnimationBackground; @@ -560,7 +564,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private void tryDispatchOnBackStarted( IOnBackInvokedCallback callback, BackMotionEvent backEvent) { - if (mOnBackStartDispatched || callback == null || !mPointerPilfered) { + if (mOnBackStartDispatched + || callback == null + || (!mPointerPilfered && mRequirePointerPilfer)) { return; } dispatchOnBackStarted(callback, backEvent); @@ -1006,6 +1012,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont pw.println(prefix + " mBackGestureStarted=" + mBackGestureStarted); pw.println(prefix + " mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress); pw.println(prefix + " mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent); + pw.println(prefix + " mPointerPilfered=" + mPointerPilfered); + pw.println(prefix + " mRequirePointerPilfer=" + mRequirePointerPilfer); pw.println(prefix + " mCurrentTracker state:"); mCurrentTracker.dump(pw, prefix + " "); pw.println(prefix + " mQueuedTracker state:"); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 8fd6ffe15cfe..474430eb44ab 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -717,11 +717,6 @@ public class BubbleStackView extends FrameLayout // Hide the stack after a delay, if needed. updateTemporarilyInvisibleAnimation(false /* hideImmediately */); - - if (mShouldReorderBubblesAfterGestureCompletes) { - mShouldReorderBubblesAfterGestureCompletes = false; - updateBubbleOrderInternal(mBubbleData.getBubbles(), true); - } } }; @@ -2732,6 +2727,12 @@ public class BubbleStackView extends FrameLayout ev.getAction() != MotionEvent.ACTION_UP && ev.getAction() != MotionEvent.ACTION_CANCEL; + // If there is a deferred reorder action, and the gesture is over, run it now. + if (mShouldReorderBubblesAfterGestureCompletes && !mIsGestureInProgress) { + mShouldReorderBubblesAfterGestureCompletes = false; + updateBubbleOrderInternal(mBubbleData.getBubbles(), false); + } + return dispatched; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index 2ea43162d225..ad01d0fa311a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -20,12 +20,12 @@ import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_CANCEL; import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_END; import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_START; import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY; -import static android.view.inputmethod.ImeTracker.TOKEN_NONE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.res.Configuration; @@ -51,6 +51,7 @@ import android.view.inputmethod.InputMethodManagerGlobal; import androidx.annotation.VisibleForTesting; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.wm.shell.sysui.ShellInit; import java.util.ArrayList; @@ -122,7 +123,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } if (mDisplayController.getDisplayLayout(displayId).rotation() != pd.mRotation && isImeShowing(displayId)) { - pd.startAnimation(true, false /* forceRestart */, null /* statsToken */); + pd.startAnimation(true, false /* forceRestart */, + SoftInputShowHideReason.DISPLAY_CONFIGURATION_CHANGED); } } @@ -257,7 +259,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mInsetsState.set(insetsState, true /* copySources */); if (mImeShowing && !Objects.equals(oldFrame, newFrame) && newSourceVisible) { if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation"); - startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */); + startAnimation(mImeShowing, true /* forceRestart */, + SoftInputShowHideReason.DISPLAY_INSETS_CHANGED); } } @@ -291,7 +294,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged final boolean positionChanged = !imeSourceControl.getSurfacePosition().equals(lastSurfacePosition); if (positionChanged) { - startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */); + startAnimation(mImeShowing, true /* forceRestart */, + SoftInputShowHideReason.DISPLAY_CONTROLS_CHANGED); } } else { if (!haveSameLeash(mImeSourceControl, imeSourceControl)) { @@ -384,7 +388,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } private void startAnimation(final boolean show, final boolean forceRestart, - @Nullable ImeTracker.Token statsToken) { + @SoftInputShowHideReason int reason) { + final var imeSource = mInsetsState.peekSource(InsetsSource.ID_IME); + if (imeSource == null || mImeSourceControl == null) { + return; + } + final var statsToken = ImeTracker.forLogging().onStart( + show ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_WM_SHELL, + reason, false /* fromUser */); + + startAnimation(show, forceRestart, statsToken); + } + + private void startAnimation(final boolean show, final boolean forceRestart, + @NonNull final ImeTracker.Token statsToken) { final InsetsSource imeSource = mInsetsState.peekSource(InsetsSource.ID_IME); if (imeSource == null || mImeSourceControl == null) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE); @@ -458,7 +475,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE); mAnimation.addListener(new AnimatorListenerAdapter() { private boolean mCancelled = false; - @Nullable + @NonNull private final ImeTracker.Token mStatsToken = statsToken; @Override @@ -484,7 +501,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START, - statsToken != null ? statsToken.getTag() : TOKEN_NONE, + mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, mDisplayId, mAnimationDirection, alpha, startY , endY, Objects.toString(mImeSourceControl.getLeash()), Objects.toString(mImeSourceControl.getInsetsHint()), @@ -500,7 +517,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mCancelled = true; if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_IME_REMOTE_ANIM_CANCEL, - statsToken != null ? statsToken.getTag() : TOKEN_NONE, mDisplayId, + mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, + mDisplayId, Objects.toString(mImeSourceControl.getInsetsHint())); } } @@ -528,7 +546,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END, - statsToken != null ? statsToken.getTag() : TOKEN_NONE, + mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, mDisplayId, mAnimationDirection, endY, Objects.toString(mImeSourceControl.getLeash()), Objects.toString(mImeSourceControl.getInsetsHint()), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java index 9bdda14cf00b..ca06024a9adb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java @@ -277,8 +277,7 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan * * @param types {@link InsetsType} to show * @param fromIme true if this request originated from IME (InputMethodService). - * @param statsToken the token tracking the current IME show request - * or {@code null} otherwise. + * @param statsToken the token tracking the current IME request or {@code null} otherwise. */ default void showInsets(@InsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {} @@ -288,8 +287,7 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan * * @param types {@link InsetsType} to hide * @param fromIme true if this request originated from IME (InputMethodService). - * @param statsToken the token tracking the current IME hide request - * or {@code null} otherwise. + * @param statsToken the token tracking the current IME request or {@code null} otherwise. */ default void hideInsets(@InsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken) {} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index f801b0d01084..a87116ea4670 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -75,7 +75,6 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { private SurfaceControlViewHost mViewHost; private DividerHandleView mHandle; private DividerRoundedCorner mCorners; - private View mBackground; private int mTouchElevation; private VelocityTracker mVelocityTracker; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS index 7237d2bde39f..37ccd15587e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS @@ -1,2 +1,4 @@ # WM shell sub-modules splitscreen owner chenghsiuchang@google.com +jeremysim@google.com +peanutbutter@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java index 2b1037711249..dae62ac74483 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java @@ -138,8 +138,10 @@ public class SplitDecorManager extends WindowlessWindowManager { mViewHost.setView(rootLayout, lp); } - /** Releases the surfaces for split decor. */ - public void release(SurfaceControl.Transaction t) { + /** + * Cancels any currently running animations. + */ + public void cancelRunningAnimations() { if (mFadeAnimator != null) { if (mFadeAnimator.isRunning()) { mFadeAnimator.cancel(); @@ -152,6 +154,11 @@ public class SplitDecorManager extends WindowlessWindowManager { } mScreenshotAnimator = null; } + } + + /** Releases the surfaces for split decor. */ + public void release(SurfaceControl.Transaction t) { + cancelRunningAnimations(); if (mViewHost != null) { mViewHost.release(); mViewHost = null; @@ -277,7 +284,7 @@ public class SplitDecorManager extends WindowlessWindowManager { } @Override - public void onAnimationEnd(@androidx.annotation.NonNull Animator animation) { + public void onAnimationEnd(@NonNull Animator animation) { mRunningAnimationCount--; animT.remove(mScreenshot); animT.apply(); 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 53caddb52f23..6b2d544c192a 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 @@ -510,16 +510,18 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } } - /** Updates divide position and split bounds base on the ratio within root bounds. */ + /** + * Updates divide position and split bounds base on the ratio within root bounds. Falls back + * to middle position if the provided SnapTarget is not supported. + */ public void setDivideRatio(@PersistentSnapPosition int snapPosition) { final DividerSnapAlgorithm.SnapTarget snapTarget = mDividerSnapAlgorithm.findSnapTarget( snapPosition); - if (snapTarget == null) { - throw new IllegalArgumentException("No SnapTarget for position " + snapPosition); - } - - setDividePosition(snapTarget.position, false /* applyLayoutChange */); + setDividePosition(snapTarget != null + ? snapTarget.position + : mDividerSnapAlgorithm.getMiddleTarget().position, + false /* applyLayoutChange */); } /** Resets divider position. */ 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 f757e1c88cb8..fb3c35b6a1e3 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 @@ -201,9 +201,11 @@ public abstract class WMShellModule { @Provides static WindowDecorViewModel provideWindowDecorViewModel( Context context, + @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler, @ShellMainThread Choreographer mainChoreographer, ShellInit shellInit, + IWindowManager windowManager, ShellCommandHandler shellCommandHandler, ShellTaskOrganizer taskOrganizer, DisplayController displayController, @@ -216,10 +218,12 @@ public abstract class WMShellModule { if (DesktopModeStatus.isEnabled()) { return new DesktopModeWindowDecorViewModel( context, + mainExecutor, mainHandler, mainChoreographer, shellInit, shellCommandHandler, + windowManager, taskOrganizer, displayController, shellController, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java index 7091c4b7210a..fb0ed1587055 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java @@ -98,6 +98,7 @@ public class DesktopModeVisualIndicator { * Based on the coordinates of the current drag event, determine which indicator type we should * display, including no visible indicator. */ + @NonNull IndicatorType updateIndicatorType(PointF inputCoordinates, int windowingMode) { final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId); // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone. @@ -136,18 +137,18 @@ public class DesktopModeVisualIndicator { Region calculateFullscreenRegion(DisplayLayout layout, @WindowConfiguration.WindowingMode int windowingMode, int captionHeight) { final Region region = new Region(); - int edgeTransitionHeight = mContext.getResources().getDimensionPixelSize( - com.android.wm.shell.R.dimen.desktop_mode_transition_area_height); + int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM + ? 2 * layout.stableInsets().top + : mContext.getResources().getDimensionPixelSize( + com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height); // A thin, short Rect at the top of the screen. if (windowingMode == WINDOWING_MODE_FREEFORM) { int fromFreeformWidth = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_width); - int fromFreeformHeight = mContext.getResources().getDimensionPixelSize( - com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height); region.union(new Rect((layout.width() / 2) - (fromFreeformWidth / 2), -captionHeight, (layout.width() / 2) + (fromFreeformWidth / 2), - fromFreeformHeight)); + transitionHeight)); } // A screen-wide, shorter Rect if the task is in fullscreen or split. if (windowingMode == WINDOWING_MODE_FULLSCREEN @@ -155,7 +156,7 @@ public class DesktopModeVisualIndicator { region.union(new Rect(0, -captionHeight, layout.width(), - edgeTransitionHeight)); + transitionHeight)); } return region; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index b9d0342137c5..654409f4a637 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -62,7 +62,6 @@ import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.annotations.ExternalThread import com.android.wm.shell.common.annotations.ShellMainThread import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT -import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener import com.android.wm.shell.draganddrop.DragAndDropController @@ -141,7 +140,7 @@ class DesktopTasksController( private val transitionAreaHeight get() = context.resources.getDimensionPixelSize( - com.android.wm.shell.R.dimen.desktop_mode_transition_area_height + com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height ) private val transitionAreaWidth @@ -417,23 +416,9 @@ class DesktopTasksController( splitScreenController.getStageOfTask(taskInfo.taskId), EXIT_REASON_DESKTOP_MODE ) - getOtherSplitTask(taskInfo.taskId)?.let { otherTaskInfo -> - wct.removeTask(otherTaskInfo.token) - } } } - private fun getOtherSplitTask(taskId: Int): RunningTaskInfo? { - val remainingTaskPosition: Int = - if (splitScreenController.getSplitPosition(taskId) - == SPLIT_POSITION_BOTTOM_OR_RIGHT) { - SPLIT_POSITION_TOP_OR_LEFT - } else { - SPLIT_POSITION_BOTTOM_OR_RIGHT - } - return splitScreenController.getTaskInfo(remainingTaskPosition) - } - /** * The second part of the animated drag to desktop transition, called after * [startDragToDesktop]. @@ -580,30 +565,7 @@ class DesktopTasksController( * @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to. */ fun snapToHalfScreen(taskInfo: RunningTaskInfo, position: SnapPosition) { - val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return - - val stableBounds = Rect() - displayLayout.getStableBounds(stableBounds) - - val destinationWidth = stableBounds.width() / 2 - val destinationBounds = when (position) { - SnapPosition.LEFT -> { - Rect( - stableBounds.left, - stableBounds.top, - stableBounds.left + destinationWidth, - stableBounds.bottom - ) - } - SnapPosition.RIGHT -> { - Rect( - stableBounds.right - destinationWidth, - stableBounds.top, - stableBounds.right, - stableBounds.bottom - ) - } - } + val destinationBounds = getSnapBounds(taskInfo, position) if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return @@ -624,8 +586,35 @@ class DesktopTasksController( outBounds.set(0, 0, desiredWidth, desiredHeight) // Center the task in screen bounds outBounds.offset( - screenBounds.centerX() - outBounds.centerX(), - screenBounds.centerY() - outBounds.centerY()) + screenBounds.centerX() - outBounds.centerX(), + screenBounds.centerY() - outBounds.centerY()) + } + + private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect { + val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return Rect() + + val stableBounds = Rect() + displayLayout.getStableBounds(stableBounds) + + val destinationWidth = stableBounds.width() / 2 + return when (position) { + SnapPosition.LEFT -> { + Rect( + stableBounds.left, + stableBounds.top, + stableBounds.left + destinationWidth, + stableBounds.bottom + ) + } + SnapPosition.RIGHT -> { + Rect( + stableBounds.right - destinationWidth, + stableBounds.top, + stableBounds.right, + stableBounds.bottom + ) + } + } } /** @@ -661,7 +650,7 @@ class DesktopTasksController( ?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) } } - private fun releaseVisualIndicator() { + fun releaseVisualIndicator() { val t = SurfaceControl.Transaction() visualIndicator?.releaseVisualIndicator(t) visualIndicator = null @@ -942,16 +931,13 @@ class DesktopTasksController( taskSurface: SurfaceControl, inputX: Float, taskTop: Float - ) { + ): DesktopModeVisualIndicator.IndicatorType { // If the visual indicator does not exist, create it. - if (visualIndicator == null) { - visualIndicator = DesktopModeVisualIndicator( - syncQueue, taskInfo, displayController, context, taskSurface, - rootTaskDisplayAreaOrganizer) - } - // Then, update the indicator type. - val indicator = visualIndicator ?: return - indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode) + val indicator = visualIndicator ?: DesktopModeVisualIndicator( + syncQueue, taskInfo, displayController, context, taskSurface, + rootTaskDisplayAreaOrganizer) + if (visualIndicator == null) visualIndicator = indicator + return indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode) } /** @@ -971,20 +957,28 @@ class DesktopTasksController( if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) { return } - if (taskBounds.top <= transitionAreaHeight) { - moveToFullscreenWithAnimation(taskInfo, position) - return - } - if (inputCoordinate.x <= transitionAreaWidth) { - releaseVisualIndicator() - snapToHalfScreen(taskInfo, SnapPosition.LEFT) - return - } - if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width() - ?.minus(transitionAreaWidth) ?: return)) { - releaseVisualIndicator() - snapToHalfScreen(taskInfo, SnapPosition.RIGHT) - return + + val indicator = visualIndicator ?: return + val indicatorType = indicator.updateIndicatorType( + PointF(inputCoordinate.x, taskBounds.top.toFloat()), + taskInfo.windowingMode + ) + when (indicatorType) { + DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> { + moveToFullscreenWithAnimation(taskInfo, position) + } + DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { + releaseVisualIndicator() + snapToHalfScreen(taskInfo, SnapPosition.LEFT) + } + DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { + releaseVisualIndicator() + snapToHalfScreen(taskInfo, SnapPosition.RIGHT) + } + DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR, + DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR -> { + releaseVisualIndicator() + } } // A freeform drag-move ended, remove the indicator immediately. releaseVisualIndicator() @@ -997,14 +991,28 @@ class DesktopTasksController( * @param y height of drag, to be checked against status bar height. */ fun onDragPositioningEndThroughStatusBar( + inputCoordinates: PointF, taskInfo: RunningTaskInfo, freeformBounds: Rect ) { - finalizeDragToDesktop(taskInfo, freeformBounds) - } - - private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int { - return displayController.getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0 + val indicator = visualIndicator ?: return + val indicatorType = indicator + .updateIndicatorType(inputCoordinates, taskInfo.windowingMode) + when (indicatorType) { + DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> { + finalizeDragToDesktop(taskInfo, freeformBounds) + } + DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR, + DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> { + cancelDragToDesktop(taskInfo) + } + DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { + finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.LEFT)) + } + DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { + finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.RIGHT)) + } + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index c7daf561f682..87e372cc304c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -63,6 +63,7 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.os.RemoteException; import android.os.SystemProperties; +import android.util.Rational; import android.view.Choreographer; import android.view.Display; import android.view.Surface; @@ -126,6 +127,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, SystemProperties.getInt( "persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 400); + private static final float PIP_ASPECT_RATIO_MISMATCH_THRESHOLD = 0.005f; + private final Context mContext; private final SyncTransactionQueue mSyncTransactionQueue; private final PipBoundsState mPipBoundsState; @@ -767,6 +770,37 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPictureInPictureParams.getTitle()); mPipParamsChangedForwarder.notifySubtitleChanged( mPictureInPictureParams.getSubtitle()); + + if (mPictureInPictureParams.hasSourceBoundsHint() + && mPictureInPictureParams.hasSetAspectRatio()) { + Rational sourceRectHintAspectRatio = new Rational( + mPictureInPictureParams.getSourceRectHint().width(), + mPictureInPictureParams.getSourceRectHint().height()); + if (sourceRectHintAspectRatio.compareTo( + mPictureInPictureParams.getAspectRatio()) != 0) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "Aspect ratio of source rect hint (%d/%d) does not match the provided " + + "aspect ratio value (%d/%d). Consider matching them for " + + "improved animation. Future releases might override the " + + "value to match.", + mPictureInPictureParams.getSourceRectHint().width(), + mPictureInPictureParams.getSourceRectHint().height(), + mPictureInPictureParams.getAspectRatio().getNumerator(), + mPictureInPictureParams.getAspectRatio().getDenominator()); + } + if (Math.abs(sourceRectHintAspectRatio.floatValue() + - mPictureInPictureParams.getAspectRatioFloat()) + > PIP_ASPECT_RATIO_MISMATCH_THRESHOLD) { + ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "Aspect ratio of source rect hint (%f) does not match the provided " + + "aspect ratio value (%f) and is above threshold of %f. " + + "Consider matching them for improved animation. Future " + + "releases might override the value to match.", + sourceRectHintAspectRatio.floatValue(), + mPictureInPictureParams.getAspectRatioFloat(), + PIP_ASPECT_RATIO_MISMATCH_THRESHOLD); + } + } } mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo); 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 2cdec81d77ac..4d47ca998d8b 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 @@ -122,6 +122,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb private static final long PIP_KEEP_CLEAR_AREAS_DELAY = SystemProperties.getLong("persist.wm.debug.pip_keep_clear_areas_delay", 200); + private static final long ENABLE_TOUCH_DELAY_MS = 200L; + private Context mContext; protected ShellExecutor mMainExecutor; private DisplayController mDisplayController; @@ -152,6 +154,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb private final Runnable mMovePipInResponseToKeepClearAreasChangeCallback = this::onKeepClearAreasChangedCallback; + private final Runnable mEnableTouchCallback = () -> mTouchHandler.setTouchEnabled(true); + private void onKeepClearAreasChangedCallback() { if (mIsKeyguardShowingOrAnimating) { // early bail out if the change was caused by keyguard showing up @@ -1037,6 +1041,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb saveReentryState(pipBounds); } // Disable touches while the animation is running + mMainExecutor.removeCallbacks(mEnableTouchCallback); mTouchHandler.setTouchEnabled(false); if (mPinnedStackAnimationRecentsCallback != null) { mPinnedStackAnimationRecentsCallback.onPipAnimationStarted(); @@ -1067,7 +1072,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb InteractionJankMonitor.getInstance().end(CUJ_PIP_TRANSITION); // Re-enable touches after the animation completes - mTouchHandler.setTouchEnabled(true); + mMainExecutor.executeDelayed(mEnableTouchCallback, ENABLE_TOUCH_DELAY_MS); mTouchHandler.onPinnedStackAnimationEnded(direction); } 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 e4213569b526..1c54754e9953 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 @@ -163,14 +163,14 @@ public class RecentTasksController implements TaskStackListenerCallback, /** * Adds a split pair. This call does not validate the taskIds, only that they are not the same. */ - public void addSplitPair(int taskId1, int taskId2, SplitBounds splitBounds) { + public boolean addSplitPair(int taskId1, int taskId2, SplitBounds splitBounds) { if (taskId1 == taskId2) { - return; + return false; } if (mSplitTasks.get(taskId1, INVALID_TASK_ID) == taskId2 && mTaskSplitBoundsMap.get(taskId1).equals(splitBounds)) { // If the two tasks are already paired and the bounds are the same, then skip updating - return; + return false; } // Remove any previous pairs removeSplitPair(taskId1); @@ -185,6 +185,7 @@ public class RecentTasksController implements TaskStackListenerCallback, notifyRecentTasksChanged(); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Add split pair: %d, %d, %s", taskId1, taskId2, splitBounds); + return true; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java index e52235fda80f..64e26dbd70be 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java @@ -16,11 +16,14 @@ package com.android.wm.shell.splitscreen; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; + import android.content.Context; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; @@ -50,6 +53,8 @@ class MainStage extends StageTaskListener { void activate(WindowContainerTransaction wct, boolean includingTopTask) { if (mIsActive) return; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: main stage includingTopTask=%b", + includingTopTask); if (includingTopTask) { reparentTopTask(wct); @@ -64,6 +69,8 @@ class MainStage extends StageTaskListener { void deactivate(WindowContainerTransaction wct, boolean toTop) { if (!mIsActive) return; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: main stage toTop=%b rootTaskInfo=%s", + toTop, mRootTaskInfo); mIsActive = false; if (mRootTaskInfo == null) return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS index 7237d2bde39f..37ccd15587e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OWNERS @@ -1,2 +1,4 @@ # WM shell sub-modules splitscreen owner chenghsiuchang@google.com +jeremysim@google.com +peanutbutter@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java index 9903113c5453..f5fbae55960a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java @@ -16,12 +16,15 @@ package com.android.wm.shell.splitscreen; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; + import android.app.ActivityManager; import android.content.Context; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; @@ -47,6 +50,8 @@ class SideStage extends StageTaskListener { } boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b", + mChildrenTaskInfo.size(), toTop); if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, @@ -59,6 +64,8 @@ class SideStage extends StageTaskListener { boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) { final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId, + task != null); if (task == null) return false; wct.reparent(task.token, newParent, false /* onTop */); return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index b60e361caad8..1a53a1d10dd2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -25,6 +25,8 @@ import static com.android.wm.shell.animation.Interpolators.ALPHA_IN; import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT; import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION; import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS; import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString; @@ -101,6 +103,7 @@ class SplitScreenTransitions { @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playAnimation: transition=%d", info.getDebugId()); initTransition(transition, finishTransaction, finishCallback); final TransitSession pendingTransition = getPendingTransition(transition); @@ -123,10 +126,12 @@ class SplitScreenTransitions { playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot, topRoot); } - /** Internal funcation of playAnimation. */ + /** Internal function of playAnimation. */ private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playInternalAnimation: transition=%d", + info.getDebugId()); // Play some place-holder fade animations final boolean isEnter = isPendingEnter(transition); for (int i = info.getChanges().size() - 1; i >= 0; --i) { @@ -220,6 +225,8 @@ class SplitScreenTransitions { @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull WindowContainerToken toTopRoot, @NonNull SplitDecorManager toTopDecor, @NonNull WindowContainerToken topRoot) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playDragDismissAnimation: transition=%d", + info.getDebugId()); initTransition(transition, finishTransaction, finishCallback); for (int i = info.getChanges().size() - 1; i >= 0; --i) { @@ -259,6 +266,7 @@ class SplitScreenTransitions { @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot, @NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playResizeAnimation: transition=%d", info.getDebugId()); initTransition(transition, finishTransaction, finishCallback); for (int i = info.getChanges().size() - 1; i >= 0; --i) { @@ -312,13 +320,15 @@ class SplitScreenTransitions { @Nullable private TransitSession getPendingTransition(IBinder transition) { if (isPendingEnter(transition)) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved enter transition"); return mPendingEnter; } else if (isPendingDismiss(transition)) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved dismiss transition"); return mPendingDismiss; } else if (isPendingResize(transition)) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved resize transition"); return mPendingResize; } - return null; } @@ -339,7 +349,7 @@ class SplitScreenTransitions { Transitions.TransitionHandler handler, int extraTransitType, boolean resizeAnim) { if (mPendingEnter != null) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition " + " skip to start enter split transition since it already exist. "); return null; } @@ -355,8 +365,10 @@ class SplitScreenTransitions { mPendingEnter = new EnterSession( transition, remoteTransition, extraTransitType, resizeAnim); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition " + " deduced Enter split screen"); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setEnterTransition: transitType=%d resize=%b", + extraTransitType, resizeAnim); } /** Starts a transition to dismiss split. */ @@ -364,7 +376,7 @@ class SplitScreenTransitions { Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop, @SplitScreenController.ExitReason int reason) { if (mPendingDismiss != null) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition " + " skip to start dismiss split transition since it already exist. reason to " + " dismiss = %s", exitReasonToString(reason)); return null; @@ -381,32 +393,33 @@ class SplitScreenTransitions { @SplitScreenController.ExitReason int reason) { mPendingDismiss = new DismissSession(transition, reason, dismissTop); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition " + " deduced Dismiss due to %s. toTop=%s", exitReasonToString(reason), stageTypeToString(dismissTop)); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setDismissTransition: reason=%s dismissTop=%s", + exitReasonToString(reason), stageTypeToString(dismissTop)); } IBinder startResizeTransition(WindowContainerTransaction wct, Transitions.TransitionHandler handler, @Nullable TransitionConsumedCallback consumedCallback, - @Nullable TransitionFinishedCallback finishCallback) { + @Nullable TransitionFinishedCallback finishCallback, + @NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + " splitTransition deduced Resize split screen."); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setResizeTransition: hasPendingResize=%b", + mPendingResize != null); if (mPendingResize != null) { + mainDecor.cancelRunningAnimations(); + sideDecor.cancelRunningAnimations(); mPendingResize.cancel(null); mAnimations.clear(); onFinish(null /* wct */); } IBinder transition = mTransitions.startTransition(TRANSIT_CHANGE, wct, handler); - setResizeTransition(transition, consumedCallback, finishCallback); - return transition; - } - - void setResizeTransition(@NonNull IBinder transition, - @Nullable TransitionConsumedCallback consumedCallback, - @Nullable TransitionFinishedCallback finishCallback) { mPendingResize = new TransitSession(transition, consumedCallback, finishCallback); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " - + " deduced Resize split screen"); + return transition; } void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, @@ -444,12 +457,15 @@ class SplitScreenTransitions { mPendingEnter.onConsumed(aborted); mPendingEnter = null; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for enter transition"); } else if (isPendingDismiss(transition)) { mPendingDismiss.onConsumed(aborted); mPendingDismiss = null; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for dismiss transition"); } else if (isPendingResize(transition)) { mPendingResize.onConsumed(aborted); mPendingResize = null; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for resize transition"); } // TODO: handle transition consumed for active remote handler @@ -462,12 +478,15 @@ class SplitScreenTransitions { if (isPendingEnter(mAnimatingTransition)) { mPendingEnter.onFinished(wct, mFinishTransaction); mPendingEnter = null; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for enter transition"); } else if (isPendingDismiss(mAnimatingTransition)) { mPendingDismiss.onFinished(wct, mFinishTransaction); mPendingDismiss = null; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for dismiss transition"); } else if (isPendingResize(mAnimatingTransition)) { mPendingResize.onFinished(wct, mFinishTransaction); mPendingResize = null; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for resize transition"); } mActiveRemoteHandler = null; 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 2933cf48614a..76504447339f 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 @@ -44,6 +44,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT import static com.android.wm.shell.common.split.SplitScreenConstants.splitPositionToString; import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition; import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.shared.TransitionUtil.isClosingType; import static com.android.wm.shell.shared.TransitionUtil.isOpeningType; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; @@ -54,6 +55,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASO import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP; +import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT; @@ -138,6 +140,7 @@ import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.shared.TransitionUtil; import com.android.wm.shell.splitscreen.SplitScreen.StageType; import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason; +import com.android.wm.shell.splitscreen.SplitScreenController.SplitEnterReason; import com.android.wm.shell.transition.DefaultMixedHandler; import com.android.wm.shell.transition.LegacyTransitions; import com.android.wm.shell.transition.Transitions; @@ -313,6 +316,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task"); mMainStage = new MainStage( mContext, mTaskOrganizer, @@ -454,6 +458,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition, WindowContainerTransaction wct) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveToStage: task=%d position=%d", task.taskId, + stagePosition); prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */); if (ENABLE_SHELL_TRANSITIONS) { mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, @@ -474,6 +480,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } boolean removeFromSideStage(int taskId) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "removeFromSideStage: task=%d", taskId); final WindowContainerTransaction wct = new WindowContainerTransaction(); /** @@ -498,11 +505,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, enteredSplitSelect |= listener.onRequestEnterSplitSelect(taskInfo, splitPosition, taskBounds); } - if (enteredSplitSelect) mTaskOrganizer.applyTransaction(wct); + if (enteredSplitSelect) { + mTaskOrganizer.applyTransaction(wct); + } } void startShortcut(String packageName, String shortcutId, @SplitPosition int position, Bundle options, UserHandle user) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startShortcut: pkg=%s id=%s position=%d user=%d", + packageName, shortcutId, position, user.getIdentifier()); final boolean isEnteringSplit = !isSplitActive(); IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() { @@ -564,6 +575,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Use this method to launch an existing Task via a taskId */ void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startTask: task=%d position=%d", taskId, position); mSplitRequest = new SplitRequest(taskId, position); final WindowContainerTransaction wct = new WindowContainerTransaction(); options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); @@ -595,6 +607,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Launches an activity into split. */ void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startIntent: intent=%s position=%d", intent.getIntent(), + position); mSplitRequest = new SplitRequest(intent.getIntent(), position); if (!ENABLE_SHELL_TRANSITIONS) { startIntentLegacy(intent, fillInIntent, position, options); @@ -690,6 +704,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "startTasks: task1=%d task2=%d position=%d snapPosition=%d", + taskId1, taskId2, splitPosition, snapPosition); final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId2 == INVALID_TASK_ID) { if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) { @@ -718,6 +735,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "startIntentAndTask: intent=%s task1=%d position=%d snapPosition=%d", + pendingIntent.getIntent(), taskId, splitPosition, snapPosition); final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId == INVALID_TASK_ID) { options1 = options1 != null ? options1 : new Bundle(); @@ -740,6 +760,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "startShortcutAndTask: shortcut=%s task1=%d position=%d snapPosition=%d", + shortcutInfo, taskId, splitPosition, snapPosition); final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId == INVALID_TASK_ID) { options1 = options1 != null ? options1 : new Bundle(); @@ -801,6 +824,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "startIntents: intent1=%s intent2=%s position=%d snapPosition=%d", + pendingIntent1.getIntent(), pendingIntent2.getIntent(), splitPosition, + snapPosition); final WindowContainerTransaction wct = new WindowContainerTransaction(); if (pendingIntent2 == null) { options1 = options1 != null ? options1 : new Bundle(); @@ -1302,6 +1329,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } void switchSplitPosition(String reason) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "switchSplitPosition"); final SurfaceControl.Transaction t = mTransactionPool.acquire(); mTempRect1.setEmpty(); final StageTaskListener topLeftStage = @@ -1343,7 +1371,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, }); }); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason); + ProtoLog.v(WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason); mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(), getSideStagePosition(), mSideStage.getTopChildTaskUid(), mSplitLayout.isLeftRightSplit()); @@ -1376,11 +1404,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!mMainStage.isActive()) { return; } - + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onKeyguardVisibilityChanged: showing=%b", showing); setDividerVisibility(!mKeyguardShowing, null); } void onFinishedWakingUp() { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinishedWakingUp"); if (!mMainStage.isActive()) { return; } @@ -1421,6 +1450,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: topTaskId=%d reason=%s active=%b", + toTopTaskId, exitReasonToString(exitReason), mMainStage.isActive()); if (!mMainStage.isActive()) return; StageTaskListener childrenToTop = null; @@ -1439,6 +1470,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void exitSplitScreen(@Nullable StageTaskListener childrenToTop, @ExitReason int exitReason) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: mainStageToTop=%b reason=%s active=%b", + childrenToTop == mMainStage, exitReasonToString(exitReason), mMainStage.isActive()); if (!mMainStage.isActive()) return; final WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -1447,6 +1480,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void applyExitSplitScreen(@Nullable StageTaskListener childrenToTop, WindowContainerTransaction wct, @ExitReason int exitReason) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "applyExitSplitScreen: reason=%s", + exitReasonToString(exitReason)); if (!mMainStage.isActive() || mIsExiting) return; onSplitScreenExit(); @@ -1502,7 +1537,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } }); - Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason)); // Log the exit if (childrenToTop != null) { logExitToStage(exitReason, childrenToTop == mMainStage); @@ -1527,6 +1561,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * Exits the split screen by finishing one of the tasks. */ protected void exitStage(@SplitPosition int stageToClose) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitStage: stageToClose=%d", stageToClose); mSplitLayout.flingDividerToDismiss(stageToClose == SPLIT_POSITION_BOTTOM_OR_RIGHT, EXIT_REASON_APP_FINISHED); } @@ -1540,12 +1575,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, try { activityTaskManagerService.setFocusedTask(getTaskId(stageToFocus)); } catch (RemoteException | NullPointerException e) { - ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + ProtoLog.e(WM_SHELL_SPLIT_SCREEN, "Unable to update focus on the chosen stage: %s", e.getMessage()); } } private void clearRequestIfPresented() { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearRequestIfPresented"); if (mSideStageListener.mVisible && mSideStageListener.mHasChildren && mMainStageListener.mVisible && mSideStageListener.mHasChildren) { mSplitRequest = null; @@ -1572,6 +1608,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // The device is folded case EXIT_REASON_FULLSCREEN_SHORTCUT: // User has used a keyboard shortcut to go back to fullscreen from split + case EXIT_REASON_DESKTOP_MODE: + // One of the children enters desktop mode return true; default: return false; @@ -1581,6 +1619,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, void clearSplitPairedInRecents(@ExitReason int exitReason) { if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) return; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearSplitPairedInRecents: reason=%s", + exitReasonToString(exitReason)); mRecentTasks.ifPresent(recentTasks -> { // Notify recents if we are exiting in a way that breaks the pair, and disable further // updates to splits in the recents until we enter split again @@ -1597,11 +1637,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, void prepareExitSplitScreen(@StageType int stageToTop, @NonNull WindowContainerTransaction wct) { if (!mMainStage.isActive()) return; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop); mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN); } private void prepareEnterSplitScreen(WindowContainerTransaction wct) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen"); prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED, !mIsDropEntering); } @@ -1613,6 +1655,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, void prepareEnterSplitScreen(WindowContainerTransaction wct, @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition, boolean resizeAnim) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen: position=%d resize=%b", + startPosition, resizeAnim); onSplitScreenEnter(); // Preemptively reset the reparenting behavior if we know that we are entering, as starting // split tasks with activity trampolines can inadvertently trigger the task to be @@ -1629,6 +1673,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void prepareBringSplit(WindowContainerTransaction wct, @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition, boolean resizeAnim) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareBringSplit: task=%d isSplitVisible=%b", + taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible()); if (taskInfo != null) { wct.startTask(taskInfo.taskId, resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct)); @@ -1649,6 +1695,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void prepareActiveSplit(WindowContainerTransaction wct, @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition, boolean resizeAnim) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareActiveSplit: task=%d isSplitVisible=%b", + taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible()); if (!ENABLE_SHELL_TRANSITIONS) { // Legacy transition we need to create divider here, shell transition case we will // create it on #finishEnterSplitScreen @@ -1667,6 +1715,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } private void prepareSplitLayout(WindowContainerTransaction wct, boolean resizeAnim) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareSplitLayout: resize=%b", resizeAnim); if (resizeAnim) { mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); } else { @@ -1686,6 +1735,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } void finishEnterSplitScreen(SurfaceControl.Transaction finishT) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "finishEnterSplitScreen"); mSplitLayout.update(finishT, true /* resetImePosition */); mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash, getMainStageBounds()); @@ -1835,12 +1885,20 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition()); if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) { // Update the pair for the top tasks - recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds); + boolean added = recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, + splitBounds); + if (added) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "updateRecentTasksSplitPair: adding split pair ltTask=%d rbTask=%d", + leftTopTaskId, rightBottomTaskId); + } } }); } private void sendSplitVisibilityChanged() { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "sendSplitVisibilityChanged: dividerVisible=%b", + mDividerVisible); for (int i = mListeners.size() - 1; i >= 0; --i) { final SplitScreen.SplitScreenListener l = mListeners.get(i); l.onSplitVisibilityChanged(mDividerVisible); @@ -1855,6 +1913,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, throw new IllegalArgumentException(this + "\n Unknown task appeared: " + taskInfo); } + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%s", taskInfo); mRootTaskInfo = taskInfo; mRootTaskLeash = leash; @@ -1880,6 +1939,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitLayout != null && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration) && mMainStage.isActive()) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: task=%d updating", + taskInfo.taskId); // Clear the divider remote animating flag as the divider will be re-rendered to apply // the new rotation config. Don't reset the IME state since those updates are not in // sync with task info changes. @@ -1892,6 +1953,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override @CallSuper public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%s", taskInfo); if (mRootTaskInfo == null) { throw new IllegalArgumentException(this + "\n Unknown task vanished: " + taskInfo); } @@ -1911,6 +1973,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @VisibleForTesting void onRootTaskAppeared() { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b", + mRootTaskInfo, mMainStageListener.mHasRootTask, mSideStageListener.mHasRootTask); // Wait unit all root tasks appeared. if (mRootTaskInfo == null || !mMainStageListener.mHasRootTask @@ -1937,6 +2001,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * #onStageHasChildrenChanged because this would be called every time child task appeared. * NOTICE: This only be called on legacy transition. */ private void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onChildTaskAppeared: isMainStage=%b task=%d", + stageListener == mMainStageListener, taskId); // Handle entering split screen while there is a split pair running in the background. if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive() && mSplitRequest == null) { @@ -1960,6 +2026,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } private void onRootTaskVanished() { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskVanished"); final WindowContainerTransaction wct = new WindowContainerTransaction(); mLaunchAdjacentController.clearLaunchAdjacentRoot(); applyExitSplitScreen(null /* childrenToTop */, wct, EXIT_REASON_ROOT_TASK_VANISHED); @@ -1990,6 +2057,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } + // TODO Protolog + // Check if it needs to dismiss split screen when both stage invisible. if (!mainStageVisible && mExitSplitScreenOnHide) { exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RETURN_HOME); @@ -2020,14 +2089,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, - "Request to %s divider bar from %s.", - (visible ? "show" : "hide"), Debug.getCaller()); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "setDividerVisibility: visible=%b keyguardShowing=%b dividerAnimating=%b caller=%s", + visible, mKeyguardShowing, mIsDividerRemoteAnimating, Debug.getCaller()); // Defer showing divider bar after keyguard dismissed, so it won't interfere with keyguard // dismissing animation. if (visible && mKeyguardShowing) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, " Defer showing divider bar due to keyguard showing."); return; } @@ -2036,7 +2105,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, sendSplitVisibilityChanged(); if (mIsDividerRemoteAnimating) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, " Skip animating divider bar due to it's remote animating."); return; } @@ -2050,12 +2119,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void applyDividerVisibility(@Nullable SurfaceControl.Transaction t) { final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); if (dividerLeash == null) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, " Skip animating divider bar due to divider leash not ready."); return; } if (mIsDividerRemoteAnimating) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, " Skip animating divider bar due to it's remote animating."); return; } @@ -2119,6 +2188,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Callback when split roots have child or haven't under it. * NOTICE: This only be called on legacy transition. */ private void onStageHasChildrenChanged(StageListenerImpl stageListener) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onStageHasChildrenChanged: isMainStage=%b", + stageListener == mMainStageListener); final boolean hasChildren = stageListener.mHasChildren; final boolean isSideStage = stageListener == mSideStageListener; if (!hasChildren && !mIsExiting && mMainStage.isActive()) { @@ -2170,13 +2241,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } @Override - public void onSnappedToDismiss(boolean bottomOrRight, int reason) { + public void onSnappedToDismiss(boolean bottomOrRight, @ExitReason int exitReason) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onSnappedToDismiss: bottomOrRight=%b reason=%s", + bottomOrRight, exitReasonToString(exitReason)); final boolean mainStageToTop = bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT; final StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage; if (!ENABLE_SHELL_TRANSITIONS) { - exitSplitScreen(toTopStage, reason); + exitSplitScreen(toTopStage, exitReason); return; } @@ -2219,6 +2292,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onLayoutSizeChanged(SplitLayout layout) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onLayoutSizeChanged"); // Reset this flag every time onLayoutSizeChanged. mShowDecorImmediately = false; @@ -2237,10 +2311,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (ENABLE_SHELL_TRANSITIONS) { mSplitLayout.setDividerInteractive(false, false, "onSplitResizeStart"); mSplitTransitions.startResizeTransition(wct, this, (aborted) -> { - mSplitLayout.setDividerInteractive(true, false, "onSplitResizeConsumed"); - }, (finishWct, t) -> { - mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish"); - }); + mSplitLayout.setDividerInteractive(true, false, "onSplitResizeConsumed"); + }, (finishWct, t) -> { + mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish"); + }, mMainStage.getSplitDecorManager(), mSideStage.getSplitDecorManager()); } else { // Only need screenshot for legacy case because shell transition should screenshot // itself during transition. @@ -2278,8 +2352,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - return layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, + boolean updated = layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "updateWindowBounds: topLeftStage=%s bottomRightStage=%s", + layout.getBounds1(), layout.getBounds2()); + return updated; } void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t, @@ -2291,6 +2368,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer, applyResizingOffset); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "updateSurfaceBounds: topLeftStage=%s bottomRightStage=%s", + layout.getBounds1(), layout.getBounds2()); } @Override @@ -2329,6 +2409,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setLayoutOffsetTarget: x=%d y=%d", + offsetX, offsetY); final StageTaskListener topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = @@ -2343,6 +2425,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (displayId != DEFAULT_DISPLAY) { return; } + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onDisplayAdded: display=%d", displayId); mDisplayController.addDisplayChangingController(this::onDisplayChange); } @@ -2357,8 +2440,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onDisplayChange(int displayId, int fromRotation, int toRotation, @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) { - if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) return; + if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) { + return; + } + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "onDisplayChange: display=%d fromRot=%d toRot=%d config=%s", + displayId, fromRotation, toRotation, + newDisplayAreaInfo != null ? newDisplayAreaInfo.configuration : null); mSplitLayout.rotateTo(toRotation); if (newDisplayAreaInfo != null) { mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration); @@ -2369,6 +2458,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @VisibleForTesting void onFoldedStateChanged(boolean folded) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFoldedStateChanged: folded=%b", folded); mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; if (!folded) return; @@ -2439,6 +2529,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); if (triggerTask == null) { if (isSplitActive()) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d display rotation", + request.getDebugId()); // Check if the display is rotating. final TransitionRequestInfo.DisplayChange displayChange = request.getDisplayChange(); @@ -2467,6 +2559,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } if (isSplitActive()) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d split active", + request.getDebugId()); // Try to handle everything while in split-screen, so return a WCT even if it's empty. ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split" + "Transition to handle request. triggerTask=%d type=%s mainChildren=%d" @@ -2541,6 +2635,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return null; } else { if (isOpening && getStageOfTask(triggerTask) != null) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d enter split", + request.getDebugId()); // One task is appearing into split, prepare to enter split screen. out = new WindowContainerTransaction(); prepareEnterSplitScreen(out); @@ -2557,6 +2653,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ public void addEnterOrExitIfNeeded(@Nullable TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "addEnterOrExitIfNeeded: transition=%d", + request.getDebugId()); final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); if (triggerTask != null && triggerTask.displayId != mDisplayId) { // Skip handling task on the other display. @@ -2591,6 +2689,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, public void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "mergeAnimation: transition=%d", info.getDebugId()); mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback); } @@ -2602,6 +2701,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, @Nullable SurfaceControl.Transaction finishT) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed"); mSplitTransitions.onTransitionConsumed(transition, aborted, finishT); } @@ -2617,6 +2717,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // If we're not in split-mode, just abort so something else can handle it. if (!mMainStage.isActive()) return false; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startAnimation: transition=%d", info.getDebugId()); mSplitLayout.setFreezeDividerWindow(false); final StageChangeRecord record = new StageChangeRecord(); final int transitType = info.getType(); @@ -2727,6 +2828,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info, startTransaction, finishTransaction, finishCallback)) { if (mSplitTransitions.isPendingResize(transition)) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "startAnimation: transition=%d display change", info.getDebugId()); // Only need to update in resize because divider exist before transition. mSplitLayout.update(startTransaction, true /* resetImePosition */); startTransaction.apply(); @@ -2797,6 +2900,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startPendingAnimation: transition=%d", + info.getDebugId()); boolean shouldAnimate = true; if (mSplitTransitions.isPendingEnter(transition)) { shouldAnimate = startPendingEnterAnimation(transition, @@ -2830,6 +2935,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Called to clean-up state and do house-keeping after the animation is done. */ public void onTransitionAnimationComplete() { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionAnimationComplete"); // If still playing, let it finish. if (!mMainStage.isActive() && !mIsExiting) { // Update divider state after animation so that it is still around and positioned @@ -2842,6 +2948,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @NonNull SplitScreenTransitions.EnterSession enterTransition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startPendingEnterAnimation: enterTransition=%s", + enterTransition); // First, verify that we actually have opened apps in both splits. TransitionInfo.Change mainChild = null; TransitionInfo.Change sideChild = null; @@ -2959,6 +3067,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } public void goToFullscreenFromSplit() { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "goToFullscreenFromSplit"); // If main stage is focused, toEnd = true if // mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT. Otherwise toEnd = false // If side stage is focused, toEnd = true if @@ -2974,6 +3083,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Move the specified task to fullscreen, regardless of focus state. */ public void moveTaskToFullscreen(int taskId, int exitReason) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveTaskToFullscreen"); boolean leftOrTop; if (mMainStage.containsTask(taskId)) { leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); @@ -2994,6 +3104,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ public void onPipExpandToSplit(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo taskInfo) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onPipExpandToSplit: task=%s", taskInfo); prepareEnterSplitScreen(wct, taskInfo, getActivateSplitPosition(taskInfo), false /*resizeAnim*/); @@ -3040,6 +3151,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "prepareDismissAnimation: transition=%d toStage=%d reason=%s", + info.getDebugId(), toStage, exitReasonToString(dismissReason)); // Make some noise if things aren't totally expected. These states shouldn't effect // transitions locally, but remotes (like Launcher) may get confused if they were // depending on listener callbacks. This can happen because task-organizer callbacks @@ -3126,6 +3240,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @NonNull SplitScreenTransitions.DismissSession dismissTransition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "startPendingDismissAnimation: transition=%d dismissTransition=%s", + info.getDebugId(), dismissTransition); prepareDismissAnimation(dismissTransition.mDismissTop, dismissTransition.mReason, info, t, finishT); if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) { @@ -3146,6 +3263,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Call this when starting the open-recents animation while split-screen is active. */ public void onRecentsInSplitAnimationStart(TransitionInfo info) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationStart: transition=%d", + info.getDebugId()); if (isSplitScreenVisible()) { // Cache tasks on live tile. for (int i = 0; i < info.getChanges().size(); ++i) { @@ -3178,6 +3297,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Call this when the recents animation during split-screen finishes. */ public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationFinish"); mPausingTasks.clear(); // Check if the recent transition is finished by returning to the current // split, so we can restore the divider bar. @@ -3203,6 +3323,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Call this when the recents animation finishes by doing pair-to-pair switch. */ public void onRecentsPairToPairAnimationFinish(WindowContainerTransaction finishWct) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsPairToPairAnimationFinish"); // Pair-to-pair switch happened so here should evict the live tile from its stage. // Otherwise, the task will remain in stage, and occluding the new task when next time // user entering recents. @@ -3284,6 +3405,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * handled. */ private void setSplitsVisible(boolean visible) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setSplitsVisible: visible=%b", visible); mMainStageListener.mVisible = mSideStageListener.mVisible = visible; mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible; } @@ -3292,6 +3414,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * Sets drag info to be logged when splitscreen is next entered. */ public void onDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onDroppedToSplit: position=%d", position); if (!isSplitScreenVisible()) { mIsDropEntering = true; mSkipEvictingMainStageChildren = true; @@ -3308,7 +3431,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** * Sets info to be logged when splitscreen is next entered. */ - public void onRequestToSplit(InstanceId sessionId, int enterReason) { + public void onRequestToSplit(InstanceId sessionId, @SplitEnterReason int enterReason) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRequestToSplit: reason=%d", enterReason); if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) { // If split running background, exit split first. // Skip this on shell transition due to we could evict existing tasks on transition @@ -3384,6 +3508,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo); if (mMainStage.isActive()) { final boolean isMainStage = mMainStageListener == this; if (!ENABLE_SHELL_TRANSITIONS) { @@ -3393,7 +3518,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } - final int stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; + // If visible, we preserve the app and keep it running. If an app becomes + // unsupported in the bg, break split without putting anything on top + boolean splitScreenVisible = isSplitScreenVisible(); + int stageType = STAGE_TYPE_UNDEFINED; + if (splitScreenVisible) { + stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; + } final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(stageType, wct); clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); @@ -3402,7 +3533,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow", "app package " + taskInfo.baseActivity.getPackageName() + " does not support splitscreen, or is a controlled activity type")); - mSplitUnsupportedToast.show(); + if (splitScreenVisible) { + mSplitUnsupportedToast.show(); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index af7bf360f036..f33ab33dafcc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -25,6 +25,7 @@ import static android.view.RemoteAnimationTarget.MODE_OPENING; import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES; import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES; import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import android.annotation.CallSuper; @@ -44,6 +45,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; @@ -175,6 +177,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { @Override @CallSuper public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%d taskParent=%d rootTask=%d", + taskInfo.taskId, taskInfo.parentTaskId, + mRootTaskInfo != null ? mRootTaskInfo.taskId : -1); if (mRootTaskInfo == null) { mRootLeash = leash; mRootTaskInfo = taskInfo; @@ -225,6 +230,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { || !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType()) || !ArrayUtils.contains(CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, taskInfo.getWindowingMode())) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "onTaskInfoChanged: task=%d no longer supports multiwindow", + taskInfo.taskId); // Leave split screen if the task no longer supports multi window or have // uncontrolled task. mCallbacks.onNoLongerSupportMultiWindow(taskInfo); @@ -251,6 +259,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { @Override @CallSuper public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%d", taskInfo.taskId); final int taskId = taskInfo.taskId; if (mRootTaskInfo.taskId == taskId) { mCallbacks.onRootTaskVanished(); @@ -333,6 +342,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "addTask: task=%d", task.taskId); // Clear overridden bounds and windowing mode to make sure the child task can inherit // windowing mode and bounds from split root. wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED) @@ -342,6 +352,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "reorderChild: task=%d onTop=%b", taskId, onTop); if (!containsTask(taskId)) { return; } @@ -357,6 +368,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { /** Collects all the current child tasks and prepares transaction to evict them to display. */ void evictAllChildren(WindowContainerTransaction wct) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evicting all children"); for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) { final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i); wct.reparent(taskInfo.token, null /* parent */, false /* onTop */); @@ -367,11 +379,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) { final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i); if (taskId == taskInfo.taskId) continue; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict other child: task=%d", taskId); wct.reparent(taskInfo.token, null /* parent */, false /* onTop */); } } void evictNonOpeningChildren(RemoteAnimationTarget[] apps, WindowContainerTransaction wct) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "evictNonOpeningChildren"); final SparseArray<ActivityManager.RunningTaskInfo> toBeEvict = mChildrenTaskInfo.clone(); for (int i = 0; i < apps.length; i++) { if (apps[i].mode == MODE_OPENING) { @@ -380,6 +394,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } for (int i = toBeEvict.size() - 1; i >= 0; i--) { final ActivityManager.RunningTaskInfo taskInfo = toBeEvict.valueAt(i); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict non-opening child: task=%d", taskInfo.taskId); wct.reparent(taskInfo.token, null /* parent */, false /* onTop */); } } @@ -388,12 +403,15 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) { final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i); if (!taskInfo.isVisible) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict invisible child: task=%d", + taskInfo.taskId); wct.reparent(taskInfo.token, null /* parent */, false /* onTop */); } } } void evictChildren(WindowContainerTransaction wct, int taskId) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict child: task=%d", taskId); final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.get(taskId); if (taskInfo != null) { wct.reparent(taskInfo.token, null /* parent */, false /* onTop */); 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 bfb60c0f4fc8..da2965c05ee4 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 @@ -417,7 +417,7 @@ public class SplashscreenContentDrawer { final SplashViewBuilder builder = new SplashViewBuilder(context, ai); final SplashScreenView view = builder .setWindowBGColor(themeBGColor) - .chooseStyle(STARTING_WINDOW_TYPE_SPLASH_SCREEN) + .chooseStyle(STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN) .build(); view.setNotCopyable(); return view; 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 96eaa1edbae4..91e9601c6a27 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 @@ -190,6 +190,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL mRelayoutParams.mShadowRadiusId = shadowRadiusID; mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; mRelayoutParams.mSetTaskPositionAndCrop = setTaskCropAndPosition; + mRelayoutParams.mAllowCaptionInputFallthrough = false; relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index caa894fcbbc7..f4ccd689f938 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -30,6 +30,10 @@ import static android.view.WindowInsets.Type.statusBars; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; +import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR; +import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR; +import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; +import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR; import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION; import static com.android.wm.shell.windowdecor.MoveToDesktopAnimator.DRAG_FREEFORM_SCALE; @@ -48,9 +52,13 @@ import android.graphics.Region; import android.hardware.input.InputManager; import android.os.Handler; import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; import android.util.SparseArray; import android.view.Choreographer; import android.view.GestureDetector; +import android.view.ISystemGestureExclusionListener; +import android.view.IWindowManager; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventReceiver; @@ -73,9 +81,11 @@ import com.android.wm.shell.ShellTaskOrganizer; 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.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.desktopmode.DesktopModeStatus; +import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; @@ -88,6 +98,7 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener; +import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import java.io.PrintWriter; import java.util.Optional; @@ -102,6 +113,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private static final String TAG = "DesktopModeWindowDecorViewModel"; private final DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory; + private final IWindowManager mWindowManager; + private final ShellExecutor mMainExecutor; private final ActivityTaskManager mActivityTaskManager; private final ShellCommandHandler mShellCommandHandler; private final ShellTaskOrganizer mTaskOrganizer; @@ -111,7 +124,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final Choreographer mMainChoreographer; private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; - private final Optional<DesktopTasksController> mDesktopTasksController; + private final DesktopTasksController mDesktopTasksController; + private final InputManager mInputManager; private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); @@ -119,8 +133,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final ExclusionRegionListener mExclusionRegionListener = new ExclusionRegionListenerImpl(); - private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId = - new SparseArray<>(); + private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId; private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl(); private final InputMonitorFactory mInputMonitorFactory; private TaskOperations mTaskOperations; @@ -135,14 +148,31 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { new DesktopModeKeyguardChangeListener(); private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; private final DisplayInsetsController mDisplayInsetsController; + private final Region mExclusionRegion = Region.obtain(); private boolean mInImmersiveMode; + private final ISystemGestureExclusionListener mGestureExclusionListener = + new ISystemGestureExclusionListener.Stub() { + @Override + public void onSystemGestureExclusionChanged(int displayId, + Region systemGestureExclusion, Region systemGestureExclusionUnrestricted) { + if (mContext.getDisplayId() != displayId) { + return; + } + mMainExecutor.execute(() -> { + mExclusionRegion.set(systemGestureExclusion); + }); + } + }; + public DesktopModeWindowDecorViewModel( Context context, + ShellExecutor shellExecutor, Handler mainHandler, Choreographer mainChoreographer, ShellInit shellInit, ShellCommandHandler shellCommandHandler, + IWindowManager windowManager, ShellTaskOrganizer taskOrganizer, DisplayController displayController, ShellController shellController, @@ -154,10 +184,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { ) { this( context, + shellExecutor, mainHandler, mainChoreographer, shellInit, shellCommandHandler, + windowManager, taskOrganizer, displayController, shellController, @@ -168,16 +200,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory(), SurfaceControl.Transaction::new, - rootTaskDisplayAreaOrganizer); + rootTaskDisplayAreaOrganizer, + new SparseArray<>()); } @VisibleForTesting DesktopModeWindowDecorViewModel( Context context, + ShellExecutor shellExecutor, Handler mainHandler, Choreographer mainChoreographer, ShellInit shellInit, ShellCommandHandler shellCommandHandler, + IWindowManager windowManager, ShellTaskOrganizer taskOrganizer, DisplayController displayController, ShellController shellController, @@ -188,8 +223,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory, Supplier<SurfaceControl.Transaction> transactionFactory, - RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId) { mContext = context; + mMainExecutor = shellExecutor; mMainHandler = mainHandler; mMainChoreographer = mainChoreographer; mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class); @@ -199,12 +236,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDisplayInsetsController = displayInsetsController; mSyncQueue = syncQueue; mTransitions = transitions; - mDesktopTasksController = desktopTasksController; + mDesktopTasksController = desktopTasksController.get(); mShellCommandHandler = shellCommandHandler; + mWindowManager = windowManager; mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory; mInputMonitorFactory = inputMonitorFactory; mTransactionFactory = transactionFactory; mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; + mInputManager = mContext.getSystemService(InputManager.class); + mWindowDecorByTaskId = windowDecorByTaskId; shellInit.addInitCallback(this::onInit, this); } @@ -214,8 +254,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mShellCommandHandler.addDumpCallback(this::dump, this); mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(), new DesktopModeOnInsetsChangedListener()); - mDesktopTasksController.ifPresent(c -> c.setOnTaskResizeAnimationListener( - new DeskopModeOnTaskResizeAnimationListener())); + mDesktopTasksController.setOnTaskResizeAnimationListener( + new DeskopModeOnTaskResizeAnimationListener()); + try { + mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener, + mContext.getDisplayId()); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register window manager callbacks", e); + } } @Override @@ -233,7 +279,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId); if (decor != null && DesktopModeStatus.isEnabled() && decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { - mDesktopTasksController.ifPresent(c -> c.moveToSplit(decor.mTaskInfo)); + mDesktopTasksController.moveToSplit(decor.mTaskInfo); } } } @@ -300,8 +346,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Override public void destroyWindowDecoration(RunningTaskInfo taskInfo) { - final DesktopModeWindowDecoration decoration = - mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId); + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId); if (decoration == null) return; decoration.close(); @@ -309,18 +354,29 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { if (mEventReceiversByDisplay.contains(displayId)) { removeTaskFromEventReceiver(displayId); } + // Remove the decoration from the cache last because WindowDecoration#close could still + // issue CANCEL MotionEvents to touch listeners before its view host is released. + // See b/327664694. + mWindowDecorByTaskId.remove(taskInfo.taskId); } private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener, View.OnGenericMotionListener , DragDetector.MotionEventHandler { private static final int CLOSE_MAXIMIZE_MENU_DELAY_MS = 150; + private final int mTaskId; private final WindowContainerToken mTaskToken; private final DragPositioningCallback mDragPositioningCallback; private final DragDetector mDragDetector; private final GestureDetector mGestureDetector; + /** + * Whether to pilfer the next motion event to send cancellations to the windows below. + * Useful when the caption window is spy and the gesture should be handle by the system + * instead of by the app for their custom header content. + */ + private boolean mShouldPilferCaptionEvents; private boolean mIsDragging; private boolean mTouchscreenInUse; private boolean mHasLongClicked; @@ -367,14 +423,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { decoration.closeHandleMenu(); } } else if (id == R.id.desktop_button) { - if (mDesktopTasksController.isPresent()) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // App sometimes draws before the insets from WindowDecoration#relayout have - // been added, so they must be added here - mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct); - mDesktopTasksController.get().moveToDesktop(mTaskId, wct); - closeOtherSplitTask(mTaskId); - } + final WindowContainerTransaction wct = new WindowContainerTransaction(); + // App sometimes draws before the insets from WindowDecoration#relayout have + // been added, so they must be added here + mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct); + mDesktopTasksController.moveToDesktop(mTaskId, wct); decoration.closeHandleMenu(); } else if (id == R.id.fullscreen_button) { decoration.closeHandleMenu(); @@ -382,42 +435,37 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mSplitScreenController.moveTaskToFullscreen(mTaskId, SplitScreenController.EXIT_REASON_DESKTOP_MODE); } else { - mDesktopTasksController.ifPresent(c -> - c.moveToFullscreen(mTaskId)); + mDesktopTasksController.moveToFullscreen(mTaskId); } } else if (id == R.id.split_screen_button) { decoration.closeHandleMenu(); - mDesktopTasksController.ifPresent(c -> { - c.requestSplit(decoration.mTaskInfo); - }); + mDesktopTasksController.requestSplit(decoration.mTaskInfo); } else if (id == R.id.collapse_menu_button) { decoration.closeHandleMenu(); } else if (id == R.id.select_button) { if (DesktopModeStatus.IS_DISPLAY_CHANGE_ENABLED) { // TODO(b/278084491): dev option to enable display switching // remove when select is implemented - mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId)); + mDesktopTasksController.moveToNextDisplay(mTaskId); } } else if (id == R.id.maximize_window) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); - mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo)); + mDesktopTasksController.toggleDesktopTaskSize(taskInfo); } else if (id == R.id.maximize_menu_maximize_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; - mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo)); + mDesktopTasksController.toggleDesktopTaskSize(taskInfo); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } else if (id == R.id.maximize_menu_snap_left_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; - mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen( - taskInfo, SnapPosition.LEFT)); + mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.LEFT); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } else if (id == R.id.maximize_menu_snap_right_button) { final RunningTaskInfo taskInfo = decoration.mTaskInfo; - mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen( - taskInfo, SnapPosition.RIGHT)); + mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.RIGHT); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } @@ -438,6 +486,40 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); moveTaskToFront(decoration.mTaskInfo); + final int actionMasked = e.getActionMasked(); + final boolean isDown = actionMasked == MotionEvent.ACTION_DOWN; + final boolean isUpOrCancel = actionMasked == MotionEvent.ACTION_CANCEL + || actionMasked == MotionEvent.ACTION_UP; + if (isDown) { + final boolean downInCustomizableCaptionRegion = + decoration.checkTouchEventInCustomizableRegion(e); + final boolean downInExclusionRegion = mExclusionRegion.contains( + (int) e.getRawX(), (int) e.getRawY()); + final boolean isTransparentCaption = + TaskInfoKt.isTransparentCaptionBarAppearance(decoration.mTaskInfo); + // The caption window may be a spy window when the caption background is + // transparent, which means events will fall through to the app window. Make + // sure to cancel these events if they do not happen in the intersection of the + // customizable region and what the app reported as exclusion areas, because + // the drag-move or other caption gestures should take priority outside those + // regions. + mShouldPilferCaptionEvents = !(downInCustomizableCaptionRegion + && downInExclusionRegion && isTransparentCaption); + } + + if (!mShouldPilferCaptionEvents) { + // The event will be handled by a window below. + return false; + } + // Otherwise pilfer so that windows below receive cancellations for this gesture, and + // continue normal handling as a caption gesture. + if (mInputManager != null) { + mInputManager.pilferPointers(v.getViewRootImpl().getInputToken()); + } + if (isUpOrCancel) { + // Gesture is finished, reset state. + mShouldPilferCaptionEvents = false; + } if (!mHasLongClicked && id != R.id.maximize_window) { decoration.closeMaximizeMenuIfNeeded(e); } @@ -478,8 +560,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } else if (ev.getAction() == ACTION_HOVER_EXIT) { if (!decoration.isMaximizeMenuActive() && id == R.id.maximize_window) { decoration.onMaximizeWindowHoverExit(); - } else if (id == R.id.maximize_window - || MaximizeMenu.Companion.isMaximizeMenuView(id)) { + } else if (id == R.id.maximize_window || id == R.id.maximize_menu) { // Close menu if not hovering over maximize menu or maximize button after a // delay to give user a chance to re-enter view or to move from one maximize // menu view to another. @@ -493,7 +574,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private void moveTaskToFront(RunningTaskInfo taskInfo) { if (!taskInfo.isFocused) { - mDesktopTasksController.ifPresent(c -> c.moveTaskToFront(taskInfo)); + mDesktopTasksController.moveTaskToFront(taskInfo); } } @@ -537,10 +618,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final int dragPointerIdx = e.findPointerIndex(mDragPointerId); final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningMove( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); - mDesktopTasksController.ifPresent(c -> c.onDragPositioningMove(taskInfo, + mDesktopTasksController.onDragPositioningMove(taskInfo, decoration.mTaskSurface, e.getRawX(dragPointerIdx), - newTaskBounds)); + newTaskBounds); mIsDragging = true; return true; } @@ -562,10 +643,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { (int) (e.getRawY(dragPointerIdx) - e.getY(dragPointerIdx))); final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); - mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo, - position, + mDesktopTasksController.onDragPositioningEnd(taskInfo, position, new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)), - newTaskBounds)); + newTaskBounds); if (touchingButton && !mHasLongClicked) { // We need the input event to not be consumed here to end the ripple // effect on the touched button. We will reset drag state in the ensuing @@ -593,10 +673,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && action != MotionEvent.ACTION_CANCEL)) { return false; } - mDesktopTasksController.ifPresent(c -> { - final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); - c.toggleDesktopTaskSize(decoration.mTaskInfo); - }); + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); + mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo); return true; } } @@ -760,20 +838,29 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return; } if (mTransitionDragActive) { + final DesktopModeVisualIndicator.IndicatorType indicatorType = + mDesktopTasksController.updateVisualIndicator(relevantDecor.mTaskInfo, + relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()); mTransitionDragActive = false; - final int statusBarHeight = getStatusBarHeight( - relevantDecor.mTaskInfo.displayId); - if (ev.getRawY() > 2 * statusBarHeight) { + if (indicatorType == TO_DESKTOP_INDICATOR + || indicatorType == TO_SPLIT_LEFT_INDICATOR + || indicatorType == TO_SPLIT_RIGHT_INDICATOR) { if (DesktopModeStatus.isEnabled()) { animateToDesktop(relevantDecor, ev); } mMoveToDesktopAnimator = null; return; } else if (mMoveToDesktopAnimator != null) { - mDesktopTasksController.ifPresent( - c -> c.cancelDragToDesktop(relevantDecor.mTaskInfo)); + mDesktopTasksController.onDragPositioningEndThroughStatusBar( + new PointF(ev.getRawX(), ev.getRawY()), + relevantDecor.mTaskInfo, + calculateFreeformBounds(ev.getDisplayId(), DRAG_FREEFORM_SCALE)); mMoveToDesktopAnimator = null; return; + } else { + // In cases where we create an indicator but do not start the + // move-to-desktop animation, we need to dismiss it. + mDesktopTasksController.releaseVisualIndicator(); } } relevantDecor.checkClickEvent(ev); @@ -785,20 +872,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return; } if (mTransitionDragActive) { - mDesktopTasksController.ifPresent( - c -> c.updateVisualIndicator( + final DesktopModeVisualIndicator.IndicatorType indicatorType = + mDesktopTasksController.updateVisualIndicator( relevantDecor.mTaskInfo, - relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY())); - final int statusBarHeight = getStatusBarHeight( - relevantDecor.mTaskInfo.displayId); - if (ev.getRawY() > statusBarHeight) { + relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()); + if (indicatorType != TO_FULLSCREEN_INDICATOR) { if (mMoveToDesktopAnimator == null) { mMoveToDesktopAnimator = new MoveToDesktopAnimator( mContext, mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo, relevantDecor.mTaskSurface); - mDesktopTasksController.ifPresent( - c -> c.startDragToDesktop(relevantDecor.mTaskInfo, - mMoveToDesktopAnimator)); + mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo, + mMoveToDesktopAnimator); } } if (mMoveToDesktopAnimator != null) { @@ -844,6 +928,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * Animates a window to the center, grows to freeform size, and transitions to Desktop Mode. * @param relevantDecor the window decor of the task to be animated * @param ev the motion event that triggers the animation + * TODO(b/315527000): This animation needs to be adjusted to allow snap left/right cases. + * Currently fullscreen -> split snap still animates to center screen before readjusting. */ private void centerAndMoveToDesktopWithAnimation(DesktopModeWindowDecoration relevantDecor, MotionEvent ev) { @@ -867,13 +953,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - mDesktopTasksController.ifPresent( - c -> { - c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo, - calculateFreeformBounds(ev.getDisplayId(), - DesktopTasksController - .DESKTOP_MODE_INITIAL_BOUNDS_SCALE)); - }); + mDesktopTasksController.onDragPositioningEndThroughStatusBar( + new PointF(ev.getRawX(), ev.getRawY()), + relevantDecor.mTaskInfo, + calculateFreeformBounds(ev.getDisplayId(), + DesktopTasksController + .DESKTOP_MODE_INITIAL_BOUNDS_SCALE)); } }); animator.start(); @@ -1003,7 +1088,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final DragPositioningCallback dragPositioningCallback; final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.desktop_mode_transition_area_height); + R.dimen.desktop_mode_fullscreen_from_desktop_height); if (!DesktopModeStatus.isVeiledResizeEnabled()) { dragPositioningCallback = new FluidResizeTaskPositioner( mTaskOrganizer, mTransitions, windowDecoration, mDisplayController, @@ -1038,12 +1123,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return mSplitScreenController.getTaskInfo(remainingTaskPosition); } - private void closeOtherSplitTask(int taskId) { - if (isTaskInSplitScreen(taskId)) { - mTaskOperations.closeTask(getOtherSplitTask(taskId).token); - } - } - private boolean isTaskInSplitScreen(int taskId) { return mSplitScreenController != null && mSplitScreenController.isTaskInSplitScreen(taskId); @@ -1108,12 +1187,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Override public void onExclusionRegionChanged(int taskId, Region region) { - mDesktopTasksController.ifPresent(d -> d.onExclusionRegionChanged(taskId, region)); + mDesktopTasksController.onExclusionRegionChanged(taskId, region); } @Override public void onExclusionRegionDismissed(int taskId) { - mDesktopTasksController.ifPresent(d -> d.removeExclusionRegionForTask(taskId)); + mDesktopTasksController.removeExclusionRegionForTask(taskId); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 74f460bf1226..39803e2afd34 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -56,6 +56,7 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; +import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder; import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder; import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder; @@ -317,24 +318,23 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode()); relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId); - // The "app controls" type caption bar should report the occluding elements as bounding - // rects to the insets system so that apps can draw in the empty space left in the center. - if (captionLayoutId == R.layout.desktop_mode_app_controls_window_decor) { - // The "app chip" section of the caption bar, it's aligned to the left and its width - // varies depending on the length of the app name, but we'll report its max width for - // now. - // TODO(b/316387515): consider reporting the true width after it's been laid out. + if (captionLayoutId == R.layout.desktop_mode_app_controls_window_decor + && TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) { + // App is requesting to customize the caption bar. Allow input to fall through to the + // windows below so that the app can respond to input events on their custom content. + relayoutParams.mAllowCaptionInputFallthrough = true; + // Report occluding elements as bounding rects to the insets system so that apps can + // draw in the empty space in the center: + // First, the "app chip" section of the caption bar (+ some extra margins). final RelayoutParams.OccludingCaptionElement appChipElement = new RelayoutParams.OccludingCaptionElement(); - appChipElement.mWidthResId = R.dimen.desktop_mode_app_details_max_width; + appChipElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_start; appChipElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.START; relayoutParams.mOccludingCaptionElements.add(appChipElement); - // The "controls" section of the caption bar (maximize, close btns). These are aligned - // to the right of the caption bar and have a fixed width. - // TODO(b/316387515): add additional padding for an exclusive drag-move region. + // Then, the right-aligned section (drag space, maximize and close buttons). final RelayoutParams.OccludingCaptionElement controlsElement = new RelayoutParams.OccludingCaptionElement(); - controlsElement.mWidthResId = R.dimen.desktop_mode_right_edge_buttons_width; + controlsElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_end; controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END; relayoutParams.mOccludingCaptionElements.add(controlsElement); } @@ -699,6 +699,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } /** + * Checks whether the touch event falls inside the customizable caption region. + */ + boolean checkTouchEventInCustomizableRegion(MotionEvent ev) { + return mResult.mCustomizableCaptionRegion.contains((int) ev.getRawX(), (int) ev.getRawY()); + } + + /** * Check a passed MotionEvent if a click has occurred on any button on this caption * Note this should only be called when a regular onClick is not possible * (i.e. the button was clicked through status bar layer) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index dc65855646ea..32c2d1e9b257 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -31,6 +31,7 @@ import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.os.Binder; import android.view.Display; import android.view.InsetsSource; @@ -311,6 +312,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> if (numOfElements == 0) { boundingRects = null; } else { + // The customizable region can at most be equal to the caption bar. + if (params.mAllowCaptionInputFallthrough) { + outResult.mCustomizableCaptionRegion.set(mCaptionInsetsRect); + } boundingRects = new Rect[numOfElements]; for (int i = 0; i < numOfElements; i++) { final OccludingCaptionElement element = @@ -319,9 +324,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> resources.getDimensionPixelSize(element.mWidthResId); boundingRects[i] = calculateBoundingRect(element, elementWidthPx, mCaptionInsetsRect); + // Subtract the regions used by the caption elements, the rest is + // customizable. + if (params.mAllowCaptionInputFallthrough) { + outResult.mCustomizableCaptionRegion.op(boundingRects[i], + Region.Op.DIFFERENCE); + } } } - // Add this caption as an inset source. wct.addInsetsSource(mTaskInfo.token, mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect, @@ -389,6 +399,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); lp.setTitle("Caption of Task=" + mTaskInfo.taskId); lp.setTrustedOverlay(); + if (params.mAllowCaptionInputFallthrough) { + lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY; + } else { + lp.inputFeatures &= ~WindowManager.LayoutParams.INPUT_FEATURE_SPY; + } if (mViewHost == null) { mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay, mCaptionWindowManager); @@ -596,6 +611,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> int mCaptionHeightId; int mCaptionWidthId; final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>(); + boolean mAllowCaptionInputFallthrough; int mShadowRadiusId; int mCornerRadius; @@ -610,6 +626,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mCaptionHeightId = Resources.ID_NULL; mCaptionWidthId = Resources.ID_NULL; mOccludingCaptionElements.clear(); + mAllowCaptionInputFallthrough = false; mShadowRadiusId = Resources.ID_NULL; mCornerRadius = 0; @@ -637,6 +654,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> int mCaptionHeight; int mCaptionWidth; int mCaptionX; + final Region mCustomizableCaptionRegion = Region.obtain(); int mWidth; int mHeight; T mRootView; @@ -647,6 +665,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mCaptionHeight = 0; mCaptionWidth = 0; mCaptionX = 0; + mCustomizableCaptionRegion.setEmpty(); mRootView = null; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt new file mode 100644 index 000000000000..5dd96aceaec7 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 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.windowdecor.extension + +import android.app.TaskInfo +import android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS +import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND + +val TaskInfo.isTransparentCaptionBarAppearance: Boolean + get() { + val appearance = taskDescription?.statusBarAppearance ?: 0 + return (appearance and APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) != 0 + } + +val TaskInfo.isLightCaptionBarAppearance: Boolean + get() { + val appearance = taskDescription?.statusBarAppearance ?: 0 + return (appearance and APPEARANCE_LIGHT_CAPTION_BARS) != 0 + } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt index 7e5b9bd649f2..58bbb030da01 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt @@ -8,12 +8,11 @@ import android.graphics.Bitmap import android.graphics.Color import android.view.View import android.view.View.OnLongClickListener -import android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS -import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView import androidx.core.content.withStyledAttributes +import androidx.core.view.isVisible import com.android.internal.R.attr.materialColorOnSecondaryContainer import com.android.internal.R.attr.materialColorOnSurface import com.android.internal.R.attr.materialColorSecondaryContainer @@ -22,6 +21,8 @@ import com.android.internal.R.attr.materialColorSurfaceContainerLow import com.android.internal.R.attr.materialColorSurfaceDim import com.android.wm.shell.R import com.android.wm.shell.windowdecor.MaximizeButtonView +import com.android.wm.shell.windowdecor.extension.isLightCaptionBarAppearance +import com.android.wm.shell.windowdecor.extension.isTransparentCaptionBarAppearance /** * A desktop mode window decoration used when the window is floating (i.e. freeform). It hosts @@ -76,6 +77,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( closeWindowButton.imageTintList = ColorStateList.valueOf(color) maximizeWindowButton.imageTintList = ColorStateList.valueOf(color) expandMenuButton.imageTintList = ColorStateList.valueOf(color) + appNameTextView.isVisible = !taskInfo.isTransparentCaptionBarAppearance appNameTextView.setTextColor(color) appIconImageView.imageAlpha = alpha maximizeWindowButton.imageAlpha = alpha @@ -107,7 +109,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( @ColorInt private fun getCaptionBackgroundColor(taskInfo: RunningTaskInfo): Int { - if (isTransparentBackgroundRequested(taskInfo)) { + if (taskInfo.isTransparentCaptionBarAppearance) { return Color.TRANSPARENT } val materialColorAttr: Int = @@ -133,10 +135,10 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( @ColorInt private fun getAppNameAndButtonColor(taskInfo: RunningTaskInfo): Int { val materialColorAttr = when { - isTransparentBackgroundRequested(taskInfo) && - isLightCaptionBar(taskInfo) -> materialColorOnSecondaryContainer - isTransparentBackgroundRequested(taskInfo) && - !isLightCaptionBar(taskInfo) -> materialColorOnSurface + taskInfo.isTransparentCaptionBarAppearance && + taskInfo.isLightCaptionBarAppearance -> materialColorOnSecondaryContainer + taskInfo.isTransparentCaptionBarAppearance && + !taskInfo.isLightCaptionBarAppearance -> materialColorOnSurface isDarkMode() -> materialColorOnSurface else -> materialColorOnSecondaryContainer } @@ -167,16 +169,6 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( Configuration.UI_MODE_NIGHT_YES } - private fun isTransparentBackgroundRequested(taskInfo: RunningTaskInfo): Boolean { - val appearance = taskInfo.taskDescription?.statusBarAppearance ?: 0 - return (appearance and APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) != 0 - } - - private fun isLightCaptionBar(taskInfo: RunningTaskInfo): Boolean { - val appearance = taskInfo.taskDescription?.statusBarAppearance ?: 0 - return (appearance and APPEARANCE_LIGHT_CAPTION_BARS) != 0 - } - companion object { private const val TAG = "DesktopModeAppControlsWindowDecorationViewHolder" private const val DARK_THEME_UNFOCUSED_OPACITY = 140 // 55% diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java index 01e2f988fbfc..2c0aa12f22d2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java @@ -38,6 +38,7 @@ import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; +import android.view.inputmethod.ImeTracker; import androidx.test.filters.SmallTest; @@ -51,6 +52,12 @@ import org.mockito.MockitoAnnotations; import java.util.concurrent.Executor; +/** + * Tests for the display IME controller. + * + * <p> Build/Install/Run: + * atest WMShellUnitTests:DisplayImeControllerTest + */ @SmallTest public class DisplayImeControllerTest extends ShellTestCase { @@ -99,13 +106,13 @@ public class DisplayImeControllerTest extends ShellTestCase { @Test public void showInsets_schedulesNoWorkOnExecutor() { - mPerDisplay.showInsets(ime(), true /* fromIme */, null /* statsToken */); + mPerDisplay.showInsets(ime(), true /* fromIme */, ImeTracker.Token.empty()); verifyZeroInteractions(mExecutor); } @Test public void hideInsets_schedulesNoWorkOnExecutor() { - mPerDisplay.hideInsets(ime(), true /* fromIme */, null /* statsToken */); + mPerDisplay.hideInsets(ime(), true /* fromIme */, ImeTracker.Token.empty()); verifyZeroInteractions(mExecutor); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java index 956f1cd419c2..669e433ba386 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java @@ -50,6 +50,12 @@ import org.mockito.MockitoAnnotations; import java.util.List; +/** + * Tests for the display insets controller. + * + * <p> Build/Install/Run: + * atest WMShellUnitTests:DisplayInsetsControllerTest + */ @SmallTest public class DisplayInsetsControllerTest extends ShellTestCase { @@ -114,9 +120,9 @@ public class DisplayInsetsControllerTest extends ShellTestCase { mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsChanged(null); mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsControlChanged(null, null); mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).showInsets(0, false, - null /* statsToken */); + ImeTracker.Token.empty()); mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).hideInsets(0, false, - null /* statsToken */); + ImeTracker.Token.empty()); mExecutor.flushAll(); assertTrue(defaultListener.topFocusedWindowChangedCount == 1); @@ -136,9 +142,9 @@ public class DisplayInsetsControllerTest extends ShellTestCase { mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsChanged(null); mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsControlChanged(null, null); mInsetsControllersByDisplayId.get(SECOND_DISPLAY).showInsets(0, false, - null /* statsToken */); + ImeTracker.Token.empty()); mInsetsControllersByDisplayId.get(SECOND_DISPLAY).hideInsets(0, false, - null /* statsToken */); + ImeTracker.Token.empty()); mExecutor.flushAll(); assertTrue(defaultListener.topFocusedWindowChangedCount == 1); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt index f8ce4ee8e1ce..9703dce8bf53 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt @@ -56,18 +56,16 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() { context, taskSurface, taskDisplayAreaOrganizer) whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width()) whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height()) + whenever(displayLayout.stableInsets()).thenReturn(STABLE_INSETS) } @Test fun testFullscreenRegionCalculation() { val transitionHeight = context.resources.getDimensionPixelSize( - R.dimen.desktop_mode_transition_area_height) + R.dimen.desktop_mode_fullscreen_from_desktop_height) val fromFreeformWidth = mContext.resources.getDimensionPixelSize( R.dimen.desktop_mode_fullscreen_from_desktop_width ) - val fromFreeformHeight = mContext.resources.getDimensionPixelSize( - R.dimen.desktop_mode_fullscreen_from_desktop_height - ) var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, WINDOWING_MODE_FULLSCREEN, CAPTION_HEIGHT) assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight)) @@ -77,7 +75,7 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() { DISPLAY_BOUNDS.width() / 2 - fromFreeformWidth / 2, -50, DISPLAY_BOUNDS.width() / 2 + fromFreeformWidth / 2, - fromFreeformHeight)) + 2 * STABLE_INSETS.top)) testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, WINDOWING_MODE_MULTI_WINDOW, CAPTION_HEIGHT) assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight)) @@ -135,5 +133,12 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() { private const val TRANSITION_AREA_WIDTH = 32 private const val CAPTION_HEIGHT = 50 private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600) + private const val NAVBAR_HEIGHT = 50 + private val STABLE_INSETS = Rect( + DISPLAY_BOUNDS.left, + DISPLAY_BOUNDS.top + CAPTION_HEIGHT, + DISPLAY_BOUNDS.right, + DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT + ) } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index d4e9ac96d221..e74c804d4f40 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -36,7 +36,6 @@ import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.os.RemoteException; -import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Rational; @@ -45,6 +44,8 @@ import android.view.DisplayInfo; import android.view.SurfaceControl; import android.window.WindowContainerToken; +import androidx.test.filters.SmallTest; + import com.android.wm.shell.MockSurfaceControlHelper; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; 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 5d968d3360ab..3384509f1da9 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 @@ -42,10 +42,11 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.Bundle; import android.os.RemoteException; -import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.filters.SmallTest; + import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java index 45f6c8c7f69f..72db6e091307 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java @@ -23,20 +23,21 @@ import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_EXPAND_COLLAPSE; import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_FULLSCREEN; import static com.android.wm.shell.pip.tv.TvPipAction.ACTION_MOVE; -import static java.util.Collections.EMPTY_LIST; - import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static java.util.Collections.EMPTY_LIST; + import android.app.PendingIntent; import android.app.RemoteAction; import android.graphics.drawable.Icon; -import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.filters.SmallTest; + import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.pip.PipMediaController; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java index 050443914355..fbc0db9c2850 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java @@ -32,7 +32,6 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.graphics.Rect; import android.os.IBinder; -import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.SurfaceControl; @@ -40,6 +39,8 @@ import android.window.TransitionInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import androidx.test.filters.SmallTest; + import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.transition.Transitions; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index f84685a92b57..9bb5482de715 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -29,8 +29,10 @@ import android.hardware.display.VirtualDisplay import android.os.Handler import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper +import android.util.SparseArray import android.view.Choreographer import android.view.Display.DEFAULT_DISPLAY +import android.view.IWindowManager import android.view.InputChannel import android.view.InputMonitor import android.view.InsetsSource @@ -71,6 +73,8 @@ import org.mockito.kotlin.eq import org.mockito.kotlin.whenever import java.util.Optional import java.util.function.Supplier +import org.mockito.Mockito +import org.mockito.kotlin.spy /** Tests of [DesktopModeWindowDecorViewModel] */ @@ -96,10 +100,12 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Mock private lateinit var mockShellExecutor: ShellExecutor @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer @Mock private lateinit var mockShellCommandHandler: ShellCommandHandler + @Mock private lateinit var mockWindowManager: IWindowManager private val transactionFactory = Supplier<SurfaceControl.Transaction> { SurfaceControl.Transaction() } + private val windowDecorByTaskIdSpy = spy(SparseArray<DesktopModeWindowDecoration>()) private lateinit var shellInit: ShellInit private lateinit var desktopModeOnInsetsChangedListener: DesktopModeOnInsetsChangedListener @@ -108,12 +114,15 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Before fun setUp() { shellInit = ShellInit(mockShellExecutor) + windowDecorByTaskIdSpy.clear() desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel( mContext, + mockShellExecutor, mockMainHandler, mockMainChoreographer, shellInit, mockShellCommandHandler, + mockWindowManager, mockTaskOrganizer, mockDisplayController, mockShellController, @@ -124,7 +133,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { mockDesktopModeWindowDecorFactory, mockInputMonitorFactory, transactionFactory, - mockRootTaskDisplayAreaOrganizer + mockRootTaskDisplayAreaOrganizer, + windowDecorByTaskIdSpy ) whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout) @@ -328,6 +338,19 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { verify(decoration, times(1)).relayout(task) } + @Test + fun testDestroyWindowDecoration_closesBeforeCleanup() { + val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM) + val decoration = setUpMockDecorationForTask(task) + val inOrder = Mockito.inOrder(decoration, windowDecorByTaskIdSpy) + + onTaskOpening(task) + desktopModeWindowDecorViewModel.destroyWindowDecoration(task) + + inOrder.verify(decoration).close() + inOrder.verify(windowDecorByTaskIdSpy).remove(task.taskId) + } + private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) { desktopModeWindowDecorViewModel.onTaskOpening( task, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index 40e61dd95f51..9e62bd254ac5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -16,6 +16,10 @@ package com.android.wm.shell.windowdecor; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; @@ -168,6 +172,57 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { assertThat(relayoutParams.mCornerRadius).isGreaterThan(0); } + @Test + public void updateRelayoutParams_freeformAndTransparent_allowsInputFallthrough() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setStatusBarAppearance( + APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat(relayoutParams.mAllowCaptionInputFallthrough).isTrue(); + } + + @Test + public void updateRelayoutParams_freeformButOpaque_disallowsInputFallthrough() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setStatusBarAppearance(0); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat(relayoutParams.mAllowCaptionInputFallthrough).isFalse(); + } + + @Test + public void updateRelayoutParams_fullscreen_disallowsInputFallthrough() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat(relayoutParams.mAllowCaptionInputFallthrough).isFalse(); + } + private void fillRoundedCornersResources(int fillValue) { when(mMockRoundedCornersRadiusArray.getDimensionPixelSize(anyInt(), anyInt())) .thenReturn(fillValue); diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index f0c639574a9f..49254d1c6f6e 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -81,7 +81,7 @@ ApkAssetsPtr ApkAssets::LoadOverlay(const std::string& idmap_path, package_prope std::string overlay_path(loaded_idmap->OverlayApkPath()); auto fd = unique_fd(base::utf8::open(overlay_path.c_str(), O_RDONLY | O_CLOEXEC)); std::unique_ptr<AssetsProvider> overlay_assets; - if (IsFabricatedOverlay(fd)) { + if (IsFabricatedOverlayName(overlay_path) && IsFabricatedOverlay(fd)) { // Fabricated overlays do not contain resource definitions. All of the overlay resource values // are defined inline in the idmap. overlay_assets = EmptyAssetsProvider::Create(std::move(overlay_path)); @@ -137,8 +137,7 @@ ApkAssetsPtr ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_asset, return {}; } loaded_arsc = LoadedArsc::Load(data, length, loaded_idmap.get(), property_flags); - } else if (loaded_idmap != nullptr && - IsFabricatedOverlay(std::string(loaded_idmap->OverlayApkPath()))) { + } else if (loaded_idmap != nullptr && IsFabricatedOverlay(loaded_idmap->OverlayApkPath())) { loaded_arsc = LoadedArsc::Load(loaded_idmap.get()); } else { loaded_arsc = LoadedArsc::CreateEmpty(); diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 5f98b8f8db43..982419059ead 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -18,8 +18,10 @@ #include "androidfw/Idmap.h" +#include "android-base/file.h" #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "android-base/utf8.h" #include "androidfw/misc.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" @@ -250,7 +252,12 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size } } // namespace -LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header, +// O_PATH is a lightweight way of creating an FD, only exists on Linux +#ifndef O_PATH +#define O_PATH (0) +#endif + +LoadedIdmap::LoadedIdmap(const std::string& idmap_path, const Idmap_header* header, const Idmap_data_header* data_header, const Idmap_target_entry* target_entries, const Idmap_target_entry_inline* target_inline_entries, @@ -267,10 +274,10 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header, configurations_(configs), overlay_entries_(overlay_entries), string_pool_(std::move(string_pool)), - idmap_path_(std::move(idmap_path)), + idmap_fd_(android::base::utf8::open(idmap_path.c_str(), O_RDONLY|O_CLOEXEC|O_BINARY|O_PATH)), overlay_apk_path_(overlay_apk_path), target_apk_path_(target_apk_path), - idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {} + idmap_last_mod_time_(getFileModDate(idmap_fd_.get())) {} std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) { ATRACE_CALL(); @@ -368,7 +375,7 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie } bool LoadedIdmap::IsUpToDate() const { - return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str()); + return idmap_last_mod_time_ == getFileModDate(idmap_fd_.get()); } } // namespace android diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 2c99f1aa3675..a3dd9833219e 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -54,6 +54,8 @@ #define INT32_MAX ((int32_t)(2147483647)) #endif +using namespace std::literals; + namespace android { #if defined(_WIN32) @@ -237,12 +239,24 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData)); } -bool IsFabricatedOverlay(const std::string& path) { - return IsFabricatedOverlay(path.c_str()); +bool IsFabricatedOverlayName(std::string_view path) { + static constexpr auto suffixFrro = ".frro"sv; + static constexpr auto suffixIdmap = ".frro@idmap"sv; + + return (path.size() > suffixFrro.size() && path.ends_with(suffixFrro)) + || (path.size() > suffixIdmap.size() && path.ends_with(suffixIdmap)); } -bool IsFabricatedOverlay(const char* path) { - auto fd = base::unique_fd(base::utf8::open(path, O_RDONLY|O_CLOEXEC)); +bool IsFabricatedOverlay(std::string_view path) { + if (!IsFabricatedOverlayName(path)) { + return false; + } + std::string path_copy; + if (path[path.size()] != '\0') { + path_copy.assign(path); + path = path_copy; + } + auto fd = base::unique_fd(base::utf8::open(path.data(), O_RDONLY|O_CLOEXEC|O_BINARY)); if (fd < 0) { return false; } @@ -7319,9 +7333,6 @@ class IdmapTypeMapping { public: void add(uint32_t targetResId, uint32_t overlayResId) { uint8_t targetTypeId = Res_GETTYPE(targetResId); - if (mData.find(targetTypeId) == mData.end()) { - mData.emplace(targetTypeId, std::set<std::pair<uint32_t, uint32_t>>()); - } auto& entries = mData[targetTypeId]; entries.insert(std::make_pair(targetResId, overlayResId)); } diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index d9f7c2a1ac19..c32a38ee9ec2 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -23,6 +23,7 @@ #include <variant> #include "android-base/macros.h" +#include "android-base/unique_fd.h" #include "androidfw/ConfigDescription.h" #include "androidfw/StringPiece.h" #include "androidfw/ResourceTypes.h" @@ -159,11 +160,6 @@ class LoadedIdmap { // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed. static std::unique_ptr<LoadedIdmap> Load(StringPiece idmap_path, StringPiece idmap_data); - // Returns the path to the IDMAP. - std::string_view IdmapPath() const { - return idmap_path_; - } - // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated. std::string_view OverlayApkPath() const { return overlay_apk_path_; @@ -203,7 +199,7 @@ class LoadedIdmap { const Idmap_overlay_entry* overlay_entries_; const std::unique_ptr<ResStringPool> string_pool_; - std::string idmap_path_; + android::base::unique_fd idmap_fd_; std::string_view overlay_apk_path_; std::string_view target_apk_path_; time_t idmap_last_mod_time_; @@ -211,7 +207,7 @@ class LoadedIdmap { private: DISALLOW_COPY_AND_ASSIGN(LoadedIdmap); - explicit LoadedIdmap(std::string&& idmap_path, + explicit LoadedIdmap(const std::string& idmap_path, const Idmap_header* header, const Idmap_data_header* data_header, const Idmap_target_entry* target_entries, diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 3d1403d0e039..c2648909386c 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -59,8 +59,8 @@ constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endi constexpr const uint32_t kFabricatedOverlayCurrentVersion = 3; // Returns whether or not the path represents a fabricated overlay. -bool IsFabricatedOverlay(const std::string& path); -bool IsFabricatedOverlay(const char* path); +bool IsFabricatedOverlayName(std::string_view path); +bool IsFabricatedOverlay(std::string_view path); bool IsFabricatedOverlay(android::base::borrowed_fd fd); /** diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h index d40d24ede769..077609d20d55 100644 --- a/libs/androidfw/include/androidfw/misc.h +++ b/libs/androidfw/include/androidfw/misc.h @@ -43,6 +43,8 @@ typedef enum FileType { FileType getFileType(const char* fileName); /* get the file's modification date; returns -1 w/errno set on failure */ time_t getFileModDate(const char* fileName); +/* same, but also returns -1 if the file has already been deleted */ +time_t getFileModDate(int fd); // Check if |path| or |fd| resides on a readonly filesystem. bool isReadonlyFilesystem(const char* path); diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp index d3949e9cf69f..93dcaf549a90 100644 --- a/libs/androidfw/misc.cpp +++ b/libs/androidfw/misc.cpp @@ -76,13 +76,23 @@ FileType getFileType(const char* fileName) /* * Get a file's modification date. */ -time_t getFileModDate(const char* fileName) -{ +time_t getFileModDate(const char* fileName) { struct stat sb; + if (stat(fileName, &sb) < 0) { + return (time_t)-1; + } + return sb.st_mtime; +} - if (stat(fileName, &sb) < 0) - return (time_t) -1; - +time_t getFileModDate(int fd) { + struct stat sb; + if (fstat(fd, &sb) < 0) { + return (time_t)-1; + } + if (sb.st_nlink <= 0) { + errno = ENOENT; + return (time_t)-1; + } return sb.st_mtime; } diff --git a/libs/hostgraphics/ADisplay.cpp b/libs/hostgraphics/ADisplay.cpp new file mode 100644 index 000000000000..9cc1f40184e3 --- /dev/null +++ b/libs/hostgraphics/ADisplay.cpp @@ -0,0 +1,159 @@ +/* + * Copyright 2024 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. + */ + +#include <apex/display.h> +#include <utils/Errors.h> + +namespace android::display::impl { + +/** + * Implementation of ADisplayConfig + */ +struct DisplayConfigImpl { + /** + * The width in pixels of the display configuration. + */ + int32_t width{1080}; + + /** + * The height in pixels of the display configuration. + */ + + int32_t height{1920}; + + /** + * The refresh rate of the display configuration, in frames per second. + */ + float fps{60.0}; + + /** + * The vsync offset at which surfaceflinger runs, in nanoseconds. + */ + int64_t sfOffset{0}; + + /** + * The vsync offset at which applications run, in nanoseconds. + */ + int64_t appOffset{0}; +}; + +// DisplayConfigImpl allocation is not managed through C++ memory apis, so +// preventing calling the destructor here. +static_assert(std::is_trivially_destructible<DisplayConfigImpl>::value); + +/** + * Implementation of ADisplay + */ +struct DisplayImpl { + /** + * The type of the display, i.e. whether it is an internal or external + * display. + */ + ADisplayType type; + + /** + * The preferred WCG dataspace + */ + ADataSpace wcgDataspace; + + /** + * The preferred WCG pixel format + */ + AHardwareBuffer_Format wcgPixelFormat; + + /** + * The config for this display. + */ + DisplayConfigImpl config; +}; + +// DisplayImpl allocation is not managed through C++ memory apis, so +// preventing calling the destructor here. +static_assert(std::is_trivially_destructible<DisplayImpl>::value); + +} // namespace android::display::impl + +using namespace android; +using namespace android::display::impl; + +namespace android { + +int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { + // This is running on host, so there are no physical displays available. + // Create 1 fake display instead. + DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>( + malloc(sizeof(DisplayImpl*) + sizeof(DisplayImpl))); + DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + 1); + + displayData[0] = DisplayImpl{ADisplayType::DISPLAY_TYPE_INTERNAL, + ADataSpace::ADATASPACE_UNKNOWN, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + DisplayConfigImpl()}; + impls[0] = displayData; + *outDisplays = reinterpret_cast<ADisplay**>(impls); + return 1; +} + +void ADisplay_release(ADisplay** displays) { + if (displays == nullptr) { + return; + } + free(displays); +} + +float ADisplay_getMaxSupportedFps(ADisplay* display) { + DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); + return impl->config.fps; +} + +ADisplayType ADisplay_getDisplayType(ADisplay* display) { + return reinterpret_cast<DisplayImpl*>(display)->type; +} + +void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outDataspace, + AHardwareBuffer_Format* outPixelFormat) { + DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); + *outDataspace = impl->wcgDataspace; + *outPixelFormat = impl->wcgPixelFormat; +} + +int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) { + DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); + *outConfig = reinterpret_cast<ADisplayConfig*>(&impl->config); + return OK; +} + +int32_t ADisplayConfig_getWidth(ADisplayConfig* config) { + return reinterpret_cast<DisplayConfigImpl*>(config)->width; +} + +int32_t ADisplayConfig_getHeight(ADisplayConfig* config) { + return reinterpret_cast<DisplayConfigImpl*>(config)->height; +} + +float ADisplayConfig_getFps(ADisplayConfig* config) { + return reinterpret_cast<DisplayConfigImpl*>(config)->fps; +} + +int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config) { + return reinterpret_cast<DisplayConfigImpl*>(config)->sfOffset; +} + +int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config) { + return reinterpret_cast<DisplayConfigImpl*>(config)->appOffset; +} + +} // namespace android diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp index f166fdeafa41..4407af68de99 100644 --- a/libs/hostgraphics/Android.bp +++ b/libs/hostgraphics/Android.bp @@ -22,6 +22,7 @@ cc_library_host_static { srcs: [ ":libui_host_common", + "ADisplay.cpp", "Fence.cpp", "HostBufferQueue.cpp", "PublicFormat.cpp", @@ -32,16 +33,21 @@ cc_library_host_static { // When frameworks/native/include will be removed from the list of automatic includes. // We will have to copy necessary headers with a pre-build step (generated headers). ".", - "frameworks/native/libs/nativebase/include", - "frameworks/native/libs/nativewindow/include", "frameworks/native/libs/arect/include", "frameworks/native/libs/ui/include_private", ], + + header_libs: [ + "libnativebase_headers", + "libnativedisplay_headers", + "libnativewindow_headers", + ], + export_include_dirs: ["."], target: { windows: { enabled: true, - } + }, }, } diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 40239b8c2719..54f94f5c4b14 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -346,6 +346,7 @@ cc_defaults { "jni/android_util_PathParser.cpp", "jni/Bitmap.cpp", + "jni/BitmapRegionDecoder.cpp", "jni/BufferUtils.cpp", "jni/HardwareBufferHelpers.cpp", "jni/BitmapFactory.cpp", @@ -421,7 +422,6 @@ cc_defaults { "jni/android_graphics_TextureLayer.cpp", "jni/android_graphics_HardwareRenderer.cpp", "jni/android_graphics_HardwareBufferRenderer.cpp", - "jni/BitmapRegionDecoder.cpp", "jni/GIFMovie.cpp", "jni/GraphicsStatsService.cpp", "jni/Movie.cpp", @@ -559,8 +559,13 @@ cc_defaults { "AnimatorManager.cpp", "CanvasTransform.cpp", "DamageAccumulator.cpp", + "DeviceInfo.cpp", + "FrameInfo.cpp", + "FrameInfoVisualizer.cpp", + "FrameMetricsReporter.cpp", "Gainmap.cpp", "Interpolator.cpp", + "JankTracker.cpp", "LightingInfo.cpp", "Matrix.cpp", "Mesh.cpp", @@ -623,13 +628,8 @@ cc_defaults { "utils/NdkUtils.cpp", "AutoBackendTextureRelease.cpp", "DeferredLayerUpdater.cpp", - "DeviceInfo.cpp", - "FrameInfo.cpp", - "FrameInfoVisualizer.cpp", "HardwareBitmapUploader.cpp", "HWUIProperties.sysprop", - "JankTracker.cpp", - "FrameMetricsReporter.cpp", "Layer.cpp", "LayerUpdateQueue.cpp", "ProfileDataContainer.cpp", @@ -643,7 +643,10 @@ cc_defaults { cflags: ["-Wno-implicit-fallthrough"], }, host: { - header_libs: ["libnativebase_headers"], + header_libs: [ + "libnativebase_headers", + "libnativedisplay_headers", + ], local_include_dirs: ["platform/host"], @@ -655,7 +658,11 @@ cc_defaults { "platform/host/WebViewFunctorManager.cpp", ], - cflags: ["-Wno-unused-private-field"], + cflags: [ + "-DHWUI_NULL_GPU", + "-DNULL_GPU_MAX_TEXTURE_SIZE=4096", + "-Wno-unused-private-field", + ], }, }, } diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp index 59f21694fb77..1e53fc2164c7 100644 --- a/libs/hwui/FrameInfoVisualizer.cpp +++ b/libs/hwui/FrameInfoVisualizer.cpp @@ -249,6 +249,7 @@ bool FrameInfoVisualizer::consumeProperties() { } void FrameInfoVisualizer::dumpData(int fd) { +#ifdef __ANDROID__ RETURN_IF_PROFILING_DISABLED(); // This method logs the last N frames (where N is <= mDataSize) since the @@ -268,6 +269,7 @@ void FrameInfoVisualizer::dumpData(int fd) { durationMS(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers), durationMS(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted)); } +#endif } } /* namespace uirenderer */ diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 4b0ddd2fa2ef..638a060bdb1c 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -17,10 +17,10 @@ #include "JankTracker.h" #include <cutils/ashmem.h> +#include <cutils/trace.h> #include <errno.h> #include <inttypes.h> #include <log/log.h> -#include <sys/mman.h> #include <algorithm> #include <cmath> @@ -278,7 +278,7 @@ void JankTracker::recomputeThresholds(int64_t frameBudget) REQUIRES(mDataMutex) void JankTracker::dumpData(int fd, const ProfileDataDescription* description, const ProfileData* data) { - +#ifdef __ANDROID__ if (description) { switch (description->type) { case JankTrackerType::Generic: @@ -296,9 +296,11 @@ void JankTracker::dumpData(int fd, const ProfileDataDescription* description, } data->dump(fd); dprintf(fd, "\n"); +#endif } void JankTracker::dumpFrames(int fd) { +#ifdef __ANDROID__ dprintf(fd, "\n\n---PROFILEDATA---\n"); for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) { dprintf(fd, "%s", FrameInfoNames[i]); @@ -315,6 +317,7 @@ void JankTracker::dumpFrames(int fd) { } } dprintf(fd, "\n---PROFILEDATA---\n\n"); +#endif } void JankTracker::reset() REQUIRES(mDataMutex) { diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index e358b57f6fe1..b1ad8b2eb1b9 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,32 +16,29 @@ #pragma once -#ifdef __ANDROID__ // Layoutlib does not support device info -#include "DeviceInfo.h" -#endif // __ANDROID__ - -#include "Outline.h" -#include "Rect.h" -#include "RevealClip.h" -#include "effects/StretchEffect.h" -#include "utils/MathUtils.h" -#include "utils/PaintUtils.h" - #include <SkBlendMode.h> -#include <SkImageFilter.h> #include <SkCamera.h> #include <SkColor.h> +#include <SkImageFilter.h> #include <SkMatrix.h> #include <SkRegion.h> - #include <androidfw/ResourceTypes.h> #include <cutils/compiler.h> #include <stddef.h> #include <utils/Log.h> + #include <algorithm> #include <ostream> #include <vector> +#include "DeviceInfo.h" +#include "Outline.h" +#include "Rect.h" +#include "RevealClip.h" +#include "effects/StretchEffect.h" +#include "utils/MathUtils.h" +#include "utils/PaintUtils.h" + class SkBitmap; class SkColorFilter; class SkPaint; @@ -546,13 +543,9 @@ public: } bool fitsOnLayer() const { -#ifdef __ANDROID__ // Layoutlib does not support device info const DeviceInfo* deviceInfo = DeviceInfo::get(); return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize(); -#else - return mPrimitiveFields.mWidth <= 4096 && mPrimitiveFields.mHeight <= 4096; -#endif } bool promotedToLayer() const { diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 536ff781badc..af169f4bc4cd 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -16,6 +16,7 @@ #include "VectorDrawable.h" +#include <gui/TraceUtils.h> #include <math.h> #include <string.h> #include <utils/Log.h> @@ -26,12 +27,7 @@ #include "SkSamplingOptions.h" #include "SkScalar.h" #include "hwui/Paint.h" - -#ifdef __ANDROID__ #include "renderthread/RenderThread.h" -#endif - -#include <gui/TraceUtils.h> #include "utils/Macros.h" #include "utils/VectorDrawableUtils.h" @@ -544,7 +540,7 @@ bool Tree::allocateBitmapIfNeeded(Cache& cache, int width, int height) { } bool Tree::canReuseBitmap(Bitmap* bitmap, int width, int height) { - return bitmap && width <= bitmap->width() && height <= bitmap->height(); + return bitmap && width == bitmap->width() && height == bitmap->height(); } void Tree::onPropertyChanged(TreeProperties* prop) { |