blob: e3ac0f684e44c62283c201b8531592bd5dbf7197 [file] [log] [blame]
Jim Miller5e0f7ba2009-12-22 19:04:23 -08001/*
Jim Millerdcb3d842012-08-23 19:18:12 -07002 * Copyright (C) 2012 The Android Open Source Project
Jim Miller5e0f7ba2009-12-22 19:04:23 -08003 *
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
Jim Miller5ecd8112013-01-09 18:50:26 -080017package com.android.keyguard;
Jim Miller5e0f7ba2009-12-22 19:04:23 -080018
Michael Jurka1254f2f2012-10-25 11:44:31 -070019import android.content.Context;
Selim Cinek4e8b9ed2014-06-20 16:37:04 -070020import android.graphics.Rect;
Yohei Yukawa72769462019-01-20 09:28:08 -080021import android.os.UserHandle;
Jim Miller8ecfac12011-07-08 19:15:51 -070022import android.text.Editable;
Jim Millerda3ae8f2011-04-14 15:13:49 -070023import android.text.InputType;
Xiyuan Xia09eb0332015-05-13 15:29:42 -070024import android.text.TextUtils;
Jim Miller8ecfac12011-07-08 19:15:51 -070025import android.text.TextWatcher;
Jim Miller8b9dda22010-03-02 17:24:48 -080026import android.text.method.TextKeyListener;
Michael Jurka1254f2f2012-10-25 11:44:31 -070027import android.util.AttributeSet;
Selim Cinek4e8b9ed2014-06-20 16:37:04 -070028import android.view.KeyEvent;
Michael Jurka1254f2f2012-10-25 11:44:31 -070029import android.view.View;
Jorim Jaggie0700182014-08-21 01:12:37 +020030import android.view.animation.AnimationUtils;
31import android.view.animation.Interpolator;
Selim Cinek4e8b9ed2014-06-20 16:37:04 -070032import android.view.inputmethod.EditorInfo;
Jim Miller858f8ea2011-08-19 14:50:34 -070033import android.view.inputmethod.InputMethodInfo;
34import android.view.inputmethod.InputMethodManager;
35import android.view.inputmethod.InputMethodSubtype;
Selim Cinek4e8b9ed2014-06-20 16:37:04 -070036import android.widget.TextView;
Jim Millera781d012010-02-05 18:59:25 -080037import android.widget.TextView.OnEditorActionListener;
38
Xiyuan Xiacead9192015-05-15 11:01:30 -070039import com.android.internal.widget.TextViewInputDisabler;
Sunny Goyal87fccf02019-08-13 17:39:10 -070040import com.android.systemui.R;
Xiyuan Xiacead9192015-05-15 11:01:30 -070041
Michael Jurka1254f2f2012-10-25 11:44:31 -070042import java.util.List;
Jim Miller5e0f7ba2009-12-22 19:04:23 -080043/**
Daniel Sandler2c1ce052012-10-24 23:19:34 -040044 * Displays an alphanumeric (latin-1) key entry for the user to enter
Jim Miller5e0f7ba2009-12-22 19:04:23 -080045 * an unlock password
46 */
Daniel Sandler69bdee72012-10-23 16:45:50 -040047public class KeyguardPasswordView extends KeyguardAbsKeyInputView
Jim Miller9cf2c522012-10-04 22:02:29 -070048 implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
Daniel Sandler69bdee72012-10-23 16:45:50 -040049
Chris Wrena042ac92012-11-07 11:37:06 -050050 private final boolean mShowImeAtScreenOn;
Jorim Jaggie0700182014-08-21 01:12:37 +020051 private final int mDisappearYTranslation;
Chris Wrena042ac92012-11-07 11:37:06 -050052
Yohei Yukawa982a94c2016-08-11 19:16:02 -070053 // A delay constant to be used in a workaround for the situation where InputMethodManagerService
54 // is not switched to the new user yet.
55 // TODO: Remove this by ensuring such a race condition never happens.
56 private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
57
Daniel Sandler2c1ce052012-10-24 23:19:34 -040058 InputMethodManager mImm;
Selim Cinek4e8b9ed2014-06-20 16:37:04 -070059 private TextView mPasswordEntry;
Xiyuan Xiacead9192015-05-15 11:01:30 -070060 private TextViewInputDisabler mPasswordEntryDisabler;
Yohei Yukawa982a94c2016-08-11 19:16:02 -070061 private View mSwitchImeButton;
Xiyuan Xiacead9192015-05-15 11:01:30 -070062
Jorim Jaggie0700182014-08-21 01:12:37 +020063 private Interpolator mLinearOutSlowInInterpolator;
64 private Interpolator mFastOutLinearInInterpolator;
Jim Millerac8f5752012-09-07 16:48:41 -070065
Jim Millerdcb3d842012-08-23 19:18:12 -070066 public KeyguardPasswordView(Context context) {
Chris Wrena042ac92012-11-07 11:37:06 -050067 this(context, null);
Jim Millerdcb3d842012-08-23 19:18:12 -070068 }
Jim Miller54759062010-03-11 15:46:29 -080069
Jim Millerdcb3d842012-08-23 19:18:12 -070070 public KeyguardPasswordView(Context context, AttributeSet attrs) {
71 super(context, attrs);
Chris Wrena042ac92012-11-07 11:37:06 -050072 mShowImeAtScreenOn = context.getResources().
73 getBoolean(R.bool.kg_show_ime_at_screen_on);
Jorim Jaggie0700182014-08-21 01:12:37 +020074 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);
Jim Millerdcb3d842012-08-23 19:18:12 -070080 }
81
Jim Miller4db942c2016-05-16 18:06:50 -070082 @Override
Daniel Sandler69bdee72012-10-23 16:45:50 -040083 protected void resetState() {
Yohei Yukawa72769462019-01-20 09:28:08 -080084 mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
Lucas Dupin2e838ac2019-04-17 16:50:58 -070085 if (mSecurityMessageDisplay != null) {
86 mSecurityMessageDisplay.setMessage("");
87 }
Xiyuan Xiade26ea62015-05-19 15:00:41 -070088 final boolean wasDisabled = mPasswordEntry.isEnabled();
lumarke98f04b2019-09-24 17:13:35 +080089 setPasswordEntryEnabled(true);
90 setPasswordEntryInputEnabled(true);
91 // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
lumarkd62bfd22019-01-10 15:29:11 +080092 if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
93 return;
94 }
Xiyuan Xiade26ea62015-05-19 15:00:41 -070095 if (wasDisabled) {
96 mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
97 }
Jim Miller08b2b6b2012-09-14 19:12:40 -070098 }
99
Jim Millerdcb3d842012-08-23 19:18:12 -0700100 @Override
Daniel Sandler8a26bf52012-10-30 13:29:50 -0400101 protected int getPasswordTextViewId() {
102 return R.id.passwordEntry;
103 }
104
105 @Override
Jim Miller86f96372012-10-24 20:14:47 -0700106 public boolean needsInput() {
107 return true;
108 }
109
110 @Override
Jorim Jaggie0700182014-08-21 01:12:37 +0200111 public void onResume(final int reason) {
Chris Wrena042ac92012-11-07 11:37:06 -0500112 super.onResume(reason);
Jorim Jaggie0700182014-08-21 01:12:37 +0200113
114 // Wait a bit to focus the field so the focusable flag on the window is already set then.
115 post(new Runnable() {
116 @Override
117 public void run() {
Xiyuan Xiade26ea62015-05-19 15:00:41 -0700118 if (isShown() && mPasswordEntry.isEnabled()) {
Adrian Roos991db772015-02-06 14:30:46 +0100119 mPasswordEntry.requestFocus();
120 if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
121 mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
122 }
Jorim Jaggie0700182014-08-21 01:12:37 +0200123 }
124 }
125 });
Daniel Sandler2c1ce052012-10-24 23:19:34 -0400126 }
127
128 @Override
Lucas Dupinc80c67e2017-12-04 14:29:10 -0800129 protected int getPromptReasonStringRes(int reason) {
Selim Cinek3122fa82015-06-18 01:38:59 -0700130 switch (reason) {
131 case PROMPT_REASON_RESTART:
132 return R.string.kg_prompt_reason_restart_password;
Jorim Jaggi25b4d4b2015-08-11 15:54:06 -0700133 case PROMPT_REASON_TIMEOUT:
134 return R.string.kg_prompt_reason_timeout_password;
Adrian Roosd6d253a2016-04-13 13:06:21 -0700135 case PROMPT_REASON_DEVICE_ADMIN:
136 return R.string.kg_prompt_reason_device_admin;
137 case PROMPT_REASON_USER_REQUEST:
138 return R.string.kg_prompt_reason_user_request;
Adrian Roosc13723f2016-01-12 20:29:03 +0100139 case PROMPT_REASON_NONE:
Selim Cinek3122fa82015-06-18 01:38:59 -0700140 return 0;
Adrian Roosc13723f2016-01-12 20:29:03 +0100141 default:
142 return R.string.kg_prompt_reason_timeout_password;
Selim Cinek3122fa82015-06-18 01:38:59 -0700143 }
144 }
145
146 @Override
Daniel Sandler2c1ce052012-10-24 23:19:34 -0400147 public void onPause() {
148 super.onPause();
149 mImm.hideSoftInputFromWindow(getWindowToken(), 0);
150 }
151
Yohei Yukawa982a94c2016-08-11 19:16:02 -0700152 private void updateSwitchImeButton() {
153 // If there's more than one IME, enable the IME switcher button
154 final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
155 final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false);
156 if (wasVisible != shouldBeVisible) {
157 mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
158 }
159
160 // TODO: Check if we still need this hack.
161 // If no icon is visible, reset the start margin on the password field so the text is
162 // still centered.
163 if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
164 android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
165 if (params instanceof MarginLayoutParams) {
166 final MarginLayoutParams mlp = (MarginLayoutParams) params;
167 mlp.setMarginStart(0);
168 mPasswordEntry.setLayoutParams(params);
169 }
170 }
171 }
172
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700173 @Override
Jim Millerdcb3d842012-08-23 19:18:12 -0700174 protected void onFinishInflate() {
Daniel Sandler69bdee72012-10-23 16:45:50 -0400175 super.onFinishInflate();
Jae Yong Sung8171b512010-08-05 10:44:27 -0700176
Daniel Sandler2c1ce052012-10-24 23:19:34 -0400177 mImm = (InputMethodManager) getContext().getSystemService(
178 Context.INPUT_METHOD_SERVICE);
Ben Komalo51ea88a2011-10-03 10:53:26 -0700179
Alan Viverette51efddb2017-04-05 10:00:01 -0400180 mPasswordEntry = findViewById(getPasswordTextViewId());
Yohei Yukawa72769462019-01-20 09:28:08 -0800181 mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
Xiyuan Xiacead9192015-05-15 11:01:30 -0700182 mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
Daniel Sandler2c1ce052012-10-24 23:19:34 -0400183 mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
184 mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
185 | InputType.TYPE_TEXT_VARIATION_PASSWORD);
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700186 mPasswordEntry.setOnEditorActionListener(this);
187 mPasswordEntry.addTextChangedListener(this);
Jim Miller8b9dda22010-03-02 17:24:48 -0800188
Jim Miller6b05d582011-07-18 13:09:59 -0700189 // Poke the wakelock any time the text is selected or modified
190 mPasswordEntry.setOnClickListener(new OnClickListener() {
Jim Miller4db942c2016-05-16 18:06:50 -0700191 @Override
Jim Miller6b05d582011-07-18 13:09:59 -0700192 public void onClick(View v) {
Jorim Jaggib690f0d2014-07-03 23:25:44 +0200193 mCallback.userActivity();
Jim Miller6b05d582011-07-18 13:09:59 -0700194 }
195 });
Jim Millerdcb3d842012-08-23 19:18:12 -0700196
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700197 // Set selected property on so the view can send accessibility events.
198 mPasswordEntry.setSelected(true);
199
Yohei Yukawa982a94c2016-08-11 19:16:02 -0700200 mSwitchImeButton = findViewById(R.id.switch_ime_button);
201 mSwitchImeButton.setOnClickListener(new OnClickListener() {
202 @Override
203 public void onClick(View v) {
204 mCallback.userActivity(); // Leave the screen on a bit longer
205 // Do not show auxiliary subtypes in password lock screen.
lumark0b05f9e2018-11-26 15:09:06 +0800206 mImm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */,
207 getContext().getDisplayId());
Jim Millerd9d09452011-11-02 18:32:16 -0700208 }
Yohei Yukawa982a94c2016-08-11 19:16:02 -0700209 });
210
Jian Jin44e4d822018-04-06 12:40:50 -0700211 View cancelBtn = findViewById(R.id.cancel_button);
212 if (cancelBtn != null) {
213 cancelBtn.setOnClickListener(view -> {
214 mCallback.reset();
Aarthi Balachander0a427ef2018-07-13 15:00:58 -0700215 mCallback.onCancelClicked();
Jian Jin44e4d822018-04-06 12:40:50 -0700216 });
217 }
218
Yohei Yukawa982a94c2016-08-11 19:16:02 -0700219 // If there's more than one IME, enable the IME switcher button
220 updateSwitchImeButton();
221
222 // When we the current user is switching, InputMethodManagerService sometimes has not
223 // switched internal state yet here. As a quick workaround, we check the keyboard state
224 // again.
225 // TODO: Remove this workaround by ensuring such a race condition never happens.
226 postDelayed(new Runnable() {
227 @Override
228 public void run() {
229 updateSwitchImeButton();
230 }
231 }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
Jim Miller858f8ea2011-08-19 14:50:34 -0700232 }
233
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700234 @Override
235 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
236 // send focus to the password field
237 return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
238 }
239
240 @Override
Jim Miller4db942c2016-05-16 18:06:50 -0700241 protected void resetPasswordText(boolean animate, boolean announce) {
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700242 mPasswordEntry.setText("");
243 }
244
245 @Override
Rich Canningsf64ec632019-02-21 12:40:36 -0800246 protected byte[] getPasswordText() {
247 return charSequenceToByteArray(mPasswordEntry.getText());
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700248 }
249
250 @Override
251 protected void setPasswordEntryEnabled(boolean enabled) {
Xiyuan Xiade26ea62015-05-19 15:00:41 -0700252 mPasswordEntry.setEnabled(enabled);
253 }
254
255 @Override
256 protected void setPasswordEntryInputEnabled(boolean enabled) {
Xiyuan Xiacead9192015-05-15 11:01:30 -0700257 mPasswordEntryDisabler.setInputEnabled(enabled);
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700258 }
259
Jim Miller858f8ea2011-08-19 14:50:34 -0700260 /**
261 * Method adapted from com.android.inputmethod.latin.Utils
262 *
263 * @param imm The input method manager
264 * @param shouldIncludeAuxiliarySubtypes
265 * @return true if we have multiple IMEs to choose from
266 */
267 private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
268 final boolean shouldIncludeAuxiliarySubtypes) {
Yohei Yukawa1fb13c52019-02-05 07:55:28 -0800269 final List<InputMethodInfo> enabledImis =
270 imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser());
Jim Miller858f8ea2011-08-19 14:50:34 -0700271
272 // Number of the filtered IMEs
273 int filteredImisCount = 0;
274
275 for (InputMethodInfo imi : enabledImis) {
276 // We can return true immediately after we find two or more filtered IMEs.
277 if (filteredImisCount > 1) return true;
278 final List<InputMethodSubtype> subtypes =
279 imm.getEnabledInputMethodSubtypeList(imi, true);
280 // IMEs that have no subtypes should be counted.
281 if (subtypes.isEmpty()) {
282 ++filteredImisCount;
283 continue;
284 }
285
286 int auxCount = 0;
287 for (InputMethodSubtype subtype : subtypes) {
288 if (subtype.isAuxiliary()) {
289 ++auxCount;
290 }
291 }
292 final int nonAuxCount = subtypes.size() - auxCount;
293
294 // IMEs that have one or more non-auxiliary subtypes should be counted.
295 // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
296 // subtypes should be counted as well.
297 if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
298 ++filteredImisCount;
299 continue;
300 }
301 }
302
303 return filteredImisCount > 1
304 // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
305 // input method subtype (The current IME should be LatinIME.)
306 || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800307 }
Adam Cohen6fb841f2012-10-24 13:15:38 -0700308
309 @Override
310 public void showUsabilityHint() {
311 }
Daniel Sandler16d90922012-11-01 12:41:14 -0400312
313 @Override
314 public int getWrongPasswordStringId() {
315 return R.string.kg_wrong_password;
316 }
Jorim Jaggic14f8292014-05-27 02:25:45 +0200317
318 @Override
319 public void startAppearAnimation() {
Jorim Jaggie0700182014-08-21 01:12:37 +0200320 setAlpha(0f);
321 setTranslationY(0f);
322 animate()
323 .alpha(1)
324 .withLayer()
325 .setDuration(300)
326 .setInterpolator(mLinearOutSlowInInterpolator);
Jorim Jaggic14f8292014-05-27 02:25:45 +0200327 }
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700328
329 @Override
Jorim Jaggi76a16232014-08-08 17:00:47 +0200330 public boolean startDisappearAnimation(Runnable finishRunnable) {
Jorim Jaggie0700182014-08-21 01:12:37 +0200331 animate()
332 .alpha(0f)
333 .translationY(mDisappearYTranslation)
334 .setInterpolator(mFastOutLinearInInterpolator)
335 .setDuration(100)
336 .withEndAction(finishRunnable);
337 return true;
Jorim Jaggi76a16232014-08-08 17:00:47 +0200338 }
339
340 @Override
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700341 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
342 if (mCallback != null) {
Jorim Jaggib690f0d2014-07-03 23:25:44 +0200343 mCallback.userActivity();
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700344 }
345 }
346
347 @Override
348 public void onTextChanged(CharSequence s, int start, int before, int count) {
349 }
350
351 @Override
352 public void afterTextChanged(Editable s) {
Xiyuan Xia09eb0332015-05-13 15:29:42 -0700353 // Poor man's user edit detection, assuming empty text is programmatic and everything else
354 // is from the user.
355 if (!TextUtils.isEmpty(s)) {
356 onUserInput();
357 }
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700358 }
359
360 @Override
361 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
362 // Check if this was the result of hitting the enter key
Jim Millere8fed242014-10-03 18:54:03 -0700363 final boolean isSoftImeEvent = event == null
364 && (actionId == EditorInfo.IME_NULL
365 || actionId == EditorInfo.IME_ACTION_DONE
366 || actionId == EditorInfo.IME_ACTION_NEXT);
367 final boolean isKeyboardEnterKey = event != null
368 && KeyEvent.isConfirmKey(event.getKeyCode())
369 && event.getAction() == KeyEvent.ACTION_DOWN;
370 if (isSoftImeEvent || isKeyboardEnterKey) {
Selim Cinek4e8b9ed2014-06-20 16:37:04 -0700371 verifyPasswordAndUnlock();
372 return true;
373 }
374 return false;
375 }
Phil Weaver7d847b02018-02-13 16:02:35 -0800376
377 @Override
378 public CharSequence getTitle() {
379 return getContext().getString(
380 com.android.internal.R.string.keyguard_accessibility_password_unlock);
381 }
Rich Canningsf64ec632019-02-21 12:40:36 -0800382
383 /*
384 * This method avoids creating a new string when getting a byte array from EditView#getText().
385 */
386 private static byte[] charSequenceToByteArray(CharSequence chars) {
387 if (chars == null) {
388 return null;
389 }
390 byte[] bytes = new byte[chars.length()];
391 for (int i = 0; i < chars.length(); i++) {
392 bytes[i] = (byte) chars.charAt(i);
393 }
394 return bytes;
395 }
Jim Miller5e0f7ba2009-12-22 19:04:23 -0800396}