/* * Copyright (C) 2022 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.biometrics2.ui.viewmodel; import static com.android.settings.biometrics2.ui.model.FingerprintEnrollable.FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX; import static com.android.settings.biometrics2.ui.model.FingerprintEnrollable.FINGERPRINT_ENROLLABLE_OK; import static com.android.settings.biometrics2.ui.model.FingerprintEnrollable.FINGERPRINT_ENROLLABLE_UNKNOWN; import android.annotation.IntDef; import android.app.Application; import android.util.Log; import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import androidx.lifecycle.MediatorLiveData; import androidx.lifecycle.MutableLiveData; import com.android.settings.biometrics2.data.repository.FingerprintRepository; import com.android.settings.biometrics2.ui.model.EnrollmentRequest; import com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroStatus; import com.android.settings.biometrics2.ui.model.FingerprintEnrollable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Fingerprint intro onboarding page view model implementation */ public class FingerprintEnrollIntroViewModel extends AndroidViewModel { private static final String TAG = "FingerprintEnrollIntroViewModel"; private static final boolean HAS_SCROLLED_TO_BOTTOM_DEFAULT = false; private static final FingerprintEnrollable ENROLLABLE_STATUS_DEFAULT = FINGERPRINT_ENROLLABLE_UNKNOWN; /** * User clicks 'Done' button on this page */ public static final int FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH = 0; /** * User clicks 'Agree' button on this page */ public static final int FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL = 1; /** * User clicks 'Skip' button on this page */ public static final int FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL = 2; @IntDef(prefix = { "FINGERPRINT_ENROLL_INTRO_ACTION_" }, value = { FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH, FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL, FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL }) @Retention(RetentionPolicy.SOURCE) public @interface FingerprintEnrollIntroAction {} @NonNull private final FingerprintRepository mFingerprintRepository; private final MutableLiveData mHasScrolledToBottomLiveData = new MutableLiveData<>(HAS_SCROLLED_TO_BOTTOM_DEFAULT); private final MutableLiveData mEnrollableStatusLiveData = new MutableLiveData<>(ENROLLABLE_STATUS_DEFAULT); private final MediatorLiveData mPageStatusLiveData = new MediatorLiveData<>(); private final MutableLiveData mActionLiveData = new MutableLiveData<>(); private final int mUserId; @NonNull private final EnrollmentRequest mRequest; public FingerprintEnrollIntroViewModel(@NonNull Application application, @NonNull FingerprintRepository fingerprintRepository, @NonNull EnrollmentRequest request, int userId) { super(application); mFingerprintRepository = fingerprintRepository; mRequest = request; mUserId = userId; mPageStatusLiveData.addSource( mEnrollableStatusLiveData, enrollable -> { final Boolean toBottomValue = mHasScrolledToBottomLiveData.getValue(); final FingerprintEnrollIntroStatus status = new FingerprintEnrollIntroStatus( toBottomValue != null ? toBottomValue : HAS_SCROLLED_TO_BOTTOM_DEFAULT, enrollable); mPageStatusLiveData.setValue(status); }); mPageStatusLiveData.addSource( mHasScrolledToBottomLiveData, hasScrolledToBottom -> { final FingerprintEnrollable enrollableValue = mEnrollableStatusLiveData.getValue(); final FingerprintEnrollIntroStatus status = new FingerprintEnrollIntroStatus( hasScrolledToBottom, enrollableValue != null ? enrollableValue : ENROLLABLE_STATUS_DEFAULT); mPageStatusLiveData.setValue(status); }); } /** * Get enrollment request */ public EnrollmentRequest getRequest() { return mRequest; } private void updateEnrollableStatus() { final int num = mFingerprintRepository.getNumOfEnrolledFingerprintsSize(mUserId); final int max = mRequest.isSuw() && !mRequest.isAfterSuwOrSuwSuggestedAction() ? mFingerprintRepository.getMaxFingerprintsInSuw(getApplication().getResources()) : mFingerprintRepository.getMaxFingerprints(); mEnrollableStatusLiveData.postValue(num >= max ? FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX : FINGERPRINT_ENROLLABLE_OK); } /** * Get enrollable status and hasScrollToBottom live data */ public LiveData getPageStatusLiveData() { updateEnrollableStatus(); return mPageStatusLiveData; } /** * Clear user's action live data */ public void clearActionLiveData() { mActionLiveData.setValue(null); } /** * Get user's action live data (like clicking Agree, Skip, or Done) */ public LiveData getActionLiveData() { return mActionLiveData; } /** * The first sensor type is UDFPS sensor or not */ public boolean canAssumeUdfps() { return mFingerprintRepository.canAssumeUdfps(); } /** * Update onboarding intro page has scrolled to bottom */ public void setHasScrolledToBottom(boolean value) { mHasScrolledToBottomLiveData.postValue(value); } /** * Get parental consent required or not during enrollment process */ public boolean isParentalConsentRequired() { return mFingerprintRepository.isParentalConsentRequired(getApplication()); } /** * Get fingerprint is disable by admin or not */ public boolean isBiometricUnlockDisabledByAdmin() { return mFingerprintRepository.isDisabledByAdmin(getApplication(), mUserId); } /** * User clicks next button */ public void onNextButtonClick() { final FingerprintEnrollable status = mEnrollableStatusLiveData.getValue(); switch (status != null ? status : ENROLLABLE_STATUS_DEFAULT) { case FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX: mActionLiveData.postValue(FINGERPRINT_ENROLL_INTRO_ACTION_DONE_AND_FINISH); break; case FINGERPRINT_ENROLLABLE_OK: mActionLiveData.postValue(FINGERPRINT_ENROLL_INTRO_ACTION_CONTINUE_ENROLL); break; default: Log.w(TAG, "fail to click next, enrolled:" + status); } } /** * User clicks skip/cancel button */ public void onSkipOrCancelButtonClick() { mActionLiveData.postValue(FINGERPRINT_ENROLL_INTRO_ACTION_SKIP_OR_CANCEL); } }