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