Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 52a46d0a authored by Milton Wu's avatar Milton Wu
Browse files

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
parent 2c427629
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -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;
    }

+27 −2
Original line number Diff line number Diff line
@@ -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() {
+49 −2
Original line number Diff line number Diff line
@@ -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)
+2 −0
Original line number Diff line number Diff line
@@ -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();
        }
    }
+7 −0
Original line number Diff line number Diff line
@@ -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;
                }
Loading