• 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.AlertDialog;
20 import android.app.Dialog;
21 import android.app.settings.SettingsEnums;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.hardware.face.Face;
25 import android.hardware.face.FaceManager;
26 import android.os.Bundle;
27 import android.util.Log;
28 import android.view.View;
29 import android.widget.Button;
30 import android.widget.Toast;
31 
32 import androidx.preference.Preference;
33 
34 import com.android.settings.R;
35 import com.android.settings.SettingsActivity;
36 import com.android.settings.biometrics.BiometricUtils;
37 import com.android.settings.core.BasePreferenceController;
38 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
39 import com.android.settings.overlay.FeatureFactory;
40 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
41 import com.android.settingslib.widget.LayoutPreference;
42 
43 import com.google.android.setupdesign.util.ButtonStyler;
44 import com.google.android.setupdesign.util.PartnerStyleHelper;
45 
46 import java.util.List;
47 
48 /**
49  * Controller for the remove button. This assumes that there is only a single face enrolled. The UI
50  * will likely change if multiple enrollments are allowed/supported.
51  */
52 public class FaceSettingsRemoveButtonPreferenceController extends BasePreferenceController
53         implements View.OnClickListener {
54 
55     private static final String TAG = "FaceSettings/Remove";
56     static final String KEY = "security_settings_face_delete_faces_container";
57 
58     public static class ConfirmRemoveDialog extends InstrumentedDialogFragment {
59 
60         private boolean mIsConvenience;
61         private DialogInterface.OnClickListener mOnClickListener;
62 
63         @Override
getMetricsCategory()64         public int getMetricsCategory() {
65             return SettingsEnums.DIALOG_FACE_REMOVE;
66         }
67 
68         @Override
onCreateDialog(Bundle savedInstanceState)69         public Dialog onCreateDialog(Bundle savedInstanceState) {
70             AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
71 
72             builder.setTitle(R.string.security_settings_face_settings_remove_dialog_title)
73                     .setMessage(mIsConvenience
74                             ? R.string.security_settings_face_settings_remove_dialog_details_convenience
75                             : R.string.security_settings_face_settings_remove_dialog_details)
76                     .setPositiveButton(R.string.delete, mOnClickListener)
77                     .setNegativeButton(R.string.cancel, mOnClickListener);
78             AlertDialog dialog = builder.create();
79             dialog.setCanceledOnTouchOutside(false);
80             return dialog;
81         }
82 
setIsConvenience(boolean isConvenience)83         public void setIsConvenience(boolean isConvenience) {
84             mIsConvenience = isConvenience;
85         }
86 
setOnClickListener(DialogInterface.OnClickListener listener)87         public void setOnClickListener(DialogInterface.OnClickListener listener) {
88             mOnClickListener = listener;
89         }
90     }
91 
92     interface Listener {
onRemoved()93         void onRemoved();
94     }
95 
96     private Preference mPreference;
97     private Button mButton;
98     private Listener mListener;
99     private SettingsActivity mActivity;
100     private int mUserId;
101     private boolean mRemoving;
102 
103     private final MetricsFeatureProvider mMetricsFeatureProvider;
104     private final Context mContext;
105     private final FaceManager mFaceManager;
106     private final FaceUpdater mFaceUpdater;
107     private final FaceManager.RemovalCallback mRemovalCallback = new FaceManager.RemovalCallback() {
108         @Override
109         public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
110             Log.e(TAG, "Unable to remove face: " + face.getBiometricId()
111                     + " error: " + errMsgId + " " + errString);
112             Toast.makeText(mContext, errString, Toast.LENGTH_SHORT).show();
113             mRemoving = false;
114         }
115 
116         @Override
117         public void onRemovalSucceeded(Face face, int remaining) {
118             if (remaining == 0) {
119                 final List<Face> faces = mFaceManager.getEnrolledFaces(mUserId);
120                 if (!faces.isEmpty()) {
121                     mButton.setEnabled(true);
122                 } else {
123                     mRemoving = false;
124                     mListener.onRemoved();
125                 }
126             } else {
127                 Log.v(TAG, "Remaining: " + remaining);
128             }
129         }
130     };
131 
132     private final DialogInterface.OnClickListener mOnClickListener
133             = new DialogInterface.OnClickListener() {
134         @Override
135         public void onClick(DialogInterface dialog, int which) {
136             if (which == DialogInterface.BUTTON_POSITIVE) {
137                 mButton.setEnabled(false);
138                 final List<Face> faces = mFaceManager.getEnrolledFaces(mUserId);
139                 if (faces.isEmpty()) {
140                     Log.e(TAG, "No faces");
141                     return;
142                 }
143                 if (faces.size() > 1) {
144                     Log.e(TAG, "Multiple enrollments: " + faces.size());
145                 }
146 
147                 // Remove the first/only face
148                 mFaceUpdater.remove(faces.get(0), mUserId, mRemovalCallback);
149             } else {
150                 mButton.setEnabled(true);
151                 mRemoving = false;
152             }
153         }
154     };
155 
FaceSettingsRemoveButtonPreferenceController(Context context, String preferenceKey)156     public FaceSettingsRemoveButtonPreferenceController(Context context, String preferenceKey) {
157         super(context, preferenceKey);
158         mContext = context;
159         mFaceManager = context.getSystemService(FaceManager.class);
160         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
161         mFaceUpdater = new FaceUpdater(context, mFaceManager);
162     }
163 
FaceSettingsRemoveButtonPreferenceController(Context context)164     public FaceSettingsRemoveButtonPreferenceController(Context context) {
165         this(context, KEY);
166     }
167 
setUserId(int userId)168     public void setUserId(int userId) {
169         mUserId = userId;
170     }
171 
172     @Override
updateState(Preference preference)173     public void updateState(Preference preference) {
174         super.updateState(preference);
175 
176         mPreference = preference;
177         mButton = ((LayoutPreference) preference)
178                 .findViewById(R.id.security_settings_face_settings_remove_button);
179 
180         if (PartnerStyleHelper.shouldApplyPartnerResource(mButton)) {
181             ButtonStyler.applyPartnerCustomizationPrimaryButtonStyle(mContext, mButton);
182         }
183 
184         mButton.setOnClickListener(this);
185 
186         if (!FaceSettings.isFaceHardwareDetected(mContext)) {
187             mButton.setEnabled(false);
188         } else {
189             mButton.setEnabled(!mRemoving);
190         }
191     }
192 
193     @Override
getAvailabilityStatus()194     public int getAvailabilityStatus() {
195         return AVAILABLE;
196     }
197 
198     @Override
getPreferenceKey()199     public String getPreferenceKey() {
200         return KEY;
201     }
202 
203     @Override
onClick(View v)204     public void onClick(View v) {
205         if (v == mButton) {
206             mMetricsFeatureProvider.logClickedPreference(mPreference, getMetricsCategory());
207             mRemoving = true;
208             ConfirmRemoveDialog dialog = new ConfirmRemoveDialog();
209             dialog.setOnClickListener(mOnClickListener);
210             dialog.setIsConvenience(BiometricUtils.isConvenience(mFaceManager));
211             dialog.show(mActivity.getSupportFragmentManager(), ConfirmRemoveDialog.class.getName());
212         }
213     }
214 
setListener(Listener listener)215     public void setListener(Listener listener) {
216         mListener = listener;
217     }
218 
setActivity(SettingsActivity activity)219     public void setActivity(SettingsActivity activity) {
220         mActivity = activity;
221     }
222 }
223