package com.williammora.snackbar;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.williammora.snackbar.listeners.SwipeDismissTouchListener;
import com.fastbootmobile.encore.app.R;
/**
* View that provides quick feedback about an operation in a small popup at the base of the screen
*/
public class Snackbar extends SnackbarLayout {
private static int PHONE_MIN_HEIGHT_DP = 56;
private static int PHONE_MAX_HEIGHT_DP = 80;
private static int TABLET_MIN_WIDTH_DP = 288;
private static int TABLET_MAX_WIDTH_DP = 568;
private static int TABLET_HEIGHT_DP = 48;
private static int TABLET_MARGIN_DP = 24;
public enum SnackbarType {
SINGLE_LINE(PHONE_MIN_HEIGHT_DP, 1), MULTI_LINE(PHONE_MAX_HEIGHT_DP, 2);
private int height;
private int maxLines;
SnackbarType(int height, int maxLines) {
this.height = height;
this.maxLines = maxLines;
}
public int getHeight() {
return height;
}
public int getMaxLines() {
return maxLines;
}
}
public enum SnackbarDuration {
LENGTH_SHORT(2000), LENGTH_LONG(3500), LENGTH_VERY_LONG(30000);
private long duration;
SnackbarDuration(long duration) {
this.duration = duration;
}
public long getDuration() {
return duration;
}
}
private SnackbarType mType = SnackbarType.SINGLE_LINE;
private SnackbarDuration mDuration = SnackbarDuration.LENGTH_LONG;
private CharSequence mText;
private int mColor = 0xff323232;
private int mTextColor = Color.WHITE;
private CharSequence mActionLabel;
private int mActionColor = Color.GREEN;
private boolean mAnimated = true;
private long mCustomDuration = -1;
private ActionClickListener mActionClickListener;
private boolean mShouldDismiss = true;
private DismissListener mDismissListener;
private boolean mIsShowing = false;
private Snackbar(Context context) {
super(context);
}
public static Snackbar with(Context context) {
return new Snackbar(context);
}
/**
* Sets the type of {@link Snackbar} to be displayed.
*
* @param type the {@link Snackbar.SnackbarType} of this instance
* @return
*/
public Snackbar type(SnackbarType type) {
mType = type;
return this;
}
/**
* Sets the text to be displayed in this {@link Snackbar}
*
* @param text
* @return
*/
public Snackbar text(CharSequence text) {
mText = text;
return this;
}
/**
* Sets the background color of this {@link Snackbar}
*
* @param color
* @return
*/
public Snackbar color(int color) {
mColor = color;
return this;
}
/**
* Sets the text color of this {@link Snackbar}
*
* @param textColor
* @return
*/
public Snackbar textColor(int textColor) {
mTextColor = textColor;
return this;
}
/**
* Sets the action label to be displayed, if any. Note that if this is not set, the action
* button will not be displayed
*
* @param actionButtonLabel
* @return
*/
public Snackbar actionLabel(CharSequence actionButtonLabel) {
mActionLabel = actionButtonLabel;
return this;
}
/**
* Sets the color of the action button label. Note that you must set a button label with
* {@link Snackbar#actionLabel(CharSequence)} for this button to be displayed
*
* @param actionColor
* @return
*/
public Snackbar actionColor(int actionColor) {
mActionColor = actionColor;
return this;
}
/**
* Determines whether this {@link Snackbar} should dismiss when the action button is touched
*
* @param shouldDismiss
* @return
*/
public Snackbar dismissOnActionClicked(boolean shouldDismiss) {
mShouldDismiss = shouldDismiss;
return this;
}
/**
* Sets the listener to be called when the {@link Snackbar} action is
* selected. Note that you must set a button label with
* {@link Snackbar#actionLabel(CharSequence)} for this button to be displayed
*
* @param listener
* @return
*/
public Snackbar actionListener(ActionClickListener listener) {
mActionClickListener = listener;
return this;
}
/**
* Sets the listener to be called when the {@link Snackbar} is dismissed.
*
* @param listener
* @return
*/
public Snackbar dismissListener(DismissListener listener) {
mDismissListener = listener;
return this;
}
/**
* Sets on/off animation for this {@link Snackbar}
*
* @param withAnimation
* @return
*/
public Snackbar animation(boolean withAnimation) {
mAnimated = withAnimation;
return this;
}
/**
* Sets the duration of this {@link Snackbar}. See
* {@link Snackbar.SnackbarDuration} for available options
*
* @param duration
* @return
*/
public Snackbar duration(SnackbarDuration duration) {
mDuration = duration;
return this;
}
/**
* Sets a custom duration of this {@link Snackbar}
*
* @param duration
* @return
*/
public Snackbar duration(long duration) {
mCustomDuration = duration;
return this;
}
private FrameLayout.LayoutParams init(Activity parent) {
SnackbarLayout layout = (SnackbarLayout) LayoutInflater.from(parent)
.inflate(R.layout.widget_snackbar, this, true);
float scale = getResources().getDisplayMetrics().density;
FrameLayout.LayoutParams params;
if (getResources().getConfiguration().smallestScreenWidthDp < 600) {
// Phone
layout.setMinimumHeight(dpToPx(PHONE_MIN_HEIGHT_DP, scale));
layout.setMaxHeight(dpToPx(mType.getHeight(), scale));
layout.setBackgroundColor(mColor);
params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
} else {
// Tablet/desktop
layout.setMinimumWidth(dpToPx(TABLET_MIN_WIDTH_DP, scale));
layout.setMaxWidth(dpToPx(TABLET_MAX_WIDTH_DP, scale));
layout.setBackgroundResource(R.drawable.snackbar_background);
GradientDrawable bg = (GradientDrawable) layout.getBackground();
bg.setColor(mColor);
params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT, dpToPx(TABLET_HEIGHT_DP, scale));
int margin = dpToPx(TABLET_MARGIN_DP, scale);
params.leftMargin = margin;
params.bottomMargin = margin;
}
params.gravity = Gravity.BOTTOM;
TextView snackbarText = (TextView) layout.findViewById(R.id.snackbar_text);
snackbarText.setText(mText);
snackbarText.setTextColor(mTextColor);
snackbarText.setMaxLines(mType.getMaxLines());
TextView snackbarAction = (TextView) layout.findViewById(R.id.snackbar_action);
if (!TextUtils.isEmpty(mActionLabel)) {
snackbarAction.setText(mActionLabel);
snackbarAction.setTextColor(mActionColor);
snackbarAction.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (mActionClickListener != null) {
mActionClickListener.onActionClicked();
}
if (mShouldDismiss) {
dismiss();
}
}
});
snackbarAction.setMaxLines(mType.getMaxLines());
} else {
snackbarAction.setVisibility(GONE);
}
setClickable(true);
setOnTouchListener(new SwipeDismissTouchListener(this, null,
new SwipeDismissTouchListener.DismissCallbacks() {
@Override
public boolean canDismiss(Object token) {
return true;
}
@Override
public void onDismiss(View view, Object token) {
if (view != null) {
dismiss();
}
}
}));
return params;
}
private static int dpToPx(int dp, float scale) {
return (int) (dp * scale + 0.5f);
}
/**
* Displays the {@link Snackbar} at the bottom of the
* {@link android.app.Activity} provided.
*
* @param targetActivity
*/
public void show(Activity targetActivity) {
FrameLayout.LayoutParams params = init(targetActivity);
ViewGroup root = (ViewGroup) targetActivity.findViewById(android.R.id.content);
root.addView(this, params);
mIsShowing = true;
if (mAnimated) {
startSnackbarAnimation();
} else {
postDelayed(new Runnable() {
@Override
public void run() {
dismiss();
}
}, getDuration());
}
}
private void startSnackbarAnimation() {
final Animation fadeOut = AnimationUtils.loadAnimation(getContext(), R.anim.snackbar_out);
fadeOut.setStartOffset(getDuration());
fadeOut.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
post(new Runnable() {
@Override
public void run() {
dismiss();
}
});
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
Animation slideIn = AnimationUtils.loadAnimation(getContext(), R.anim.snackbar_in);
slideIn.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
post(new Runnable() {
@Override
public void run() {
startAnimation(fadeOut);
}
});
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
startAnimation(slideIn);
}
public void dismiss() {
clearAnimation();
ViewGroup parent = (ViewGroup) getParent();
if (parent != null) {
parent.removeView(this);
}
if (mDismissListener != null && mIsShowing) {
mDismissListener.onDismiss();
}
mIsShowing = false;
}
public int getActionColor() {
return mActionColor;
}
public CharSequence getActionLabel() {
return mActionLabel;
}
public int getTextColor() {
return mTextColor;
}
public int getColor() {
return mColor;
}
public CharSequence getText() {
return mText;
}
public long getDuration() {
return mCustomDuration == -1 ? mDuration.getDuration() : mCustomDuration;
}
public SnackbarType getType() {
return mType;
}
public boolean isAnimated() {
return mAnimated;
}
public boolean shouldDismissOnActionClicked() {
return mShouldDismiss;
}
/**
* Returns whether this {@link com.williammora.snackbar.Snackbar} is currently showing
* @return
*/
public boolean isShowing() {
return mIsShowing;
}
/**
* Returns whether this {@link com.williammora.snackbar.Snackbar} has been dismissed
* @return
*/
public boolean isDismissed() {
return !mIsShowing;
}
public interface ActionClickListener {
public void onActionClicked();
}
public interface DismissListener {
public void onDismiss();
}
}