diff options
Diffstat (limited to 'java/com/android/incallui/incall/impl/CheckableLabeledButton.java')
-rw-r--r-- | java/com/android/incallui/incall/impl/CheckableLabeledButton.java | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/java/com/android/incallui/incall/impl/CheckableLabeledButton.java b/java/com/android/incallui/incall/impl/CheckableLabeledButton.java new file mode 100644 index 000000000..a681adcb4 --- /dev/null +++ b/java/com/android/incallui/incall/impl/CheckableLabeledButton.java @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2016 The Android Open Source 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.incallui.incall.impl; + +import android.animation.AnimatorInflater; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.PorterDuff.Mode; +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.DrawableRes; +import android.support.annotation.StringRes; +import android.text.TextUtils.TruncateAt; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.SoundEffectConstants; +import android.widget.Checkable; +import android.widget.ImageView; +import android.widget.ImageView.ScaleType; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** A button to show on the incall screen */ +public class CheckableLabeledButton extends LinearLayout implements Checkable { + + private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked}; + private static final float DISABLED_STATE_OPACITY = .3f; + private boolean broadcasting; + private boolean isChecked; + private OnCheckedChangeListener onCheckedChangeListener; + private ImageView iconView; + private TextView labelView; + private Drawable background; + private Drawable backgroundMore; + + public CheckableLabeledButton(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public CheckableLabeledButton(Context context) { + this(context, null); + } + + private void init(Context context, AttributeSet attrs) { + setOrientation(VERTICAL); + setGravity(Gravity.CENTER_HORIZONTAL); + Drawable icon; + CharSequence labelText; + boolean enabled; + + backgroundMore = getResources().getDrawable(R.drawable.incall_button_background_more, null); + background = getResources().getDrawable(R.drawable.incall_button_background, null); + + TypedArray typedArray = + context.obtainStyledAttributes(attrs, R.styleable.CheckableLabeledButton); + icon = typedArray.getDrawable(R.styleable.CheckableLabeledButton_incall_icon); + labelText = typedArray.getString(R.styleable.CheckableLabeledButton_incall_labelText); + enabled = typedArray.getBoolean(R.styleable.CheckableLabeledButton_android_enabled, true); + typedArray.recycle(); + + int paddingSize = getResources().getDimensionPixelOffset(R.dimen.incall_button_padding); + setPadding(paddingSize, paddingSize, paddingSize, paddingSize); + + int iconSize = getResources().getDimensionPixelSize(R.dimen.incall_labeled_button_size); + + iconView = new ImageView(context, null, android.R.style.Widget_Material_Button_Colored); + LayoutParams iconParams = generateDefaultLayoutParams(); + iconParams.width = iconSize; + iconParams.height = iconSize; + iconView.setLayoutParams(iconParams); + iconView.setScaleType(ScaleType.CENTER_INSIDE); + iconView.setImageDrawable(icon); + iconView.setImageTintMode(Mode.SRC_IN); + iconView.setImageTintList(getResources().getColorStateList(R.color.incall_button_icon, null)); + iconView.setBackground(getResources().getDrawable(R.drawable.incall_button_background, null)); + iconView.setDuplicateParentStateEnabled(true); + iconView.setElevation(getResources().getDimension(R.dimen.incall_button_elevation)); + iconView.setStateListAnimator( + AnimatorInflater.loadStateListAnimator(context, R.animator.incall_button_elevation)); + addView(iconView); + + labelView = new TextView(context); + LayoutParams labelParams = generateDefaultLayoutParams(); + labelParams.width = LayoutParams.WRAP_CONTENT; + labelParams.height = LayoutParams.WRAP_CONTENT; + labelParams.topMargin = + context.getResources().getDimensionPixelOffset(R.dimen.incall_button_label_margin); + labelView.setLayoutParams(labelParams); + labelView.setTextAppearance(R.style.Dialer_Incall_TextAppearance_Label); + labelView.setText(labelText); + labelView.setSingleLine(); + labelView.setMaxEms(9); + labelView.setEllipsize(TruncateAt.END); + labelView.setGravity(Gravity.CENTER); + labelView.setDuplicateParentStateEnabled(true); + addView(labelView); + + setFocusable(true); + setClickable(true); + setEnabled(enabled); + setOutlineProvider(null); + } + + @Override + public void refreshDrawableState() { + super.refreshDrawableState(); + iconView.setAlpha(isEnabled() ? 1f : DISABLED_STATE_OPACITY); + labelView.setAlpha(isEnabled() ? 1f : DISABLED_STATE_OPACITY); + } + + public void setIconDrawable(@DrawableRes int drawableRes) { + iconView.setImageResource(drawableRes); + } + + public void setLabelText(@StringRes int stringRes) { + labelView.setText(stringRes); + } + + /** Shows or hides a little down arrow to indicate that the button will pop up a menu. */ + public void setShouldShowMoreIndicator(boolean shouldShow) { + iconView.setBackground(shouldShow ? backgroundMore : background); + } + + @Override + public boolean isChecked() { + return isChecked; + } + + @Override + public void setChecked(boolean checked) { + performSetChecked(checked); + } + + @Override + public void toggle() { + userRequestedSetChecked(!isChecked()); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + if (isChecked()) { + mergeDrawableStates(drawableState, CHECKED_STATE_SET); + } + return drawableState; + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + invalidate(); + } + + public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { + this.onCheckedChangeListener = listener; + } + + @Override + public boolean performClick() { + if (!isCheckable()) { + return super.performClick(); + } + + toggle(); + final boolean handled = super.performClick(); + if (!handled) { + // View only makes a sound effect if the onClickListener was + // called, so we'll need to make one here instead. + playSoundEffect(SoundEffectConstants.CLICK); + } + return handled; + } + + private boolean isCheckable() { + return onCheckedChangeListener != null; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + SavedState savedState = (SavedState) state; + super.onRestoreInstanceState(savedState.getSuperState()); + performSetChecked(savedState.isChecked); + requestLayout(); + } + + @Override + protected Parcelable onSaveInstanceState() { + return new SavedState(isChecked(), super.onSaveInstanceState()); + } + + /** + * Called when the state of the button should be updated, this should not be the result of user + * interaction. + * + * @param checked {@code true} if the button should be in the checked state, {@code false} + * otherwise. + */ + private void performSetChecked(boolean checked) { + if (isChecked() == checked) { + return; + } + isChecked = checked; + refreshDrawableState(); + } + + /** + * Called when the user interacts with a button. This should not result in the button updating + * state, rather the request should be propagated to the associated listener. + * + * @param checked {@code true} if the button should be in the checked state, {@code false} + * otherwise. + */ + private void userRequestedSetChecked(boolean checked) { + if (isChecked() == checked) { + return; + } + if (broadcasting) { + return; + } + broadcasting = true; + if (onCheckedChangeListener != null) { + onCheckedChangeListener.onCheckedChanged(this, checked); + } + broadcasting = false; + } + + /** Callback interface to notify when the button's checked state has changed */ + public interface OnCheckedChangeListener { + + void onCheckedChanged(CheckableLabeledButton checkableLabeledButton, boolean isChecked); + } + + private static class SavedState extends BaseSavedState { + + public static final Creator<SavedState> CREATOR = + new Creator<SavedState>() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + public final boolean isChecked; + + private SavedState(boolean isChecked, Parcelable superState) { + super(superState); + this.isChecked = isChecked; + } + + protected SavedState(Parcel in) { + super(in); + isChecked = in.readByte() != 0; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeByte((byte) (isChecked ? 1 : 0)); + } + } +} |