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