By default, this method returns {@link SettingsEnums#PAGE_UNKNOWN}. This indicates that
+ * the feedback category is unknown, and the absence of a feedback menu.
+ *
+ * @return The feedback category, which is {@link SettingsEnums#PAGE_UNKNOWN} by default.
+ */
+ protected int getFeedbackCategory() {
+ return SettingsEnums.PAGE_UNKNOWN;
+ }
}
diff --git a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
index 10813a7e262..eb0c93b755f 100644
--- a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
@@ -79,6 +79,12 @@ public class ToggleScreenReaderPreferenceFragmentForSetupWizard
return SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SCREEN_READER;
}
+ @Override
+ public int getFeedbackCategory() {
+ // The feedback options should not be displayed on the setup wizard page.
+ return SettingsEnums.PAGE_UNKNOWN;
+ }
+
@Override
public void onStop() {
// Log the final choice in value if it's different from the previous value.
diff --git a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
index 10796b5d218..14dc0bc1caf 100644
--- a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
@@ -79,6 +79,12 @@ public class ToggleSelectToSpeakPreferenceFragmentForSetupWizard
return SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SELECT_TO_SPEAK;
}
+ @Override
+ public int getFeedbackCategory() {
+ // The feedback options should not be displayed on the setup wizard page.
+ return SettingsEnums.PAGE_UNKNOWN;
+ }
+
@Override
public void onStop() {
// Log the final choice in value if it's different from the previous value.
diff --git a/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java b/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java
index 826583df866..ad1f823b48c 100644
--- a/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java
+++ b/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java
@@ -95,8 +95,7 @@ public class ExternalSourcesDetails extends AppInfoWithHeader
userHandle)) {
if (RestrictedLockUtilsInternal.isPolicyEnforcedByAdvancedProtection(context,
DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userHandle.getIdentifier())) {
- return context.getString(com.android.settingslib.widget.restricted
- .R.string.disabled_by_advanced_protection);
+ return context.getString(com.android.settingslib.R.string.disabled);
} else {
return context.getString(
com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
index ef1970995df..83f23bdcf7a 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
@@ -107,7 +107,10 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
// intent will include this extra containing a bundle of the form:
// "modality" -> consented (boolean).
public static final String EXTRA_PARENTAL_CONSENT_STATUS = "consent_status";
-
+ // Whether the face enrollment should be launched first when there are multiple biometrics
+ // supported.
+ public static final String EXTRA_LAUNCH_FACE_ENROLL_FIRST =
+ "launch_face_enroll_first";
private static final String SAVED_STATE_CONFIRMING_CREDENTIALS = "confirming_credentials";
private static final String SAVED_STATE_IS_SINGLE_ENROLLING =
"is_single_enrolling";
@@ -130,6 +133,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
private boolean mIsFingerprintEnrollable = false;
private boolean mParentalOptionsRequired = false;
private boolean mSkipReturnToParent = false;
+ private boolean mLaunchFaceEnrollFirst = false;
private Bundle mParentalOptions;
@Nullable private Long mGkPwHandle;
@Nullable private ParentalConsentHelper mParentalConsentHelper;
@@ -214,6 +218,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
mParentalOptionsRequired = intent.getBooleanExtra(EXTRA_REQUIRE_PARENTAL_CONSENT, false);
mSkipReturnToParent = intent.getBooleanExtra(EXTRA_SKIP_RETURN_TO_PARENT, false);
+ mLaunchFaceEnrollFirst = intent.getBooleanExtra(EXTRA_LAUNCH_FACE_ENROLL_FIRST, false);
// determine what can be enrolled
final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
@@ -221,6 +226,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
Log.d(TAG, "parentalOptionsRequired: " + mParentalOptionsRequired
+ ", skipReturnToParent: " + mSkipReturnToParent
+ + ", launchFaceEnrollFirst: " + mLaunchFaceEnrollFirst
+ ", isSetupWizard: " + isSetupWizard
+ ", isMultiSensor: " + isMultiSensor);
@@ -356,7 +362,8 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
} else if (canUseFace || canUseFingerprint) {
if (mGkPwHandle == null) {
setOrConfirmCredentialsNow();
- } else if (canUseFingerprint && mIsFingerprintEnrollable) {
+ } else if (canUseFingerprint && mIsFingerprintEnrollable
+ && !(canUseFace && mIsFaceEnrollable && mLaunchFaceEnrollFirst)) {
launchFingerprintOnlyEnroll();
} else if (canUseFace && mIsFaceEnrollable) {
launchFaceOnlyEnroll();
@@ -510,7 +517,8 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
int requestCode, int resultCode, Intent data) {
Log.d(TAG, "handleOnActivityResultWhileEnrolling, request = " + requestCode + ""
- + ", resultCode = " + resultCode);
+ + ", resultCode = " + resultCode + ", launchFaceEnrollFirst="
+ + mLaunchFaceEnrollFirst);
switch (requestCode) {
case REQUEST_HANDOFF_PARENT:
setResult(RESULT_OK, newResultIntent());
@@ -526,7 +534,8 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
// SetupFingerprintEnrollIntroduction/FingerprintEnrollmentActivity
TransitionHelper.applyForwardTransition(this, TRANSITION_FADE_THROUGH);
updateGatekeeperPasswordHandle(data);
- if (mIsFingerprintEnrollable) {
+ if (mIsFingerprintEnrollable
+ && !(mIsFaceEnrollable && mLaunchFaceEnrollFirst)) {
launchFingerprintOnlyEnroll();
} else {
launchFaceOnlyEnroll();
@@ -548,7 +557,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
}
if ((resultCode == BiometricEnrollBase.RESULT_SKIP
|| resultCode == BiometricEnrollBase.RESULT_FINISHED)
- && mIsFaceEnrollable) {
+ && mIsFaceEnrollable && !mLaunchFaceEnrollFirst) {
// Apply forward animation during the transition from
// SetupFingerprintEnroll*/FingerprintEnrollmentActivity to
// SetupFaceEnrollIntroduction
@@ -556,6 +565,9 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
mIsPreviousEnrollmentCanceled =
resultCode != BiometricEnrollBase.RESULT_FINISHED;
launchFaceOnlyEnroll();
+ } else if (resultCode == Activity.RESULT_CANCELED && mIsFaceEnrollable
+ && mLaunchFaceEnrollFirst) {
+ launchFaceOnlyEnroll();
} else {
notifySafetyIssueActionLaunchedIfNeeded(resultCode);
finishOrLaunchHandToParent(resultCode);
@@ -563,7 +575,14 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
break;
case REQUEST_SINGLE_ENROLL_FACE:
mIsSingleEnrolling = false;
- if (resultCode == Activity.RESULT_CANCELED && mIsFingerprintEnrollable) {
+ if ((resultCode == BiometricEnrollBase.RESULT_SKIP
+ || resultCode == BiometricEnrollBase.RESULT_FINISHED)
+ && mIsFingerprintEnrollable && mLaunchFaceEnrollFirst) {
+ mIsPreviousEnrollmentCanceled =
+ resultCode != BiometricEnrollBase.RESULT_FINISHED;
+ launchFingerprintOnlyEnroll();
+ } else if (resultCode == Activity.RESULT_CANCELED && mIsFingerprintEnrollable
+ && !mLaunchFaceEnrollFirst) {
mIsPreviousEnrollmentCanceled = true;
launchFingerprintOnlyEnroll();
} else {
diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java
index db6abc3c979..21b0fa03f06 100644
--- a/src/com/android/settings/biometrics/BiometricUtils.java
+++ b/src/com/android/settings/biometrics/BiometricUtils.java
@@ -43,6 +43,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
+import com.android.settings.biometrics.face.FaceEnroll;
import com.android.settings.biometrics.fingerprint.FingerprintEnroll;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollFindSensor;
import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollFindSensor;
@@ -282,9 +283,7 @@ public class BiometricUtils {
*/
public static Intent getFaceIntroIntent(@NonNull Context context,
@NonNull Intent activityIntent) {
- final Intent intent = new Intent(context,
- FeatureFactory.getFeatureFactory().getFaceFeatureProvider()
- .getEnrollActivityClassProvider().getNext());
+ final Intent intent = new Intent(context, FaceEnroll.class);
WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
return intent;
}
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java
index 8cc7d6af331..42029ff89f1 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java
@@ -28,6 +28,7 @@ import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settingslib.utils.ThreadUtils;
@@ -76,17 +77,22 @@ public class ActiveUnlockContentListener {
mContentKey = contentKey;
String authority = new ActiveUnlockStatusUtils(mContext).getAuthority();
if (authority != null) {
- mUri = new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority(authority)
- .appendPath(CONTENT_PROVIDER_PATH)
- .build();
+ mUri = getUri(authority);
} else {
mUri = null;
}
}
+ /** Returns Active Unlock Uri. */
+ public static @NonNull Uri getUri(@NonNull String authority) {
+ return new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority)
+ .appendPath(CONTENT_PROVIDER_PATH)
+ .build();
+ }
+
/** Returns true if start listening for updates from the ContentProvider, false otherwise. */
public synchronized boolean subscribe() {
if (mSubscribed || mUri == null) {
@@ -123,25 +129,40 @@ public class ActiveUnlockContentListener {
Log.e(mLogTag, "Uri null when trying to fetch content");
return;
}
- ContentResolver contentResolver = mContext.getContentResolver();
- ContentProviderClient client = contentResolver.acquireContentProviderClient(mUri);
- Bundle bundle;
- try {
- bundle = client.call(mMethodName, null /* arg */, null /* extras */);
- } catch (RemoteException e) {
- Log.e(mLogTag, "Failed to call contentProvider", e);
- return;
- } finally {
- client.close();
- }
- if (bundle == null) {
- Log.e(mLogTag, "Null bundle returned from contentProvider");
- return;
- }
- String newValue = bundle.getString(mContentKey);
+
+ @Nullable String newValue = getContentFromUri(
+ mContext, mUri, mLogTag, mMethodName, mContentKey);
if (!TextUtils.equals(mContent, newValue)) {
mContent = newValue;
mContentChangedListener.onContentChanged(mContent);
}
}
+
+ /** Get the content from Uri. */
+ public static @Nullable String getContentFromUri(
+ @NonNull Context context,
+ @NonNull Uri uri,
+ @NonNull String logTag,
+ @NonNull String methodName,
+ @NonNull String contentKey) {
+ ContentResolver contentResolver = context.getContentResolver();
+ ContentProviderClient client = contentResolver.acquireContentProviderClient(uri);
+
+ @Nullable Bundle bundle = null;
+
+ try {
+ bundle = client.call(methodName, /* arg= */ null, /* extras = */ null);
+ } catch (RemoteException e) {
+ Log.e(logTag, "Failed to call contentProvider", e);
+ } finally {
+ client.close();
+ }
+
+ if (bundle == null) {
+ Log.e(logTag, "Null bundle returned from contentProvider");
+ return null;
+ }
+
+ return bundle.getString(contentKey);
+ }
}
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockDeviceNameListener.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockDeviceNameListener.java
index 1badb0f26ec..9e8176294e5 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockDeviceNameListener.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockDeviceNameListener.java
@@ -21,8 +21,8 @@ import android.content.Context;
/** Listens to device name updates from the content provider and fetches the latest value. */
public class ActiveUnlockDeviceNameListener {
private static final String TAG = "ActiveUnlockDeviceNameListener";
- private static final String METHOD_NAME = "getDeviceName";
- private static final String DEVICE_NAME_KEY = "com.android.settings.active_unlock.device_name";
+ static final String METHOD_NAME = "getDeviceName";
+ static final String DEVICE_NAME_KEY = "com.android.settings.active_unlock.device_name";
private final ActiveUnlockContentListener mActiveUnlockContentListener;
public ActiveUnlockDeviceNameListener(
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
index 4ff2e900ae2..66485d37601 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
@@ -155,10 +155,17 @@ public class ActiveUnlockStatusUtils {
return BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
}
+ /**
+ * Returns the title of active unlock only.
+ */
+ public @NonNull String getTitleForActiveUnlockOnly() {
+ return mContext.getString(R.string.security_settings_activeunlock);
+ }
+
/**
* Returns the title of the combined biometric settings entity when active unlock is enabled.
*/
- public String getTitleForActiveUnlock() {
+ public @NonNull String getTitleForActiveUnlock() {
final boolean faceAllowed = Utils.hasFaceHardware(mContext);
final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
return mContext.getString(getTitleRes(faceAllowed, fingerprintAllowed));
@@ -264,6 +271,30 @@ public class ActiveUnlockStatusUtils {
return mContext.getString(getUseBiometricTitleRes(faceAllowed, fingerprintAllowed));
}
+ /**
+ * Returns the summary from content provider.
+ */
+ @Nullable
+ public static String getSummaryFromContentProvider(
+ @NonNull Context context, @NonNull String authority, @NonNull String logTag) {
+ return ActiveUnlockContentListener.getContentFromUri(
+ context, ActiveUnlockContentListener.getUri(authority), logTag,
+ ActiveUnlockSummaryListener.METHOD_NAME,
+ ActiveUnlockSummaryListener.SUMMARY_KEY);
+ }
+
+ /**
+ * Returns the device name from content provider.
+ */
+ @Nullable
+ public static String getDeviceNameFromContentProvider(
+ @NonNull Context context, @NonNull String authority, @NonNull String logTag) {
+ return ActiveUnlockContentListener.getContentFromUri(
+ context, ActiveUnlockContentListener.getUri(authority), logTag,
+ ActiveUnlockDeviceNameListener.METHOD_NAME,
+ ActiveUnlockDeviceNameListener.DEVICE_NAME_KEY);
+ }
+
@StringRes
private static int getUseBiometricTitleRes(
boolean isFaceAllowed, boolean isFingerprintAllowed) {
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockSummaryListener.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockSummaryListener.java
index bcffe6297d1..38e137bd379 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockSummaryListener.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockSummaryListener.java
@@ -21,8 +21,8 @@ import android.content.Context;
/** Listens to summary updates from the content provider and fetches the latest value. */
public class ActiveUnlockSummaryListener {
private static final String TAG = "ActiveUnlockSummaryListener";
- private static final String METHOD_NAME = "getSummary";
- private static final String SUMMARY_KEY = "com.android.settings.summary";
+ static final String METHOD_NAME = "getSummary";
+ static final String SUMMARY_KEY = "com.android.settings.summary";
private final ActiveUnlockContentListener mContentListener;
public ActiveUnlockSummaryListener(
diff --git a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
index 1d8b7a10a3c..ed4b713df20 100644
--- a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
+++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
@@ -66,7 +66,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
@VisibleForTesting
static final int CONFIRM_REQUEST = 2001;
private static final int CHOOSE_LOCK_REQUEST = 2002;
- protected static final int ACTIVE_UNLOCK_REQUEST = 2003;
+ public static final int ACTIVE_UNLOCK_REQUEST = 2003;
@VisibleForTesting
static final int BIOMETRIC_AUTH_REQUEST = 2004;
diff --git a/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java b/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
index 700b601a608..a1459134b85 100644
--- a/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
@@ -25,11 +25,11 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.SliderPreferenceController;
-import com.android.settings.widget.SeekBarPreference;
+import com.android.settingslib.widget.SliderPreference;
public class NightDisplayIntensityPreferenceController extends SliderPreferenceController {
- private ColorDisplayManager mColorDisplayManager;
+ private final ColorDisplayManager mColorDisplayManager;
public NightDisplayIntensityPreferenceController(Context context, String key) {
super(context, key);
@@ -64,11 +64,11 @@ public class NightDisplayIntensityPreferenceController extends SliderPreferenceC
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
- final SeekBarPreference preference = screen.findPreference(getPreferenceKey());
- preference.setContinuousUpdates(true);
+ SliderPreference preference = screen.findPreference(getPreferenceKey());
+ preference.setUpdatesContinuously(true);
preference.setMax(getMax());
preference.setMin(getMin());
- preference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
+ // TODO(b/394828723) add haptic feedback
}
@Override
diff --git a/src/com/android/settings/inputmethod/MousePointerSpeedPreferenceController.java b/src/com/android/settings/inputmethod/MousePointerSpeedPreferenceController.java
new file mode 100644
index 00000000000..bb91b3c73c6
--- /dev/null
+++ b/src/com/android/settings/inputmethod/MousePointerSpeedPreferenceController.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2025 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.inputmethod;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.hardware.input.InputSettings;
+
+import androidx.annotation.NonNull;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.SliderPreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.SeekBarPreference;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+public class MousePointerSpeedPreferenceController extends SliderPreferenceController {
+
+ private final MetricsFeatureProvider mMetricsFeatureProvider;
+
+ public MousePointerSpeedPreferenceController(@NonNull Context context, @NonNull String key) {
+ super(context, key);
+ mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ SeekBarPreference preference = screen.findPreference(getPreferenceKey());
+ preference.setMax(getMax());
+ preference.setMin(getMin());
+ preference.setProgress(getSliderPosition());
+ updateState(preference);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public boolean setSliderPosition(int position) {
+ if (position < getMin() || position > getMax()) {
+ return false;
+ }
+ InputSettings.setPointerSpeed(mContext, position);
+ mMetricsFeatureProvider.action(
+ mContext, SettingsEnums.ACTION_GESTURE_POINTER_SPEED_CHANGED, position);
+ return true;
+ }
+
+ @Override
+ public int getSliderPosition() {
+ return InputSettings.getPointerSpeed(mContext);
+ }
+
+ @Override
+ public int getMin() {
+ return InputSettings.MIN_POINTER_SPEED;
+ }
+
+ @Override
+ public int getMax() {
+ return InputSettings.MAX_POINTER_SPEED;
+ }
+}
diff --git a/src/com/android/settings/localepicker/LocaleDialogFragment.java b/src/com/android/settings/localepicker/LocaleDialogFragment.java
index a3a4b8fee72..5c7958a29ca 100644
--- a/src/com/android/settings/localepicker/LocaleDialogFragment.java
+++ b/src/com/android/settings/localepicker/LocaleDialogFragment.java
@@ -26,11 +26,6 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
@@ -57,6 +52,7 @@ public class LocaleDialogFragment extends InstrumentedDialogFragment {
static final String ARG_DIALOG_TYPE = "arg_dialog_type";
static final String ARG_TARGET_LOCALE = "arg_target_locale";
static final String ARG_SHOW_DIALOG = "arg_show_dialog";
+ static final String ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED = "arg_show_dialog_for_not_translated";
private boolean mShouldKeepDialog;
private OnBackInvokedDispatcher mBackDispatcher;
@@ -185,6 +181,7 @@ public class LocaleDialogFragment extends InstrumentedDialogFragment {
private final int mDialogType;
private final LocaleStore.LocaleInfo mLocaleInfo;
private final MetricsFeatureProvider mMetricsFeatureProvider;
+ private final boolean mShowDialogForNotTranslated;
private LocaleListEditor mParent;
@@ -194,6 +191,7 @@ public class LocaleDialogFragment extends InstrumentedDialogFragment {
mContext = context;
Bundle arguments = dialogFragment.getArguments();
mDialogType = arguments.getInt(ARG_DIALOG_TYPE);
+ mShowDialogForNotTranslated = arguments.getBoolean(ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED);
mLocaleInfo = (LocaleStore.LocaleInfo) arguments.getSerializable(ARG_TARGET_LOCALE);
mMetricsFeatureProvider =
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
@@ -215,6 +213,7 @@ public class LocaleDialogFragment extends InstrumentedDialogFragment {
bundle.putInt(ARG_DIALOG_TYPE, mDialogType);
bundle.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE, mLocaleInfo);
intent.putExtras(bundle);
+ intent.putExtra(ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED, mShowDialogForNotTranslated);
mParent.onActivityResult(mDialogType, result, intent);
mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_CHANGE_LANGUAGE,
changed);
diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
index 907fe7bd722..af8b6681cae 100644
--- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
+++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
@@ -364,12 +364,25 @@ class LocaleDragAndDropAdapter
}
public void notifyListChanged(LocaleStore.LocaleInfo localeInfo) {
- if (!localeInfo.getLocale().equals(mCacheItemList.get(0).getLocale())) {
+ if (listChanged()) {
mFeedItemList = new ArrayList<>(mCacheItemList);
notifyDataSetChanged();
}
}
+ private boolean listChanged() {
+ if (mFeedItemList.size() == mCacheItemList.size()) {
+ for (int i = 0; i < mFeedItemList.size(); i++) {
+ if (!mFeedItemList.get(i).getLocale().equals(mCacheItemList.get(i).getLocale())) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+ }
+
public void setCacheItemList() {
mCacheItemList = new ArrayList<>(mFeedItemList);
}
diff --git a/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java b/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java
index a7ebe32b841..df0af6392fd 100644
--- a/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java
+++ b/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java
@@ -151,7 +151,7 @@ public class LocaleLinearLayoutManager extends LinearLayoutManager {
}
if (result) {
- mLocaleListEditor.showConfirmDialog(false, mAdapter.getFeedItemList().get(0));
+ mLocaleListEditor.showConfirmDialog(mAdapter.getFeedItemList().get(0), null);
}
return result;
}
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index b1f005a79ab..e2da851f8e5 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -44,6 +44,7 @@ import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
@@ -235,7 +236,9 @@ public class LocaleListEditor extends RestrictedSettingsFragment implements View
localeInfo = mAdapter.getFeedItemList().get(0);
if (resultCode == Activity.RESULT_OK) {
mAdapter.doTheUpdate();
- if (!localeInfo.isTranslated()) {
+ boolean showNotTranslatedDialog = data.getBooleanExtra(
+ LocaleDialogFragment.ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED, true);
+ if (showNotTranslatedDialog && !localeInfo.isTranslated()) {
Bundle args = new Bundle();
args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE,
LocaleDialogFragment.DIALOG_NOT_AVAILABLE_LOCALE);
@@ -428,13 +431,10 @@ public class LocaleListEditor extends RestrictedSettingsFragment implements View
// to remove.
mRemoveMode = false;
mShowingRemoveDialog = false;
- LocaleStore.LocaleInfo firstLocale =
- mAdapter.getFeedItemList().get(0);
+ Locale defaultBeforeRemoval = Locale.getDefault();
mAdapter.removeChecked();
- boolean isFirstRemoved =
- firstLocale != mAdapter.getFeedItemList().get(0);
- showConfirmDialog(isFirstRemoved, isFirstRemoved ? firstLocale
- : mAdapter.getFeedItemList().get(0));
+ showConfirmDialog(mAdapter.getFeedItemList().get(0),
+ defaultBeforeRemoval);
setRemoveMode(false);
dialogHelper.getDialog().dismiss();
})
@@ -520,27 +520,73 @@ public class LocaleListEditor extends RestrictedSettingsFragment implements View
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP
|| event.getAction() == MotionEvent.ACTION_CANCEL) {
- showConfirmDialog(false, mAdapter.getFeedItemList().get(0));
+ showConfirmDialog(mAdapter.getFeedItemList().get(0), null);
}
return false;
}
- public void showConfirmDialog(boolean isFirstRemoved, LocaleStore.LocaleInfo localeInfo) {
+ protected void showConfirmDialog(LocaleStore.LocaleInfo localeInfo,
+ @Nullable Locale defaultLocaleBeforeRemoval) {
Locale currentSystemLocale = LocalePicker.getLocales().get(0);
if (!localeInfo.getLocale().equals(currentSystemLocale)) {
- final LocaleDialogFragment localeDialogFragment =
- LocaleDialogFragment.newInstance();
- Bundle args = new Bundle();
- args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
- args.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE,
- isFirstRemoved ? LocaleStore.getLocaleInfo(currentSystemLocale) : localeInfo);
- localeDialogFragment.setArguments(args);
- localeDialogFragment.show(mFragmentManager, TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT);
+ displayDialogFragment(localeInfo, true);
} else {
- mAdapter.doTheUpdate();
+ if (!localeInfo.isTranslated()) {
+ if (defaultLocaleBeforeRemoval == null) {
+ showDialogDueToDragAndDrop();
+ } else {
+ showDialogDueToRemoval(defaultLocaleBeforeRemoval);
+ }
+ } else {
+ mAdapter.doTheUpdate();
+ }
}
}
+ private void showDialogDueToDragAndDrop() {
+ LocaleStore.LocaleInfo newLocale = mAdapter.getFeedItemList().stream().filter(
+ i -> i.isTranslated()).findFirst().orElse(null);
+ if (newLocale == null) {
+ return;
+ }
+ LocaleStore.LocaleInfo oldLocale = null;
+ final LocaleList localeList = LocalePicker.getLocales();
+ for (int i = 0; i < localeList.size(); i++) {
+ LocaleStore.LocaleInfo temp = LocaleStore.getLocaleInfo(localeList.get(i));
+ if (temp.isTranslated()) {
+ oldLocale = temp;
+ break;
+ }
+ }
+ if (oldLocale != null && !newLocale.getLocale().equals(
+ oldLocale.getLocale())) {
+ displayDialogFragment(newLocale, false);
+ }
+ }
+
+ private void showDialogDueToRemoval(Locale preDefault) {
+ if (preDefault == null) {
+ return;
+ }
+ LocaleStore.LocaleInfo currentDefault = mAdapter.getFeedItemList().stream().filter(
+ i -> i.isTranslated()).findFirst().orElse(null);
+ if (currentDefault != null && !preDefault.equals(currentDefault.getLocale())) {
+ displayDialogFragment(currentDefault, false);
+ }
+ }
+
+ private void displayDialogFragment(LocaleStore.LocaleInfo localeInfo,
+ boolean showDialogForNotTranslated) {
+ final LocaleDialogFragment localeDialogFragment = LocaleDialogFragment.newInstance();
+ Bundle args = new Bundle();
+ args.putBoolean(LocaleDialogFragment.ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED,
+ showDialogForNotTranslated);
+ args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
+ args.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE, localeInfo);
+ localeDialogFragment.setArguments(args);
+ localeDialogFragment.show(mFragmentManager, TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT);
+ }
+
// Hide the "Remove" menu if there is only one locale in the list, show it otherwise
// This is called when the menu is first created, and then one add / remove locale
private void updateVisibilityOfRemoveMenu() {
diff --git a/src/com/android/settings/overlay/FeatureFactory.kt b/src/com/android/settings/overlay/FeatureFactory.kt
index 46aa19b0d05..7e04f0d4373 100644
--- a/src/com/android/settings/overlay/FeatureFactory.kt
+++ b/src/com/android/settings/overlay/FeatureFactory.kt
@@ -17,7 +17,7 @@ package com.android.settings.overlay
import android.content.Context
import com.android.settings.accessibility.AccessibilityFeedbackFeatureProvider
-import com.android.settings.accessibility.AccessibilityMetricsFeatureProvider
+import com.android.settings.accessibility.AccessibilityPageIdFeatureProvider
import com.android.settings.accessibility.AccessibilitySearchFeatureProvider
import com.android.settings.accounts.AccountFeatureProvider
import com.android.settings.applications.ApplicationFeatureProvider
@@ -145,9 +145,9 @@ abstract class FeatureFactory {
abstract val accessibilitySearchFeatureProvider: AccessibilitySearchFeatureProvider
/**
- * Retrieves implementation for Accessibility metrics category feature.
+ * Retrieves implementation for Accessibility page id category feature.
*/
- abstract val accessibilityMetricsFeatureProvider: AccessibilityMetricsFeatureProvider
+ abstract val accessibilityPageIdFeatureProvider: AccessibilityPageIdFeatureProvider
/**
* Retrieves implementation for advanced vpn feature.
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.kt b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
index 08abf2bd466..4949c3f7f0c 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.kt
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
@@ -22,8 +22,8 @@ import android.net.VpnManager
import android.os.UserManager
import com.android.settings.accessibility.AccessibilityFeedbackFeatureProvider
import com.android.settings.accessibility.AccessibilityFeedbackFeatureProviderImpl
-import com.android.settings.accessibility.AccessibilityMetricsFeatureProvider
-import com.android.settings.accessibility.AccessibilityMetricsFeatureProviderImpl
+import com.android.settings.accessibility.AccessibilityPageIdFeatureProvider
+import com.android.settings.accessibility.AccessibilityPageIdFeatureProviderImpl
import com.android.settings.accessibility.AccessibilitySearchFeatureProvider
import com.android.settings.accessibility.AccessibilitySearchFeatureProviderImpl
import com.android.settings.accounts.AccountFeatureProvider
@@ -174,8 +174,8 @@ open class FeatureFactoryImpl : FeatureFactory() {
AccessibilitySearchFeatureProviderImpl()
}
- override val accessibilityMetricsFeatureProvider: AccessibilityMetricsFeatureProvider by lazy {
- AccessibilityMetricsFeatureProviderImpl()
+ override val accessibilityPageIdFeatureProvider: AccessibilityPageIdFeatureProvider by lazy {
+ AccessibilityPageIdFeatureProviderImpl()
}
override val advancedVpnFeatureProvider by lazy { AdvancedVpnFeatureProviderImpl() }
diff --git a/src/com/android/settings/safetycenter/LockScreenSafetySource.java b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
index 00a4c676a80..61f05f7f02b 100644
--- a/src/com/android/settings/safetycenter/LockScreenSafetySource.java
+++ b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
@@ -131,6 +131,7 @@ public final class LockScreenSafetySource {
if (Flags.biometricsOnboardingEducation()) {
FaceSafetySource.onBiometricsChanged(context);
FingerprintSafetySource.onBiometricsChanged(context);
+ WearSafetySource.onBiometricsChanged(context);
} else {
BiometricsSafetySource.onBiometricsChanged(context);
}
diff --git a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
index a49b7e0f860..4cf40ddbdbe 100644
--- a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
+++ b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
@@ -86,6 +86,9 @@ public class SafetySourceBroadcastReceiver extends BroadcastReceiver {
if (sourceIds.contains(FingerprintSafetySource.SAFETY_SOURCE_ID)) {
FingerprintSafetySource.setSafetySourceData(context, safetyEvent);
}
+ if (sourceIds.contains(WearSafetySource.SAFETY_SOURCE_ID)) {
+ WearSafetySource.setSafetySourceData(context, safetyEvent);
+ }
}
private static void refreshAllSafetySources(Context context, SafetyEvent safetyEvent) {
@@ -95,5 +98,6 @@ public class SafetySourceBroadcastReceiver extends BroadcastReceiver {
PrivateSpaceSafetySource.setSafetySourceData(context, safetyEvent);
FaceSafetySource.setSafetySourceData(context, safetyEvent);
FingerprintSafetySource.setSafetySourceData(context, safetyEvent);
+ WearSafetySource.setSafetySourceData(context, safetyEvent);
}
}
diff --git a/src/com/android/settings/safetycenter/WearSafetySource.java b/src/com/android/settings/safetycenter/WearSafetySource.java
new file mode 100644
index 00000000000..a345096728b
--- /dev/null
+++ b/src/com/android/settings/safetycenter/WearSafetySource.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2025 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.safetycenter;
+
+import static com.android.settings.biometrics.combination.BiometricsSettingsBase.ACTIVE_UNLOCK_REQUEST;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.UserManager;
+import android.safetycenter.SafetyEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
+import com.android.settings.flags.Flags;
+
+/** Wear Safety Source for Safety Center. */
+public final class WearSafetySource {
+
+ private static final String TAG = "WearSafetySource";
+ public static final String SAFETY_SOURCE_ID = "AndroidWearUnlock";
+ private static boolean sIsTestingEnv = false;
+ private static String sSummaryForTesting = "";
+ private static boolean sHasEnrolledForTesting;
+
+ private WearSafetySource() {}
+
+ /** Sets test value for summary. */
+ @VisibleForTesting
+ public static void setSummaryForTesting(@NonNull String summary) {
+ sIsTestingEnv = true;
+ sSummaryForTesting = summary;
+ }
+
+ /** Sets test value for hasEnrolled. */
+ @VisibleForTesting
+ public static void setHasEnrolledForTesting(boolean hasEnrolled) {
+ sIsTestingEnv = true;
+ sHasEnrolledForTesting = hasEnrolled;
+ }
+
+ /** Sets biometric safety data for Safety Center. */
+ public static void setSafetySourceData(
+ @NonNull Context context, @NonNull SafetyEvent safetyEvent) {
+ if (!SafetyCenterManagerWrapper.get().isEnabled(context)) {
+ return;
+ }
+ if (!Flags.biometricsOnboardingEducation()) { // this source is effectively turned off
+ sendNullData(context, safetyEvent);
+ return;
+ }
+
+ // Handle private profile case.
+ UserManager userManager = UserManager.get(context);
+ if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && userManager.isPrivateProfile()) {
+ // SC always expects a response from the source if the broadcast has been sent for this
+ // source, therefore, we need to send a null SafetySourceData.
+ sendNullData(context, safetyEvent);
+ return;
+ }
+
+ ActiveUnlockStatusUtils activeUnlockStatusUtils = new ActiveUnlockStatusUtils(context);
+ if (!userManager.isProfile() && activeUnlockStatusUtils.isAvailable()) {
+ boolean hasEnrolled = false;
+ String summary = "";
+
+ if (sIsTestingEnv) {
+ hasEnrolled = sHasEnrolledForTesting;
+ summary = sSummaryForTesting;
+ } else {
+ String authority = new ActiveUnlockStatusUtils(context).getAuthority();
+ hasEnrolled = getHasEnrolledFromContentProvider(context, authority);
+ summary = getSummaryFromContentProvider(context, authority);
+ }
+
+ BiometricSourcesUtils.setBiometricSafetySourceData(
+ SAFETY_SOURCE_ID,
+ context,
+ activeUnlockStatusUtils.getTitleForActiveUnlockOnly(),
+ summary,
+ PendingIntent.getActivity(context, ACTIVE_UNLOCK_REQUEST,
+ activeUnlockStatusUtils.getIntent(),
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT),
+ /* enabled= */ true,
+ hasEnrolled,
+ safetyEvent);
+ return;
+ }
+
+ sendNullData(context, safetyEvent);
+ }
+
+ private static void sendNullData(Context context, SafetyEvent safetyEvent) {
+ SafetyCenterManagerWrapper.get()
+ .setSafetySourceData(
+ context, SAFETY_SOURCE_ID, /* safetySourceData= */ null, safetyEvent);
+ }
+
+ /** Notifies Safety Center of a change in wear biometrics settings. */
+ public static void onBiometricsChanged(@NonNull Context context) {
+ setSafetySourceData(
+ context,
+ new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED)
+ .build());
+ }
+
+ private static boolean getHasEnrolledFromContentProvider(
+ @NonNull Context context, @Nullable String authority) {
+ if (authority == null) {
+ return false;
+ }
+ return ActiveUnlockStatusUtils.getDeviceNameFromContentProvider(context, authority, TAG)
+ != null;
+ }
+
+ private static String getSummaryFromContentProvider(
+ @NonNull Context context, @Nullable String authority) {
+ if (authority == null) {
+ return "";
+ }
+ String summary = ActiveUnlockStatusUtils.getSummaryFromContentProvider(
+ context, authority, TAG);
+ if (summary == null) {
+ return "";
+ }
+ return summary;
+ }
+
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt b/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
index 7e160960efa..9bb3051efab 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
@@ -91,10 +91,15 @@ private class AppInstallerInfoPresenter(
}
}.sharedFlow()
- val isAvailableFlow = installerLabelFlow.map { installerLabel ->
- withContext(Dispatchers.IO) {
- !AppUtils.isMainlineModule(packageManager, app.packageName) &&
- installerLabel != null
+ val isAvailableFlow = installerLabelFlow.map() { installerLabel ->
+ // Do not show the install info for the special case of the Play Store app.
+ if (app.packageName == context.getString(R.string.config_mainline_module_update_package)) {
+ false
+ } else {
+ withContext(Dispatchers.IO) {
+ val isMainlineModule = AppUtils.isMainlineModule(packageManager, app.packageName)
+ !isMainlineModule && installerLabel != null
+ }
}
}
diff --git a/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java b/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java
index eb28dfbe624..267b5bc4c21 100644
--- a/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java
+++ b/tests/componenttests/src/com/android/settings/biometrics/BiometricEnrollActivityTest.java
@@ -23,6 +23,7 @@ import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static com.android.settings.biometrics.BiometricEnrollActivity.EXTRA_LAUNCH_FACE_ENROLL_FIRST;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT;
@@ -39,6 +40,7 @@ import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
@@ -145,7 +147,7 @@ public class BiometricEnrollActivityTest {
assumeTrue(mHasFace || mHasFingerprint);
setPin();
- final Intent intent = getIntent(true /* useInternal */);
+ final Intent intent = getIntent(true /* useInternal */, null);
LockPatternChecker.verifyCredential(new LockPatternUtils(mContext),
LockscreenCredential.createPin(TEST_PIN), UserHandle.myUserId(),
LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, (response, timeoutMs) -> {
@@ -162,6 +164,26 @@ public class BiometricEnrollActivityTest {
}
}
+ @Test
+ public void launchWithPinAndPwHandle_confirmsPin_firstEnrollmentIsFace() throws Exception {
+ assumeTrue(mHasFace && mHasFingerprint);
+
+ setPin();
+ final Intent intent = getFaceEnrollFirstIntent();
+ LockPatternChecker.verifyCredential(new LockPatternUtils(mContext),
+ LockscreenCredential.createPin(TEST_PIN), UserHandle.myUserId(),
+ LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, (response, timeoutMs) -> {
+ assertThat(response.containsGatekeeperPasswordHandle()).isTrue();
+ intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
+ response.getGatekeeperPasswordHandle());
+ }).get();
+
+ try (ActivityScenario