diff options
| author | 2025-01-29 12:39:43 -0800 | |
|---|---|---|
| committer | 2025-01-29 12:39:43 -0800 | |
| commit | e364898e225a71d7dc5818129d72b5886a97d229 (patch) | |
| tree | d20ddd8e486da886f56d45d3668e4b1944c0f295 | |
| parent | 9030ff14c8a20f7b51ef42669fdcdb73ec4f0a82 (diff) | |
| parent | a91be2535be774eaf4b88c5042a657d9a54a38c5 (diff) | |
Merge changes from topic "new-devopt-config" into main
* changes:
Add flag to enable desktop mode on specific devices
Change the config to show Desktop Mode Dev option
8 files changed, 566 insertions, 26 deletions
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java index 3f2aa1ccecd8..29186109f818 100644 --- a/core/java/android/window/DesktopModeFlags.java +++ b/core/java/android/window/DesktopModeFlags.java @@ -148,28 +148,22 @@ public enum DesktopModeFlags { return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption); } + public static boolean isDesktopModeForcedEnabled() { + return getToggleOverride() == ToggleOverride.OVERRIDE_ON; + } + private static boolean isFlagTrue(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) { if (!shouldOverrideByDevOption) return flagFunction.getAsBoolean(); if (Flags.showDesktopExperienceDevOption()) { - return switch (getToggleOverride(null)) { + return switch (getToggleOverride()) { case OVERRIDE_UNSET, OVERRIDE_OFF -> flagFunction.getAsBoolean(); case OVERRIDE_ON -> true; }; } if (Flags.showDesktopWindowingDevOption()) { - Application application = ActivityThread.currentApplication(); - if (application == null) { - Log.w(TAG, "Could not get the current application."); - return flagFunction.getAsBoolean(); - } - ContentResolver contentResolver = application.getContentResolver(); - if (contentResolver == null) { - Log.w(TAG, "Could not get the content resolver for the application."); - return flagFunction.getAsBoolean(); - } boolean shouldToggleBeEnabledByDefault = Flags.enableDesktopWindowingMode(); - return switch (getToggleOverride(contentResolver)) { + return switch (getToggleOverride()) { case OVERRIDE_UNSET -> flagFunction.getAsBoolean(); // When toggle override matches its default state, don't override flags. This // helps users reset their feature overrides. @@ -180,14 +174,13 @@ public enum DesktopModeFlags { return flagFunction.getAsBoolean(); } - private static ToggleOverride getToggleOverride(@Nullable ContentResolver contentResolver) { + private static ToggleOverride getToggleOverride() { // If cached, return it if (sCachedToggleOverride != null) { return sCachedToggleOverride; } - // Otherwise, fetch and cache it - ToggleOverride override = getToggleOverrideFromSystem(contentResolver); + ToggleOverride override = getToggleOverrideFromSystem(); sCachedToggleOverride = override; Log.d(TAG, "Toggle override initialized to: " + override); return override; @@ -196,8 +189,7 @@ public enum DesktopModeFlags { /** * Returns {@link ToggleOverride} from Settings.Global set by toggle. */ - private static ToggleOverride getToggleOverrideFromSystem( - @Nullable ContentResolver contentResolver) { + private static ToggleOverride getToggleOverrideFromSystem() { int settingValue; if (Flags.showDesktopExperienceDevOption()) { settingValue = SystemProperties.getInt( @@ -205,6 +197,16 @@ public enum DesktopModeFlags { ToggleOverride.OVERRIDE_UNSET.getSetting() ); } else { + final Application application = ActivityThread.currentApplication(); + if (application == null) { + Log.w(TAG, "Could not get the current application."); + return ToggleOverride.OVERRIDE_UNSET; + } + final ContentResolver contentResolver = application.getContentResolver(); + if (contentResolver == null) { + Log.w(TAG, "Could not get the content resolver for the application."); + return ToggleOverride.OVERRIDE_UNSET; + } settingValue = Settings.Global.getInt( contentResolver, Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 51d488fdd76b..d20b06738f8c 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -600,3 +600,13 @@ flag { description: "Enables split screen on non default displays" bug: "384999213" } + +flag { + name: "enable_desktop_mode_through_dev_option" + namespace: "lse_desktop_experience" + description: "Enables support for desktop mode through developer options for devices eligible for desktop mode." + bug: "382238347" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 586cafdd2b57..a49e03484192 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -7245,6 +7245,9 @@ <!-- Whether desktop mode is supported on the current device --> <bool name="config_isDesktopModeSupported">false</bool> + <!-- Whether the developer option for desktop mode is supported on the current device --> + <bool name="config_isDesktopModeDevOptionSupported">false</bool> + <!-- Maximum number of active tasks on a given Desktop Windowing session. Set to 0 for unlimited. --> <integer name="config_maxDesktopWindowingActiveTasks">0</integer> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 772a7413a4a7..aca9d30a2607 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -5709,6 +5709,9 @@ <!-- Whether desktop mode is supported on the current device --> <java-symbol type="bool" name="config_isDesktopModeSupported" /> + <!-- Whether the developer option for desktop mode is supported on the current device --> + <java-symbol type="bool" name="config_isDesktopModeDevOptionSupported" /> + <!-- Maximum number of active tasks on a given Desktop Windowing session. Set to 0 for unlimited. --> <java-symbol type="integer" name="config_maxDesktopWindowingActiveTasks"/> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java index 1ee71ca78815..e196880aad0f 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java @@ -212,10 +212,18 @@ public class DesktopModeStatus { } /** + * Return {@code true} if the current device supports the developer option for desktop mode. + */ + private static boolean isDesktopModeDevOptionSupported(@NonNull Context context) { + return context.getResources().getBoolean(R.bool.config_isDesktopModeDevOptionSupported); + } + + /** * Return {@code true} if desktop mode dev option should be shown on current device */ public static boolean canShowDesktopModeDevOption(@NonNull Context context) { - return isDeviceEligibleForDesktopMode(context) && Flags.showDesktopWindowingDevOption(); + return isDeviceEligibleForDesktopModeDevOption(context) + && Flags.showDesktopWindowingDevOption(); } /** @@ -226,17 +234,25 @@ public class DesktopModeStatus { } /** Returns if desktop mode dev option should be enabled if there is no user override. */ - public static boolean shouldDevOptionBeEnabledByDefault() { - return Flags.enableDesktopWindowingMode(); + public static boolean shouldDevOptionBeEnabledByDefault(Context context) { + return isDeviceEligibleForDesktopMode(context) && Flags.enableDesktopWindowingMode(); } /** * Return {@code true} if desktop mode is enabled and can be entered on the current device. */ public static boolean canEnterDesktopMode(@NonNull Context context) { - if (!isDeviceEligibleForDesktopMode(context)) return false; + return (isDeviceEligibleForDesktopMode(context) + && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue()) + || isDesktopModeEnabledByDevOption(context); + } - return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue(); + /** + * Check if Desktop mode should be enabled because the dev option is shown and enabled. + */ + private static boolean isDesktopModeEnabledByDevOption(@NonNull Context context) { + return DesktopModeFlags.isDesktopModeForcedEnabled() + && canShowDesktopModeDevOption(context); } /** @@ -298,7 +314,21 @@ public class DesktopModeStatus { * Return {@code true} if desktop mode is unrestricted and is supported in the device. */ public static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) { - return !enforceDeviceRestrictions() || isDesktopModeSupported(context); + return !enforceDeviceRestrictions() || isDesktopModeSupported(context) || ( + Flags.enableDesktopModeThroughDevOption() && isDesktopModeDevOptionSupported( + context)); + } + + /** + * Return {@code true} if the developer option for desktop mode is unrestricted and is supported + * in the device. + * + * Note that, if {@link #isDeviceEligibleForDesktopMode(Context)} is true, then + * {@link #isDeviceEligibleForDesktopModeDevOption(Context)} is also true. + */ + private static boolean isDeviceEligibleForDesktopModeDevOption(@NonNull Context context) { + return !enforceDeviceRestrictions() || isDesktopModeSupported(context) + || isDesktopModeDevOptionSupported(context); } /** diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt new file mode 100644 index 000000000000..4dac99b14aaf --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2025 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.shared.desktopmode + +import android.content.Context +import android.content.res.Resources +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.annotations.Presubmit +import android.platform.test.flag.junit.SetFlagsRule +import android.provider.Settings +import android.provider.Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES +import android.window.DesktopModeFlags +import androidx.test.filters.SmallTest +import com.android.internal.R +import com.android.window.flags.Flags +import com.android.wm.shell.ShellTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +@SmallTest +@Presubmit +@EnableFlags(Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) +class DesktopModeStatusTest : ShellTestCase() { + @get:Rule + val mSetFlagsRule = SetFlagsRule() + + private val mockContext = mock<Context>() + private val mockResources = mock<Resources>() + + @Before + fun setUp() { + doReturn(mockResources).whenever(mockContext).getResources() + doReturn(false).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)) + doReturn(false).whenever(mockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported) + ) + doReturn(context.contentResolver).whenever(mockContext).contentResolver + resetDesktopModeFlagsCache() + resetEnforceDeviceRestriction() + resetFlagOverride() + } + + @After + fun tearDown() { + resetDesktopModeFlagsCache() + resetEnforceDeviceRestriction() + resetFlagOverride() + } + + @DisableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE, + Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION + ) + @Test + fun canEnterDesktopMode_DWFlagDisabled_configsOff_returnsFalse() { + assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse() + } + + @DisableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE, + Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION + ) + @Test + fun canEnterDesktopMode_DWFlagDisabled_configsOn_disableDeviceRestrictions_returnsFalse() { + doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)) + doReturn(true).whenever(mockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported) + ) + disableEnforceDeviceRestriction() + + assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse() + } + + @DisableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE, + Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION + ) + @Test + fun canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_returnsFalse() { + doReturn(true).whenever(mockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported) + ) + + assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse() + } + + @DisableFlags( + Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE, + Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION + ) + @Test + fun canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_flagOverrideOn_returnsTrue() { + doReturn(true).whenever(mockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported) + ) + setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON) + + assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue() + } + + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Test + fun canEnterDesktopMode_DWFlagEnabled_configsOff_returnsFalse() { + assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse() + } + + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Test + fun canEnterDesktopMode_DWFlagEnabled_configDesktopModeOff_returnsFalse() { + doReturn(true).whenever(mockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported) + ) + + assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse() + } + + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Test + fun canEnterDesktopMode_DWFlagEnabled_configDesktopModeOn_returnsTrue() { + doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)) + + assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue() + } + + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Test + fun canEnterDesktopMode_DWFlagEnabled_configsOff_disableDeviceRestrictions_returnsTrue() { + disableEnforceDeviceRestriction() + + assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue() + } + + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Test + fun canEnterDesktopMode_DWFlagEnabled_configDevOptionOn_flagOverrideOn_returnsTrue() { + doReturn(true).whenever(mockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported) + ) + setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON) + + assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue() + } + + @Test + fun isDeviceEligibleForDesktopMode_configDEModeOn_returnsTrue() { + doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)) + + assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isTrue() + } + + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @Test + fun isDeviceEligibleForDesktopMode_supportFlagOff_returnsFalse() { + assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse() + } + + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @Test + fun isDeviceEligibleForDesktopMode_supportFlagOn_returnsFalse() { + assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse() + } + + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @Test + fun isDeviceEligibleForDesktopMode_supportFlagOn_configDevOptModeOn_returnsTrue() { + doReturn(true).whenever(mockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported) + ) + + assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isTrue() + } + + private fun resetEnforceDeviceRestriction() { + setEnforceDeviceRestriction(true) + } + + private fun disableEnforceDeviceRestriction() { + setEnforceDeviceRestriction(false) + } + + private fun setEnforceDeviceRestriction(value: Boolean) { + val field = DesktopModeStatus::class.java.getDeclaredField("ENFORCE_DEVICE_RESTRICTIONS") + field.isAccessible = true + field.setBoolean(null, value) + } + + private fun resetDesktopModeFlagsCache() { + val cachedToggleOverride = + DesktopModeFlags::class.java.getDeclaredField("sCachedToggleOverride") + cachedToggleOverride.isAccessible = true + cachedToggleOverride.set(null, null) + } + + private fun resetFlagOverride() { + Settings.Global.putString( + context.contentResolver, + DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, null + ) + } + + private fun setFlagOverride(override: DesktopModeFlags.ToggleOverride) { + Settings.Global.putInt( + context.contentResolver, + DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, override.setting + ) + } +} diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java index 6bf1c466aeb5..e3906f9119c2 100644 --- a/services/core/java/com/android/server/wm/DesktopModeHelper.java +++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java @@ -23,6 +23,7 @@ import android.window.DesktopModeFlags; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; /** * Constants for desktop mode feature @@ -35,7 +36,7 @@ public final class DesktopModeHelper { "persist.wm.debug.desktop_mode_enforce_device_restrictions", true); /** Whether desktop mode is enabled. */ - static boolean isDesktopModeEnabled() { + private static boolean isDesktopModeEnabled() { return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue(); } @@ -56,11 +57,30 @@ public final class DesktopModeHelper { return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported); } + static boolean isDesktopModeDevOptionsSupported(@NonNull Context context) { + return context.getResources().getBoolean(R.bool.config_isDesktopModeDevOptionSupported); + } + + /** + * Check if Desktop mode should be enabled because the dev option is shown and enabled. + */ + private static boolean isDesktopModeEnabledByDevOption(@NonNull Context context) { + return DesktopModeFlags.isDesktopModeForcedEnabled() && (isDesktopModeDevOptionsSupported( + context) || isDeviceEligibleForDesktopMode(context)); + } + + @VisibleForTesting + static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) { + return !shouldEnforceDeviceRestrictions() || isDesktopModeSupported(context) || ( + Flags.enableDesktopModeThroughDevOption() && isDesktopModeDevOptionsSupported( + context)); + } + /** * Return {@code true} if desktop mode can be entered on the current device. */ static boolean canEnterDesktopMode(@NonNull Context context) { - return isDesktopModeEnabled() - && (!shouldEnforceDeviceRestrictions() || isDesktopModeSupported(context)); + return (isDesktopModeEnabled() && isDeviceEligibleForDesktopMode(context)) + || isDesktopModeEnabledByDevOption(context); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java new file mode 100644 index 000000000000..e0b700a4ffe3 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2025 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.server.wm; + +import static android.provider.Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; + +import android.content.Context; +import android.content.res.Resources; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Settings; +import android.window.DesktopModeFlags; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.internal.R; +import com.android.window.flags.Flags; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.lang.reflect.Field; + +/** + * Test class for {@link DesktopModeHelper}. + */ +@SmallTest +@Presubmit +@EnableFlags(Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) +public class DesktopModeHelperTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private Context mContext; + private Context mMockContext; + private Resources mMockResources; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + mMockContext = mock(Context.class); + mMockResources = mock(Resources.class); + + doReturn(mMockResources).when(mMockContext).getResources(); + doReturn(false).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)); + doReturn(false).when(mMockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported)); + doReturn(mContext.getContentResolver()).when(mMockContext).getContentResolver(); + resetDesktopModeFlagsCache(); + resetEnforceDeviceRestriction(); + resetFlagOverride(); + } + + @After + public void tearDown() throws Exception { + resetDesktopModeFlagsCache(); + resetEnforceDeviceRestriction(); + resetFlagOverride(); + } + + @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE, + Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION}) + @Test + public void canEnterDesktopMode_DWFlagDisabled_configsOff_returnsFalse() { + assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse(); + } + + @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE, + Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION}) + @Test + public void canEnterDesktopMode_DWFlagDisabled_configsOn_disableDeviceCheck_returnsFalse() + throws Exception { + doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)); + doReturn(true).when(mMockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported)); + disableEnforceDeviceRestriction(); + + assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse(); + } + + @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE, + Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION}) + @Test + public void canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_returnsFalse() { + doReturn(true).when(mMockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported)); + + assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse(); + } + + @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE, + Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION}) + @Test + public void canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_flagOverrideOn_returnsTrue() + throws Exception { + doReturn(true).when(mMockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported)); + setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON); + + assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue(); + } + + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Test + public void canEnterDesktopMode_DWFlagEnabled_configsOff_returnsFalse() { + assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse(); + } + + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Test + public void canEnterDesktopMode_DWFlagEnabled_configDesktopModeOff_returnsFalse() { + doReturn(true).when(mMockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported)); + + assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse(); + } + + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Test + public void canEnterDesktopMode_DWFlagEnabled_configDesktopModeOn_returnsTrue() { + doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)); + + assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue(); + } + + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Test + public void canEnterDesktopMode_DWFlagEnabled_configsOff_disableDeviceRestrictions_returnsTrue() + throws Exception { + disableEnforceDeviceRestriction(); + + assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue(); + } + + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE) + @Test + public void canEnterDesktopMode_DWFlagEnabled_configDevOptionOn_flagOverrideOn_returnsTrue() { + doReturn(true).when(mMockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported) + ); + setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON); + + assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue(); + } + + @Test + public void isDeviceEligibleForDesktopMode_configDEModeOn_returnsTrue() { + doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported)); + + assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isTrue(); + } + + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @Test + public void isDeviceEligibleForDesktopMode_supportFlagOff_returnsFalse() { + assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isFalse(); + } + + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @Test + public void isDeviceEligibleForDesktopMode_supportFlagOn_returnsFalse() { + assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isFalse(); + } + + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION) + @Test + public void isDeviceEligibleForDesktopMode_supportFlagOn_configDevOptModeOn_returnsTrue() { + doReturn(true).when(mMockResources).getBoolean( + eq(R.bool.config_isDesktopModeDevOptionSupported) + ); + + assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isTrue(); + } + + private void resetEnforceDeviceRestriction() throws Exception { + setEnforceDeviceRestriction(true); + } + + private void disableEnforceDeviceRestriction() throws Exception { + setEnforceDeviceRestriction(false); + } + + private void setEnforceDeviceRestriction(boolean value) throws Exception { + Field deviceRestriction = DesktopModeHelper.class.getDeclaredField( + "ENFORCE_DEVICE_RESTRICTIONS"); + deviceRestriction.setAccessible(true); + deviceRestriction.setBoolean(/* obj= */ null, /* z= */ value); + } + + private void resetDesktopModeFlagsCache() throws Exception { + Field cachedToggleOverride = DesktopModeFlags.class.getDeclaredField( + "sCachedToggleOverride"); + cachedToggleOverride.setAccessible(true); + cachedToggleOverride.set(/* obj= */ null, /* value= */ null); + } + + private void resetFlagOverride() { + Settings.Global.putString(mContext.getContentResolver(), + DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, null); + } + + private void setFlagOverride(DesktopModeFlags.ToggleOverride override) { + Settings.Global.putInt(mContext.getContentResolver(), + DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, override.getSetting()); + } +} |