From aa0d63ce4022fa3ae29b77c299c4dce37d11b338 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 7 Aug 2024 11:45:28 +0800 Subject: [PATCH 1/7] Check ims for isVoImsOptInEnabled If the device does not have PackageManager.FEATURE_TELEPHONY_IMS, ProvisioningManager.getProvisioningIntValue() could not be called. Fix: 346708541 Flag: EXEMPT bug fix Test: manual - SIM settings Change-Id: I52b5a62d20ee7df14ecf93c9b0dbbed6f7dc0f0d --- .../android/settings/network/ims/VolteQueryImsState.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/com/android/settings/network/ims/VolteQueryImsState.java b/src/com/android/settings/network/ims/VolteQueryImsState.java index b09a1304136..5acf2db2129 100644 --- a/src/com/android/settings/network/ims/VolteQueryImsState.java +++ b/src/com/android/settings/network/ims/VolteQueryImsState.java @@ -19,6 +19,7 @@ package com.android.settings.network.ims; import static android.telephony.ims.ProvisioningManager.KEY_VOIMS_OPT_IN_STATUS; import android.content.Context; +import android.content.pm.PackageManager; import android.telecom.TelecomManager; import android.telephony.AccessNetworkConstants; import android.telephony.SubscriptionManager; @@ -143,6 +144,11 @@ public class VolteQueryImsState extends ImsQueryController { * @return true when VoIMS opt-in has been enabled, otherwise false */ public boolean isVoImsOptInEnabled() { + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) { + // If the device does not have PackageManager.FEATURE_TELEPHONY_IMS, + // ProvisioningManager.getProvisioningIntValue() could not be called. + return false; + } int voImsOptInStatus = ProvisioningManager.createForSubscriptionId(mSubId) .getProvisioningIntValue(KEY_VOIMS_OPT_IN_STATUS); return voImsOptInStatus == ProvisioningManager.PROVISIONING_VALUE_ENABLED; From 7f26cdbb4a9761de349fbe73019edc4fa28fc1b0 Mon Sep 17 00:00:00 2001 From: Joshua McCloskey Date: Wed, 31 Jul 2024 22:14:01 +0000 Subject: [PATCH 2/7] Added haptic feedback in fingerprint settings auth Test: Verified manually that the haptic vibration occurs Fixes: b/296887919 Flag: EXEMPT bugfix Change-Id: I34c71701b662ec7f6fb655eeb395bbe1d5d5754b --- .../fingerprint/FingerprintSettings.java | 10 ++++++- .../FingerprintSettingsFragmentTest.java | 27 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index 65d93660276..526ae8f6ed0 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -42,6 +42,8 @@ import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; +import android.os.VibrationEffect; +import android.os.Vibrator; import android.text.InputFilter; import android.text.Spanned; import android.text.TextUtils; @@ -109,6 +111,9 @@ public class FingerprintSettings extends SubSettings { private static final int RESULT_FINISHED = BiometricEnrollBase.RESULT_FINISHED; private static final int RESULT_SKIP = BiometricEnrollBase.RESULT_SKIP; private static final int RESULT_TIMEOUT = BiometricEnrollBase.RESULT_TIMEOUT; + @VisibleForTesting + static final VibrationEffect SUCCESS_VIBRATION_EFFECT = + VibrationEffect.get(VibrationEffect.EFFECT_CLICK); @Override public Intent getIntent() { @@ -285,6 +290,7 @@ public class FingerprintSettings extends SubSettings { private FingerprintAuthenticateSidecar mAuthenticateSidecar; private FingerprintRemoveSidecar mRemovalSidecar; private HashMap mFingerprintsRenaming; + private Vibrator mVibrator; @Nullable private UdfpsEnrollCalibrator mCalibrator; @@ -367,8 +373,8 @@ public class FingerprintSettings extends SubSettings { break; case MSG_FINGER_AUTH_HELP: { // Not used + break; } - break; } } }; @@ -511,6 +517,7 @@ public class FingerprintSettings extends SubSettings { addFirstFingerprint(null); } } + mVibrator = getContext().getSystemService(Vibrator.class); final PreferenceScreen root = getPreferenceScreen(); root.removeAll(); addPreferencesFromResource(getPreferenceScreenResId()); @@ -1095,6 +1102,7 @@ public class FingerprintSettings extends SubSettings { } private void highlightFingerprintItem(int fpId) { + mVibrator.vibrate(SUCCESS_VIBRATION_EFFECT); String prefName = genKey(fpId); FingerprintPreference fpref = (FingerprintPreference) findPreference(prefName); final Drawable highlight = getHighlightDrawable(); diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java index b3e1c5dd08b..ca76c1e6069 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java @@ -54,6 +54,7 @@ import android.os.Bundle; import android.os.CancellationSignal; import android.os.Looper; import android.os.UserHandle; +import android.os.Vibrator; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; @@ -128,6 +129,9 @@ public class FingerprintSettingsFragmentTest { mAuthenticationCallbackArgumentCaptor = ArgumentCaptor.forClass( FingerprintManager.AuthenticationCallback.class); + @Mock + private Vibrator mVibrator; + private FingerprintAuthenticateSidecar mFingerprintAuthenticateSidecar; private FingerprintRemoveSidecar mFingerprintRemoveSidecar; @@ -141,6 +145,7 @@ public class FingerprintSettingsFragmentTest { doReturn(mContext).when(mFragment).getContext(); doReturn(mBiometricManager).when(mContext).getSystemService(BiometricManager.class); doReturn(true).when(mFingerprintManager).isHardwareDetected(); + doReturn(mVibrator).when(mContext).getSystemService(Vibrator.class); when(mBiometricManager.canAuthenticate( BiometricManager.Authenticators.MANDATORY_BIOMETRICS)) .thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE); @@ -287,6 +292,28 @@ public class FingerprintSettingsFragmentTest { assertThat(mFragment.isVisible()).isTrue(); } + @Test + @Ignore("b/353726774") + public void fingerprintVibratesOnAuthSuccess() { + setUpFragment(false); + + doNothing().when(mFingerprintManager).authenticate(any(), + mCancellationSignalArgumentCaptor.capture(), + mAuthenticationCallbackArgumentCaptor.capture(), any(), anyInt()); + + mFingerprintAuthenticateSidecar.startAuthentication(1); + + assertThat(mAuthenticationCallbackArgumentCaptor.getValue()).isNotNull(); + assertThat(mCancellationSignalArgumentCaptor.getValue()).isNotNull(); + + mAuthenticationCallbackArgumentCaptor.getValue() + .onAuthenticationSucceeded(new FingerprintManager.AuthenticationResult(null, + new Fingerprint("finger 1", 1, 1), 0 /* userId */, false)); + + shadowOf(Looper.getMainLooper()).idle(); + verify(mVibrator).vibrate(FingerprintSettings.SUCCESS_VIBRATION_EFFECT); + } + @Test public void testNotIndexable_whenDisabled() { doReturn(mPackageManager).when(mContext).getPackageManager(); From 1e80db4ee376a075a8841b3564d2c7e9034f5145 Mon Sep 17 00:00:00 2001 From: Riley Jones Date: Tue, 2 Jul 2024 03:22:41 +0000 Subject: [PATCH 3/7] Adjust edit shortcuts page to display button & gesture Page is altered to display the FAB shortcut and gesture shortcut simultaneously when the user is in gesture navigation mode. The gesture option does not display the "more options" link. Additionally, the gesture option controller uses the GESTURE type, which means it will read from & write to the gesture setting. All this behavior is flag protected. Bug: 300318311 Test: atest FloatingButtonShortcutOptionControllerTest GestureShortcutOptionControllerTest Flag: android.provider.a11y_standalone_gesture_enabled Change-Id: I915f05b2102ce499bb906df2c13e0870ae0a36d5 --- .../EditShortcutsPreferenceFragment.java | 7 ++- ...loatingButtonShortcutOptionController.java | 9 +++- .../GestureShortcutOptionController.java | 24 +++++++--- ...ingButtonShortcutOptionControllerTest.java | 37 ++++++++++------ .../GestureShortcutOptionControllerTest.java | 44 +++++++++++++++++++ .../testutils/AccessibilityTestUtils.java | 21 ++++----- 6 files changed, 109 insertions(+), 33 deletions(-) diff --git a/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java b/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java index bdf785766c2..d04b34a4c3f 100644 --- a/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java +++ b/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java @@ -20,6 +20,7 @@ import static android.app.Activity.RESULT_CANCELED; import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE; import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS; import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED; +import static android.provider.Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED; import static android.provider.Settings.Secure.ACCESSIBILITY_QS_TARGETS; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; @@ -93,7 +94,8 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment { Settings.Secure.getUriFor(ACCESSIBILITY_BUTTON_MODE); private static final Uri BUTTON_SHORTCUT_SETTING = Settings.Secure.getUriFor(ACCESSIBILITY_BUTTON_TARGETS); - + private static final Uri GESTURE_SHORTCUT_SETTING = + Settings.Secure.getUriFor(ACCESSIBILITY_GESTURE_TARGETS); private static final Uri TRIPLE_TAP_SHORTCUT_SETTING = Settings.Secure.getUriFor(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED); private static final Uri TWO_FINGERS_DOUBLE_TAP_SHORTCUT_SETTING = @@ -107,6 +109,7 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment { VOLUME_KEYS_SHORTCUT_SETTING, BUTTON_SHORTCUT_MODE_SETTING, BUTTON_SHORTCUT_SETTING, + GESTURE_SHORTCUT_SETTING, TRIPLE_TAP_SHORTCUT_SETTING, TWO_FINGERS_DOUBLE_TAP_SHORTCUT_SETTING, QUICK_SETTINGS_SHORTCUT_SETTING, @@ -173,6 +176,8 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment { } else if (BUTTON_SHORTCUT_MODE_SETTING.equals(uri) || BUTTON_SHORTCUT_SETTING.equals(uri)) { refreshSoftwareShortcutControllers(); + } else if (GESTURE_SHORTCUT_SETTING.equals(uri)) { + refreshPreferenceController(GestureShortcutOptionController.class); } else if (TRIPLE_TAP_SHORTCUT_SETTING.equals(uri)) { refreshPreferenceController(TripleTapShortcutOptionController.class); } else if (TWO_FINGERS_DOUBLE_TAP_SHORTCUT_SETTING.equals(uri)) { diff --git a/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionController.java b/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionController.java index 2242cabba8a..20ff12d46fb 100644 --- a/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionController.java +++ b/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionController.java @@ -50,7 +50,14 @@ public class FloatingButtonShortcutOptionController @Override protected boolean isShortcutAvailable() { - return AccessibilityUtil.isFloatingMenuEnabled(mContext); + if (android.provider.Flags.a11yStandaloneGestureEnabled()) { + // FAB should be available when in gesture navigation mode, + // or if we're in the FAB button mode while in navbar navigation mode. + return AccessibilityUtil.isGestureNavigateEnabled(mContext) + || AccessibilityUtil.isFloatingMenuEnabled(mContext); + } else { + return AccessibilityUtil.isFloatingMenuEnabled(mContext); + } } @Nullable diff --git a/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionController.java b/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionController.java index e65aab9a14a..3c19b5dd10e 100644 --- a/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionController.java +++ b/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionController.java @@ -16,6 +16,8 @@ package com.android.settings.accessibility.shortcuts; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; + import android.content.Context; import android.text.SpannableStringBuilder; @@ -51,11 +53,22 @@ public class GestureShortcutOptionController extends SoftwareShortcutOptionPrefe } } + @Override + protected int getShortcutType() { + return android.provider.Flags.a11yStandaloneGestureEnabled() + ? GESTURE : super.getShortcutType(); + } + @Override protected boolean isShortcutAvailable() { - return !isInSetupWizard() - && !AccessibilityUtil.isFloatingMenuEnabled(mContext) - && AccessibilityUtil.isGestureNavigateEnabled(mContext); + if (android.provider.Flags.a11yStandaloneGestureEnabled()) { + return !isInSetupWizard() + && AccessibilityUtil.isGestureNavigateEnabled(mContext); + } else { + return !isInSetupWizard() + && AccessibilityUtil.isGestureNavigateEnabled(mContext) + && !AccessibilityUtil.isFloatingMenuEnabled(mContext); + } } @Override @@ -68,9 +81,8 @@ public class GestureShortcutOptionController extends SoftwareShortcutOptionPrefe final SpannableStringBuilder sb = new SpannableStringBuilder(); sb.append(instruction); - if (!isInSetupWizard()) { - sb.append("\n\n"); - sb.append(getCustomizeAccessibilityButtonLink()); + if (!isInSetupWizard() && !android.provider.Flags.a11yStandaloneGestureEnabled()) { + sb.append("\n\n").append(getCustomizeAccessibilityButtonLink()); } return sb; diff --git a/tests/robotests/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionControllerTest.java index 20f5d5da1fa..0952b099f38 100644 --- a/tests/robotests/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/shortcuts/FloatingButtonShortcutOptionControllerTest.java @@ -16,37 +16,46 @@ package com.android.settings.accessibility.shortcuts; -import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; -import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE; - import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.spy; + import android.content.ComponentName; import android.content.Context; -import android.provider.Settings; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Flags; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; +import com.android.settings.testutils.AccessibilityTestUtils; +import com.android.settings.testutils.shadow.SettingsShadowResources; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; import java.util.Set; /** * Tests for {@link FloatingButtonShortcutOptionController} */ +@Config(shadows = SettingsShadowResources.class) @RunWith(RobolectricTestRunner.class) public class FloatingButtonShortcutOptionControllerTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final String PREF_KEY = "prefKey"; private static final String TARGET = new ComponentName("FakePackage", "FakeClass").flattenToString(); - private final Context mContext = ApplicationProvider.getApplicationContext(); + private final Context mContext = spy(ApplicationProvider.getApplicationContext()); private FloatingButtonShortcutOptionController mController; private ShortcutOptionPreference mShortcutOptionPreference; @@ -61,7 +70,6 @@ public class FloatingButtonShortcutOptionControllerTest { mShortcutOptionPreference.setKey(PREF_KEY); mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext); mPreferenceScreen.addPreference(mShortcutOptionPreference); - setFloatingButtonEnabled(true); } @Test @@ -95,23 +103,26 @@ public class FloatingButtonShortcutOptionControllerTest { @Test public void isShortcutAvailable_floatingMenuEnabled_returnTrue() { - setFloatingButtonEnabled(true); + AccessibilityTestUtils.setSoftwareShortcutMode( + mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ true); assertThat(mController.isShortcutAvailable()).isTrue(); } @Test public void isShortcutAvailable_floatingMenuDisabled_returnFalse() { - setFloatingButtonEnabled(false); + AccessibilityTestUtils.setSoftwareShortcutMode( + mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ false); assertThat(mController.isShortcutAvailable()).isFalse(); } - private void setFloatingButtonEnabled(boolean enable) { - int mode = enable - ? ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU : ACCESSIBILITY_BUTTON_MODE_GESTURE; + @Test + @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED) + public void isShortcutAvailable_gestureNavigationMode_returnsTrue() { + AccessibilityTestUtils.setSoftwareShortcutMode( + mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ false); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mode); + assertThat(mController.isShortcutAvailable()).isTrue(); } } diff --git a/tests/robotests/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionControllerTest.java index 0149cc3520f..1d46caef087 100644 --- a/tests/robotests/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/shortcuts/GestureShortcutOptionControllerTest.java @@ -16,6 +16,8 @@ package com.android.settings.accessibility.shortcuts; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.settings.testutils.AccessibilityTestUtils.setupMockAccessibilityManager; import static com.google.common.truth.Truth.assertThat; @@ -25,6 +27,10 @@ import static org.mockito.Mockito.when; import android.content.ComponentName; import android.content.Context; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Flags; import android.view.accessibility.AccessibilityManager; import androidx.preference.PreferenceManager; @@ -37,6 +43,7 @@ import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settingslib.utils.StringUtil; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -50,6 +57,8 @@ import java.util.Set; @Config(shadows = SettingsShadowResources.class) @RunWith(RobolectricTestRunner.class) public class GestureShortcutOptionControllerTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final String PREF_KEY = "prefKey"; private static final String TARGET = new ComponentName("FakePackage", "FakeClass").flattenToString(); @@ -136,6 +145,18 @@ public class GestureShortcutOptionControllerTest { assertThat(mController.getSummary().toString()).isEqualTo(expected); } + @Test + @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED) + public void getSummary_standaloneGestureFlagOn_verifyNoCustomizeA11yButtonTest() { + enableTouchExploration(true); + String expected = StringUtil.getIcuPluralsString( + mContext, + /* count= */ 3, + R.string.accessibility_shortcut_edit_dialog_summary_gesture); + + assertThat(mController.getSummary().toString()).isEqualTo(expected); + } + @Test public void isShortcutAvailable_inSuw_returnFalse() { mController.setInSetupWizard(true); @@ -144,6 +165,7 @@ public class GestureShortcutOptionControllerTest { } @Test + @DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED) public void isShortcutAvailable_notInSuwUseGestureNavSystemUseFab_returnFalse() { mController.setInSetupWizard(false); AccessibilityTestUtils.setSoftwareShortcutMode( @@ -179,6 +201,28 @@ public class GestureShortcutOptionControllerTest { assertThat(mController.isShortcutAvailable()).isFalse(); } + @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED) + @Test + public void isShortcutAvailable_floatingMenuEnabled_gestureNavEnabled_returnsTrue() { + mController.setInSetupWizard(false); + AccessibilityTestUtils.setSoftwareShortcutMode( + mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ true); + + assertThat(mController.isShortcutAvailable()).isTrue(); + } + + @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED) + @Test + public void getShortcutType_gesture() { + assertThat(mController.getShortcutType()).isEqualTo(GESTURE); + } + + @DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED) + @Test + public void getShortcutType_software() { + assertThat(mController.getShortcutType()).isEqualTo(SOFTWARE); + } + private void enableTouchExploration(boolean enable) { AccessibilityManager am = setupMockAccessibilityManager(mContext); when(am.isTouchExplorationEnabled()).thenReturn(enable); diff --git a/tests/robotests/src/com/android/settings/testutils/AccessibilityTestUtils.java b/tests/robotests/src/com/android/settings/testutils/AccessibilityTestUtils.java index 5d895d908d6..4d698215c43 100644 --- a/tests/robotests/src/com/android/settings/testutils/AccessibilityTestUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/AccessibilityTestUtils.java @@ -20,6 +20,8 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATIN import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -46,20 +48,15 @@ public class AccessibilityTestUtils { public static void setSoftwareShortcutMode( Context context, boolean gestureNavEnabled, boolean floatingButtonEnabled) { - int mode = floatingButtonEnabled ? ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU : -1; + int buttonMode = floatingButtonEnabled ? ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU : -1; + int navMode = gestureNavEnabled ? NAV_BAR_MODE_GESTURAL : NAV_BAR_MODE_3BUTTON; Settings.Secure.putInt(context.getContentResolver(), - Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mode); - - if (gestureNavEnabled) { - SettingsShadowResources.overrideResource( - com.android.internal.R.integer.config_navBarInteractionMode, - NAV_BAR_MODE_GESTURAL); - } else { - SettingsShadowResources.overrideResource( - com.android.internal.R.integer.config_navBarInteractionMode, - NAV_BAR_MODE_3BUTTON); - } + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, buttonMode); + SettingsShadowResources.overrideResource( + com.android.internal.R.integer.config_navBarInteractionMode, navMode); + assertThat(context.getResources().getInteger( + com.android.internal.R.integer.config_navBarInteractionMode)).isEqualTo(navMode); } /** From 3d0dcda008f15762f2e2139a4562896d146b2c34 Mon Sep 17 00:00:00 2001 From: Alexander Roederer Date: Tue, 6 Aug 2024 22:31:51 +0000 Subject: [PATCH 4/7] Remove the ZenModeSliceBuilder We don't want the old Do Not Disturb page to show up in the search index results, so we can remove the custom Slice Builder. Note that this is part of the general Do Not Disturb/Modes Settings Changes: see b/353700470 Bug: 341726633 Test: atest SettingsSliceProviderTest, flash+test Flag: android.app.modes_ui Change-Id: Ibba372d5fa6caf3ee28d9fd2a900047060b212da --- .../slices/SettingsSliceProvider.java | 17 +++++++++++++++-- .../slices/SettingsSliceProviderTest.java | 19 ++++++++++++------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index ec60f44110a..872016005a3 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -120,6 +120,16 @@ public class SettingsSliceProvider extends SliceProvider { * permission can use them. */ private static final List PUBLICLY_SUPPORTED_CUSTOM_SLICE_URIS = + android.app.Flags.modesUi() + ? + Arrays.asList( + CustomSliceRegistry.BLUETOOTH_URI, + CustomSliceRegistry.FLASHLIGHT_SLICE_URI, + CustomSliceRegistry.LOCATION_SLICE_URI, + CustomSliceRegistry.MOBILE_DATA_SLICE_URI, + CustomSliceRegistry.WIFI_CALLING_URI, + CustomSliceRegistry.WIFI_SLICE_URI + ) : Arrays.asList( CustomSliceRegistry.BLUETOOTH_URI, CustomSliceRegistry.FLASHLIGHT_SLICE_URI, @@ -184,7 +194,9 @@ public class SettingsSliceProvider extends SliceProvider { } if (CustomSliceRegistry.ZEN_MODE_SLICE_URI.equals(sliceUri)) { - registerIntentToUri(ZenModeSliceBuilder.INTENT_FILTER, sliceUri); + if (!android.app.Flags.modesUi()) { + registerIntentToUri(ZenModeSliceBuilder.INTENT_FILTER, sliceUri); + } return; } else if (CustomSliceRegistry.BLUETOOTH_URI.equals(sliceUri)) { registerIntentToUri(BluetoothSliceBuilder.INTENT_FILTER, sliceUri); @@ -256,7 +268,8 @@ public class SettingsSliceProvider extends SliceProvider { .getSlicesFeatureProvider() .getNewWifiCallingSliceHelper(getContext()) .createWifiCallingSlice(sliceUri); - } else if (CustomSliceRegistry.ZEN_MODE_SLICE_URI.equals(sliceUri)) { + } else if (!android.app.Flags.modesUi() + && CustomSliceRegistry.ZEN_MODE_SLICE_URI.equals(sliceUri)) { return ZenModeSliceBuilder.getSlice(getContext()); } else if (CustomSliceRegistry.BLUETOOTH_URI.equals(sliceUri)) { return BluetoothSliceBuilder.getSlice(getContext()); diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java index 84c8586602b..edb310e7800 100644 --- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java +++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java @@ -30,7 +30,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -131,12 +130,18 @@ public class SettingsSliceProviderTest { CustomSliceRegistry.LOCATION_SLICE_URI ); - private static final List SPECIAL_CASE_OEM_URIS = Arrays.asList( - CustomSliceRegistry.ZEN_MODE_SLICE_URI, - CustomSliceRegistry.FLASHLIGHT_SLICE_URI, - CustomSliceRegistry.MOBILE_DATA_SLICE_URI, - CustomSliceRegistry.WIFI_CALLING_URI - ); + private static final List SPECIAL_CASE_OEM_URIS = android.app.Flags.modesUi() + ? Arrays.asList( + CustomSliceRegistry.FLASHLIGHT_SLICE_URI, + CustomSliceRegistry.MOBILE_DATA_SLICE_URI, + CustomSliceRegistry.WIFI_CALLING_URI + ) : + Arrays.asList( + CustomSliceRegistry.ZEN_MODE_SLICE_URI, + CustomSliceRegistry.FLASHLIGHT_SLICE_URI, + CustomSliceRegistry.MOBILE_DATA_SLICE_URI, + CustomSliceRegistry.WIFI_CALLING_URI + ); @Before public void setUp() { From 572541c3ab9e84de2065a131c907c5e97f461c94 Mon Sep 17 00:00:00 2001 From: Diya Bera Date: Thu, 8 Aug 2024 01:48:46 +0000 Subject: [PATCH 5/7] Do not call credential if authenticators does not allow Flag: android.hardware.biometrics.flags.mandatory_biometrics Test: N/A Fixes: 358180327 Change-Id: I236fbcc84502602e6e6fb02615f002e02136202e --- .../password/ConfirmDeviceCredentialActivity.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java index 4f355324fc6..72dbed457be 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java @@ -104,6 +104,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { private boolean mForceVerifyPath = false; private boolean mGoingToBackground; private boolean mWaitingForBiometricCallback; + private int mBiometricsAuthenticators; private Executor mExecutor = (runnable -> { mHandler.post(runnable); @@ -122,8 +123,14 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { Log.i(TAG, "Finishing, user no longer valid: " + mUserId); finish(); } else { - // All other errors go to some version of CC - showConfirmCredentials(); + if ((mBiometricsAuthenticators + & BiometricManager.Authenticators.DEVICE_CREDENTIAL) != 0) { + // All other errors go to some version of CC + showConfirmCredentials(); + } else { + Log.i(TAG, "Finishing, device credential not requested"); + finish(); + } } } else if (mWaitingForBiometricCallback) { // mGoingToBackground is true mWaitingForBiometricCallback = false; @@ -188,7 +195,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { mDetails = intent.getCharSequenceExtra(KeyguardManager.EXTRA_DESCRIPTION); String alternateButton = intent.getStringExtra( KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL); - final int authenticators = intent.getIntExtra(BIOMETRIC_PROMPT_AUTHENTICATORS, + mBiometricsAuthenticators = intent.getIntExtra(BIOMETRIC_PROMPT_AUTHENTICATORS, BiometricManager.Authenticators.DEVICE_CREDENTIAL | BiometricManager.Authenticators.BIOMETRIC_WEAK); final String negativeButtonText = intent.getStringExtra( @@ -229,7 +236,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { promptInfo.setTitle(mTitle); promptInfo.setDescription(mDetails); promptInfo.setDisallowBiometricsIfPolicyExists(mCheckDevicePolicyManager); - promptInfo.setAuthenticators(authenticators); + promptInfo.setAuthenticators(mBiometricsAuthenticators); promptInfo.setNegativeButtonText(negativeButtonText); if (android.multiuser.Flags.enablePrivateSpaceFeatures() From 427c9c3187938485346b0000f92cdc5da4f48239 Mon Sep 17 00:00:00 2001 From: mxyyiyi Date: Thu, 8 Aug 2024 13:35:43 +0800 Subject: [PATCH 6/7] Migrate Tips Card in battery uasge to Settings Card. before: [Dark-settings]:https://screenshot.googleplex.com/56EiS3PbtRsh5RR [Light-app]:https://screenshot.googleplex.com/8UDuFEkkrLUt53D after: [Dark-settings]:https://screenshot.googleplex.com/3zKxxGi2Awa6s72 [Light-settings]:https://screenshot.googleplex.com/rsgVh9P62x7itUa [Dark-app]:https://screenshot.googleplex.com/56EiS3PbtRsh5RR [Light-app]:https://screenshot.googleplex.com/6bfs2u43nZpHL8y Bug: 357603119 Test: atest BatteryTipsControllerTest Flag: EXEMPT bug fix Change-Id: I25e3b407378b01ef9868326a005554ceb6e94181 --- res/drawable/battery_tips_all_rounded_bg.xml | 22 -- .../battery_tips_all_rounded_bg_ripple.xml | 21 -- .../battery_tips_half_rounded_bottom_bg.xml | 27 -- .../battery_tips_half_rounded_top_bg.xml | 27 -- res/layout/battery_tips_card.xml | 61 ---- res/xml/power_usage_advanced.xml | 2 +- .../batteryusage/AnomalyEventWrapper.java | 13 +- .../BatteryTipsCardPreference.java | 150 --------- .../batteryusage/BatteryTipsController.java | 81 ++--- .../settings/widget/TipCardPreference.kt | 6 + .../BatteryTipsCardPreferenceTest.java | 293 ------------------ .../BatteryTipsControllerTest.java | 177 ++++++----- .../settings/testutils/BatteryTestUtils.java | 22 +- 13 files changed, 178 insertions(+), 724 deletions(-) delete mode 100644 res/drawable/battery_tips_all_rounded_bg.xml delete mode 100644 res/drawable/battery_tips_all_rounded_bg_ripple.xml delete mode 100644 res/drawable/battery_tips_half_rounded_bottom_bg.xml delete mode 100644 res/drawable/battery_tips_half_rounded_top_bg.xml delete mode 100644 res/layout/battery_tips_card.xml delete mode 100644 src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java delete mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java diff --git a/res/drawable/battery_tips_all_rounded_bg.xml b/res/drawable/battery_tips_all_rounded_bg.xml deleted file mode 100644 index ba164b97023..00000000000 --- a/res/drawable/battery_tips_all_rounded_bg.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/res/drawable/battery_tips_all_rounded_bg_ripple.xml b/res/drawable/battery_tips_all_rounded_bg_ripple.xml deleted file mode 100644 index 31805704b3b..00000000000 --- a/res/drawable/battery_tips_all_rounded_bg_ripple.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/res/drawable/battery_tips_half_rounded_bottom_bg.xml b/res/drawable/battery_tips_half_rounded_bottom_bg.xml deleted file mode 100644 index 7766de63400..00000000000 --- a/res/drawable/battery_tips_half_rounded_bottom_bg.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/res/drawable/battery_tips_half_rounded_top_bg.xml b/res/drawable/battery_tips_half_rounded_top_bg.xml deleted file mode 100644 index aba1a4fb329..00000000000 --- a/res/drawable/battery_tips_half_rounded_top_bg.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/res/layout/battery_tips_card.xml b/res/layout/battery_tips_card.xml deleted file mode 100644 index 18c326f8b85..00000000000 --- a/res/layout/battery_tips_card.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/xml/power_usage_advanced.xml b/res/xml/power_usage_advanced.xml index c1294535a74..f0104e502bf 100644 --- a/res/xml/power_usage_advanced.xml +++ b/res/xml/power_usage_advanced.xml @@ -27,7 +27,7 @@ "com.android.settings.fuelgauge.batteryusage.BatteryTipsController" settings:isPreferenceVisible="false"> - diff --git a/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java b/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java index be5de06e582..c62728b5162 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java +++ b/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java @@ -28,10 +28,11 @@ import androidx.annotation.Nullable; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.core.SubSettingLauncher; +import com.android.settings.widget.TipCardPreference; import java.util.function.Function; -final class AnomalyEventWrapper { +class AnomalyEventWrapper { private static final String TAG = "AnomalyEventWrapper"; private final Context mContext; @@ -235,16 +236,16 @@ final class AnomalyEventWrapper { return mHighlightSlotPair; } - boolean updateTipsCardPreference(BatteryTipsCardPreference preference) { + boolean updateTipsCardPreference(TipCardPreference preference) { final String titleString = getTitleString(); if (TextUtils.isEmpty(titleString)) { return false; } preference.setTitle(titleString); - preference.setIconResourceId(getIconResId()); - preference.setButtonColorResourceId(getColorResId()); - preference.setMainButtonLabel(getMainBtnString()); - preference.setDismissButtonLabel(getDismissBtnString()); + preference.setIconResId(getIconResId()); + preference.setTintColorResId(getColorResId()); + preference.setPrimaryButtonText(getDismissBtnString()); + preference.setSecondaryButtonText(getMainBtnString()); return true; } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java deleted file mode 100644 index bbd10997509..00000000000 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.fuelgauge.batteryusage; - -import android.content.Context; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; -import androidx.preference.PreferenceViewHolder; - -import com.android.settings.R; - -import com.google.android.material.button.MaterialButton; - -/** A preference for displaying the battery tips card view. */ -public class BatteryTipsCardPreference extends Preference implements View.OnClickListener { - - private static final String TAG = "BatteryTipsCardPreference"; - - interface OnConfirmListener { - void onConfirm(); - } - - interface OnRejectListener { - void onReject(); - } - - private OnConfirmListener mOnConfirmListener; - private OnRejectListener mOnRejectListener; - private int mIconResourceId = 0; - private int mButtonColorResourceId = 0; - - @VisibleForTesting CharSequence mMainButtonLabel; - @VisibleForTesting CharSequence mDismissButtonLabel; - - public BatteryTipsCardPreference(Context context, AttributeSet attrs) { - super(context, attrs); - setLayoutResource(R.layout.battery_tips_card); - setViewId(R.id.battery_tips_card); - setSelectable(false); - } - - public void setOnConfirmListener(OnConfirmListener listener) { - mOnConfirmListener = listener; - } - - public void setOnRejectListener(OnRejectListener listener) { - mOnRejectListener = listener; - } - - /** - * Sets the icon in tips card. - */ - public void setIconResourceId(int resourceId) { - if (mIconResourceId != resourceId) { - mIconResourceId = resourceId; - notifyChanged(); - } - } - - /** - * Sets the background color for main button and the text color for dismiss button. - */ - public void setButtonColorResourceId(int resourceId) { - if (mButtonColorResourceId != resourceId) { - mButtonColorResourceId = resourceId; - notifyChanged(); - } - } - - /** - * Sets the label of main button in tips card. - */ - public void setMainButtonLabel(CharSequence label) { - if (!TextUtils.equals(mMainButtonLabel, label)) { - mMainButtonLabel = label; - notifyChanged(); - } - } - - /** - * Sets the label of dismiss button in tips card. - */ - public void setDismissButtonLabel(CharSequence label) { - if (!TextUtils.equals(mDismissButtonLabel, label)) { - mDismissButtonLabel = label; - notifyChanged(); - } - } - - @Override - public void onClick(View view) { - final int viewId = view.getId(); - if (viewId == R.id.main_button || viewId == R.id.battery_tips_card) { - if (mOnConfirmListener != null) { - mOnConfirmListener.onConfirm(); - } - } else if (viewId == R.id.dismiss_button) { - if (mOnRejectListener != null) { - mOnRejectListener.onReject(); - } - } - } - - @Override - public void onBindViewHolder(PreferenceViewHolder view) { - super.onBindViewHolder(view); - - ((TextView) view.findViewById(R.id.title)).setText(getTitle()); - - final LinearLayout tipsCard = (LinearLayout) view.findViewById(R.id.battery_tips_card); - tipsCard.setOnClickListener(this); - final MaterialButton mainButton = (MaterialButton) view.findViewById(R.id.main_button); - mainButton.setOnClickListener(this); - mainButton.setText(mMainButtonLabel); - final MaterialButton dismissButton = - (MaterialButton) view.findViewById(R.id.dismiss_button); - dismissButton.setOnClickListener(this); - dismissButton.setText(mDismissButtonLabel); - if (mButtonColorResourceId != 0) { - final int colorInt = getContext().getColor(mButtonColorResourceId); - mainButton.setBackgroundColor(colorInt); - dismissButton.setTextColor(colorInt); - } - - if (mIconResourceId != 0) { - ((ImageView) view.findViewById(R.id.icon)).setImageResource(mIconResourceId); - } - } -} diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java index 821c8680522..405b78651a5 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java @@ -25,6 +25,7 @@ import androidx.preference.PreferenceScreen; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.TipCardPreference; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** Controls the update for battery tips card */ @@ -50,10 +51,9 @@ public class BatteryTipsController extends BasePreferenceController { void onAnomalyReject(); } - private OnAnomalyConfirmListener mOnAnomalyConfirmListener; - private OnAnomalyRejectListener mOnAnomalyRejectListener; - - @VisibleForTesting BatteryTipsCardPreference mCardPreference; + @VisibleForTesting OnAnomalyConfirmListener mOnAnomalyConfirmListener; + @VisibleForTesting OnAnomalyRejectListener mOnAnomalyRejectListener; + @VisibleForTesting TipCardPreference mCardPreference; @VisibleForTesting AnomalyEventWrapper mAnomalyEventWrapper = null; @VisibleForTesting Boolean mIsAcceptable = false; @@ -117,42 +117,20 @@ public class BatteryTipsController extends BasePreferenceController { return; } - // Set battery tips card listener - mCardPreference.setOnConfirmListener( + mCardPreference.setPrimaryButtonAction( () -> { - mCardPreference.setVisible(false); - if (mOnAnomalyConfirmListener != null) { - mOnAnomalyConfirmListener.onAnomalyConfirm(); - } else if (mAnomalyEventWrapper.updateSystemSettingsIfAvailable() - || mAnomalyEventWrapper.launchSubSetting()) { - mMetricsFeatureProvider.action( - /* attribution= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - /* action= */ SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, - /* pageId= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - /* key= */ ANOMALY_KEY, - /* value= */ anomalyKeyNumber); - } + onBatteryTipsCardDismiss(anomalyKeyNumber); + return null; }); - mCardPreference.setOnRejectListener( + mCardPreference.setSecondaryButtonAction( () -> { - mCardPreference.setVisible(false); - if (mOnAnomalyRejectListener != null) { - mOnAnomalyRejectListener.onAnomalyReject(); - } - // For anomaly events with same record key, dismissed until next time full - // charged. - final String dismissRecordKey = mAnomalyEventWrapper.getDismissRecordKey(); - if (!TextUtils.isEmpty(dismissRecordKey)) { - DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey); - } - mMetricsFeatureProvider.action( - /* attribution= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - /* action= */ SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, - /* pageId= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - /* key= */ ANOMALY_KEY, - /* value= */ anomalyKeyNumber); + onBatteryTipsCardAccept(anomalyKeyNumber); + return null; }); + mCardPreference.setPrimaryButtonVisibility(true); + mCardPreference.setSecondaryButtonVisibility(true); + mCardPreference.buildContent(); mCardPreference.setVisible(true); mMetricsFeatureProvider.action( /* attribution= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, @@ -161,4 +139,37 @@ public class BatteryTipsController extends BasePreferenceController { /* key= */ ANOMALY_KEY, /* value= */ anomalyKeyNumber); } + + private void onBatteryTipsCardDismiss(final int anomalyKeyNumber) { + mCardPreference.setVisible(false); + if (mOnAnomalyRejectListener != null) { + mOnAnomalyRejectListener.onAnomalyReject(); + } + // For anomaly events with same record key, dismissed until next time full charged. + final String dismissRecordKey = mAnomalyEventWrapper.getDismissRecordKey(); + if (!TextUtils.isEmpty(dismissRecordKey)) { + DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey); + } + mMetricsFeatureProvider.action( + /* attribution= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, + /* action= */ SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, + /* pageId= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, + /* key= */ ANOMALY_KEY, + /* value= */ anomalyKeyNumber); + } + + private void onBatteryTipsCardAccept(final int anomalyKeyNumber) { + mCardPreference.setVisible(false); + if (mOnAnomalyConfirmListener != null) { + mOnAnomalyConfirmListener.onAnomalyConfirm(); + } else if (mAnomalyEventWrapper.updateSystemSettingsIfAvailable() + || mAnomalyEventWrapper.launchSubSetting()) { + mMetricsFeatureProvider.action( + /* attribution= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, + /* action= */ SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, + /* pageId= */ SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, + /* key= */ ANOMALY_KEY, + /* value= */ anomalyKeyNumber); + } + } } diff --git a/src/com/android/settings/widget/TipCardPreference.kt b/src/com/android/settings/widget/TipCardPreference.kt index 0ae7d2a66f8..0ca0272a031 100644 --- a/src/com/android/settings/widget/TipCardPreference.kt +++ b/src/com/android/settings/widget/TipCardPreference.kt @@ -19,6 +19,7 @@ package com.android.settings.widget import android.content.Context import android.content.res.Resources import android.util.AttributeSet +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import com.android.settings.spa.preference.ComposePreference @@ -37,6 +38,9 @@ constructor( /** A icon resource id for displaying icon on tips card. */ var iconResId: Int? = null + /** A color resource id for displaying icon and button text on tips card. */ + var tintColorResId: Int? = null + /** The primary button's text. */ var primaryButtonText: String = "" @@ -85,6 +89,8 @@ constructor( title = title?.toString() ?: "", text = summary?.toString() ?: "", buttons = listOfNotNull(configPrimaryButton(), configSecondaryButton()), + tintColor = tintColorResId?.let { Color(context.getColor(it)) } + ?: Color.Unspecified, onDismiss = onDismiss, imageVector = iconResId diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java deleted file mode 100644 index 5f863011915..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.fuelgauge.batteryusage; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.settings.SettingsEnums; -import android.content.Context; -import android.content.Intent; -import android.provider.Settings; -import android.util.Pair; -import android.view.View; - -import com.android.settings.DisplaySettings; -import com.android.settings.R; -import com.android.settings.SettingsActivity; -import com.android.settings.testutils.BatteryTestUtils; -import com.android.settings.testutils.FakeFeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -import java.util.Map; -import java.util.Optional; -import java.util.TimeZone; - -@RunWith(RobolectricTestRunner.class) -public final class BatteryTipsCardPreferenceTest { - - private Context mContext; - private FakeFeatureFactory mFeatureFactory; - private BatteryTipsCardPreference mBatteryTipsCardPreference; - private PowerUsageAdvanced mPowerUsageAdvanced; - private BatteryTipsController mBatteryTipsController; - private BatteryChartPreferenceController mBatteryChartPreferenceController; - - @Mock private View mFakeView; - @Mock private BatteryUsageBreakdownController mBatteryUsageBreakdownController; - @Mock private BatteryDiffEntry mFakeEntry; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - TimeZone.setDefault(TimeZone.getTimeZone("GMT+8")); - mContext = spy(RuntimeEnvironment.application); - mFeatureFactory = FakeFeatureFactory.setupForTest(); - mBatteryTipsCardPreference = new BatteryTipsCardPreference(mContext, /* attrs= */ null); - mBatteryTipsController = new BatteryTipsController(mContext); - mBatteryChartPreferenceController = - spy(new BatteryChartPreferenceController(mContext, null, null)); - mBatteryChartPreferenceController.mPrefContext = mContext; - mBatteryTipsController.mCardPreference = mBatteryTipsCardPreference; - - mPowerUsageAdvanced = spy(new PowerUsageAdvanced()); - doReturn(mContext).when(mPowerUsageAdvanced).getContext(); - mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController; - mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController; - mPowerUsageAdvanced.mBatteryUsageBreakdownController = mBatteryUsageBreakdownController; - mPowerUsageAdvanced.mBatteryLevelData = - Optional.of( - new BatteryLevelData( - Map.of( - 1694354400000L, 1, // 2023-09-10 22:00:00 - 1694361600000L, 2, // 2023-09-11 00:00:00 - 1694368800000L, 3))); // 2023-09-11 02:00:00 - doReturn("TestEntriesKey").when(mFakeEntry).getKey(); - } - - @Test - public void constructor_returnExpectedResult() { - assertThat(mBatteryTipsCardPreference.getLayoutResource()) - .isEqualTo(R.layout.battery_tips_card); - } - - @Test - public void onClick_mainBtnOfSettingsAnomalyLaunchPage_getAdaptiveBrightnessLauncher() { - final ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); - PowerAnomalyEvent adaptiveBrightnessAnomaly = - BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(/* changeSettings= */ false); - when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - when(mFakeView.getId()).thenReturn(R.id.main_button); - doNothing().when(mContext).startActivity(captor.capture()); - - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated( - adaptiveBrightnessAnomaly, adaptiveBrightnessAnomaly); - mBatteryTipsCardPreference.onClick(mFakeView); - - assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); - verify(mContext).startActivity(any(Intent.class)); - final Intent intent = captor.getValue(); - assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) - .isEqualTo(DisplaySettings.class.getName()); - assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, -1)) - .isEqualTo(SettingsEnums.DISPLAY); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_BRIGHTNESS.getNumber()); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_BRIGHTNESS.getNumber()); - } - - @Test - public void onClick_mainBtnOfSettingsAnomalyChangeSettings_settingsChanged() - throws Settings.SettingNotFoundException { - Settings.System.putInt( - mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); - final ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); - PowerAnomalyEvent adaptiveBrightnessAnomaly = - BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(/* changeSettings= */ true); - when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - when(mFakeView.getId()).thenReturn(R.id.main_button); - doNothing().when(mContext).startActivity(captor.capture()); - - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated( - adaptiveBrightnessAnomaly, adaptiveBrightnessAnomaly); - mBatteryTipsCardPreference.onClick(mFakeView); - - assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); - assertThat( - Settings.System.getInt( - mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE)) - .isEqualTo(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - verify(mContext, never()).startActivity(any(Intent.class)); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_BRIGHTNESS.getNumber()); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_BRIGHTNESS.getNumber()); - } - - @Test - public void onClick_dismissBtnOfSettingsAnomaly_cardDismissAndLogged() { - final PowerAnomalyEvent screenTimeoutAnomaly = - BatteryTestUtils.createScreenTimeoutAnomalyEvent(); - DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext); - when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - when(mFakeView.getId()).thenReturn(R.id.dismiss_button); - - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated( - screenTimeoutAnomaly, screenTimeoutAnomaly); - mBatteryTipsCardPreference.onClick(mFakeView); - - assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); - assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext)).hasSize(1); - assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext)) - .contains(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name()); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_SCREEN_TIMEOUT.getNumber()); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_SCREEN_TIMEOUT.getNumber()); - } - - @Test - public void onClick_mainBtnOfAppsAnomaly_selectHighlightSlot() { - final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent(); - when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - when(mFakeView.getId()).thenReturn(R.id.main_button); - doNothing().when(mBatteryChartPreferenceController).selectHighlightSlotIndex(); - when(mPowerUsageAdvanced.findRelatedBatteryDiffEntry(any())).thenReturn(mFakeEntry); - - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly, appsAnomaly); - assertHighlightSlotIndexPair(1, 0); - mBatteryTipsCardPreference.onClick(mFakeView); - - assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); - verify(mContext, never()).startActivity(any(Intent.class)); - verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0)); - verify(mBatteryChartPreferenceController).selectHighlightSlotIndex(); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL.getNumber()); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL.getNumber()); - } - - @Test - public void onClick_dismissBtnOfAppsAnomaly_keepHighlightSlotIndex() { - final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent(); - when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - when(mFakeView.getId()).thenReturn(R.id.dismiss_button); - when(mPowerUsageAdvanced.findRelatedBatteryDiffEntry(any())).thenReturn(mFakeEntry); - - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly, appsAnomaly); - assertHighlightSlotIndexPair(1, 0); - mBatteryTipsCardPreference.onClick(mFakeView); - - assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); - verify(mContext, never()).startActivity(any(Intent.class)); - verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0)); - verify(mBatteryChartPreferenceController, never()).selectHighlightSlotIndex(); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL.getNumber()); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL.getNumber()); - } - - private void assertHighlightSlotIndexPair( - int dailyHighlightSlotIndex, int hourlyHighlightSlotIndex) { - assertThat(mPowerUsageAdvanced.mBatteryLevelData.isPresent()).isTrue(); - assertThat(mPowerUsageAdvanced.mHighlightEventWrapper.isPresent()).isTrue(); - Pair slotIndexPair = - mPowerUsageAdvanced - .mHighlightEventWrapper - .get() - .getHighlightSlotPair(mPowerUsageAdvanced.mBatteryLevelData.get()); - assertThat(slotIndexPair) - .isEqualTo(Pair.create(dailyHighlightSlotIndex, hourlyHighlightSlotIndex)); - assertThat(mPowerUsageAdvanced.mBatteryChartPreferenceController.mDailyHighlightSlotIndex) - .isEqualTo(dailyHighlightSlotIndex); - assertThat(mPowerUsageAdvanced.mBatteryChartPreferenceController.mHourlyHighlightSlotIndex) - .isEqualTo(hourlyHighlightSlotIndex); - } -} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java index 954437f6440..6c29036b086 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java @@ -16,8 +16,13 @@ package com.android.settings.fuelgauge.batteryusage; + +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -29,11 +34,11 @@ import android.os.LocaleList; import com.android.settings.R; import com.android.settings.testutils.BatteryTestUtils; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.widget.TipCardPreference; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @@ -47,134 +52,156 @@ public final class BatteryTipsControllerTest { private Context mContext; private FakeFeatureFactory mFeatureFactory; private BatteryTipsController mBatteryTipsController; - - @Mock private BatteryTipsCardPreference mBatteryTipsCardPreference; + private TipCardPreference mCardPreference; @Before public void setUp() { MockitoAnnotations.initMocks(this); Locale.setDefault(new Locale("en_US")); - org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false); TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + mContext = spy(RuntimeEnvironment.application); final Resources resources = spy(mContext.getResources()); resources.getConfiguration().setLocales(new LocaleList(new Locale("en_US"))); doReturn(resources).when(mContext).getResources(); mFeatureFactory = FakeFeatureFactory.setupForTest(); - mBatteryTipsController = new BatteryTipsController(mContext); - mBatteryTipsController.mCardPreference = mBatteryTipsCardPreference; + mBatteryTipsController = spy(new BatteryTipsController(mContext)); + mCardPreference = new TipCardPreference(mContext); + mBatteryTipsController.mCardPreference = mCardPreference; } @Test public void handleBatteryTipsCardUpdated_null_hidePreference() { mBatteryTipsController.handleBatteryTipsCardUpdated(/* powerAnomalyEvents= */ null, false); - verify(mBatteryTipsCardPreference).setVisible(false); + assertThat(mCardPreference.isVisible()).isFalse(); } @Test public void handleBatteryTipsCardUpdated_adaptiveBrightnessAnomaly_showAnomaly() { - PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(); + AnomalyEventWrapper anomalyEventWrapper = + spy( + new AnomalyEventWrapper( + mContext, + BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(true))); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - mBatteryTipsController.handleBatteryTipsCardUpdated( - new AnomalyEventWrapper(mContext, event), false); + mBatteryTipsController.handleBatteryTipsCardUpdated(anomalyEventWrapper, false); - // Check pre-defined string - verify(mBatteryTipsCardPreference) - .setTitle("Turn on adaptive brightness to extend battery life"); - verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb); - verify(mBatteryTipsCardPreference).setButtonColorResourceId(R.color.color_accent_selector); - verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings"); - verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it"); - // Check proto info - verify(mBatteryTipsCardPreference).setVisible(true); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_BRIGHTNESS.getNumber()); + assertThat(mCardPreference.getTitle()) + .isEqualTo("Turn on adaptive brightness to extend battery life"); + assertThat(mCardPreference.getPrimaryButtonText()).isEqualTo("Got it"); + assertThat(mCardPreference.getSecondaryButtonText()).isEqualTo("View Settings"); + assertThat(mCardPreference.getIconResId()).isEqualTo(R.drawable.ic_battery_tips_lightbulb); + assertThat(mCardPreference.getTintColorResId()).isEqualTo(R.color.color_accent_selector); + assertThat(mCardPreference.getPrimaryButtonVisibility()).isTrue(); + assertThat(mCardPreference.getSecondaryButtonVisibility()).isTrue(); + assertCardButtonActionAndMetrics(anomalyEventWrapper); } @Test public void handleBatteryTipsCardUpdated_screenTimeoutAnomaly_showAnomaly() { - PowerAnomalyEvent event = BatteryTestUtils.createScreenTimeoutAnomalyEvent(); + AnomalyEventWrapper anomalyEventWrapper = + spy( + new AnomalyEventWrapper( + mContext, BatteryTestUtils.createScreenTimeoutAnomalyEvent(true))); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - mBatteryTipsController.handleBatteryTipsCardUpdated( - new AnomalyEventWrapper(mContext, event), false); + mBatteryTipsController.handleBatteryTipsCardUpdated(anomalyEventWrapper, false); - verify(mBatteryTipsCardPreference).setTitle("Reduce screen timeout to extend battery life"); - verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb); - verify(mBatteryTipsCardPreference).setButtonColorResourceId(R.color.color_accent_selector); - verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings"); - verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it"); - verify(mBatteryTipsCardPreference).setVisible(true); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_SCREEN_TIMEOUT.getNumber()); + assertThat(mCardPreference.getTitle()) + .isEqualTo("Reduce screen timeout to extend battery life"); + assertThat(mCardPreference.getPrimaryButtonText()).isEqualTo("Got it"); + assertThat(mCardPreference.getSecondaryButtonText()).isEqualTo("View Settings"); + assertThat(mCardPreference.getIconResId()).isEqualTo(R.drawable.ic_battery_tips_lightbulb); + assertThat(mCardPreference.getTintColorResId()).isEqualTo(R.color.color_accent_selector); + assertThat(mCardPreference.getPrimaryButtonVisibility()).isTrue(); + assertThat(mCardPreference.getSecondaryButtonVisibility()).isTrue(); + assertCardButtonActionAndMetrics(anomalyEventWrapper); } @Test public void handleBatteryTipsCardUpdated_screenTimeoutAnomalyHasTitle_showAnomaly() { - PowerAnomalyEvent event = BatteryTestUtils.createScreenTimeoutAnomalyEvent(); + PowerAnomalyEvent anomalyEvent = BatteryTestUtils.createScreenTimeoutAnomalyEvent(true); String testTitle = "TestTitle"; - event = - event.toBuilder() + anomalyEvent = + anomalyEvent.toBuilder() .setWarningBannerInfo( - event.getWarningBannerInfo().toBuilder() + anomalyEvent.getWarningBannerInfo().toBuilder() .setTitleString(testTitle) .build()) .build(); + AnomalyEventWrapper anomalyEventWrapper = + spy(new AnomalyEventWrapper(mContext, anomalyEvent)); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - mBatteryTipsController.handleBatteryTipsCardUpdated( - new AnomalyEventWrapper(mContext, event), false); + mBatteryTipsController.handleBatteryTipsCardUpdated(anomalyEventWrapper, false); - verify(mBatteryTipsCardPreference).setTitle(testTitle); - verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb); - verify(mBatteryTipsCardPreference).setButtonColorResourceId(R.color.color_accent_selector); - verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings"); - verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it"); - verify(mBatteryTipsCardPreference).setVisible(true); - verify(mFeatureFactory.metricsFeatureProvider) - .action( - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, - SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_SCREEN_TIMEOUT.getNumber()); + assertThat(mCardPreference.getTitle()).isEqualTo(testTitle); + assertThat(mCardPreference.getPrimaryButtonText()).isEqualTo("Got it"); + assertThat(mCardPreference.getSecondaryButtonText()).isEqualTo("View Settings"); + assertThat(mCardPreference.getIconResId()).isEqualTo(R.drawable.ic_battery_tips_lightbulb); + assertThat(mCardPreference.getTintColorResId()).isEqualTo(R.color.color_accent_selector); + assertThat(mCardPreference.getPrimaryButtonVisibility()).isTrue(); + assertThat(mCardPreference.getSecondaryButtonVisibility()).isTrue(); + assertCardButtonActionAndMetrics(anomalyEventWrapper); } @Test public void handleBatteryTipsCardUpdated_appAnomaly_showAnomaly() { - PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent(); + AnomalyEventWrapper anomalyEventWrapper = + spy(new AnomalyEventWrapper(mContext, BatteryTestUtils.createAppAnomalyEvent())); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - AnomalyEventWrapper eventWrapper = new AnomalyEventWrapper(mContext, event); - eventWrapper.setRelatedBatteryDiffEntry(new BatteryDiffEntry(mContext, "", "Chrome", 0)); - mBatteryTipsController.handleBatteryTipsCardUpdated(eventWrapper, false); + anomalyEventWrapper.setRelatedBatteryDiffEntry( + new BatteryDiffEntry(mContext, "", "Chrome", 0)); + mBatteryTipsController.setOnAnomalyConfirmListener( + () -> mBatteryTipsController.acceptTipsCard()); + mBatteryTipsController.handleBatteryTipsCardUpdated(anomalyEventWrapper, true); - verify(mBatteryTipsCardPreference).setTitle("Chrome used more battery than usual"); - verify(mBatteryTipsCardPreference) - .setIconResourceId(R.drawable.ic_battery_tips_warning_icon); - verify(mBatteryTipsCardPreference) - .setButtonColorResourceId(R.color.color_battery_anomaly_app_warning_selector); - verify(mBatteryTipsCardPreference).setMainButtonLabel("Check"); - verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it"); - verify(mBatteryTipsCardPreference).setVisible(true); + assertThat(mCardPreference.getTitle()).isEqualTo("Chrome used more battery than usual"); + assertThat(mCardPreference.getPrimaryButtonText()).isEqualTo("Got it"); + assertThat(mCardPreference.getSecondaryButtonText()).isEqualTo("Check"); + assertThat(mCardPreference.getIconResId()) + .isEqualTo(R.drawable.ic_battery_tips_warning_icon); + assertThat(mCardPreference.getTintColorResId()) + .isEqualTo(R.color.color_battery_anomaly_app_warning_selector); + assertThat(mCardPreference.getPrimaryButtonVisibility()).isTrue(); + assertThat(mCardPreference.getSecondaryButtonVisibility()).isTrue(); + assertThat(mCardPreference.isVisible()).isTrue(); + assertCardButtonActionAndMetrics(anomalyEventWrapper); + } + + private void assertCardButtonActionAndMetrics(final AnomalyEventWrapper anomalyEventWrapper) { + when(anomalyEventWrapper.updateSystemSettingsIfAvailable()).thenReturn(true); + + final int powerAnomalyKeyNumber = anomalyEventWrapper.getAnomalyKeyNumber(); + assertCardMetrics(SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, powerAnomalyKeyNumber); + assertThat(mCardPreference.isVisible()).isTrue(); + + // Check accept button action + mCardPreference.setVisible(true); + mCardPreference.getSecondaryButtonAction().invoke(); + assertCardMetrics(SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, powerAnomalyKeyNumber); + assertThat(mCardPreference.isVisible()).isFalse(); + final boolean isAppAnomalyCard = powerAnomalyKeyNumber > 1; + verify(anomalyEventWrapper, isAppAnomalyCard ? never() : times(1)) + .updateSystemSettingsIfAvailable(); + + // Check reject button action + mCardPreference.setVisible(true); + mCardPreference.getPrimaryButtonAction().invoke(); + assertCardMetrics(SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, powerAnomalyKeyNumber); + assertThat(mCardPreference.isVisible()).isFalse(); + } + + private void assertCardMetrics(final int action, final int powerAnomalyKeyNumber) { verify(mFeatureFactory.metricsFeatureProvider) .action( SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, - SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, + action, SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL, BatteryTipsController.ANOMALY_KEY, - PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL.getNumber()); + powerAnomalyKeyNumber); } } diff --git a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java index a151632de15..951f2f00137 100644 --- a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java @@ -264,18 +264,28 @@ public class BatteryTestUtils { /** Create a power anomaly event proto of screen timeout. */ public static PowerAnomalyEvent createScreenTimeoutAnomalyEvent() { + return createScreenTimeoutAnomalyEvent(false); + } + + /** Create a power anomaly event proto of screen timeout. */ + public static PowerAnomalyEvent createScreenTimeoutAnomalyEvent(boolean changeSettings) { + WarningBannerInfo.Builder warningBannerInfoBuilder = + WarningBannerInfo.newBuilder() + .setMainButtonDestination(ScreenTimeoutSettings.class.getName()) + .setMainButtonSourceMetricsCategory(SettingsEnums.SCREEN_TIMEOUT) + .setMainButtonSourceHighlightKey("60000"); + if (changeSettings) { + warningBannerInfoBuilder + .setMainButtonConfigSettingsName(Settings.System.SCREEN_OFF_TIMEOUT) + .setMainButtonConfigSettingsValue(60000); + } return PowerAnomalyEvent.newBuilder() .setEventId("ScreenTimeoutAnomaly") .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER) .setKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT) .setDismissRecordKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name()) .setScore(1.1f) - .setWarningBannerInfo( - WarningBannerInfo.newBuilder() - .setMainButtonDestination(ScreenTimeoutSettings.class.getName()) - .setMainButtonSourceMetricsCategory(SettingsEnums.SCREEN_TIMEOUT) - .setMainButtonSourceHighlightKey("60000") - .build()) + .setWarningBannerInfo(warningBannerInfoBuilder.build()) .build(); } From 2d382d5e3695a05b415c350d5c4e69833524c991 Mon Sep 17 00:00:00 2001 From: Diya Bera Date: Thu, 8 Aug 2024 16:25:11 +0000 Subject: [PATCH 7/7] Update mandatory biometrics string String has been changed from "This is needed since Identity Check is on" to "Identity Check is on" Flag: android.hardware.biometrics.flags.mandatory_biometrics Fixes: 358175977 Test: Manual Change-Id: I12976f8eb52f419e5a97c88c35a1264e843ec259 --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 1d602185c8a..6c018c23ec9 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -937,7 +937,7 @@ Face, fingerprints, and %s added - This is needed since Identity Check is on + Identity Check is on Remote Authenticator Unlock