1 /*
2  * Copyright (C) 2019 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.homepage.contextualcards.slices;
18 
19 
20 import android.app.PendingIntent;
21 import android.app.settings.SettingsEnums;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.hardware.face.FaceManager;
25 import android.net.Uri;
26 import android.os.UserHandle;
27 import android.provider.Settings;
28 
29 import androidx.core.graphics.drawable.IconCompat;
30 import androidx.slice.Slice;
31 import androidx.slice.builders.ListBuilder;
32 import androidx.slice.builders.ListBuilder.RowBuilder;
33 import androidx.slice.builders.SliceAction;
34 
35 import com.android.settings.R;
36 import com.android.settings.SubSettings;
37 import com.android.settings.Utils;
38 import com.android.settings.biometrics.face.FaceStatusPreferenceController;
39 import com.android.settings.homepage.contextualcards.FaceReEnrollDialog;
40 import com.android.settings.security.SecuritySettings;
41 import com.android.settings.slices.CustomSliceRegistry;
42 import com.android.settings.slices.CustomSliceable;
43 import com.android.settings.slices.SliceBuilderUtils;
44 
45 /**
46  * This class is used for showing re-enroll suggestions in the Settings page. By either having an
47  * un-enrolled user or setting {@link Settings.Secure#FACE_UNLOCK_RE_ENROLL} to one of the
48  * states listed in {@link Settings.Secure} the slice will change its text and potentially show
49  * a {@link FaceReEnrollDialog}.
50  */
51 public class FaceSetupSlice implements CustomSliceable {
52 
53     private final Context mContext;
54     /**
55      * If a user currently is not enrolled then this class will show a recommendation to
56      * enroll their face.
57      */
58     private FaceManager mFaceManager;
59 
60     /**
61      * Various states the {@link FaceSetupSlice} can be in,
62      * See {@link Settings.Secure#FACE_UNLOCK_RE_ENROLL} for more details.
63      */
64 
65     // No re-enrollment.
66     public static final int FACE_NO_RE_ENROLL_REQUIRED = 0;
67     // Re enrollment is suggested.
68     public static final int FACE_RE_ENROLL_SUGGESTED = 1;
69     // Re enrollment is required after a set time period.
70     public static final int FACE_RE_ENROLL_AFTER_TIMEOUT = 2;
71     // Re enrollment is required immediately.
72     public static final int FACE_RE_ENROLL_REQUIRED = 3;
73 
FaceSetupSlice(Context context)74     public FaceSetupSlice(Context context) {
75         mContext = context;
76     }
77 
78     @Override
getSlice()79     public Slice getSlice() {
80         mFaceManager = Utils.getFaceManagerOrNull(mContext);
81         if (mFaceManager == null) {
82             return new ListBuilder(mContext, CustomSliceRegistry.FACE_ENROLL_SLICE_URI,
83                     ListBuilder.INFINITY).setIsError(true).build();
84         }
85 
86         final int userId = UserHandle.myUserId();
87         final boolean hasEnrolledTemplates = mFaceManager.hasEnrolledTemplates(userId);
88         final int shouldReEnroll = FaceSetupSlice.getReEnrollSetting(mContext, userId);
89 
90         CharSequence title = "";
91         CharSequence subtitle = "";
92 
93         // Set the title and subtitle according to the different states, the icon and layout will
94         // stay the same.
95         if (!hasEnrolledTemplates) {
96             title = mContext.getText(R.string.security_settings_face_settings_enroll);
97             subtitle = mContext.getText(
98                     R.string.security_settings_face_settings_context_subtitle);
99         } else if (shouldReEnroll == FACE_RE_ENROLL_SUGGESTED) {
100             title = mContext.getText(
101                     R.string.security_settings_face_enroll_should_re_enroll_title);
102             subtitle = mContext.getText(
103                     R.string.security_settings_face_enroll_should_re_enroll_subtitle);
104         } else if (shouldReEnroll == FACE_RE_ENROLL_REQUIRED) {
105             title = mContext.getText(
106                     R.string.security_settings_face_enroll_must_re_enroll_title);
107             subtitle = mContext.getText(
108                     R.string.security_settings_face_enroll_must_re_enroll_subtitle);
109         } else {
110             return new ListBuilder(mContext, CustomSliceRegistry.FACE_ENROLL_SLICE_URI,
111                     ListBuilder.INFINITY).setIsError(true).build();
112         }
113 
114         final ListBuilder listBuilder = new ListBuilder(mContext,
115                 CustomSliceRegistry.FACE_ENROLL_SLICE_URI, ListBuilder.INFINITY)
116                 .setAccentColor(Utils.getColorAccentDefaultColor(mContext));
117         final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_face_24dp);
118         return listBuilder.addRow(buildRowBuilder(title, subtitle, icon, mContext, getIntent()))
119                 .build();
120     }
121 
122     @Override
getUri()123     public Uri getUri() {
124         return CustomSliceRegistry.FACE_ENROLL_SLICE_URI;
125     }
126 
127     @Override
getIntent()128     public Intent getIntent() {
129         final boolean hasEnrolledTemplates = mFaceManager.hasEnrolledTemplates(
130                 UserHandle.myUserId());
131         if (!hasEnrolledTemplates) {
132             return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
133                     SecuritySettings.class.getName(),
134                     FaceStatusPreferenceController.KEY_FACE_SETTINGS,
135                     mContext.getText(R.string.security_settings_face_settings_enroll).toString(),
136                     SettingsEnums.SLICE, this)
137                     .setClassName(mContext.getPackageName(), SubSettings.class.getName());
138         } else {
139             return new Intent(mContext, FaceReEnrollDialog.class);
140         }
141     }
142 
143     @Override
getSliceHighlightMenuRes()144     public int getSliceHighlightMenuRes() {
145         return R.string.menu_key_security;
146     }
147 
buildRowBuilder(CharSequence title, CharSequence subTitle, IconCompat icon, Context context, Intent intent)148     private static RowBuilder buildRowBuilder(CharSequence title, CharSequence subTitle,
149             IconCompat icon, Context context, Intent intent) {
150         final SliceAction primarySliceAction = SliceAction.createDeeplink(
151                 PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE),
152                 icon, ListBuilder.ICON_IMAGE, title);
153         return new RowBuilder()
154                 .setTitleItem(icon, ListBuilder.ICON_IMAGE)
155                 .setTitle(title)
156                 .setSubtitle(subTitle)
157                 .setPrimaryAction(primarySliceAction);
158     }
159 
getReEnrollSetting(Context context, int userId)160     public static int getReEnrollSetting(Context context, int userId) {
161         return Settings.Secure.getIntForUser(context.getContentResolver(),
162                 Settings.Secure.FACE_UNLOCK_RE_ENROLL, FACE_NO_RE_ENROLL_REQUIRED, userId);
163     }
164 
165 }