| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * Copyright (C) 2023 The LineageOS Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.dialer.dialpadview; |
| |
| import android.animation.AnimatorListenerAdapter; |
| import android.content.Context; |
| import android.content.res.ColorStateList; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.content.res.TypedArray; |
| import android.graphics.drawable.RippleDrawable; |
| import android.text.Spannable; |
| import android.text.TextUtils; |
| import android.text.style.TtsSpan; |
| import android.util.AttributeSet; |
| import android.util.TypedValue; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.ViewPropertyAnimator; |
| import android.view.ViewTreeObserver.OnPreDrawListener; |
| import android.view.accessibility.AccessibilityManager; |
| import android.widget.EditText; |
| import android.widget.ImageButton; |
| import android.widget.LinearLayout; |
| import android.widget.TextView; |
| |
| import com.android.dialer.animation.AnimUtils; |
| import com.android.dialer.common.Assert; |
| import com.android.dialer.common.LogUtil; |
| import com.android.dialer.i18n.LocaleUtils; |
| |
| import java.text.DecimalFormat; |
| import java.text.NumberFormat; |
| import java.util.Locale; |
| |
| /** View that displays a twelve-key phone dialpad. */ |
| public class DialpadView extends LinearLayout { |
| |
| private static final String TAG = DialpadView.class.getSimpleName(); |
| |
| // Parameters for animation |
| private static final double DELAY_MULTIPLIER = 0.66; |
| private static final double DURATION_MULTIPLIER = 0.8; |
| private static final int KEY_FRAME_DURATION = 33; |
| |
| // Resource IDs for buttons (0-9, *, and #) |
| private static final int[] BUTTON_IDS = |
| new int[] { |
| R.id.zero, |
| R.id.one, |
| R.id.two, |
| R.id.three, |
| R.id.four, |
| R.id.five, |
| R.id.six, |
| R.id.seven, |
| R.id.eight, |
| R.id.nine, |
| R.id.star, |
| R.id.pound |
| }; |
| |
| private final AttributeSet attributeSet; |
| private final ColorStateList rippleColor; |
| private final OnPreDrawListenerForKeyLayoutAdjust onPreDrawListenerForKeyLayoutAdjust; |
| private final String[] primaryLettersMapping; |
| private final String[] secondaryLettersMapping; |
| private final boolean isRtl; // whether the dialpad is shown in a right-to-left locale |
| private final int translateDistance; |
| |
| private EditText digits; |
| private TextView digitsHint; |
| private ImageButton delete; |
| private View overflowMenuButton; |
| private ViewGroup rateContainer; |
| private TextView ildCountry; |
| private TextView ildRate; |
| private boolean isLandscapeMode; |
| |
| public DialpadView(Context context) { |
| this(context, null); |
| } |
| |
| public DialpadView(Context context, AttributeSet attrs) { |
| this(context, attrs, 0); |
| } |
| |
| public DialpadView(Context context, AttributeSet attrs, int defStyle) { |
| super(context, attrs, defStyle); |
| attributeSet = attrs; |
| |
| TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Dialpad); |
| rippleColor = a.getColorStateList(R.styleable.Dialpad_dialpad_key_button_touch_tint); |
| a.recycle(); |
| |
| translateDistance = |
| getResources().getDimensionPixelSize(R.dimen.dialpad_key_button_translate_y); |
| isRtl = |
| TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL; |
| |
| primaryLettersMapping = DialpadCharMappings.getDefaultKeyToCharsMap(); |
| secondaryLettersMapping = DialpadCharMappings.getKeyToCharsMap(context); |
| |
| onPreDrawListenerForKeyLayoutAdjust = new OnPreDrawListenerForKeyLayoutAdjust(); |
| } |
| |
| @Override |
| protected void onDetachedFromWindow() { |
| super.onDetachedFromWindow(); |
| getViewTreeObserver().removeOnPreDrawListener(onPreDrawListenerForKeyLayoutAdjust); |
| } |
| |
| @Override |
| protected void onFinishInflate() { |
| super.onFinishInflate(); |
| |
| // The orientation obtained at this point should be used as the only truth for DialpadView as we |
| // observed inconsistency between configurations obtained here and in |
| // OnPreDrawListenerForKeyLayoutAdjust under rare circumstances. |
| isLandscapeMode = |
| (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE); |
| |
| setupKeypad(); |
| digits = (EditText) findViewById(R.id.digits); |
| digitsHint = findViewById(R.id.digits_hint); |
| delete = (ImageButton) findViewById(R.id.deleteButton); |
| overflowMenuButton = findViewById(R.id.dialpad_overflow); |
| rateContainer = (ViewGroup) findViewById(R.id.rate_container); |
| ildCountry = (TextView) rateContainer.findViewById(R.id.ild_country); |
| ildRate = (TextView) rateContainer.findViewById(R.id.ild_rate); |
| |
| AccessibilityManager accessibilityManager = |
| (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); |
| if (accessibilityManager.isEnabled()) { |
| // The text view must be selected to send accessibility events. |
| digits.setSelected(true); |
| } |
| |
| // As OnPreDrawListenerForKeyLayoutAdjust makes changes to LayoutParams, it is added here to |
| // ensure it can only be triggered after the layout is inflated. |
| getViewTreeObserver().removeOnPreDrawListener(onPreDrawListenerForKeyLayoutAdjust); |
| getViewTreeObserver().addOnPreDrawListener(onPreDrawListenerForKeyLayoutAdjust); |
| } |
| |
| private void setupKeypad() { |
| final Resources resources = getContext().getResources(); |
| final NumberFormat numberFormat = getNumberFormat(); |
| |
| for (int i = 0; i < BUTTON_IDS.length; i++) { |
| DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]); |
| TextView numberView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_number); |
| |
| final String numberString; |
| final CharSequence numberContentDescription; |
| if (BUTTON_IDS[i] == R.id.pound) { |
| numberString = resources.getString(R.string.dialpad_pound_number); |
| numberContentDescription = numberString; |
| } else if (BUTTON_IDS[i] == R.id.star) { |
| numberString = resources.getString(R.string.dialpad_star_number); |
| numberContentDescription = numberString; |
| } else if (BUTTON_IDS[i] == R.id.zero) { |
| numberString = numberFormat.format(i); |
| numberContentDescription = numberString; |
| } else { |
| numberString = numberFormat.format(i); |
| // The content description is used for Talkback key presses. The number is |
| // separated by a "," to introduce a slight delay. Convert letters into a verbatim |
| // span so that they are read as letters instead of as one word. |
| String letters = primaryLettersMapping[i]; |
| Spannable spannable = |
| Spannable.Factory.getInstance().newSpannable(numberString + "," + letters); |
| spannable.setSpan( |
| (new TtsSpan.VerbatimBuilder(letters)).build(), |
| numberString.length() + 1, |
| numberString.length() + 1 + letters.length(), |
| Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
| numberContentDescription = spannable; |
| } |
| |
| final RippleDrawable rippleBackground = |
| (RippleDrawable) getContext().getDrawable(R.drawable.btn_dialpad_key); |
| if (rippleColor != null) { |
| rippleBackground.setColor(rippleColor); |
| } |
| |
| numberView.setText(numberString); |
| numberView.setElegantTextHeight(false); |
| dialpadKey.setContentDescription(numberContentDescription); |
| dialpadKey.setBackground(rippleBackground); |
| |
| TextView primaryLettersView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_letters); |
| TextView secondaryLettersView = |
| (TextView) dialpadKey.findViewById(R.id.dialpad_key_secondary_letters); |
| if (primaryLettersView != null) { |
| primaryLettersView.setText(primaryLettersMapping[i]); |
| } |
| if (primaryLettersView != null && secondaryLettersView != null) { |
| if (secondaryLettersMapping == null) { |
| secondaryLettersView.setVisibility(View.GONE); |
| } else { |
| secondaryLettersView.setVisibility(View.VISIBLE); |
| secondaryLettersView.setText(secondaryLettersMapping[i]); |
| |
| // Adjust the font size of the letters if a secondary alphabet is available. |
| TypedArray a = |
| getContext() |
| .getTheme() |
| .obtainStyledAttributes(attributeSet, R.styleable.Dialpad, 0, 0); |
| int textSize = |
| a.getDimensionPixelSize( |
| R.styleable.Dialpad_dialpad_key_letters_size_for_dual_alphabets, 0); |
| a.recycle(); |
| primaryLettersView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); |
| secondaryLettersView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); |
| } |
| } |
| } |
| |
| final DialpadKeyButton one = (DialpadKeyButton) findViewById(R.id.one); |
| one.setLongHoverContentDescription(resources.getText(R.string.description_voicemail_button)); |
| |
| final DialpadKeyButton zero = (DialpadKeyButton) findViewById(R.id.zero); |
| zero.setLongHoverContentDescription(resources.getText(R.string.description_image_button_plus)); |
| } |
| |
| private NumberFormat getNumberFormat() { |
| Locale locale = LocaleUtils.getLocale(getContext()); |
| |
| // Return the Persian number format if the current language is Persian. |
| return "fas".equals(locale.getISO3Language()) |
| ? DecimalFormat.getInstance(locale) |
| : DecimalFormat.getInstance(Locale.ENGLISH); |
| } |
| |
| /** |
| * Configure whether or not the digits above the dialpad can be edited. |
| * |
| * <p>If we allow editing digits, the backspace button will be shown. |
| */ |
| public void setCanDigitsBeEdited(boolean canBeEdited) { |
| View deleteButton = findViewById(R.id.deleteButton); |
| deleteButton.setVisibility(canBeEdited ? View.VISIBLE : View.INVISIBLE); |
| View overflowMenuButton = findViewById(R.id.dialpad_overflow); |
| overflowMenuButton.setVisibility(canBeEdited ? View.VISIBLE : View.GONE); |
| |
| EditText digits = (EditText) findViewById(R.id.digits); |
| digits.setClickable(canBeEdited); |
| digits.setLongClickable(canBeEdited); |
| digits.setFocusableInTouchMode(canBeEdited); |
| digits.setCursorVisible(false); |
| } |
| |
| public void setCallRateInformation(String countryName, String displayRate) { |
| if (TextUtils.isEmpty(countryName) && TextUtils.isEmpty(displayRate)) { |
| rateContainer.setVisibility(View.GONE); |
| return; |
| } |
| rateContainer.setVisibility(View.VISIBLE); |
| ildCountry.setText(countryName); |
| ildRate.setText(displayRate); |
| } |
| |
| /** |
| * Always returns true for onHoverEvent callbacks, to fix problems with accessibility due to the |
| * dialpad overlaying other fragments. |
| */ |
| @Override |
| public boolean onHoverEvent(MotionEvent event) { |
| return true; |
| } |
| |
| public void animateShow() { |
| // This is a hack; without this, the setTranslationY is delayed in being applied, and the |
| // numbers appear at their original position (0) momentarily before animating. |
| final AnimatorListenerAdapter showListener = new AnimatorListenerAdapter() {}; |
| |
| for (int i = 0; i < BUTTON_IDS.length; i++) { |
| int delay = (int) (getKeyButtonAnimationDelay(BUTTON_IDS[i]) * DELAY_MULTIPLIER); |
| int duration = (int) (getKeyButtonAnimationDuration(BUTTON_IDS[i]) * DURATION_MULTIPLIER); |
| final DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]); |
| |
| ViewPropertyAnimator animator = dialpadKey.animate(); |
| if (isLandscapeMode) { |
| // Landscape orientation requires translation along the X axis. |
| // For RTL locales, ensure we translate negative on the X axis. |
| dialpadKey.setTranslationX((isRtl ? -1 : 1) * translateDistance); |
| animator.translationX(0); |
| } else { |
| // Portrait orientation requires translation along the Y axis. |
| dialpadKey.setTranslationY(translateDistance); |
| animator.translationY(0); |
| } |
| animator |
| .setInterpolator(AnimUtils.EASE_OUT_EASE_IN) |
| .setStartDelay(delay) |
| .setDuration(duration) |
| .setListener(showListener) |
| .start(); |
| } |
| } |
| |
| public EditText getDigits() { |
| return digits; |
| } |
| |
| public TextView getDigitsHint() { |
| return digitsHint; |
| } |
| |
| public ImageButton getDeleteButton() { |
| return delete; |
| } |
| |
| public View getOverflowMenuButton() { |
| return overflowMenuButton; |
| } |
| |
| /** |
| * Get the animation delay for the buttons, taking into account whether the dialpad is in |
| * landscape left-to-right, landscape right-to-left, or portrait. |
| * |
| * @param buttonId The button ID. |
| * @return The animation delay. |
| */ |
| private int getKeyButtonAnimationDelay(int buttonId) { |
| if (isLandscapeMode) { |
| if (isRtl) { |
| if (buttonId == R.id.three) { |
| return KEY_FRAME_DURATION * 1; |
| } else if (buttonId == R.id.six) { |
| return KEY_FRAME_DURATION * 2; |
| } else if (buttonId == R.id.nine) { |
| return KEY_FRAME_DURATION * 3; |
| } else if (buttonId == R.id.pound) { |
| return KEY_FRAME_DURATION * 4; |
| } else if (buttonId == R.id.two) { |
| return KEY_FRAME_DURATION * 5; |
| } else if (buttonId == R.id.five) { |
| return KEY_FRAME_DURATION * 6; |
| } else if (buttonId == R.id.eight) { |
| return KEY_FRAME_DURATION * 7; |
| } else if (buttonId == R.id.zero) { |
| return KEY_FRAME_DURATION * 8; |
| } else if (buttonId == R.id.one) { |
| return KEY_FRAME_DURATION * 9; |
| } else if (buttonId == R.id.four) { |
| return KEY_FRAME_DURATION * 10; |
| } else if (buttonId == R.id.seven || buttonId == R.id.star) { |
| return KEY_FRAME_DURATION * 11; |
| } |
| } else { |
| if (buttonId == R.id.one) { |
| return KEY_FRAME_DURATION * 1; |
| } else if (buttonId == R.id.four) { |
| return KEY_FRAME_DURATION * 2; |
| } else if (buttonId == R.id.seven) { |
| return KEY_FRAME_DURATION * 3; |
| } else if (buttonId == R.id.star) { |
| return KEY_FRAME_DURATION * 4; |
| } else if (buttonId == R.id.two) { |
| return KEY_FRAME_DURATION * 5; |
| } else if (buttonId == R.id.five) { |
| return KEY_FRAME_DURATION * 6; |
| } else if (buttonId == R.id.eight) { |
| return KEY_FRAME_DURATION * 7; |
| } else if (buttonId == R.id.zero) { |
| return KEY_FRAME_DURATION * 8; |
| } else if (buttonId == R.id.three) { |
| return KEY_FRAME_DURATION * 9; |
| } else if (buttonId == R.id.six) { |
| return KEY_FRAME_DURATION * 10; |
| } else if (buttonId == R.id.nine || buttonId == R.id.pound) { |
| return KEY_FRAME_DURATION * 11; |
| } |
| } |
| } else { |
| if (buttonId == R.id.one) { |
| return KEY_FRAME_DURATION * 1; |
| } else if (buttonId == R.id.two) { |
| return KEY_FRAME_DURATION * 2; |
| } else if (buttonId == R.id.three) { |
| return KEY_FRAME_DURATION * 3; |
| } else if (buttonId == R.id.four) { |
| return KEY_FRAME_DURATION * 4; |
| } else if (buttonId == R.id.five) { |
| return KEY_FRAME_DURATION * 5; |
| } else if (buttonId == R.id.six) { |
| return KEY_FRAME_DURATION * 6; |
| } else if (buttonId == R.id.seven) { |
| return KEY_FRAME_DURATION * 7; |
| } else if (buttonId == R.id.eight) { |
| return KEY_FRAME_DURATION * 8; |
| } else if (buttonId == R.id.nine) { |
| return KEY_FRAME_DURATION * 9; |
| } else if (buttonId == R.id.star) { |
| return KEY_FRAME_DURATION * 10; |
| } else if (buttonId == R.id.zero || buttonId == R.id.pound) { |
| return KEY_FRAME_DURATION * 11; |
| } |
| } |
| |
| LogUtil.e(TAG, "Attempted to get animation delay for invalid key button id."); |
| return 0; |
| } |
| |
| /** |
| * Get the button animation duration, taking into account whether the dialpad is in landscape |
| * left-to-right, landscape right-to-left, or portrait. |
| * |
| * @param buttonId The button ID. |
| * @return The animation duration. |
| */ |
| private int getKeyButtonAnimationDuration(int buttonId) { |
| if (isLandscapeMode) { |
| if (isRtl) { |
| if (buttonId == R.id.one |
| || buttonId == R.id.four |
| || buttonId == R.id.seven |
| || buttonId == R.id.star) { |
| return KEY_FRAME_DURATION * 8; |
| } else if (buttonId == R.id.two |
| || buttonId == R.id.five |
| || buttonId == R.id.eight |
| || buttonId == R.id.zero) { |
| return KEY_FRAME_DURATION * 9; |
| } else if (buttonId == R.id.three |
| || buttonId == R.id.six |
| || buttonId == R.id.nine |
| || buttonId == R.id.pound) { |
| return KEY_FRAME_DURATION * 10; |
| } |
| } else { |
| if (buttonId == R.id.one |
| || buttonId == R.id.four |
| || buttonId == R.id.seven |
| || buttonId == R.id.star) { |
| return KEY_FRAME_DURATION * 10; |
| } else if (buttonId == R.id.two |
| || buttonId == R.id.five |
| || buttonId == R.id.eight |
| || buttonId == R.id.zero) { |
| return KEY_FRAME_DURATION * 9; |
| } else if (buttonId == R.id.three |
| || buttonId == R.id.six |
| || buttonId == R.id.nine |
| || buttonId == R.id.pound) { |
| return KEY_FRAME_DURATION * 8; |
| } |
| } |
| } else { |
| if (buttonId == R.id.one |
| || buttonId == R.id.two |
| || buttonId == R.id.three |
| || buttonId == R.id.four |
| || buttonId == R.id.five |
| || buttonId == R.id.six) { |
| return KEY_FRAME_DURATION * 10; |
| } else if (buttonId == R.id.seven || buttonId == R.id.eight || buttonId == R.id.nine) { |
| return KEY_FRAME_DURATION * 9; |
| } else if (buttonId == R.id.star || buttonId == R.id.zero || buttonId == R.id.pound) { |
| return KEY_FRAME_DURATION * 8; |
| } |
| } |
| |
| LogUtil.e(TAG, "Attempted to get animation duration for invalid key button id."); |
| return 0; |
| } |
| |
| /** |
| * An {@link OnPreDrawListener} that adjusts the height/width of each key layout so that they can |
| * be properly aligned. |
| * |
| * <p>When the device is in portrait mode, the layout height for key "1" can be different from |
| * those of other <b>digit</b> keys due to the voicemail icon. Adjustments are needed to ensure |
| * the layouts for all <b>digit</b> keys are of the same height. Key "*" and key "#" are excluded |
| * because their styles are different from other keys'. |
| * |
| * <p>When the device is in landscape mode, keys can have different layout widths due to the |
| * icon/characters associated with them. Adjustments are needed to ensure the layouts for all keys |
| * are of the same width. |
| * |
| * <p>Note that adjustments can only be made after the layouts are measured, which is why the |
| * logic lives in an {@link OnPreDrawListener} that is invoked when the view tree is about to be |
| * drawn. |
| */ |
| private class OnPreDrawListenerForKeyLayoutAdjust implements OnPreDrawListener { |
| |
| /** |
| * This method is invoked when the view tree is about to be drawn. At this point, all views in |
| * the tree have been measured and given a frame. |
| * |
| * <p>If the keys have been adjusted, we instruct the current drawing pass to proceed by |
| * returning true. Otherwise, adjustments will be made and the current drawing pass will be |
| * cancelled by returning false. |
| * |
| * <p>It is imperative to schedule another layout pass of the view tree after adjustments are |
| * made so that {@link #onPreDraw()} can be invoked again to check the layouts and proceed with |
| * the drawing pass. |
| */ |
| @Override |
| public boolean onPreDraw() { |
| if (!shouldAdjustKeySizes()) { |
| // Return true to proceed with the current drawing pass. |
| // Note that we must NOT remove this listener here. The fact that we don't need to adjust |
| // keys at the moment doesn't mean they won't need adjustments in the future. For example, |
| // when DialpadFragment is hidden, all keys are of the same size (0) and nothing needs to be |
| // done. Removing the listener will cost us the ability to adjust them when they reappear. |
| // It is only safe to remove the listener after adjusting keys for the first time. See the |
| // comment below for more details. |
| return true; |
| } |
| |
| adjustKeySizes(); |
| |
| // After all keys are adjusted for the first time, no more adjustments will be needed during |
| // the rest of DialpadView's lifecycle. It is therefore safe to remove this listener. |
| // Another important reason for removing the listener is that it can be triggered AFTER a |
| // device orientation change but BEFORE DialpadView's onDetachedFromWindow() and |
| // onFinishInflate() are called, i.e., the listener will attempt to adjust the layout before |
| // it is inflated, which results in a crash. |
| getViewTreeObserver().removeOnPreDrawListener(this); |
| |
| return false; // Return false to cancel the current drawing pass. |
| } |
| |
| private boolean shouldAdjustKeySizes() { |
| return isLandscapeMode ? shouldAdjustKeyWidths() : shouldAdjustDigitKeyHeights(); |
| } |
| |
| /** |
| * Return true if not all key layouts have the same width. This method must be called when the |
| * device is in landscape mode. |
| */ |
| private boolean shouldAdjustKeyWidths() { |
| Assert.checkState(isLandscapeMode); |
| |
| DialpadKeyButton dialpadKeyButton = (DialpadKeyButton) findViewById(BUTTON_IDS[0]); |
| LinearLayout keyLayout = |
| (LinearLayout) dialpadKeyButton.findViewById(R.id.dialpad_key_layout); |
| final int width = keyLayout.getWidth(); |
| |
| for (int i = 1; i < BUTTON_IDS.length; i++) { |
| dialpadKeyButton = (DialpadKeyButton) findViewById(BUTTON_IDS[i]); |
| keyLayout = (LinearLayout) dialpadKeyButton.findViewById(R.id.dialpad_key_layout); |
| if (width != keyLayout.getWidth()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Return true if not all <b>digit</b> key layouts have the same height. This method must be |
| * called when the device is in portrait mode. |
| */ |
| private boolean shouldAdjustDigitKeyHeights() { |
| Assert.checkState(!isLandscapeMode); |
| |
| DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[0]); |
| LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout); |
| final int height = keyLayout.getHeight(); |
| |
| // BUTTON_IDS[i] is the resource ID for button i when 0 <= i && i <= 9. |
| // For example, BUTTON_IDS[3] is the resource ID for button "3" on the dialpad. |
| for (int i = 1; i <= 9; i++) { |
| dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]); |
| keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout); |
| if (height != keyLayout.getHeight()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private void adjustKeySizes() { |
| if (isLandscapeMode) { |
| adjustKeyWidths(); |
| } else { |
| adjustDigitKeyHeights(); |
| } |
| } |
| |
| /** |
| * Make the heights of all <b>digit</b> keys the same. |
| * |
| * <p>When the device is in portrait mode, we first find the maximum height among digit key |
| * layouts. Then for each key, we adjust the height of the layout containing letters/the |
| * voicemail icon to ensure the height of each digit key is the same. |
| * |
| * <p>A layout pass will be scheduled in this method by {@link |
| * LinearLayout#setLayoutParams(ViewGroup.LayoutParams)}. |
| */ |
| private void adjustDigitKeyHeights() { |
| Assert.checkState(!isLandscapeMode); |
| |
| int maxHeight = 0; |
| |
| // BUTTON_IDS[i] is the resource ID for button i when 0 <= i && i <= 9. |
| // For example, BUTTON_IDS[3] is the resource ID for button "3" on the dialpad. |
| for (int i = 0; i <= 9; i++) { |
| DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]); |
| LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout); |
| maxHeight = Math.max(maxHeight, keyLayout.getHeight()); |
| } |
| |
| for (int i = 0; i <= 9; i++) { |
| DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]); |
| LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout); |
| |
| DialpadTextView numberView = |
| (DialpadTextView) keyLayout.findViewById(R.id.dialpad_key_number); |
| MarginLayoutParams numberViewLayoutParams = |
| (MarginLayoutParams) numberView.getLayoutParams(); |
| |
| LinearLayout iconOrLettersLayout = |
| (LinearLayout) keyLayout.findViewById(R.id.dialpad_key_icon_or_letters_layout); |
| iconOrLettersLayout.setLayoutParams( |
| new LayoutParams( |
| LayoutParams.WRAP_CONTENT /* width */, |
| maxHeight |
| - numberView.getHeight() |
| - numberViewLayoutParams.topMargin |
| - numberViewLayoutParams.bottomMargin /* height */)); |
| } |
| } |
| |
| /** |
| * Make the widths of all keys the same. |
| * |
| * <p>When the device is in landscape mode, we first find the maximum width among key layouts. |
| * Then we adjust the width of each layout's horizontal placeholder so that each key has the |
| * same width. |
| * |
| * <p>A layout pass will be scheduled in this method by {@link |
| * View#setLayoutParams(ViewGroup.LayoutParams)}. |
| */ |
| private void adjustKeyWidths() { |
| Assert.checkState(isLandscapeMode); |
| |
| int maxWidth = 0; |
| for (int buttonId : BUTTON_IDS) { |
| DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(buttonId); |
| LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout); |
| maxWidth = Math.max(maxWidth, keyLayout.getWidth()); |
| } |
| |
| for (int buttonId : BUTTON_IDS) { |
| DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(buttonId); |
| LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout); |
| View horizontalPlaceholder = |
| keyLayout.findViewById(R.id.dialpad_key_horizontal_placeholder); |
| horizontalPlaceholder.setLayoutParams( |
| new LayoutParams( |
| maxWidth - keyLayout.getWidth() /* width */, |
| LayoutParams.MATCH_PARENT /* height */)); |
| } |
| } |
| } |
| } |