• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.content.Context;
20 import android.graphics.Rect;
21 import android.text.Editable;
22 import android.text.InputType;
23 import android.text.TextWatcher;
24 import android.text.method.TextKeyListener;
25 import android.util.AttributeSet;
26 import android.view.KeyEvent;
27 import android.view.View;
28 import android.view.animation.AnimationUtils;
29 import android.view.animation.Interpolator;
30 import android.view.inputmethod.EditorInfo;
31 import android.view.inputmethod.InputMethodInfo;
32 import android.view.inputmethod.InputMethodManager;
33 import android.view.inputmethod.InputMethodSubtype;
34 import android.widget.TextView;
35 import android.widget.TextView.OnEditorActionListener;
36 
37 import java.util.List;
38 /**
39  * Displays an alphanumeric (latin-1) key entry for the user to enter
40  * an unlock password
41  */
42 
43 public class KeyguardPasswordView extends KeyguardAbsKeyInputView
44         implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
45 
46     private final boolean mShowImeAtScreenOn;
47     private final int mDisappearYTranslation;
48 
49     InputMethodManager mImm;
50     private TextView mPasswordEntry;
51     private Interpolator mLinearOutSlowInInterpolator;
52     private Interpolator mFastOutLinearInInterpolator;
53 
KeyguardPasswordView(Context context)54     public KeyguardPasswordView(Context context) {
55         this(context, null);
56     }
57 
KeyguardPasswordView(Context context, AttributeSet attrs)58     public KeyguardPasswordView(Context context, AttributeSet attrs) {
59         super(context, attrs);
60         mShowImeAtScreenOn = context.getResources().
61                 getBoolean(R.bool.kg_show_ime_at_screen_on);
62         mDisappearYTranslation = getResources().getDimensionPixelSize(
63                 R.dimen.disappear_y_translation);
64         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
65                 context, android.R.interpolator.linear_out_slow_in);
66         mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
67                 context, android.R.interpolator.fast_out_linear_in);
68     }
69 
resetState()70     protected void resetState() {
71         mSecurityMessageDisplay.setMessage(R.string.kg_password_instructions, false);
72         mPasswordEntry.setEnabled(true);
73     }
74 
75     @Override
getPasswordTextViewId()76     protected int getPasswordTextViewId() {
77         return R.id.passwordEntry;
78     }
79 
80     @Override
needsInput()81     public boolean needsInput() {
82         return true;
83     }
84 
85     @Override
onResume(final int reason)86     public void onResume(final int reason) {
87         super.onResume(reason);
88 
89         // Wait a bit to focus the field so the focusable flag on the window is already set then.
90         post(new Runnable() {
91             @Override
92             public void run() {
93                 mPasswordEntry.requestFocus();
94                 if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
95                     mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
96                 }
97             }
98         });
99     }
100 
101     @Override
onPause()102     public void onPause() {
103         super.onPause();
104         mImm.hideSoftInputFromWindow(getWindowToken(), 0);
105     }
106 
107     @Override
reset()108     public void reset() {
109         super.reset();
110         mPasswordEntry.requestFocus();
111     }
112 
113     @Override
onFinishInflate()114     protected void onFinishInflate() {
115         super.onFinishInflate();
116 
117         boolean imeOrDeleteButtonVisible = false;
118 
119         mImm = (InputMethodManager) getContext().getSystemService(
120                 Context.INPUT_METHOD_SERVICE);
121 
122         mPasswordEntry = (TextView) findViewById(getPasswordTextViewId());
123         mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
124         mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
125                 | InputType.TYPE_TEXT_VARIATION_PASSWORD);
126         mPasswordEntry.setOnEditorActionListener(this);
127         mPasswordEntry.addTextChangedListener(this);
128 
129         // Poke the wakelock any time the text is selected or modified
130         mPasswordEntry.setOnClickListener(new OnClickListener() {
131             public void onClick(View v) {
132                 mCallback.userActivity();
133             }
134         });
135 
136         // Set selected property on so the view can send accessibility events.
137         mPasswordEntry.setSelected(true);
138 
139         mPasswordEntry.addTextChangedListener(new TextWatcher() {
140             public void onTextChanged(CharSequence s, int start, int before, int count) {
141             }
142 
143             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
144             }
145 
146             public void afterTextChanged(Editable s) {
147                 if (mCallback != null) {
148                     mCallback.userActivity();
149                 }
150             }
151         });
152 
153         mPasswordEntry.requestFocus();
154 
155         // If there's more than one IME, enable the IME switcher button
156         View switchImeButton = findViewById(R.id.switch_ime_button);
157         if (switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(mImm, false)) {
158             switchImeButton.setVisibility(View.VISIBLE);
159             imeOrDeleteButtonVisible = true;
160             switchImeButton.setOnClickListener(new OnClickListener() {
161                 public void onClick(View v) {
162                     mCallback.userActivity(); // Leave the screen on a bit longer
163                     mImm.showInputMethodPicker();
164                 }
165             });
166         }
167 
168         // If no icon is visible, reset the start margin on the password field so the text is
169         // still centered.
170         if (!imeOrDeleteButtonVisible) {
171             android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
172             if (params instanceof MarginLayoutParams) {
173                 final MarginLayoutParams mlp = (MarginLayoutParams) params;
174                 mlp.setMarginStart(0);
175                 mPasswordEntry.setLayoutParams(params);
176             }
177         }
178     }
179 
180     @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)181     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
182         // send focus to the password field
183         return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
184     }
185 
186     @Override
resetPasswordText(boolean animate)187     protected void resetPasswordText(boolean animate) {
188         mPasswordEntry.setText("");
189     }
190 
191     @Override
getPasswordText()192     protected String getPasswordText() {
193         return mPasswordEntry.getText().toString();
194     }
195 
196     @Override
setPasswordEntryEnabled(boolean enabled)197     protected void setPasswordEntryEnabled(boolean enabled) {
198         mPasswordEntry.setEnabled(enabled);
199     }
200 
201     /**
202      * Method adapted from com.android.inputmethod.latin.Utils
203      *
204      * @param imm The input method manager
205      * @param shouldIncludeAuxiliarySubtypes
206      * @return true if we have multiple IMEs to choose from
207      */
hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm, final boolean shouldIncludeAuxiliarySubtypes)208     private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
209             final boolean shouldIncludeAuxiliarySubtypes) {
210         final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
211 
212         // Number of the filtered IMEs
213         int filteredImisCount = 0;
214 
215         for (InputMethodInfo imi : enabledImis) {
216             // We can return true immediately after we find two or more filtered IMEs.
217             if (filteredImisCount > 1) return true;
218             final List<InputMethodSubtype> subtypes =
219                     imm.getEnabledInputMethodSubtypeList(imi, true);
220             // IMEs that have no subtypes should be counted.
221             if (subtypes.isEmpty()) {
222                 ++filteredImisCount;
223                 continue;
224             }
225 
226             int auxCount = 0;
227             for (InputMethodSubtype subtype : subtypes) {
228                 if (subtype.isAuxiliary()) {
229                     ++auxCount;
230                 }
231             }
232             final int nonAuxCount = subtypes.size() - auxCount;
233 
234             // IMEs that have one or more non-auxiliary subtypes should be counted.
235             // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
236             // subtypes should be counted as well.
237             if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
238                 ++filteredImisCount;
239                 continue;
240             }
241         }
242 
243         return filteredImisCount > 1
244         // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
245         // input method subtype (The current IME should be LatinIME.)
246                 || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
247     }
248 
249     @Override
showUsabilityHint()250     public void showUsabilityHint() {
251     }
252 
253     @Override
getWrongPasswordStringId()254     public int getWrongPasswordStringId() {
255         return R.string.kg_wrong_password;
256     }
257 
258     @Override
startAppearAnimation()259     public void startAppearAnimation() {
260         setAlpha(0f);
261         setTranslationY(0f);
262         animate()
263                 .alpha(1)
264                 .withLayer()
265                 .setDuration(300)
266                 .setInterpolator(mLinearOutSlowInInterpolator);
267     }
268 
269     @Override
startDisappearAnimation(Runnable finishRunnable)270     public boolean startDisappearAnimation(Runnable finishRunnable) {
271         animate()
272                 .alpha(0f)
273                 .translationY(mDisappearYTranslation)
274                 .setInterpolator(mFastOutLinearInInterpolator)
275                 .setDuration(100)
276                 .withEndAction(finishRunnable);
277         return true;
278     }
279 
280     @Override
beforeTextChanged(CharSequence s, int start, int count, int after)281     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
282         if (mCallback != null) {
283             mCallback.userActivity();
284         }
285     }
286 
287     @Override
onTextChanged(CharSequence s, int start, int before, int count)288     public void onTextChanged(CharSequence s, int start, int before, int count) {
289     }
290 
291     @Override
afterTextChanged(Editable s)292     public void afterTextChanged(Editable s) {
293     }
294 
295     @Override
onEditorAction(TextView v, int actionId, KeyEvent event)296     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
297         // Check if this was the result of hitting the enter key
298         final boolean isSoftImeEvent = event == null
299                 && (actionId == EditorInfo.IME_NULL
300                 || actionId == EditorInfo.IME_ACTION_DONE
301                 || actionId == EditorInfo.IME_ACTION_NEXT);
302         final boolean isKeyboardEnterKey = event != null
303                 && KeyEvent.isConfirmKey(event.getKeyCode())
304                 && event.getAction() == KeyEvent.ACTION_DOWN;
305         if (isSoftImeEvent || isKeyboardEnterKey) {
306             verifyPasswordAndUnlock();
307             return true;
308         }
309         return false;
310     }
311 }
312