• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.keyguard;
18 
19 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_ADAPTIVE_AUTH_REQUEST;
20 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
21 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
22 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT;
23 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
24 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
25 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE;
26 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
27 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
28 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
29 import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
30 
31 import android.animation.Animator;
32 import android.animation.AnimatorSet;
33 import android.animation.ValueAnimator;
34 import android.content.Context;
35 import android.graphics.Rect;
36 import android.util.AttributeSet;
37 import android.view.KeyEvent;
38 import android.view.View;
39 
40 import androidx.annotation.CallSuper;
41 
42 import com.android.app.animation.Interpolators;
43 import com.android.internal.widget.LockscreenCredential;
44 import com.android.systemui.res.R;
45 
46 import java.util.ArrayList;
47 import java.util.List;
48 
49 /**
50  * A Pin based Keyguard input view
51  */
52 public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView {
53 
54     protected PasswordTextView mPasswordEntry;
55     private NumPadButton mOkButton;
56     private NumPadButton mDeleteButton;
57     private NumPadKey[] mButtons = new NumPadKey[10];
58 
KeyguardPinBasedInputView(Context context)59     public KeyguardPinBasedInputView(Context context) {
60         this(context, null);
61     }
62 
KeyguardPinBasedInputView(Context context, AttributeSet attrs)63     public KeyguardPinBasedInputView(Context context, AttributeSet attrs) {
64         super(context, attrs);
65     }
66 
67     @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)68     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
69         // send focus to the password field
70         return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
71     }
72 
73     @Override
resetState()74     protected void resetState() {
75     }
76 
77     @Override
setPasswordEntryEnabled(boolean enabled)78     protected void setPasswordEntryEnabled(boolean enabled) {
79         mPasswordEntry.setEnabled(enabled);
80         mOkButton.setEnabled(enabled);
81         if (enabled && !mPasswordEntry.hasFocus()) {
82             mPasswordEntry.requestFocus();
83         }
84     }
85 
86     @Override
setPasswordEntryInputEnabled(boolean enabled)87     protected void setPasswordEntryInputEnabled(boolean enabled) {
88         mPasswordEntry.setEnabled(enabled);
89         mOkButton.setEnabled(enabled);
90         if (enabled) {
91             mPasswordEntry.requestFocus();
92         }
93     }
94 
95     @Override
onKeyDown(int keyCode, KeyEvent event)96     public boolean onKeyDown(int keyCode, KeyEvent event) {
97         if (keyCode == KeyEvent.KEYCODE_DEL) {
98             mDeleteButton.performClick();
99             return true;
100         }
101         if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
102             int number = keyCode - KeyEvent.KEYCODE_0;
103             performNumberClick(number);
104             return true;
105         }
106         if (keyCode >= KeyEvent.KEYCODE_NUMPAD_0 && keyCode <= KeyEvent.KEYCODE_NUMPAD_9) {
107             int number = keyCode - KeyEvent.KEYCODE_NUMPAD_0;
108             performNumberClick(number);
109             return true;
110         }
111         return super.onKeyDown(keyCode, event);
112     }
113 
114     @Override
onKeyUp(int keyCode, KeyEvent event)115     public boolean onKeyUp(int keyCode, KeyEvent event) {
116         if (KeyEvent.isConfirmKey(keyCode)) {
117             mOkButton.performClick();
118             return true;
119         }
120         return super.onKeyUp(keyCode, event);
121     }
122 
123     @Override
getPromptReasonStringRes(int reason)124     protected int getPromptReasonStringRes(int reason) {
125         switch (reason) {
126             case PROMPT_REASON_RESTART:
127                 return R.string.kg_prompt_reason_restart_pin;
128             case PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE:
129                 return R.string.kg_prompt_after_update_pin;
130             case PROMPT_REASON_TIMEOUT:
131                 return R.string.kg_prompt_reason_timeout_pin;
132             case PROMPT_REASON_DEVICE_ADMIN:
133                 return R.string.kg_prompt_reason_device_admin;
134             case PROMPT_REASON_USER_REQUEST:
135                 return R.string.kg_prompt_after_user_lockdown_pin;
136             case PROMPT_REASON_PREPARE_FOR_UPDATE:
137                 return R.string.kg_prompt_added_security_pin;
138             case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
139                 return R.string.kg_prompt_reason_timeout_pin;
140             case PROMPT_REASON_TRUSTAGENT_EXPIRED:
141                 return R.string.kg_prompt_reason_timeout_pin;
142             case PROMPT_REASON_ADAPTIVE_AUTH_REQUEST:
143                 return R.string.kg_prompt_after_adaptive_auth_lock;
144             case PROMPT_REASON_NONE:
145                 return 0;
146             default:
147                 return R.string.kg_prompt_reason_timeout_pin;
148         }
149     }
150 
performNumberClick(int number)151     private void performNumberClick(int number) {
152         if (number >= 0 && number <= 9) {
153             mButtons[number].performClick();
154         }
155     }
156 
157     @Override
resetPasswordText(boolean animate, boolean announce)158     protected void resetPasswordText(boolean animate, boolean announce) {
159         mPasswordEntry.reset(animate, announce);
160     }
161 
162     @Override
getEnteredCredential()163     protected LockscreenCredential getEnteredCredential() {
164         return LockscreenCredential.createPinOrNone(mPasswordEntry.getText());
165     }
166 
167     @Override
168     @CallSuper
onFinishInflate()169     protected void onFinishInflate() {
170         super.onFinishInflate();
171         mPasswordEntry = findViewById(getPasswordTextViewId());
172 
173         // Set selected property on so the view can send accessibility events.
174         mPasswordEntry.setSelected(true);
175         if (!pinInputFieldStyledFocusState()) {
176             mPasswordEntry.setDefaultFocusHighlightEnabled(false);
177         }
178 
179         mOkButton = findViewById(R.id.key_enter);
180 
181         mDeleteButton = findViewById(R.id.delete_button);
182         mDeleteButton.setVisibility(View.VISIBLE);
183 
184         mButtons[0] = findViewById(R.id.key0);
185         mButtons[1] = findViewById(R.id.key1);
186         mButtons[2] = findViewById(R.id.key2);
187         mButtons[3] = findViewById(R.id.key3);
188         mButtons[4] = findViewById(R.id.key4);
189         mButtons[5] = findViewById(R.id.key5);
190         mButtons[6] = findViewById(R.id.key6);
191         mButtons[7] = findViewById(R.id.key7);
192         mButtons[8] = findViewById(R.id.key8);
193         mButtons[9] = findViewById(R.id.key9);
194 
195         mPasswordEntry.requestFocus();
196         super.onFinishInflate();
197         reloadColors();
198     }
199 
getButtons()200     NumPadKey[] getButtons() {
201         return mButtons;
202     }
203 
204     /**
205      * Reload colors from resources.
206      **/
reloadColors()207     public void reloadColors() {
208         for (NumPadKey key : mButtons) {
209             key.reloadColors();
210         }
211         mPasswordEntry.reloadColors();
212         mDeleteButton.reloadColors();
213         mOkButton.reloadColors();
214     }
215 
216     @Override
getTitle()217     public CharSequence getTitle() {
218         return getContext().getString(
219                 com.android.internal.R.string.keyguard_accessibility_pin_unlock);
220     }
221 
222     /**
223      * Begins an error animation for this view.
224      **/
startErrorAnimation()225     public void startErrorAnimation() {
226         AnimatorSet animatorSet = new AnimatorSet();
227         List<Animator> animators = new ArrayList();
228         List<View> buttons = new ArrayList<>();
229         for (int i = 1; i <= 9; i++) {
230             buttons.add(mButtons[i]);
231         }
232         buttons.add(mDeleteButton);
233         buttons.add(mButtons[0]);
234         buttons.add(mOkButton);
235 
236         int delay = 0;
237         for (int i = 0; i < buttons.size(); i++) {
238             final View button = buttons.get(i);
239             AnimatorSet animateWrapper = new AnimatorSet();
240             animateWrapper.setStartDelay(delay);
241 
242             ValueAnimator scaleDownAnimator =  ValueAnimator.ofFloat(1f, 0.8f);
243             scaleDownAnimator.setInterpolator(Interpolators.STANDARD);
244             scaleDownAnimator.addUpdateListener(valueAnimator -> {
245                 button.setScaleX((float) valueAnimator.getAnimatedValue());
246                 button.setScaleY((float) valueAnimator.getAnimatedValue());
247             });
248             scaleDownAnimator.setDuration(50);
249 
250             ValueAnimator scaleUpAnimator =  ValueAnimator.ofFloat(0.8f, 1f);
251             scaleUpAnimator.setInterpolator(Interpolators.STANDARD);
252             scaleUpAnimator.addUpdateListener(valueAnimator -> {
253                 button.setScaleX((float) valueAnimator.getAnimatedValue());
254                 button.setScaleY((float) valueAnimator.getAnimatedValue());
255             });
256             scaleUpAnimator.setDuration(617);
257 
258             animateWrapper.playSequentially(scaleDownAnimator, scaleUpAnimator);
259             animators.add(animateWrapper);
260             delay += 33;
261         }
262         animatorSet.playTogether(animators);
263         animatorSet.start();
264     }
265 }
266