diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java index 02d5c27e5c6..ef054dfa405 100644 --- a/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java +++ b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java @@ -21,6 +21,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN; @@ -51,6 +52,9 @@ import com.android.settingslib.applications.AppUtils; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.widget.ActionButtonsPreference; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + import java.util.ArrayList; import java.util.List; @@ -63,29 +67,41 @@ public class UserAspectRatioDetails extends AppInfoBase implements private static final String KEY_HEADER_SUMMARY = "app_aspect_ratio_summary"; private static final String KEY_HEADER_BUTTONS = "header_view"; - private static final String KEY_PREF_FULLSCREEN = "fullscreen_pref"; + private static final String KEY_PREF_HALF_SCREEN = "half_screen_pref"; private static final String KEY_PREF_DISPLAY_SIZE = "display_size_pref"; private static final String KEY_PREF_16_9 = "16_9_pref"; private static final String KEY_PREF_4_3 = "4_3_pref"; @VisibleForTesting + static final String KEY_PREF_FULLSCREEN = "fullscreen_pref"; + @VisibleForTesting static final String KEY_PREF_DEFAULT = "app_default_pref"; @VisibleForTesting static final String KEY_PREF_3_2 = "3_2_pref"; + @VisibleForTesting + @NonNull String mSelectedKey = KEY_PREF_DEFAULT; + + /** Radio button preference key mapped to {@link PackageManager.UserMinAspectRatio} value */ + @VisibleForTesting + final BiMap mKeyToAspectRatioMap = HashBiMap.create(); + private final List mAspectRatioPreferences = new ArrayList<>(); @NonNull private UserAspectRatioManager mUserAspectRatioManager; - @NonNull private String mSelectedKey = KEY_PREF_DEFAULT; + private boolean mIsOverrideToFullscreenEnabled; @Override public void onCreate(@NonNull Bundle savedInstanceState) { super.onCreate(savedInstanceState); mUserAspectRatioManager = new UserAspectRatioManager(getContext()); + mIsOverrideToFullscreenEnabled = getAspectRatioManager() + .isOverrideToFullscreenEnabled(mPackageName, mUserId); + initPreferences(); try { - final int userAspectRatio = mUserAspectRatioManager + final int userAspectRatio = getAspectRatioManager() .getUserMinAspectRatioValue(mPackageName, mUserId); mSelectedKey = getSelectedKey(userAspectRatio); } catch (RemoteException e) { @@ -148,43 +164,23 @@ public class UserAspectRatioDetails extends AppInfoBase implements } @PackageManager.UserMinAspectRatio - private int getSelectedUserMinAspectRatio(@NonNull String selectedKey) { - switch (selectedKey) { - case KEY_PREF_FULLSCREEN: - return USER_MIN_ASPECT_RATIO_FULLSCREEN; - case KEY_PREF_HALF_SCREEN: - return USER_MIN_ASPECT_RATIO_SPLIT_SCREEN; - case KEY_PREF_DISPLAY_SIZE: - return USER_MIN_ASPECT_RATIO_DISPLAY_SIZE; - case KEY_PREF_3_2: - return USER_MIN_ASPECT_RATIO_3_2; - case KEY_PREF_4_3: - return USER_MIN_ASPECT_RATIO_4_3; - case KEY_PREF_16_9: - return USER_MIN_ASPECT_RATIO_16_9; - default: - return USER_MIN_ASPECT_RATIO_UNSET; - } + @VisibleForTesting + int getSelectedUserMinAspectRatio(@NonNull String selectedKey) { + final int appDefault = mKeyToAspectRatioMap + .getOrDefault(KEY_PREF_DEFAULT, USER_MIN_ASPECT_RATIO_UNSET); + return mKeyToAspectRatioMap.getOrDefault(selectedKey, appDefault); } @NonNull private String getSelectedKey(@PackageManager.UserMinAspectRatio int userMinAspectRatio) { - switch (userMinAspectRatio) { - case USER_MIN_ASPECT_RATIO_FULLSCREEN: - return KEY_PREF_FULLSCREEN; - case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: - return KEY_PREF_HALF_SCREEN; - case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: - return KEY_PREF_DISPLAY_SIZE; - case USER_MIN_ASPECT_RATIO_3_2: - return KEY_PREF_3_2; - case USER_MIN_ASPECT_RATIO_4_3: - return KEY_PREF_4_3; - case USER_MIN_ASPECT_RATIO_16_9: - return KEY_PREF_16_9; - default: - return KEY_PREF_DEFAULT; + final String appDefault = mKeyToAspectRatioMap.inverse() + .getOrDefault(USER_MIN_ASPECT_RATIO_UNSET, KEY_PREF_DEFAULT); + + if (userMinAspectRatio == USER_MIN_ASPECT_RATIO_UNSET && mIsOverrideToFullscreenEnabled) { + // Pre-select fullscreen option if device manufacturer has overridden app to fullscreen + userMinAspectRatio = USER_MIN_ASPECT_RATIO_FULLSCREEN; } + return mKeyToAspectRatioMap.inverse().getOrDefault(userMinAspectRatio, appDefault); } @Override @@ -217,7 +213,11 @@ public class UserAspectRatioDetails extends AppInfoBase implements .setButton1Icon(R.drawable.ic_settings_open) .setButton1OnClickListener(v -> launchApplication()); - addPreference(KEY_PREF_DEFAULT, USER_MIN_ASPECT_RATIO_UNSET); + if (mIsOverrideToFullscreenEnabled) { + addPreference(KEY_PREF_DEFAULT, USER_MIN_ASPECT_RATIO_APP_DEFAULT); + } else { + addPreference(KEY_PREF_DEFAULT, USER_MIN_ASPECT_RATIO_UNSET); + } addPreference(KEY_PREF_FULLSCREEN, USER_MIN_ASPECT_RATIO_FULLSCREEN); addPreference(KEY_PREF_DISPLAY_SIZE, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE); addPreference(KEY_PREF_HALF_SCREEN, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN); @@ -232,12 +232,13 @@ public class UserAspectRatioDetails extends AppInfoBase implements if (pref == null) { return; } - if (!mUserAspectRatioManager.hasAspectRatioOption(aspectRatio, mPackageName)) { + if (!getAspectRatioManager().hasAspectRatioOption(aspectRatio, mPackageName)) { pref.setVisible(false); return; } pref.setTitle(mUserAspectRatioManager.getAccessibleEntry(aspectRatio, mPackageName)); pref.setOnClickListener(this); + mKeyToAspectRatioMap.put(key, aspectRatio); mAspectRatioPreferences.add(pref); } diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java index 3cca5f6771e..3bf61099f13 100644 --- a/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java +++ b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java @@ -16,19 +16,32 @@ package com.android.settings.applications.appcompat; +import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET; import static android.os.UserHandle.getUserHandleForUid; +import static android.os.UserHandle.getUserId; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE; import static java.lang.Boolean.FALSE; import android.app.AppGlobals; +import android.app.compat.CompatChanges; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.os.RemoteException; +import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.ArrayMap; @@ -37,6 +50,7 @@ import androidx.annotation.Nullable; import com.android.settings.R; import com.android.settings.Utils; +import com.android.window.flags.Flags; import com.google.common.annotations.VisibleForTesting; @@ -55,6 +69,8 @@ public class UserAspectRatioManager { "enable_app_compat_user_aspect_ratio_fullscreen"; private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN = true; + final boolean mIsUserMinAspectRatioAppDefaultFlagEnabled = Flags.userMinAspectRatioAppDefault(); + private final Context mContext; private final IPackageManager mIPm; /** Apps that have launcher entry defined in manifest */ @@ -62,8 +78,13 @@ public class UserAspectRatioManager { private final Map mUserAspectRatioA11yMap; public UserAspectRatioManager(@NonNull Context context) { + this(context, AppGlobals.getPackageManager()); + } + + @VisibleForTesting + UserAspectRatioManager(@NonNull Context context, @NonNull IPackageManager pm) { mContext = context; - mIPm = AppGlobals.getPackageManager(); + mIPm = pm; mUserAspectRatioA11yMap = new ArrayMap<>(); mUserAspectRatioMap = getUserMinAspectRatioMapping(); } @@ -86,7 +107,7 @@ public class UserAspectRatioManager { throws RemoteException { final int aspectRatio = mIPm.getUserMinAspectRatio(packageName, uid); return hasAspectRatioOption(aspectRatio, packageName) - ? aspectRatio : PackageManager.USER_MIN_ASPECT_RATIO_UNSET; + ? aspectRatio : USER_MIN_ASPECT_RATIO_UNSET; } /** @@ -94,11 +115,18 @@ public class UserAspectRatioManager { */ @NonNull public String getUserMinAspectRatioEntry(@PackageManager.UserMinAspectRatio int aspectRatio, - String packageName) { - if (!hasAspectRatioOption(aspectRatio, packageName)) { - return mUserAspectRatioMap.get(PackageManager.USER_MIN_ASPECT_RATIO_UNSET); + @NonNull String packageName, int userId) { + final String appDefault = getAspectRatioStringOrDefault( + mUserAspectRatioMap.get(USER_MIN_ASPECT_RATIO_UNSET), + USER_MIN_ASPECT_RATIO_UNSET); + + if (!hasAspectRatioOption(aspectRatio, packageName)) { + return appDefault; } - return mUserAspectRatioMap.get(aspectRatio); + + return isCurrentSelectionFromManufacturerOverride(packageName, userId, aspectRatio) + ? getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, packageName, userId) + : mUserAspectRatioMap.getOrDefault(aspectRatio, appDefault); } /** @@ -106,19 +134,22 @@ public class UserAspectRatioManager { */ @NonNull public CharSequence getAccessibleEntry(@PackageManager.UserMinAspectRatio int aspectRatio, - String packageName) { - return mUserAspectRatioA11yMap.getOrDefault(aspectRatio, - getUserMinAspectRatioEntry(aspectRatio, packageName)); + @NonNull String packageName) { + final int userId = mContext.getUserId(); + return isCurrentSelectionFromManufacturerOverride(packageName, userId, aspectRatio) + ? getAccessibleEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, packageName) + : mUserAspectRatioA11yMap.getOrDefault(aspectRatio, + getUserMinAspectRatioEntry(aspectRatio, packageName, userId)); } /** * @return corresponding aspect ratio string for package name and user */ @NonNull - public String getUserMinAspectRatioEntry(@NonNull String packageName, int uid) + public String getUserMinAspectRatioEntry(@NonNull String packageName, int userId) throws RemoteException { - final int aspectRatio = getUserMinAspectRatioValue(packageName, uid); - return getUserMinAspectRatioEntry(aspectRatio, packageName); + final int aspectRatio = getUserMinAspectRatioValue(packageName, userId); + return getUserMinAspectRatioEntry(aspectRatio, packageName, userId); } /** @@ -128,8 +159,7 @@ public class UserAspectRatioManager { */ public boolean hasAspectRatioOption(@PackageManager.UserMinAspectRatio int option, String packageName) { - if (option == PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN - && !isFullscreenOptionEnabled(packageName)) { + if (option == USER_MIN_ASPECT_RATIO_FULLSCREEN && !isFullscreenOptionEnabled(packageName)) { return false; } return mUserAspectRatioMap.containsKey(option); @@ -154,6 +184,18 @@ public class UserAspectRatioManager { return !FALSE.equals(appAllowsUserAspectRatioOverride) && hasLauncherEntry(app); } + /** + * Whether the app has been overridden to fullscreen by device manufacturer or + * whether the app's aspect ratio has been overridden by the user. + */ + public boolean isAppOverridden(@NonNull ApplicationInfo app, + @PackageManager.UserMinAspectRatio int userOverride) { + return (userOverride != USER_MIN_ASPECT_RATIO_UNSET + && userOverride != USER_MIN_ASPECT_RATIO_APP_DEFAULT) + || isCurrentSelectionFromManufacturerOverride(app.packageName, getUserId(app.uid), + userOverride); + } + /** * Whether fullscreen option in per-app user aspect ratio settings is enabled */ @@ -168,6 +210,32 @@ public class UserAspectRatioManager { DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN); } + /** + * Whether the device manufacturer has overridden app's orientation to + * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER} to force app to fullscreen + * and app has not opted-out from the treatment + */ + boolean isOverrideToFullscreenEnabled(String pkgName, int userId) { + Boolean appAllowsOrientationOverride = readComponentProperty(mContext.getPackageManager(), + pkgName, PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE); + return mIsUserMinAspectRatioAppDefaultFlagEnabled + && hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, pkgName) + && !FALSE.equals(appAllowsOrientationOverride) + && isFullscreenCompatChangeEnabled(pkgName, userId); + } + + @VisibleForTesting + boolean isFullscreenCompatChangeEnabled(String pkgName, int userId) { + return CompatChanges.isChangeEnabled( + OVERRIDE_ANY_ORIENTATION_TO_USER, pkgName, UserHandle.of(userId)); + } + + private boolean isCurrentSelectionFromManufacturerOverride(String pkgName, int userId, + @PackageManager.UserMinAspectRatio int aspectRatio) { + return aspectRatio == USER_MIN_ASPECT_RATIO_UNSET + && isOverrideToFullscreenEnabled(pkgName, userId); + } + private boolean hasLauncherEntry(@NonNull ApplicationInfo app) { return !mContext.getSystemService(LauncherApps.class) .getActivityList(app.packageName, getUserHandleForUid(app.uid)) @@ -197,13 +265,13 @@ public class UserAspectRatioManager { boolean containsColon = aspectRatioString.contains(":"); switch (aspectRatioVal) { // Only map known values of UserMinAspectRatio and ignore unknown entries - case PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN: - case PackageManager.USER_MIN_ASPECT_RATIO_UNSET: - case PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: - case PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: - case PackageManager.USER_MIN_ASPECT_RATIO_4_3: - case PackageManager.USER_MIN_ASPECT_RATIO_16_9: - case PackageManager.USER_MIN_ASPECT_RATIO_3_2: + case USER_MIN_ASPECT_RATIO_FULLSCREEN: + case USER_MIN_ASPECT_RATIO_UNSET: + case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: + case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: + case USER_MIN_ASPECT_RATIO_4_3: + case USER_MIN_ASPECT_RATIO_16_9: + case USER_MIN_ASPECT_RATIO_3_2: if (containsColon) { String[] aspectRatioDigits = aspectRatioString.split(":"); String accessibleString = getAccessibleOption(aspectRatioDigits[0], @@ -215,10 +283,18 @@ public class UserAspectRatioManager { userMinAspectRatioMap.put(aspectRatioVal, aspectRatioString); } } - if (!userMinAspectRatioMap.containsKey(PackageManager.USER_MIN_ASPECT_RATIO_UNSET)) { + if (!userMinAspectRatioMap.containsKey(USER_MIN_ASPECT_RATIO_UNSET)) { throw new RuntimeException("config_userAspectRatioOverrideValues options must have" + " USER_MIN_ASPECT_RATIO_UNSET value"); } + if (mIsUserMinAspectRatioAppDefaultFlagEnabled) { + userMinAspectRatioMap.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT, + userMinAspectRatioMap.get(USER_MIN_ASPECT_RATIO_UNSET)); + if (mUserAspectRatioA11yMap.containsKey(USER_MIN_ASPECT_RATIO_UNSET)) { + mUserAspectRatioA11yMap.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT, + mUserAspectRatioA11yMap.get(USER_MIN_ASPECT_RATIO_UNSET)); + } + } return userMinAspectRatioMap; } @@ -236,17 +312,17 @@ public class UserAspectRatioManager { } // Options are customized per device and if strings are set to @null, use default switch (aspectRatioVal) { - case PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN: + case USER_MIN_ASPECT_RATIO_FULLSCREEN: return mContext.getString(R.string.user_aspect_ratio_fullscreen); - case PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: + case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: return mContext.getString(R.string.user_aspect_ratio_half_screen); - case PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: + case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: return mContext.getString(R.string.user_aspect_ratio_device_size); - case PackageManager.USER_MIN_ASPECT_RATIO_4_3: + case USER_MIN_ASPECT_RATIO_4_3: return mContext.getString(R.string.user_aspect_ratio_4_3); - case PackageManager.USER_MIN_ASPECT_RATIO_16_9: + case USER_MIN_ASPECT_RATIO_16_9: return mContext.getString(R.string.user_aspect_ratio_16_9); - case PackageManager.USER_MIN_ASPECT_RATIO_3_2: + case USER_MIN_ASPECT_RATIO_3_2: return mContext.getString(R.string.user_aspect_ratio_3_2); default: return mContext.getString(R.string.user_aspect_ratio_app_default); diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt index 7368750fce5..15cd1bab5f6 100644 --- a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt +++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt @@ -22,6 +22,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PackageManager.GET_ACTIVITIES +import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET import android.os.Build import android.os.Bundle @@ -139,7 +140,9 @@ class UserAspectRatioAppListModel(private val context: Context) recordList: List ): List { val hasSuggested = recordList.any { it.suggested } - val hasOverride = recordList.any { it.userOverride != USER_MIN_ASPECT_RATIO_UNSET } + val hasOverride = recordList.any { + userAspectRatioManager.isAppOverridden(it.app, it.userOverride) + } val options = mutableListOf(SpinnerItem.All) // Add suggested filter first as default if (hasSuggested) options.add(0, SpinnerItem.Suggested) @@ -187,7 +190,9 @@ class UserAspectRatioAppListModel(private val context: Context) ): Flow> = recordListFlow.filterItem( when (SpinnerItem.entries.getOrNull(option)) { SpinnerItem.Suggested -> ({ it.canDisplay && it.suggested }) - SpinnerItem.Overridden -> ({ it.userOverride != USER_MIN_ASPECT_RATIO_UNSET }) + SpinnerItem.Overridden -> ({ + userAspectRatioManager.isAppOverridden(it.app, it.userOverride) + }) else -> ({ it.canDisplay }) } ) @@ -197,7 +202,7 @@ class UserAspectRatioAppListModel(private val context: Context) val summary by remember(record.userOverride) { flow { emit(userAspectRatioManager.getUserMinAspectRatioEntry(record.userOverride, - record.app.packageName)) + record.app.packageName, record.app.userId)) }.flowOn(Dispatchers.IO) }.collectAsStateWithLifecycle(initialValue = stringResource(R.string.summary_placeholder)) return { summary } diff --git a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java index b615163fbc7..f3b105bc3d6 100644 --- a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java +++ b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java @@ -16,12 +16,21 @@ package com.android.settings.applications.appcompat; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET; + +import static com.android.settings.applications.AppInfoBase.ARG_PACKAGE_NAME; import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_PREF_3_2; import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_PREF_DEFAULT; +import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_PREF_FULLSCREEN; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -30,15 +39,20 @@ import static org.mockito.Mockito.when; import android.app.IActivityManager; import android.app.settings.SettingsEnums; import android.content.Context; +import android.os.Bundle; import android.os.RemoteException; +import androidx.fragment.app.testing.EmptyFragmentActivity; import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.rules.ActivityScenarioRule; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowActivityManager; +import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; @@ -51,9 +65,13 @@ import org.robolectric.annotation.Config; * To run test: atest SettingsRoboTests:UserAspectRatioDetailsTest */ @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowActivityManager.class}) +@Config(shadows = {ShadowActivityManager.class, ShadowFragment.class}) public class UserAspectRatioDetailsTest { + @Rule + public ActivityScenarioRule rule = + new ActivityScenarioRule<>(EmptyFragmentActivity.class); + @Mock private UserAspectRatioManager mUserAspectRatioManager; @Mock @@ -72,6 +90,8 @@ public class UserAspectRatioDetailsTest { mFragment = spy(new UserAspectRatioDetails()); when(mFragment.getContext()).thenReturn(mContext); when(mFragment.getAspectRatioManager()).thenReturn(mUserAspectRatioManager); + when(mUserAspectRatioManager.isOverrideToFullscreenEnabled(anyString(), anyInt())) + .thenReturn(false); ShadowActivityManager.setService(mAm); mRadioButtonPref = new RadioWithImagePreference(mContext); final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); @@ -80,6 +100,8 @@ public class UserAspectRatioDetailsTest { @Test public void onRadioButtonClicked_prefChange_shouldStopActivity() throws RemoteException { + doReturn(USER_MIN_ASPECT_RATIO_UNSET).when(mFragment) + .getSelectedUserMinAspectRatio(anyString()); // Default was already selected mRadioButtonPref.setKey(KEY_PREF_DEFAULT); mFragment.onRadioButtonClicked(mRadioButtonPref); @@ -92,6 +114,8 @@ public class UserAspectRatioDetailsTest { @Test public void onRadioButtonClicked_prefChange_shouldSetAspectRatio() throws RemoteException { + doReturn(USER_MIN_ASPECT_RATIO_UNSET).when(mFragment) + .getSelectedUserMinAspectRatio(anyString()); // Default was already selected mRadioButtonPref.setKey(KEY_PREF_DEFAULT); mFragment.onRadioButtonClicked(mRadioButtonPref); @@ -105,6 +129,8 @@ public class UserAspectRatioDetailsTest { @Test public void onRadioButtonClicked_prefChange_logMetrics() throws NullPointerException { + doReturn(USER_MIN_ASPECT_RATIO_UNSET).when(mFragment) + .getSelectedUserMinAspectRatio(anyString()); // Default was already selected mRadioButtonPref.setKey(KEY_PREF_DEFAULT); mFragment.onRadioButtonClicked(mRadioButtonPref); @@ -129,4 +155,47 @@ public class UserAspectRatioDetailsTest { any(), anyInt()); } + + @Test + public void onButtonClicked_overrideEnabled_fullscreenPreselected() + throws RemoteException { + doReturn(true).when(mUserAspectRatioManager) + .isOverrideToFullscreenEnabled(anyString(), anyInt()); + doReturn(USER_MIN_ASPECT_RATIO_UNSET).when(mUserAspectRatioManager) + .getUserMinAspectRatioValue(anyString(), anyInt()); + doReturn(mRadioButtonPref).when(mFragment).findPreference(KEY_PREF_DEFAULT); + doReturn(mRadioButtonPref).when(mFragment).findPreference(KEY_PREF_FULLSCREEN); + doReturn(true).when(mUserAspectRatioManager) + .hasAspectRatioOption(anyInt(), anyString()); + + rule.getScenario().onActivity(a -> doReturn(a).when(mFragment).getActivity()); + final Bundle args = new Bundle(); + args.putString(ARG_PACKAGE_NAME, anyString()); + mFragment.setArguments(args); + mFragment.onCreate(Bundle.EMPTY); + + // Fullscreen should be pre-selected + assertEquals(KEY_PREF_FULLSCREEN, mFragment.mSelectedKey); + assertEquals(USER_MIN_ASPECT_RATIO_FULLSCREEN, + mFragment.getSelectedUserMinAspectRatio(mFragment.mSelectedKey)); + + // Revert to app default, should be set to app default from unset + mRadioButtonPref.setKey(KEY_PREF_DEFAULT); + mFragment.onRadioButtonClicked(mRadioButtonPref); + verify(mUserAspectRatioManager).setUserMinAspectRatio( + any(), anyInt(), anyInt()); + assertEquals(USER_MIN_ASPECT_RATIO_APP_DEFAULT, + mFragment.getSelectedUserMinAspectRatio(mFragment.mSelectedKey)); + assertEquals(KEY_PREF_DEFAULT, mFragment.mSelectedKey); + + // Fullscreen override disabled, should be changed to unset from app default + when(mUserAspectRatioManager.isOverrideToFullscreenEnabled(anyString(), anyInt())) + .thenReturn(false); + mFragment.mKeyToAspectRatioMap.clear(); + mFragment.onCreate(Bundle.EMPTY); + + assertEquals(KEY_PREF_DEFAULT, mFragment.mSelectedKey); + assertEquals(USER_MIN_ASPECT_RATIO_UNSET, + mFragment.getSelectedUserMinAspectRatio(mFragment.mSelectedKey)); + } } diff --git a/tests/spa_unit/AndroidManifest.xml b/tests/spa_unit/AndroidManifest.xml index 5a7f5659ce6..51ac1b740e9 100644 --- a/tests/spa_unit/AndroidManifest.xml +++ b/tests/spa_unit/AndroidManifest.xml @@ -22,6 +22,8 @@ + + mLauncherActivities; @Before - public void setUp() { + public void setUp() throws RemoteException, PackageManager.NameNotFoundException { mContext = spy(ApplicationProvider.getApplicationContext()); mResources = mock(Resources.class); - mLauncherApps = mock(LauncherApps.class); + final LauncherApps launcherApps = mock(LauncherApps.class); mLauncherActivities = mock(List.class); + mIPm = mock(IPackageManager.class); + mPm = mock(PackageManager.class); + when(mContext.getPackageManager()).thenReturn(mPm); when(mContext.getResources()).thenReturn(mResources); - when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps); + when(mContext.getSystemService(LauncherApps.class)).thenReturn(launcherApps); enableAllDefaultAspectRatioOptions(); + mSetFlagsRule.disableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT); - mUtils = new UserAspectRatioManager(mContext); + mUtils = new FakeUserAspectRatioManager(mContext, mIPm); - doReturn(mLauncherActivities).when(mLauncherApps).getActivityList(anyString(), any()); + doReturn(mLauncherActivities).when(launcherApps).getActivityList(anyString(), any()); mOriginalSettingsFlag = DeviceConfig.getProperty( DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS); @@ -102,6 +117,10 @@ public class UserAspectRatioManagerTest { DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN); setAspectRatioFullscreenBuildTimeFlagEnabled(true); setAspectRatioFullscreenDeviceConfigEnabled("true" /* enabled */, false /* makeDefault */); + + mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE, true); + mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, true); + mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, true); } @After @@ -114,13 +133,13 @@ public class UserAspectRatioManagerTest { @Test public void testCanDisplayAspectRatioUi() { final ApplicationInfo canDisplay = new ApplicationInfo(); - canDisplay.packageName = "com.app.candisplay"; + canDisplay.packageName = mPackageName; doReturn(false).when(mLauncherActivities).isEmpty(); assertTrue(mUtils.canDisplayAspectRatioUi(canDisplay)); final ApplicationInfo noLauncherEntry = new ApplicationInfo(); - noLauncherEntry.packageName = "com.app.nolauncherentry"; + noLauncherEntry.packageName = mPackageName; doReturn(true).when(mLauncherActivities).isEmpty(); assertFalse(mUtils.canDisplayAspectRatioUi(noLauncherEntry)); @@ -221,36 +240,40 @@ public class UserAspectRatioManagerTest { mPackageName)); } + private String getUserMinAspectRatioEntry(int aspectRatio, String packageName) { + return mUtils.getUserMinAspectRatioEntry(aspectRatio, packageName, mContext.getUserId()); + } + @Test public void testGetUserMinAspectRatioEntry() { final Context context = ApplicationProvider.getApplicationContext(); // R.string.user_aspect_ratio_app_default final String appDefault = ResourcesUtils.getResourcesString(context, "user_aspect_ratio_app_default"); - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) .isEqualTo(appDefault); // should always return default if value does not correspond to anything - assertThat(mUtils.getUserMinAspectRatioEntry(-1, mPackageName)) + assertThat(getUserMinAspectRatioEntry(-1, mPackageName)) .isEqualTo(appDefault); // R.string.user_aspect_ratio_half_screen - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN, + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN, mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_half_screen")); // R.string.user_aspect_ratio_display_size - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_DISPLAY_SIZE, + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_DISPLAY_SIZE, mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_device_size")); // R.string.user_aspect_ratio_16_9 - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_16_9, mPackageName)) + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_16_9, mPackageName)) .isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_16_9")); // R.string.user_aspect_ratio_4_3 - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_4_3, mPackageName)) + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_4_3, mPackageName)) .isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_4_3")); // R.string.user_aspect_ratio_3_2 - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_3_2, mPackageName)) + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_3_2, mPackageName)) .isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_3_2")); // R.string.user_aspect_ratio_fullscreen - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_fullscreen")); } @@ -258,7 +281,7 @@ public class UserAspectRatioManagerTest { @Test public void testGetUserMinAspectRatioEntry_fullscreenDisabled_shouldReturnDefault() { setAspectRatioFullscreenBuildTimeFlagEnabled(false); - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName)).isEqualTo(ResourcesUtils.getResourcesString( ApplicationProvider.getApplicationContext(), "user_aspect_ratio_app_default")); @@ -270,9 +293,9 @@ public class UserAspectRatioManagerTest { when(mResources.getIntArray(anyInt())).thenReturn(new int[] {USER_MIN_ASPECT_RATIO_UNSET}); when(mResources.getStringArray(anyInt())).thenReturn(new String[] {newOptionName}); - mUtils = new UserAspectRatioManager(mContext); + mUtils = new FakeUserAspectRatioManager(mContext, mIPm); - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) .isEqualTo(newOptionName); } @@ -282,7 +305,7 @@ public class UserAspectRatioManagerTest { when(mResources.getIntArray(anyInt())).thenReturn(new int[] {USER_MIN_ASPECT_RATIO_4_3}); when(mResources.getStringArray(anyInt())).thenReturn(new String[] {"4:3"}); - assertThrows(RuntimeException.class, () -> new UserAspectRatioManager(mContext)); + assertThrows(RuntimeException.class, () -> new FakeUserAspectRatioManager(mContext, mIPm)); } @Test @@ -292,7 +315,88 @@ public class UserAspectRatioManagerTest { USER_MIN_ASPECT_RATIO_4_3}); when(mResources.getStringArray(anyInt())).thenReturn(new String[] {"4:3"}); - assertThrows(RuntimeException.class, () -> new UserAspectRatioManager(mContext)); + assertThrows(RuntimeException.class, () -> new FakeUserAspectRatioManager(mContext, mIPm)); + } + + @Test + public void testGetUserMinAspectRatioMapping_appDefaultFlagEnabled() { + // Flag is disabled by default, app default not loaded + assertFalse(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName)); + + mSetFlagsRule.enableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT); + mUtils = new FakeUserAspectRatioManager(mContext, mIPm); + + assertTrue(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName)); + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName)) + .isEqualTo(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)); + } + + @Test + public void testGetUserMinAspectRatioEntry_enabledFullscreenOverride_returnsFullscreen() { + setIsOverrideToFullscreenEnabled(true); + // Fullscreen option is pre-selected + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) + .isEqualTo(ResourcesUtils.getResourcesString( + ApplicationProvider.getApplicationContext(), + "user_aspect_ratio_fullscreen")); + + // App default exists + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName)) + .isEqualTo(ResourcesUtils.getResourcesString( + ApplicationProvider.getApplicationContext(), + "user_aspect_ratio_app_default")); + } + + @Test + public void testGetUserMinAspectRatioEntry_disabledFullscreenOverride_returnsUnchanged() { + setIsOverrideToFullscreenEnabled(false); + // Fullscreen option is not pre-selected + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) + .isEqualTo(ResourcesUtils.getResourcesString( + ApplicationProvider.getApplicationContext(), + "user_aspect_ratio_app_default")); + } + + @Test + public void testIsOverrideToFullscreenEnabled_returnsTrue() + throws PackageManager.NameNotFoundException { + setIsOverrideToFullscreenEnabled(true); + assertTrue(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId())); + + mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, true); + assertTrue(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId())); + } + + @Test + public void testIsOverrideToFullscreenEnabled_optOut_returnsFalse() + throws PackageManager.NameNotFoundException { + setIsOverrideToFullscreenEnabled(true); + mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, false); + assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId())); + } + + @Test + public void testIsOverrideToFullscreenEnabled_flagDisabled_returnsFalse() { + mUtils.setFullscreenCompatChange(true); + assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId())); + } + + @Test + public void testIsOverrideToFullscreenEnabled_optionDisabled_returnsFalse() { + mUtils.setFullscreenCompatChange(true); + when(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName)) + .thenReturn(false); + assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId())); + } + + private void setIsOverrideToFullscreenEnabled(boolean enabled) { + if (enabled) { + mSetFlagsRule.enableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT); + mUtils = new FakeUserAspectRatioManager(mContext, mIPm); + } + mUtils.setFullscreenCompatChange(enabled); + when(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName)) + .thenReturn(enabled); } private void enableAllDefaultAspectRatioOptions() { @@ -328,9 +432,7 @@ public class UserAspectRatioManagerTest { throws PackageManager.NameNotFoundException { PackageManager.Property prop = new PackageManager.Property( propertyName, value, mPackageName, "" /* className */); - PackageManager pm = mock(PackageManager.class); - when(mContext.getPackageManager()).thenReturn(pm); - when(pm.getProperty(propertyName, mPackageName)).thenReturn(prop); + when(mPm.getProperty(propertyName, mPackageName)).thenReturn(prop); } private void setAspectRatioSettingsBuildTimeFlagEnabled(boolean enabled) { @@ -352,4 +454,21 @@ public class UserAspectRatioManagerTest { DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN, enabled, makeDefault); } + + private static class FakeUserAspectRatioManager extends UserAspectRatioManager { + private boolean mFullscreenCompatChange = false; + + private FakeUserAspectRatioManager(@NonNull Context context, IPackageManager pm) { + super(context, pm); + } + + @Override + boolean isFullscreenCompatChangeEnabled(String pkgName, int userId) { + return mFullscreenCompatChange; + } + + void setFullscreenCompatChange(boolean enabled) { + mFullscreenCompatChange = enabled; + } + } }