311 lines
9.8 KiB
Java
311 lines
9.8 KiB
Java
/*
|
|
* 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.ColorStateList;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.Color;
|
|
import android.graphics.PorterDuff.Mode;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.support.annotation.ColorInt;
|
|
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.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;
|
|
@DrawableRes private int iconResource = 0;
|
|
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, context.getTheme());
|
|
background =
|
|
getResources().getDrawable(R.drawable.incall_button_background, context.getTheme());
|
|
|
|
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);
|
|
int imageSize = getResources().getDimensionPixelSize(R.dimen.incall_labeled_button_icon_size);
|
|
int iconPadding = (iconSize - imageSize) / 2;
|
|
|
|
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.setPadding(iconPadding, iconPadding, iconPadding, iconPadding);
|
|
iconView.setImageDrawable(icon);
|
|
iconView.setImageTintMode(Mode.SRC_IN);
|
|
iconView.setImageTintList(
|
|
getResources().getColorStateList(R.color.incall_button_icon, context.getTheme()));
|
|
|
|
iconView.setBackground(
|
|
getResources().getDrawable(R.drawable.incall_button_background, context.getTheme()));
|
|
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 setCheckedColor(@ColorInt int color) {
|
|
iconView.setImageTintList(
|
|
new ColorStateList(
|
|
new int[][] {new int[] {android.R.attr.state_checked}, new int[] {}},
|
|
new int[] {color, Color.WHITE}));
|
|
}
|
|
|
|
public Drawable getIconDrawable() {
|
|
return iconView.getDrawable();
|
|
}
|
|
|
|
public void setIconDrawable(@DrawableRes int drawableRes) {
|
|
if (iconResource != drawableRes) {
|
|
iconView.setImageResource(drawableRes);
|
|
iconResource = 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));
|
|
}
|
|
}
|
|
}
|