1 /*
2  * Copyright (C) 2012 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 android.view.WindowInsets.Type.ime;
20 
21 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
22 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
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_TIMEOUT;
26 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
27 
28 import android.animation.Animator;
29 import android.animation.AnimatorListenerAdapter;
30 import android.animation.ValueAnimator;
31 import android.content.Context;
32 import android.graphics.Insets;
33 import android.graphics.Rect;
34 import android.util.AttributeSet;
35 import android.view.WindowInsetsAnimationControlListener;
36 import android.view.WindowInsetsAnimationController;
37 import android.view.animation.AnimationUtils;
38 import android.view.animation.Interpolator;
39 import android.widget.TextView;
40 
41 import androidx.annotation.NonNull;
42 import androidx.annotation.Nullable;
43 
44 import com.android.internal.widget.LockscreenCredential;
45 import com.android.internal.widget.TextViewInputDisabler;
46 import com.android.systemui.R;
47 import com.android.systemui.animation.Interpolators;
48 /**
49  * Displays an alphanumeric (latin-1) key entry for the user to enter
50  * an unlock password
51  */
52 public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
53 
54     private final int mDisappearYTranslation;
55 
56     private static final long IME_DISAPPEAR_DURATION_MS = 125;
57 
58     // A delay constant to be used in a workaround for the situation where InputMethodManagerService
59     // is not switched to the new user yet.
60     // TODO: Remove this by ensuring such a race condition never happens.
61 
62     private TextView mPasswordEntry;
63     private TextViewInputDisabler mPasswordEntryDisabler;
64 
65     private Interpolator mLinearOutSlowInInterpolator;
66     private Interpolator mFastOutLinearInInterpolator;
67 
KeyguardPasswordView(Context context)68     public KeyguardPasswordView(Context context) {
69         this(context, null);
70     }
71 
KeyguardPasswordView(Context context, AttributeSet attrs)72     public KeyguardPasswordView(Context context, AttributeSet attrs) {
73         super(context, attrs);
74         mDisappearYTranslation = getResources().getDimensionPixelSize(
75                 R.dimen.disappear_y_translation);
76         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
77                 context, android.R.interpolator.linear_out_slow_in);
78         mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
79                 context, android.R.interpolator.fast_out_linear_in);
80     }
81 
82     @Override
resetState()83     protected void resetState() {
84     }
85 
86     @Override
getPasswordTextViewId()87     protected int getPasswordTextViewId() {
88         return R.id.passwordEntry;
89     }
90 
91     @Override
getPromptReasonStringRes(int reason)92     protected int getPromptReasonStringRes(int reason) {
93         switch (reason) {
94             case PROMPT_REASON_RESTART:
95                 return R.string.kg_prompt_reason_restart_password;
96             case PROMPT_REASON_TIMEOUT:
97                 return R.string.kg_prompt_reason_timeout_password;
98             case PROMPT_REASON_DEVICE_ADMIN:
99                 return R.string.kg_prompt_reason_device_admin;
100             case PROMPT_REASON_USER_REQUEST:
101                 return R.string.kg_prompt_reason_user_request;
102             case PROMPT_REASON_PREPARE_FOR_UPDATE:
103                 return R.string.kg_prompt_reason_timeout_password;
104             case PROMPT_REASON_NONE:
105                 return 0;
106             default:
107                 return R.string.kg_prompt_reason_timeout_password;
108         }
109     }
110 
111 
112     @Override
onFinishInflate()113     protected void onFinishInflate() {
114         super.onFinishInflate();
115 
116         mPasswordEntry = findViewById(getPasswordTextViewId());
117         mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
118     }
119 
120     @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)121     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
122         // send focus to the password field
123         return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
124     }
125 
126     @Override
resetPasswordText(boolean animate, boolean announce)127     protected void resetPasswordText(boolean animate, boolean announce) {
128         mPasswordEntry.setText("");
129     }
130 
131     @Override
getEnteredCredential()132     protected LockscreenCredential getEnteredCredential() {
133         return LockscreenCredential.createPasswordOrNone(mPasswordEntry.getText());
134     }
135 
136     @Override
setPasswordEntryEnabled(boolean enabled)137     protected void setPasswordEntryEnabled(boolean enabled) {
138         mPasswordEntry.setEnabled(enabled);
139     }
140 
141     @Override
setPasswordEntryInputEnabled(boolean enabled)142     protected void setPasswordEntryInputEnabled(boolean enabled) {
143         mPasswordEntryDisabler.setInputEnabled(enabled);
144     }
145 
146     @Override
getWrongPasswordStringId()147     public int getWrongPasswordStringId() {
148         return R.string.kg_wrong_password;
149     }
150 
151     @Override
startAppearAnimation()152     public void startAppearAnimation() {
153         // Reset state, and let IME animation reveal the view as it slides in, if one exists.
154         // It is possible for an IME to have no view, so provide a default animation since no
155         // calls to animateForIme would occur
156         setAlpha(0f);
157         animate()
158             .alpha(1f)
159             .setDuration(500)
160             .setStartDelay(300)
161             .start();
162 
163         setTranslationY(0f);
164     }
165 
166     @Override
startDisappearAnimation(Runnable finishRunnable)167     public boolean startDisappearAnimation(Runnable finishRunnable) {
168         getWindowInsetsController().controlWindowInsetsAnimation(ime(),
169                 100,
170                 Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
171 
172                     @Override
173                     public void onReady(@NonNull WindowInsetsAnimationController controller,
174                             int types) {
175                         ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
176                         anim.addUpdateListener(animation -> {
177                             if (controller.isCancelled()) {
178                                 return;
179                             }
180                             Insets shownInsets = controller.getShownStateInsets();
181                             Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
182                                     (int) (-shownInsets.bottom / 4
183                                             * anim.getAnimatedFraction())));
184                             controller.setInsetsAndAlpha(insets,
185                                     (float) animation.getAnimatedValue(),
186                                     anim.getAnimatedFraction());
187                         });
188                         anim.addListener(new AnimatorListenerAdapter() {
189                             @Override
190                             public void onAnimationStart(Animator animation) {
191                             }
192 
193                             @Override
194                             public void onAnimationEnd(Animator animation) {
195                                 controller.finish(false);
196                                 runOnFinishImeAnimationRunnable();
197                                 finishRunnable.run();
198                             }
199                         });
200                         anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
201                         anim.start();
202                     }
203 
204                     @Override
205                     public void onFinished(
206                             @NonNull WindowInsetsAnimationController controller) {
207                     }
208 
209                     @Override
210                     public void onCancelled(
211                             @Nullable WindowInsetsAnimationController controller) {
212                         // It is possible to be denied control of ime insets, which means onReady
213                         // is never called. We still need to notify the runnables in order to
214                         // complete the bouncer disappearing
215                         runOnFinishImeAnimationRunnable();
216                         finishRunnable.run();
217                     }
218                 });
219         return true;
220     }
221 
222 
223     @Override
animateForIme(float interpolatedFraction, boolean appearingAnim)224     public void animateForIme(float interpolatedFraction, boolean appearingAnim) {
225         animate().cancel();
226         setAlpha(appearingAnim
227                 ? Math.max(interpolatedFraction, getAlpha())
228                 : 1 - interpolatedFraction);
229     }
230 
231     @Override
getTitle()232     public CharSequence getTitle() {
233         return getResources().getString(
234                 com.android.internal.R.string.keyguard_accessibility_password_unlock);
235     }
236 }
237