From 775280aa2769ba89bdbad4d9b42bc24206ff617d Mon Sep 17 00:00:00 2001 From: marcusge Date: Thu, 14 Sep 2023 18:16:26 +0000 Subject: [PATCH 01/13] Update Settings homepage color role mappings doc: go/settings-color-roles-remapping-result Test: local raven device Bug: 299907492 Change-Id: I8ba90ec61970fa2aa56f9d5d4029c22c74940120 --- res/layout/homepage_preference.xml | 3 ++- res/values-night/colors.xml | 2 +- res/values/styles.xml | 2 +- src/com/android/settings/Utils.java | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/res/layout/homepage_preference.xml b/res/layout/homepage_preference.xml index f0b1b71c20c..38cb4910d81 100644 --- a/res/layout/homepage_preference.xml +++ b/res/layout/homepage_preference.xml @@ -17,6 +17,7 @@ #783BE5 #3F5FBD @*android:color/material_grey_900 - ?androidprv:attr/materialColorSurfaceContainerHigh + ?androidprv:attr/materialColorSurfaceBright #5F6368 diff --git a/res/values/styles.xml b/res/values/styles.xml index ee78a45d066..9905bb91ed6 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -257,7 +257,7 @@ center @*android:string/config_headlineFontFamily @dimen/search_bar_text_size - ?android:attr/textColorSecondary + ?androidprv:attr/materialColorOnSurfaceVariant true diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index b41650c3264..32bb12769e1 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -1277,7 +1277,8 @@ public final class Utils extends com.android.settingslib.Utils { */ @ColorInt public static int getHomepageIconColor(Context context) { - return getColorAttrDefaultColor(context, android.R.attr.textColorPrimary); + return getColorAttrDefaultColor( + context, com.android.internal.R.attr.materialColorOnSurface); } /** From 8d80de844688995e184bb7104f33d2611396d41d Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Fri, 9 Feb 2024 13:00:56 +0800 Subject: [PATCH 02/13] Fix logging format and add test cases For logging purpose, CachedBluetoothDevice#toString is more clear then BluetoothDevice#toString, so change to use CachedBluetoothDevice#toString Bug: 307890347 Test: atest HearingDevicePairingFragmentTest Change-Id: Ia0af65565ca7067fa6c4d5db286c3739fb65c1d2 --- .../HearingDevicePairingFragment.java | 18 +++++++++--------- .../HearingDevicePairingFragmentTest.java | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/com/android/settings/accessibility/HearingDevicePairingFragment.java b/src/com/android/settings/accessibility/HearingDevicePairingFragment.java index 0b762f3b589..c797559c290 100644 --- a/src/com/android/settings/accessibility/HearingDevicePairingFragment.java +++ b/src/com/android/settings/accessibility/HearingDevicePairingFragment.java @@ -192,7 +192,7 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im public void onDeviceBondStateChanged(@NonNull CachedBluetoothDevice cachedDevice, int bondState) { if (DEBUG) { - Log.d(TAG, "onDeviceBondStateChanged: " + cachedDevice.getDevice() + ", state = " + Log.d(TAG, "onDeviceBondStateChanged: " + cachedDevice + ", state = " + bondState); } if (bondState == BluetoothDevice.BOND_BONDED) { @@ -276,13 +276,13 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im } mDevicePreferenceMap.put(cachedDevice, preference); if (DEBUG) { - Log.d(TAG, "Add device. device: " + cachedDevice.getDevice()); + Log.d(TAG, "Add device. device: " + cachedDevice); } } void removeDevice(CachedBluetoothDevice cachedDevice) { if (DEBUG) { - Log.d(TAG, "removeDevice: " + cachedDevice.getDevice()); + Log.d(TAG, "removeDevice: " + cachedDevice); } BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice); if (mAvailableHearingDeviceGroup != null && preference != null) { @@ -331,13 +331,13 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im cachedDevice = mCachedDeviceManager.addDevice(device); } else if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) { if (DEBUG) { - Log.d(TAG, "Skip this device, already bonded: " + cachedDevice.getDevice()); + Log.d(TAG, "Skip this device, already bonded: " + cachedDevice); } return; } if (cachedDevice.getHearingAidInfo() == null) { if (DEBUG) { - Log.d(TAG, "Set hearing aid info on device: " + cachedDevice.getDevice()); + Log.d(TAG, "Set hearing aid info on device: " + cachedDevice); } cachedDevice.setHearingAidInfo(new HearingAidInfo.Builder().build()); } @@ -455,7 +455,7 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im void discoverServices(CachedBluetoothDevice cachedDevice) { if (DEBUG) { - Log.d(TAG, "connectGattToCheckCompatibility, device: " + cachedDevice.getDevice()); + Log.d(TAG, "connectGattToCheckCompatibility, device: " + cachedDevice); } BluetoothGatt gatt = cachedDevice.getDevice().connectGatt(getContext(), false, new BluetoothGattCallback() { @@ -465,7 +465,7 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im super.onConnectionStateChange(gatt, status, newState); if (DEBUG) { Log.d(TAG, "onConnectionStateChange, status: " + status + ", newState: " - + newState + ", device: " + cachedDevice.getDevice()); + + newState + ", device: " + cachedDevice); } if (status == GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) { @@ -481,14 +481,14 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im super.onServicesDiscovered(gatt, status); if (DEBUG) { Log.d(TAG, "onServicesDiscovered, status: " + status + ", device: " - + cachedDevice.getDevice()); + + cachedDevice); } if (status == GATT_SUCCESS) { if (gatt.getService(BluetoothUuid.HEARING_AID.getUuid()) != null || gatt.getService(BluetoothUuid.HAS.getUuid()) != null) { if (DEBUG) { Log.d(TAG, "compatible with Android, device: " - + cachedDevice.getDevice()); + + cachedDevice); } addDevice(cachedDevice); } diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingFragmentTest.java index e14686e4a18..db82be6db3a 100644 --- a/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/HearingDevicePairingFragmentTest.java @@ -178,7 +178,7 @@ public class HearingDevicePairingFragmentTest { } @Test - public void handleLeScanResult_isNotAndroidCompatible_() { + public void handleLeScanResult_isNotAndroidCompatible_discoverServices() { ScanResult scanResult = mock(ScanResult.class); doReturn(mDevice).when(scanResult).getDevice(); doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice); @@ -189,6 +189,19 @@ public class HearingDevicePairingFragmentTest { verify(mFragment).discoverServices(mCachedDevice); } + @Test + public void handleLeScanResult_alreadyBonded_doNothing() { + ScanResult scanResult = mock(ScanResult.class); + doReturn(mDevice).when(scanResult).getDevice(); + doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice); + doReturn(BluetoothDevice.BOND_BONDED).when(mCachedDevice).getBondState(); + + mFragment.handleLeScanResult(scanResult); + + verify(mFragment, never()).addDevice(mCachedDevice); + verify(mFragment, never()).discoverServices(mCachedDevice); + } + @Test public void onProfileConnectionStateChanged_deviceConnected_inSelectedList_finish() { doReturn(true).when(mCachedDevice).isConnected(); From deba8599d86676510ace759f86bfc84795a0748f Mon Sep 17 00:00:00 2001 From: Chun-Ku Lin Date: Sat, 9 Mar 2024 03:26:13 +0000 Subject: [PATCH 03/13] Reflect QS shortcut changes in a11y pages. Bug: 314843909 Test: manual (modify qs tiles in QS panel while the a11y page is open, verify the a11y page reflect the qs changes) Test: atest com.android.settings.accessibility Test: atest com.android.settings.accessibility.shortcuts Flag: ACONFIG android.view.accessibility.a11y_qs_shortcut Change-Id: Ie5b46459faab902912a214ca131eb5d0b105a7ef --- .../accessibility/AccessibilitySettings.java | 3 + ...cessibilityShortcutPreferenceFragment.java | 3 + .../accessibility/ColorAndMotionFragment.java | 3 + .../ToggleFeaturePreferenceFragment.java | 3 + .../gestures/OneHandedSettingsUtils.java | 15 ++++ .../AccessibilitySettingsTest.java | 85 +++++++++++-------- .../ToggleFeaturePreferenceFragmentTest.java | 34 +++++++- ...enMagnificationPreferenceFragmentTest.java | 37 +++++++- .../gestures/OneHandedSettingsUtilsTest.java | 72 +++++++++++++++- 9 files changed, 217 insertions(+), 38 deletions(-) diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index fe89bf203b4..35fe6e4e600 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -175,6 +175,9 @@ public class AccessibilitySettings extends DashboardFragment implements // Observe changes from accessibility selection menu shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); + if (android.view.accessibility.Flags.a11yQsShortcut()) { + shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS); + } shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_STICKY_KEYS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SLOW_KEYS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS); diff --git a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java index 8af284db15f..41c5d750b69 100644 --- a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java +++ b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java @@ -125,6 +125,9 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted final List shortcutFeatureKeys = new ArrayList<>(); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); + if (android.view.accessibility.Flags.a11yQsShortcut()) { + shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS); + } mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler()); mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> { updateShortcutPreferenceData(); diff --git a/src/com/android/settings/accessibility/ColorAndMotionFragment.java b/src/com/android/settings/accessibility/ColorAndMotionFragment.java index 28c533532d7..4ea22260c79 100644 --- a/src/com/android/settings/accessibility/ColorAndMotionFragment.java +++ b/src/com/android/settings/accessibility/ColorAndMotionFragment.java @@ -74,6 +74,9 @@ public class ColorAndMotionFragment extends DashboardFragment { mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED); mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); + if (android.view.accessibility.Flags.a11yQsShortcut()) { + mShortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS); + } if (Flags.forceInvertColor()) { mShortcutFeatureKeys.add(ToggleForceInvertPreferenceController.SETTINGS_KEY); } diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index 6d5f5362e25..ed47007fbcf 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -179,6 +179,9 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment final List shortcutFeatureKeys = new ArrayList<>(); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); + if (android.view.accessibility.Flags.a11yQsShortcut()) { + shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS); + } return shortcutFeatureKeys; } diff --git a/src/com/android/settings/gestures/OneHandedSettingsUtils.java b/src/com/android/settings/gestures/OneHandedSettingsUtils.java index 04898dc0219..fe7db4f7c3e 100644 --- a/src/com/android/settings/gestures/OneHandedSettingsUtils.java +++ b/src/com/android/settings/gestures/OneHandedSettingsUtils.java @@ -50,6 +50,8 @@ public class OneHandedSettingsUtils { Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); static final Uri HARDWARE_SHORTCUT_ENABLED_URI = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); + static final Uri QS_SHORTCUT_ENABLED_URI = + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_QS_TARGETS); public enum OneHandedTimeout { NEVER(0), SHORT(4), MEDIUM(8), LONG(12); @@ -254,6 +256,16 @@ public class OneHandedSettingsUtils { if (!TextUtils.isEmpty(targetsHW) && targetsHW.contains(ONE_HANDED_MODE_TARGET_NAME)) { return true; } + + if (android.view.accessibility.Flags.a11yQsShortcut()) { + // Checks QS_SHORTCUT_KEY + final String targetsQs = Settings.Secure.getStringForUser(context.getContentResolver(), + Settings.Secure.ACCESSIBILITY_QS_TARGETS, sCurrentUserId); + if (!TextUtils.isEmpty(targetsQs) && targetsQs.contains(ONE_HANDED_MODE_TARGET_NAME)) { + return true; + } + } + return false; } @@ -301,6 +313,9 @@ public class OneHandedSettingsUtils { resolver.registerContentObserver(SHOW_NOTIFICATION_ENABLED_URI, true, this); resolver.registerContentObserver(SOFTWARE_SHORTCUT_ENABLED_URI, true, this); resolver.registerContentObserver(HARDWARE_SHORTCUT_ENABLED_URI, true, this); + if (android.view.accessibility.Flags.a11yQsShortcut()) { + resolver.registerContentObserver(QS_SHORTCUT_ENABLED_URI, true, this); + } } @Override diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java index 05e56ca74a4..624a39ab330 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java @@ -38,8 +38,12 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.database.ContentObserver; import android.os.Build; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.Flags; import androidx.fragment.app.Fragment; import androidx.test.core.app.ApplicationProvider; @@ -59,6 +63,8 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.search.SearchIndexableRaw; import com.android.settingslib.testutils.shadow.ShadowColorDisplayManager; +import com.google.common.truth.BooleanSubject; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -103,6 +109,7 @@ public class AccessibilitySettingsTest { @Rule public final MockitoRule mocks = MockitoJUnit.rule(); + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private final Context mContext = ApplicationProvider.getApplicationContext(); @Spy private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo( @@ -316,30 +323,39 @@ public class AccessibilitySettingsTest { } @Test - public void onCreate_haveRegisterToSpecificUrisAndActions() { + @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) + public void onCreate_flagDisabled_haveRegisterToSpecificUrisAndActions() { setupFragment(); - ShadowContentResolver shadowContentResolver = shadowOf(mContext.getContentResolver()); - Collection a11yButtonTargetsObservers = - shadowContentResolver.getContentObservers( - Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS)); - Collection a11yShortcutTargetServiceObservers = - shadowContentResolver.getContentObservers(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, + AccessibilitySettingsContentObserver.class).isTrue(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + AccessibilitySettingsContentObserver.class).isTrue(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS, + AccessibilitySettingsContentObserver.class).isFalse(); + List broadcastReceivers = + shadowOf((Application) ApplicationProvider.getApplicationContext()) + .getRegisteredReceivers() + .stream().map(wrapper -> wrapper.broadcastReceiver).toList(); + assertThat(broadcastReceivers.stream().anyMatch( + broadcastReceiver -> broadcastReceiver instanceof PackageMonitor)).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) + public void onCreate_flagEnabled_haveRegisterToSpecificUrisAndActions() { + setupFragment(); + + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, + AccessibilitySettingsContentObserver.class).isTrue(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + AccessibilitySettingsContentObserver.class).isTrue(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS, + AccessibilitySettingsContentObserver.class).isTrue(); List broadcastReceivers = shadowOf((Application) ApplicationProvider.getApplicationContext()) .getRegisteredReceivers() .stream().map(wrapper -> wrapper.broadcastReceiver).toList(); - assertThat( - a11yButtonTargetsObservers.stream() - .anyMatch(contentObserver -> - contentObserver instanceof AccessibilitySettingsContentObserver)) - .isTrue(); - assertThat( - a11yShortcutTargetServiceObservers.stream() - .anyMatch(contentObserver -> - contentObserver instanceof AccessibilitySettingsContentObserver)) - .isTrue(); assertThat(broadcastReceivers.stream().anyMatch( broadcastReceiver -> broadcastReceiver instanceof PackageMonitor)).isTrue(); } @@ -350,27 +366,16 @@ public class AccessibilitySettingsTest { mActivityController.pause().stop().destroy(); - ShadowContentResolver shadowContentResolver = shadowOf(mContext.getContentResolver()); - Collection a11yButtonTargetsObservers = - shadowContentResolver.getContentObservers( - Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS)); - Collection a11yShortcutTargetServiceObservers = - shadowContentResolver.getContentObservers(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, + AccessibilitySettingsContentObserver.class).isFalse(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + AccessibilitySettingsContentObserver.class).isFalse(); + assertUriObserversContainsClazz(Settings.Secure.ACCESSIBILITY_QS_TARGETS, + AccessibilitySettingsContentObserver.class).isFalse(); List broadcastReceivers = shadowOf((Application) ApplicationProvider.getApplicationContext()) .getRegisteredReceivers() .stream().map(wrapper -> wrapper.broadcastReceiver).toList(); - assertThat( - a11yButtonTargetsObservers.stream() - .anyMatch(contentObserver -> - contentObserver instanceof AccessibilitySettingsContentObserver)) - .isFalse(); - assertThat( - a11yShortcutTargetServiceObservers.stream() - .anyMatch(contentObserver -> - contentObserver instanceof AccessibilitySettingsContentObserver)) - .isFalse(); assertThat(broadcastReceivers.stream().anyMatch( broadcastReceiver -> broadcastReceiver instanceof PackageMonitor)).isFalse(); } @@ -491,4 +496,14 @@ public class AccessibilitySettingsTest { Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, enabled ? componentName.flattenToString() : ""); } + + private BooleanSubject assertUriObserversContainsClazz( + String settingUri, Class clazz) { + ShadowContentResolver shadowContentResolver = shadowOf(mContext.getContentResolver()); + Collection observers = + shadowContentResolver.getContentObservers( + Settings.Secure.getUriFor(settingUri)); + + return assertThat(observers.stream().anyMatch(clazz::isInstance)); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index e963bd01f68..6fb1c3fb08a 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -146,8 +147,9 @@ public class ToggleFeaturePreferenceFragmentTest { } @Test + @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) @Config(shadows = {ShadowFragment.class}) - public void onResume_haveRegisterToSpecificUris() { + public void onResume_flagEnabled_haveRegisterToSpecificUris() { mFragment.onAttach(mContext); mFragment.onCreate(Bundle.EMPTY); @@ -162,6 +164,36 @@ public class ToggleFeaturePreferenceFragmentTest { Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)), eq(false), any(AccessibilitySettingsContentObserver.class)); + verify(mContentResolver).registerContentObserver( + eq(Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_QS_TARGETS)), + eq(false), + any(AccessibilitySettingsContentObserver.class)); + } + + @Test + @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) + @Config(shadows = {ShadowFragment.class}) + public void onResume_flagDisabled_haveRegisterToSpecificUris() { + mFragment.onAttach(mContext); + mFragment.onCreate(Bundle.EMPTY); + + mFragment.onResume(); + + verify(mContentResolver).registerContentObserver( + eq(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS)), + eq(false), + any(AccessibilitySettingsContentObserver.class)); + verify(mContentResolver).registerContentObserver( + eq(Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)), + eq(false), + any(AccessibilitySettingsContentObserver.class)); + verify(mContentResolver, never()).registerContentObserver( + eq(Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_QS_TARGETS)), + eq(false), + any(AccessibilitySettingsContentObserver.class)); } @Test diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java index 3d24fbb7237..1d85705a9e7 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java @@ -292,7 +292,39 @@ public class ToggleScreenMagnificationPreferenceFragmentTest { } @Test - public void onResume_haveRegisterToSpecificUris() { + @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) + public void onResume_flagEnabled_haveRegisterToSpecificUris() { + ShadowContentResolver shadowContentResolver = Shadows.shadowOf( + mContext.getContentResolver()); + Uri[] observedUri = new Uri[]{ + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS), + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE), + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_QS_TARGETS), + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED), + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED) + }; + for (Uri uri : observedUri) { + // verify no observer registered before launching the fragment + assertThat(shadowContentResolver.getContentObservers(uri)).isEmpty(); + } + + mFragController.create(R.id.main_content, /* bundle= */ null).start().resume(); + + for (Uri uri : observedUri) { + Collection observers = shadowContentResolver.getContentObservers(uri); + assertThat(observers.size()).isEqualTo(1); + assertThat(observers.stream().findFirst().get()).isInstanceOf( + AccessibilitySettingsContentObserver.class); + } + } + + @Test + @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT) + public void onResume_flagDisabled_haveRegisterToSpecificUris() { ShadowContentResolver shadowContentResolver = Shadows.shadowOf( mContext.getContentResolver()); Uri[] observedUri = new Uri[]{ @@ -317,6 +349,9 @@ public class ToggleScreenMagnificationPreferenceFragmentTest { assertThat(observers.stream().findFirst().get()).isInstanceOf( AccessibilitySettingsContentObserver.class); } + assertThat(shadowContentResolver.getContentObservers( + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_QS_TARGETS))).hasSize(0); } @Test diff --git a/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsUtilsTest.java b/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsUtilsTest.java index 9559043570c..ee5f72ed2d4 100644 --- a/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsUtilsTest.java +++ b/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsUtilsTest.java @@ -16,13 +16,20 @@ package com.android.settings.gestures; +import static com.android.settings.gestures.OneHandedSettingsUtils.ONE_HANDED_MODE_TARGET_NAME; + import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; +import android.view.accessibility.Flags; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -30,7 +37,8 @@ import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class OneHandedSettingsUtilsTest { - + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final int OFF = 0; private static final int ON = 1; @@ -120,4 +128,66 @@ public class OneHandedSettingsUtilsTest { OneHandedSettingsUtils.OneHandedTimeout.LONG.getValue(), mCurrentUserId)) .isEqualTo(12); } + + @Test + public void getShortcutEnabled_a11yButtonVolumeKeysShortcutEnabled_returnTrue() { + setupShortcuts( + /* enableFab= */ true, /* enableVolumeKeys= */ true, /* enableQs=*/ false); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isTrue(); + } + + @Test + public void getShortcutEnabled_a11yButtonShortcutEnabled_returnTrue() { + setupShortcuts( + /* enableFab= */ true, /* enableVolumeKeys= */ false, /* enableQs=*/ false); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isTrue(); + } + + @Test + public void getShortcutEnabled_volumeKeysShortcutEnabled_returnTrue() { + setupShortcuts( + /* enableFab= */ false, /* enableVolumeKeys= */ true, /* enableQs=*/ false); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isTrue(); + } + + @Test + public void getShortcutEnabled_noShortcutsEnabled_returnFalse() { + setupShortcuts( + /* enableFab= */ false, /* enableVolumeKeys= */ false, /* enableQs=*/ false); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isFalse(); + } + + @Test + @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) + public void getShortcutEnabled_qsShortcutEnabled_returnTrue() { + setupShortcuts( + /* enableFab= */ false, /* enableVolumeKeys= */ false, /* enableQs=*/ true); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isTrue(); + } + + @Test + @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) + public void getShortcutEnabled_flagDisabled_qsShortcutEnabled_returnFalse() { + setupShortcuts( + /* enableFab= */ false, /* enableVolumeKeys= */ false, /* enableQs=*/ true); + + assertThat(OneHandedSettingsUtils.getShortcutEnabled(mContext)).isFalse(); + } + + private void setupShortcuts(boolean enableFab, boolean enableVolumeKeys, boolean enableQs) { + setupShortcut(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, enableFab); + setupShortcut(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, enableVolumeKeys); + setupShortcut(Settings.Secure.ACCESSIBILITY_QS_TARGETS, enableQs); + } + + private void setupShortcut(String shortcutSettingKey, boolean enabled) { + final String targetName = enabled ? ONE_HANDED_MODE_TARGET_NAME : ""; + Settings.Secure.putStringForUser( + mContext.getContentResolver(), shortcutSettingKey, targetName, mCurrentUserId); + } } From e0b5de28aebff47bb8a9f5fe531a80e66aeca8fa Mon Sep 17 00:00:00 2001 From: songferngwang Date: Tue, 12 Mar 2024 18:13:13 +0000 Subject: [PATCH 04/13] Fix the settings crash When the user select mobile network settings item with subid=-1 in the search list, then some of objects are not initialized. Bug: 325956182 Test: atest AutoSelectPreferenceControllerTest atest MobileNetworkPhoneNumberPreferenceControllerTest atest MobileNetworkSpnPreferenceControllerTest atest MobileNetworkImeiPreferenceControllerTest atest WifiCallingPreferenceControllerTest Change-Id: I17c24b6f542392f018c65b689862f9735fad4b9f --- .../telephony/MobileNetworkEidPreferenceController.kt | 7 +++++++ .../telephony/MobileNetworkImeiPreferenceController.kt | 7 +++++++ .../MobileNetworkPhoneNumberPreferenceController.kt | 8 ++++++++ .../telephony/MobileNetworkSpnPreferenceController.kt | 10 ++++++++++ .../telephony/WifiCallingPreferenceController.kt | 8 ++++++++ .../telephony/gsm/AutoSelectPreferenceController.kt | 3 ++- 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/network/telephony/MobileNetworkEidPreferenceController.kt b/src/com/android/settings/network/telephony/MobileNetworkEidPreferenceController.kt index 907bab1217c..1e635a587fe 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkEidPreferenceController.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkEidPreferenceController.kt @@ -84,6 +84,13 @@ open class MobileNetworkEidPreferenceController(context: Context, key: String) : } override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { + if (!this::lazyViewModel.isInitialized) { + Log.e( + this.javaClass.simpleName, + "lateinit property lazyViewModel has not been initialized" + ) + return + } preference.isVisible = false val viewModel by lazyViewModel diff --git a/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceController.kt b/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceController.kt index 8ec313b0483..e1346819f7d 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceController.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkImeiPreferenceController.kt @@ -78,6 +78,13 @@ class MobileNetworkImeiPreferenceController(context: Context, key: String) : } override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { + if (!this::lazyViewModel.isInitialized) { + Log.e( + this.javaClass.simpleName, + "lateinit property lazyViewModel has not been initialized" + ) + return + } val viewModel by lazyViewModel val coroutineScope = viewLifecycleOwner.lifecycleScope diff --git a/src/com/android/settings/network/telephony/MobileNetworkPhoneNumberPreferenceController.kt b/src/com/android/settings/network/telephony/MobileNetworkPhoneNumberPreferenceController.kt index 65a4b7e6dce..10a8b53e5d4 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkPhoneNumberPreferenceController.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkPhoneNumberPreferenceController.kt @@ -19,6 +19,7 @@ package com.android.settings.network.telephony import android.content.Context import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager +import android.util.Log import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels @@ -66,6 +67,13 @@ class MobileNetworkPhoneNumberPreferenceController(context: Context, key: String } override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { + if (!this::lazyViewModel.isInitialized) { + Log.e( + this.javaClass.simpleName, + "lateinit property lazyViewModel has not been initialized" + ) + return + } val viewModel by lazyViewModel val coroutineScope = viewLifecycleOwner.lifecycleScope diff --git a/src/com/android/settings/network/telephony/MobileNetworkSpnPreferenceController.kt b/src/com/android/settings/network/telephony/MobileNetworkSpnPreferenceController.kt index ac055b02f06..4736eb7df83 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSpnPreferenceController.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkSpnPreferenceController.kt @@ -19,6 +19,7 @@ package com.android.settings.network.telephony import android.content.Context import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager +import android.util.Log import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels @@ -26,6 +27,7 @@ import androidx.lifecycle.LifecycleOwner import androidx.preference.Preference import androidx.preference.PreferenceScreen import com.android.settings.flags.Flags +import com.android.settings.network.SimOnboardingActivity import com.android.settings.network.SubscriptionInfoListViewModel import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle @@ -57,6 +59,14 @@ class MobileNetworkSpnPreferenceController(context: Context, key: String) : } override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { + if (!this::lazyViewModel.isInitialized) { + Log.e( + this.javaClass.simpleName, + "lateinit property lazyViewModel has not been initialized" + ) + return + } + val viewModel by lazyViewModel viewModel.subscriptionInfoListFlow diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt index 0ee1d87cd62..f184092821e 100644 --- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt +++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt @@ -22,6 +22,7 @@ import android.telecom.TelecomManager import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.telephony.ims.ImsMmTelManager +import android.util.Log import androidx.lifecycle.LifecycleOwner import androidx.preference.Preference import androidx.preference.PreferenceScreen @@ -76,6 +77,13 @@ open class WifiCallingPreferenceController @JvmOverloads constructor( } override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { + if(mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID){ + Log.e( + this.javaClass.simpleName, + "mSubId is INVALID_SUBSCRIPTION_ID" + ) + return + } wifiCallingRepositoryFactory(mSubId).wifiCallingReadyFlow() .collectLatestWithLifecycle(viewLifecycleOwner) { isReady -> preference.isVisible = isReady diff --git a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.kt b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.kt index d709574997a..67a23563727 100644 --- a/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.kt +++ b/src/com/android/settings/network/telephony/gsm/AutoSelectPreferenceController.kt @@ -23,6 +23,7 @@ import android.os.PersistableBundle import android.provider.Settings import android.telephony.CarrierConfigManager import android.telephony.ServiceState +import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import androidx.annotation.VisibleForTesting import androidx.compose.runtime.Composable @@ -80,7 +81,7 @@ class AutoSelectPreferenceController @JvmOverloads constructor( @VisibleForTesting var progressDialog: ProgressDialog? = null - private var subId by notNull() + private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID /** * Initialization based on given subscription id. From 11c9f1bff490f1eb0185ae3eab75ec4e27cbd858 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Thu, 29 Feb 2024 22:39:33 +0800 Subject: [PATCH 05/13] [DataStore] Migrate LocaleNotification SharedPreferences to BackupRestoreStorage Bug: 325144964 Test: Manual tests (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:cc0a94d73247fa81eff0054a0dcf06e99c45adb1) Merged-In: I195ee91f29030fcfe7176ed0c18fb2e2d0d40257 Change-Id: I195ee91f29030fcfe7176ed0c18fb2e2d0d40257 --- src/com/android/settings/SettingsApplication.java | 6 +++++- .../android/settings/backup/SettingsBackupHelper.java | 5 ----- .../localepicker/LocaleNotificationDataManager.java | 9 +++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/com/android/settings/SettingsApplication.java b/src/com/android/settings/SettingsApplication.java index 1a53b835f46..c0d2445821f 100644 --- a/src/com/android/settings/SettingsApplication.java +++ b/src/com/android/settings/SettingsApplication.java @@ -30,6 +30,7 @@ import com.android.settings.activityembedding.ActivityEmbeddingUtils; import com.android.settings.core.instrumentation.ElapsedTimeUtils; import com.android.settings.fuelgauge.BatterySettingsStorage; import com.android.settings.homepage.SettingsHomepageActivity; +import com.android.settings.localepicker.LocaleNotificationDataManager; import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactoryImpl; import com.android.settings.spa.SettingsSpaEnvironment; @@ -56,7 +57,10 @@ public class SettingsApplication extends Application { public void onCreate() { super.onCreate(); - BackupRestoreStorageManager.getInstance(this).add(new BatterySettingsStorage(this)); + BackupRestoreStorageManager.getInstance(this) + .add( + new BatterySettingsStorage(this), + LocaleNotificationDataManager.getSharedPreferencesStorage(this)); // Add null checking to avoid test case failed. if (getApplicationContext() != null) { diff --git a/src/com/android/settings/backup/SettingsBackupHelper.java b/src/com/android/settings/backup/SettingsBackupHelper.java index 0861af29d6c..556ab725c28 100644 --- a/src/com/android/settings/backup/SettingsBackupHelper.java +++ b/src/com/android/settings/backup/SettingsBackupHelper.java @@ -16,10 +16,8 @@ package com.android.settings.backup; -import static com.android.settings.localepicker.LocaleNotificationDataManager.LOCALE_NOTIFICATION; import android.app.backup.BackupAgentHelper; -import android.app.backup.SharedPreferencesBackupHelper; import com.android.settings.flags.Flags; import com.android.settings.onboarding.OnboardingFeatureProvider; @@ -29,15 +27,12 @@ import com.android.settingslib.datastore.BackupRestoreStorageManager; /** Backup agent for Settings APK */ public class SettingsBackupHelper extends BackupAgentHelper { - private static final String PREF_LOCALE_NOTIFICATION = "localeNotificationSharedPref"; public static final String SOUND_BACKUP_HELPER = "SoundSettingsBackup"; @Override public void onCreate() { super.onCreate(); BackupRestoreStorageManager.getInstance(this).addBackupAgentHelpers(this); - addHelper(PREF_LOCALE_NOTIFICATION, - new SharedPreferencesBackupHelper(this, LOCALE_NOTIFICATION)); if (Flags.enableSoundBackup()) { OnboardingFeatureProvider onboardingFeatureProvider = FeatureFactory.getFeatureFactory().getOnboardingFeatureProvider(); diff --git a/src/com/android/settings/localepicker/LocaleNotificationDataManager.java b/src/com/android/settings/localepicker/LocaleNotificationDataManager.java index 0e89366fbcc..6ec578d165d 100644 --- a/src/com/android/settings/localepicker/LocaleNotificationDataManager.java +++ b/src/com/android/settings/localepicker/LocaleNotificationDataManager.java @@ -19,8 +19,11 @@ package com.android.settings.localepicker; import android.content.Context; import android.content.SharedPreferences; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import com.android.settingslib.datastore.SharedPreferencesStorage; + import com.google.gson.Gson; import java.util.HashMap; @@ -43,6 +46,12 @@ public class LocaleNotificationDataManager { this.mContext = context; } + /** Returns the underlying {@link SharedPreferences} storage. */ + @NonNull + public static SharedPreferencesStorage getSharedPreferencesStorage(@NonNull Context context) { + return new SharedPreferencesStorage(context, LOCALE_NOTIFICATION, Context.MODE_PRIVATE); + } + private static SharedPreferences getSharedPreferences(Context context) { return context.getSharedPreferences(LOCALE_NOTIFICATION, Context.MODE_PRIVATE); } From a76aade27ef612368d06fb48d3cf19d040b4f85f Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Wed, 6 Mar 2024 17:57:57 +0800 Subject: [PATCH 06/13] [DataStore] Support backup data with compression Bug: 325144964 Test: Manual tests (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:23821a69f31c5712cb369bfdd7776a66926dc5ac) Merged-In: Id73a30c2b58fafa6d9d1cbe247a6d9c38397394b Change-Id: Id73a30c2b58fafa6d9d1cbe247a6d9c38397394b --- .../fuelgauge/BatterySettingsStorage.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/com/android/settings/fuelgauge/BatterySettingsStorage.java b/src/com/android/settings/fuelgauge/BatterySettingsStorage.java index ff3223fce73..ca78cc254e2 100644 --- a/src/com/android/settings/fuelgauge/BatterySettingsStorage.java +++ b/src/com/android/settings/fuelgauge/BatterySettingsStorage.java @@ -39,6 +39,7 @@ import androidx.annotation.Nullable; import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.datastore.BackupCodec; import com.android.settingslib.datastore.BackupContext; import com.android.settingslib.datastore.BackupRestoreEntity; import com.android.settingslib.datastore.BackupRestoreStorageManager; @@ -159,6 +160,22 @@ public final class BatterySettingsStorage extends ObservableBackupRestoreStorage return Arrays.asList(allowlistedApps); } + @NonNull + @Override + public OutputStream wrapBackupOutputStream( + @NonNull BackupCodec codec, @NonNull OutputStream outputStream) { + // not using any codec for backward compatibility + return outputStream; + } + + @NonNull + @Override + public InputStream wrapRestoreInputStream( + @NonNull BackupCodec codec, @NonNull InputStream inputStream) { + // not using any codec for backward compatibility + return inputStream; + } + @Override public void writeNewStateDescription(@NonNull ParcelFileDescriptor newState) { BatterySettingsMigrateChecker.verifySaverConfiguration(mApplication); From 85e130ad7a94097e790a3c6b74bec7e26eeb452d Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Thu, 7 Mar 2024 10:03:19 +0800 Subject: [PATCH 07/13] [DataStore] Support backup data state computation Bug: 325144964 Test: Manual tests (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:5847d50e7bcab7d45d03351df802fb1228226aa5) Merged-In: I8ad4f2788f9f364eceb1bca841830e1f3a68fe77 Change-Id: I8ad4f2788f9f364eceb1bca841830e1f3a68fe77 --- src/com/android/settings/fuelgauge/BatterySettingsStorage.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatterySettingsStorage.java b/src/com/android/settings/fuelgauge/BatterySettingsStorage.java index ca78cc254e2..36fa5bbd029 100644 --- a/src/com/android/settings/fuelgauge/BatterySettingsStorage.java +++ b/src/com/android/settings/fuelgauge/BatterySettingsStorage.java @@ -26,7 +26,6 @@ import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.os.Build; import android.os.IDeviceIdleController; -import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -177,7 +176,7 @@ public final class BatterySettingsStorage extends ObservableBackupRestoreStorage } @Override - public void writeNewStateDescription(@NonNull ParcelFileDescriptor newState) { + public void onRestoreFinished() { BatterySettingsMigrateChecker.verifySaverConfiguration(mApplication); performRestoreIfNeeded(); } From b03de69d79ddabc5e638b8daaa2f3fd43f38795e Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Fri, 8 Mar 2024 11:27:30 +0800 Subject: [PATCH 08/13] Sort applications to avoid backup data change Bug: 328698829 Fix: 328698829 Test: Manual test (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ea8ed47d070be8b74297f1ca330dfdf9b196ec29) Merged-In: I0e5bf1145d972db136bbd85571f015b4ab6fb171 Change-Id: I0e5bf1145d972db136bbd85571f015b4ab6fb171 --- .../fuelgauge/BatterySettingsStorage.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatterySettingsStorage.java b/src/com/android/settings/fuelgauge/BatterySettingsStorage.java index 36fa5bbd029..99edbecd617 100644 --- a/src/com/android/settings/fuelgauge/BatterySettingsStorage.java +++ b/src/com/android/settings/fuelgauge/BatterySettingsStorage.java @@ -52,6 +52,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.List; /** An implementation to backup and restore battery configurations. */ @@ -321,8 +322,8 @@ public final class BatterySettingsStorage extends ObservableBackupRestoreStorage @NonNull BackupContext backupContext, @NonNull OutputStream outputStream) throws IOException { final long timestamp = System.currentTimeMillis(); - final ArraySet applications = getInstalledApplications(); - if (applications == null || applications.isEmpty()) { + final ApplicationInfo[] applications = getInstalledApplications(); + if (applications.length == 0) { Log.w(TAG, "no data found in the getInstalledApplications()"); return EntityBackupResult.DELETE; } @@ -360,15 +361,24 @@ public final class BatterySettingsStorage extends ObservableBackupRestoreStorage TAG, String.format( "backup getInstalledApplications():%d count=%d in %d/ms", - applications.size(), + applications.length, backupCount, (System.currentTimeMillis() - timestamp))); return EntityBackupResult.UPDATE; } - private @Nullable ArraySet getInstalledApplications() { - return BatteryOptimizeUtils.getInstalledApplications( - mApplication, AppGlobals.getPackageManager()); + private ApplicationInfo[] getInstalledApplications() { + ArraySet installedApplications = + BatteryOptimizeUtils.getInstalledApplications( + mApplication, AppGlobals.getPackageManager()); + ApplicationInfo[] applicationInfos = new ApplicationInfo[0]; + if (installedApplications == null || installedApplications.isEmpty()) { + return applicationInfos; + } + applicationInfos = installedApplications.toArray(applicationInfos); + // sort the list to ensure backup data is stable + Arrays.sort(applicationInfos, Comparator.comparing(info -> info.packageName)); + return applicationInfos; } static @NonNull SharedPreferences getSharedPreferences(Context context) { From a234e1696e356aa9829d94b1d145064ffe69ecc1 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Fri, 8 Mar 2024 08:54:59 +0800 Subject: [PATCH 09/13] Enable android:restoreAnyVersion Bug: 317149339 Test: Manual tests (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:65e283dca7ac2d353fe36d95bfd133767b2b347f) Merged-In: If1f37519b59159c5cd6fa49a3e23dcc85348336e Change-Id: If1f37519b59159c5cd6fa49a3e23dcc85348336e --- AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index aed51f3e0eb..5ffbbf84d01 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -153,6 +153,7 @@ android:requiredForAllUsers="true" android:supportsRtl="true" android:backupAgent="com.android.settings.backup.SettingsBackupHelper" + android:restoreAnyVersion="true" android:usesCleartextTraffic="true" android:defaultToDeviceProtectedStorage="true" android:directBootAware="true" From 081dbac72e13b3ec09b2f654df2737ae85b80926 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Fri, 8 Mar 2024 19:36:14 +0800 Subject: [PATCH 10/13] Reset app preferences does not trigger backup for App battery usages Bug: 328712606 Fix: 328712606 Test: UT && Verify logcat when change/reset App battery usages (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ef8a51fac85eedee8f9509b9f7c002281c1ae093) Merged-In: Ia3917c8dc2654185f5f048c048362fd47379b7d1 Change-Id: Ia3917c8dc2654185f5f048c048362fd47379b7d1 --- .../fuelgauge/AdvancedPowerUsageDetail.java | 9 ------ .../fuelgauge/BatteryOptimizeUtils.java | 12 +++++++ .../fuelgauge/PowerBackgroundUsageDetail.java | 9 ------ .../AdvancedPowerUsageDetailTest.java | 31 ------------------- .../fuelgauge/BatteryOptimizeUtilsTest.java | 15 +++++++++ 5 files changed, 27 insertions(+), 49 deletions(-) diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index 6ef5aa82a49..42e6d9c4b68 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -52,7 +52,6 @@ import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.instrumentation.Instrumentable; -import com.android.settingslib.datastore.ChangeReason; import com.android.settingslib.widget.LayoutPreference; import java.util.ArrayList; @@ -272,7 +271,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment public void onPause() { super.onPause(); - notifyBackupManager(); final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); logMetricCategory(currentOptimizeMode); @@ -289,13 +287,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment Log.d(TAG, "Leave with mode: " + currentOptimizeMode); } - @VisibleForTesting - void notifyBackupManager() { - if (mOptimizationMode != mBatteryOptimizeUtils.getAppOptimizationMode()) { - BatterySettingsStorage.get(getContext()).notifyChange(ChangeReason.UPDATE); - } - } - @VisibleForTesting void initHeader() { final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header); diff --git a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java index dc4aade4545..001876c394b 100644 --- a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java @@ -33,6 +33,7 @@ import androidx.annotation.VisibleForTesting; import com.android.settings.R; import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; +import com.android.settingslib.datastore.ChangeReason; import com.android.settingslib.fuelgauge.PowerAllowlistBackend; import java.lang.annotation.Retention; @@ -222,6 +223,10 @@ public class BatteryOptimizeUtils { return; } + // App preferences are already clear when code reach here, and there may be no + // setAppUsageStateInternal call to notifyChange. So always trigger notifyChange here. + BatterySettingsStorage.get(context).notifyChange(ChangeReason.DELETE); + allowlistBackend.refreshList(); // Resets optimization mode for each application. for (ApplicationInfo info : applications) { @@ -351,6 +356,9 @@ public class BatteryOptimizeUtils { } BatteryOptimizeLogUtils.writeLog( context, action, packageNameKey, createLogEvent(appStandbyMode, allowListed)); + if (action != Action.RESET) { // reset has been notified in resetAppOptimizationMode + BatterySettingsStorage.get(context).notifyChange(toChangeReason(action)); + } } private static String createLogEvent(int appStandbyMode, boolean allowListed) { @@ -362,4 +370,8 @@ public class BatteryOptimizeUtils { allowListed, getAppOptimizationMode(appStandbyMode, allowListed)); } + + private static @ChangeReason int toChangeReason(Action action) { + return action == Action.RESTORE ? ChangeReason.RESTORE : ChangeReason.UPDATE; + } } diff --git a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java index 56702cf5c2a..b662d3ef908 100644 --- a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java +++ b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java @@ -41,7 +41,6 @@ import com.android.settingslib.HelpUtils; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.datastore.ChangeReason; import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.LayoutPreference; import com.android.settingslib.widget.MainSwitchPreference; @@ -116,7 +115,6 @@ public class PowerBackgroundUsageDetail extends DashboardFragment public void onPause() { super.onPause(); - notifyBackupManager(); final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); logMetricCategory(currentOptimizeMode); @@ -183,13 +181,6 @@ public class PowerBackgroundUsageDetail extends DashboardFragment onRadioButtonClicked(isEnabled ? mOptimizePreference : null); } - @VisibleForTesting - void notifyBackupManager() { - if (mOptimizationMode != mBatteryOptimizeUtils.getAppOptimizationMode()) { - BatterySettingsStorage.get(getContext()).notifyChange(ChangeReason.UPDATE); - } - } - @VisibleForTesting int getSelectedPreference() { if (!mMainSwitchPreference.isChecked()) { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java index 0648de4ad69..80739e9d47a 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java @@ -60,12 +60,8 @@ import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.instantapps.InstantAppDataProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import com.android.settingslib.datastore.ChangeReason; -import com.android.settingslib.datastore.Observer; import com.android.settingslib.widget.LayoutPreference; -import com.google.common.util.concurrent.MoreExecutors; - import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -119,10 +115,8 @@ public class AdvancedPowerUsageDetailTest { @Mock private AppOpsManager mAppOpsManager; @Mock private LoaderManager mLoaderManager; @Mock private BatteryOptimizeUtils mBatteryOptimizeUtils; - @Mock private Observer mObserver; private Context mContext; - private BatterySettingsStorage mBatterySettingsStorage; private PrimarySwitchPreference mAllowBackgroundUsagePreference; private AdvancedPowerUsageDetail mFragment; private SettingsActivity mTestActivity; @@ -134,7 +128,6 @@ public class AdvancedPowerUsageDetailTest { @Before public void setUp() { mContext = spy(ApplicationProvider.getApplicationContext()); - mBatterySettingsStorage = BatterySettingsStorage.get(mContext); when(mContext.getPackageName()).thenReturn("foo"); mFeatureFactory = FakeFeatureFactory.setupForTest(); mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; @@ -448,28 +441,4 @@ public class AdvancedPowerUsageDetailTest { TimeUnit.SECONDS.sleep(1); verifyNoInteractions(mMetricsFeatureProvider); } - - @Test - public void notifyBackupManager_optimizationModeIsNotChanged_notInvokeDataChanged() { - mBatterySettingsStorage.addObserver(mObserver, MoreExecutors.directExecutor()); - final int mode = BatteryOptimizeUtils.MODE_RESTRICTED; - mFragment.mOptimizationMode = mode; - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(mode); - - mFragment.notifyBackupManager(); - - verifyNoInteractions(mObserver); - } - - @Test - public void notifyBackupManager_optimizationModeIsChanged_invokeDataChanged() { - mBatterySettingsStorage.addObserver(mObserver, MoreExecutors.directExecutor()); - mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_RESTRICTED; - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_UNRESTRICTED); - - mFragment.notifyBackupManager(); - - verify(mObserver).onChanged(ChangeReason.UPDATE); - } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java index 3551eeb431e..6085b9a3ce4 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java @@ -49,8 +49,12 @@ import android.os.UserManager; import android.util.ArraySet; import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; +import com.android.settingslib.datastore.ChangeReason; +import com.android.settingslib.datastore.Observer; import com.android.settingslib.fuelgauge.PowerAllowlistBackend; +import com.google.common.util.concurrent.MoreExecutors; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -74,14 +78,18 @@ public class BatteryOptimizeUtilsTest { @Mock private PowerAllowlistBackend mMockBackend; @Mock private IPackageManager mMockIPackageManager; @Mock private UserManager mMockUserManager; + @Mock private Observer mObserver; private Context mContext; private BatteryOptimizeUtils mBatteryOptimizeUtils; + private BatterySettingsStorage mBatterySettingsStorage; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); + mBatterySettingsStorage = BatterySettingsStorage.get(mContext); + mBatterySettingsStorage.addObserver(mObserver, MoreExecutors.directExecutor()); mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME)); mBatteryOptimizeUtils.mAppOpsManager = mMockAppOpsManager; mBatteryOptimizeUtils.mBatteryUtils = mMockBatteryUtils; @@ -156,6 +164,7 @@ public class BatteryOptimizeUtilsTest { TimeUnit.SECONDS.sleep(1); verifySetAppOptimizationMode(AppOpsManager.MODE_IGNORED, /* allowListed */ false); + verify(mObserver).onChanged(ChangeReason.UPDATE); } @Test @@ -169,6 +178,7 @@ public class BatteryOptimizeUtilsTest { TimeUnit.SECONDS.sleep(1); verifySetAppOptimizationMode(AppOpsManager.MODE_ALLOWED, /* allowListed */ true); + verify(mObserver).onChanged(ChangeReason.UPDATE); } @Test @@ -182,6 +192,7 @@ public class BatteryOptimizeUtilsTest { TimeUnit.SECONDS.sleep(1); verifySetAppOptimizationMode(AppOpsManager.MODE_ALLOWED, /* allowListed */ false); + verify(mObserver).onChanged(ChangeReason.UPDATE); } @Test @@ -197,6 +208,7 @@ public class BatteryOptimizeUtilsTest { verify(mMockBatteryUtils, never()).setForceAppStandby(anyInt(), anyString(), anyInt()); verify(mMockBackend, never()).addApp(anyString()); verify(mMockBackend, never()).removeApp(anyString()); + verifyNoInteractions(mObserver); } @Test @@ -288,6 +300,7 @@ public class BatteryOptimizeUtilsTest { inOrder.verify(mMockBackend).isAllowlisted(PACKAGE_NAME, UID); inOrder.verify(mMockBackend).isSysAllowlisted(PACKAGE_NAME); verifyNoMoreInteractions(mMockBackend); + verify(mObserver).onChanged(ChangeReason.DELETE); } @Test @@ -298,6 +311,7 @@ public class BatteryOptimizeUtilsTest { /* isSystemOrDefaultApp */ false); verifySetAppOptimizationMode(AppOpsManager.MODE_ALLOWED, /* allowListed */ false); + verify(mObserver).onChanged(ChangeReason.DELETE); } @Test @@ -308,6 +322,7 @@ public class BatteryOptimizeUtilsTest { /* isSystemOrDefaultApp */ false); verifySetAppOptimizationMode(AppOpsManager.MODE_ALLOWED, /* allowListed */ false); + verify(mObserver).onChanged(ChangeReason.DELETE); } private void runTestForResetWithMode( From 6693a02a2bc9e589102b6a565083859993f3669a Mon Sep 17 00:00:00 2001 From: Charlotte Lu Date: Wed, 13 Mar 2024 12:13:30 +0800 Subject: [PATCH 11/13] Fix certX509 has not been initialized. Test: Manual Fix: 329243388 Change-Id: If9507f2aeddc1a5077a39246ac2af2f32b2b94bc --- .../wifi/details2/CertificateDetailsPreferenceController.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceController.kt b/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceController.kt index 46303657488..8e1fa23fdb8 100644 --- a/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceController.kt +++ b/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceController.kt @@ -72,7 +72,6 @@ class CertificateDetailsPreferenceController(context: Context, preferenceKey: St } private fun getCertX509(wifiEntry: WifiEntry): Boolean { - if (certX509 != null ) return true certificateAliases = wifiEntry.wifiConfiguration?.enterpriseConfig?.caCertificateAliases?.get(0) ?: return false From f2174611b8be192815f16e0ebc42eebfe3bfca0d Mon Sep 17 00:00:00 2001 From: Wilson Wu Date: Wed, 13 Mar 2024 06:33:08 +0000 Subject: [PATCH 12/13] Fix keyboard vibration settings delay The default settings observer delay is 10s for background service. It cause the apps get the settings update delay and lead a unsync behavior. Update the settings change without delay. Bug: 327094397 Test: Manual test with bug steps Test: atest KeyboardVibrationTogglePreferenceControllerTest Change-Id: Ib1c23ec9e1442e8397c7a36ffa157594ac64a9b3 --- .../KeyboardVibrationTogglePreferenceController.java | 8 ++++++-- .../KeyboardVibrationTogglePreferenceControllerTest.java | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java b/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java index 869443c5fae..de433015e18 100644 --- a/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java +++ b/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java @@ -22,6 +22,7 @@ import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import android.app.settings.SettingsEnums; +import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.net.Uri; @@ -157,8 +158,11 @@ public class KeyboardVibrationTogglePreferenceController extends TogglePreferenc } private boolean updateKeyboardVibrationSetting(boolean enable) { - final boolean success = Settings.System.putInt(mContext.getContentResolver(), - KEYBOARD_VIBRATION_ENABLED, enable ? ON : OFF); + final ContentResolver contentResolver = mContext.getContentResolver(); + final boolean success = Settings.System.putInt(contentResolver, + KEYBOARD_VIBRATION_ENABLED, enable ? ON : OFF); + contentResolver.notifyChange(Settings.System.getUriFor(KEYBOARD_VIBRATION_ENABLED), + null /* observer */, ContentResolver.NOTIFY_NO_DELAY); if (!success) { Log.w(TAG, "Update settings database error!"); } diff --git a/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java index cf12e341337..75cbf9b0d6d 100644 --- a/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; import android.app.settings.SettingsEnums; +import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.os.vibrator.Flags; @@ -60,6 +61,9 @@ public class KeyboardVibrationTogglePreferenceControllerTest { @Mock private PreferenceScreen mPreferenceScreen; + @Mock + private ContentResolver mContentResolver; + private Context mContext; private Resources mResources; private KeyboardVibrationTogglePreferenceController mController; @@ -72,6 +76,7 @@ public class KeyboardVibrationTogglePreferenceControllerTest { mContext = spy(ApplicationProvider.getApplicationContext()); mResources = spy(mContext.getResources()); when(mContext.getResources()).thenReturn(mResources); + when(mContext.getContentResolver()).thenReturn(mContentResolver); mFeatureFactory = FakeFeatureFactory.setupForTest(); mController = new KeyboardVibrationTogglePreferenceController(mContext, "preferenceKey"); mPreference = new SwitchPreference(mContext); From 08e06a7775b661b063ada01fc5309594c7ee9e5c Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 13 Mar 2024 14:55:38 +0800 Subject: [PATCH 13/13] Improve the number on "Label SIM" page Bug: 318310357 Test: manual - on Mobile Settings Change-Id: I79149db550e8d84dd2104cbfd72e144dddeb81cd --- .../spa/network/SimOnboardingLabelSim.kt | 82 ++++++++++--------- .../settings/spa/network/SimsSection.kt | 13 ++- 2 files changed, 53 insertions(+), 42 deletions(-) diff --git a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt index 3b2d5ec0d80..94e4a88ae5e 100644 --- a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt +++ b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt @@ -16,6 +16,7 @@ package com.android.settings.spa.network +import android.telephony.SubscriptionInfo import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -63,52 +64,55 @@ fun SimOnboardingLabelSimImpl( cancelAction ), ) { - labelSimBody(onboardingService) + LabelSimBody(onboardingService) } } @Composable -private fun labelSimBody(onboardingService: SimOnboardingService) { +private fun LabelSimBody(onboardingService: SimOnboardingService) { Column(Modifier.padding(SettingsDimension.itemPadding)) { SettingsBody(stringResource(R.string.sim_onboarding_label_sim_msg)) } for (subInfo in onboardingService.getSelectableSubscriptionInfoList()) { - var titleSimName by remember { - mutableStateOf( - onboardingService.getSubscriptionInfoDisplayName(subInfo) - ) - } - var summaryNumber = subInfo.number - // TODO using the SubscriptionUtil.getFormattedPhoneNumber - val alertDialogPresenter = rememberAlertDialogPresenter( - confirmButton = AlertDialogButton( - stringResource(R.string.mobile_network_sim_name_rename) - ) { - onboardingService.addItemForRenaming(subInfo, titleSimName) - }, - dismissButton = AlertDialogButton(stringResource(R.string.cancel)) { - titleSimName = onboardingService.getSubscriptionInfoDisplayName(subInfo) - }, - title = stringResource(R.string.sim_onboarding_label_sim_dialog_title), - text = { - Text(summaryNumber, - modifier = Modifier.padding(bottom = SettingsDimension.itemPaddingVertical)) - SettingsOutlinedTextField( - value = titleSimName, - label = stringResource(R.string.sim_onboarding_label_sim_dialog_label), - enabled = true, - shape = MaterialTheme.shapes.extraLarge - ) { - titleSimName = it - } - }, - ) - Preference(object : PreferenceModel { - override val title = titleSimName - override val summary: () -> String - get() = { summaryNumber } - override val onClick = alertDialogPresenter::open - }) + LabelSimPreference(onboardingService, subInfo) } -} \ No newline at end of file +} + +@Composable +private fun LabelSimPreference( + onboardingService: SimOnboardingService, + subInfo: SubscriptionInfo, +) { + 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) + }, + dismissButton = AlertDialogButton(stringResource(R.string.cancel)) { + titleSimName = onboardingService.getSubscriptionInfoDisplayName(subInfo) + }, + title = stringResource(R.string.sim_onboarding_label_sim_dialog_title), + text = { + Text( + phoneNumber.value ?: "", + modifier = Modifier.padding(bottom = SettingsDimension.itemPaddingVertical) + ) + SettingsOutlinedTextField( + value = titleSimName, + label = stringResource(R.string.sim_onboarding_label_sim_dialog_label), + shape = MaterialTheme.shapes.extraLarge + ) { + titleSimName = it + } + }, + ) + Preference(object : PreferenceModel { + override val title = titleSimName + override val summary = { phoneNumber.value ?: "" } + override val onClick = alertDialogPresenter::open + }) +} diff --git a/src/com/android/settings/spa/network/SimsSection.kt b/src/com/android/settings/spa/network/SimsSection.kt index 9e4cf9f4c12..0b638c45ae5 100644 --- a/src/com/android/settings/spa/network/SimsSection.kt +++ b/src/com/android/settings/spa/network/SimsSection.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Add import androidx.compose.runtime.Composable +import androidx.compose.runtime.State import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -58,9 +59,7 @@ private fun SimPreference(subInfo: SubscriptionInfo) { val checked = remember(subInfo.subscriptionId) { context.isSubscriptionEnabledFlow(subInfo.subscriptionId) }.collectAsStateWithLifecycle(initialValue = false) - val phoneNumber = remember(subInfo) { - context.phoneNumberFlow(subInfo) - }.collectAsStateWithLifecycle(initialValue = null) + val phoneNumber = phoneNumber(subInfo) RestrictedTwoTargetSwitchPreference( model = object : SwitchPreferenceModel { override val title = subInfo.displayName.toString() @@ -80,6 +79,14 @@ private fun SimPreference(subInfo: SubscriptionInfo) { } } +@Composable +fun phoneNumber(subInfo: SubscriptionInfo): State { + val context = LocalContext.current + return remember(subInfo) { + context.phoneNumberFlow(subInfo) + }.collectAsStateWithLifecycle(initialValue = null) +} + @Composable private fun AddSim() { val context = LocalContext.current