From 398ca1ad59f252782b4cff8af003b074408a74bd Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 1 Feb 2024 15:38:22 +0800 Subject: [PATCH 01/14] Fix SurveyMixinTest The test doesn't need to mock fragment at all, just need to pass in a fragment without activity. Bug: 314929422 Test: atest Change-Id: I9fb2f38c9c00109dd0d74c8f1e0d405022f634e3 --- .../settings/survey/SurveyMixinTest.java | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java index 635343e2142..c38e36afea5 100644 --- a/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java +++ b/tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java @@ -2,53 +2,56 @@ package com.android.settings.survey; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import android.content.Context; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.testing.FragmentScenario; +import androidx.test.core.app.ApplicationProvider; -import androidx.fragment.app.FragmentActivity; - -import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.overlay.SurveyFeatureProvider; import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; -import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.Robolectric; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class SurveyMixinTest { + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private static final String FAKE_KEY = "fake_key"; - - private Context mContext; private SurveyFeatureProvider mProvider; - @Mock - private InstrumentedPreferenceFragment mFragment; @Before public void setUp() { // set up the fakefeature factory to mock out the survey provider - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; - mProvider = FakeFeatureFactory.setupForTest().getSurveyFeatureProvider(mContext); + mProvider = FakeFeatureFactory.setupForTest().getSurveyFeatureProvider( + ApplicationProvider.getApplicationContext()); } - @Ignore("b/314929422") @Test public void onResume_noActionIfActivityDoesNotExist() { - // Pretend we are an activity that is starting up - FragmentActivity temp = Robolectric.setupActivity(FragmentActivity.class); - when(mFragment.getActivity()).thenReturn(null); - SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY); + // Initialize a fragment without associating with an activity + Fragment fragment = new Fragment(); + SurveyMixin mixin = new SurveyMixin(fragment, FAKE_KEY); mixin.onResume(); verify(mProvider, times(0)).sendActivityIfAvailable(FAKE_KEY); } + + @Test + public void onResume_sendActivityWhenSurveyFeatureExists() { + try (var fragmentScenario = FragmentScenario.launch(Fragment.class)) { + fragmentScenario.onFragment(fragment -> { + SurveyMixin mixin = new SurveyMixin(fragment, FAKE_KEY); + mixin.onResume(); + }); + } + // Verify one send activity action is attempted + verify(mProvider).sendActivityIfAvailable(FAKE_KEY); + } } From e5f62676c868db35f69110535f3049c51024cb57 Mon Sep 17 00:00:00 2001 From: Joshua McCloskey Date: Thu, 21 Dec 2023 21:31:53 +0000 Subject: [PATCH 02/14] Adding biometric FRR Notification atom. Test: statsd_testdrive 793 Test: statsd_testdrive 184 Test: m -j50 RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.biometrics Test: atest FingerprintUpdaterTest FaceUpdaterTest Bug: 302171089 Change-Id: I4c921b75321db79cc975b98b54b176a43379cd7c --- .../settings/biometrics/BiometricUtils.java | 2 ++ .../biometrics/face/FaceEnrollEducation.java | 2 ++ .../biometrics/face/FaceEnrollEnrolling.java | 2 +- .../face/FaceEnrollIntroduction.java | 3 ++ .../biometrics/face/FaceEnrollSidecar.java | 8 +++-- .../settings/biometrics/face/FaceUpdater.java | 21 ++++++++++--- .../FingerprintEnrollEnrolling.java | 2 +- .../FingerprintEnrollFindSensor.java | 4 ++- .../FingerprintEnrollIntroduction.java | 3 +- .../fingerprint/FingerprintEnrollSidecar.java | 26 +++++++++++++--- .../fingerprint/FingerprintUpdater.java | 18 +++++++++-- .../FingerprintManagerInteractorImpl.kt | 27 ++++++++++++++++ .../FingerprintEnrollmentV2Activity.kt | 1 + .../fragment/FingerprintSettingsV2Fragment.kt | 1 + .../FingerprintEnrollProgressViewModel.java | 6 ++-- .../face/FaceEnrollIntroductionTest.java | 15 +++++++++ .../FingerprintEnrollEnrollingTest.java | 3 +- .../FingerprintEnrollFindSensorTest.java | 3 +- .../FingerprintEnrollIntroductionTest.java | 14 +++++++++ .../biometrics/face/FaceUpdaterTest.java | 31 ++++++++++++------- .../fingerprint/FingerprintUpdaterTest.java | 16 ++++++---- ...ingerprintEnrollProgressViewModelTest.java | 13 ++++---- .../FingerprintManagerInteractorTest.kt | 11 +++++-- 23 files changed, 184 insertions(+), 48 deletions(-) diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java index 53892bd9a1c..3376d86c115 100644 --- a/src/com/android/settings/biometrics/BiometricUtils.java +++ b/src/com/android/settings/biometrics/BiometricUtils.java @@ -25,6 +25,7 @@ import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.SensorProperties; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; @@ -65,6 +66,7 @@ import java.lang.annotation.RetentionPolicy; */ public class BiometricUtils { private static final String TAG = "BiometricUtils"; + public static final String EXTRA_ENROLL_REASON = BiometricManager.EXTRA_ENROLL_REASON; /** The character ' • ' to separate the setup choose options */ public static final String SEPARATOR = " \u2022 "; diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEducation.java b/src/com/android/settings/biometrics/face/FaceEnrollEducation.java index 62e9757b365..6862bc921d7 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollEducation.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollEducation.java @@ -265,6 +265,8 @@ public class FaceEnrollEducation extends BiometricEnrollBase { } intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, !mSwitchDiversity.isChecked()); + intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON, + getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)); if (!mSwitchDiversity.isChecked() && mAccessibilityEnabled) { FaceEnrollAccessibilityDialog dialog = FaceEnrollAccessibilityDialog.newInstance(); diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java index e8bd0efea26..da3689a6b95 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java @@ -165,7 +165,7 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling { disabledFeatures[i] = mDisabledFeatures.get(i); } - return new FaceEnrollSidecar(disabledFeatures); + return new FaceEnrollSidecar(disabledFeatures, getIntent()); } @Override diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java index eb50e0874c0..b0996a4f2b6 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java @@ -484,6 +484,9 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction { protected Intent getEnrollingIntent() { Intent intent = new Intent(this, FaceEnrollEducation.class); WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent); + intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON, + getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)); + return intent; } diff --git a/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java b/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java index de713dd3386..b7ba1b52508 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollSidecar.java @@ -18,6 +18,7 @@ package com.android.settings.biometrics.face; import android.app.Activity; import android.app.settings.SettingsEnums; +import android.content.Intent; import android.hardware.face.FaceManager; import com.android.settings.biometrics.BiometricEnrollSidecar; @@ -33,8 +34,11 @@ public class FaceEnrollSidecar extends BiometricEnrollSidecar { private FaceUpdater mFaceUpdater; - public FaceEnrollSidecar(int[] disabledFeatures) { + private Intent mIntent; + + public FaceEnrollSidecar(int[] disabledFeatures, Intent intent) { mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); + mIntent = intent; } @Override @@ -47,7 +51,7 @@ public class FaceEnrollSidecar extends BiometricEnrollSidecar { public void startEnrollment() { super.startEnrollment(); mFaceUpdater.enroll(mUserId, mToken, mEnrollmentCancel, - mEnrollmentCallback, mDisabledFeatures); + mEnrollmentCallback, mDisabledFeatures, mIntent); } private FaceManager.EnrollmentCallback mEnrollmentCallback diff --git a/src/com/android/settings/biometrics/face/FaceUpdater.java b/src/com/android/settings/biometrics/face/FaceUpdater.java index 57c11953d6a..ddb68129df1 100644 --- a/src/com/android/settings/biometrics/face/FaceUpdater.java +++ b/src/com/android/settings/biometrics/face/FaceUpdater.java @@ -17,8 +17,10 @@ package com.android.settings.biometrics.face; import android.content.Context; +import android.content.Intent; import android.hardware.face.Face; import android.hardware.face.FaceEnrollCell; +import android.hardware.face.FaceEnrollOptions; import android.hardware.face.FaceManager; import android.os.CancellationSignal; import android.view.Surface; @@ -26,6 +28,7 @@ import android.view.Surface; import androidx.annotation.Nullable; import com.android.settings.Utils; +import com.android.settings.biometrics.BiometricUtils; import com.android.settings.safetycenter.BiometricsSafetySource; /** @@ -49,19 +52,19 @@ public class FaceUpdater { /** Wrapper around the {@link FaceManager#enroll} method. */ public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, - FaceManager.EnrollmentCallback callback, int[] disabledFeatures) { + FaceManager.EnrollmentCallback callback, int[] disabledFeatures, Intent intent) { this.enroll(userId, hardwareAuthToken, cancel, new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures, - null, false); + null, false, intent); } /** Wrapper around the {@link FaceManager#enroll} method. */ public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, FaceManager.EnrollmentCallback callback, int[] disabledFeatures, - @Nullable Surface previewSurface, boolean debugConsent) { + @Nullable Surface previewSurface, boolean debugConsent, Intent intent) { mFaceManager.enroll(userId, hardwareAuthToken, cancel, new NotifyingEnrollmentCallback(mContext, callback), disabledFeatures, - previewSurface, debugConsent); + previewSurface, debugConsent, toFaceEnrollOptions(intent)); } /** Wrapper around the {@link FaceManager#remove} method. */ @@ -135,4 +138,14 @@ public class FaceUpdater { BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed } } + + private FaceEnrollOptions toFaceEnrollOptions(Intent intent) { + final int reason = intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1); + final FaceEnrollOptions.Builder builder = new FaceEnrollOptions.Builder(); + builder.setEnrollReason(FaceEnrollOptions.ENROLL_REASON_UNKNOWN); + if (reason != -1) { + builder.setEnrollReason(reason); + } + return builder.build(); + } } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java index 175e8f908ba..90b93461b9c 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java @@ -352,7 +352,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { @Override protected BiometricEnrollSidecar getSidecar() { final FingerprintEnrollSidecar sidecar = new FingerprintEnrollSidecar(this, - FingerprintManager.ENROLL_ENROLL); + FingerprintManager.ENROLL_ENROLL, getIntent()); return sidecar; } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java index be3a769839e..6b7538ae220 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java @@ -292,6 +292,8 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements @Override protected Intent getFingerprintEnrollingIntent() { final Intent ret = super.getFingerprintEnrollingIntent(); + ret.putExtra(BiometricUtils.EXTRA_ENROLL_REASON, + getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)); if (Flags.udfpsEnrollCalibration()) { if (mCalibrator != null) { ret.putExtras(mCalibrator.getExtrasForNextIntent(true)); @@ -349,7 +351,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements FingerprintEnrollEnrolling.TAG_SIDECAR); if (mSidecar == null) { mSidecar = new FingerprintEnrollSidecar(this, - FingerprintManager.ENROLL_FIND_SENSOR); + FingerprintManager.ENROLL_FIND_SENSOR, getIntent()); getSupportFragmentManager().beginTransaction() .add(mSidecar, FingerprintEnrollEnrolling.TAG_SIDECAR) .commitAllowingStateLoss(); diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java index 242b7450c4d..f92cfbf6696 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java @@ -59,7 +59,6 @@ import com.google.android.setupdesign.util.DeviceHelper; import java.util.List; public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction { - private static final String TAG = "FingerprintIntro"; @VisibleForTesting @@ -388,6 +387,8 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction { intent.putExtras(mCalibrator.getExtrasForNextIntent(false)); } } + intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON, + getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)); return intent; } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java index 493302bd13c..52d7d2be7a3 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollSidecar.java @@ -21,6 +21,8 @@ import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL; import android.app.Activity; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.Intent; +import android.hardware.fingerprint.FingerprintEnrollOptions; import android.hardware.fingerprint.FingerprintManager; import android.os.SystemClock; import android.util.Log; @@ -28,6 +30,9 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.biometrics.BiometricEnrollSidecar; +import com.android.settings.biometrics.BiometricUtils; + +import com.google.android.setupcompat.util.WizardManagerHelper; /** * Sidecar fragment to handle the state around fingerprint enrollment. @@ -39,15 +44,18 @@ public class FingerprintEnrollSidecar extends BiometricEnrollSidecar { private @FingerprintManager.EnrollReason int mEnrollReason; private final MessageDisplayController mMessageDisplayController; private final boolean mMessageDisplayControllerFlag; + private final Intent mIntent; /** * Create a new FingerprintEnrollSidecar object. - * @param context associated context + * + * @param context associated context * @param enrollReason reason for enrollment */ public FingerprintEnrollSidecar(Context context, - @FingerprintManager.EnrollReason int enrollReason) { + @FingerprintManager.EnrollReason int enrollReason, Intent intent) { mEnrollReason = enrollReason; + mIntent = intent; int helpMinimumDisplayTime = context.getResources().getInteger( R.integer.enrollment_help_minimum_time_display); @@ -85,14 +93,21 @@ public class FingerprintEnrollSidecar extends BiometricEnrollSidecar { return; } + if (mIntent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) == -1) { + final boolean isSuw = WizardManagerHelper.isAnySetupWizard(mIntent); + mIntent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON, + isSuw ? FingerprintEnrollOptions.ENROLL_REASON_SUW : + FingerprintEnrollOptions.ENROLL_REASON_SETTINGS); + } + if (mEnrollReason == ENROLL_ENROLL && mMessageDisplayControllerFlag) { //API calls need to be processed for {@link FingerprintEnrollEnrolling} mFingerprintUpdater.enroll(mToken, mEnrollmentCancel, mUserId, - mMessageDisplayController, mEnrollReason); + mMessageDisplayController, mEnrollReason, mIntent); } else { //No processing required for {@link FingerprintEnrollFindSensor} mFingerprintUpdater.enroll(mToken, mEnrollmentCancel, mUserId, mEnrollmentCallback, - mEnrollReason); + mEnrollReason, mIntent); } } @@ -100,7 +115,8 @@ public class FingerprintEnrollSidecar extends BiometricEnrollSidecar { mEnrollReason = enrollReason; } - @VisibleForTesting FingerprintManager.EnrollmentCallback mEnrollmentCallback + @VisibleForTesting + FingerprintManager.EnrollmentCallback mEnrollmentCallback = new FingerprintManager.EnrollmentCallback() { @Override diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java index 306b1a3e136..ea9abf1bb5c 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUpdater.java @@ -17,13 +17,16 @@ package com.android.settings.biometrics.fingerprint; import android.content.Context; +import android.content.Intent; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintEnrollOptions; import android.hardware.fingerprint.FingerprintManager; import android.os.CancellationSignal; import androidx.annotation.Nullable; import com.android.settings.Utils; +import com.android.settings.biometrics.BiometricUtils; import com.android.settings.safetycenter.BiometricsSafetySource; /** @@ -48,9 +51,10 @@ public class FingerprintUpdater { /** Wrapper around the {@link FingerprintManager#enroll} method. */ public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId, FingerprintManager.EnrollmentCallback callback, - @FingerprintManager.EnrollReason int enrollReason) { + @FingerprintManager.EnrollReason int enrollReason, Intent intent) { mFingerprintManager.enroll(hardwareAuthToken, cancel, userId, - new NotifyingEnrollmentCallback(mContext, callback), enrollReason); + new NotifyingEnrollmentCallback(mContext, callback), enrollReason, + toFingerprintEnrollOptions(intent)); } /** Wrapper around the {@link FingerprintManager#remove} method. */ @@ -138,4 +142,14 @@ public class FingerprintUpdater { BiometricsSafetySource.onBiometricsChanged(mContext); // biometrics data changed } } + + private FingerprintEnrollOptions toFingerprintEnrollOptions(Intent intent) { + final int reason = intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1); + final FingerprintEnrollOptions.Builder builder = new FingerprintEnrollOptions.Builder(); + builder.setEnrollReason(FingerprintEnrollOptions.ENROLL_REASON_UNKNOWN); + if (reason != -1) { + builder.setEnrollReason(reason); + } + return builder.build(); + } } diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt index 135a36ce538..900f7cfb734 100644 --- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt +++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt @@ -18,12 +18,16 @@ package com.android.settings.biometrics.fingerprint2.domain.interactor import android.content.Context import android.content.Intent +import android.hardware.biometrics.BiometricConstants; +import android.hardware.biometrics.BiometricFingerprintConstants +import android.hardware.fingerprint.FingerprintEnrollOptions; import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback import android.hardware.fingerprint.FingerprintManager.RemovalCallback import android.os.CancellationSignal import android.util.Log import com.android.settings.biometrics.GatekeeperPasswordProvider +import com.android.settings.biometrics.BiometricUtils import com.android.settings.biometrics.fingerprint2.conversion.Util.toEnrollError import com.android.settings.biometrics.fingerprint2.conversion.Util.toOriginalReason import com.android.settings.biometrics.fingerprint2.data.repository.FingerprintSensorRepository @@ -35,6 +39,8 @@ import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintData import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow import com.android.settings.biometrics.fingerprint2.lib.model.SetupWizard import com.android.settings.password.ChooseLockSettingsHelper +import com.android.systemui.biometrics.shared.model.toFingerprintSensor +import com.google.android.setupcompat.util.WizardManagerHelper import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.CancellableContinuation @@ -60,6 +66,7 @@ class FingerprintManagerInteractorImpl( private val gatekeeperPasswordProvider: GatekeeperPasswordProvider, private val pressToAuthInteractor: PressToAuthInteractor, private val fingerprintFlow: FingerprintFlow, + private val intent: Intent, ) : FingerprintManagerInteractor { private val maxFingerprints = @@ -158,12 +165,21 @@ class FingerprintManagerInteractorImpl( } val cancellationSignal = CancellationSignal() + + if (intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) === -1) { + val isSuw: Boolean = WizardManagerHelper.isAnySetupWizard(intent) + intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON, + if (isSuw) FingerprintEnrollOptions.ENROLL_REASON_SUW else + FingerprintEnrollOptions.ENROLL_REASON_SETTINGS) + } + fingerprintManager.enroll( hardwareAuthToken, cancellationSignal, applicationContext.userId, enrollmentCallback, enrollReason.toOriginalReason(), + toFingerprintEnrollOptions(intent) ) awaitClose { // If the stream has not been ended, and the user has stopped collecting the flow @@ -244,4 +260,15 @@ class FingerprintManagerInteractorImpl( applicationContext.userId, ) } + + private fun toFingerprintEnrollOptions(intent: Intent): FingerprintEnrollOptions { + val reason: Int = + intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1) + val builder: FingerprintEnrollOptions.Builder = FingerprintEnrollOptions.Builder() + builder.setEnrollReason(FingerprintEnrollOptions.ENROLL_REASON_UNKNOWN) + if (reason != -1) { + builder.setEnrollReason(reason) + } + return builder.build() + } } diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt index 9f3935bf070..65030de3f07 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/activity/FingerprintEnrollmentV2Activity.kt @@ -191,6 +191,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() { GatekeeperPasswordProvider(LockPatternUtils(context)), pressToAuthInteractor, enrollType, + getIntent(), ) var challenge: Long? = intent.getExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE) as Long? diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt index a5f20212180..bd905242fc3 100644 --- a/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt +++ b/src/com/android/settings/biometrics/fingerprint2/ui/settings/fragment/FingerprintSettingsV2Fragment.kt @@ -232,6 +232,7 @@ class FingerprintSettingsV2Fragment : GatekeeperPasswordProvider(LockPatternUtils(context.applicationContext)), pressToAuthInteractor, Settings, + getIntent() ) val token = intent.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN) diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java index 9b25ee8f0a1..1cfec52df69 100644 --- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java +++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java @@ -23,6 +23,7 @@ import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITI import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_STEPS; import android.app.Application; +import android.content.Intent; import android.content.res.Resources; import android.hardware.fingerprint.FingerprintManager.EnrollReason; import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback; @@ -212,10 +213,11 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel { res.getBoolean(R.bool.enrollment_progress_priority_over_help), res.getBoolean(R.bool.enrollment_prioritize_acquire_messages), res.getInteger(R.integer.enrollment_collect_time)); - mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, callback, reason); + mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, callback, reason, + new Intent()); } else { mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, mEnrollmentCallback, - reason); + reason, new Intent()); } return mCancellationSignal; } diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java index beb7a9f4f11..a56c34cacdf 100644 --- a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java @@ -43,6 +43,7 @@ import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import android.hardware.face.Face; +import android.hardware.face.FaceEnrollOptions; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorProperties; import android.hardware.face.FaceSensorPropertiesInternal; @@ -196,6 +197,8 @@ public class FaceEnrollIntroductionTest { final Intent testIntent = new Intent(); // Set the challenge token so the confirm screen will not be shown testIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]); + testIntent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON, + FaceEnrollOptions.ENROLL_REASON_SETTINGS); when(mFakeFeatureFactory.mFaceFeatureProvider.getPostureGuidanceIntent(any())).thenReturn( null /* Simulate no posture intent */); @@ -220,6 +223,8 @@ public class FaceEnrollIntroductionTest { testIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]); testIntent.putExtra(EXTRA_KEY_NEXT_LAUNCHED, false); testIntent.putExtra(EXTRA_LAUNCHED_POSTURE_GUIDANCE, false); + testIntent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON, + FaceEnrollOptions.ENROLL_REASON_SETTINGS); when(mFakeFeatureFactory.mFaceFeatureProvider.getPostureGuidanceIntent(any())).thenReturn( testIntent); @@ -641,4 +646,14 @@ public class FaceEnrollIntroductionTest { final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNull(); } + + @Test + public void testFaceEnrollIntroduction_forwardsEnrollOptions() { + setupActivity(); + final Intent intent = mActivity.getEnrollingIntent(); + + assertThat(intent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)) + .isEqualTo(FaceEnrollOptions.ENROLL_REASON_SETTINGS); + } + } diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java index 4de369e5f41..91707cd6e84 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java @@ -669,7 +669,8 @@ public class FingerprintEnrollEnrollingTest { any(CancellationSignal.class), anyInt(), callbackCaptor.capture(), - eq(FingerprintManager.ENROLL_ENROLL)); + eq(FingerprintManager.ENROLL_ENROLL), + any()); return callbackCaptor.getValue(); } diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java index 4d13bb63d19..68395b27001 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensorTest.java @@ -227,7 +227,8 @@ public class FingerprintEnrollFindSensorTest { any(CancellationSignal.class), anyInt(), callbackCaptor.capture(), - eq(FingerprintManager.ENROLL_FIND_SENSOR)); + eq(FingerprintManager.ENROLL_FIND_SENSOR), + any()); return callbackCaptor.getValue(); } diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java index 3eba91c854f..89e70d2990f 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java @@ -40,6 +40,7 @@ import android.content.res.Resources; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.SensorProperties; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintEnrollOptions; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -52,6 +53,7 @@ import androidx.annotation.Nullable; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.VerifyCredentialResponse; import com.android.settings.R; +import com.android.settings.biometrics.BiometricUtils; import com.android.settings.biometrics.GatekeeperPasswordProvider; import com.google.android.setupcompat.util.WizardManagerHelper; @@ -289,6 +291,18 @@ public class FingerprintEnrollIntroductionTest { assertThat(mFingerprintEnrollIntroduction.shouldFinishWhenBackgrounded()).isEqualTo(true); } + @Test + public void testFingerprintEnrollIntroduction_forwardsEnrollOptions() { + final Intent intent = newTokenOnlyIntent(); + intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON, + FingerprintEnrollOptions.ENROLL_REASON_SETTINGS); + setupFingerprintEnrollIntroWith(intent); + + final Intent enrollingIntent = mFingerprintEnrollIntroduction.getEnrollingIntent(); + assertThat(enrollingIntent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)) + .isEqualTo(FingerprintEnrollOptions.ENROLL_REASON_SUW); + } + private Intent newTokenOnlyIntent() { return new Intent() .putExtra(EXTRA_KEY_CHALLENGE_TOKEN, new byte[] { 1 }); diff --git a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java index 9190d0a5b82..1701eaae14e 100644 --- a/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java +++ b/tests/unit/src/com/android/settings/biometrics/face/FaceUpdaterTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; +import android.content.Intent; import android.hardware.face.Face; import android.hardware.face.FaceEnrollCell; import android.hardware.face.FaceEnrollStages; @@ -93,7 +94,7 @@ public class FaceUpdaterTest { ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, - DISABLED_FEATURES); + DISABLED_FEATURES, new Intent()); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), @@ -101,7 +102,8 @@ public class FaceUpdaterTest { callbackCaptor.capture(), same(DISABLED_FEATURES), same(null), - eq(false)); + eq(false), + any()); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING); @@ -121,7 +123,7 @@ public class FaceUpdaterTest { ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, - DISABLED_FEATURES); + DISABLED_FEATURES, new Intent()); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), @@ -129,7 +131,8 @@ public class FaceUpdaterTest { callbackCaptor.capture(), same(DISABLED_FEATURES), same(null), - eq(false)); + eq(false), + any()); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 0); @@ -142,7 +145,7 @@ public class FaceUpdaterTest { ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, - DISABLED_FEATURES); + DISABLED_FEATURES, new Intent()); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), @@ -150,7 +153,8 @@ public class FaceUpdaterTest { callbackCaptor.capture(), same(DISABLED_FEATURES), same(null), - eq(false)); + eq(false), + any()); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 1); @@ -163,7 +167,7 @@ public class FaceUpdaterTest { ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, - DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT); + DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent()); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), @@ -171,7 +175,8 @@ public class FaceUpdaterTest { callbackCaptor.capture(), same(DISABLED_FEATURES), same(PREVIEW_SURFACE), - eq(DEBUG_CONSENT)); + eq(DEBUG_CONSENT), + any()); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING); @@ -191,7 +196,7 @@ public class FaceUpdaterTest { ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, - DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT); + DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent()); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), @@ -199,7 +204,8 @@ public class FaceUpdaterTest { callbackCaptor.capture(), same(DISABLED_FEATURES), same(PREVIEW_SURFACE), - eq(DEBUG_CONSENT)); + eq(DEBUG_CONSENT), + any()); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 0); @@ -212,7 +218,7 @@ public class FaceUpdaterTest { ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class); mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback, - DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT); + DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent()); verify(mFaceManager).enroll( eq(USER_ID), same(HARDWARE_AUTH_TOKEN), @@ -220,7 +226,8 @@ public class FaceUpdaterTest { callbackCaptor.capture(), same(DISABLED_FEATURES), same(PREVIEW_SURFACE), - eq(DEBUG_CONSENT)); + eq(DEBUG_CONSENT), + any()); FaceManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 1); diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java index 62435b4f8b2..6218efb0172 100644 --- a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java +++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintUpdaterTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; +import android.content.Intent; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.os.CancellationSignal; @@ -78,13 +79,14 @@ public class FingerprintUpdaterTest { ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class); mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID, - mEnrollmentCallback, ENROLL_REASON); + mEnrollmentCallback, ENROLL_REASON, new Intent()); verify(mFingerprintManager).enroll( same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), eq(USER_ID), callbackCaptor.capture(), - eq(ENROLL_REASON)); + eq(ENROLL_REASON), + any()); FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING); @@ -101,13 +103,14 @@ public class FingerprintUpdaterTest { ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class); mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID, - mEnrollmentCallback, ENROLL_REASON); + mEnrollmentCallback, ENROLL_REASON, new Intent()); verify(mFingerprintManager).enroll( same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), eq(USER_ID), callbackCaptor.capture(), - eq(ENROLL_REASON)); + eq(ENROLL_REASON), + any()); FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 0); @@ -120,13 +123,14 @@ public class FingerprintUpdaterTest { ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class); mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID, - mEnrollmentCallback, ENROLL_REASON); + mEnrollmentCallback, ENROLL_REASON, new Intent()); verify(mFingerprintManager).enroll( same(HARDWARE_AUTH_TOKEN), same(CANCELLATION_SIGNAL), eq(USER_ID), callbackCaptor.capture(), - eq(ENROLL_REASON)); + eq(ENROLL_REASON), + any()); FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue(); callback.onEnrollmentProgress(/* remaining= */ 1); diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java index 418db04f7cc..2c7afa62fe4 100644 --- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java +++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java @@ -98,7 +98,7 @@ public class FingerprintEnrollProgressViewModelTest { mCallbackWrapper.mValue = invocation.getArgument(3); return null; }).when(mFingerprintUpdater).enroll(any(byte[].class), any(CancellationSignal.class), - eq(TEST_USER_ID), any(EnrollmentCallback.class), anyInt()); + eq(TEST_USER_ID), any(EnrollmentCallback.class), anyInt(), any()); } @Test @@ -112,7 +112,7 @@ public class FingerprintEnrollProgressViewModelTest { assertThat(ret).isNotNull(); verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class), - eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason)); + eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason), any()); assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse(); } @@ -127,7 +127,7 @@ public class FingerprintEnrollProgressViewModelTest { assertThat(ret).isNotNull(); verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class), - eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason)); + eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason), any()); assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse(); } @@ -146,7 +146,7 @@ public class FingerprintEnrollProgressViewModelTest { assertThat(ret).isNotNull(); verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class), - eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason)); + eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason), any()); assertThat(mCallbackWrapper.mValue).isNotNull(); assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isTrue(); @@ -158,7 +158,7 @@ public class FingerprintEnrollProgressViewModelTest { // Shall not use the same MessageDisplayController verify(mFingerprintUpdater, times(2)).enroll(eq(token), any(CancellationSignal.class), - eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason)); + eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason), any()); assertThat(mCallbackWrapper.mValue).isNotNull(); assertThat(callback1).isNotEqualTo(mCallbackWrapper.mValue); } @@ -170,7 +170,8 @@ public class FingerprintEnrollProgressViewModelTest { assertThat(ret).isNull(); verify(mFingerprintUpdater, never()).enroll(any(byte[].class), - any(CancellationSignal.class), anyInt(), any(EnrollmentCallback.class), anyInt()); + any(CancellationSignal.class), anyInt(), any(EnrollmentCallback.class), anyInt(), + any()); } @Test diff --git a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt index c284a6fee7f..2b4bff77bc7 100644 --- a/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt +++ b/tests/unit/src/com/android/settings/fingerprint2/domain/interactor/FingerprintManagerInteractorTest.kt @@ -19,6 +19,7 @@ package com.android.settings.fingerprint2.domain.interactor import android.content.Context import android.content.Intent import android.hardware.fingerprint.Fingerprint +import android.hardware.fingerprint.FingerprintEnrollOptions import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintManager.CryptoObject import android.hardware.fingerprint.FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT @@ -99,6 +100,7 @@ class FingerprintManagerInteractorTest { gateKeeperPasswordProvider, pressToAuthInteractor, Default, + Intent(), ) } @@ -312,7 +314,8 @@ class FingerprintManagerInteractorTest { any(CancellationSignal::class.java), anyInt(), capture(enrollCallback), - eq(FingerprintManager.ENROLL_FIND_SENSOR) + eq(FingerprintManager.ENROLL_FIND_SENSOR), + any(FingerprintEnrollOptions::class.java), ) enrollCallback.value.onEnrollmentProgress(1) runCurrent() @@ -336,7 +339,8 @@ class FingerprintManagerInteractorTest { any(CancellationSignal::class.java), anyInt(), capture(enrollCallback), - eq(FingerprintManager.ENROLL_FIND_SENSOR) + eq(FingerprintManager.ENROLL_FIND_SENSOR), + any(FingerprintEnrollOptions::class.java), ) enrollCallback.value.onEnrollmentHelp(-1, "help") runCurrent() @@ -360,7 +364,8 @@ class FingerprintManagerInteractorTest { any(CancellationSignal::class.java), anyInt(), capture(enrollCallback), - eq(FingerprintManager.ENROLL_FIND_SENSOR) + eq(FingerprintManager.ENROLL_FIND_SENSOR), + any(FingerprintEnrollOptions::class.java), ) enrollCallback.value.onEnrollmentError(-1, "error") runCurrent() From 507024bbdc32e3ed759221d970feadc054be2f5a Mon Sep 17 00:00:00 2001 From: sunnyshao Date: Sat, 17 Feb 2024 11:07:01 +0800 Subject: [PATCH 03/14] Update the datetime page UI - Use the SwitchPreferenceCompat to replace the SwitchPreference. Fixes: 322679264 Test: manual test Change-Id: I2b940538b7f73a7cc0e365a05780eb84900034d4 --- res/xml/date_time_prefs.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/xml/date_time_prefs.xml b/res/xml/date_time_prefs.xml index f1c62bb753c..32684666b74 100644 --- a/res/xml/date_time_prefs.xml +++ b/res/xml/date_time_prefs.xml @@ -57,7 +57,7 @@ settings:controller="com.android.settings.datetime.LocationProviderStatusPreferenceController"/> - @@ -77,12 +77,12 @@ android:key="time_format_preference_category" android:title="@string/time_format_category_title" settings:keywords="@string/keywords_time_format"> - - From c7e3149a2505180fd75fc68e46c0190b6c822a0b Mon Sep 17 00:00:00 2001 From: Wu Ahan Date: Mon, 19 Feb 2024 14:49:26 +0000 Subject: [PATCH 04/14] Refactor SfpsEnrollmentFeatureImplTest Bug: 288155127 Test: atest SfpsEnrollmentFeatureImplTest Change-Id: I2725e73ad595db752328bc5092ff4295f634c154 --- .../feature/SfpsEnrollmentFeatureImpl.java | 6 +- .../feature/SfpsEnrollmentFeatureImplTest.kt | 109 +++++++----------- 2 files changed, 46 insertions(+), 69 deletions(-) diff --git a/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java index abc31c94580..60ced6e95f1 100644 --- a/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java +++ b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImpl.java @@ -33,11 +33,14 @@ import android.view.animation.AccelerateInterpolator; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import java.util.function.Function; public class SfpsEnrollmentFeatureImpl implements SfpsEnrollmentFeature { + @VisibleForTesting + public static final int HELP_ANIMATOR_DURATION = 550; @Nullable private FingerprintManager mFingerprintManager = null; @@ -96,12 +99,11 @@ public class SfpsEnrollmentFeatureImpl implements SfpsEnrollmentFeature { @Override public Animator getHelpAnimator(@NonNull View target) { final float translationX = 40; - final int duration = 550; final ObjectAnimator help = ObjectAnimator.ofFloat(target, "translationX" /* propertyName */, 0, translationX, -1 * translationX, translationX, 0f); help.setInterpolator(new AccelerateInterpolator()); - help.setDuration(duration); + help.setDuration(HELP_ANIMATOR_DURATION); help.setAutoCancel(false); return help; } diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt b/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt index 794cfec1b3a..f7256b9d754 100644 --- a/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt +++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeatureImplTest.kt @@ -44,16 +44,24 @@ import org.mockito.Mockito.`when` as whenever @RunWith(AndroidJUnit4::class) class SfpsEnrollmentFeatureImplTest { + private val STAGE_0 = 0 + private val STAGE_1 = 1 + private val STAGE_2 = 2 + private val STAGE_3 = 3 + private val STAGE_4 = 4 + + private val THRESHOLD_0 = 0f + private val THRESHOLD_1 = .36f + private val THRESHOLD_2 = .52f + private val THRESHOLD_3 = .76f + private val THRESHOLD_4 = 1f + @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @Spy private val context: Context = ApplicationProvider.getApplicationContext() - private val settingsPackageName = "com.android.settings" - - private lateinit var settingsContext: Context - @Mock private lateinit var mockFingerprintManager: FingerprintManager @@ -61,31 +69,34 @@ class SfpsEnrollmentFeatureImplTest { @Before fun setUp() { - assertThat(mSfpsEnrollmentFeatureImpl).isInstanceOf(SfpsEnrollmentFeatureImpl::class.java) whenever(context.getSystemService(FingerprintManager::class.java)) .thenReturn(mockFingerprintManager) - doReturn(0f).`when`(mockFingerprintManager).getEnrollStageThreshold(0) - doReturn(0.36f).`when`(mockFingerprintManager).getEnrollStageThreshold(1) - doReturn(0.52f).`when`(mockFingerprintManager).getEnrollStageThreshold(2) - doReturn(0.76f).`when`(mockFingerprintManager).getEnrollStageThreshold(3) - doReturn(1f).`when`(mockFingerprintManager).getEnrollStageThreshold(4) - settingsContext = context.createPackageContext(settingsPackageName, 0) + doReturn(THRESHOLD_0).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_0) + doReturn(THRESHOLD_1).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_1) + doReturn(THRESHOLD_2).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_2) + doReturn(THRESHOLD_3).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_3) + doReturn(THRESHOLD_4).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_4) } @Test fun testGetEnrollStageThreshold() { - assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 0)).isEqualTo(0f) - assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 1)).isEqualTo(0.36f) - assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 2)).isEqualTo(0.52f) - assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 3)).isEqualTo(0.76f) - assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 4)).isEqualTo(1f) + assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_0)) + .isEqualTo(THRESHOLD_0) + assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_1)) + .isEqualTo(THRESHOLD_1) + assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_2)) + .isEqualTo(THRESHOLD_2) + assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_3)) + .isEqualTo(THRESHOLD_3) + assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_4)) + .isEqualTo(THRESHOLD_4) } @Test fun testGetHelpAnimator() { val mockView: View = mock(View::class.java) val animator: Animator = mSfpsEnrollmentFeatureImpl.getHelpAnimator(mockView) - assertThat(animator.duration).isEqualTo(550) + assertThat(animator.duration).isEqualTo(SfpsEnrollmentFeatureImpl.HELP_ANIMATOR_DURATION) } @Test @@ -115,42 +126,27 @@ class SfpsEnrollmentFeatureImplTest { assertThat( mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_NO_ANIMATION) ).isEqualTo( - settingsContext.resources.getIdentifier( - "security_settings_fingerprint_enroll_repeat_title", - type, - settingsPackageName) + getSettingsResourcesId(type, "security_settings_fingerprint_enroll_repeat_title") ) assertThat( mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_CENTER) ).isEqualTo( - settingsContext.resources.getIdentifier( - "security_settings_sfps_enroll_finger_center_title", - type, - settingsPackageName) + getSettingsResourcesId(type, "security_settings_sfps_enroll_finger_center_title") ) assertThat( mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_FINGERTIP) ).isEqualTo( - settingsContext.resources.getIdentifier( - "security_settings_sfps_enroll_fingertip_title", - type, - settingsPackageName) + getSettingsResourcesId(type, "security_settings_sfps_enroll_fingertip_title") ) assertThat( mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_LEFT_EDGE) ).isEqualTo( - settingsContext.resources.getIdentifier( - "security_settings_sfps_enroll_left_edge_title", - type, - settingsPackageName) + getSettingsResourcesId(type, "security_settings_sfps_enroll_left_edge_title") ) assertThat( mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_RIGHT_EDGE) ).isEqualTo( - settingsContext.resources.getIdentifier( - "security_settings_sfps_enroll_right_edge_title", - type, - settingsPackageName) + getSettingsResourcesId(type, "security_settings_sfps_enroll_right_edge_title") ) } @@ -159,43 +155,22 @@ class SfpsEnrollmentFeatureImplTest { val type = "raw" assertThat( mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_NO_ANIMATION) - ).isEqualTo( - settingsContext.resources.getIdentifier( - "sfps_lottie_no_animation", - type, - settingsPackageName) - ) + ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_no_animation")) assertThat( mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_CENTER) - ).isEqualTo( - settingsContext.resources.getIdentifier( - "sfps_lottie_pad_center", - type, - settingsPackageName) - ) + ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_pad_center")) assertThat( mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_FINGERTIP) - ).isEqualTo( - settingsContext.resources.getIdentifier( - "sfps_lottie_tip", - type, - settingsPackageName) - ) + ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_tip")) assertThat( mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_LEFT_EDGE) - ).isEqualTo( - settingsContext.resources.getIdentifier( - "sfps_lottie_left_edge", - type, - settingsPackageName) - ) + ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_left_edge")) assertThat( mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_RIGHT_EDGE) - ).isEqualTo( - settingsContext.resources.getIdentifier( - "sfps_lottie_right_edge", - type, - settingsPackageName) - ) + ).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_right_edge")) + } + + private fun getSettingsResourcesId(type: String, name: String) : Int { + return context.resources.getIdentifier(name, type, context.packageName) } } \ No newline at end of file From 13461d2fb8cfcb35f0b52f8c84a8111125fa83d8 Mon Sep 17 00:00:00 2001 From: chelseahao Date: Tue, 20 Feb 2024 11:58:38 +0800 Subject: [PATCH 05/14] Add auto on toggle in settings. Test: atest com.android.settings.bluetooth Bug: b/316822488 b/316985153 Flag: ACONFIG com.android.settingslib.flags.bluetooth_qs_tile_dialog_auto_on_toggle DISABLED Change-Id: Iaa8ce3d3f6e2ffa25d8b7a35b5f55f4774ac4a40 --- res/values/strings.xml | 6 + res/xml/bluetooth_screen.xml | 5 + .../BluetoothAutoOnPreferenceController.java | 144 ++++++++++++++++++ .../BluetoothSwitchPreferenceController.java | 68 ++++++--- ...uetoothAutoOnPreferenceControllerTest.java | 97 ++++++++++++ 5 files changed, 303 insertions(+), 17 deletions(-) create mode 100644 src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 45305ece4f6..f82aa68075a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -132,6 +132,8 @@ Pair new device bluetooth + + Automatically turn on again tomorrow @@ -1800,8 +1802,12 @@ Pair & connect When Bluetooth is turned on, your device can communicate with other nearby Bluetooth devices + + When Bluetooth is on, your device can communicate with other nearby Bluetooth devices. Features like Quick Share, Find My Device, and device location use Bluetooth. When Bluetooth is turned on, your device can communicate with other nearby Bluetooth devices.\n\nTo improve device experience, apps and services can still scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services. You can change this in Bluetooth scanning settings. + + When Bluetooth is on, your device can communicate with other nearby Bluetooth devices. Features like Quick Share, Find My Device, and device location use Bluetooth.\n\nApps and services can still scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services. You can change this in Bluetooth scanning settings. Change diff --git a/res/xml/bluetooth_screen.xml b/res/xml/bluetooth_screen.xml index 86dc8a12c72..51507fb0ba1 100644 --- a/res/xml/bluetooth_screen.xml +++ b/res/xml/bluetooth_screen.xml @@ -18,6 +18,11 @@ xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/bluetooth_settings_title"> + + { + updateValue(); + mContext.getMainExecutor() + .execute( + () -> { + if (mPreference != null) { + updateState(mPreference); + } + }); + }); + } + }; + private int mAutoOnValue = UNSET; + @Nullable private TwoStatePreference mPreference; + + public BluetoothAutoOnPreferenceController( + @NonNull Context context, @NonNull String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public void onStart() { + mContext.getContentResolver() + .registerContentObserver( + Settings.Secure.getUriFor(SETTING_NAME), + /* notifyForDescendants= */ false, + mContentObserver); + } + + @Override + public void onStop() { + mContext.getContentResolver().unregisterContentObserver(mContentObserver); + } + + @Override + public int getAvailabilityStatus() { + if (!Flags.bluetoothQsTileDialogAutoOnToggle()) { + return UNSUPPORTED_ON_DEVICE; + } + updateValue(); + return mAutoOnValue != UNSET ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + @Override + public void displayPreference(@NonNull PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + } + + @Override + public String getPreferenceKey() { + return PREF_KEY; + } + + @Override + public boolean isChecked() { + return mAutoOnValue == ENABLED; + } + + @Override + public boolean setChecked(boolean isChecked) { + if (getAvailabilityStatus() != AVAILABLE) { + Log.w(TAG, "Trying to set toggle value while feature not available."); + return false; + } + var unused = + ThreadUtils.postOnBackgroundThread( + () -> { + boolean updated = + Settings.Secure.putIntForUser( + mContext.getContentResolver(), + SETTING_NAME, + isChecked ? ENABLED : DISABLED, + UserHandle.myUserId()); + if (updated) { + updateValue(); + } + }); + return true; + } + + @Override + public int getSliceHighlightMenuRes() { + return 0; + } + + private void updateValue() { + mAutoOnValue = + Settings.Secure.getIntForUser( + mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId()); + } +} diff --git a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java index 6fd5070303c..ac5575803c1 100644 --- a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java +++ b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java @@ -15,8 +15,13 @@ */ package com.android.settings.bluetooth; +import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME; +import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET; + import android.app.settings.SettingsEnums; import android.content.Context; +import android.os.UserHandle; +import android.provider.Settings; import android.view.View; import androidx.annotation.VisibleForTesting; @@ -29,6 +34,7 @@ import com.android.settings.widget.SwitchWidgetController; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; +import com.android.settingslib.flags.Flags; import com.android.settingslib.widget.FooterPreference; /** @@ -36,8 +42,11 @@ import com.android.settingslib.widget.FooterPreference; * is delegated to the SwitchWidgetController it uses. */ public class BluetoothSwitchPreferenceController - implements LifecycleObserver, OnStart, OnStop, - SwitchWidgetController.OnSwitchChangeListener, View.OnClickListener { + implements LifecycleObserver, + OnStart, + OnStop, + SwitchWidgetController.OnSwitchChangeListener, + View.OnClickListener { private BluetoothEnabler mBluetoothEnabler; private RestrictionUtils mRestrictionUtils; @@ -46,18 +55,21 @@ public class BluetoothSwitchPreferenceController private FooterPreference mFooterPreference; private boolean mIsAlwaysDiscoverable; - @VisibleForTesting - AlwaysDiscoverable mAlwaysDiscoverable; + @VisibleForTesting AlwaysDiscoverable mAlwaysDiscoverable; - public BluetoothSwitchPreferenceController(Context context, + public BluetoothSwitchPreferenceController( + Context context, SwitchWidgetController switchController, FooterPreference footerPreference) { this(context, new RestrictionUtils(), switchController, footerPreference); } @VisibleForTesting - public BluetoothSwitchPreferenceController(Context context, RestrictionUtils restrictionUtils, - SwitchWidgetController switchController, FooterPreference footerPreference) { + public BluetoothSwitchPreferenceController( + Context context, + RestrictionUtils restrictionUtils, + SwitchWidgetController switchController, + FooterPreference footerPreference) { mRestrictionUtils = restrictionUtils; mSwitch = switchController; mContext = context; @@ -66,11 +78,13 @@ public class BluetoothSwitchPreferenceController mSwitch.setupView(); updateText(mSwitch.isChecked()); - mBluetoothEnabler = new BluetoothEnabler(context, - switchController, - FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(), - SettingsEnums.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE, - mRestrictionUtils); + mBluetoothEnabler = + new BluetoothEnabler( + context, + switchController, + FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(), + SettingsEnums.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE, + mRestrictionUtils); mBluetoothEnabler.setToggleCallback(this); mAlwaysDiscoverable = new AlwaysDiscoverable(context); } @@ -97,8 +111,8 @@ public class BluetoothSwitchPreferenceController /** * Set whether the device can be discovered. By default the value will be {@code false}. * - * @param isAlwaysDiscoverable {@code true} if the device can be discovered, - * otherwise {@code false} + * @param isAlwaysDiscoverable {@code true} if the device can be discovered, otherwise {@code + * false} */ public void setAlwaysDiscoverable(boolean isAlwaysDiscoverable) { mIsAlwaysDiscoverable = isAlwaysDiscoverable; @@ -119,15 +133,35 @@ public class BluetoothSwitchPreferenceController .launch(); } - @VisibleForTesting void updateText(boolean isChecked) { + @VisibleForTesting + void updateText(boolean isChecked) { if (!isChecked && Utils.isBluetoothScanningEnabled(mContext)) { - mFooterPreference.setTitle(R.string.bluetooth_scanning_on_info_message); + if (isAutoOnFeatureAvailable()) { + mFooterPreference.setTitle( + R.string.bluetooth_scanning_on_info_message_auto_on_available); + } else { + mFooterPreference.setTitle(R.string.bluetooth_scanning_on_info_message); + } mFooterPreference.setLearnMoreText(mContext.getString(R.string.bluetooth_scan_change)); mFooterPreference.setLearnMoreAction(v -> onClick(v)); } else { - mFooterPreference.setTitle(R.string.bluetooth_empty_list_bluetooth_off); + if (isAutoOnFeatureAvailable()) { + mFooterPreference.setTitle( + R.string.bluetooth_empty_list_bluetooth_off_auto_on_available); + } else { + mFooterPreference.setTitle(R.string.bluetooth_empty_list_bluetooth_off); + } mFooterPreference.setLearnMoreText(""); mFooterPreference.setLearnMoreAction(null); } } + + private boolean isAutoOnFeatureAvailable() { + if (!Flags.bluetoothQsTileDialogAutoOnToggle()) { + return false; + } + return Settings.Secure.getIntForUser( + mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId()) + != UNSET; + } } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java new file mode 100644 index 00000000000..d82dcc43ac3 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.bluetooth; + +import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.DISABLED; +import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.ENABLED; +import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.PREF_KEY; +import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME; +import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET; +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; +import static com.android.settingslib.flags.Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; + +import android.content.ContentResolver; +import android.content.Context; +import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Settings; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class BluetoothAutoOnPreferenceControllerTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private Context mContext; + private ContentResolver mContentResolver; + private BluetoothAutoOnPreferenceController mController; + + @Before + public void setUp() { + mSetFlagsRule.enableFlags(FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE); + mContext = spy(ApplicationProvider.getApplicationContext()); + mContentResolver = mContext.getContentResolver(); + mController = new BluetoothAutoOnPreferenceController(mContext, PREF_KEY); + } + + @Test + public void getAvailability_valueUnset_returnUnsupported() { + Settings.Secure.putInt(mContentResolver, SETTING_NAME, UNSET); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + } + + @Test + public void getAvailability_valueSet_returnAvailable() { + Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + public void isChecked_valueEnabled_returnTrue() { + Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + assertThat(mController.isChecked()).isEqualTo(true); + } + + @Test + public void setChecked_returnTrue() { + Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED); + + mController.setChecked(true); + assertThat(mController.isChecked()).isEqualTo(true); + } + + @Test + public void setChecked_returnFalse() { + Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED); + + mController.setChecked(false); + assertThat(mController.isChecked()).isEqualTo(false); + } +} From 5bbc1166f3b3068d53d7c7e11a51c3bddc229f56 Mon Sep 17 00:00:00 2001 From: Jigar Thakkar Date: Wed, 14 Feb 2024 21:01:17 +0000 Subject: [PATCH 06/14] Use SetScreenLockDialogActivity to power screen lock setup prompt With this change, we move to using SetScreenLockDialogActivity to enable the prompt to set up the screen lock. This prompt is shown whenever someone taps the private space settings page option to either setup/unlock the space. We will also be using the same dialog prompt in other places in the framework (UserManagerService.requestQuietModeEnabled) as well. Test: atest PrivateSpaceAuthenticationActivityTest Bug: 316129700 Flag: android.multiuser.show_set_screen_lock_dialog Change-Id: Ib9fe009d3b36e0eea242fbf894e616b1efcb9d6b --- .../PrivateSpaceAuthenticationActivity.java | 45 ++++++++++++------- .../PrivateSpaceAuthenticationActivityTest.kt | 11 +++-- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java index 149c0d6881e..63b1dc95a98 100644 --- a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java +++ b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java @@ -18,6 +18,8 @@ package com.android.settings.privatespace; import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD; +import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS; + import android.app.ActivityOptions; import android.app.AlertDialog; import android.app.KeyguardManager; @@ -37,6 +39,7 @@ import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.SetScreenLockDialogActivity; import com.android.settings.R; import com.android.settings.core.SubSettingLauncher; import com.android.settingslib.transition.SettingsTransitionHelper; @@ -112,23 +115,31 @@ public class PrivateSpaceAuthenticationActivity extends FragmentActivity { private void promptToSetDeviceLock() { Log.d(TAG, "Show prompt to set device lock before using private space feature"); - new AlertDialog.Builder(this) - .setTitle(R.string.no_device_lock_title) - .setMessage(R.string.no_device_lock_summary) - .setPositiveButton( - R.string.no_device_lock_action_label, - (DialogInterface dialog, int which) -> { - Log.d(TAG, "Start activity to set new device lock"); - mSetDeviceLock.launch(new Intent(ACTION_SET_NEW_PASSWORD)); - }) - .setNegativeButton( - R.string.no_device_lock_cancel, - (DialogInterface dialog, int which) -> finish()) - .setOnCancelListener( - (DialogInterface dialog) -> { - finish(); - }) - .show(); + if (android.multiuser.Flags.showSetScreenLockDialog()) { + Intent setScreenLockPromptIntent = + SetScreenLockDialogActivity + .createBaseIntent(LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS); + startActivity(setScreenLockPromptIntent); + finish(); + } else { + new AlertDialog.Builder(this) + .setTitle(R.string.no_device_lock_title) + .setMessage(R.string.no_device_lock_summary) + .setPositiveButton( + R.string.no_device_lock_action_label, + (DialogInterface dialog, int which) -> { + Log.d(TAG, "Start activity to set new device lock"); + mSetDeviceLock.launch(new Intent(ACTION_SET_NEW_PASSWORD)); + }) + .setNegativeButton( + R.string.no_device_lock_cancel, + (DialogInterface dialog, int which) -> finish()) + .setOnCancelListener( + (DialogInterface dialog) -> { + finish(); + }) + .show(); + } } private KeyguardManager getKeyguardManager() { diff --git a/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt b/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt index 87514716082..8eadd9dde92 100644 --- a/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt +++ b/tests/uitests/src/com/android/settings/ui/privatespace/PrivateSpaceAuthenticationActivityTest.kt @@ -18,9 +18,11 @@ package com.android.settings.ui.privatespace import android.os.Flags +import android.platform.test.annotations.RequiresFlagsDisabled import android.platform.test.annotations.RequiresFlagsEnabled import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.provider.Settings +import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By @@ -77,15 +79,18 @@ class PrivateSpaceAuthenticationActivityTest { Thread.sleep(1000) device.assertHasTexts(listOf(SET_LOCK_BUTTON,CANCEL_TEXT)) device.clickObject(By.text(SET_LOCK_BUTTON)) - device.assertHasTexts(listOf(LOCK_SCREEN_TITLE)) + Thread.sleep(1000) + device.assertHasTexts(listOf(PATTERN_TEXT, PIN_TEXT, PASSWORD_TEXT)) } private companion object { // Items we really want to always show - val PRIVATE_SPACE_SETTING = "Private Space" + val PRIVATE_SPACE_SETTING = "Private space" const val SET_LOCK_BUTTON = "Set screen lock" val CANCEL_TEXT = "Cancel" val DIALOG_TITLE = "Set a screen lock" - val LOCK_SCREEN_TITLE = "Choose screen lock" + val PATTERN_TEXT = "Pattern" + val PIN_TEXT = "PIN" + val PASSWORD_TEXT = "Password" } } From 55969e59c1ff657703f7163699c2466f817e7522 Mon Sep 17 00:00:00 2001 From: David Liu Date: Tue, 20 Feb 2024 23:18:20 +0000 Subject: [PATCH 07/14] [Two-pane] Fix keyboard navigation issue, searchbar not able to be selected. - make search toolbar focusable - set next tab navigation element to homepage_container Bug: 324420544 Test: manual Change-Id: I2783b7540d954f7adf64c6ac3e90d91c196646c5 --- res/layout/search_bar_two_pane_version.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/res/layout/search_bar_two_pane_version.xml b/res/layout/search_bar_two_pane_version.xml index f98985ce46d..337294e0280 100644 --- a/res/layout/search_bar_two_pane_version.xml +++ b/res/layout/search_bar_two_pane_version.xml @@ -29,6 +29,9 @@ android:paddingStart="@dimen/search_bar_padding_start_two_pane" android:paddingEnd="@dimen/search_bar_padding_end_two_pane" android:background="@drawable/search_bar_selected_background" + android:focusable="true" + android:focusableInTouchMode="true" + android:nextFocusForward="@+id/homepage_container" android:contentInsetStartWithNavigation="@dimen/search_bar_content_inset" android:navigationIcon="@drawable/ic_homepage_search"> - \ No newline at end of file + From efcf1b7f056790d6c3c88f9cd4d1f0abd55b9c9f Mon Sep 17 00:00:00 2001 From: Nate Jiang Date: Tue, 20 Feb 2024 16:13:29 -0800 Subject: [PATCH 08/14] Move to new read only flag Mainline module can only support read-only flag now Bug: 325521253 Test: TH Change-Id: I4514fcd21560f95084ed3c9da1a0a9b13c625c91 --- src/com/android/settings/network/NetworkProviderSettings.java | 3 ++- .../android/settings/wifi/WepNetworksPreferenceController.kt | 2 +- src/com/android/settings/wifi/WifiConfigController.java | 2 +- src/com/android/settings/wifi/WifiConfigController2.java | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java index 0da1034994c..bcf0d00089b 100644 --- a/src/com/android/settings/network/NetworkProviderSettings.java +++ b/src/com/android/settings/network/NetworkProviderSettings.java @@ -1261,7 +1261,8 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment WifiEntryConnectCallback callback = new WifiEntryConnectCallback(wifiEntry, editIfNoConfig, fullScreenEdit); - if (Flags.wepUsage() && wifiEntry.getSecurityTypes().contains(WifiEntry.SECURITY_WEP)) { + if (Flags.androidVWifiApi() && wifiEntry.getSecurityTypes() + .contains(WifiEntry.SECURITY_WEP)) { WepNetworkDialogActivity.checkWepAllowed( getContext(), getViewLifecycleOwner(), wifiEntry.getSsid(), () -> { wifiEntry.connect(callback); diff --git a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt index fca75a93710..6263bfdafbf 100644 --- a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt +++ b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt @@ -48,7 +48,7 @@ class WepNetworksPreferenceController(context: Context, preferenceKey: String) : preference = screen.findPreference(preferenceKey)!! } - override fun getAvailabilityStatus() = if (Flags.wepUsage()) AVAILABLE + override fun getAvailabilityStatus() = if (Flags.androidVWifiApi()) AVAILABLE else UNSUPPORTED_ON_DEVICE @Composable diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java index 1627056e5a0..5d45cb235b8 100644 --- a/src/com/android/settings/wifi/WifiConfigController.java +++ b/src/com/android/settings/wifi/WifiConfigController.java @@ -276,7 +276,7 @@ public class WifiConfigController implements TextWatcher, mDoNotProvideEapUserCertString = mContext.getString(R.string.wifi_do_not_provide_eap_user_cert); - if (Flags.wepUsage() && mAccessPointSecurity == WifiEntry.SECURITY_WEP) { + if (Flags.androidVWifiApi() && mAccessPointSecurity == WifiEntry.SECURITY_WEP) { LinearLayout wepWarningLayout = (LinearLayout) mView.findViewById(R.id.wep_warning_layout); wepWarningLayout.setVisibility(View.VISIBLE); diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java index 6a6244b6f09..7c9b1d1b471 100644 --- a/src/com/android/settings/wifi/WifiConfigController2.java +++ b/src/com/android/settings/wifi/WifiConfigController2.java @@ -289,7 +289,7 @@ public class WifiConfigController2 implements TextWatcher, mContext.getString(R.string.wifi_do_not_provide_eap_user_cert); mInstallCertsString = mContext.getString(R.string.wifi_install_credentials); - if (Flags.wepUsage() && mWifiEntrySecurity == WifiEntry.SECURITY_WEP) { + if (Flags.androidVWifiApi() && mWifiEntrySecurity == WifiEntry.SECURITY_WEP) { LinearLayout wepWarningLayout = (LinearLayout) mView.findViewById(R.id.wep_warning_layout); wepWarningLayout.setVisibility(View.VISIBLE); From 86db475673ab0abe1920d9685d3026e6cbb019f8 Mon Sep 17 00:00:00 2001 From: SongFerng Wang Date: Wed, 21 Feb 2024 03:44:26 +0000 Subject: [PATCH 09/14] To extend the character limit for Automatic data switching Bug: 326154869 Change-Id: I115de46ce19789c812d455b9e79449a8fedda890 Test: NA --- 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 746dbf51f36..0f94cc0182a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -11576,7 +11576,7 @@ Calls Texts - + Automatic data switching Use data from either SIM depending on coverage and availability From ca1917b6886b4c07a17ea1cf23d59df754aa6bcf Mon Sep 17 00:00:00 2001 From: Angela Wang Date: Wed, 21 Feb 2024 06:09:05 +0000 Subject: [PATCH 10/14] Update footer string for hearing device related pages Users sometimes will confuse why their device is not shown on hearing device related pages. Describe more that only ASHA and LE Audio hearing aids will be listed in these pages to let them know the reason. Bug: 326167961 Test: build pass, only simple string update Change-Id: I624c7a23e98c04f0a659742f63465918a45bb888 --- 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 caf3e7f7ad6..ba426e0188f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5146,7 +5146,7 @@ About hearing devices - Make sure your hearing device is turned on and ready to pair + Make sure your hearing device is turned on and ready to pair. Only ASHA and LE Audio hearing devices show on this page. Pair hearing device From 83f0adc5c054a702daedf1378518d7be5378421c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20G=C3=B6llner?= Date: Wed, 21 Feb 2024 15:14:30 +0000 Subject: [PATCH 11/14] Settings - Move Activity embedding split min width config to resources On some devices the values need to be configured to be different than the default. Bug: 326215764 Test: Manual Change-Id: I56a3a6608a4ac4eaf7d7dede4f6cb4278638f29c --- res/values/config.xml | 7 +++++++ .../ActivityEmbeddingRulesController.java | 10 ++++++---- .../activityembedding/ActivityEmbeddingUtils.java | 13 ++++--------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/res/values/config.xml b/res/values/config.xml index 9d7167101d7..afd6fdd16d2 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -679,6 +679,13 @@ 0.3636 + + 600 + + + 720 + diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java index 50134ba7eb6..4e39070febd 100644 --- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java +++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java @@ -113,8 +113,9 @@ public class ActivityEmbeddingRulesController { .setFinishPrimaryWithSecondary(finishPrimaryWithSecondary) .setFinishSecondaryWithPrimary(finishSecondaryWithPrimary) .setClearTop(clearTop) - .setMinWidthDp(ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthDp()) - .setMinSmallestWidthDp(ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthDp()) + .setMinWidthDp(ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthDp(context)) + .setMinSmallestWidthDp( + ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthDp(context)) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW) .setDefaultSplitAttributes(attributes) .build(); @@ -234,8 +235,9 @@ public class ActivityEmbeddingRulesController { .build(); final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule.Builder( activityFilters, intent) - .setMinWidthDp(ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthDp()) - .setMinSmallestWidthDp(ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthDp()) + .setMinWidthDp(ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthDp(mContext)) + .setMinSmallestWidthDp( + ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthDp(mContext)) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW) .setSticky(false) .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ADJACENT) diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java index 74a967332da..b91e0e5eacd 100644 --- a/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java +++ b/src/com/android/settings/activityembedding/ActivityEmbeddingUtils.java @@ -33,11 +33,6 @@ import com.google.android.setupcompat.util.WizardManagerHelper; /** An util class collecting all common methods for the embedding activity features. */ public class ActivityEmbeddingUtils { - // The smallest value of current width of the window when the split should be used. - private static final int MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP = 720; - // The smallest value of the smallest-width (sw) of the window in any rotation when - // the split should be used. - private static final int MIN_SMALLEST_SCREEN_SPLIT_WIDTH_DP = 600; // The minimum width of the activity to show the regular homepage layout. private static final float MIN_REGULAR_HOMEPAGE_LAYOUT_WIDTH_DP = 380f; @@ -58,16 +53,16 @@ public class ActivityEmbeddingUtils { private static final String TAG = "ActivityEmbeddingUtils"; /** Get the smallest width dp of the window when the split should be used. */ - public static int getMinCurrentScreenSplitWidthDp() { - return MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP; + public static int getMinCurrentScreenSplitWidthDp(Context context) { + return context.getResources().getInteger(R.integer.config_activity_embed_split_min_cur_dp); } /** * Get the smallest dp value of the smallest-width (sw) of the window in any rotation when * the split should be used. */ - public static int getMinSmallestScreenSplitWidthDp() { - return MIN_SMALLEST_SCREEN_SPLIT_WIDTH_DP; + public static int getMinSmallestScreenSplitWidthDp(Context context) { + return context.getResources().getInteger(R.integer.config_activity_embed_split_min_sw_dp); } /** From a20232d7a1fd11ed60b641f7cda01f6294cbf0c7 Mon Sep 17 00:00:00 2001 From: pajacechen Date: Thu, 22 Feb 2024 11:27:03 +0800 Subject: [PATCH 12/14] [Shadow] Fix failed test case for battery defender - Fix failed test case in BatteryInfoTest and BatteryDefenderTipTest Test: http://ab/I73800010248910938 Bug: 315748218 Change-Id: Iaa01a1d777236b7772fa762c94ab094246c38f21 --- .../src/com/android/settings/fuelgauge/BatteryInfoTest.java | 2 +- .../fuelgauge/batterytip/tips/BatteryDefenderTipTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java index 0339f574b0b..e99c4e0a778 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java @@ -64,7 +64,7 @@ public class BatteryInfoTest { private static final String STATUS_CHARGING_NO_TIME = "50% - charging"; private static final String STATUS_CHARGING_TIME = "50% - 0 min left until full"; private static final String STATUS_NOT_CHARGING = "Not charging"; - private static final String STATUS_CHARGING_FUTURE_BYPASS = "50% - Charging optimized"; + private static final String STATUS_CHARGING_FUTURE_BYPASS = "50% - Charging"; private static final String STATUS_CHARGING_PAUSED = "50% - Charging optimized"; private static final long REMAINING_TIME_NULL = -1; private static final long REMAINING_TIME = 2; diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java index 4efd8509fab..a605ee32dfa 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java @@ -82,7 +82,7 @@ public class BatteryDefenderTipTest { @Test public void getIcon_showIcon() { assertThat(mBatteryDefenderTip.getIconId()) - .isEqualTo(R.drawable.ic_battery_status_good_theme); + .isEqualTo(R.drawable.ic_battery_defender_tip_shield); } @Test From 0aefad88590946db69278075e9564e802848fca1 Mon Sep 17 00:00:00 2001 From: Joshua McCloskey Date: Thu, 22 Feb 2024 03:47:30 +0000 Subject: [PATCH 13/14] Fixed fingerprint enroll test Test: atest FingerprintEnrollIntroductionTest Fixes: 326325883 Change-Id: Ib0d0ebff90d401861fa0663403fc2366cd308f22 --- .../fingerprint/FingerprintEnrollIntroductionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java index 89e70d2990f..1aedce53980 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroductionTest.java @@ -300,7 +300,7 @@ public class FingerprintEnrollIntroductionTest { final Intent enrollingIntent = mFingerprintEnrollIntroduction.getEnrollingIntent(); assertThat(enrollingIntent.getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)) - .isEqualTo(FingerprintEnrollOptions.ENROLL_REASON_SUW); + .isEqualTo(FingerprintEnrollOptions.ENROLL_REASON_SETTINGS); } private Intent newTokenOnlyIntent() { From baecaee2a56a679590e65bc2db181b225dd45fc5 Mon Sep 17 00:00:00 2001 From: Olivier Nshimiye Date: Thu, 8 Feb 2024 17:41:27 +0000 Subject: [PATCH 14/14] Add a toggle for enabling/disabling sensitive lockscreen notifications for Private Space This adds a toggle in the Private Space Settings page to control the lockscreen sensitive notifications for the private profile. Sensitive notifications for private profile will still be disabled if sensitive notifications are disabled for the main user. Demo link:https://drive.google.com/file/d/1--LNXziTRTMvfdsJZAh90SiNiPxWNcI8/view?usp=sharing Bug: 317067050 Test: atest SettingsUnitTests:HidePrivateSpaceSensitiveNotificationsControllerTest Change-Id: Ie7f18c1a940e5ffd64112a01c48ac9dee58cb1ab --- res/values/strings.xml | 6 + res/xml/private_space_settings.xml | 11 ++ ...SpaceSensitiveNotificationsController.java | 105 ++++++++++++ .../privatespace/PrivateSpaceMaintainer.java | 24 ++- ...eSensitiveNotificationsControllerTest.java | 162 ++++++++++++++++++ .../PrivateSpaceMaintainerTest.java | 26 +++ 6 files changed, 329 insertions(+), 5 deletions(-) create mode 100644 src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java create mode 100644 tests/unit/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 012ddc3498b..2ae7ed5d96a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1372,6 +1372,12 @@ Set a password for your private space Set a pattern for your private space + + Apps and notifications + + Sensitive notifications on lock screen + + Show sensitive content when private space is unlocked You can add up to %d fingerprints diff --git a/res/xml/private_space_settings.xml b/res/xml/private_space_settings.xml index 0ed9c93fb84..e718ca81a6b 100644 --- a/res/xml/private_space_settings.xml +++ b/res/xml/private_space_settings.xml @@ -59,6 +59,17 @@ + + + + + + diff --git a/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java b/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java new file mode 100644 index 00000000000..1a89d37b8b6 --- /dev/null +++ b/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsController.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.privatespace; + +import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; + +import android.content.Context; +import android.os.UserHandle; +import android.provider.Settings; + +import androidx.annotation.NonNull; + +import com.android.settings.core.TogglePreferenceController; + +import java.util.Objects; + +/** + * A controller object for sensitive notifications in Private Space settings page. + */ +public class HidePrivateSpaceSensitiveNotificationsController extends TogglePreferenceController { + private final PrivateSpaceMaintainer mPrivateSpaceMaintainer; + private final UserHandle mPrivateProfileId; + public static final int ENABLED = 1; + public static final int DISABLED = 0; + private static final int DEVICE_SENSITIVE_NOTIFICATIONS_DEFAULT = ENABLED; + private static final int DEVICE_LOCK_SCREEN_NOTIFICATIONS_DEFAULT = ENABLED; + private static final int PRIVATE_SPACE_SENSITIVE_NOTIFICATIONS_DEFAULT = DISABLED; + + public HidePrivateSpaceSensitiveNotificationsController(@NonNull Context context, + @NonNull String preferenceKey) { + super(context, preferenceKey); + mPrivateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(context); + mPrivateProfileId = Objects.requireNonNull( + mPrivateSpaceMaintainer.getPrivateProfileHandle()); + } + + @Override + public int getAvailabilityStatus() { + if (!android.os.Flags.allowPrivateProfile() + || !android.multiuser.Flags.enablePsSensitiveNotificationsToggle() + || !mPrivateSpaceMaintainer.doesPrivateSpaceExist()) { + return UNSUPPORTED_ON_DEVICE; + } + if (!getLockscreenNotificationsEnabled(mContext) + || !getLockscreenSensitiveNotificationsEnabledOnDevice(mContext)) { + return DISABLED_DEPENDENT_SETTING; + } + return AVAILABLE; + } + + @Override + public boolean isChecked() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + PRIVATE_SPACE_SENSITIVE_NOTIFICATIONS_DEFAULT, mPrivateProfileId.getIdentifier()) + != DISABLED; + } + + @Override + public boolean setChecked(boolean isChecked) { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + isChecked ? ENABLED : DISABLED, mPrivateProfileId.getIdentifier()); + return true; + } + + @Override + public int getSliceHighlightMenuRes() { + return 0; + } + + /** + * If notifications are disabled on the device, the toggle for private space sensitive + * notifications should be unavailable. + */ + private static boolean getLockscreenNotificationsEnabled(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, + DEVICE_LOCK_SCREEN_NOTIFICATIONS_DEFAULT) != DISABLED; + } + + /** + * If sensitive notifications are hidden on the device, they should be hidden for private space + * also. + */ + private static boolean getLockscreenSensitiveNotificationsEnabledOnDevice(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + DEVICE_SENSITIVE_NOTIFICATIONS_DEFAULT) != DISABLED; + } +} diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java index a2831478a8b..2d38ae2007f 100644 --- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java +++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java @@ -45,6 +45,7 @@ import com.android.internal.annotations.GuardedBy; import java.util.List; // TODO(b/293569406): Update the javadoc when we have the setup flow in place to create PS + /** A class to help with the creation / deletion of Private Space */ public class PrivateSpaceMaintainer { private static final String TAG = "PrivateSpaceMaintainer"; @@ -65,9 +66,9 @@ public class PrivateSpaceMaintainer { public static final int PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL = PRIVATE_SPACE_AUTO_LOCK_NEVER; public enum ErrorDeletingPrivateSpace { - DELETE_PS_ERROR_NONE, - DELETE_PS_ERROR_NO_PRIVATE_SPACE, - DELETE_PS_ERROR_INTERNAL + DELETE_PS_ERROR_NONE, + DELETE_PS_ERROR_NO_PRIVATE_SPACE, + DELETE_PS_ERROR_INTERNAL } /** @@ -90,7 +91,7 @@ public class PrivateSpaceMaintainer { if (mUserHandle == null) { try { mUserHandle = mUserManager.createProfile( - userName, USER_TYPE_PROFILE_PRIVATE, new ArraySet<>()); + userName, USER_TYPE_PROFILE_PRIVATE, new ArraySet<>()); } catch (Exception e) { Log.e(TAG, "Error creating private space", e); return false; @@ -117,7 +118,8 @@ public class PrivateSpaceMaintainer { return true; } - /** Returns the {@link ErrorDeletingPrivateSpace} enum representing the result of operation. + /** + * Returns the {@link ErrorDeletingPrivateSpace} enum representing the result of operation. * *

This method should be used ONLY by the delete-PS controller in the PS Settings page. */ @@ -212,6 +214,7 @@ public class PrivateSpaceMaintainer { // TODO(b/307281644): Remove this method once new auth change is merged + /** * Returns true if private space exists and a separate private profile lock is set * otherwise false when the private space does not exit or exists but does not have a @@ -290,9 +293,20 @@ public class PrivateSpaceMaintainer { return false; } + @GuardedBy("this") private void resetPrivateSpaceSettings() { setHidePrivateSpaceEntryPointSetting(HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL); setPrivateSpaceAutoLockSetting(PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL); + setPrivateSpaceSensitiveNotificationsDefaultValue(); + } + + /** Sets private space sensitive notifications hidden on lockscreen by default */ + @GuardedBy("this") + private void setPrivateSpaceSensitiveNotificationsDefaultValue() { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + HidePrivateSpaceSensitiveNotificationsController.DISABLED, + mUserHandle.getIdentifier()); } /** diff --git a/tests/unit/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsControllerTest.java b/tests/unit/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsControllerTest.java new file mode 100644 index 00000000000..1430dfd1736 --- /dev/null +++ b/tests/unit/src/com/android/settings/privatespace/HidePrivateSpaceSensitiveNotificationsControllerTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.privatespace; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING; +import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assume.assumeTrue; +import static org.mockito.Mockito.spy; + +import android.content.ContentResolver; +import android.content.Context; +import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Settings; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +/** + * Tests for HidePrivateSpaceSensitiveNotificationsController. + * Run as {@code atest SettingsUnitTests:HidePrivateSpaceSensitiveNotificationsControllerTest} + */ +@RunWith(AndroidJUnit4.class) +public class HidePrivateSpaceSensitiveNotificationsControllerTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private Context mContext; + private HidePrivateSpaceSensitiveNotificationsController + mHidePrivateSpaceSensitiveNotificationsController; + @Mock + private ContentResolver mContentResolver; + private int mOriginalDeviceSensitiveNotifValue; + private int mOriginalDeviceNotifValue; + private int mOriginalPsSensitiveNotifValue; + private int mPrivateProfileId; + + @Before + public void setUp() { + mContext = spy(ApplicationProvider.getApplicationContext()); + mContentResolver = mContext.getContentResolver(); + assumeTrue(PrivateSpaceMaintainer.getInstance(mContext).doesPrivateSpaceExist()); + + mSetFlagsRule.enableFlags( + android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE); + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE); + + mPrivateProfileId = PrivateSpaceMaintainer.getInstance( + mContext).getPrivateProfileHandle().getIdentifier(); + + mOriginalDeviceSensitiveNotifValue = Settings.Secure.getInt(mContentResolver, + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); + mOriginalDeviceNotifValue = Settings.Secure.getInt(mContentResolver, + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mOriginalPsSensitiveNotifValue = Settings.Secure.getIntForUser(mContentResolver, + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mPrivateProfileId); + + final String preferenceKey = "private_space_sensitive_notifications"; + mHidePrivateSpaceSensitiveNotificationsController = + new HidePrivateSpaceSensitiveNotificationsController(mContext, preferenceKey); + } + + @After + public void tearDown() { + Settings.Secure.putInt(mContentResolver, + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + mOriginalDeviceSensitiveNotifValue + ); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, mOriginalDeviceNotifValue); + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + mOriginalPsSensitiveNotifValue, mPrivateProfileId); + } + + /** + * Tests that the controller is unavailable if lockscreen sensitive notifications are disabled + * on the device. + */ + @Test + public void getAvailabilityStatus_lockScreenPrivateNotificationsOff() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0); + assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus()) + .isEqualTo(DISABLED_DEPENDENT_SETTING); + } + + /** + * Tests that the controller is unavailable if lockscreen notifications are disabled on the + * device. + */ + @Test + public void getAvailabilityStatus_lockScreenNotificationsOff() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); + assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus()) + .isEqualTo(DISABLED_DEPENDENT_SETTING); + } + + /** + * Tests that the controller is available if lockscreen notifications and lockscreen private + * notifications are enabled on the device. + */ + @Test + public void getAvailabilityStatus_returnAvailable() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); + assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus()) + .isEqualTo(AVAILABLE); + } + + + /** + * Tests that toggle is not available if the flag for this feature and MVP flag are disabled. + */ + @Test + public void getAvailabilityStatus_flagDisabled() { + mSetFlagsRule.disableFlags( + android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE); + mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); + assertThat(mHidePrivateSpaceSensitiveNotificationsController.getAvailabilityStatus()) + .isEqualTo(UNSUPPORTED_ON_DEVICE); + } + + @Test + public void testSetChecked() { + assertThat(mHidePrivateSpaceSensitiveNotificationsController.setChecked(true)).isTrue(); + assertThat(mHidePrivateSpaceSensitiveNotificationsController.isChecked()).isEqualTo(true); + assertThat(mHidePrivateSpaceSensitiveNotificationsController.setChecked(false)).isTrue(); + assertThat(mHidePrivateSpaceSensitiveNotificationsController.isChecked()).isEqualTo(false); + } +} diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java index 4bb5d43b59d..8510b11c9c6 100644 --- a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java +++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java @@ -16,6 +16,7 @@ package com.android.settings.privatespace; +import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK; import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL; @@ -139,6 +140,24 @@ public class PrivateSpaceMaintainerTest { .isEqualTo(HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL); } + /** + * Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} sets the PS sensitive + * notifications to hidden by default. + */ + @Test + public void createPrivateSpace_psDoesNotExist_setsDefaultPsSensitiveNotificationsValue() { + mSetFlagsRule.enableFlags( + Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_PS_SENSITIVE_NOTIFICATIONS_TOGGLE); + PrivateSpaceMaintainer privateSpaceMaintainer = + PrivateSpaceMaintainer.getInstance(mContext); + privateSpaceMaintainer.deletePrivateSpace(); + privateSpaceMaintainer.createPrivateSpace(); + assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isTrue(); + assertThat(getPsSensitiveNotificationsValue(privateSpaceMaintainer)) + .isEqualTo(HidePrivateSpaceSensitiveNotificationsController.DISABLED); + } + /** * Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} when PS exist does not reset * hide PS Settings. @@ -287,4 +306,11 @@ public class PrivateSpaceMaintainerTest { 0, privateSpaceMaintainer.getPrivateProfileHandle().getIdentifier()); } + + private int getPsSensitiveNotificationsValue(PrivateSpaceMaintainer privateSpaceMaintainer) { + return Settings.Secure.getIntForUser(mContentResolver, + LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + HidePrivateSpaceSensitiveNotificationsController.ENABLED, + privateSpaceMaintainer.getPrivateProfileHandle().getIdentifier()); + } }