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

Commit 13c96068 authored by Milton Wu's avatar Milton Wu Committed by Android (Google) Code Review
Browse files

Merge "[BiometricsV2] Refine fingerprint ui and flow"

parents 0f00b5e1 8b0fc14f
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -78,4 +78,11 @@ public final class FingerprintEnrollIntroStatus {
    public boolean hasScrollToBottom() {
        return mHasScrollToBottom;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode())
                + "{scrollToBottom:" + mHasScrollToBottom
                + ", enrollableStatus:" + mEnrollableStatus + "}";
    }
}
+64 −33
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ import com.google.android.setupdesign.util.DynamicColorPalette;
public class FingerprintEnrollIntroFragment extends Fragment {

    private static final String TAG = "FingerprintEnrollIntroFragment";
    private static final boolean DEBUG = false;

    private FingerprintEnrollIntroViewModel mViewModel = null;

@@ -128,17 +129,6 @@ public class FingerprintEnrollIntroFragment extends Fragment {
        footerLink.setText(Html.fromHtml(footerLinkStr));

        // footer buttons
        mPrimaryFooterButton = new FooterButton.Builder(context)
                .setText(R.string.security_settings_fingerprint_enroll_introduction_agree)
                .setButtonType(FooterButton.ButtonType.OPT_IN)
                .setTheme(R.style.SudGlifButton_Primary)
                .build();
        mSecondaryFooterButton = new FooterButton.Builder(context)
                .setButtonType(FooterButton.ButtonType.NEXT)
                .setTheme(R.style.SudGlifButton_Primary)
                .build();
        getFooterBarMixin().setPrimaryButton(mPrimaryFooterButton);
        getFooterBarMixin().setSecondaryButton(mSecondaryFooterButton, true /* usePrimaryStyle */);

        return mView;
    }
@@ -149,9 +139,6 @@ public class FingerprintEnrollIntroFragment extends Fragment {

        final Context context = view.getContext();

        mPrimaryFooterButton.setOnClickListener(mOnNextClickListener);
        mSecondaryFooterButton.setOnClickListener(mOnSkipOrCancelClickListener);

        if (mViewModel.canAssumeUdfps()) {
            mFooterMessage6.setVisibility(View.VISIBLE);
            mIconShield.setVisibility(View.VISIBLE);
@@ -159,10 +146,6 @@ public class FingerprintEnrollIntroFragment extends Fragment {
            mFooterMessage6.setVisibility(View.GONE);
            mIconShield.setVisibility(View.GONE);
        }
        mSecondaryFooterButton.setText(context,
                mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()
                ? R.string.security_settings_fingerprint_enroll_introduction_cancel
                : R.string.security_settings_fingerprint_enroll_introduction_no_thanks);

        final GlifLayoutHelper glifLayoutHelper = new GlifLayoutHelper(getActivity(), getLayout());
        if (mViewModel.isBiometricUnlockDisabledByAdmin()
@@ -178,16 +161,64 @@ public class FingerprintEnrollIntroFragment extends Fragment {
                    R.string.security_settings_fingerprint_enroll_introduction_v3_message,
                    DeviceHelper.getDeviceName(context)));
        }
    }

    @Override
    public void onStart() {
        final Context context = requireContext();
        final FooterBarMixin footerBarMixin = getFooterBarMixin();
        initPrimaryFooterButton(context, footerBarMixin);
        initSecondaryFooterButton(context, footerBarMixin);
        observePageStatusLiveDataIfNeed();
        super.onStart();
    }

    private void initPrimaryFooterButton(@NonNull Context context,
            @NonNull FooterBarMixin footerBarMixin) {
        if (footerBarMixin.getPrimaryButton() != null) {
            return;
        }

        mPrimaryFooterButton = new FooterButton.Builder(context)
                .setText(R.string.security_settings_fingerprint_enroll_introduction_agree)
                .setButtonType(FooterButton.ButtonType.OPT_IN)
                .setTheme(R.style.SudGlifButton_Primary)
                .build();
        mPrimaryFooterButton.setOnClickListener(mOnNextClickListener);
        footerBarMixin.setPrimaryButton(mPrimaryFooterButton);
    }

    private void initSecondaryFooterButton(@NonNull Context context,
            @NonNull FooterBarMixin footerBarMixin) {
        if (footerBarMixin.getSecondaryButton() != null) {
            return;
        }

        mSecondaryFooterButton = new FooterButton.Builder(context)
                .setText(mViewModel.getRequest().isAfterSuwOrSuwSuggestedAction()
                        ? R.string.security_settings_fingerprint_enroll_introduction_cancel
                        : R.string.security_settings_fingerprint_enroll_introduction_no_thanks)
                .setButtonType(FooterButton.ButtonType.NEXT)
                .setTheme(R.style.SudGlifButton_Primary)
                .build();
        mSecondaryFooterButton.setOnClickListener(mOnSkipOrCancelClickListener);
        footerBarMixin.setSecondaryButton(mSecondaryFooterButton, true /* usePrimaryStyle */);
    }

    private void observePageStatusLiveDataIfNeed() {
        final LiveData<FingerprintEnrollIntroStatus> statusLiveData =
                mViewModel.getPageStatusLiveData();
        final FingerprintEnrollIntroStatus status = statusLiveData.getValue();
        if (status != null && status.hasScrollToBottom()) {
            // Do not requireScrollWithButton() again when "I agree" or "Done" button is visible,
            // because if we requireScrollWithButton() again, it will become "More" after scroll-up.
        if (DEBUG) {
            Log.e(TAG, "observePageStatusLiveDataIfNeed() requireScrollWithButton, status:"
                    + status);
        }
        if (status != null && (status.hasScrollToBottom()
                || status.getEnrollableStatus() == FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)) {
            // Update once and do not requireScrollWithButton() again when page has scrolled to
            // bottom or User has enrolled at least a fingerprint, because if we
            // requireScrollWithButton() again, primary button will become "More" after scrolling.
            updateFooterButtons(status);
            return;
        }

@@ -196,10 +227,6 @@ public class FingerprintEnrollIntroFragment extends Fragment {
        requireScrollMixin.requireScrollWithButton(getActivity(), mPrimaryFooterButton,
                getMoreButtonTextRes(), mOnNextClickListener);

        // Always set true to setHasScrolledToBottom() before registering listener through
        // setOnRequireScrollStateChangedListener(), because listener will not be called if first
        // scrollNeeded is true
        mViewModel.setHasScrolledToBottom(true);
        requireScrollMixin.setOnRequireScrollStateChangedListener(
                scrollNeeded -> mViewModel.setHasScrolledToBottom(!scrollNeeded));
        statusLiveData.observe(this, this::updateFooterButtons);
@@ -249,15 +276,19 @@ public class FingerprintEnrollIntroFragment extends Fragment {
    }

    void updateFooterButtons(@NonNull FingerprintEnrollIntroStatus status) {
        @StringRes final int scrollToBottomPrimaryResId =
                status.getEnrollableStatus() == FINGERPRINT_ENROLLABLE_OK
                        ? R.string.security_settings_fingerprint_enroll_introduction_agree
                        : R.string.done;

        if (DEBUG) {
            Log.d(TAG, "updateFooterButtons(" + status + ")");
        }
        mPrimaryFooterButton.setText(getContext(),
                status.hasScrollToBottom() ? scrollToBottomPrimaryResId : getMoreButtonTextRes());
        mSecondaryFooterButton.setVisibility(
                status.hasScrollToBottom() ? View.VISIBLE : View.INVISIBLE);
                status.getEnrollableStatus() == FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX
                        ? R.string.done
                        : status.hasScrollToBottom()
                                ? R.string.security_settings_fingerprint_enroll_introduction_agree
                                : getMoreButtonTextRes());
        mSecondaryFooterButton.setVisibility(status.hasScrollToBottom()
                && status.getEnrollableStatus() != FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX
                ? View.VISIBLE
                : View.INVISIBLE);

        final TextView errorTextView = mView.findViewById(R.id.error_text);
        switch (status.getEnrollableStatus()) {
+24 −25
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.settings.biometrics2.ui.view;

import static androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY;

import static com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR_KEY;
@@ -100,12 +101,8 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
    private static final String TAG = "FingerprintEnrollmentActivity";

    private static final String INTRO_TAG = "intro";
    private static final String FIND_UDFPS_TAG = "find-udfps";
    private static final String FIND_SFPS_TAG = "find-sfps";
    private static final String FIND_RFPS_TAG = "find-rfps";
    private static final String ENROLLING_UDFPS_TAG = "enrolling-udfps";
    private static final String ENROLLING_SFPS_TAG = "enrolling-sfps";
    private static final String ENROLLING_RFPS_TAG = "enrolling-rfps";
    private static final String FIND_SENSOR_TAG = "find-sensor";
    private static final String ENROLLING_TAG = "enrolling";
    private static final String FINISH_TAG = "finish";
    private static final String SKIP_SETUP_FIND_FPS_DIALOG_TAG = "skip-setup-dialog";
    private static final String ENROLLING_ERROR_DIALOG_TAG = "enrolling-error-dialog";
@@ -213,12 +210,10 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
            final String tag = fragment.getTag();
            if (INTRO_TAG.equals(tag)) {
                attachIntroViewModel();
            } else if (FIND_UDFPS_TAG.equals(tag) || FIND_SFPS_TAG.equals(tag)
                    || FIND_RFPS_TAG.equals(tag)) {
            } else if (FIND_SENSOR_TAG.equals(tag)) {
                attachFindSensorViewModel();
                attachIntroViewModel();
            } else if (ENROLLING_UDFPS_TAG.equals(tag) || ENROLLING_SFPS_TAG.equals(tag)
                    || ENROLLING_RFPS_TAG.equals(tag)) {
            } else if (ENROLLING_TAG.equals(tag)) {
                attachEnrollingViewModel();
                attachFindSensorViewModel();
                attachIntroViewModel();
@@ -289,19 +284,15 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {

        attachFindSensorViewModel();

        final String tag;
        final Class<? extends Fragment> fragmentClass;
        if (mViewModel.canAssumeUdfps()) {
            tag = FIND_UDFPS_TAG;
            fragmentClass = FingerprintEnrollFindUdfpsFragment.class;
        } else if (mViewModel.canAssumeSfps()) {
            tag = FIND_SFPS_TAG;
            fragmentClass = FingerprintEnrollFindSfpsFragment.class;
        } else {
            tag = FIND_RFPS_TAG;
            fragmentClass = FingerprintEnrollFindRfpsFragment.class;
        }
        startFragment(fragmentClass, tag);
        startFragment(fragmentClass, FIND_SENSOR_TAG);
    }

    private void attachFindSensorViewModel() {
@@ -325,19 +316,15 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {

        attachEnrollingViewModel();

        final String tag;
        final Class<? extends Fragment> fragmentClass;
        if (mViewModel.canAssumeUdfps()) {
            tag = ENROLLING_UDFPS_TAG;
            fragmentClass = FingerprintEnrollEnrollingUdfpsFragment.class;
        } else if (mViewModel.canAssumeSfps()) {
            tag = ENROLLING_SFPS_TAG;
            fragmentClass = FingerprintEnrollEnrollingSfpsFragment.class;
        } else {
            tag = ENROLLING_RFPS_TAG;
            fragmentClass = FingerprintEnrollEnrollingRfpsFragment.class;
        }
        startFragment(fragmentClass, tag);
        startFragment(fragmentClass, ENROLLING_TAG);
    }

    private void attachEnrollingViewModel() {
@@ -354,9 +341,8 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
        mViewModel.setIsNewFingerprintAdded();
        attachFinishViewModel();

        getSupportFragmentManager().popBackStack();
        if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
            // Replace enrolling page
        if (mViewModel.getRequest().isSkipFindSensor()) {
            // Set page to Finish
            getSupportFragmentManager().beginTransaction()
                    .setReorderingAllowed(true)
                    .setCustomAnimations(R.anim.shared_x_axis_activity_open_enter_dynamic_color,
@@ -367,8 +353,21 @@ public class FingerprintEnrollmentActivity extends FragmentActivity {
                            null, FINISH_TAG)
                    .commit();
        } else {
            // Remove Enrolling page from backstack, and add Finish page. Latest backstack will
            // be changed from Intro->FindSensor->Enrolling to Intro->FindSensor->Finish
            // Remove Enrolling page
            getSupportFragmentManager().popBackStack();

            // Remove old Finish page if any
            if (getSupportFragmentManager().findFragmentByTag(FINISH_TAG) != null) {
                getSupportFragmentManager().popBackStack(FINISH_TAG, POP_BACK_STACK_INCLUSIVE);
            }

            // Remove FindSensor page if maxEnrolled
            if (mViewModel.isMaxEnrolledReached(mAutoCredentialViewModel.getUserId())
                    && getSupportFragmentManager().findFragmentByTag(FIND_SENSOR_TAG) != null) {
                getSupportFragmentManager().popBackStack(FIND_SENSOR_TAG, POP_BACK_STACK_INCLUSIVE);
            }

            // Add Finish page
            getSupportFragmentManager().beginTransaction()
                    .setReorderingAllowed(true)
                    .setCustomAnimations(R.anim.shared_x_axis_activity_open_enter_dynamic_color,
+1 −2
Original line number Diff line number Diff line
@@ -107,8 +107,6 @@ public class FingerprintEnrollIntroViewModel extends AndroidViewModel {
                            enrollableValue != null ? enrollableValue : ENROLLABLE_STATUS_DEFAULT);
                    mPageStatusLiveData.setValue(status);
                });

        updateEnrollableStatus();
    }

    /**
@@ -133,6 +131,7 @@ public class FingerprintEnrollIntroViewModel extends AndroidViewModel {
     * Get enrollable status and hasScrollToBottom live data
     */
    public LiveData<FingerprintEnrollIntroStatus> getPageStatusLiveData() {
        updateEnrollableStatus();
        return mPageStatusLiveData;
    }

+8 −1
Original line number Diff line number Diff line
@@ -87,7 +87,6 @@ public class FingerprintEnrollmentViewModel extends AndroidViewModel {
    @NonNull
    public ActivityResult getOverrideActivityResult(@NonNull ActivityResult result,
            @Nullable Bundle generatingChallengeExtras) {
        // TODO write tests
        final int newResultCode = mIsNewFingerprintAdded
                ? BiometricEnrollBase.RESULT_FINISHED
                : (mRequest.isAfterSuwOrSuwSuggestedAction()
@@ -165,6 +164,14 @@ public class FingerprintEnrollmentViewModel extends AndroidViewModel {
        outState.putBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED, mIsNewFingerprintAdded);
    }

    /**
     * Gets the result about fingerprint enrollable
     */
    public boolean isMaxEnrolledReached(int userId) {
        return mFingerprintRepository.getMaxFingerprints()
                <= mFingerprintRepository.getNumOfEnrolledFingerprintsSize(userId);
    }

    /**
     * The first sensor type is UDFPS sensor or not
     */
Loading