Merge "Remove TODO which is resolved by changes in ZenModeHelper" into main
diff --git a/aconfig/settings_bluetooth_declarations.aconfig b/aconfig/settings_bluetooth_declarations.aconfig
index 3e771cd..3d4f415 100644
--- a/aconfig/settings_bluetooth_declarations.aconfig
+++ b/aconfig/settings_bluetooth_declarations.aconfig
@@ -13,4 +13,14 @@
namespace: "pixel_cross_device_control"
description: "Gates whether to enable checker for bluetooth profile toggle visibility"
bug: "321178209"
+}
+
+flag {
+ name: "hide_le_audio_toggle_for_le_audio_only_device"
+ namespace: "pixel_cross_device_control"
+ description: "Gates whether to hide LeAudio toggle for LeAudio-only device"
+ bug: "333827147"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/aconfig/settings_flag_declarations.aconfig b/aconfig/settings_flag_declarations.aconfig
index 814f7e1..c7a21e9 100644
--- a/aconfig/settings_flag_declarations.aconfig
+++ b/aconfig/settings_flag_declarations.aconfig
@@ -28,3 +28,17 @@
description: "Feature flag to enable new settings homepage UX."
bug: "321612737"
}
+
+flag {
+ name: "dynamic_injection_category"
+ namespace: "android_settings"
+ description: "Feature flag to enable injection into PreferenceCategory."
+ bug: "333547416"
+}
+
+flag {
+ name: "slices_retirement"
+ namespace: "android_settings"
+ description: "Feature flag to remove relevant slices dependencies."
+ bug: "297367302"
+}
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bf30734..4ee7c0e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4814,6 +4814,8 @@
<!-- Summary for accessibility magnification preference when one finger panning feature is turned off.
The feature when disabled, allows user to move the magnification area by dragging two fingers instead of one. [CHAR LIMIT=60] -->
<string name="accessibility_magnification_one_finger_panning_summary_off">Move the magnification area by dragging two fingers.</string>
+ <!-- Summary for accessibility magnification preference when one finger panning feature is unavailable while partial-only capability. [CHAR LIMIT=none] -->
+ <string name="accessibility_magnification_one_finger_panning_summary_unavailable">Unavailable while only magnifying part of the screen</string>
<!-- Title for the accessibility preference screen to enable navigation bar screen magnification. [CHAR LIMIT=35] -->
<string name="accessibility_screen_magnification_navbar_title">Magnify with shortcut</string>
<!-- Summary for the accessibility magnification setting indicating both "Magnify with button" and "Magnify with triple-tap" are enabled [CHAR LIMIT=50] -->
diff --git a/src/com/android/settings/accessibility/MagnificationOneFingerPanningPreferenceController.java b/src/com/android/settings/accessibility/MagnificationOneFingerPanningPreferenceController.java
index a2ce948..4eb5090 100644
--- a/src/com/android/settings/accessibility/MagnificationOneFingerPanningPreferenceController.java
+++ b/src/com/android/settings/accessibility/MagnificationOneFingerPanningPreferenceController.java
@@ -21,27 +21,43 @@
import android.content.Context;
import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
-import com.android.server.accessibility.Flags;
import com.android.settings.R;
+import com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode;
import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
public class MagnificationOneFingerPanningPreferenceController
- extends TogglePreferenceController {
+ extends TogglePreferenceController implements LifecycleObserver, OnResume, OnPause {
static final String PREF_KEY = Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED;
- @Nullable
private TwoStatePreference mSwitchPreference;
@VisibleForTesting
final boolean mDefaultValue;
+ @VisibleForTesting
+ final ContentObserver mContentObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, @Nullable Uri uri) {
+ updateState(mSwitchPreference);
+ }
+ };
+
public MagnificationOneFingerPanningPreferenceController(Context context) {
super(context, PREF_KEY);
boolean defaultValue;
@@ -55,9 +71,18 @@
}
@Override
+ public void onResume() {
+ MagnificationCapabilities.registerObserver(mContext, mContentObserver);
+ }
+
+ @Override
+ public void onPause() {
+ MagnificationCapabilities.unregisterObserver(mContext, mContentObserver);
+ }
+
+ @Override
public int getAvailabilityStatus() {
- return (Flags.enableMagnificationOneFingerPanningGesture())
- ? AVAILABLE : DISABLED_FOR_USER;
+ return AVAILABLE;
}
@Override
@@ -73,14 +98,17 @@
var toReturn = Settings.Secure.putInt(mContext.getContentResolver(),
PREF_KEY,
(isChecked ? ON : OFF));
- if (mSwitchPreference != null) {
- refreshSummary(mSwitchPreference);
- }
+ refreshSummary(mSwitchPreference);
return toReturn;
}
@Override
public CharSequence getSummary() {
+ if (!mSwitchPreference.isEnabled()) {
+ return mContext.getString(
+ R.string.accessibility_magnification_one_finger_panning_summary_unavailable);
+ }
+
return (isChecked())
? mContext.getString(
R.string.accessibility_magnification_one_finger_panning_summary_on)
@@ -97,6 +125,20 @@
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mSwitchPreference = screen.findPreference(getPreferenceKey());
- refreshSummary(mSwitchPreference);
+ updateState(mSwitchPreference);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+
+ if (preference == null) {
+ return;
+ }
+ @MagnificationMode int mode =
+ MagnificationCapabilities.getCapabilities(mContext);
+ preference.setEnabled(
+ mode == MagnificationMode.FULLSCREEN || mode == MagnificationMode.ALL);
+ refreshSummary(preference);
}
}
diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
index 0e50a5c..72e1ad8 100644
--- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
@@ -443,8 +443,7 @@
final ApplicationInfo appInfo =
a11yServiceInfo.getResolveInfo().serviceInfo.applicationInfo;
final Uri packageUri = Uri.parse("package:" + appInfo.packageName);
- final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri)
- .setPackage(getString(R.string.config_package_installer_package_name));
+ final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
return uninstallIntent;
}
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index d162272..e965a0d 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -202,7 +202,6 @@
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
generalCategory.addPreference(mSettingsPreference);
- addOneFingerPanningSetting(generalCategory);
final MagnificationModePreferenceController magnificationModePreferenceController =
new MagnificationModePreferenceController(getContext(),
MagnificationModePreferenceController.PREF_KEY);
@@ -212,6 +211,7 @@
addPreferenceController(magnificationModePreferenceController);
addFollowTypingSetting(generalCategory);
+ addOneFingerPanningSetting(generalCategory);
addAlwaysOnSetting(generalCategory);
addJoystickSetting(generalCategory);
}
@@ -302,6 +302,7 @@
var oneFingerPanningPreferenceController =
new MagnificationOneFingerPanningPreferenceController(getContext());
+ getSettingsLifecycle().addObserver(oneFingerPanningPreferenceController);
oneFingerPanningPreferenceController.displayPreference(getPreferenceScreen());
addPreferenceController(oneFingerPanningPreferenceController);
}
diff --git a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
index 277f54b..53de7c5 100644
--- a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
@@ -539,8 +539,6 @@
// Create new intent to launch Uninstaller activity
Uri packageUri = Uri.parse("package:" + packageName);
Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
- uninstallIntent.setPackage(mContext.getString(
- R.string.config_package_installer_package_name));
uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
mMetricsFeatureProvider.action(mActivity, SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP);
@@ -558,6 +556,11 @@
ActivityManager am = (ActivityManager) mActivity.getSystemService(
Context.ACTIVITY_SERVICE);
Log.d(TAG, "Stopping package " + pkgName);
+ if (android.app.Flags.appRestrictionsApi()) {
+ am.noteAppRestrictionEnabled(pkgName, mAppEntry.info.uid,
+ ActivityManager.RESTRICTION_LEVEL_FORCE_STOPPED, true,
+ ActivityManager.RESTRICTION_REASON_USER, "settings", 0L);
+ }
am.forceStopPackage(pkgName);
int userId = UserHandle.getUserId(mAppEntry.info.uid);
mState.invalidatePackage(pkgName, userId);
diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
index afb33ae..90d733e 100644
--- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
+++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
@@ -642,7 +642,6 @@
// Create new intent to launch Uninstaller activity
final Uri packageURI = Uri.parse("package:" + packageName);
final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
- uninstallIntent.setPackage(getString(R.string.config_package_installer_package_name));
uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
mMetricsFeatureProvider.action(
getContext(), SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP);
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index a92b755..ca3786a 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -115,6 +115,7 @@
private @Nullable Delegate mDelegate = null;
private @Nullable String mFlagOverrideForTest = null;
private @Nullable PreferenceScreen mPreferenceScreen = null;
+ private @Nullable PreferenceGroup mPreferenceGroup = null;
private Optional<Boolean> mSimulateHiddenForTests = Optional.empty();
private boolean mIsWorkProfile = false;
@@ -161,12 +162,6 @@
return UNSUPPORTED_ON_DEVICE;
}
- // If there is no top provider or any providers in the list then
- // we should hide this pref.
- if (isHiddenDueToNoProviderSet()) {
- return CONDITIONALLY_UNAVAILABLE;
- }
-
if (!hasNonPrimaryServices()) {
return CONDITIONALLY_UNAVAILABLE;
}
@@ -355,24 +350,11 @@
}
// Get the list of new providers and components.
- List<CredentialProviderInfo> newProviders =
+ setAvailableServices(
mCredentialManager.getCredentialProviderServices(
getUser(),
- CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN);
- Set<ComponentName> newComponents = buildComponentNameSet(newProviders, false);
- Set<ComponentName> newPrimaryComponents = buildComponentNameSet(newProviders, true);
-
- // Get the list of old components
- Set<ComponentName> oldComponents = buildComponentNameSet(mServices, false);
- Set<ComponentName> oldPrimaryComponents = buildComponentNameSet(mServices, true);
-
- // If the sets are equal then don't update the UI.
- if (oldComponents.equals(newComponents)
- && oldPrimaryComponents.equals(newPrimaryComponents)) {
- return;
- }
-
- setAvailableServices(newProviders, null);
+ CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN),
+ null);
if (mPreferenceScreen != null) {
displayPreference(mPreferenceScreen);
@@ -396,11 +378,7 @@
}
@VisibleForTesting
- public boolean isHiddenDueToNoProviderSet() {
- return isHiddenDueToNoProviderSet(getProviders());
- }
-
- private boolean isHiddenDueToNoProviderSet(
+ public boolean isHiddenDueToNoProviderSet(
Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair) {
if (mSimulateHiddenForTests.isPresent()) {
return mSimulateHiddenForTests.get();
@@ -444,17 +422,67 @@
@Override
public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
+ final String prefKey = getPreferenceKey();
+ if (TextUtils.isEmpty(prefKey)) {
+ Log.w(TAG, "Skipping displayPreference because key is empty");
+ return;
+ }
- // Since the UI is being cleared, clear any refs.
+ // Store this reference for later.
+ if (mPreferenceScreen == null) {
+ mPreferenceScreen = screen;
+ mPreferenceGroup = screen.findPreference(prefKey);
+ }
+
+ final Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair = getProviders();
+
+ maybeUpdateListOfPrefs(providerPair);
+ maybeUpdatePreferenceVisibility(providerPair);
+ }
+
+ private void maybeUpdateListOfPrefs(
+ Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair) {
+ if (mPreferenceScreen == null || mPreferenceGroup == null) {
+ return;
+ }
+
+ // Build the new list of prefs.
+ Map<String, CombiPreference> newPrefs =
+ buildPreferenceList(mPreferenceScreen.getContext(), providerPair);
+
+ // Determine if we need to update the prefs.
+ Set<String> existingPrefPackageNames = mPrefs.keySet();
+ if (existingPrefPackageNames.equals(newPrefs.keySet())) {
+ return;
+ }
+
+ // Since the UI is being cleared, clear any refs and prefs.
mPrefs.clear();
+ mPreferenceGroup.removeAll();
- mPreferenceScreen = screen;
- PreferenceGroup group = screen.findPreference(getPreferenceKey());
- group.removeAll();
+ // Populate the preference list with new data.
+ mPrefs.putAll(newPrefs);
+ for (CombiPreference pref : newPrefs.values()) {
+ mPreferenceGroup.addPreference(pref);
+ }
+ }
- Context context = screen.getContext();
- mPrefs.putAll(buildPreferenceList(context, group));
+ private void maybeUpdatePreferenceVisibility(
+ Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair) {
+ if (mPreferenceScreen == null || mPreferenceGroup == null) {
+ return;
+ }
+
+ final boolean isAvailable =
+ (getAvailabilityStatus() == AVAILABLE) && !isHiddenDueToNoProviderSet(providerPair);
+
+ if (isAvailable) {
+ mPreferenceScreen.addPreference(mPreferenceGroup);
+ mPreferenceGroup.setVisible(true);
+ } else {
+ mPreferenceScreen.removePreference(mPreferenceGroup);
+ mPreferenceGroup.setVisible(false);
+ }
}
/**
@@ -511,9 +539,9 @@
/** Aggregates the list of services and builds a list of UI prefs to show. */
@VisibleForTesting
public @NonNull Map<String, CombiPreference> buildPreferenceList(
- @NonNull Context context, @NonNull PreferenceGroup group) {
- // Get the providers and extract the values.
- Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair = getProviders();
+ @NonNull Context context,
+ @NonNull Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair) {
+ // Extract the values.
CombinedProviderInfo topProvider = providerPair.second;
List<CombinedProviderInfo> providers = providerPair.first;
@@ -554,7 +582,6 @@
combinedInfo.getSettingsActivity(),
combinedInfo.getDeviceAdminRestrictions(context, getUser()));
output.put(packageName, pref);
- group.addPreference(pref);
}
// Set the visibility if we have services.
@@ -1023,11 +1050,12 @@
public void onClick(View buttonView) {
// Forward the event.
if (mSwitch != null && mOnClickListener != null) {
- if (!mOnClickListener.onCheckChanged(CombiPreference.this, mSwitch.isChecked())) {
- // The update was not successful since there were too
- // many enabled providers to manually reset any state.
- mChecked = false;
- mSwitch.setChecked(false);
+ if (!mOnClickListener.onCheckChanged(
+ CombiPreference.this, mSwitch.isChecked())) {
+ // The update was not successful since there were too
+ // many enabled providers to manually reset any state.
+ mChecked = false;
+ mSwitch.setChecked(false);
}
}
}
@@ -1083,8 +1111,10 @@
if (mSwitch != null && !TextUtils.isEmpty(appName)) {
mSwitch.setContentDescription(
- getContext().getString(
- R.string.credman_on_off_switch_content_description, appName));
+ getContext()
+ .getString(
+ R.string.credman_on_off_switch_content_description,
+ appName));
}
}
diff --git a/src/com/android/settings/applications/manageapplications/CloneBackend.java b/src/com/android/settings/applications/manageapplications/CloneBackend.java
index 06b4a99..406c930 100644
--- a/src/com/android/settings/applications/manageapplications/CloneBackend.java
+++ b/src/com/android/settings/applications/manageapplications/CloneBackend.java
@@ -34,7 +34,6 @@
import androidx.fragment.app.FragmentActivity;
-import com.android.settings.R;
import com.android.settings.Utils;
import java.util.HashSet;
@@ -79,8 +78,6 @@
// Create new intent to launch Uninstaller activity.
Uri packageUri = Uri.parse("package:" + packageName);
Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
- uninstallIntent.setPackage(mContext.getString(
- R.string.config_package_installer_package_name));
uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
uninstallIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(mCloneUserId));
// Trigger uninstall as clone user.
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 943d99b..3fa811a 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -43,6 +43,7 @@
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HeadsetProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LeAudioProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
@@ -512,6 +513,19 @@
refresh();
}
+ private boolean isLeAudioOnlyDevice() {
+ if (mCachedDevice.getProfiles().stream()
+ .noneMatch(profile -> profile instanceof LeAudioProfile)) {
+ return false;
+ }
+ return mCachedDevice.getProfiles().stream()
+ .noneMatch(
+ profile ->
+ profile instanceof HearingAidProfile
+ || profile instanceof A2dpProfile
+ || profile instanceof HeadsetProfile);
+ }
+
private void updateLeAudioConfig() {
mIsLeContactSharingEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, true);
@@ -520,6 +534,13 @@
boolean isLeEnabledByDefault =
SystemProperties.getBoolean(LE_AUDIO_CONNECTION_BY_DEFAULT_PROPERTY, true);
mIsLeAudioToggleEnabled = isLeAudioToggleVisible || isLeEnabledByDefault;
+ if (Flags.hideLeAudioToggleForLeAudioOnlyDevice() && isLeAudioOnlyDevice()) {
+ mIsLeAudioToggleEnabled = false;
+ Log.d(
+ TAG,
+ "Hide LeAudio toggle for LeAudio-only Device: "
+ + mCachedDevice.getDevice().getAnonymizedAddress());
+ }
Log.d(TAG, "BT_LE_AUDIO_CONTACT_SHARING_ENABLED:" + mIsLeContactSharingEnabled
+ ", LE_AUDIO_TOGGLE_VISIBLE_PROPERTY:" + isLeAudioToggleVisible
+ ", LE_AUDIO_CONNECTION_BY_DEFAULT_PROPERTY:" + isLeEnabledByDefault);
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index 0973076..6df80f1 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -46,6 +46,7 @@
import com.android.settings.core.CategoryMixin.CategoryHandler;
import com.android.settings.core.CategoryMixin.CategoryListener;
import com.android.settings.core.PreferenceControllerListHelper;
+import com.android.settings.flags.Flags;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -543,13 +544,23 @@
observers = mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(
getActivity(), this, forceRoundedIcons, pref, tile, key,
mPlaceholderPreferenceController.getOrder());
- if (tile.hasGroupKey() && mDashboardTilePrefKeys.containsKey(tile.getGroupKey())) {
- final Preference group = screen.findPreference(tile.getGroupKey());
- if (group instanceof PreferenceCategory) {
+ if (Flags.dynamicInjectionCategory()) {
+ Preference group = screen.findPreference(tile.getGroupKey());
+ if (tile.hasGroupKey() && group instanceof PreferenceCategory) {
((PreferenceCategory) group).addPreference(pref);
+ } else {
+ screen.addPreference(pref);
}
} else {
- screen.addPreference(pref);
+ if (tile.hasGroupKey()
+ && mDashboardTilePrefKeys.containsKey(tile.getGroupKey())) {
+ Preference group = screen.findPreference(tile.getGroupKey());
+ if (group instanceof PreferenceCategory) {
+ ((PreferenceCategory) group).addPreference(pref);
+ }
+ } else {
+ screen.addPreference(pref);
+ }
}
registerDynamicDataObservers(observers);
mDashboardTilePrefKeys.put(key, observers);
@@ -564,9 +575,13 @@
for (Map.Entry<String, List<DynamicDataObserver>> entry : remove.entrySet()) {
final String key = entry.getKey();
mDashboardTilePrefKeys.remove(key);
- final Preference preference = screen.findPreference(key);
- if (preference != null) {
- screen.removePreference(preference);
+ if (Flags.dynamicInjectionCategory()) {
+ screen.removePreferenceRecursively(key);
+ } else {
+ Preference preference = screen.findPreference(key);
+ if (preference != null) {
+ screen.removePreference(preference);
+ }
}
unregisterDynamicDataObservers(entry.getValue());
}
diff --git a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java
index 001876c3..62e28fe 100644
--- a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java
@@ -345,9 +345,9 @@
try {
batteryUtils.setForceAppStandby(uid, packageName, appStandbyMode);
if (allowListed) {
- powerAllowlistBackend.addApp(packageName);
+ powerAllowlistBackend.addApp(packageName, uid);
} else {
- powerAllowlistBackend.removeApp(packageName);
+ powerAllowlistBackend.removeApp(packageName, uid);
}
} catch (Exception e) {
// Error cases, set standby mode as -1 for logging.
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 767f2c0..b53bf47 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -15,6 +15,7 @@
*/
package com.android.settings.fuelgauge;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
@@ -386,6 +387,14 @@
// Control whether app could run in the background if it is pre O app
mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName, mode);
}
+ // Notify system of reason for change
+ if (isForceAppStandbyEnabled(uid, packageName) != (mode == AppOpsManager.MODE_IGNORED)) {
+ mContext.getSystemService(ActivityManager.class).noteAppRestrictionEnabled(
+ packageName, uid, ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED,
+ mode == AppOpsManager.MODE_IGNORED,
+ ActivityManager.RESTRICTION_REASON_USER,
+ "settings", 0);
+ }
// Control whether app could run jobs in the background
mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode);
diff --git a/src/com/android/settings/fuelgauge/HighPowerDetail.java b/src/com/android/settings/fuelgauge/HighPowerDetail.java
index b2585a6..ee9efae 100644
--- a/src/com/android/settings/fuelgauge/HighPowerDetail.java
+++ b/src/com/android/settings/fuelgauge/HighPowerDetail.java
@@ -142,9 +142,9 @@
if (newValue) {
mBatteryUtils.setForceAppStandby(
mPackageUid, mPackageName, AppOpsManager.MODE_ALLOWED);
- mBackend.addApp(mPackageName);
+ mBackend.addApp(mPackageName, mPackageUid);
} else {
- mBackend.removeApp(mPackageName);
+ mBackend.removeApp(mPackageName, mPackageUid);
}
}
}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
index 35a173b..dd30e69 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -39,6 +39,9 @@
/** Check whether the battery tips card is enabled in the battery usage page */
boolean isBatteryTipsEnabled();
+ /** Check whether to log the optimization mode of app entry in period job */
+ boolean isAppOptimizationModeLogged();
+
/**
* Returns a threshold (in milliseconds) for the minimal screen on time in battery usage list
*/
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index f0616ed..5c66dbc 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -84,6 +84,11 @@
}
@Override
+ public boolean isAppOptimizationModeLogged() {
+ return false;
+ }
+
+ @Override
public double getBatteryUsageListScreenOnTimeThresholdInMs() {
return 0;
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryOptimizationModeCache.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryOptimizationModeCache.java
new file mode 100644
index 0000000..6b35fb9
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryOptimizationModeCache.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 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.fuelgauge.batteryusage;
+
+import android.content.Context;
+import android.util.ArrayMap;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.fuelgauge.BatteryOptimizeUtils;
+import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
+
+import java.util.Map;
+
+/** A cache to log battery optimization mode of an app */
+final class BatteryOptimizationModeCache {
+ private static final String TAG = "BatteryOptimizationModeCache";
+
+ @VisibleForTesting final Map<Integer, BatteryOptimizationMode> mBatteryOptimizeModeCacheMap;
+
+ private final Context mContext;
+
+ BatteryOptimizationModeCache(final Context context) {
+ mContext = context;
+ mBatteryOptimizeModeCacheMap = new ArrayMap<>();
+ PowerAllowlistBackend.getInstance(mContext).refreshList();
+ }
+
+ BatteryOptimizationMode getBatteryOptimizeMode(final int uid, final String packageName) {
+ if (!mBatteryOptimizeModeCacheMap.containsKey(uid)) {
+ final BatteryOptimizeUtils batteryOptimizeUtils =
+ new BatteryOptimizeUtils(mContext, uid, packageName);
+ mBatteryOptimizeModeCacheMap.put(
+ uid,
+ BatteryOptimizationMode.forNumber(
+ batteryOptimizeUtils.getAppOptimizationMode(/* refreshList= */ false)));
+ }
+ return mBatteryOptimizeModeCacheMap.get(uid);
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
index 870faec..26bb6dd 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
@@ -27,6 +27,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
+import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
import com.android.settings.overlay.FeatureFactory;
@@ -124,9 +125,15 @@
userIdsSeries,
/* isFromPeriodJob= */ true,
batteryDiffDataMap -> {
+ final PowerUsageFeatureProvider featureProvider =
+ FeatureFactory.getFeatureFactory()
+ .getPowerUsageFeatureProvider();
DatabaseUtils.sendBatteryUsageSlotData(
context,
- ConvertUtils.convertToBatteryUsageSlotList(batteryDiffDataMap));
+ ConvertUtils.convertToBatteryUsageSlotList(
+ context,
+ batteryDiffDataMap,
+ featureProvider.isAppOptimizationModeLogged()));
if (batteryDiffDataMap.values().stream()
.anyMatch(
data ->
@@ -135,12 +142,10 @@
.isEmpty()
|| !data.getAppDiffEntryList()
.isEmpty()))) {
- FeatureFactory.getFeatureFactory()
- .getPowerUsageFeatureProvider()
- .detectPowerAnomaly(
- context,
- /* displayDrain= */ 0,
- DetectRequestSourceType.TYPE_DATA_LOADER);
+ featureProvider.detectPowerAnomaly(
+ context,
+ /* displayDrain= */ 0,
+ DetectRequestSourceType.TYPE_DATA_LOADER);
}
});
if (batteryLevelData == null) {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index df9f063..1cbe6da 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -345,10 +345,15 @@
/** Converts from {@link Map<Long, BatteryDiffData>} to {@link List<BatteryUsageSlot>} */
public static List<BatteryUsageSlot> convertToBatteryUsageSlotList(
- final Map<Long, BatteryDiffData> batteryDiffDataMap) {
+ final Context context,
+ final Map<Long, BatteryDiffData> batteryDiffDataMap,
+ final boolean isAppOptimizationModeLogged) {
List<BatteryUsageSlot> batteryUsageSlotList = new ArrayList<>();
+ final BatteryOptimizationModeCache optimizationModeCache =
+ isAppOptimizationModeLogged ? new BatteryOptimizationModeCache(context) : null;
for (BatteryDiffData batteryDiffData : batteryDiffDataMap.values()) {
- batteryUsageSlotList.add(convertToBatteryUsageSlot(batteryDiffData));
+ batteryUsageSlotList.add(
+ convertToBatteryUsageSlot(batteryDiffData, optimizationModeCache));
}
return batteryUsageSlotList;
}
@@ -479,9 +484,10 @@
}
}
-
@VisibleForTesting
- static BatteryUsageDiff convertToBatteryUsageDiff(BatteryDiffEntry batteryDiffEntry) {
+ static BatteryUsageDiff convertToBatteryUsageDiff(
+ final BatteryDiffEntry batteryDiffEntry,
+ final @Nullable BatteryOptimizationModeCache optimizationModeCache) {
BatteryUsageDiff.Builder builder =
BatteryUsageDiff.newBuilder()
.setUid(batteryDiffEntry.mUid)
@@ -511,11 +517,18 @@
if (batteryDiffEntry.mLegacyLabel != null) {
builder.setLabel(batteryDiffEntry.mLegacyLabel);
}
+ // Log the battery optimization mode of AppEntry while converting to batteryUsageSlot.
+ if (optimizationModeCache != null && !batteryDiffEntry.isSystemEntry()) {
+ builder.setAppOptimizationMode(
+ optimizationModeCache.getBatteryOptimizeMode(
+ (int) batteryDiffEntry.mUid, batteryDiffEntry.getPackageName()));
+ }
return builder.build();
}
private static BatteryUsageSlot convertToBatteryUsageSlot(
- final BatteryDiffData batteryDiffData) {
+ final BatteryDiffData batteryDiffData,
+ final @Nullable BatteryOptimizationModeCache optimizationModeCache) {
if (batteryDiffData == null) {
return BatteryUsageSlot.getDefaultInstance();
}
@@ -527,10 +540,11 @@
.setEndBatteryLevel(batteryDiffData.getEndBatteryLevel())
.setScreenOnTime(batteryDiffData.getScreenOnTime());
for (BatteryDiffEntry batteryDiffEntry : batteryDiffData.getAppDiffEntryList()) {
- builder.addAppUsage(convertToBatteryUsageDiff(batteryDiffEntry));
+ builder.addAppUsage(convertToBatteryUsageDiff(batteryDiffEntry, optimizationModeCache));
}
for (BatteryDiffEntry batteryDiffEntry : batteryDiffData.getSystemDiffEntryList()) {
- builder.addSystemUsage(convertToBatteryUsageDiff(batteryDiffEntry));
+ builder.addSystemUsage(
+ convertToBatteryUsageDiff(batteryDiffEntry, /* optimizationModeCache= */ null));
}
return builder.build();
}
diff --git a/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto b/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto
index 7f67770..4e3e3c4 100644
--- a/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto
+++ b/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto
@@ -14,6 +14,13 @@
repeated BatteryUsageDiff system_usage = 7;
}
+enum BatteryOptimizationMode {
+ MODE_UNKNOWN = 0;
+ MODE_RESTRICTED = 1;
+ MODE_UNRESTRICTED = 2;
+ MODE_OPTIMIZED = 3;
+}
+
message BatteryUsageDiff {
optional int64 uid = 1;
optional int64 user_id = 2;
@@ -32,4 +39,5 @@
optional int64 background_usage_time = 15;
optional int64 screen_on_time = 16;
optional int64 foreground_service_usage_time = 17;
+ optional BatteryOptimizationMode app_optimization_mode = 18;
}
diff --git a/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.kt b/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.kt
index b7ee41d..312d446 100644
--- a/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.kt
+++ b/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.kt
@@ -26,7 +26,7 @@
import com.android.settings.R
import com.android.settings.core.BasePreferenceController
import com.android.settings.network.SubscriptionUtil
-import com.android.settings.wifi.dpp.WifiDppUtils
+import com.android.settings.network.telephony.MobileNetworkUtils
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
/** This controls a preference allowing the user to delete the profile for an eSIM. */
@@ -65,7 +65,7 @@
override fun handlePreferenceTreeClick(preference: Preference): Boolean {
if (preference.key != preferenceKey) return false
- WifiDppUtils.showLockScreen(mContext) { deleteSim() }
+ MobileNetworkUtils.showLockScreen(mContext) { deleteSim() }
return true
}
diff --git a/src/com/android/settings/network/telephony/MobileNetworkUtils.java b/src/com/android/settings/network/telephony/MobileNetworkUtils.java
index 4430642..ea549ae 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkUtils.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkUtils.java
@@ -31,6 +31,7 @@
import static com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
import static com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
+import android.app.KeyguardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -41,13 +42,18 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
+import android.hardware.biometrics.BiometricPrompt;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
import android.os.PersistableBundle;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.telecom.PhoneAccountHandle;
@@ -68,6 +74,7 @@
import android.util.Log;
import android.view.Gravity;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -1050,4 +1057,53 @@
.launch();
}
+ /**
+ * Shows authentication screen to confirm credentials (pin/pattern/password) for the current
+ * user of the device.
+ *
+ * <p>Similar to WifiDppUtils.showLockScreen(), but doesn't check for the existence of
+ * SIM PIN lock, only screen PIN lock.
+ *
+ * @param context The {@code Context} used to get {@link KeyguardManager} service
+ * @param onSuccess The {@code Runnable} which will be executed if the user does not setup
+ * device security or if lock screen is unlocked
+ */
+ public static void showLockScreen(@NonNull Context context, @NonNull Runnable onSuccess) {
+ final KeyguardManager keyguardManager =
+ context.getSystemService(KeyguardManager.class);
+
+ if (keyguardManager.isDeviceSecure()) {
+ final BiometricPrompt.AuthenticationCallback authenticationCallback =
+ new BiometricPrompt.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationSucceeded(
+ BiometricPrompt.AuthenticationResult result) {
+ onSuccess.run();
+ }
+
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ // Do nothing
+ }
+ };
+
+ final int userId = UserHandle.myUserId();
+ final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context)
+ .setTitle(context.getText(R.string.wifi_dpp_lockscreen_title))
+ .setDeviceCredentialAllowed(true)
+ .setTextForDeviceCredential(
+ /* title= */ null,
+ Utils.getConfirmCredentialStringForUser(
+ context, userId, Utils.getCredentialType(context, userId)),
+ /* description= */ null)
+ .build();
+ final Handler handler = new Handler(Looper.getMainLooper());
+ biometricPrompt.authenticate(
+ new CancellationSignal(),
+ handler::post,
+ authenticationCallback);
+ } else {
+ onSuccess.run();
+ }
+ }
}
diff --git a/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt b/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt
index 2067b8c..dfa79cb 100644
--- a/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt
+++ b/src/com/android/settings/network/telephony/scan/NetworkScanRepository.kt
@@ -25,8 +25,8 @@
import android.telephony.TelephonyManager
import android.telephony.TelephonyScanManager
import android.util.Log
-import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LifecycleOwner
+import com.android.settings.R
import com.android.settings.network.telephony.CellInfoUtil
import com.android.settings.network.telephony.CellInfoUtil.getNetworkTitle
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
@@ -37,7 +37,7 @@
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn
-class NetworkScanRepository(context: Context, subId: Int) {
+class NetworkScanRepository(private val context: Context, subId: Int) {
sealed interface NetworkScanResult
data class NetworkScanCellInfos(val cellInfos: List<CellInfo>) : NetworkScanResult
@@ -105,7 +105,7 @@
NetworkScanRequest.SCAN_TYPE_ONE_SHOT,
radioAccessSpecifiers,
NetworkScanRequest.MIN_SEARCH_PERIODICITY_SEC, // one shot, not used
- MAX_SEARCH_TIME_SEC,
+ context.resources.getInteger(R.integer.config_network_scan_helper_max_search_time_sec),
true,
INCREMENTAL_RESULTS_PERIODICITY_SEC,
null,
@@ -158,10 +158,6 @@
companion object {
private const val TAG = "NetworkScanRepository"
- @VisibleForTesting
- val MAX_SEARCH_TIME_SEC = 300
-
- @VisibleForTesting
- val INCREMENTAL_RESULTS_PERIODICITY_SEC = 3
+ private const val INCREMENTAL_RESULTS_PERIODICITY_SEC = 3
}
}
diff --git a/src/com/android/settings/security/ScreenPinningSettings.java b/src/com/android/settings/security/ScreenPinningSettings.java
index 4d3743c..45c3d84 100644
--- a/src/com/android/settings/security/ScreenPinningSettings.java
+++ b/src/com/android/settings/security/ScreenPinningSettings.java
@@ -240,9 +240,9 @@
mUseScreenLock.setChecked(isScreenLockUsed());
mUseScreenLock.setTitle(getCurrentSecurityTitle(mLockPatternUtils));
} else {
- mFooterPreference.setSummary(getAppPinningContent());
mUseScreenLock.setEnabled(false);
}
+ mFooterPreference.setSummary(getAppPinningContent());
}
private boolean isGuestModeSupported() {
diff --git a/src/com/android/settings/spa/app/AppUtil.kt b/src/com/android/settings/spa/app/AppUtil.kt
index 2b30c6f..64da613 100644
--- a/src/com/android/settings/spa/app/AppUtil.kt
+++ b/src/com/android/settings/spa/app/AppUtil.kt
@@ -20,7 +20,6 @@
import android.content.Intent
import android.net.Uri
import android.os.UserHandle
-import com.android.settings.R
/**
* Based on PackageManagerService design, and it looks like the suggested replacement in the
@@ -37,7 +36,6 @@
val packageUri = Uri.parse("package:$packageName")
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri).apply {
- setPackage(getString(R.string.config_package_installer_package_name))
putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, forAllUsers)
}
startActivityAsUser(intent, userHandle)
diff --git a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
index 81abae5..d5ce3af 100644
--- a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
+++ b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
@@ -16,6 +16,7 @@
package com.android.settings.spa.app.appinfo
+import android.app.ActivityManager
import android.app.settings.SettingsEnums
import android.content.Context
import android.content.Intent
@@ -154,6 +155,13 @@
logAction(SettingsEnums.ACTION_APP_FORCE_STOP)
coroutineScope.launch(Dispatchers.Default) {
Log.d(TAG, "Stopping package $packageName")
+ if (android.app.Flags.appRestrictionsApi()) {
+ val uid = userPackageManager.getPackageUid(packageName, 0)
+ context.activityManager.noteAppRestrictionEnabled(
+ packageName, uid,
+ ActivityManager.RESTRICTION_LEVEL_FORCE_STOPPED, true,
+ ActivityManager.RESTRICTION_REASON_USER, "settings", 0)
+ }
context.activityManager.forceStopPackageAsUser(packageName, userId)
}
}
diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
index 0ccedeb..640ad7a 100644
--- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
+++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.IntentFilter
import android.os.Bundle
+import android.provider.Settings
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
@@ -57,6 +58,7 @@
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow
+import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBooleanFlow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
@@ -110,6 +112,8 @@
val subscriptionViewModel = viewModel<SubscriptionInfoListViewModel>()
+ CollectAirplaneModeAndFinishIfOn()
+
remember {
allOfFlows(context, subscriptionViewModel.selectableSubscriptionInfoListFlow)
}.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
@@ -327,6 +331,17 @@
}
}
+@Composable
+fun CollectAirplaneModeAndFinishIfOn() {
+ val context = LocalContext.current
+ context.settingsGlobalBooleanFlow(Settings.Global.AIRPLANE_MODE_ON)
+ .collectLatestWithLifecycle(LocalLifecycleOwner.current) { isAirplaneModeOn ->
+ if (isAirplaneModeOn) {
+ context.getActivity()?.finish()
+ }
+ }
+}
+
private fun getWifiPickerTrackerHelper(
context: Context,
lifecycleOwner: LifecycleOwner
@@ -336,6 +351,7 @@
null /* WifiPickerTrackerCallback */
)
}
+
private fun Context.defaultVoiceSubscriptionFlow(): Flow<Int> =
merge(
flowOf(null), // kick an initial value
diff --git a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
index 66b7d4b..2b40a91 100644
--- a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
@@ -85,13 +85,16 @@
onboardingService: SimOnboardingService,
subInfo: SubscriptionInfo,
) {
+ val originalSimCarrierName = subInfo.displayName.toString()
var titleSimName by remember {
mutableStateOf(onboardingService.getSubscriptionInfoDisplayName(subInfo))
}
val phoneNumber = phoneNumber(subInfo)
val alertDialogPresenter = rememberAlertDialogPresenter(
confirmButton = AlertDialogButton(stringResource(R.string.mobile_network_sim_name_rename)) {
- onboardingService.addItemForRenaming(subInfo, titleSimName)
+ onboardingService.addItemForRenaming(
+ subInfo, if (titleSimName.isEmpty()) originalSimCarrierName else titleSimName
+ )
},
dismissButton = AlertDialogButton(stringResource(R.string.cancel)) {
titleSimName = onboardingService.getSubscriptionInfoDisplayName(subInfo)
@@ -105,6 +108,7 @@
SettingsOutlinedTextField(
value = titleSimName,
label = stringResource(R.string.sim_onboarding_label_sim_dialog_label),
+ placeholder = {Text(text = originalSimCarrierName)},
modifier = Modifier.fillMaxWidth()
) {
titleSimName = it
diff --git a/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt b/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
index 838154f..2341231 100644
--- a/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
@@ -83,7 +83,7 @@
): String = "${name}/$subId"
}
-private fun Context.getActivity(): Activity? = when (this) {
+fun Context.getActivity(): Activity? = when (this) {
is Activity -> this
is ContextWrapper -> baseContext.getActivity()
else -> null
diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationOneFingerPanningPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationOneFingerPanningPreferenceControllerTest.java
index 4501d27..bfc8313 100644
--- a/tests/robotests/src/com/android/settings/accessibility/MagnificationOneFingerPanningPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationOneFingerPanningPreferenceControllerTest.java
@@ -18,19 +18,15 @@
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
-import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_FOR_USER;
+import static com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import androidx.preference.PreferenceManager;
@@ -38,131 +34,137 @@
import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
-import com.android.server.accessibility.Flags;
import com.android.settings.R;
-import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowContentResolver;
@RunWith(RobolectricTestRunner.class)
public class MagnificationOneFingerPanningPreferenceControllerTest {
private static final String ONE_FINGER_PANNING_KEY =
Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
private final Context mContext = ApplicationProvider.getApplicationContext();
+ private ShadowContentResolver mShadowContentResolver;
private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext));
private final MagnificationOneFingerPanningPreferenceController mController =
new MagnificationOneFingerPanningPreferenceController(mContext);
- private PreferenceScreen mScreen;
-
@Before
public void setUp() {
+ mShadowContentResolver = Shadow.extract(mContext.getContentResolver());
+
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
- mScreen = preferenceManager.createPreferenceScreen(mContext);
+ final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
mSwitchPreference.setKey(MagnificationOneFingerPanningPreferenceController.PREF_KEY);
- mScreen.addPreference(mSwitchPreference);
- mController.displayPreference(mScreen);
- }
-
- @After
- public void cleanup() {
- // Can't use resetToDefaults as it NPE with
- // "Cannot invoke "android.content.IContentProvider.call"
- Settings.Secure.putInt(
- mContext.getContentResolver(),
- MagnificationOneFingerPanningPreferenceController.PREF_KEY,
- (mController.mDefaultValue) ? ON : OFF);
+ screen.addPreference(mSwitchPreference);
+ mController.displayPreference(screen);
}
@Test
- public void displayPreference_defaultState_correctSummarySet() {
- assertThat(mSwitchPreference.getSummary())
- .isEqualTo(mContext.getString(
- R.string.accessibility_magnification_one_finger_panning_summary_off));
+ public void onResume_verifyRegisterCapabilityObserver() {
+ mController.onResume();
+ assertThat(mShadowContentResolver.getContentObservers(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY)))
+ .hasSize(1);
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
- public void getAvailabilityStatus_flagDisabled_disabled() {
- int status = mController.getAvailabilityStatus();
-
- assertThat(status).isEqualTo(DISABLED_FOR_USER);
+ public void onPause_verifyUnregisterCapabilityObserver() {
+ mController.onResume();
+ mController.onPause();
+ assertThat(mShadowContentResolver.getContentObservers(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY)))
+ .isEmpty();
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
- public void getAvailabilityStatus_featureFlagEnabled_enabled() {
- int status = mController.getAvailabilityStatus();
+ public void updateState_windowModeOnly_preferenceIsUnavailable() {
+ MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.WINDOW);
+ mController.updateState(mSwitchPreference);
- assertThat(status).isEqualTo(AVAILABLE);
+ assertThat(mSwitchPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void updateState_fullscreenModeOnly_preferenceIsAvailable() {
+ MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.FULLSCREEN);
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mSwitchPreference.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void updateState_switchMode_preferenceIsAvailable() {
+ MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.ALL);
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mSwitchPreference.isEnabled()).isTrue();
}
@Test
public void isChecked_defaultState_returnFalse() {
+ mController.updateState(mSwitchPreference);
+
assertThat(mController.isChecked()).isFalse();
assertThat(mSwitchPreference.isChecked()).isFalse();
}
@Test
- public void isChecked_settingsEnabled_returnTrue() {
+ public void isChecked_settingsOn_returnTrue() {
Settings.Secure.putInt(mContext.getContentResolver(), ONE_FINGER_PANNING_KEY, ON);
+ mController.updateState(mSwitchPreference);
assertThat(mController.isChecked()).isTrue();
}
@Test
- public void isChecked_settingsDisabled_returnTrue() {
+ public void isChecked_settingsOff_returnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(), ONE_FINGER_PANNING_KEY, OFF);
+ mController.updateState(mSwitchPreference);
assertThat(mController.isChecked()).isFalse();
}
@Test
- public void setChecked_enabled_enabledSummarySet() {
- mController.setChecked(true);
-
- assertThat(mSwitchPreference.getSummary()).isEqualTo(enabledSummary());
- assertThat(mController.isChecked()).isTrue();
- }
-
- @Test
- public void setChecked_disabled_disabledSummarySet() {
- mController.setChecked(false);
-
- assertThat(mController.isChecked()).isFalse();
- assertThat(mSwitchPreference.getSummary()).isEqualTo(disabledSummary());
- }
-
- @Test
- public void getSummary_disable_disableSummaryTextUsed() {
+ public void getSummary_switchModeAndSettingsOff_disabledSummaryTextUsed() {
+ MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.ALL);
Settings.Secure.putInt(mContext.getContentResolver(), ONE_FINGER_PANNING_KEY, OFF);
- var summary = mController.getSummary();
+ mController.updateState(mSwitchPreference);
- assertThat(summary).isEqualTo(disabledSummary());
+ assertThat(mController.getSummary()).isEqualTo(disabledSummary());
}
@Test
- public void getSummary_enable_enabledSummaryTextUsed() {
+ public void getSummary_switchModeAndSettingsOn_enabledSummaryTextUsed() {
+ MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.ALL);
Settings.Secure.putInt(mContext.getContentResolver(), ONE_FINGER_PANNING_KEY, ON);
- var summary = mController.getSummary();
+ mController.updateState(mSwitchPreference);
- assertThat(summary).isEqualTo(enabledSummary());
+ assertThat(mController.getSummary()).isEqualTo(enabledSummary());
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
- public void performClick_switchDefaultState_shouldReturnTrue() {
+ public void getSummary_windowModeOnly_unavailableSummaryTextUsed() {
+ MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.WINDOW);
+
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mController.getSummary()).isEqualTo(unavailableSummary());
+ }
+
+ @Test
+ public void performClick_defaultSettings_toggleOn() {
+ MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.ALL);
+ mController.updateState(mSwitchPreference);
+ reset(mSwitchPreference);
+
mSwitchPreference.performClick();
verify(mSwitchPreference).setChecked(true);
@@ -170,6 +172,20 @@
assertThat(mSwitchPreference.isChecked()).isTrue();
}
+ @Test
+ public void performClick_settingsOn_toggleOff() {
+ MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.ALL);
+ Settings.Secure.putInt(mContext.getContentResolver(), ONE_FINGER_PANNING_KEY, ON);
+ mController.updateState(mSwitchPreference);
+ reset(mSwitchPreference);
+
+ mSwitchPreference.performClick();
+
+ verify(mSwitchPreference).setChecked(false);
+ assertThat(mController.isChecked()).isFalse();
+ assertThat(mSwitchPreference.isChecked()).isFalse();
+ }
+
private String enabledSummary() {
return mContext.getString(
R.string.accessibility_magnification_one_finger_panning_summary_on);
@@ -179,4 +195,9 @@
return mContext.getString(
R.string.accessibility_magnification_one_finger_panning_summary_off);
}
+
+ private String unavailableSummary() {
+ return mContext.getString(
+ R.string.accessibility_magnification_one_finger_panning_summary_unavailable);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
index cc1c72e..ab2e9d1 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
@@ -122,6 +122,8 @@
private static final String KEY_FOLLOW_TYPING =
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED;
+ private static final String KEY_SINGLE_FINGER_PANNING =
+ Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED;
private static final String KEY_ALWAYS_ON =
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED;
private static final String KEY_JOYSTICK =
@@ -216,6 +218,43 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
+ public void onResume_defaultStateForOneFingerPan_switchPreferenceShouldReturnFalse() {
+ mFragController.create(R.id.main_content, /* bundle= */ null).start().resume();
+
+ final TwoStatePreference switchPreference = mFragController.get().findPreference(
+ MagnificationOneFingerPanningPreferenceController.PREF_KEY);
+ assertThat(switchPreference).isNotNull();
+ assertThat(switchPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
+ public void onResume_enableOneFingerPan_switchPreferenceShouldReturnTrue() {
+ setKeyOneFingerPanEnabled(true);
+
+ mFragController.create(R.id.main_content, /* bundle= */ null).start().resume();
+
+ final TwoStatePreference switchPreference = mFragController.get().findPreference(
+ MagnificationOneFingerPanningPreferenceController.PREF_KEY);
+ assertThat(switchPreference).isNotNull();
+ assertThat(switchPreference.isChecked()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
+ public void onResume_disableOneFingerPan_switchPreferenceShouldReturnFalse() {
+ setKeyOneFingerPanEnabled(false);
+
+ mFragController.create(R.id.main_content, /* bundle= */ null).start().resume();
+
+ final TwoStatePreference switchPreference = mFragController.get().findPreference(
+ MagnificationOneFingerPanningPreferenceController.PREF_KEY);
+ assertThat(switchPreference).isNotNull();
+ assertThat(switchPreference.isChecked()).isFalse();
+ }
+
+ @Test
public void onResume_defaultStateForAlwaysOn_switchPreferenceShouldReturnTrue() {
setAlwaysOnSupported(true);
@@ -778,6 +817,16 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
+ public void onCreateView_oneFingerPanNotSupported_settingsPreferenceIsNull() {
+ mFragController.create(R.id.main_content, /* bundle= */ null).start().resume();
+
+ final TwoStatePreference switchPreference = mFragController.get().findPreference(
+ MagnificationOneFingerPanningPreferenceController.PREF_KEY);
+ assertThat(switchPreference).isNull();
+ }
+
+ @Test
public void onCreateView_alwaysOnNotSupported_settingsPreferenceIsNull() {
setAlwaysOnSupported(false);
@@ -817,7 +866,25 @@
}
@Test
- public void onCreateView_addTheAlwaysOnControllerToLifeCycleObserver() {
+ @EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
+ public void onCreateView_oneFingerPanSupported_addControllerToLifeCycleObserver() {
+ Correspondence instanceOf = Correspondence.transforming(
+ observer -> (observer instanceof MagnificationOneFingerPanningPreferenceController),
+ "contains MagnificationOneFingerPanningPreferenceController");
+
+ ToggleScreenMagnificationPreferenceFragment fragment = mFragController.create(
+ R.id.main_content, /* bundle= */ null).start().resume().get();
+
+ List<LifecycleObserver> lifecycleObservers = ReflectionHelpers.getField(
+ fragment.getSettingsLifecycle(), "mObservers");
+ assertThat(lifecycleObservers).isNotNull();
+ assertThat(lifecycleObservers).comparingElementsUsing(instanceOf).contains(true);
+ }
+
+ @Test
+ public void onCreateView_alwaysOnSupported_addControllerToLifeCycleObserver() {
+ setAlwaysOnSupported(true);
+
Correspondence instanceOf = Correspondence.transforming(
observer -> (observer instanceof MagnificationAlwaysOnPreferenceController),
"contains MagnificationAlwaysOnPreferenceController");
@@ -984,6 +1051,11 @@
enabled ? ON : OFF);
}
+ private void setKeyOneFingerPanEnabled(boolean enabled) {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_SINGLE_FINGER_PANNING,
+ enabled ? ON : OFF);
+ }
+
private void setAlwaysOnSupported(boolean supported) {
ShadowDeviceConfig.setProperty(
DeviceConfig.NAMESPACE_WINDOW_MANAGER,
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
index 9b1466b..9b92234 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java
@@ -558,4 +558,49 @@
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
assertThat(switches.get(0).isVisible()).isTrue();
}
+
+ @Test
+ public void classicAudioDeviceWithLeAudio_showLeAudioToggle() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HIDE_LE_AUDIO_TOGGLE_FOR_LE_AUDIO_ONLY_DEVICE);
+ setupDevice(makeDefaultDeviceConfig());
+
+ LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
+ when(leAudioProfile.getNameResource(mDevice))
+ .thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
+ when(leAudioProfile.isProfileReady()).thenReturn(true);
+ when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
+ when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
+ mConnectableProfiles.add(leAudioProfile);
+ when(mCachedDevice.getProfiles())
+ .thenAnswer(
+ invocation ->
+ ImmutableList.of(
+ leAudioProfile, addMockA2dpProfile(false, false, false)));
+
+ showScreen(mController);
+
+ List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
+ assertThat(switches.get(0).isVisible()).isTrue();
+ }
+
+ @Test
+ public void leAudioOnlyDevice_hideLeAudioToggle() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HIDE_LE_AUDIO_TOGGLE_FOR_LE_AUDIO_ONLY_DEVICE);
+ setupDevice(makeDefaultDeviceConfig());
+
+ LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
+ when(leAudioProfile.getNameResource(mDevice))
+ .thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
+ when(leAudioProfile.isProfileReady()).thenReturn(true);
+ when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
+ when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
+ mConnectableProfiles.add(leAudioProfile);
+ when(mCachedDevice.getProfiles())
+ .thenAnswer(invocation -> ImmutableList.of(leAudioProfile));
+
+ showScreen(mController);
+
+ List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
+ assertThat(switches.get(0).isVisible()).isFalse();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java
index 6085b9a..9686709 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java
@@ -206,8 +206,8 @@
TimeUnit.SECONDS.sleep(1);
verify(mMockBatteryUtils, never()).setForceAppStandby(anyInt(), anyString(), anyInt());
- verify(mMockBackend, never()).addApp(anyString());
- verify(mMockBackend, never()).removeApp(anyString());
+ verify(mMockBackend, never()).addApp(anyString(), anyInt());
+ verify(mMockBackend, never()).removeApp(anyString(), anyInt());
verifyNoInteractions(mObserver);
}
@@ -358,9 +358,9 @@
private void verifySetAppOptimizationMode(int appStandbyMode, boolean allowListed) {
verify(mMockBatteryUtils).setForceAppStandby(UID, PACKAGE_NAME, appStandbyMode);
if (allowListed) {
- verify(mMockBackend).addApp(PACKAGE_NAME);
+ verify(mMockBackend).addApp(PACKAGE_NAME, UID);
} else {
- verify(mMockBackend).removeApp(PACKAGE_NAME);
+ verify(mMockBackend).removeApp(PACKAGE_NAME, UID);
}
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
index db4c359..3158688 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java
@@ -73,6 +73,11 @@
}
@Test
+ public void testIsAppOptimizationModeLogged_returnFalse() {
+ assertThat(mPowerFeatureProvider.isAppOptimizationModeLogged()).isFalse();
+ }
+
+ @Test
public void testGetBatteryUsageListConsumePowerThreshold_return0() {
assertThat(mPowerFeatureProvider.getBatteryUsageListConsumePowerThreshold()).isEqualTo(0.0);
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
index 5ce449b..3ed2dd1 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -382,9 +382,13 @@
/* foregroundServiceUsageConsumePower= */ 1.3,
/* backgroundUsageConsumePower= */ 1.4,
/* cachedUsageConsumePower= */ 1.5);
+ BatteryOptimizationModeCache optimizationModeCache =
+ new BatteryOptimizationModeCache(mContext);
+ optimizationModeCache.mBatteryOptimizeModeCacheMap.put(
+ (int) batteryDiffEntry.mUid, BatteryOptimizationMode.MODE_OPTIMIZED);
final BatteryUsageDiff batteryUsageDiff =
- ConvertUtils.convertToBatteryUsageDiff(batteryDiffEntry);
+ ConvertUtils.convertToBatteryUsageDiff(batteryDiffEntry, optimizationModeCache);
assertThat(batteryUsageDiff.getUid()).isEqualTo(101L);
assertThat(batteryUsageDiff.getUserId()).isEqualTo(1001L);
@@ -402,6 +406,8 @@
assertThat(batteryUsageDiff.getBackgroundUsageTime()).isEqualTo(5678L);
assertThat(batteryUsageDiff.getScreenOnTime()).isEqualTo(123L);
assertThat(batteryUsageDiff.getKey()).isEqualTo("key");
+ assertThat(batteryUsageDiff.getAppOptimizationMode())
+ .isEqualTo(BatteryOptimizationMode.MODE_OPTIMIZED);
assertThat(batteryUsageDiff.hasPackageName()).isFalse();
assertThat(batteryUsageDiff.hasLabel()).isFalse();
}
@@ -591,7 +597,7 @@
Map.of(11L, batteryDiffData1, 21L, batteryDiffData2, 31L, batteryDiffData3);
final List<BatteryUsageSlot> batteryUsageSlotList =
- ConvertUtils.convertToBatteryUsageSlotList(batteryDiffDataMap);
+ ConvertUtils.convertToBatteryUsageSlotList(mContext, batteryDiffDataMap, false);
assertThat(batteryUsageSlotList).hasSize(3);
assertThat(batteryUsageSlotList.stream().map((s) -> s.getScreenOnTime()).sorted().toList())
diff --git a/tests/robotests/src/com/android/settings/security/ScreenPinningSettingsTest.java b/tests/robotests/src/com/android/settings/security/ScreenPinningSettingsTest.java
index 045ef65..99b41f4 100644
--- a/tests/robotests/src/com/android/settings/security/ScreenPinningSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/security/ScreenPinningSettingsTest.java
@@ -18,34 +18,48 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.robolectric.Shadows.shadowOf;
+
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.Intent;
+import android.icu.text.MessageFormat;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import androidx.fragment.app.Fragment;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
+import com.android.settings.SettingsActivity;
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
import com.android.settingslib.search.SearchIndexableRaw;
+import com.android.settingslib.widget.FooterPreference;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowUserManager;
import java.util.List;
+import java.util.function.Consumer;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowLockPatternUtils.class)
public class ScreenPinningSettingsTest {
-
+ private static final String KEY_FOOTER = "screen_pinning_settings_screen_footer";
private Context mContext;
+ private UserManager mUserManager;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
+ mUserManager = mContext.getSystemService(UserManager.class);
}
@After
@@ -110,4 +124,97 @@
assertThat(indexRaws.get(0).title).isEqualTo(
mContext.getString(R.string.screen_pinning_unlock_none));
}
+
+ @Test
+ public void onCreate_lockToAppEnabled_guestModeSupported_verifyFooterText() {
+ setupLockToAppState(/* enabled= */ true);
+ setupGuestModeState(/* supported= */ true);
+
+ launchFragmentAndRunTest(fragment -> {
+ FooterPreference footer = fragment.findPreference(KEY_FOOTER);
+
+ assertThat(footer.getSummary())
+ .isEqualTo(getExpectedFooterText(/* guestModeSupported= */ true));
+ });
+ }
+
+ @Test
+ public void onCreate_lockToAppEnabled_guestModeNotSupported_verifyFooterText() {
+ setupLockToAppState(/* enabled= */ true);
+ setupGuestModeState(/* supported= */ false);
+
+ launchFragmentAndRunTest(fragment -> {
+ FooterPreference footer = fragment.findPreference(KEY_FOOTER);
+
+ assertThat(footer.getSummary())
+ .isEqualTo(getExpectedFooterText(/* guestModeSupported= */ false));
+ });
+ }
+
+ @Test
+ public void onCreate_lockToAppDisabled_guestModeSupported_verifyFooterText() {
+ setupLockToAppState(/* enabled= */ false);
+ setupGuestModeState(/* supported= */ true);
+
+ launchFragmentAndRunTest(fragment -> {
+ FooterPreference footer = fragment.findPreference(KEY_FOOTER);
+
+ assertThat(footer.getSummary())
+ .isEqualTo(getExpectedFooterText(/* guestModeSupported= */ true));
+ });
+ }
+
+ @Test
+ public void onCreate_lockToAppDisabled_guestModeNotSupported_verifyFooterText() {
+ setupLockToAppState(/* enabled= */ false);
+ setupGuestModeState(/* supported= */ false);
+
+ launchFragmentAndRunTest(fragment -> {
+ FooterPreference footer = fragment.findPreference(KEY_FOOTER);
+
+ assertThat(footer.getSummary())
+ .isEqualTo(getExpectedFooterText(/* guestModeSupported= */ false));
+ });
+ }
+
+ private CharSequence getExpectedFooterText(boolean guestModeSupported) {
+ final int stringResource = guestModeSupported
+ ? R.string.screen_pinning_guest_user_description
+ : R.string.screen_pinning_description;
+ return MessageFormat.format(mContext.getString(stringResource), 1, 2, 3);
+ }
+
+ private void setupLockToAppState(boolean enabled) {
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.LOCK_TO_APP_ENABLED,
+ enabled ? 1 : 0);
+ }
+
+ private void setupGuestModeState(boolean supported) {
+ ShadowUserManager shadowUserManager = shadowOf(mUserManager);
+ shadowUserManager.setSupportsMultipleUsers(supported);
+ shadowUserManager.setUserRestriction(
+ UserHandle.of(UserHandle.myUserId()), UserManager.DISALLOW_USER_SWITCH, !supported);
+ }
+
+ private void launchFragmentAndRunTest(Consumer<ScreenPinningSettings> test) {
+ Intent intent = new Intent();
+ intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
+ SecurityAdvancedSettings.class.getName());
+ // ScreenPinningSettings is tightly coupled with the SettingsActivity
+ // In order to successfully launch the ScreenPinningSettings, have to use an indirect route
+ // to launch the SecurityAdvancedSetting first, then replace it with ScreenPinningSettings.
+ try (ActivityController<SettingsActivity> controller =
+ ActivityController.of(new SettingsActivity(), intent)) {
+ controller.create().start().resume();
+
+ controller.get().getSupportFragmentManager().beginTransaction().replace(
+ R.id.main_content, ScreenPinningSettings.class, null).commitNow();
+ Fragment fragment = controller.get().getSupportFragmentManager()
+ .findFragmentById(R.id.main_content);
+ assertThat(fragment).isNotNull();
+ assertThat(fragment).isInstanceOf(ScreenPinningSettings.class);
+
+ test.accept((ScreenPinningSettings) fragment);
+ }
+ }
}
diff --git a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
index c0023ee..3cd1533 100644
--- a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
@@ -17,7 +17,6 @@
package com.android.settings.applications.credentials;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
@@ -36,7 +35,9 @@
import android.net.Uri;
import android.os.Looper;
import android.provider.Settings;
+import android.util.Pair;
+import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
@@ -124,19 +125,37 @@
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
- assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
+ assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
- public void getAvailabilityStatus_isHidden_returnsConditionallyUnavailable() {
+ public void isHiddenDueToNoProviderSet_hiddenDueToEmptyPair() {
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
- controller.setSimulateConnectedForTests(true);
- assertThat(controller.isConnected()).isTrue();
- controller.setSimulateHiddenForTests(Optional.of(true));
- assertThat(controller.isHiddenDueToNoProviderSet()).isTrue();
- assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isTrue();
+ }
+
+ @Test
+ public void isHiddenDueToNoProviderSet_hiddenDueToNoPrimaryProvider() {
+ CredentialManagerPreferenceController controller =
+ createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
+
+ Pair<List<CombinedProviderInfo>, CombinedProviderInfo> testPair =
+ new Pair<>(Lists.newArrayList(createCombinedProviderInfo()), null);
+ assertThat(controller.isHiddenDueToNoProviderSet(testPair)).isTrue();
+ }
+
+ @Test
+ public void isHiddenDueToNoProviderSet_validDataSoNotHidden() {
+ CredentialManagerPreferenceController controller =
+ createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
+
+ Pair<List<CombinedProviderInfo>, CombinedProviderInfo> testPair =
+ new Pair<>(
+ Lists.newArrayList(createCombinedProviderInfo()),
+ createCombinedProviderInfo());
+ assertThat(controller.isHiddenDueToNoProviderSet(testPair)).isFalse();
}
@Test
@@ -146,7 +165,7 @@
controller.setSimulateConnectedForTests(true);
controller.setSimulateHiddenForTests(Optional.of(false));
- assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
+ assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isFalse();
assertThat(controller.isConnected()).isTrue();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
@@ -170,7 +189,7 @@
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
- assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
+ assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
// Test the data is correct.
@@ -214,7 +233,7 @@
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
- assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
+ assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
// Ensure that we stay under 5 providers (one is reserved for primary).
@@ -283,7 +302,7 @@
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
- assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
+ assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
// Test the data is correct.
@@ -336,17 +355,26 @@
createControllerWithServices(
Lists.newArrayList(serviceA1, serviceB1, serviceC1, serviceC2, serviceC3));
controller.setSimulateConnectedForTests(true);
- controller.setSimulateHiddenForTests(Optional.of(false));
- assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.isConnected()).isTrue();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
- controller.displayPreference(mScreen);
- assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(3);
+ CombinedProviderInfo combinedProviderA =
+ new CombinedProviderInfo(Lists.newArrayList(serviceA1), null, false, false);
+ CombinedProviderInfo combinedProviderB =
+ new CombinedProviderInfo(Lists.newArrayList(serviceB1), null, false, false);
+ CombinedProviderInfo combinedProviderC =
+ new CombinedProviderInfo(
+ Lists.newArrayList(serviceC1, serviceC2, serviceC3), null, false, false);
+ Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair =
+ createPair(
+ Lists.newArrayList(combinedProviderA, combinedProviderB, combinedProviderC),
+ createCombinedProviderInfo());
+
+ assertThat(controller.isHiddenDueToNoProviderSet(providerPair)).isFalse();
Map<String, CredentialManagerPreferenceController.CombiPreference> prefs =
- controller.buildPreferenceList(mContext, mCredentialsPreferenceCategory);
+ controller.buildPreferenceList(mContext, providerPair);
assertThat(prefs.keySet())
.containsExactly(TEST_PACKAGE_NAME_A, TEST_PACKAGE_NAME_B, TEST_PACKAGE_NAME_C);
assertThat(prefs.size()).isEqualTo(3);
@@ -531,8 +559,7 @@
@Test
public void hasNonPrimaryServices_allServicesArePrimary() {
CredentialManagerPreferenceController controller =
- createControllerWithServices(
- Lists.newArrayList(createCredentialProviderPrimary()));
+ createControllerWithServices(Lists.newArrayList(createCredentialProviderPrimary()));
assertThat(controller.hasNonPrimaryServices()).isFalse();
}
@@ -540,8 +567,8 @@
public void hasNonPrimaryServices_mixtureOfServices() {
CredentialManagerPreferenceController controller =
createControllerWithServices(
- Lists.newArrayList(createCredentialProviderInfo(),
- createCredentialProviderPrimary()));
+ Lists.newArrayList(
+ createCredentialProviderInfo(), createCredentialProviderPrimary()));
assertThat(controller.hasNonPrimaryServices()).isTrue();
}
@@ -599,11 +626,25 @@
private CredentialProviderInfo createCredentialProviderPrimary() {
return createCredentialProviderInfoBuilder(
- "com.android.primary", "CredManProvider", "Service Label", "App Name")
+ "com.android.primary", "CredManProvider", "Service Label", "App Name")
.setPrimary(true)
.build();
}
+ private Pair<List<CombinedProviderInfo>, CombinedProviderInfo> createPair() {
+ return createPair(Lists.newArrayList(), null);
+ }
+
+ private Pair<List<CombinedProviderInfo>, CombinedProviderInfo> createPair(
+ List<CombinedProviderInfo> providers, @Nullable CombinedProviderInfo primaryProvider) {
+ return new Pair<>(providers, primaryProvider);
+ }
+
+ private CombinedProviderInfo createCombinedProviderInfo() {
+ return new CombinedProviderInfo(
+ Lists.newArrayList(createCredentialProviderInfo()), null, false, false);
+ }
+
private CredentialProviderInfo createCredentialProviderInfoWithSubtitle(
String packageName, String className, CharSequence label, CharSequence subtitle) {
ServiceInfo si = new ServiceInfo();