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