diff --git a/Android.bp b/Android.bp index d2b8fc75bdc..71e0542f793 100644 --- a/Android.bp +++ b/Android.bp @@ -83,6 +83,7 @@ android_library { "net-utils-framework-common", "app-usage-event-protos-lite", "battery-event-protos-lite", + "power-anomaly-event-protos-lite", "settings-contextual-card-protos-lite", "settings-log-bridge-protos-lite", "settings-telephony-protos-lite", diff --git a/res/values/config.xml b/res/values/config.xml index 432b1cad01b..687fa1596bc 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -611,23 +611,26 @@ 3 - - @string/user_aspect_ratio_app_default + @string/user_aspect_ratio_fullscreen @string/user_aspect_ratio_half_screen + @string/user_aspect_ratio_device_size @string/user_aspect_ratio_16_9 @string/user_aspect_ratio_4_3 @string/user_aspect_ratio_3_2 - 0 + 6 1 + 2 4 3 5 diff --git a/res/values/strings.xml b/res/values/strings.xml index e7b5d138683..ba5f72db554 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -12081,25 +12081,32 @@ other {Apps installed more than # months ago} } - - - Screen size - - Choose an aspect ratio for apps if they haven’t been optimized for your %1$s + + + Aspect ratio + + Choose an aspect ratio to view this app if it hasn\'t been designed to fit your %1$s Suggested apps Apps you have overridden App default - - Half-screen + + Full screen + + Half screen + + Device aspect ratio 16:9 3:2 4:3 + + The app will restart when you change aspect ratio. You may lose unsaved changes. + Fingerprint sensor diff --git a/res/xml/apps.xml b/res/xml/apps.xml index 386a07bde55..73bcbc9b9e7 100644 --- a/res/xml/apps.xml +++ b/res/xml/apps.xml @@ -81,7 +81,7 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioAppsPreferenceController.java b/src/com/android/settings/applications/appcompat/UserAspectRatioAppsPreferenceController.java index 6ec2528da4a..ff68fb015f2 100644 --- a/src/com/android/settings/applications/appcompat/UserAspectRatioAppsPreferenceController.java +++ b/src/com/android/settings/applications/appcompat/UserAspectRatioAppsPreferenceController.java @@ -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); } } diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java new file mode 100644 index 00000000000..f8406f90a50 --- /dev/null +++ b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java @@ -0,0 +1,224 @@ +/* + * 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_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 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_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_DEFAULT = "app_default_pref"; + @VisibleForTesting + static final String KEY_PREF_3_2 = "3_2_pref"; + + private final List 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_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; + } + } + + @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; + } + } + + 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_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); + 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; + } +} diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java index 35bd7a93a1d..c132fd0f602 100644 --- a/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java +++ b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java @@ -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; @@ -49,6 +50,9 @@ public class UserAspectRatioManager { @VisibleForTesting static final String KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS = "enable_app_compat_user_aspect_ratio_settings"; + static final String KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN = + "enable_app_compat_user_aspect_ratio_fullscreen"; + private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN = true; private final Context mContext; private final IPackageManager mIPm; @@ -70,7 +74,8 @@ public class UserAspectRatioManager { public static boolean isFeatureEnabled(Context context) { final boolean isBuildTimeFlagEnabled = context.getResources().getBoolean( com.android.internal.R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled); - return isBuildTimeFlagEnabled && getValueFromDeviceConfig(); + return getValueFromDeviceConfig(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS, + DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS) && isBuildTimeFlagEnabled; } /** @@ -79,7 +84,9 @@ public class UserAspectRatioManager { @PackageManager.UserMinAspectRatio public int getUserMinAspectRatioValue(@NonNull String packageName, int uid) throws RemoteException { - return mIPm.getUserMinAspectRatio(packageName, uid); + final int aspectRatio = mIPm.getUserMinAspectRatio(packageName, uid); + return containsAspectRatioOption(aspectRatio) + ? aspectRatio : PackageManager.USER_MIN_ASPECT_RATIO_UNSET; } /** @@ -87,8 +94,41 @@ public class UserAspectRatioManager { */ @NonNull public String getUserMinAspectRatioEntry(@PackageManager.UserMinAspectRatio int aspectRatio) { - return mUserAspectRatioMap.getOrDefault( - aspectRatio, mContext.getString(R.string.user_aspect_ratio_app_default)); + if (!containsAspectRatioOption(aspectRatio)) { + return mUserAspectRatioMap.get(PackageManager.USER_MIN_ASPECT_RATIO_UNSET); + } + return mUserAspectRatioMap.get(aspectRatio); + } + + /** + * @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} + * and is enabled by device config + */ + public boolean containsAspectRatioOption(@PackageManager.UserMinAspectRatio int option) { + if (option == PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN + && !isFullscreenOptionEnabled()) { + return false; + } + 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); } /** @@ -101,11 +141,20 @@ public class UserAspectRatioManager { return hasLauncherEntry; } - private static boolean getValueFromDeviceConfig() { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_WINDOW_MANAGER, - KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS, - DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS); + /** + * Whether fullscreen option in per-app user aspect ratio settings is enabled + */ + @VisibleForTesting + boolean isFullscreenOptionEnabled() { + final boolean isBuildTimeFlagEnabled = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled); + return isBuildTimeFlagEnabled && getValueFromDeviceConfig( + KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN, + DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN); + } + + private static boolean getValueFromDeviceConfig(String name, boolean defaultValue) { + return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER, name, defaultValue); } @NonNull @@ -122,14 +171,18 @@ public class UserAspectRatioManager { final Map 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_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: - userMinAspectRatioMap.put(aspectRatioVal, userMinAspectRatioStrings[i]); + userMinAspectRatioMap.put(aspectRatioVal, aspectRatioString); } } if (!userMinAspectRatioMap.containsKey(PackageManager.USER_MIN_ASPECT_RATIO_UNSET)) { @@ -139,6 +192,31 @@ 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_FULLSCREEN: + return mContext.getString(R.string.user_aspect_ratio_fullscreen); + 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); diff --git a/src/com/android/settings/dream/WhenToDreamPicker.java b/src/com/android/settings/dream/WhenToDreamPicker.java index 13cdadf1981..3052d20ba71 100644 --- a/src/com/android/settings/dream/WhenToDreamPicker.java +++ b/src/com/android/settings/dream/WhenToDreamPicker.java @@ -50,7 +50,7 @@ public class WhenToDreamPicker extends RadioButtonPickerFragment { @Override public int getMetricsCategory() { - return SettingsEnums.DREAM; + return SettingsEnums.SETTINGS_WHEN_TO_DREAM; } @Override diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java index c06e7f023d9..d345be9c194 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java @@ -18,6 +18,7 @@ package com.android.settings.fuelgauge; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import android.util.ArrayMap; import android.util.SparseIntArray; @@ -129,9 +130,14 @@ public interface PowerUsageFeatureProvider { boolean delayHourlyJobWhenBooting(); /** - * Insert device usage data for anomaly detection + * Insert settings configuration data for anomaly detection */ - void insertSettingsData(Context context); + void insertSettingsData(Context context, double displayDrain); + + /** + * Returns {@link Bundle} for settings anomaly detection result + */ + Bundle detectSettingsAnomaly(Context context, double displayDrain); /** * Gets an intent for one time bypass charge limited to resume charging. diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java index 89d793a9e92..e4c6fd9dc70 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java @@ -21,6 +21,7 @@ import static com.android.settings.Utils.SYSTEMUI_PACKAGE_NAME; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.os.Bundle; import android.os.Process; import android.util.ArrayMap; import android.util.ArraySet; @@ -161,7 +162,12 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider } @Override - public void insertSettingsData(Context context) {} + public void insertSettingsData(Context context, double displayDrain) {} + + @Override + public Bundle detectSettingsAnomaly(Context context, double displayDrain) { + return null; + } @Override public Set getOthersSystemComponentSet() { diff --git a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java index 254cf046bb8..e08f4ba56db 100644 --- a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java +++ b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java @@ -18,6 +18,7 @@ package com.android.settings.fuelgauge; import android.content.ComponentName; import android.content.Context; +import android.os.BatteryManager; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -139,7 +140,10 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle if (Utils.containsIncompatibleChargers(mContext, TAG)) { return mContext.getString(R.string.battery_info_status_not_charging); } - if (!info.discharging && info.chargeLabel != null) { + if (info.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING) { + // Present status only if no remaining time or status anomalous + return info.statusLabel; + } else if (!info.discharging && info.chargeLabel != null) { return info.chargeLabel; } else if (info.remainingLabel == null) { return info.batteryPercentString; diff --git a/src/com/android/settings/fuelgauge/protos/Android.bp b/src/com/android/settings/fuelgauge/protos/Android.bp index 3af2aef0796..1f3cdd98b02 100644 --- a/src/com/android/settings/fuelgauge/protos/Android.bp +++ b/src/com/android/settings/fuelgauge/protos/Android.bp @@ -30,3 +30,11 @@ java_library { }, srcs: ["fuelgauge_usage_state.proto"], } + +java_library { + name: "power-anomaly-event-protos-lite", + proto: { + type: "lite", + }, + srcs: ["power_anomaly_event.proto"], +} diff --git a/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto b/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto new file mode 100644 index 00000000000..b4277c4bcc6 --- /dev/null +++ b/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto @@ -0,0 +1,37 @@ +syntax = "proto2"; + +option java_multiple_files = true; +option java_package = "com.android.settings.fuelgauge.batteryusage"; +option java_outer_classname = "PowerAnomalyEventProto"; + +message PowerAnomalyEvent { + optional int64 timestamp = 1; + optional string type = 2; // e.g. settings, apps + optional string key = 3; // e.g. brightness, significant_increase + optional float score = 4; + oneof info { + WarningBannerInfo warning_banner_info = 5; + WarningItemInfo warning_item_info = 6; + } +} + +message WarningBannerInfo { + optional string title_string = 1; + optional string description_string = 2; + optional string main_button_string = 3; + optional string main_button_action = 4; + optional string cancel_button_string = 5; + optional string cancel_button_action = 6; +} + +message WarningItemInfo { + optional int64 start_timestamp = 1; + optional int64 end_timestamp = 2; + optional string top_card_string = 3; + optional string title_string = 4; + optional string description_string = 5; + optional string main_button_string = 6; + optional string main_button_action = 7; + optional string cancel_button_string = 8; + optional string cancel_button_action = 9; +} diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt new file mode 100644 index 00000000000..3680715705e --- /dev/null +++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreference.kt @@ -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, + ) +} \ No newline at end of file diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt index 34b6b663066..ff904921ec6 100644 --- a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt +++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt @@ -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.AppItem() { val app = record.app AppListItem( - onClick = AppInfoSettingsProvider.navigator(app) + onClick = { navigateToAppAspectRatioSettings(context, app) } ) } diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt index d59a4f72c86..e6df933df1f 100644 --- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt +++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt @@ -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) diff --git a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java new file mode 100644 index 00000000000..31ff76c7d11 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java @@ -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()); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java index b44430973a3..f6bc2975f34 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java @@ -31,6 +31,7 @@ import android.content.Intent; import android.hardware.usb.UsbManager; import android.hardware.usb.UsbPort; import android.hardware.usb.UsbPortStatus; +import android.os.BatteryManager; import androidx.preference.Preference; import androidx.test.core.app.ApplicationProvider; @@ -145,6 +146,17 @@ public class TopLevelBatteryPreferenceControllerTest { .isEqualTo(mContext.getString(R.string.battery_info_status_not_charging)); } + @Test + public void getDashboardLabel_notChargingState_returnsCorrectLabel() { + mController.mPreference = new Preference(mContext); + BatteryInfo info = new BatteryInfo(); + info.batteryStatus = BatteryManager.BATTERY_STATUS_NOT_CHARGING; + info.statusLabel = "expected returned label"; + + assertThat(mController.getDashboardLabel(mContext, info, true)) + .isEqualTo(info.statusLabel); + } + @Test public void getSummary_batteryNotPresent_shouldShowWarningMessage() { mController.mIsBatteryPresent = false; diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreferenceTest.kt new file mode 100644 index 00000000000..342405acf9d --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppPreferenceTest.kt @@ -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 + } + } + } +} \ No newline at end of file diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt index e0eb5b2c86a..0d2869c6cdf 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt @@ -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() } diff --git a/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java b/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java index 36f2f54e2c5..f4dcaf8ff2e 100644 --- a/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java +++ b/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java @@ -19,9 +19,11 @@ package com.android.settings.applications.appcompat; 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_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 com.android.settings.applications.appcompat.UserAspectRatioManager.KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN; import static com.android.settings.applications.appcompat.UserAspectRatioManager.KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS; import static com.google.common.truth.Truth.assertThat; @@ -30,16 +32,19 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.provider.DeviceConfig; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.android.internal.R; import com.android.settings.testutils.ResourcesUtils; import org.junit.After; @@ -54,21 +59,35 @@ import org.junit.runner.RunWith; public class UserAspectRatioManagerTest { private Context mContext; + private Resources mResources; private UserAspectRatioManager mUtils; - private String mOriginalFlag; + private String mOriginalSettingsFlag; + private String mOriginalFullscreenFlag; @Before public void setUp() { mContext = spy(ApplicationProvider.getApplicationContext()); + mResources = spy(mContext.getResources()); mUtils = spy(new UserAspectRatioManager(mContext)); - mOriginalFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, - KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS); + + when(mContext.getResources()).thenReturn(mResources); + + mOriginalSettingsFlag = DeviceConfig.getProperty( + DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS); + setAspectRatioSettingsBuildTimeFlagEnabled(true); + setAspectRatioSettingsDeviceConfigEnabled("true" /* enabled */, false /* makeDefault */); + + mOriginalFullscreenFlag = DeviceConfig.getProperty( + DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN); + setAspectRatioFullscreenBuildTimeFlagEnabled(true); + setAspectRatioFullscreenDeviceConfigEnabled("true" /* enabled */, false /* makeDefault */); } @After public void tearDown() { - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, - KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS, mOriginalFlag, true /* makeDefault */); + setAspectRatioSettingsDeviceConfigEnabled(mOriginalSettingsFlag, true /* makeDefault */); + setAspectRatioFullscreenDeviceConfigEnabled(mOriginalFullscreenFlag, + true /* makeDefault */); } @Test @@ -87,13 +106,52 @@ public class UserAspectRatioManagerTest { @Test public void testIsFeatureEnabled() { - assertFalse(UserAspectRatioManager.isFeatureEnabled(mContext)); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, - KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS, "true", false /* makeDefault */); assertTrue(UserAspectRatioManager.isFeatureEnabled(mContext)); } + @Test + public void testIsFeatureEnabled_disabledBuildTimeFlag_returnFalse() { + setAspectRatioSettingsBuildTimeFlagEnabled(false); + assertFalse(UserAspectRatioManager.isFeatureEnabled(mContext)); + } + + @Test + public void testIsFeatureEnabled_disabledRuntimeFlag_returnFalse() { + setAspectRatioSettingsDeviceConfigEnabled("false" /* enabled */, false /* makeDefault */); + assertFalse(UserAspectRatioManager.isFeatureEnabled(mContext)); + } + + @Test + public void testIsFullscreenOptionEnabled() { + assertTrue(mUtils.isFullscreenOptionEnabled()); + } + + @Test + public void testIsFullscreenOptionEnabled_settingsDisabled_returnFalse() { + setAspectRatioFullscreenBuildTimeFlagEnabled(false); + assertFalse(mUtils.isFullscreenOptionEnabled()); + } + + @Test + public void testIsFullscreenOptionEnabled_disabledBuildTimeFlag_returnFalse() { + setAspectRatioFullscreenBuildTimeFlagEnabled(false); + assertFalse(mUtils.isFullscreenOptionEnabled()); + } + + @Test + public void testIsFullscreenOptionEnabled_disabledRuntimeFlag_returnFalse() { + setAspectRatioFullscreenDeviceConfigEnabled("false" /* enabled */, false /*makeDefault */); + assertFalse(mUtils.isFullscreenOptionEnabled()); + } + + @Test + public void containsAspectRatioOption_fullscreen() { + assertTrue(mUtils.containsAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN)); + + when(mUtils.isFullscreenOptionEnabled()).thenReturn(false); + assertFalse(mUtils.containsAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN)); + } + @Test public void testGetUserMinAspectRatioEntry() { // R.string.user_aspect_ratio_app_default @@ -117,6 +175,38 @@ public class UserAspectRatioManagerTest { // R.string.user_aspect_ratio_16_9 assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_16_9)) .isEqualTo(ResourcesUtils.getResourcesString(mContext, "user_aspect_ratio_16_9")); + // R.string.user_aspect_ratio_fullscreen + assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN)) + .isEqualTo(ResourcesUtils.getResourcesString(mContext, + "user_aspect_ratio_fullscreen")); + } + + @Test + public void testGetUserMinAspectRatioEntry_fullscreenDisabled_shouldReturnDefault() { + setAspectRatioFullscreenBuildTimeFlagEnabled(false); + assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN)) + .isEqualTo(ResourcesUtils.getResourcesString(mContext, + "user_aspect_ratio_app_default")); + } + + private void setAspectRatioSettingsBuildTimeFlagEnabled(boolean enabled) { + when(mResources.getBoolean(R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled)) + .thenReturn(enabled); + } + + private void setAspectRatioSettingsDeviceConfigEnabled(String enabled, boolean makeDefault) { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS, enabled, makeDefault); + } + + private void setAspectRatioFullscreenBuildTimeFlagEnabled(boolean enabled) { + when(mResources.getBoolean(R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled)) + .thenReturn(enabled); + } + + private void setAspectRatioFullscreenDeviceConfigEnabled(String enabled, boolean makeDefault) { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN, enabled, makeDefault); } private void addResolveInfoLauncherEntry(String packageName) {