Pass Timeout back to upper biometric preference
When FaceSettings or FingerprintSettings are closed because of onStop(), this information can't been passed back to previous Preference screen, CombinedBiometricSettings, because handlePreferenceTreeClick() from AbstractPreferenceController class only can launchActivity() throguh preference's Context. In order to recevice the activity result code from FaceSettings or FingerprintSettings, add handleBiometricPreferenceTreeClick() method in BiometricStatusPreferenceController. Then CombinedBiometricSettings uses this method to show FaceSettings or FingerprintSettings through launchActivityForResult(). Bug: 263057093 Test: atest BiometricNavigationUtilsTest Test: Manually open camera through double-click power key on different pages inside "Face & Fingerprint Unlock" Change-Id: I99167739766ad5ea5f204b0f0543ba6ad18fac31
This commit is contained in:
@@ -26,6 +26,9 @@ import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.app.UnlaunchableAppActivity;
|
||||
import com.android.settings.core.SettingsBaseActivity;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
@@ -49,15 +52,23 @@ public class BiometricNavigationUtils {
|
||||
*
|
||||
* @param className The class name of Settings screen to launch.
|
||||
* @param extras Extras to put into the launching {@link Intent}.
|
||||
* @param launcher Launcher to launch activity if non-quiet mode
|
||||
* @return true if the Settings screen is launching.
|
||||
*/
|
||||
public boolean launchBiometricSettings(Context context, String className, Bundle extras) {
|
||||
public boolean launchBiometricSettings(Context context, String className, Bundle extras,
|
||||
@Nullable ActivityResultLauncher<Intent> launcher) {
|
||||
final Intent quietModeDialogIntent = getQuietModeDialogIntent(context);
|
||||
if (quietModeDialogIntent != null) {
|
||||
context.startActivity(quietModeDialogIntent);
|
||||
return false;
|
||||
}
|
||||
context.startActivity(getSettingsPageIntent(className, extras));
|
||||
|
||||
final Intent settingsPageIntent = getSettingsPageIntent(className, extras);
|
||||
if (launcher != null) {
|
||||
launcher.launch(settingsPageIntent);
|
||||
} else {
|
||||
context.startActivity(settingsPageIntent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -17,10 +17,14 @@
|
||||
package com.android.settings.biometrics;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
@@ -29,6 +33,8 @@ import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public abstract class BiometricStatusPreferenceController extends BasePreferenceController {
|
||||
|
||||
protected final UserManager mUm;
|
||||
@@ -39,6 +45,8 @@ public abstract class BiometricStatusPreferenceController extends BasePreference
|
||||
|
||||
private final BiometricNavigationUtils mBiometricNavigationUtils;
|
||||
private final ActiveUnlockStatusUtils mActiveUnlockStatusUtils;
|
||||
@NonNull private WeakReference<ActivityResultLauncher<Intent>> mLauncherWeakReference =
|
||||
new WeakReference<>(null);
|
||||
|
||||
/**
|
||||
* @return true if the controller should be shown exclusively.
|
||||
@@ -118,14 +126,31 @@ public abstract class BiometricStatusPreferenceController extends BasePreference
|
||||
preference.setSummary(getSummaryText());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ActivityResultLauncher that will be used later during handlePreferenceTreeClick()
|
||||
*
|
||||
* @param preference the preference being compared
|
||||
* @param launcher the ActivityResultLauncher
|
||||
* @return {@code true} if matched preference.
|
||||
*/
|
||||
public boolean setPreferenceTreeClickLauncher(@NonNull Preference preference,
|
||||
@Nullable ActivityResultLauncher<Intent> launcher) {
|
||||
if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mLauncherWeakReference = new WeakReference<>(launcher);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||
if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
|
||||
return super.handlePreferenceTreeClick(preference);
|
||||
}
|
||||
|
||||
return mBiometricNavigationUtils.launchBiometricSettings(
|
||||
preference.getContext(), getSettingsClassName(), preference.getExtras());
|
||||
return mBiometricNavigationUtils.launchBiometricSettings(preference.getContext(),
|
||||
getSettingsClassName(), preference.getExtras(), mLauncherWeakReference.get());
|
||||
}
|
||||
|
||||
protected int getUserId() {
|
||||
|
@@ -33,6 +33,9 @@ import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
@@ -42,14 +45,19 @@ import androidx.preference.Preference;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.biometrics.BiometricEnrollBase;
|
||||
import com.android.settings.biometrics.BiometricStatusPreferenceController;
|
||||
import com.android.settings.biometrics.BiometricUtils;
|
||||
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
|
||||
import com.android.settings.core.SettingsBaseActivity;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.password.ChooseLockGeneric;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.transition.SettingsTransitionHelper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base fragment with the confirming credential functionality for combined biometrics settings.
|
||||
*/
|
||||
@@ -78,6 +86,18 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
|
||||
@Nullable private String mRetryPreferenceKey = null;
|
||||
@Nullable private Bundle mRetryPreferenceExtra = null;
|
||||
|
||||
private final ActivityResultLauncher<Intent> mFaceOrFingerprintPreferenceLauncher =
|
||||
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
|
||||
this::onFaceOrFingerprintPreferenceResult);
|
||||
|
||||
private void onFaceOrFingerprintPreferenceResult(@Nullable ActivityResult result) {
|
||||
if (result != null && result.getResultCode() == BiometricEnrollBase.RESULT_TIMEOUT) {
|
||||
// When "Face Unlock" or "Fingerprint Unlock" is closed due to entering onStop(),
|
||||
// "Face & Fingerprint Unlock" shall also close itself and back to "Security" page.
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
@@ -165,7 +185,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
|
||||
extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
|
||||
extras.putInt(BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, sensorId);
|
||||
extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
|
||||
super.onPreferenceTreeClick(preference);
|
||||
onFaceOrFingerprintPreferenceTreeClick(preference);
|
||||
} catch (IllegalStateException e) {
|
||||
if (retry) {
|
||||
mRetryPreferenceKey = preference.getKey();
|
||||
@@ -200,7 +220,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
|
||||
final Bundle extras = preference.getExtras();
|
||||
extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
|
||||
extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
|
||||
super.onPreferenceTreeClick(preference);
|
||||
onFaceOrFingerprintPreferenceTreeClick(preference);
|
||||
} catch (IllegalStateException e) {
|
||||
if (retry) {
|
||||
mRetryPreferenceKey = preference.getKey();
|
||||
@@ -224,6 +244,33 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
|
||||
return BiometricUtils.requestGatekeeperHat(context, gkPwHandle, userId, challenge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle preference tree click action for "Face Unlock" or "Fingerprint Unlock" with a launcher
|
||||
* because "Face & Fingerprint Unlock" has to close itself when it gets a specific activity
|
||||
* error code.
|
||||
*
|
||||
* @param preference "Face Unlock" or "Fingerprint Unlock" preference.
|
||||
*/
|
||||
private void onFaceOrFingerprintPreferenceTreeClick(@NonNull Preference preference) {
|
||||
Collection<List<AbstractPreferenceController>> controllers = getPreferenceControllers();
|
||||
for (List<AbstractPreferenceController> controllerList : controllers) {
|
||||
for (AbstractPreferenceController controller : controllerList) {
|
||||
if (controller instanceof BiometricStatusPreferenceController) {
|
||||
final BiometricStatusPreferenceController biometricController =
|
||||
(BiometricStatusPreferenceController) controller;
|
||||
if (biometricController.setPreferenceTreeClickLauncher(preference,
|
||||
mFaceOrFingerprintPreferenceLauncher)) {
|
||||
if (biometricController.handlePreferenceTreeClick(preference)) {
|
||||
writePreferenceClickMetric(preference);
|
||||
}
|
||||
biometricController.setPreferenceTreeClickLauncher(preference, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(Preference preference) {
|
||||
return onRetryPreferenceTreeClick(preference, true)
|
||||
|
@@ -325,6 +325,8 @@ public class FaceSettings extends DashboardFragment {
|
||||
mFaceManager.revokeChallenge(mSensorId, mUserId, mChallenge);
|
||||
mToken = null;
|
||||
}
|
||||
// Let parent "Face & Fingerprint Unlock" can use this error code to close itself.
|
||||
setResult(RESULT_TIMEOUT);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
@@ -669,6 +669,7 @@ public class FingerprintSettings extends SubSettings {
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
if (!getActivity().isChangingConfigurations() && !mLaunchedConfirm && !mIsEnrolling) {
|
||||
setResult(RESULT_TIMEOUT);
|
||||
getActivity().finish();
|
||||
}
|
||||
}
|
||||
@@ -874,6 +875,12 @@ public class FingerprintSettings extends SubSettings {
|
||||
} else if (requestCode == AUTO_ADD_FIRST_FINGERPRINT_REQUEST) {
|
||||
if (resultCode != RESULT_FINISHED || data == null) {
|
||||
Log.d(TAG, "Add first fingerprint, fail or null data, result:" + resultCode);
|
||||
if (resultCode == BiometricEnrollBase.RESULT_TIMEOUT) {
|
||||
// If "Fingerprint Unlock" is closed because of timeout, notify result code
|
||||
// back because "Face & Fingerprint Unlock" has to close itself for timeout
|
||||
// case.
|
||||
setResult(resultCode);
|
||||
}
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -33,6 +34,7 @@ import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
@@ -55,6 +57,8 @@ public class BiometricNavigationUtilsTest {
|
||||
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private ActivityResultLauncher<Intent> mLauncher;
|
||||
private Context mContext;
|
||||
private BiometricNavigationUtils mBiometricNavigationUtils;
|
||||
|
||||
@@ -72,7 +76,7 @@ public class BiometricNavigationUtilsTest {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
|
||||
|
||||
mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
|
||||
Bundle.EMPTY);
|
||||
Bundle.EMPTY, null);
|
||||
|
||||
assertQuietModeDialogLaunchRequested();
|
||||
}
|
||||
@@ -82,7 +86,17 @@ public class BiometricNavigationUtilsTest {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
|
||||
|
||||
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY)).isFalse();
|
||||
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchBiometricSettings_quietMode_withLauncher_notThroughLauncher() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
|
||||
|
||||
mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
|
||||
Bundle.EMPTY, mLauncher);
|
||||
|
||||
verify(mLauncher, never()).launch(any(Intent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,7 +104,7 @@ public class BiometricNavigationUtilsTest {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY);
|
||||
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null);
|
||||
|
||||
assertSettingsPageLaunchRequested(false /* shouldContainExtras */);
|
||||
}
|
||||
@@ -100,7 +114,7 @@ public class BiometricNavigationUtilsTest {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY)).isTrue();
|
||||
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,17 +122,29 @@ public class BiometricNavigationUtilsTest {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
final Bundle extras = createNotEmptyExtras();
|
||||
mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME, extras);
|
||||
mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, extras, null);
|
||||
|
||||
assertSettingsPageLaunchRequested(true /* shouldContainExtras */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchBiometricSettings_noQuietMode_withLauncher_launchesThroughLauncher() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
final Bundle extras = createNotEmptyExtras();
|
||||
mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, extras, mLauncher);
|
||||
|
||||
verify(mLauncher).launch(any(Intent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchBiometricSettings_noQuietMode_withExtras_returnsTrue() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, createNotEmptyExtras())).isTrue();
|
||||
mContext, SETTINGS_CLASS_NAME, createNotEmptyExtras(), null)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Reference in New Issue
Block a user