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

Commit 667341c3 authored by Wu Ahan's avatar Wu Ahan
Browse files

Reland sfps enroll improvement feature with feature provider

Prior cl, ag/24720067, was reverted due to platinum failed,
this cl fixes the fails (NPE on UDFPS).

Bug: 288155127
Bug: 305132251
Test: Manually check on Lynx and Felix
Test: atest BiometricsE2eTests:FingerprintEnrollSuccessTest
Test: abtd for platinum, see b/305048300#comment14
Change-Id: I90f4ea14853edf03abd8ffe0b7874894eb2d4f81
parent 55b8e790
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ aconfig_declarations {
        "settings_experience_flag_declarations.aconfig",
        "settings_onboarding_experience_flag_declarations.aconfig",
        "settings_telephony_flag_declarations.aconfig",
        "settings_biometrics_integration_declarations.aconfig",
    ],
}

+9 −0
Original line number Diff line number Diff line
package: "com.android.settings.flags"

flag {
  name: "sfps_enroll_refinement"
  namespace: "biometrics_integration"
  description: "This flag controls whether the sfps pause enrollment feature should be enabled"
  bug: "288155127"
}
+85 −42
Original line number Diff line number Diff line
@@ -68,10 +68,13 @@ import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollSidecar;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.display.DisplayDensityUtils;

import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieCompositionFactory;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.model.KeyPath;
@@ -84,6 +87,7 @@ import com.google.android.setupdesign.template.HeaderMixin;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.function.Function;

/**
 * Activity which handles the actual enrolling for fingerprint.
@@ -99,27 +103,22 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {

    private static final int PROGRESS_BAR_MAX = 10000;

    private static final int STAGE_UNKNOWN = -1;
    public static final int STAGE_UNKNOWN = -1;
    private static final int STAGE_CENTER = 0;
    private static final int STAGE_GUIDED = 1;
    private static final int STAGE_FINGERTIP = 2;
    private static final int STAGE_LEFT_EDGE = 3;
    private static final int STAGE_RIGHT_EDGE = 4;

    @VisibleForTesting
    protected static final int SFPS_STAGE_NO_ANIMATION = 0;
    public static final int SFPS_STAGE_NO_ANIMATION = 0;

    @VisibleForTesting
    protected static final int SFPS_STAGE_CENTER = 1;
    public static final int SFPS_STAGE_CENTER = 1;

    @VisibleForTesting
    protected static final int SFPS_STAGE_FINGERTIP = 2;
    public static final int SFPS_STAGE_FINGERTIP = 2;

    @VisibleForTesting
    protected static final int SFPS_STAGE_LEFT_EDGE = 3;
    public static final int SFPS_STAGE_LEFT_EDGE = 3;

    @VisibleForTesting
    protected static final int SFPS_STAGE_RIGHT_EDGE = 4;
    public static final int SFPS_STAGE_RIGHT_EDGE = 4;

    @IntDef({STAGE_UNKNOWN, STAGE_CENTER, STAGE_GUIDED, STAGE_FINGERTIP, STAGE_LEFT_EDGE,
            STAGE_RIGHT_EDGE})
@@ -196,6 +195,9 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
    private OrientationEventListener mOrientationEventListener;
    private int mPreviousRotation = 0;

    @NonNull
    private SfpsEnrollmentFeature mSfpsEnrollmentFeature = new EmptySfpsEnrollmentFeature();

    @VisibleForTesting
    protected boolean shouldShowLottie() {
        DisplayDensityUtils displayDensity = new DisplayDensityUtils(getApplicationContext());
@@ -244,6 +246,8 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
            setContentView(layout);
            setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
        } else if (mCanAssumeSfps) {
            mSfpsEnrollmentFeature = FeatureFactory.getFeatureFactory()
                    .getFingerprintFeatureProvider().getSfpsEnrollmentFeature();
            setContentView(R.layout.sfps_enroll_enrolling);
            setHelpAnimation();
        } else {
@@ -599,7 +603,8 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
        }
        switch (getCurrentSfpsStage()) {
            case SFPS_STAGE_NO_ANIMATION:
                setHeaderText(R.string.security_settings_fingerprint_enroll_repeat_title);
                setHeaderText(mSfpsEnrollmentFeature
                        .getFeaturedStageHeaderResource(SFPS_STAGE_NO_ANIMATION));
                if (!mHaveShownSfpsNoAnimationLottie && mIllustrationLottie != null) {
                    mHaveShownSfpsNoAnimationLottie = true;
                    mIllustrationLottie.setContentDescription(
@@ -608,39 +613,48 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
                                    0
                            )
                    );
                    configureEnrollmentStage(R.raw.sfps_lottie_no_animation);
                    configureEnrollmentStage(mSfpsEnrollmentFeature
                            .getSfpsEnrollLottiePerStage(SFPS_STAGE_NO_ANIMATION));
                }
                break;

            case SFPS_STAGE_CENTER:
                setHeaderText(R.string.security_settings_sfps_enroll_finger_center_title);
                setHeaderText(mSfpsEnrollmentFeature
                        .getFeaturedStageHeaderResource(SFPS_STAGE_CENTER));
                if (!mHaveShownSfpsCenterLottie && mIllustrationLottie != null) {
                    mHaveShownSfpsCenterLottie = true;
                    configureEnrollmentStage(R.raw.sfps_lottie_pad_center);
                    configureEnrollmentStage(mSfpsEnrollmentFeature
                            .getSfpsEnrollLottiePerStage(SFPS_STAGE_CENTER));
                }
                break;

            case SFPS_STAGE_FINGERTIP:
                setHeaderText(R.string.security_settings_sfps_enroll_fingertip_title);
                setHeaderText(mSfpsEnrollmentFeature
                        .getFeaturedStageHeaderResource(SFPS_STAGE_FINGERTIP));
                if (!mHaveShownSfpsTipLottie && mIllustrationLottie != null) {
                    mHaveShownSfpsTipLottie = true;
                    configureEnrollmentStage(R.raw.sfps_lottie_tip);
                    configureEnrollmentStage(mSfpsEnrollmentFeature
                            .getSfpsEnrollLottiePerStage(SFPS_STAGE_FINGERTIP));
                }
                break;

            case SFPS_STAGE_LEFT_EDGE:
                setHeaderText(R.string.security_settings_sfps_enroll_left_edge_title);
                setHeaderText(mSfpsEnrollmentFeature
                        .getFeaturedStageHeaderResource(SFPS_STAGE_LEFT_EDGE));
                if (!mHaveShownSfpsLeftEdgeLottie && mIllustrationLottie != null) {
                    mHaveShownSfpsLeftEdgeLottie = true;
                    configureEnrollmentStage(R.raw.sfps_lottie_left_edge);
                    configureEnrollmentStage(mSfpsEnrollmentFeature
                            .getSfpsEnrollLottiePerStage(SFPS_STAGE_LEFT_EDGE));
                }
                break;

            case SFPS_STAGE_RIGHT_EDGE:
                setHeaderText(R.string.security_settings_sfps_enroll_right_edge_title);
                setHeaderText(mSfpsEnrollmentFeature
                        .getFeaturedStageHeaderResource(SFPS_STAGE_RIGHT_EDGE));
                if (!mHaveShownSfpsRightEdgeLottie && mIllustrationLottie != null) {
                    mHaveShownSfpsRightEdgeLottie = true;
                    configureEnrollmentStage(R.raw.sfps_lottie_right_edge);
                    configureEnrollmentStage(mSfpsEnrollmentFeature
                            .getSfpsEnrollLottiePerStage(SFPS_STAGE_RIGHT_EDGE));
                }
                break;

@@ -665,11 +679,16 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
            setDescriptionText("");
        }
        LottieCompositionFactory.fromRawRes(this, lottie)
                .addListener((c) -> {
                    mIllustrationLottie.setComposition(c);
                    mIllustrationLottie.setVisibility(View.VISIBLE);
                    mIllustrationLottie.playAnimation();
                });
                .addListener((c) -> onLottieComposition(mIllustrationLottie, c));
    }

    private void onLottieComposition(LottieAnimationView view, LottieComposition composition) {
        if (view == null || composition == null) {
            return;
        }
        view.setComposition(composition);
        view.setVisibility(View.VISIBLE);
        view.playAnimation();
    }

    @EnrollStage
@@ -699,17 +718,8 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
        }

        final int progressSteps = mSidecar.getEnrollmentSteps() - mSidecar.getEnrollmentRemaining();
        if (progressSteps < getStageThresholdSteps(0)) {
            return SFPS_STAGE_NO_ANIMATION;
        } else if (progressSteps < getStageThresholdSteps(1)) {
            return SFPS_STAGE_CENTER;
        } else if (progressSteps < getStageThresholdSteps(2)) {
            return SFPS_STAGE_FINGERTIP;
        } else if (progressSteps < getStageThresholdSteps(3)) {
            return SFPS_STAGE_LEFT_EDGE;
        } else {
            return SFPS_STAGE_RIGHT_EDGE;
        }
        return mSfpsEnrollmentFeature
                .getCurrentSfpsEnrollStage(progressSteps, this::getStageThresholdSteps);
    }

    private boolean isStageHalfCompleted() {
@@ -740,22 +750,31 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
            Log.w(TAG, "getStageThresholdSteps: Enrollment not started yet");
            return 1;
        }
        return Math.round(mSidecar.getEnrollmentSteps()
                * mFingerprintManager.getEnrollStageThreshold(index));
        final float threshold = mCanAssumeSfps
                ? mSfpsEnrollmentFeature.getEnrollStageThreshold(this, index)
                : mFingerprintManager.getEnrollStageThreshold(index);
        return Math.round(mSidecar.getEnrollmentSteps() * threshold);
    }

    @Override
    public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
        if (!TextUtils.isEmpty(helpString)) {
        final CharSequence featuredString = mCanAssumeSfps
                ? mSfpsEnrollmentFeature.getFeaturedVendorString(this, helpMsgId, helpString)
                : helpString;

        if (!TextUtils.isEmpty(featuredString)) {
            if (!(mCanAssumeUdfps || mCanAssumeSfps)) {
                mErrorText.removeCallbacks(mTouchAgainRunnable);
            }
            showError(helpString);
            showError(featuredString);

            if (mUdfpsEnrollHelper != null) mUdfpsEnrollHelper.onEnrollmentHelp();
        }

        dismissTouchDialogIfSfps();
        if (mCanAssumeSfps) {
            mSfpsEnrollmentFeature.handleOnEnrollmentHelp(helpMsgId, featuredString, () -> this);
        }
    }

    @Override
@@ -1170,4 +1189,28 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
            return SettingsEnums.DIALOG_FINGERPRINT_ICON_TOUCH;
        }
    }

    private static class EmptySfpsEnrollmentFeature implements SfpsEnrollmentFeature {
        private final String exceptionStr = "Assume sfps but no SfpsEnrollmentFeature impl.";

        @Override
        public int getCurrentSfpsEnrollStage(int progressSteps, Function<Integer, Integer> mapper) {
            throw new IllegalStateException(exceptionStr);
        }

        @Override
        public int getFeaturedStageHeaderResource(int stage) {
            throw new IllegalStateException(exceptionStr);
        }

        @Override
        public int getSfpsEnrollLottiePerStage(int stage) {
            throw new IllegalStateException(exceptionStr);
        }

        @Override
        public float getEnrollStageThreshold(@NonNull Context context, int index) {
            throw new IllegalStateException(exceptionStr);
        }
    }
}
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.biometrics.fingerprint;

import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;

public interface FingerprintFeatureProvider {
    /**
     * Gets the feature implementation of SFPS enrollment.
     * @return the feature implementation
     */
    SfpsEnrollmentFeature getSfpsEnrollmentFeature();
}
+36 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.biometrics.fingerprint;

import androidx.annotation.Nullable;

import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeatureImpl;

public class FingerprintFeatureProviderImpl implements FingerprintFeatureProvider {

    @Nullable
    private SfpsEnrollmentFeature mSfpsEnrollmentFeatureImpl = null;

    @Override
    public SfpsEnrollmentFeature getSfpsEnrollmentFeature() {
        if (mSfpsEnrollmentFeatureImpl == null) {
            mSfpsEnrollmentFeatureImpl = new SfpsEnrollmentFeatureImpl();
        }
        return mSfpsEnrollmentFeatureImpl;
    }
}
Loading