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