package com.beardedhen.androidbootstrap;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewParent;
import com.beardedhen.androidbootstrap.api.attributes.BootstrapBrand;
import com.beardedhen.androidbootstrap.api.attributes.ViewGroupPosition;
import com.beardedhen.androidbootstrap.api.defaults.ButtonMode;
import com.beardedhen.androidbootstrap.api.defaults.DefaultBootstrapSize;
import com.beardedhen.androidbootstrap.api.view.BadgeContainerView;
import com.beardedhen.androidbootstrap.api.view.BootstrapBadgeView;
import com.beardedhen.androidbootstrap.api.view.BootstrapSizeView;
import com.beardedhen.androidbootstrap.api.view.ButtonModeView;
import com.beardedhen.androidbootstrap.api.view.OutlineableView;
import com.beardedhen.androidbootstrap.api.view.RoundableView;
import com.beardedhen.androidbootstrap.utils.DimenUtils;
import com.beardedhen.androidbootstrap.utils.ViewUtils;
import java.io.Serializable;
/**
* BootstrapButtons are regular buttons styled with BootstrapBrand colors, roundable corners, and an
* 'outlineable' mode. It is possible to group together multiple buttons using BootstrapButtonGroup,
* allowing the use of different selection modes e.g. Checkbox/Radio group.
*/
public class BootstrapButton extends AwesomeTextView implements BootstrapSizeView,
OutlineableView, RoundableView, ButtonModeView, BadgeContainerView,
BootstrapBadgeView {
/**
* instances of this can be used with .setOnCheckedChangedLisener to notify you when the state of a radio, togle or checkbox button has changed.
*/
public interface OnCheckedChangedListener{
/**
* This method will get called when the state of a radio button, checkbox or toggle button changes.
* @param bootstrapButton the view thats state is changing
* @param isChecked weather the button is checked or not.
*/
public void OnCheckedChanged(BootstrapButton bootstrapButton, boolean isChecked);
}
private static final String TAG = "com.beardedhen.androidbootstrap.BootstrapButton";
private static final String KEY_MODE = "com.beardedhen.androidbootstrap.BootstrapButton.MODE";
private static final String KEY_INDEX = "com.beardedhen.androidbootstrap.BootstrapButton.KEY_INDEX";
private int parentIndex;
private ViewGroupPosition viewGroupPosition = ViewGroupPosition.SOLO;
private ButtonMode buttonMode = ButtonMode.REGULAR;
private float bootstrapSize;
private boolean roundedCorners;
private boolean showOutline;
private boolean mustBeSelected;
private float baselineFontSize;
private float baselineVertPadding;
private float baselineHoriPadding;
private float baselineStrokeWidth;
private float baselineCornerRadius;
private BootstrapBadge bootstrapBadge;
private String badgeText;
private OnCheckedChangedListener onCheckedChangedListener;
public BootstrapButton(Context context) {
super(context);
initialise(null);
}
public BootstrapButton(Context context, AttributeSet attrs) {
super(context, attrs);
initialise(attrs);
}
public BootstrapButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialise(attrs);
}
private void initialise(AttributeSet attrs) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.BootstrapButton);
viewGroupPosition = ViewGroupPosition.SOLO;
try {
this.roundedCorners = a.getBoolean(R.styleable.BootstrapButton_roundedCorners, false);
this.showOutline = a.getBoolean(R.styleable.BootstrapButton_showOutline, false);
this.mustBeSelected = a.getBoolean(R.styleable.BootstrapButton_checked, false);
this.badgeText = a.getString(R.styleable.BootstrapButton_badgeText);
int sizeOrdinal = a.getInt(R.styleable.BootstrapButton_bootstrapSize, -1);
int modeOrdinal = a.getInt(R.styleable.BootstrapButton_buttonMode, -1);
bootstrapSize = DefaultBootstrapSize.fromAttributeValue(sizeOrdinal).scaleFactor();
buttonMode = ButtonMode.fromAttributeValue(modeOrdinal);
}
finally {
a.recycle();
}
baselineFontSize = DimenUtils.pixelsFromSpResource(getContext(), R.dimen.bootstrap_button_default_font_size);
baselineVertPadding = DimenUtils.pixelsFromDpResource(getContext(), R.dimen.bootstrap_button_default_vert_padding);
baselineHoriPadding = DimenUtils.pixelsFromDpResource(getContext(), R.dimen.bootstrap_button_default_hori_padding);
baselineStrokeWidth = DimenUtils.pixelsFromDpResource(getContext(), R.dimen.bootstrap_button_default_edge_width);
baselineCornerRadius = DimenUtils.pixelsFromDpResource(getContext(), R.dimen.bootstrap_button_default_corner_radius);
updateBootstrapState();
if (badgeText != null) {
setBadge(new BootstrapBadge(getContext()));
setBadgeText(badgeText);
}
}
@Override public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(TAG, super.onSaveInstanceState());
bundle.putBoolean(RoundableView.KEY, roundedCorners);
bundle.putBoolean(OutlineableView.KEY, showOutline);
bundle.putInt(KEY_INDEX, parentIndex);
bundle.putFloat(BootstrapSizeView.KEY, bootstrapSize);
bundle.putSerializable(KEY_MODE, buttonMode);
if (bootstrapBadge != null) {
bundle.putString(BadgeContainerView.KEY, bootstrapBadge
.getBadgeText());
}
return bundle;
}
@Override public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
this.roundedCorners = bundle.getBoolean(RoundableView.KEY);
this.showOutline = bundle.getBoolean(OutlineableView.KEY);
this.parentIndex = bundle.getInt(KEY_INDEX);
this.bootstrapSize = bundle.getFloat(BootstrapSizeView.KEY);
if (bootstrapBadge != null) {
setBadgeText(bundle.getString(BadgeContainerView.KEY));
}
Serializable m = bundle.getSerializable(KEY_MODE);
if (m instanceof ButtonMode) {
buttonMode = (ButtonMode) m;
}
}
super.onRestoreInstanceState(state);
}
@Override protected void updateBootstrapState() {
super.updateBootstrapState();
BootstrapBrand bootstrapBrand = getBootstrapBrand();
float cornerRadius = baselineCornerRadius;
float strokeWidth = baselineStrokeWidth;
final float fontSize = baselineFontSize * bootstrapSize;
setTextSize(fontSize);
cornerRadius *= bootstrapSize;
strokeWidth *= bootstrapSize;
setTextColor(BootstrapDrawableFactory.bootstrapButtonText(
getContext(),
showOutline,
bootstrapBrand));
Drawable bg = BootstrapDrawableFactory.bootstrapButton(
getContext(),
bootstrapBrand,
(int) strokeWidth,
(int) cornerRadius,
viewGroupPosition,
showOutline,
roundedCorners);
ViewUtils.setBackgroundDrawable(this, bg);
int vert = (int) (baselineVertPadding * bootstrapSize);
int hori = (int) (baselineHoriPadding * bootstrapSize);
setPadding(hori, vert, hori, vert);
}
@Override public boolean onTouchEvent(@NonNull MotionEvent event) {
switch (buttonMode) {
case REGULAR:
return super.onTouchEvent(event);
case TOGGLE:
return handleToggle(event);
case CHECKBOX:
return handleToggle(event);
case RADIO:
return handleRadioEvent(event);
default:
return super.onTouchEvent(event);
}
}
@Override
public void setSelected(boolean selected) {
super.setSelected(selected);
if (onCheckedChangedListener != null) {
onCheckedChangedListener.OnCheckedChanged(this, selected);
}
}
private boolean handleRadioEvent(@NonNull MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
setSelected(true); // notify parent to deselect any peers
ViewParent parent = getParent();
if (parent instanceof BootstrapButtonGroup) {
((BootstrapButtonGroup) parent).onRadioToggle(parentIndex);
}
return true;
}
else {
return false;
}
}
private boolean handleToggle(@NonNull MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
setSelected(!isSelected());
return true;
}
else {
return false;
}
}
/**
* Called by the ViewParent, notifies the child of its position so that it can update its
* drawable to match the position
*
* @param viewGroupPosition the position in the ViewGroup
*/
void setViewGroupPosition(ViewGroupPosition viewGroupPosition, int parentIndex) {
this.viewGroupPosition = viewGroupPosition;
this.parentIndex = parentIndex;
updateBootstrapState();
}
void updateFromParent(BootstrapBrand bootstrapBrand,
float bootstrapSize,
ButtonMode buttonMode,
boolean outline,
boolean rounded) { // called when viewparent attrs are updated
// called by BootstrapButtonGroup when updating state all at once
this.bootstrapSize = bootstrapSize;
this.buttonMode = buttonMode;
this.showOutline = outline;
this.roundedCorners = rounded;
setBootstrapBrand(bootstrapBrand);
updateBootstrapState();
}
@Override public void displayBadgeDrawable() {
if (bootstrapBadge != null) {
Drawable badgeDrawable = this.bootstrapBadge.getBadgeDrawable();
if (badgeDrawable != null) {
setCompoundDrawablesWithIntrinsicBounds(
null,
null, badgeDrawable,
null);
setCompoundDrawablePadding(DimenUtils.dpToPixels(4));
}
}
}
/*
* Getters/Setters
*/
public boolean isMustBeSelected() {
return mustBeSelected;
}
public void setChecked(boolean checked) {
this.mustBeSelected = checked;
}
@Override public boolean isShowOutline() {
return showOutline;
}
@Override public boolean isRounded() {
return roundedCorners;
}
@Override public void setShowOutline(boolean showOutline) {
this.showOutline = showOutline;
updateBootstrapState();
}
@Override public void setRounded(boolean rounded) {
this.roundedCorners = rounded;
updateBootstrapState();
}
@NonNull @Override public ButtonMode getButtonMode() {
return buttonMode;
}
@Override public void setButtonMode(@NonNull ButtonMode buttonMode) {
this.buttonMode = buttonMode;
}
@Override public void setBadge(BootstrapBadge badge) {
this.bootstrapBadge = badge;
this.bootstrapBadge.setBootstrapBrand(getBootstrapBrand(), true);
this.bootstrapBadge.setBootstrapSize(getBootstrapSize());
displayBadgeDrawable();
}
@Nullable
@Override
public String getBadgeText() {
return bootstrapBadge != null ? bootstrapBadge.getBadgeText() : null;
}
@Override
public void setBadgeText(@Nullable String badgeText) {
if (bootstrapBadge != null) {
this.badgeText = badgeText;
this.bootstrapBadge.setBadgeText(this.badgeText);
displayBadgeDrawable();
}
}
@Override public float getBootstrapSize() {
return bootstrapSize;
}
@Override public BootstrapBadge getBootstrapBadge() {
return bootstrapBadge;
}
@Override public void setBootstrapSize(DefaultBootstrapSize bootstrapSize) {
setBootstrapSize(bootstrapSize.scaleFactor());
}
@Override public void setBootstrapSize(float bootstrapSize) {
this.bootstrapSize = bootstrapSize;
updateBootstrapState();
}
/**
* NOTE this method only works if the buttons mode is not set to regular.
* for non Toggle, checkbox and radio see {@link BootstrapButton#setOnClickListener}
* @param listener OnCheckedChangedListener that will be fired when the schecked state ofa button is changed.
*/
public void setOnCheckedChangedListener(OnCheckedChangedListener listener){
onCheckedChangedListener = listener;
}
/**
* NOTE this method only works if the buttons mode is set to regular.
* for Toggle, checkbox and radio see {@link BootstrapButton#setOnCheckedChangedListener}
* @param l OnClickListener that will be fired on click.
*/
@Override
public void setOnClickListener(OnClickListener l) {
super.setOnClickListener(l);
}
}