DialpadView.java revision 13b8948c0cc543f62a68fa32c4692ba16a2e93c6
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.phone.common.dialpad;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.content.Context;
22import android.content.res.ColorStateList;
23import android.content.res.Resources;
24import android.content.res.TypedArray;
25import android.graphics.PorterDuff;
26import android.graphics.drawable.RippleDrawable;
27import android.util.AttributeSet;
28import android.util.Log;
29import android.view.MotionEvent;
30import android.view.View;
31import android.widget.EditText;
32import android.widget.ImageButton;
33import android.widget.LinearLayout;
34import android.widget.TextView;
35
36import com.android.phone.common.R;
37import com.android.phone.common.animation.AnimUtils;
38
39/**
40 * View that displays a twelve-key phone dialpad.
41 */
42public class DialpadView extends LinearLayout {
43    private static final String TAG = DialpadView.class.getSimpleName();
44
45    private static final double DELAY_MULTIPLIER = 0.66;
46    private static final double DURATION_MULTIPLIER = 0.8;
47
48    private EditText mDigits;
49    private ImageButton mDelete;
50    private View mOverflowMenuButton;
51    private ColorStateList mRippleColor;
52
53    private boolean mCanDigitsBeEdited;
54
55    private final int[] mButtonIds = new int[] {R.id.zero, R.id.one, R.id.two, R.id.three,
56            R.id.four, R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.star,
57            R.id.pound};
58
59    // For animation.
60    private static final int KEY_FRAME_DURATION = 33;
61
62    private int mTranslateDistance;
63
64    public DialpadView(Context context) {
65        this(context, null);
66    }
67
68    public DialpadView(Context context, AttributeSet attrs) {
69        this(context, attrs, 0);
70    }
71
72    public DialpadView(Context context, AttributeSet attrs, int defStyle) {
73        super(context, attrs, defStyle);
74
75        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Dialpad);
76        mRippleColor = a.getColorStateList(R.styleable.Dialpad_dialpad_key_button_touch_tint);
77        a.recycle();
78
79        mTranslateDistance = getResources().getDimensionPixelSize(
80                R.dimen.dialpad_key_button_translate_y);
81    }
82
83    @Override
84    protected void onFinishInflate() {
85        setupKeypad();
86        mDigits = (EditText) findViewById(R.id.digits);
87        mDelete = (ImageButton) findViewById(R.id.deleteButton);
88        mOverflowMenuButton = findViewById(R.id.dialpad_overflow);
89    }
90
91    private void setupKeypad() {
92        final int[] numberIds = new int[] {R.string.dialpad_0_number, R.string.dialpad_1_number,
93                R.string.dialpad_2_number, R.string.dialpad_3_number, R.string.dialpad_4_number,
94                R.string.dialpad_5_number, R.string.dialpad_6_number, R.string.dialpad_7_number,
95                R.string.dialpad_8_number, R.string.dialpad_9_number, R.string.dialpad_star_number,
96                R.string.dialpad_pound_number};
97
98        final int[] letterIds = new int[] {R.string.dialpad_0_letters, R.string.dialpad_1_letters,
99                R.string.dialpad_2_letters, R.string.dialpad_3_letters, R.string.dialpad_4_letters,
100                R.string.dialpad_5_letters, R.string.dialpad_6_letters, R.string.dialpad_7_letters,
101                R.string.dialpad_8_letters, R.string.dialpad_9_letters,
102                R.string.dialpad_star_letters, R.string.dialpad_pound_letters};
103
104        final Resources resources = getContext().getResources();
105
106        DialpadKeyButton dialpadKey;
107        TextView numberView;
108        TextView lettersView;
109
110        for (int i = 0; i < mButtonIds.length; i++) {
111            dialpadKey = (DialpadKeyButton) findViewById(mButtonIds[i]);
112            numberView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_number);
113            lettersView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_letters);
114            final String numberString = resources.getString(numberIds[i]);
115            final RippleDrawable rippleBackground =
116                    (RippleDrawable) resources.getDrawable(R.drawable.btn_dialpad_key);
117            if (mRippleColor != null) {
118                rippleBackground.setColor(mRippleColor);
119            }
120
121            numberView.setText(numberString);
122            numberView.setElegantTextHeight(false);
123            dialpadKey.setContentDescription(numberString);
124            dialpadKey.setBackground(rippleBackground);
125
126            if (lettersView != null) {
127                lettersView.setText(resources.getString(letterIds[i]));
128            }
129        }
130
131        final DialpadKeyButton one = (DialpadKeyButton) findViewById(R.id.one);
132        one.setLongHoverContentDescription(
133                resources.getText(R.string.description_voicemail_button));
134
135        final DialpadKeyButton zero = (DialpadKeyButton) findViewById(R.id.zero);
136        zero.setLongHoverContentDescription(
137                resources.getText(R.string.description_image_button_plus));
138
139    }
140
141    public void setShowVoicemailButton(boolean show) {
142        View view = findViewById(R.id.dialpad_key_voicemail);
143        if (view != null) {
144            view.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
145        }
146    }
147
148    /**
149     * Whether or not the digits above the dialer can be edited.
150     *
151     * @param canBeEdited If true, the backspace button will be shown and the digits EditText
152     *         will be configured to allow text manipulation.
153     */
154    public void setCanDigitsBeEdited(boolean canBeEdited) {
155        View deleteButton = findViewById(R.id.deleteButton);
156        deleteButton.setVisibility(canBeEdited ? View.VISIBLE : View.GONE);
157        View overflowMenuButton = findViewById(R.id.dialpad_overflow);
158        overflowMenuButton.setVisibility(canBeEdited ? View.VISIBLE : View.GONE);
159
160        EditText digits = (EditText) findViewById(R.id.digits);
161        digits.setClickable(canBeEdited);
162        digits.setLongClickable(canBeEdited);
163        digits.setFocusableInTouchMode(canBeEdited);
164        digits.setCursorVisible(false);
165
166        mCanDigitsBeEdited = canBeEdited;
167    }
168
169    public boolean canDigitsBeEdited() {
170        return mCanDigitsBeEdited;
171    }
172
173    /**
174     * Always returns true for onHoverEvent callbacks, to fix problems with accessibility due to
175     * the dialpad overlaying other fragments.
176     */
177    @Override
178    public boolean onHoverEvent(MotionEvent event) {
179        return true;
180    }
181
182    public void animateShow() {
183        // This is a hack; without this, the setTranslationY is delayed in being applied, and the
184        // numbers appear at their original position (0) momentarily before animating.
185        final AnimatorListenerAdapter showListener = new AnimatorListenerAdapter() {};
186
187        for (int i = 0; i < mButtonIds.length; i++) {
188            int delay = (int)(getKeyButtonAnimationDelay(mButtonIds[i]) * DELAY_MULTIPLIER);
189            int duration =
190                    (int)(getKeyButtonAnimationDuration(mButtonIds[i]) * DURATION_MULTIPLIER);
191            final DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(mButtonIds[i]);
192
193            dialpadKey.setTranslationY(mTranslateDistance);
194            dialpadKey.animate()
195                    .translationY(0)
196                    .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
197                    .setStartDelay(delay)
198                    .setDuration(duration)
199                    .setListener(showListener)
200                    .start();
201        }
202    }
203
204    public EditText getDigits() {
205        return mDigits;
206    }
207
208    public ImageButton getDeleteButton() {
209        return mDelete;
210    }
211
212    public View getOverflowMenuButton() {
213        return mOverflowMenuButton;
214    }
215
216    private int getKeyButtonAnimationDelay(int buttonId) {
217        switch(buttonId) {
218            case R.id.one: return KEY_FRAME_DURATION * 1;
219            case R.id.two: return KEY_FRAME_DURATION * 2;
220            case R.id.three: return KEY_FRAME_DURATION * 3;
221            case R.id.four: return KEY_FRAME_DURATION * 4;
222            case R.id.five: return KEY_FRAME_DURATION * 5;
223            case R.id.six: return KEY_FRAME_DURATION * 6;
224            case R.id.seven: return KEY_FRAME_DURATION * 7;
225            case R.id.eight: return KEY_FRAME_DURATION * 8;
226            case R.id.nine: return KEY_FRAME_DURATION * 9;
227            case R.id.star: return KEY_FRAME_DURATION * 10;
228            case R.id.zero:
229            case R.id.pound:
230                return KEY_FRAME_DURATION * 11;
231        }
232
233        Log.wtf(TAG, "Attempted to get animation delay for invalid key button id.");
234        return 0;
235    }
236
237    private int getKeyButtonAnimationDuration(int buttonId) {
238        switch(buttonId) {
239            case R.id.one:
240            case R.id.two:
241            case R.id.three:
242            case R.id.four:
243            case R.id.five:
244            case R.id.six:
245                return KEY_FRAME_DURATION * 10;
246            case R.id.seven:
247            case R.id.eight:
248            case R.id.nine:
249                return KEY_FRAME_DURATION * 9;
250            case R.id.star:
251            case R.id.zero:
252            case R.id.pound:
253                return KEY_FRAME_DURATION * 8;
254        }
255
256        Log.wtf(TAG, "Attempted to get animation duration for invalid key button id.");
257        return 0;
258    }
259}
260