diff options
Diffstat (limited to 'libs')
22 files changed, 424 insertions, 186 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java index 612b38762d2d..9ea2943bc6da 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -50,6 +50,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; import java.util.Map; import java.util.concurrent.Executor; @@ -393,19 +394,31 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { if (splitAttributes == null) { return TaskFragmentAnimationParams.DEFAULT; } + final TaskFragmentAnimationParams.Builder builder = + new TaskFragmentAnimationParams.Builder(); final int animationBackgroundColor = getAnimationBackgroundColor(splitAttributes); - TaskFragmentAnimationParams.Builder builder = new TaskFragmentAnimationParams.Builder(); - if (animationBackgroundColor != DEFAULT_ANIMATION_BACKGROUND_COLOR) { - builder.setAnimationBackgroundColor(animationBackgroundColor); + builder.setAnimationBackgroundColor(animationBackgroundColor); + if (Flags.activityEmbeddingAnimationCustomizationFlag()) { + final int openAnimationResId = + splitAttributes.getAnimationParams().getOpenAnimationResId(); + builder.setOpenAnimationResId(openAnimationResId); + final int closeAnimationResId = + splitAttributes.getAnimationParams().getCloseAnimationResId(); + builder.setCloseAnimationResId(closeAnimationResId); + final int changeAnimationResId = + splitAttributes.getAnimationParams().getChangeAnimationResId(); + builder.setChangeAnimationResId(changeAnimationResId); } - // TODO(b/293658614): Allow setting custom open/close/changeAnimationResId. return builder.build(); } @ColorInt private static int getAnimationBackgroundColor(@NonNull SplitAttributes splitAttributes) { int animationBackgroundColor = DEFAULT_ANIMATION_BACKGROUND_COLOR; - final AnimationBackground animationBackground = splitAttributes.getAnimationBackground(); + AnimationBackground animationBackground = splitAttributes.getAnimationBackground(); + if (Flags.activityEmbeddingAnimationCustomizationFlag()) { + animationBackground = splitAttributes.getAnimationParams().getAnimationBackground(); + } if (animationBackground instanceof AnimationBackground.ColorBackground colorBackground) { animationBackgroundColor = colorBackground.getColor(); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java index 4267749dfa6b..c5aaddc4e0ed 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java @@ -29,6 +29,7 @@ import android.view.WindowManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.window.extensions.embedding.AnimationBackground; +import androidx.window.extensions.embedding.AnimationParams; import androidx.window.extensions.embedding.SplitAttributes; import org.junit.Before; @@ -112,5 +113,13 @@ public class WindowExtensionsTest { .isEqualTo(new SplitAttributes.SplitType.RatioSplitType(0.5f)); assertThat(splitAttributes.getAnimationBackground()) .isEqualTo(AnimationBackground.ANIMATION_BACKGROUND_DEFAULT); + assertThat(splitAttributes.getAnimationParams().getAnimationBackground()) + .isEqualTo(AnimationBackground.ANIMATION_BACKGROUND_DEFAULT); + assertThat(splitAttributes.getAnimationParams().getOpenAnimationResId()) + .isEqualTo(AnimationParams.DEFAULT_ANIMATION_RESOURCES_ID); + assertThat(splitAttributes.getAnimationParams().getCloseAnimationResId()) + .isEqualTo(AnimationParams.DEFAULT_ANIMATION_RESOURCES_ID); + assertThat(splitAttributes.getAnimationParams().getChangeAnimationResId()) + .isEqualTo(AnimationParams.DEFAULT_ANIMATION_RESOURCES_ID); } } diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp index c6dbd9b25e7f..1871203c7600 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp @@ -22,6 +22,40 @@ package { default_team: "trendy_team_multitasking_windowing", } +android_app { + name: "WMShellRobolectricScreenshotTestApp", + platform_apis: true, + certificate: "platform", + static_libs: [ + "WindowManager-Shell", + "platform-screenshot-diff-core", + ], + asset_dirs: ["goldens/robolectric"], + manifest: "AndroidManifestRobolectric.xml", + use_resource_processor: true, +} + +android_robolectric_test { + name: "WMShellRobolectricScreenshotTests", + instrumentation_for: "WMShellRobolectricScreenshotTestApp", + upstream: true, + java_resource_dirs: [ + "robolectric/config", + ], + srcs: [ + "src/**/*.kt", + ], + static_libs: [ + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "truth", + "platform-parametric-runner-lib", + ], + auto_gen_config: true, +} + android_test { name: "WMShellMultivalentScreenshotTestsOnDevice", srcs: [ diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml index a7a3f1313a9b..b4bdaeaf0eac 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml @@ -16,7 +16,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.wm.shell.multivalentscreenshot"> <application android:debuggable="true" android:supportsRtl="true"> - <uses-library android:name="android.test.runner" /> <activity android:name="platform.test.screenshot.ScreenshotActivity" android:exported="true"> diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png Binary files differnew file mode 100644 index 000000000000..723c6b8d9c93 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png Binary files differnew file mode 100644 index 000000000000..723c6b8d9c93 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties b/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties index 7a0527ccaafb..d50d976c9e84 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties @@ -1,2 +1,3 @@ sdk=NEWEST_SDK +graphicsMode=NATIVE diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt index dfc9c82d85ff..8f7fdd6a7b84 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt @@ -37,15 +37,12 @@ enum class DesktopModeFlags( // All desktop mode related flags will be added here DESKTOP_WINDOWING_MODE(DesktopModeStatus::isDesktopModeFlagEnabled, true); - private val TAG = "DesktopModeFlags" - - // Cache for toggle override, which is initialized once on its first access. It needs to be refreshed - // only on reboots as overridden state takes effect on reboots. + // Local cache for toggle override, which is initialized once on its first access. It needs to be + // refreshed only on reboots as overridden state takes effect on reboots. private var cachedToggleOverride: ToggleOverride? = null /** - * Determines state of flag based on the actual flag and desktop mode developer option - * overrides. + * Determines state of flag based on the actual flag and desktop mode developer option overrides. * * Note, this method makes sure that a constant developer toggle overrides is read until reboot. */ @@ -63,34 +60,44 @@ enum class DesktopModeFlags( } private fun getToggleOverride(context: Context): ToggleOverride { - val override = cachedToggleOverride ?: run { - // Cache toggle override the first time we encounter context. Override does not change - // with context, as context is just used to fetch a system property. + val override = + cachedToggleOverride + ?: run { + val override = getToggleOverrideFromSystem(context) + // Cache toggle override the first time we encounter context. Override does not change + // with context, as context is just used to fetch System Property and Settings.Global + cachedToggleOverride = override + Log.d(TAG, "Toggle override initialized to: $override") + override + } - // TODO(b/348193756): Cache a persistent value for Settings.Global until reboot. Current - // cache will change with process restart. - val toggleOverride = - Settings.Global.getInt( - context.contentResolver, - Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, - ToggleOverride.OVERRIDE_UNSET.setting) + return override + } - val newOverride = - settingToToggleOverrideMap[toggleOverride] - ?: run { - Log.w(TAG, "Unknown toggleOverride $toggleOverride") - ToggleOverride.OVERRIDE_UNSET - } - cachedToggleOverride = newOverride - Log.d(TAG, "Toggle override initialized to: $newOverride") - newOverride - } + private fun getToggleOverrideFromSystem(context: Context): ToggleOverride { + // A non-persistent System Property is used to store override to ensure it remains + // constant till reboot. + val overrideFromSystemProperties: ToggleOverride? = + System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, null).convertToToggleOverride() + return overrideFromSystemProperties + ?: run { + // Read Setting Global if System Property is not present (just after reboot) + // or not valid (user manually changed the value) + val overrideFromSettingsGlobal = + Settings.Global.getInt( + context.contentResolver, + Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, + ToggleOverride.OVERRIDE_UNSET.setting) + .convertToToggleOverrideWithFallback(ToggleOverride.OVERRIDE_UNSET) + // Initialize System Property + System.setProperty( + SYSTEM_PROPERTY_OVERRIDE_KEY, overrideFromSettingsGlobal.setting.toString()) - return override + overrideFromSettingsGlobal + } } - // TODO(b/348193756): Share ToggleOverride enum with Settings - // 'DesktopModePreferenceController' + // TODO(b/348193756): Share ToggleOverride enum with Settings 'DesktopModePreferenceController' /** * Override state of desktop mode developer option toggle. * @@ -107,4 +114,33 @@ enum class DesktopModeFlags( } private val settingToToggleOverrideMap = ToggleOverride.entries.associateBy { it.setting } + + private fun String?.convertToToggleOverride(): ToggleOverride? { + val intValue = this?.toIntOrNull() ?: return null + return settingToToggleOverrideMap[intValue] + ?: run { + Log.w(TAG, "Unknown toggleOverride int $intValue") + null + } + } + + private fun Int.convertToToggleOverrideWithFallback( + fallbackOverride: ToggleOverride + ): ToggleOverride { + return settingToToggleOverrideMap[this] + ?: run { + Log.w(TAG, "Unknown toggleOverride int $this") + fallbackOverride + } + } + + private companion object { + const val TAG = "DesktopModeFlags" + + /** + * Key for non-persistent System Property which is used to store desktop windowing developer + * option overrides. + */ + const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override" + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java index d270d2b4ccf1..5696a544152c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java @@ -266,6 +266,9 @@ class ActivityEmbeddingAnimationRunner { final Animation animation = animationProvider.get(info, change, openingWholeScreenBounds); if (shouldUseJumpCutForAnimation(animation)) { + if (Flags.activityEmbeddingAnimationCustomizationFlag()) { + return new ArrayList<>(); + } continue; } final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter( @@ -291,6 +294,9 @@ class ActivityEmbeddingAnimationRunner { final Animation animation = animationProvider.get(info, change, closingWholeScreenBounds); if (shouldUseJumpCutForAnimation(animation)) { + if (Flags.activityEmbeddingAnimationCustomizationFlag()) { + return new ArrayList<>(); + } continue; } final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java index f49b90d08a75..3046307702c2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java @@ -97,7 +97,7 @@ class ActivityEmbeddingAnimationSpec { Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) { if (Flags.activityEmbeddingAnimationCustomizationFlag()) { - final Animation customAnimation = loadCustomAnimation(info, change); + final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE); if (customAnimation != null) { return customAnimation; } @@ -131,7 +131,7 @@ class ActivityEmbeddingAnimationSpec { Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) { if (Flags.activityEmbeddingAnimationCustomizationFlag()) { - final Animation customAnimation = loadCustomAnimation(info, change); + final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE); if (customAnimation != null) { return customAnimation; } @@ -172,7 +172,7 @@ class ActivityEmbeddingAnimationSpec { // TODO(b/293658614): Support more complicated animations that may need more than a noop // animation as the start leash. final Animation noopAnimation = createNoopAnimation(change); - final Animation customAnimation = loadCustomAnimation(info, change); + final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE); if (customAnimation != null) { return new Animation[]{noopAnimation, customAnimation}; } @@ -227,7 +227,7 @@ class ActivityEmbeddingAnimationSpec { Animation loadOpenAnimation(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) { final boolean isEnter = TransitionUtil.isOpeningType(change.getMode()); - final Animation customAnimation = loadCustomAnimation(info, change); + final Animation customAnimation = loadCustomAnimation(info, change, change.getMode()); final Animation animation; if (customAnimation != null) { animation = customAnimation; @@ -254,7 +254,7 @@ class ActivityEmbeddingAnimationSpec { Animation loadCloseAnimation(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) { final boolean isEnter = TransitionUtil.isOpeningType(change.getMode()); - final Animation customAnimation = loadCustomAnimation(info, change); + final Animation customAnimation = loadCustomAnimation(info, change, change.getMode()); final Animation animation; if (customAnimation != null) { animation = customAnimation; @@ -287,14 +287,14 @@ class ActivityEmbeddingAnimationSpec { @Nullable private Animation loadCustomAnimation(@NonNull TransitionInfo info, - @NonNull TransitionInfo.Change change) { + @NonNull TransitionInfo.Change change, @WindowManager.TransitionType int mode) { final TransitionInfo.AnimationOptions options; if (Flags.moveAnimationOptionsToChange()) { options = change.getAnimationOptions(); } else { options = info.getAnimationOptions(); } - return loadCustomAnimationFromOptions(options, change.getMode()); + return loadCustomAnimationFromOptions(options, mode); } @Nullable @@ -319,8 +319,14 @@ class ActivityEmbeddingAnimationSpec { return null; } - final Animation anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(), - resId); + final Animation anim; + if (Flags.activityEmbeddingAnimationCustomizationFlag()) { + // TODO(b/293658614): Consider allowing custom animations from non-default packages. + // Enforce limiting to animations from the default "android" package for now. + anim = mTransitionAnimation.loadDefaultAnimationRes(resId); + } else { + anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(), resId); + } if (anim != null) { return anim; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt index 6781d08c9904..d1b2347a4411 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt @@ -19,6 +19,16 @@ package com.android.wm.shell.compatui import android.app.TaskInfo -fun isSingleTopActivityTranslucent(task: TaskInfo) = - task.isTopActivityTransparent && task.numActivities == 1 +import android.content.Context +import com.android.internal.R +// TODO(b/347289970): Consider replacing with API +fun isTopActivityExemptFromDesktopWindowing(context: Context, task: TaskInfo) = + isSystemUiTask(context, task) || (task.isTopActivityTransparent && task.numActivities == 1 + && !task.isTopActivityStyleFloating) + +private fun isSystemUiTask(context: Context, task: TaskInfo): Boolean { + val sysUiPackageName: String = + context.resources.getString(R.string.config_systemUi) + return task.baseActivity?.packageName == sysUiPackageName +} 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 da1d6daff8c6..e792f7ad4263 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 @@ -24,7 +24,6 @@ import android.os.Handler; import android.os.UserManager; import android.view.Choreographer; import android.view.IWindowManager; -import android.view.SurfaceControl; import android.view.WindowManager; import com.android.internal.jank.InteractionJankMonitor; @@ -404,8 +403,7 @@ public abstract class WMShellModule { Optional<RecentTasksController> recentTasksController, HomeTransitionObserver homeTransitionObserver) { return new RecentsTransitionHandler(shellInit, transitions, - recentTasksController.orElse(null), homeTransitionObserver, - SurfaceControl.Transaction::new); + recentTasksController.orElse(null), homeTransitionObserver); } // 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 4868a2f2f905..e24791292085 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 @@ -66,7 +66,7 @@ import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource 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.compatui.isSingleTopActivityTranslucent +import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener import com.android.wm.shell.draganddrop.DragAndDropController @@ -158,8 +158,6 @@ class DesktopTasksController( visualIndicator = null } } - private val sysUIPackageName = context.resources.getString( - com.android.internal.R.string.config_systemUi) private val transitionAreaHeight get() = @@ -219,11 +217,6 @@ class DesktopTasksController( return visualIndicator } - // TODO(b/347289970): Consider replacing with API - private fun isSystemUIApplication(taskInfo: RunningTaskInfo): Boolean { - return taskInfo.baseActivity?.packageName == sysUIPackageName - } - fun setOnTaskResizeAnimationListener(listener: OnTaskResizeAnimationListener) { toggleResizeDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener) enterDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener) @@ -351,19 +344,12 @@ class DesktopTasksController( wct: WindowContainerTransaction = WindowContainerTransaction(), transitionSource: DesktopModeTransitionSource, ) { - if (Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)) { - KtProtoLog.w( - WM_SHELL_DESKTOP_MODE, - "DesktopTasksController: Cannot enter desktop, " + - "translucent top activity found. This is likely a modal dialog." - ) - return - } - if (isSystemUIApplication(task)) { + if (Flags.enableDesktopWindowingModalsPolicy() + && isTopActivityExemptFromDesktopWindowing(context, task)) { KtProtoLog.w( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: Cannot enter desktop, " + - "systemUI top activity found." + "ineligible top activity found." ) return } @@ -942,10 +928,8 @@ class DesktopTasksController( when { // Check if the closing task needs to be handled TransitionUtil.isClosingType(request.type) -> handleTaskClosing(task) - // Check if the task has a top transparent activity - shouldLaunchAsModal(task) -> handleIncompatibleTaskLaunch(task) - // Check if the task has a top systemUI activity - isSystemUIApplication(task) -> handleIncompatibleTaskLaunch(task) + // Check if the top task shouldn't be allowed to enter desktop mode + isIncompatibleTask(task) -> handleIncompatibleTaskLaunch(task) // Check if fullscreen task should be updated task.isFullscreen -> handleFullscreenTaskLaunch(task, transition) // Check if freeform task should be updated @@ -979,9 +963,9 @@ class DesktopTasksController( .forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) } } - // TODO(b/347289970): Consider replacing with API - private fun shouldLaunchAsModal(task: TaskInfo) = - Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task) + private fun isIncompatibleTask(task: TaskInfo) = + Flags.enableDesktopWindowingModalsPolicy() + && isTopActivityExemptFromDesktopWindowing(context, task) private fun shouldHandleTaskClosing(request: TransitionRequestInfo): Boolean { return Flags.enableDesktopWindowingWallpaperActivity() && diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index e46625debcc6..234b4d0f86db 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -74,7 +74,6 @@ import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; import java.util.function.Consumer; -import java.util.function.Supplier; /** * Handles the Recents (overview) animation. Only one of these can run at a time. A recents @@ -85,7 +84,6 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { private final Transitions mTransitions; private final ShellExecutor mExecutor; - private final Supplier<SurfaceControl.Transaction> mTransactionSupplier; @Nullable private final RecentTasksController mRecentTasksController; private IApplicationThread mAnimApp = null; @@ -103,13 +101,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { public RecentsTransitionHandler(ShellInit shellInit, Transitions transitions, @Nullable RecentTasksController recentTasksController, - HomeTransitionObserver homeTransitionObserver, - Supplier<SurfaceControl.Transaction> transactionSupplier) { + HomeTransitionObserver homeTransitionObserver) { mTransitions = transitions; mExecutor = transitions.getMainExecutor(); mRecentTasksController = recentTasksController; mHomeTransitionObserver = homeTransitionObserver; - mTransactionSupplier = transactionSupplier; if (!Transitions.ENABLE_SHELL_TRANSITIONS) return; if (recentTasksController == null) return; shellInit.addInitCallback(() -> { @@ -1060,7 +1056,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { final Transitions.TransitionFinishCallback finishCB = mFinishCB; mFinishCB = null; - SurfaceControl.Transaction t = mFinishTransaction; + final SurfaceControl.Transaction t = mFinishTransaction; final WindowContainerTransaction wct = new WindowContainerTransaction(); if (mKeyguardLocked && mRecentsTask != null) { @@ -1110,16 +1106,6 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " normal finish"); - if (toHome && !mOpeningTasks.isEmpty()) { - // Attempting to start a task after swipe to home, don't show it, - // move recents to top - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - " attempting to start a task after swipe to home"); - t = mTransactionSupplier.get(); - wct.reorder(mRecentsTask, true /*onTop*/); - mClosingTasks.addAll(mOpeningTasks); - mOpeningTasks.clear(); - } // The general case: committing to recents, going home, or switching tasks. for (int i = 0; i < mOpeningTasks.size(); ++i) { t.show(mOpeningTasks.get(i).mTaskSurface); @@ -1188,10 +1174,6 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { mPipTransaction = null; } } - if (t != mFinishTransaction) { - // apply after merges because these changes are accounting for finishWCT changes. - mTransitions.setAfterMergeFinishTransaction(mTransition, t); - } cleanUp(); finishCB.onTransitionFinished(wct.isEmpty() ? null : wct); if (runnerFinishCb != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index b8abf8f2a3c0..21307a2edae0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -77,6 +77,7 @@ import androidx.annotation.BinderThread; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLog; +import com.android.window.flags.Flags; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; @@ -242,13 +243,6 @@ public class Transitions implements RemoteCallable<Transitions>, /** Ordered list of transitions which have been merged into this one. */ private ArrayList<ActiveTransition> mMerged; - /** - * @deprecated DO NOT USE THIS unless absolutely necessary. It will be removed once - * everything migrates off finishWCT. - */ - @java.lang.Deprecated - SurfaceControl.Transaction mAfterMergeFinishT; - ActiveTransition(IBinder token) { mToken = token; } @@ -586,6 +580,14 @@ public class Transitions implements RemoteCallable<Transitions>, final boolean isOpening = isOpeningType(transitType); final boolean isClosing = isClosingType(transitType); final int mode = change.getMode(); + // Ensure wallpapers stay in the back + if (change.hasFlags(FLAG_IS_WALLPAPER) && Flags.ensureWallpaperInTransitions()) { + if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { + return -zSplitLine + numChanges - i; + } else { + return -zSplitLine - i; + } + } // Put all the OPEN/SHOW on top if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { if (isOpening) { @@ -1034,20 +1036,6 @@ public class Transitions implements RemoteCallable<Transitions>, return null; } - /** @deprecated */ - @java.lang.Deprecated - public void setAfterMergeFinishTransaction(IBinder transition, - SurfaceControl.Transaction afterMergeFinishT) { - final ActiveTransition at = mKnownTransitions.get(transition); - if (at == null) return; - if (at.mAfterMergeFinishT != null) { - Log.e(TAG, "Setting after-merge-t >1 time on transition: " + at.mInfo.getDebugId()); - at.mAfterMergeFinishT.merge(afterMergeFinishT); - return; - } - at.mAfterMergeFinishT = afterMergeFinishT; - } - /** Aborts a transition. This will still queue it up to maintain order. */ private void onAbort(ActiveTransition transition) { final Track track = mTracks.get(transition.getTrack()); @@ -1108,7 +1096,6 @@ public class Transitions implements RemoteCallable<Transitions>, } // Merge all associated transactions together SurfaceControl.Transaction fullFinish = active.mFinishT; - SurfaceControl.Transaction afterMergeFinish = active.mAfterMergeFinishT; if (active.mMerged != null) { for (int iM = 0; iM < active.mMerged.size(); ++iM) { final ActiveTransition toMerge = active.mMerged.get(iM); @@ -1128,21 +1115,6 @@ public class Transitions implements RemoteCallable<Transitions>, fullFinish.merge(toMerge.mFinishT); } } - if (toMerge.mAfterMergeFinishT != null) { - if (afterMergeFinish == null) { - afterMergeFinish = toMerge.mAfterMergeFinishT; - } else { - afterMergeFinish.merge(toMerge.mAfterMergeFinishT); - } - toMerge.mAfterMergeFinishT = null; - } - } - } - if (afterMergeFinish != null) { - if (fullFinish == null) { - fullFinish = afterMergeFinish; - } else { - fullFinish.merge(afterMergeFinish); } } if (fullFinish != null) { 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 e3aa31f7e286..440fc4d314bb 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 @@ -32,7 +32,7 @@ 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.compatui.AppCompatUtils.isSingleTopActivityTranslucent; +import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; @@ -105,7 +105,6 @@ import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder; import java.io.PrintWriter; -import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; @@ -1034,12 +1033,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && taskInfo.isFocused) { return false; } - // TODO(b/347289970): Consider replacing with API if (Flags.enableDesktopWindowingModalsPolicy() - && isSingleTopActivityTranslucent(taskInfo)) { - return false; - } - if (isSystemUIApplication(taskInfo)) { + && isTopActivityExemptFromDesktopWindowing(mContext, taskInfo)) { return false; } return DesktopModeStatus.canEnterDesktopMode(mContext) @@ -1118,14 +1113,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && mSplitScreenController.isTaskInSplitScreen(taskId); } - // TODO(b/347289970): Consider replacing with API - private boolean isSystemUIApplication(RunningTaskInfo taskInfo) { - if (taskInfo.baseActivity != null) { - return (Objects.equals(taskInfo.baseActivity.getPackageName(), mSysUIPackageName)); - } - return false; - } - private void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + "DesktopModeWindowDecorViewModel"); diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt index a0a61fe2cf72..d0e8215e662e 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt @@ -117,12 +117,10 @@ class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(fli /** * Checks that all parts of the screen are covered at the start and end of the transition - * - * TODO b/197726599 Prevents all states from being checked */ @Presubmit @Test - fun entireScreenCoveredAtStartAndEnd() = flicker.entireScreenCovered(allStates = false) + fun entireScreenCoveredAtStartAndEnd() = flicker.entireScreenCovered() /** Checks [pipApp] window remains visible and on top throughout the transition */ @Presubmit diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt index 4cd2a366f5eb..ecaf970ae389 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt @@ -16,8 +16,10 @@ package com.android.wm.shell.compatui +import android.content.ComponentName import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest +import com.android.internal.R import com.android.wm.shell.ShellTestCase import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask import org.junit.Assert.assertFalse @@ -34,26 +36,55 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) @SmallTest class AppCompatUtilsTest : ShellTestCase() { - @Test - fun testIsSingleTopActivityTranslucent() { - assertTrue(isSingleTopActivityTranslucent( + fun testIsTopActivityExemptFromDesktopWindowing_topActivityTransparent() { + assertTrue(isTopActivityExemptFromDesktopWindowing(mContext, createFreeformTask(/* displayId */ 0) .apply { isTopActivityTransparent = true numActivities = 1 })) - assertFalse(isSingleTopActivityTranslucent( + assertFalse(isTopActivityExemptFromDesktopWindowing(mContext, createFreeformTask(/* displayId */ 0) .apply { isTopActivityTransparent = true numActivities = 0 })) - assertFalse(isSingleTopActivityTranslucent( + } + + @Test + fun testIsTopActivityExemptFromDesktopWindowing_singleTopActivity() { + assertTrue(isTopActivityExemptFromDesktopWindowing(mContext, + createFreeformTask(/* displayId */ 0) + .apply { + isTopActivityTransparent = true + numActivities = 1 + })) + assertFalse(isTopActivityExemptFromDesktopWindowing(mContext, createFreeformTask(/* displayId */ 0) .apply { isTopActivityTransparent = false numActivities = 1 })) } -}
\ No newline at end of file + + @Test + fun testIsTopActivityExemptFromDesktopWindowing__topActivityStyleFloating() { + assertFalse(isTopActivityExemptFromDesktopWindowing(mContext, + createFreeformTask(/* displayId */ 0) + .apply { + isTopActivityStyleFloating = true + })) + } + + @Test + fun testIsTopActivityExemptFromDesktopWindowing_systemUiTask() { + val systemUIPackageName = context.resources.getString(R.string.config_systemUi) + val baseComponent = ComponentName(systemUIPackageName, /* class */ "") + assertTrue(isTopActivityExemptFromDesktopWindowing(mContext, + createFreeformTask(/* displayId */ 0) + .apply { + baseActivity = baseComponent + })) + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 8c7de5c24bb8..bd38d360a15b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -700,19 +700,37 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun moveToDesktop_topActivityTranslucent_doesNothing() { - setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) + fun moveToDesktop_topActivityTranslucentWithStyleFloating_taskIsMovedToDesktop() { val task = - setUpFullscreenTask().apply { - isTopActivityTransparent = true - numActivities = 1 - } + setUpFullscreenTask().apply { + isTopActivityTransparent = true + isTopActivityStyleFloating = true + numActivities = 1 + } + + controller.moveToDesktop(task, transitionSource = UNKNOWN) + + val wct = getLatestEnterDesktopWct() + assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) + fun moveToDesktop_topActivityTranslucentWithoutStyleFloating_doesNothing() { + val task = + setUpFullscreenTask().apply { + isTopActivityTransparent = true + isTopActivityStyleFloating = false + numActivities = 1 + } controller.moveToDesktop(task, transitionSource = UNKNOWN) verifyEnterDesktopWCTNotExecuted() } @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) fun moveToDesktop_systemUIActivity_doesNothing() { val task = setUpFullscreenTask() @@ -1374,20 +1392,40 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun handleRequest_shouldLaunchAsModal_returnSwitchToFullscreenWCT() { - setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) + fun handleRequest_topActivityTransparentWithStyleFloating_returnSwitchToFreeformWCT() { + val freeformTask = setUpFreeformTask() + markTaskVisible(freeformTask) + val task = - setUpFreeformTask().apply { - isTopActivityTransparent = true - numActivities = 1 - } + setUpFullscreenTask().apply { + isTopActivityTransparent = true + isTopActivityStyleFloating = true + numActivities = 1 + } val result = controller.handleRequest(Binder(), createTransition(task)) assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode) - .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN + .isEqualTo(WINDOWING_MODE_FREEFORM) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) + fun handleRequest_topActivityTransparentWithoutStyleFloating_returnSwitchToFullscreenWCT() { + val task = + setUpFreeformTask().apply { + isTopActivityTransparent = true + isTopActivityStyleFloating = false + numActivities = 1 + } + + val result = controller.handleRequest(Binder(), createTransition(task)) + assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode) + .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN } @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) fun handleRequest_systemUIActivity_returnSwitchToFullscreenWCT() { val task = setUpFreeformTask() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt index 115b218f2e82..17983b27f4e8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt @@ -82,7 +82,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun isEnabled_flagOverridable_unsetOverride_featureFlagOn_returnsTrue() { + fun isEnabled_overrideUnset_featureFlagOn_returnsTrue() { setOverride(OVERRIDE_UNSET.setting) // For overridableFlag, for unset overrides, follow flag @@ -92,7 +92,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun isEnabled_flagOverridable_unsetOverride_featureFlagOff_returnsFalse() { + fun isEnabled_overrideUnset_featureFlagOff_returnsFalse() { setOverride(OVERRIDE_UNSET.setting) // For overridableFlag, for unset overrides, follow flag @@ -101,7 +101,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun isEnabled_flagOverridable_noOverride_featureFlagOn_returnsTrue() { + fun isEnabled_noOverride_featureFlagOn_returnsTrue() { setOverride(null) // For overridableFlag, in absence of overrides, follow flag @@ -111,7 +111,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun isEnabled_flagOverridable_noOverride_featureFlagOff_returnsFalse() { + fun isEnabled_noOverride_featureFlagOff_returnsFalse() { setOverride(null) // For overridableFlag, in absence of overrides, follow flag @@ -120,7 +120,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun isEnabled_flagOverridable_unrecognizableOverride_featureFlagOn_returnsTrue() { + fun isEnabled_unrecognizableOverride_featureFlagOn_returnsTrue() { setOverride(-2) // For overridableFlag, for recognizable overrides, follow flag @@ -130,7 +130,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun isEnabled_flagOverridable_unrecognizableOverride_featureFlagOff_returnsFalse() { + fun isEnabled_unrecognizableOverride_featureFlagOff_returnsFalse() { setOverride(-2) // For overridableFlag, for recognizable overrides, follow flag @@ -139,7 +139,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun isEnabled_flagOverridable_overrideOff_featureFlagOn_returnsFalse() { + fun isEnabled_overrideOff_featureFlagOn_returnsFalse() { setOverride(OVERRIDE_OFF.setting) // For overridableFlag, follow override if they exist @@ -149,7 +149,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun isEnabled_flagOverridable_overrideOn_featureFlagOff_returnsTrue() { + fun isEnabled_overrideOn_featureFlagOff_returnsTrue() { setOverride(OVERRIDE_ON.setting) // For overridableFlag, follow override if they exist @@ -158,7 +158,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun isEnabled_flagOverridable_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() { + fun isEnabled_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() { setOverride(OVERRIDE_OFF.setting) // For overridableFlag, follow override if they exist @@ -173,7 +173,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun isEnabled_flagOverridable_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() { + fun isEnabled_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() { setOverride(OVERRIDE_ON.setting) // For overridableFlag, follow override if they exist @@ -187,7 +187,7 @@ class DesktopModeFlagsTest : ShellTestCase() { @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) - fun isEnabled_flagOverridable_noOverride_featureFlagOnThenOff_returnsTrueAndFalse() { + fun isEnabled_noOverride_featureFlagOnThenOff_returnsTrueAndFalse() { setOverride(null) // For overridableFlag, in absence of overrides, follow flag assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() @@ -206,6 +206,108 @@ class DesktopModeFlagsTest : ShellTestCase() { } } + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) + @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_noSystemProperty_overrideOn_featureFlagOff_returnsTrueAndStoresPropertyOn() { + System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY) + setOverride(OVERRIDE_ON.setting) + + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + // Store System Property if not present + assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) + .isEqualTo(OVERRIDE_ON.setting.toString()) + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_noSystemProperty_overrideUnset_featureFlagOn_returnsTrueAndStoresPropertyUnset() { + System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY) + setOverride(OVERRIDE_UNSET.setting) + + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + // Store System Property if not present + assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) + .isEqualTo(OVERRIDE_UNSET.setting.toString()) + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) + @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_noSystemProperty_overrideUnset_featureFlagOff_returnsFalseAndStoresPropertyUnset() { + System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY) + setOverride(OVERRIDE_UNSET.setting) + + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + // Store System Property if not present + assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) + .isEqualTo(OVERRIDE_UNSET.setting.toString()) + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Suppress("ktlint:standard:max-line-length") + fun isEnabled_systemPropertyNotInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() { + System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "abc") + setOverride(OVERRIDE_OFF.setting) + + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + // Store System Property if currently invalid + assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) + .isEqualTo(OVERRIDE_OFF.setting.toString()) + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Suppress("ktlint:standard:max-line-length") + fun isEnabled_systemPropertyInvalidInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() { + System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "-2") + setOverride(OVERRIDE_OFF.setting) + + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + // Store System Property if currently invalid + assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) + .isEqualTo(OVERRIDE_OFF.setting.toString()) + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_systemPropertyOff_overrideOn_featureFlagOn_returnsFalseAndDoesNotUpdateProperty() { + System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_OFF.setting.toString()) + setOverride(OVERRIDE_ON.setting) + + // Have a consistent override until reboot + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() + assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) + .isEqualTo(OVERRIDE_OFF.setting.toString()) + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) + @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + fun isEnabled_systemPropertyOn_overrideOff_featureFlagOff_returnsTrueAndDoesNotUpdateProperty() { + System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_ON.setting.toString()) + setOverride(OVERRIDE_OFF.setting) + + // Have a consistent override until reboot + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) + .isEqualTo(OVERRIDE_ON.setting.toString()) + } + + @Test + @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Suppress("ktlint:standard:max-line-length") + fun isEnabled_systemPropertyUnset_overrideOff_featureFlagOn_returnsTrueAndDoesNotUpdateProperty() { + System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_UNSET.setting.toString()) + setOverride(OVERRIDE_OFF.setting) + + // Have a consistent override until reboot + assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() + assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) + .isEqualTo(OVERRIDE_UNSET.setting.toString()) + } + private fun setOverride(setting: Int?) { val contentResolver = mContext.contentResolver val key = Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES @@ -217,9 +319,16 @@ class DesktopModeFlagsTest : ShellTestCase() { } private fun resetCache() { - val cacheToggleOverride = + val cachedToggleOverride = DESKTOP_WINDOWING_MODE::class.java.getDeclaredField("cachedToggleOverride") - cacheToggleOverride.isAccessible = true - cacheToggleOverride.set(DESKTOP_WINDOWING_MODE, null) + cachedToggleOverride.isAccessible = true + cachedToggleOverride.set(DESKTOP_WINDOWING_MODE, null) + + // Clear override cache stored in System property + System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY) + } + + private companion object { + const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override" } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 8331d591fd59..409b87723e79 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -1191,8 +1191,7 @@ public class ShellTransitionTests extends ShellTestCase { mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class)); final RecentsTransitionHandler recentsHandler = new RecentsTransitionHandler(shellInit, transitions, - mock(RecentTasksController.class), mock(HomeTransitionObserver.class), - () -> mock(SurfaceControl.Transaction.class)); + mock(RecentTasksController.class), mock(HomeTransitionObserver.class)); transitions.replaceDefaultHandlerForTest(mDefaultHandler); shellInit.init(); 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 0ec671314cdf..0bf5a674aabb 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 @@ -348,10 +348,35 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { } @Test - fun testDecorationIsNotCreatedForTopTranslucentActivities() { - setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) + fun testDecorationIsCreatedForTopTranslucentActivitiesWithStyleFloating() { + val mockitoSession: StaticMockitoSession = mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(DesktopModeStatus::class.java) + .startMocking() + try { + val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply { + isTopActivityTransparent = true + isTopActivityStyleFloating = true + numActivities = 1 + } + doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } + setUpMockDecorationsForTasks(task) + + onTaskOpening(task) + verify(mockDesktopModeWindowDecorFactory) + .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any()) + } finally { + mockitoSession.finishMocking() + } + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) + fun testDecorationIsNotCreatedForTopTranslucentActivitiesWithoutStyleFloating() { val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply { isTopActivityTransparent = true + isTopActivityStyleFloating = false numActivities = 1 } onTaskOpening(task) @@ -361,6 +386,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { } @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) fun testDecorationIsNotCreatedForSystemUIActivities() { val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true) |