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
17package com.android.keyguard;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.os.PowerManager;
22import android.os.SystemClock;
23import android.util.AttributeSet;
24import android.view.HapticFeedbackConstants;
25import android.view.LayoutInflater;
26import android.view.MotionEvent;
27import android.view.View;
28import android.view.ViewGroup;
29import android.widget.TextView;
30
31import com.android.internal.widget.LockPatternUtils;
32
33public class NumPadKey extends ViewGroup {
34    // list of "ABC", etc per digit, starting with '0'
35    static String sKlondike[];
36
37    private int mDigit = -1;
38    private int mTextViewResId;
39    private PasswordTextView mTextView;
40    private TextView mDigitText;
41    private TextView mKlondikeText;
42    private boolean mEnableHaptics;
43    private PowerManager mPM;
44
45    private View.OnClickListener mListener = new View.OnClickListener() {
46        @Override
47        public void onClick(View thisView) {
48            if (mTextView == null && mTextViewResId > 0) {
49                final View v = NumPadKey.this.getRootView().findViewById(mTextViewResId);
50                if (v != null && v instanceof PasswordTextView) {
51                    mTextView = (PasswordTextView) v;
52                }
53            }
54            if (mTextView != null && mTextView.isEnabled()) {
55                mTextView.append(Character.forDigit(mDigit, 10));
56            }
57            userActivity();;
58        }
59    };
60
61    public void userActivity() {
62        mPM.userActivity(SystemClock.uptimeMillis(), false);
63    }
64
65    public NumPadKey(Context context) {
66        this(context, null);
67    }
68
69    public NumPadKey(Context context, AttributeSet attrs) {
70        this(context, attrs, 0);
71    }
72
73    public NumPadKey(Context context, AttributeSet attrs, int defStyle) {
74        this(context, attrs, defStyle, R.layout.keyguard_num_pad_key);
75    }
76
77    protected NumPadKey(Context context, AttributeSet attrs, int defStyle, int contentResource) {
78        super(context, attrs, defStyle);
79        setFocusable(true);
80
81        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NumPadKey);
82
83        try {
84            mDigit = a.getInt(R.styleable.NumPadKey_digit, mDigit);
85            mTextViewResId = a.getResourceId(R.styleable.NumPadKey_textView, 0);
86        } finally {
87            a.recycle();
88        }
89
90        setOnClickListener(mListener);
91        setOnHoverListener(new LiftToActivateListener(context));
92
93        mEnableHaptics = new LockPatternUtils(context).isTactileFeedbackEnabled();
94
95        mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
96        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
97                Context.LAYOUT_INFLATER_SERVICE);
98        inflater.inflate(contentResource, this, true);
99
100        mDigitText = (TextView) findViewById(R.id.digit_text);
101        mDigitText.setText(Integer.toString(mDigit));
102        mKlondikeText = (TextView) findViewById(R.id.klondike_text);
103
104        if (mDigit >= 0) {
105            if (sKlondike == null) {
106                sKlondike = getResources().getStringArray(R.array.lockscreen_num_pad_klondike);
107            }
108            if (sKlondike != null && sKlondike.length > mDigit) {
109                String klondike = sKlondike[mDigit];
110                final int len = klondike.length();
111                if (len > 0) {
112                    mKlondikeText.setText(klondike);
113                } else {
114                    mKlondikeText.setVisibility(View.INVISIBLE);
115                }
116            }
117        }
118
119        a = context.obtainStyledAttributes(attrs, android.R.styleable.View);
120        if (!a.hasValueOrEmpty(android.R.styleable.View_background)) {
121            setBackground(mContext.getDrawable(R.drawable.ripple_drawable));
122        }
123        a.recycle();
124        setContentDescription(mDigitText.getText().toString());
125    }
126
127    @Override
128    public boolean onTouchEvent(MotionEvent event) {
129        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
130            doHapticKeyClick();
131        }
132        return super.onTouchEvent(event);
133    }
134
135    @Override
136    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
137        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
138        measureChildren(widthMeasureSpec, heightMeasureSpec);
139    }
140
141    @Override
142    protected void onLayout(boolean changed, int l, int t, int r, int b) {
143        int digitHeight = mDigitText.getMeasuredHeight();
144        int klondikeHeight = mKlondikeText.getMeasuredHeight();
145        int totalHeight = digitHeight + klondikeHeight;
146        int top = getHeight() / 2 - totalHeight / 2;
147        int centerX = getWidth() / 2;
148        int left = centerX - mDigitText.getMeasuredWidth() / 2;
149        int bottom = top + digitHeight;
150        mDigitText.layout(left, top, left + mDigitText.getMeasuredWidth(), bottom);
151        top = (int) (bottom - klondikeHeight * 0.35f);
152        bottom = top + klondikeHeight;
153
154        left = centerX - mKlondikeText.getMeasuredWidth() / 2;
155        mKlondikeText.layout(left, top, left + mKlondikeText.getMeasuredWidth(), bottom);
156    }
157
158    @Override
159    public boolean hasOverlappingRendering() {
160        return false;
161    }
162
163    // Cause a VIRTUAL_KEY vibration
164    public void doHapticKeyClick() {
165        if (mEnableHaptics) {
166            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
167                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
168                    | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
169        }
170    }
171}
172