com.github.andrewlord1990.snackbarbuilder.SnackbarBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.github.andrewlord1990.snackbarbuilder.SnackbarBuilder.java

Source

/*
 * Copyright (C) 2016 Andrew Lord
 *
 * 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.github.andrewlord1990.snackbarbuilder;

import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DimenRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.support.design.widget.Snackbar.Duration;
import android.support.v4.content.ContextCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import android.view.View.OnClickListener;

import com.github.andrewlord1990.snackbarbuilder.callback.SnackbarActionDismissCallback;
import com.github.andrewlord1990.snackbarbuilder.callback.SnackbarCallback;
import com.github.andrewlord1990.snackbarbuilder.callback.SnackbarCombinedCallback;
import com.github.andrewlord1990.snackbarbuilder.callback.SnackbarConsecutiveDismissCallback;
import com.github.andrewlord1990.snackbarbuilder.callback.SnackbarDismissCallback;
import com.github.andrewlord1990.snackbarbuilder.callback.SnackbarManualDismissCallback;
import com.github.andrewlord1990.snackbarbuilder.callback.SnackbarShowCallback;
import com.github.andrewlord1990.snackbarbuilder.callback.SnackbarSwipeDismissCallback;
import com.github.andrewlord1990.snackbarbuilder.callback.SnackbarTimeoutDismissCallback;
import com.github.andrewlord1990.snackbarbuilder.parent.SnackbarParentFinder;

/**
 * A builder pattern to easily create and customise Android Design Support library Snackbars. On top of the
 * customisations you can make through the Snackbar API, there are some additional ones.
 * These include:
 * - Altering text and background colours
 * - Easier-to-use callbacks
 * - Appending messages
 * - Adding an icon
 * - Providing defaults for all SnackbarBuilders through theme attributes
 * The builder can either output a constructed Snackbar or a SnackbarWrapper. The SnackbarWrapper allows you to further
 * customise the Snackbar after creation and gives you more control than the Snackbar API. Various default values can
 * be loaded from a style specified within your theme. These values can be overridden by the builder methods.
 */
public final class SnackbarBuilder {

    Context context;
    View parentView;
    SpannableStringBuilder appendMessages;
    CharSequence message;

    @Duration
    int duration = Snackbar.LENGTH_LONG;

    CharSequence actionText;
    OnClickListener actionClickListener;
    SnackbarCombinedCallback.Builder callbackBuilder = SnackbarCombinedCallback.builder();
    boolean actionAllCaps = true;
    int backgroundColor;
    int actionTextColor;
    int messageTextColor;
    int parentViewId;
    Drawable icon;
    int iconMarginStart;
    int iconMarginEnd;

    /**
     * Create a builder to create a Snackbar. The Snackbar will be attached to the specified parent view.
     *
     * @param view Parent view to attach the Snackbar to.
     */
    public SnackbarBuilder(View view) {
        setup(view.getContext());
        parentView = view;
    }

    /**
     * Create a builder to create a Snackbar. The parent view to attach the Snackbar to is specified through the
     * attribute snackbarBuilder_parentViewId. This attribute is within a style provided through the theme attribute
     * snackbarBuilderStyle. The parent view will be found using this ID and the Snackbar will be attached to it.
     *
     * @param activity Activity to show the Snackbar in, it should contain a view with the ID specified in the style
     *                 attribute snackbarBuilder_parentViewId.
     */
    public SnackbarBuilder(Activity activity) {
        setup(activity);
        parentView = activity.findViewById(parentViewId);
    }

    /**
     * Create a builder to create a Snackbar. The parent view to attach the Snackbar to is found using the provided
     * SnackbarParentFinder. This gives you the flexibility of not using a single ID for the parent view, or to have
     * fallback view IDs in the case that the usual one isn't found in a particular activity.
     *
     * @param activity     Activity to show the Snackbar in.
     * @param parentFinder Used to find the parent view to attach the Snackbar to.
     */
    public SnackbarBuilder(Activity activity, SnackbarParentFinder parentFinder) {
        setup(activity);
        parentView = parentFinder.findSnackbarParent(activity);
    }

    private void setup(Context context) {
        this.context = context;
        loadThemeAttributes();
    }

    /**
     * Set the text to display on the Snackbar.
     *
     * @param message Text to display.
     * @return This instance.
     */
    public SnackbarBuilder message(CharSequence message) {
        this.message = message;
        return this;
    }

    /**
     * Set the text to display on the Snackbar.
     *
     * @param messageResId String resource of the text to display.
     * @return This instance.
     */
    public SnackbarBuilder message(@StringRes int messageResId) {
        this.message = context.getString(messageResId);
        return this;
    }

    /**
     * Set the colour to display the message on the Snackbar.
     *
     * @param messageTextColor Colour to display the message.
     * @return This instance.
     */
    public SnackbarBuilder messageTextColor(@ColorInt int messageTextColor) {
        this.messageTextColor = messageTextColor;
        return this;
    }

    /**
     * Set the colour to display the message on the Snackbar.
     *
     * @param messageTextColor Resource of the colour to display the message.
     * @return This instance.
     */
    public SnackbarBuilder messageTextColorRes(@ColorRes int messageTextColor) {
        this.messageTextColor = getColor(messageTextColor);
        return this;
    }

    /**
     * Add some text to append to the end of the message shown on the Snackbar.
     *
     * @param message Text to append to the Snackbar message.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder appendMessage(CharSequence message) {
        initialiseAppendMessages();
        appendMessages.append(message);
        return this;
    }

    /**
     * Add some text to append to the message shown on the Snackbar.
     *
     * @param messageResId String resource of the text to append to the Snackbar message.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder appendMessage(@StringRes int messageResId) {
        return appendMessage(context.getString(messageResId));
    }

    /**
     * Add some text to append to the message shown on the Snackbar and a colour to make it.
     *
     * @param message Text to append to the Snackbar message.
     * @param color   Colour to make the appended text.
     * @return This instance.
     */
    public SnackbarBuilder appendMessage(CharSequence message, @ColorInt int color) {
        initialiseAppendMessages();
        Spannable spannable = new SpannableString(message);
        spannable.setSpan(new ForegroundColorSpan(color), 0, spannable.length(),
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        appendMessages.append(spannable);
        return this;
    }

    /**
     * Add some text to append to the message shown on the Snackbar and a colour to make it.
     *
     * @param messageResId String resource of the text to append to the Snackbar message.
     * @param colorResId   Resource of the colour to make the appended text.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder appendMessage(@StringRes int messageResId, @ColorRes int colorResId) {
        return appendMessage(context.getString(messageResId), getColor(colorResId));
    }

    /**
     * Set the duration to show the Snackbar for.
     *
     * @param duration The duration.
     * @return This instance.
     */
    public SnackbarBuilder duration(@Duration int duration) {
        this.duration = duration;
        return this;
    }

    /**
     * Set the text to display as an action on the Snackbar.
     *
     * @param actionText The text to set as an action.
     * @return This instance.
     */
    public SnackbarBuilder actionText(CharSequence actionText) {
        this.actionText = actionText;
        return this;
    }

    /**
     * Set the text to display as an action on the Snackbar.
     *
     * @param actionTextResId The string resource of the text to set as an action.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder actionText(@StringRes int actionTextResId) {
        this.actionText = context.getString(actionTextResId);
        return this;
    }

    /**
     * Set the colour to display the action on the Snackbar.
     *
     * @param actionTextColor Colour to display the action.
     * @return This instance.
     */
    public SnackbarBuilder actionTextColor(@ColorInt int actionTextColor) {
        this.actionTextColor = actionTextColor;
        return this;
    }

    /**
     * Set the colour to display the action on the Snackbar.
     *
     * @param actionTextColorResId Resource of the colour to display the action.
     * @return This instance.
     */
    public SnackbarBuilder actionTextColorRes(@ColorRes int actionTextColorResId) {
        this.actionTextColor = getColor(actionTextColorResId);
        return this;
    }

    /**
     * Set the click listener for the action on the Snackbar.
     *
     * @param actionClickListener Click listener for the action.
     * @return This instance.
     */
    public SnackbarBuilder actionClickListener(OnClickListener actionClickListener) {
        this.actionClickListener = actionClickListener;
        return this;
    }

    /**
     * Set the colour to make the background of the Snackbar.
     *
     * @param backgroundColor The background colour.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder backgroundColor(@ColorInt int backgroundColor) {
        this.backgroundColor = backgroundColor;
        return this;
    }

    /**
     * Set the colour to make the background of the Snackbar.
     *
     * @param backgroundColorResId The resource of the background colour.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder backgroundColorRes(@ColorRes int backgroundColorResId) {
        this.backgroundColor = getColor(backgroundColorResId);
        return this;
    }

    /**
     * Set the standard callback for being informed of the Snackbar being shown or dismissed.
     *
     * @param callback The callback.
     * @return This instance.
     */
    public SnackbarBuilder callback(Snackbar.Callback callback) {
        callbackBuilder.callback(callback);
        return this;
    }

    /**
     * Set the enhanced callback for being informed of the Snackbar being shown or dismissed, through individual callback
     * methods.
     *
     * @param snackbarCallback The callback.
     * @return This instance.
     */
    public SnackbarBuilder snackbarCallback(SnackbarCallback snackbarCallback) {
        callbackBuilder.snackbarCallback(snackbarCallback);
        return this;
    }

    /**
     * Set the callback to be informed of the Snackbar being shown.
     *
     * @param callback The callback.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder showCallback(SnackbarShowCallback callback) {
        callbackBuilder.showCallback(callback);
        return this;
    }

    /**
     * Set the callback to be informed of the Snackbar being dismissed through some means.
     *
     * @param callback The callback.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder dismissCallback(SnackbarDismissCallback callback) {
        callbackBuilder.dismissCallback(callback);
        return this;
    }

    /**
     * Set the callback to be informed of the Snackbar being dismissed due to the action being pressed.
     *
     * @param callback The callback.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder actionDismissCallback(SnackbarActionDismissCallback callback) {
        callbackBuilder.actionDismissCallback(callback);
        return this;
    }

    /**
     * Set the callback to be informed of the Snackbar being dismissed due to being swiped away.
     *
     * @param callback The callback.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder swipeDismissCallback(SnackbarSwipeDismissCallback callback) {
        callbackBuilder.swipeDismissCallback(callback);
        return this;
    }

    /**
     * Set the callback to be informed of the Snackbar being dismissed due to a timeout.
     *
     * @param callback The callback.
     * @return This instance.
     */
    public SnackbarBuilder timeoutDismissCallback(SnackbarTimeoutDismissCallback callback) {
        callbackBuilder.timeoutDismissCallback(callback);
        return this;
    }

    /**
     * Set the callback to be informed of the Snackbar being dismissed manually, due to a call to dismiss().
     *
     * @param callback The callback.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder manualDismissCallback(SnackbarManualDismissCallback callback) {
        callbackBuilder.manualDismissCallback(callback);
        return this;
    }

    /**
     * Set the callback to be informed of the Snackbar being dismissed due to another Snackbar being shown.
     *
     * @param callback The callback.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder consecutiveDismissCallback(SnackbarConsecutiveDismissCallback callback) {
        callbackBuilder.consecutiveDismissCallback(callback);
        return this;
    }

    /**
     * Make the action be displayed with the first letter uppercase and the rest lowercase. This is useful due to the
     * action being displayed all uppercase by default on API 14 and above.
     *
     * @return This instance.
     */
    public SnackbarBuilder lowercaseAction() {
        actionAllCaps = false;
        return this;
    }

    /**
     * Set an icon to display on the Snackbar next to the message.
     *
     * @param icon The drawable of the icon.
     * @return This instance.
     */
    public SnackbarBuilder icon(Drawable icon) {
        this.icon = icon;
        return this;
    }

    /**
     * Set an icon to display on the Snackbar next to the message.
     *
     * @param iconResId The drawable resource of the icon.
     * @return This instance.
     */
    public SnackbarBuilder icon(@DrawableRes int iconResId) {
        icon = getDrawable(iconResId);
        return this;
    }

    /**
     * Set the margin to be displayed before the icon in pixels. On platform versions that support bi-directional layouts,
     * this will be the start margin, on platforms before this it will just be the left margin.
     *
     * @param iconMarginStart The margin before the icon.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder iconMarginStart(int iconMarginStart) {
        this.iconMarginStart = iconMarginStart;
        return this;
    }

    /**
     * Set the margin to be displayed before the icon. On platform versions that support bi-directional layouts, this will
     * be the start margin, on platforms before this it will just be the left margin.
     *
     * @param iconMarginStartResId The dimension resource of the margin before the icon.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder iconMarginStartRes(@DimenRes int iconMarginStartResId) {
        return iconMarginStart(context.getResources().getDimensionPixelSize(iconMarginStartResId));
    }

    /**
     * Set the margin to be displayed after the icon in pixels. On platform versions that support bi-directional layouts,
     * this will be the end margin, on platforms before this it will just be the right margin.
     *
     * @param iconMarginEnd The margin after the icon in pixels.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder iconMarginEnd(int iconMarginEnd) {
        this.iconMarginEnd = iconMarginEnd;
        return this;
    }

    /**
     * Set the margin to be displayed after the icon. On platform versions that support bi-directional layouts, this will
     * be the end margin, on platforms before this it will just be the right margin.
     *
     * @param iconMarginEndResId The margin after the icon.
     * @return This instance.
     */
    @SuppressWarnings("WeakerAccess")
    public SnackbarBuilder iconMarginEndRes(@DimenRes int iconMarginEndResId) {
        return iconMarginEnd(context.getResources().getDimensionPixelSize(iconMarginEndResId));
    }

    /**
     * Build a Snackbar using the options specified in the builder. Wrap this Snackbar into a SnackbarWrapper, which
     * allows further customisation.
     *
     * @return A SnackbarWrapper, a class which wraps a Snackbar for further customisation.
     */
    public SnackbarWrapper buildWrapper() {
        return new SnackbarWrapper(build());
    }

    /**
     * Build a Snackbar using the options specified in the builder.
     *
     * @return A Snackbar.
     */
    public Snackbar build() {
        Snackbar snackbar = Snackbar.make(parentView, message, duration);

        new SnackbarCustomiser(snackbar).customiseMessage(messageTextColor, appendMessages)
                .setBackgroundColor(backgroundColor).setAction(actionText, actionClickListener)
                .setActionTextColor(actionTextColor).setActionAllCaps(actionAllCaps)
                .setCallbacks(callbackBuilder.build()).setIcon(icon, iconMarginStart, iconMarginEnd);

        return snackbar;
    }

    private void initialiseAppendMessages() {
        if (appendMessages == null) {
            appendMessages = new SpannableStringBuilder();
        }
    }

    private void loadThemeAttributes() {
        TypedArray attrs = context.obtainStyledAttributes(null, R.styleable.SnackbarBuilderStyle,
                R.attr.snackbarBuilderStyle, 0);
        try {
            loadMessageTextColor(attrs);
            loadActionTextColor(attrs);
            loadParentViewId(attrs);
            loadDuration(attrs);
            loadBackgroundColor(attrs);

            loadFallbackAttributes(attrs);
        } finally {
            attrs.recycle();
        }
    }

    private void loadBackgroundColor(TypedArray attrs) {
        backgroundColor = attrs.getColor(R.styleable.SnackbarBuilderStyle_snackbarBuilder_backgroundColor, 0);
    }

    private void loadDuration(TypedArray attrs) {
        int durationAttr = attrs.getInteger(R.styleable.SnackbarBuilderStyle_snackbarBuilder_duration,
                Integer.MIN_VALUE);
        if (durationAttr > Integer.MIN_VALUE) {
            duration = durationAttr;
        }
    }

    private void loadParentViewId(TypedArray attrs) {
        parentViewId = attrs.getResourceId(R.styleable.SnackbarBuilderStyle_snackbarBuilder_parentViewId, 0);
    }

    private void loadActionTextColor(TypedArray attrs) {
        actionTextColor = attrs.getColor(R.styleable.SnackbarBuilderStyle_snackbarBuilder_actionTextColor, 0);
    }

    private void loadMessageTextColor(TypedArray attrs) {
        messageTextColor = attrs.getColor(R.styleable.SnackbarBuilderStyle_snackbarBuilder_messageTextColor, 0);
    }

    private void loadFallbackAttributes(TypedArray attrs) {
        if (messageTextColor == 0) {
            messageTextColor = getColor(R.color.snackbarbuilder_default_message);
        }
        if (actionTextColor == 0) {
            actionTextColor = attrs.getColor(R.styleable.SnackbarBuilderStyle_colorAccent, 0);
        }
        if (iconMarginStart == 0) {
            iconMarginStart = context.getResources()
                    .getDimensionPixelSize(R.dimen.snackbarbuilder_icon_margin_start_default);
        }
        if (iconMarginEnd == 0) {
            iconMarginEnd = context.getResources()
                    .getDimensionPixelSize(R.dimen.snackbarbuilder_icon_margin_end_default);
        }
    }

    private int getColor(@ColorRes int color) {
        return ContextCompat.getColor(context, color);
    }

    private Drawable getDrawable(@DrawableRes int drawableResId) {
        return ContextCompat.getDrawable(context, drawableResId);
    }

}