• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.biometrics.face;
18 
19 import android.app.settings.SettingsEnums;
20 import android.content.Intent;
21 import android.hardware.face.FaceManager;
22 import android.os.Bundle;
23 import android.text.TextUtils;
24 import android.util.Log;
25 import android.view.View;
26 import android.view.animation.AnimationUtils;
27 import android.view.animation.Interpolator;
28 import android.widget.TextView;
29 
30 import com.android.settings.R;
31 import com.android.settings.biometrics.BiometricEnrollBase;
32 import com.android.settings.biometrics.BiometricEnrollSidecar;
33 import com.android.settings.biometrics.BiometricErrorDialog;
34 import com.android.settings.biometrics.BiometricsEnrollEnrolling;
35 
36 import com.google.android.setupcompat.template.FooterBarMixin;
37 import com.google.android.setupcompat.template.FooterButton;
38 
39 import java.util.ArrayList;
40 
41 public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling {
42 
43     private static final String TAG = "FaceEnrollEnrolling";
44     private static final boolean DEBUG = false;
45     private static final String TAG_FACE_PREVIEW = "tag_preview";
46 
47     private TextView mErrorText;
48     private Interpolator mLinearOutSlowInInterpolator;
49     private FaceEnrollPreviewFragment mPreviewFragment;
50 
51     private ArrayList<Integer> mDisabledFeatures = new ArrayList<>();
52     private ParticleCollection.Listener mListener = new ParticleCollection.Listener() {
53         @Override
54         public void onEnrolled() {
55             FaceEnrollEnrolling.this.launchFinish(mToken);
56         }
57     };
58 
59     public static class FaceErrorDialog extends BiometricErrorDialog {
newInstance(CharSequence msg, int msgId)60         static FaceErrorDialog newInstance(CharSequence msg, int msgId) {
61             FaceErrorDialog dialog = new FaceErrorDialog();
62             Bundle args = new Bundle();
63             args.putCharSequence(KEY_ERROR_MSG, msg);
64             args.putInt(KEY_ERROR_ID, msgId);
65             dialog.setArguments(args);
66             return dialog;
67         }
68 
69         @Override
getMetricsCategory()70         public int getMetricsCategory() {
71             return SettingsEnums.DIALOG_FACE_ERROR;
72         }
73 
74         @Override
getTitleResId()75         public int getTitleResId() {
76             return R.string.security_settings_face_enroll_error_dialog_title;
77         }
78 
79         @Override
getOkButtonTextResId()80         public int getOkButtonTextResId() {
81             return R.string.security_settings_face_enroll_dialog_ok;
82         }
83     }
84 
85     @Override
onCreate(Bundle savedInstanceState)86     protected void onCreate(Bundle savedInstanceState) {
87         super.onCreate(savedInstanceState);
88         setContentView(R.layout.face_enroll_enrolling);
89         setHeaderText(R.string.security_settings_face_enroll_repeat_title);
90         mErrorText = findViewById(R.id.error_text);
91         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
92                 this, android.R.interpolator.linear_out_slow_in);
93 
94         mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
95         mFooterBarMixin.setSecondaryButton(
96                 new FooterButton.Builder(this)
97                         .setText(R.string.security_settings_face_enroll_enrolling_skip)
98                         .setListener(this::onSkipButtonClick)
99                         .setButtonType(FooterButton.ButtonType.SKIP)
100                         .setTheme(R.style.SudGlifButton_Secondary)
101                         .build()
102         );
103 
104         if (!getIntent().getBooleanExtra(BiometricEnrollBase.EXTRA_KEY_REQUIRE_DIVERSITY, true)) {
105             mDisabledFeatures.add(FaceManager.FEATURE_REQUIRE_REQUIRE_DIVERSITY);
106         }
107         if (!getIntent().getBooleanExtra(BiometricEnrollBase.EXTRA_KEY_REQUIRE_VISION, true)) {
108             mDisabledFeatures.add(FaceManager.FEATURE_REQUIRE_ATTENTION);
109         }
110 
111         startEnrollment();
112     }
113 
114     @Override
startEnrollment()115     public void startEnrollment() {
116         super.startEnrollment();
117         mPreviewFragment = (FaceEnrollPreviewFragment) getSupportFragmentManager()
118                 .findFragmentByTag(TAG_FACE_PREVIEW);
119         if (mPreviewFragment == null) {
120             mPreviewFragment = new FaceEnrollPreviewFragment();
121             getSupportFragmentManager().beginTransaction().add(mPreviewFragment, TAG_FACE_PREVIEW)
122                     .commitAllowingStateLoss();
123         }
124         mPreviewFragment.setListener(mListener);
125     }
126 
127     @Override
getFinishIntent()128     protected Intent getFinishIntent() {
129         return new Intent(this, FaceEnrollFinish.class);
130     }
131 
132     @Override
getSidecar()133     protected BiometricEnrollSidecar getSidecar() {
134         final int[] disabledFeatures = new int[mDisabledFeatures.size()];
135         for (int i = 0; i < mDisabledFeatures.size(); i++) {
136             disabledFeatures[i] = mDisabledFeatures.get(i);
137         }
138 
139         return new FaceEnrollSidecar(disabledFeatures);
140     }
141 
142     @Override
shouldStartAutomatically()143     protected boolean shouldStartAutomatically() {
144         return false;
145     }
146 
147     @Override
getMetricsCategory()148     public int getMetricsCategory() {
149         return SettingsEnums.FACE_ENROLL_ENROLLING;
150     }
151 
152     @Override
onEnrollmentHelp(int helpMsgId, CharSequence helpString)153     public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
154         if (!TextUtils.isEmpty(helpString)) {
155             showError(helpString);
156         }
157         mPreviewFragment.onEnrollmentHelp(helpMsgId, helpString);
158     }
159 
160     @Override
onEnrollmentError(int errMsgId, CharSequence errString)161     public void onEnrollmentError(int errMsgId, CharSequence errString) {
162         int msgId;
163         switch (errMsgId) {
164             case FaceManager.FACE_ERROR_TIMEOUT:
165                 msgId = R.string.security_settings_face_enroll_error_timeout_dialog_message;
166                 break;
167             default:
168                 msgId = R.string.security_settings_face_enroll_error_generic_dialog_message;
169                 break;
170         }
171         mPreviewFragment.onEnrollmentError(errMsgId, errString);
172         showErrorDialog(getText(msgId), errMsgId);
173     }
174 
175     @Override
onEnrollmentProgressChange(int steps, int remaining)176     public void onEnrollmentProgressChange(int steps, int remaining) {
177         if (DEBUG) {
178             Log.v(TAG, "Steps: " + steps + " Remaining: " + remaining);
179         }
180         mPreviewFragment.onEnrollmentProgressChange(steps, remaining);
181 
182         // TODO: Update the actual animation
183         showError("Steps: " + steps + " Remaining: " + remaining);
184 
185         // TODO: Have this match any animations that UX comes up with
186         if (remaining == 0) {
187             launchFinish(mToken);
188         }
189     }
190 
showErrorDialog(CharSequence msg, int msgId)191     private void showErrorDialog(CharSequence msg, int msgId) {
192         BiometricErrorDialog dialog = FaceErrorDialog.newInstance(msg, msgId);
193         dialog.show(getSupportFragmentManager(), FaceErrorDialog.class.getName());
194     }
195 
showError(CharSequence error)196     private void showError(CharSequence error) {
197         mErrorText.setText(error);
198         if (mErrorText.getVisibility() == View.INVISIBLE) {
199             mErrorText.setVisibility(View.VISIBLE);
200             mErrorText.setTranslationY(getResources().getDimensionPixelSize(
201                     R.dimen.fingerprint_error_text_appear_distance));
202             mErrorText.setAlpha(0f);
203             mErrorText.animate()
204                     .alpha(1f)
205                     .translationY(0f)
206                     .setDuration(200)
207                     .setInterpolator(mLinearOutSlowInInterpolator)
208                     .start();
209         } else {
210             mErrorText.animate().cancel();
211             mErrorText.setAlpha(1f);
212             mErrorText.setTranslationY(0f);
213         }
214     }
215 }
216