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 android.content.Context; 20 import android.graphics.Rect; 21 import android.util.AttributeSet; 22 import android.view.KeyEvent; 23 import android.view.MotionEvent; 24 import android.view.View; 25 26 import com.android.internal.widget.LockscreenCredential; 27 import com.android.systemui.R; 28 29 /** 30 * A Pin based Keyguard input view 31 */ 32 public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView 33 implements View.OnKeyListener, View.OnTouchListener { 34 35 protected PasswordTextView mPasswordEntry; 36 private View mOkButton; 37 private View mDeleteButton; 38 private View mButton0; 39 private View mButton1; 40 private View mButton2; 41 private View mButton3; 42 private View mButton4; 43 private View mButton5; 44 private View mButton6; 45 private View mButton7; 46 private View mButton8; 47 private View mButton9; 48 KeyguardPinBasedInputView(Context context)49 public KeyguardPinBasedInputView(Context context) { 50 this(context, null); 51 } 52 KeyguardPinBasedInputView(Context context, AttributeSet attrs)53 public KeyguardPinBasedInputView(Context context, AttributeSet attrs) { 54 super(context, attrs); 55 } 56 57 @Override onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)58 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 59 // send focus to the password field 60 return mPasswordEntry.requestFocus(direction, previouslyFocusedRect); 61 } 62 63 @Override resetState()64 protected void resetState() { 65 setPasswordEntryEnabled(true); 66 } 67 68 @Override setPasswordEntryEnabled(boolean enabled)69 protected void setPasswordEntryEnabled(boolean enabled) { 70 mPasswordEntry.setEnabled(enabled); 71 mOkButton.setEnabled(enabled); 72 if (enabled && !mPasswordEntry.hasFocus()) { 73 mPasswordEntry.requestFocus(); 74 } 75 } 76 77 @Override setPasswordEntryInputEnabled(boolean enabled)78 protected void setPasswordEntryInputEnabled(boolean enabled) { 79 mPasswordEntry.setEnabled(enabled); 80 mOkButton.setEnabled(enabled); 81 if (enabled && !mPasswordEntry.hasFocus()) { 82 mPasswordEntry.requestFocus(); 83 } 84 } 85 86 @Override onKeyDown(int keyCode, KeyEvent event)87 public boolean onKeyDown(int keyCode, KeyEvent event) { 88 if (KeyEvent.isConfirmKey(keyCode)) { 89 performClick(mOkButton); 90 return true; 91 } else if (keyCode == KeyEvent.KEYCODE_DEL) { 92 performClick(mDeleteButton); 93 return true; 94 } 95 if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) { 96 int number = keyCode - KeyEvent.KEYCODE_0; 97 performNumberClick(number); 98 return true; 99 } 100 if (keyCode >= KeyEvent.KEYCODE_NUMPAD_0 && keyCode <= KeyEvent.KEYCODE_NUMPAD_9) { 101 int number = keyCode - KeyEvent.KEYCODE_NUMPAD_0; 102 performNumberClick(number); 103 return true; 104 } 105 return super.onKeyDown(keyCode, event); 106 } 107 108 @Override getPromptReasonStringRes(int reason)109 protected int getPromptReasonStringRes(int reason) { 110 switch (reason) { 111 case PROMPT_REASON_RESTART: 112 return R.string.kg_prompt_reason_restart_pin; 113 case PROMPT_REASON_TIMEOUT: 114 return R.string.kg_prompt_reason_timeout_pin; 115 case PROMPT_REASON_DEVICE_ADMIN: 116 return R.string.kg_prompt_reason_device_admin; 117 case PROMPT_REASON_USER_REQUEST: 118 return R.string.kg_prompt_reason_user_request; 119 case PROMPT_REASON_PREPARE_FOR_UPDATE: 120 return R.string.kg_prompt_reason_timeout_pin; 121 case PROMPT_REASON_NONE: 122 return 0; 123 default: 124 return R.string.kg_prompt_reason_timeout_pin; 125 } 126 } 127 performClick(View view)128 private void performClick(View view) { 129 view.performClick(); 130 } 131 performNumberClick(int number)132 private void performNumberClick(int number) { 133 switch (number) { 134 case 0: 135 performClick(mButton0); 136 break; 137 case 1: 138 performClick(mButton1); 139 break; 140 case 2: 141 performClick(mButton2); 142 break; 143 case 3: 144 performClick(mButton3); 145 break; 146 case 4: 147 performClick(mButton4); 148 break; 149 case 5: 150 performClick(mButton5); 151 break; 152 case 6: 153 performClick(mButton6); 154 break; 155 case 7: 156 performClick(mButton7); 157 break; 158 case 8: 159 performClick(mButton8); 160 break; 161 case 9: 162 performClick(mButton9); 163 break; 164 } 165 } 166 167 @Override resetPasswordText(boolean animate, boolean announce)168 protected void resetPasswordText(boolean animate, boolean announce) { 169 mPasswordEntry.reset(animate, announce); 170 } 171 172 @Override getEnteredCredential()173 protected LockscreenCredential getEnteredCredential() { 174 return LockscreenCredential.createPinOrNone(mPasswordEntry.getText()); 175 } 176 177 @Override onFinishInflate()178 protected void onFinishInflate() { 179 mPasswordEntry = findViewById(getPasswordTextViewId()); 180 mPasswordEntry.setOnKeyListener(this); 181 182 // Set selected property on so the view can send accessibility events. 183 mPasswordEntry.setSelected(true); 184 185 mPasswordEntry.setUserActivityListener(new PasswordTextView.UserActivityListener() { 186 @Override 187 public void onUserActivity() { 188 onUserInput(); 189 } 190 }); 191 192 mOkButton = findViewById(R.id.key_enter); 193 if (mOkButton != null) { 194 mOkButton.setOnTouchListener(this); 195 mOkButton.setOnClickListener(new View.OnClickListener() { 196 @Override 197 public void onClick(View v) { 198 if (mPasswordEntry.isEnabled()) { 199 verifyPasswordAndUnlock(); 200 } 201 } 202 }); 203 mOkButton.setOnHoverListener(new LiftToActivateListener(getContext())); 204 } 205 206 mDeleteButton = findViewById(R.id.delete_button); 207 mDeleteButton.setVisibility(View.VISIBLE); 208 mDeleteButton.setOnTouchListener(this); 209 mDeleteButton.setOnClickListener(new OnClickListener() { 210 @Override 211 public void onClick(View v) { 212 // check for time-based lockouts 213 if (mPasswordEntry.isEnabled()) { 214 mPasswordEntry.deleteLastChar(); 215 } 216 } 217 }); 218 mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() { 219 @Override 220 public boolean onLongClick(View v) { 221 // check for time-based lockouts 222 if (mPasswordEntry.isEnabled()) { 223 resetPasswordText(true /* animate */, true /* announce */); 224 } 225 doHapticKeyClick(); 226 return true; 227 } 228 }); 229 230 mButton0 = findViewById(R.id.key0); 231 mButton1 = findViewById(R.id.key1); 232 mButton2 = findViewById(R.id.key2); 233 mButton3 = findViewById(R.id.key3); 234 mButton4 = findViewById(R.id.key4); 235 mButton5 = findViewById(R.id.key5); 236 mButton6 = findViewById(R.id.key6); 237 mButton7 = findViewById(R.id.key7); 238 mButton8 = findViewById(R.id.key8); 239 mButton9 = findViewById(R.id.key9); 240 241 mPasswordEntry.requestFocus(); 242 super.onFinishInflate(); 243 } 244 245 @Override onResume(int reason)246 public void onResume(int reason) { 247 super.onResume(reason); 248 mPasswordEntry.requestFocus(); 249 } 250 251 @Override onTouch(View v, MotionEvent event)252 public boolean onTouch(View v, MotionEvent event) { 253 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 254 doHapticKeyClick(); 255 } 256 return false; 257 } 258 259 @Override onKey(View v, int keyCode, KeyEvent event)260 public boolean onKey(View v, int keyCode, KeyEvent event) { 261 if (event.getAction() == KeyEvent.ACTION_DOWN) { 262 return onKeyDown(keyCode, event); 263 } 264 return false; 265 } 266 267 @Override getTitle()268 public CharSequence getTitle() { 269 return getContext().getString( 270 com.android.internal.R.string.keyguard_accessibility_pin_unlock); 271 } 272 } 273