Merge "[3/n] Add aspect ratio app info page" into udc-qpr-dev am: 386b02e906

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/23819685

Change-Id: I5f187502bc04d21629bdc1b434f74497f9def14a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Graciela Putri
2023-07-28 18:21:45 +00:00
committed by Automerger Merge Worker
13 changed files with 735 additions and 22 deletions

View File

@@ -611,23 +611,24 @@
<item>3</item>
</integer-array>
<!-- TODO(b/287448187): add USER_MIN_ASPECT_RATIO_DISPLAY_SIZE entry -->
<!-- App screen size settings screen, User aspect ratio override options. Must be the same
<!-- App aspect ratio settings screen, user aspect ratio override options. Must be the same
length and order as config_userAspectRatioOverrideValues below. -->
<string-array name="config_userAspectRatioOverrideEntries" translatable="false">
<item>@string/user_aspect_ratio_app_default</item>
<item>@string/user_aspect_ratio_half_screen</item>
<item>@string/user_aspect_ratio_device_size</item>
<item>@string/user_aspect_ratio_16_9</item>
<item>@string/user_aspect_ratio_4_3</item>
<item>@string/user_aspect_ratio_3_2</item>
</string-array>
<!-- App screen size settings screen, User aspect ratio override options. Must be the same
<!-- App aspect ratio settings screen, user aspect ratio override options. Must be the same
length and order as config_userAspectRatioOverrideEntries above. The values must
correspond to PackageManager.UserMinAspectRatio -->
<integer-array name="config_userAspectRatioOverrideValues" translatable="false">
<item>0</item> <!-- USER_MIN_ASPECT_RATIO_UNSET -->
<item>1</item> <!-- USER_MIN_ASPECT_RATIO_SPLIT_SCREEN -->
<item>2</item> <!-- USER_MIN_ASPECT_RATIO_DISPLAY_SIZE -->
<item>4</item> <!-- USER_MIN_ASPECT_RATIO_16_9 -->
<item>3</item> <!-- USER_MIN_ASPECT_RATIO_4_3 -->
<item>5</item> <!-- USER_MIN_ASPECT_RATIO_3_2 -->

View File

@@ -12081,25 +12081,30 @@
other {Apps installed more than # months ago}
}</string>
<!-- App Screen Size (User Aspect Ratio Override) -->
<!-- [CHAR LIMIT=60] Screen size app list title setting to choose aspect ratio -->
<string name="screen_size_title">Screen size</string>
<!-- [CHAR LIMIT=NONE] Screen size setting summary to choose aspect ratio for apps unoptimized for device -->
<string name="screen_size_summary">Choose an aspect ratio for apps if they havent been optimized for your <xliff:g id="device_name">%1$s</xliff:g></string>
<!-- App Aspect Ratio (User Aspect Ratio Override) -->
<!-- [CHAR LIMIT=60] Aspect ratio title setting to choose app aspect ratio -->
<string name="aspect_ratio_title">Aspect ratio</string>
<!-- [CHAR LIMIT=NONE] Aspect ratio setting summary to choose aspect ratio for apps unoptimized for device -->
<string name="aspect_ratio_summary">Choose an aspect ratio to view this app if it hasn\'t been designed to fit your <xliff:g id="device_name">%1$s</xliff:g></string>
<!-- [CHAR LIMIT=NONE] Aspect ratio suggested apps filter label -->
<string name="user_aspect_ratio_suggested_apps_label">Suggested apps</string>
<!-- [CHAR LIMIT=NONE] Filter label for apps that have user aspect ratio override applied -->
<string name="user_aspect_ratio_overridden_apps_label">Apps you have overridden</string>
<!-- [CHAR LIMIT=NONE] App default aspect ratio entry -->
<string name="user_aspect_ratio_app_default">App default</string>
<!-- [CHAR LIMIT=NONE] Half-screen aspect ratio entry -->
<string name="user_aspect_ratio_half_screen">Half-screen</string>
<!-- [CHAR LIMIT=NONE] Half screen aspect ratio entry -->
<string name="user_aspect_ratio_half_screen">Half screen</string>
<!-- [CHAR LIMIT=NONE] Device display size aspect ratio entry -->
<string name="user_aspect_ratio_device_size">Device aspect ratio</string>
<!-- [CHAR LIMIT=NONE] 16:9 aspect ratio entry -->
<string name="user_aspect_ratio_16_9">16:9</string>
<!-- [CHAR LIMIT=NONE] 3:2 aspect ratio entry -->
<string name="user_aspect_ratio_3_2">3:2</string>
<!-- [CHAR LIMIT=NONE] 4:3 aspect ratio entry -->
<string name="user_aspect_ratio_4_3">4:3</string>
<!-- [CHAR LIMIT=NONE] Warning description for app info aspect ratio page -->
<string name="app_aspect_ratio_footer">The app will restart when you change aspect ratio. You may lose unsaved changes.</string>
<!-- Accessibility label for fingerprint sensor [CHAR LIMIT=NONE] -->
<string name="accessibility_fingerprint_label">Fingerprint sensor</string>

View File

@@ -81,7 +81,7 @@
<Preference
android:key="aspect_ratio_apps"
android:title="@string/screen_size_title"
android:title="@string/aspect_ratio_title"
android:summary="@string/summary_placeholder"
android:order="14"
settings:controller="com.android.settings.applications.appcompat.UserAspectRatioAppsPreferenceController"

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/aspect_ratio_title">
<com.android.settingslib.widget.ActionButtonsPreference
android:key="header_view" />
<com.android.settingslib.widget.SelectorWithWidgetPreference
android:key="app_default_pref"
android:title="@string/user_aspect_ratio_app_default"/>
<com.android.settingslib.widget.SelectorWithWidgetPreference
android:key="half_screen_pref"
android:title="@string/user_aspect_ratio_half_screen"/>
<com.android.settingslib.widget.SelectorWithWidgetPreference
android:key="display_size_pref"
android:title="@string/user_aspect_ratio_device_size"/>
<com.android.settingslib.widget.SelectorWithWidgetPreference
android:key="16_9_pref"
android:title="@string/user_aspect_ratio_16_9"/>
<com.android.settingslib.widget.SelectorWithWidgetPreference
android:key="4_3_pref"
android:title="@string/user_aspect_ratio_4_3"/>
<com.android.settingslib.widget.SelectorWithWidgetPreference
android:key="3_2_pref"
android:title="@string/user_aspect_ratio_3_2"/>
<com.android.settingslib.widget.FooterPreference
android:title="@string/app_aspect_ratio_footer"
android:selectable="false"
settings:searchable="false"/>
</PreferenceScreen>

View File

@@ -43,6 +43,6 @@ public class UserAspectRatioAppsPreferenceController extends BasePreferenceContr
@Override
public CharSequence getSummary() {
return mContext.getResources().getString(R.string.screen_size_summary, Build.MODEL);
return mContext.getResources().getString(R.string.aspect_ratio_summary, Build.MODEL);
}
}

View File

@@ -0,0 +1,217 @@
/*
* Copyright (C) 2023 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.settings.applications.appcompat;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
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_DISPLAY_SIZE;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.applications.AppInfoWithHeader;
import com.android.settingslib.widget.ActionButtonsPreference;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import java.util.ArrayList;
import java.util.List;
/**
* App specific activity to show aspect ratio overrides
*/
public class UserAspectRatioDetails extends AppInfoWithHeader implements
SelectorWithWidgetPreference.OnClickListener {
private static final String TAG = UserAspectRatioDetails.class.getSimpleName();
private static final String KEY_HEADER_BUTTONS = "header_view";
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_DEFAULT = "app_default_pref";
@VisibleForTesting
static final String KEY_PREF_3_2 = "3_2_pref";
private final List<SelectorWithWidgetPreference> mAspectRatioPreferences = new ArrayList<>();
@NonNull private UserAspectRatioManager mUserAspectRatioManager;
@NonNull private String mSelectedKey = KEY_PREF_DEFAULT;
@Override
public void onCreate(@NonNull Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUserAspectRatioManager = new UserAspectRatioManager(getContext());
initPreferences();
try {
final int userAspectRatio = mUserAspectRatioManager
.getUserMinAspectRatioValue(mPackageName, mUserId);
mSelectedKey = getSelectedKey(userAspectRatio);
} catch (RemoteException e) {
Log.e(TAG, "Unable to get user min aspect ratio");
}
refreshUi();
}
@Override
public void onRadioButtonClicked(@NonNull SelectorWithWidgetPreference selected) {
final String selectedKey = selected.getKey();
if (mSelectedKey.equals(selectedKey)) {
return;
}
final int userAspectRatio = getSelectedUserMinAspectRatio(selectedKey);
try {
getAspectRatioManager().setUserMinAspectRatio(mPackageName, mUserId, userAspectRatio);
} catch (RemoteException e) {
Log.e(TAG, "Unable to set user min aspect ratio");
return;
}
// Only update to selected aspect ratio if nothing goes wrong
mSelectedKey = selectedKey;
updateAllPreferences(mSelectedKey);
Log.d(TAG, "Killing application process " + mPackageName);
try {
final IActivityManager am = ActivityManager.getService();
am.stopAppForUser(mPackageName, mUserId);
} catch (RemoteException e) {
Log.e(TAG, "Unable to stop application " + mPackageName);
}
}
@Override
public int getMetricsCategory() {
// TODO(b/292566895): add metrics for logging
return 0;
}
@Override
protected boolean refreshUi() {
if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
return false;
}
updateAllPreferences(mSelectedKey);
return true;
}
@Override
protected AlertDialog createDialog(int id, int errorCode) {
return null;
}
private void launchApplication() {
Intent launchIntent = mPm.getLaunchIntentForPackage(mPackageName)
.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
if (launchIntent != null) {
getContext().startActivityAsUser(launchIntent, new UserHandle(mUserId));
}
}
@PackageManager.UserMinAspectRatio
private int getSelectedUserMinAspectRatio(@NonNull String selectedKey) {
switch (selectedKey) {
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;
}
}
@NonNull
private String getSelectedKey(@PackageManager.UserMinAspectRatio int userMinAspectRatio) {
switch (userMinAspectRatio) {
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;
}
}
private void initPreferences() {
addPreferencesFromResource(R.xml.user_aspect_ratio_details);
((ActionButtonsPreference) findPreference(KEY_HEADER_BUTTONS))
.setButton1Text(R.string.launch_instant_app)
.setButton1Icon(R.drawable.ic_settings_open)
.setButton1OnClickListener(v -> launchApplication());
addPreference(KEY_PREF_DEFAULT, USER_MIN_ASPECT_RATIO_UNSET);
addPreference(KEY_PREF_DISPLAY_SIZE, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
addPreference(KEY_PREF_HALF_SCREEN, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
addPreference(KEY_PREF_16_9, USER_MIN_ASPECT_RATIO_16_9);
addPreference(KEY_PREF_4_3, USER_MIN_ASPECT_RATIO_4_3);
addPreference(KEY_PREF_3_2, USER_MIN_ASPECT_RATIO_3_2);
}
private void addPreference(@NonNull String key,
@PackageManager.UserMinAspectRatio int aspectRatio) {
final SelectorWithWidgetPreference pref = findPreference(key);
if (pref == null) {
return;
}
if (!mUserAspectRatioManager.containsAspectRatioOption(aspectRatio)) {
pref.setVisible(false);
return;
}
pref.setTitle(mUserAspectRatioManager.getUserMinAspectRatioEntry(aspectRatio));
pref.setOnClickListener(this);
mAspectRatioPreferences.add(pref);
}
private void updateAllPreferences(@NonNull String selectedKey) {
for (SelectorWithWidgetPreference pref : mAspectRatioPreferences) {
pref.setChecked(selectedKey.equals(pref.getKey()));
}
}
@VisibleForTesting
UserAspectRatioManager getAspectRatioManager() {
return mUserAspectRatioManager;
}
}

View File

@@ -28,6 +28,7 @@ import android.provider.DeviceConfig;
import android.util.ArrayMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settings.R;
@@ -91,6 +92,32 @@ public class UserAspectRatioManager {
aspectRatio, mContext.getString(R.string.user_aspect_ratio_app_default));
}
/**
* @return corresponding aspect ratio string for package name and user
*/
@NonNull
public String getUserMinAspectRatioEntry(@NonNull String packageName, int uid)
throws RemoteException {
final int aspectRatio = getUserMinAspectRatioValue(packageName, uid);
return getUserMinAspectRatioEntry(aspectRatio);
}
/**
* Whether user aspect ratio option is specified in
* {@link R.array.config_userAspectRatioOverrideValues}
*/
public boolean containsAspectRatioOption(@PackageManager.UserMinAspectRatio int option) {
return mUserAspectRatioMap.containsKey(option);
}
/**
* Sets user-specified {@link PackageManager.UserMinAspectRatio} override for an app
*/
public void setUserMinAspectRatio(@NonNull String packageName, int uid,
@PackageManager.UserMinAspectRatio int aspectRatio) throws RemoteException {
mIPm.setUserMinAspectRatio(packageName, uid, aspectRatio);
}
/**
* Whether an app's aspect ratio can be overridden by user. Only apps with launcher entry
* will be overridable.
@@ -122,14 +149,17 @@ public class UserAspectRatioManager {
final Map<Integer, String> userMinAspectRatioMap = new ArrayMap<>();
for (int i = 0; i < userMinAspectRatioValues.length; i++) {
final int aspectRatioVal = userMinAspectRatioValues[i];
final String aspectRatioString = getAspectRatioStringOrDefault(
userMinAspectRatioStrings[i], aspectRatioVal);
switch (aspectRatioVal) {
// Only map known values of UserMinAspectRatio and ignore unknown entries
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:
userMinAspectRatioMap.put(aspectRatioVal, userMinAspectRatioStrings[i]);
userMinAspectRatioMap.put(aspectRatioVal, aspectRatioString);
}
}
if (!userMinAspectRatioMap.containsKey(PackageManager.USER_MIN_ASPECT_RATIO_UNSET)) {
@@ -139,6 +169,29 @@ public class UserAspectRatioManager {
return userMinAspectRatioMap;
}
@NonNull
private String getAspectRatioStringOrDefault(@Nullable String aspectRatioString,
@PackageManager.UserMinAspectRatio int aspectRatioVal) {
if (aspectRatioString != null) {
return aspectRatioString;
}
// Options are customized per device and if strings are set to @null, use default
switch (aspectRatioVal) {
case PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN:
return mContext.getString(R.string.user_aspect_ratio_half_screen);
case PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE:
return mContext.getString(R.string.user_aspect_ratio_device_size);
case PackageManager.USER_MIN_ASPECT_RATIO_4_3:
return mContext.getString(R.string.user_aspect_ratio_4_3);
case PackageManager.USER_MIN_ASPECT_RATIO_16_9:
return mContext.getString(R.string.user_aspect_ratio_16_9);
case PackageManager.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);
}
}
@VisibleForTesting
void addInfoHasLauncherEntry(@NonNull ResolveInfo infoHasLauncherEntry) {
mInfoHasLauncherEntryList.add(infoHasLauncherEntry);

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2023 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.settings.spa.app.appcompat
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.applications.appcompat.UserAspectRatioDetails
import com.android.settings.applications.appcompat.UserAspectRatioManager
import com.android.settings.applications.appinfo.AppInfoDashboardFragment
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
fun UserAspectRatioAppPreference(app: ApplicationInfo) {
val context = LocalContext.current
val presenter = remember { UserAspectRatioAppPresenter(context, app) }
if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
Preference(object : PreferenceModel {
override val title = stringResource(R.string.aspect_ratio_title)
override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
initialValue = stringResource(R.string.summary_placeholder),
)
override val onClick = presenter::startActivity
})
}
class UserAspectRatioAppPresenter(
private val context: Context,
private val app: ApplicationInfo,
) {
private val manager = UserAspectRatioManager(context)
val isAvailableFlow = flow {
emit(UserAspectRatioManager.isFeatureEnabled(context)
&& manager.canDisplayAspectRatioUi(app))
}.flowOn(Dispatchers.IO)
fun startActivity() =
navigateToAppAspectRatioSettings(context, app)
val summaryFlow = flow {
emit(manager.getUserMinAspectRatioEntry(app.packageName, context.userId))
}.flowOn(Dispatchers.IO)
}
fun navigateToAppAspectRatioSettings(context: Context, app: ApplicationInfo) {
AppInfoDashboardFragment.startAppInfoFragment(
UserAspectRatioDetails::class.java,
app,
context,
AppInfoSettingsProvider.METRICS_CATEGORY,
)
}

View File

@@ -36,7 +36,6 @@ import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.applications.appcompat.UserAspectRatioManager
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
@@ -81,7 +80,7 @@ object UserAspectRatioAppsPageProvider : SettingsPageProvider {
@VisibleForTesting
fun EntryItem() =
Preference(object : PreferenceModel {
override val title = stringResource(R.string.screen_size_title)
override val title = stringResource(R.string.aspect_ratio_title)
override val summary = getSummary().toState()
override val onClick = navigator(name)
})
@@ -94,7 +93,7 @@ object UserAspectRatioAppsPageProvider : SettingsPageProvider {
@Composable
@VisibleForTesting
fun getSummary(): String = stringResource(R.string.screen_size_summary, Build.MODEL)
fun getSummary(): String = stringResource(R.string.aspect_ratio_summary, Build.MODEL)
}
@Composable
@@ -103,7 +102,7 @@ fun UserAspectRatioAppList(
= { AppList() },
) {
AppListPage(
title = stringResource(R.string.screen_size_title),
title = stringResource(R.string.aspect_ratio_title),
listModel = rememberContext(::UserAspectRatioAppListModel),
appList = appList,
header = {
@@ -148,7 +147,7 @@ class UserAspectRatioAppListModel(private val context: Context)
override fun AppListItemModel<UserAspectRatioAppListItemModel>.AppItem() {
val app = record.app
AppListItem(
onClick = AppInfoSettingsProvider.navigator(app)
onClick = { navigateToAppAspectRatioSettings(context, app) }
)
}

View File

@@ -35,6 +35,7 @@ import com.android.settings.R
import com.android.settings.applications.AppInfoBase
import com.android.settings.applications.appinfo.AppInfoDashboardFragment
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.app.appcompat.UserAspectRatioAppPreference
import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider
import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
@@ -150,6 +151,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) {
}
Category(title = stringResource(R.string.advanced_apps)) {
UserAspectRatioAppPreference(app)
DisplayOverOtherAppsAppListProvider.InfoPageEntryItem(app)
ModifySystemSettingsAppListProvider.InfoPageEntryItem(app)
PictureInPictureListProvider.InfoPageEntryItem(app)

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C) 2023 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.settings.applications.appcompat;
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 org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.IActivityManager;
import android.content.Context;
import android.os.RemoteException;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.testutils.shadow.ShadowActivityManager;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
/**
* To run test: atest SettingsRoboTests:UserAspectRatioDetailsTest
*/
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowActivityManager.class})
public class UserAspectRatioDetailsTest {
@Mock
private UserAspectRatioManager mUserAspectRatioManager;
@Mock
private IActivityManager mAm;
private SelectorWithWidgetPreference mRadioButtonPref;
private Context mContext;
private UserAspectRatioDetails mFragment;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
mFragment = spy(new UserAspectRatioDetails());
when(mFragment.getContext()).thenReturn(mContext);
when(mFragment.getAspectRatioManager()).thenReturn(mUserAspectRatioManager);
ShadowActivityManager.setService(mAm);
mRadioButtonPref = new SelectorWithWidgetPreference(mContext);
}
@Test
public void onRadioButtonClicked_prefChange_shouldStopActivity() throws RemoteException {
// Default was already selected
mRadioButtonPref.setKey(KEY_PREF_DEFAULT);
mFragment.onRadioButtonClicked(mRadioButtonPref);
// Preference changed
mRadioButtonPref.setKey(KEY_PREF_3_2);
mFragment.onRadioButtonClicked(mRadioButtonPref);
// Only triggered once when preference change
verify(mAm).stopAppForUser(any(), anyInt());
}
@Test
public void onRadioButtonClicked_prefChange_shouldSetAspectRatio() throws RemoteException {
// Default was already selected
mRadioButtonPref.setKey(KEY_PREF_DEFAULT);
mFragment.onRadioButtonClicked(mRadioButtonPref);
// Preference changed
mRadioButtonPref.setKey(KEY_PREF_3_2);
mFragment.onRadioButtonClicked(mRadioButtonPref);
// Only triggered once when preference changes
verify(mUserAspectRatioManager).setUserMinAspectRatio(
any(), anyInt(), anyInt());
}
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright (C) 2023 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.settings.spa.app.appcompat
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.os.Build
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasTextExactly
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER
import com.android.settings.R
import com.android.settings.applications.appinfo.AppInfoDashboardFragment
import com.android.settings.applications.appcompat.UserAspectRatioDetails
import com.android.settings.applications.appcompat.UserAspectRatioManager
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
import com.android.settings.testutils.TestDeviceConfig
import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spa.testutils.waitUntilExists
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever
/**
* To run this test: atest SettingsSpaUnitTests:UserAspectRatioAppPreferenceTest
*/
@RunWith(AndroidJUnit4::class)
class UserAspectRatioAppPreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private lateinit var mockSession: MockitoSession
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Spy
private val resources = context.resources
private val aspectRatioEnabledConfig =
TestDeviceConfig(NAMESPACE_WINDOW_MANAGER, "enable_app_compat_user_aspect_ratio_settings")
private lateinit var userAspectRatioManager: UserAspectRatioManager
@Mock
private lateinit var packageManager: PackageManager
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
.initMocks(this)
.mockStatic(UserAspectRatioDetails::class.java)
.mockStatic(AppInfoDashboardFragment::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(context.resources).thenReturn(resources)
whenever(context.packageManager).thenReturn(packageManager)
userAspectRatioManager = mock(UserAspectRatioManager::class.java)
}
@After
fun tearDown() {
aspectRatioEnabledConfig.reset()
mockSession.finishMocking()
}
@Test
fun whenConfigIsFalse_notDisplayed() {
setConfig(false)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenCannotDisplayAspectRatioUi_notDisplayed() {
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenCanDisplayAspectRatioUiAndConfigFalse_notDisplayed() {
setConfig(false)
whenever(packageManager.queryIntentActivities(any(), anyInt()))
.thenReturn(listOf(RESOLVE_INFO))
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenCannotDisplayAspectRatioUiAndConfigTrue_notDisplayed() {
setConfig(true)
setContent()
composeTestRule.onRoot().assertIsNotDisplayed()
}
@Test
fun whenCanDisplayAspectRatioUiAndConfigTrue_Displayed() {
setConfig(true)
whenever(packageManager.queryIntentActivities(any(), anyInt()))
.thenReturn(listOf(RESOLVE_INFO))
setContent()
composeTestRule.onNode(
hasTextExactly(
context.getString(R.string.aspect_ratio_title),
context.getString(R.string.user_aspect_ratio_app_default)
),
).assertIsDisplayed().assertIsEnabled()
}
@Test
fun onClick_startActivity() {
setConfig(true)
whenever(packageManager.queryIntentActivities(any(), anyInt()))
.thenReturn(listOf(RESOLVE_INFO))
setContent()
composeTestRule.onRoot().performClick()
ExtendedMockito.verify {
AppInfoDashboardFragment.startAppInfoFragment(
UserAspectRatioDetails::class.java,
APP,
context,
AppInfoSettingsProvider.METRICS_CATEGORY,
)
}
}
private fun setConfig(enabled: Boolean) {
whenever(resources.getBoolean(
com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled
)).thenReturn(enabled)
aspectRatioEnabledConfig.override(enabled)
}
private fun setContent() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
UserAspectRatioAppPreference(APP)
}
}
composeTestRule.delay()
}
private companion object {
const val PACKAGE_NAME = "package.name"
const val UID = 123
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
uid = UID
}
private val RESOLVE_INFO = ResolveInfo().apply {
activityInfo = ActivityInfo().apply {
packageName = PACKAGE_NAME
}
}
}
}

View File

@@ -60,21 +60,21 @@ class UserAspectRatioAppsPageProviderTest {
@Test
fun injectEntry_title() {
setInjectEntry()
composeTestRule.onNodeWithText(context.getString(R.string.screen_size_title))
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_title))
.assertIsDisplayed()
}
@Test
fun injectEntry_summary() {
setInjectEntry()
composeTestRule.onNodeWithText(context.getString(R.string.screen_size_summary, Build.MODEL))
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_summary, Build.MODEL))
.assertIsDisplayed()
}
@Test
fun injectEntry_onClick_navigate() {
setInjectEntry()
composeTestRule.onNodeWithText(context.getString(R.string.screen_size_title)).performClick()
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_title)).performClick()
assertThat(fakeNavControllerWrapper.navigateCalledWith).isEqualTo("UserAspectRatioAppsPage")
}
@@ -92,7 +92,7 @@ class UserAspectRatioAppsPageProviderTest {
UserAspectRatioAppList {}
}
composeTestRule.onNodeWithText(context.getString(R.string.screen_size_title))
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_title))
.assertIsDisplayed()
}