package com.afollestad.aesthetic;
import static com.afollestad.aesthetic.Rx.onErrorLogAndRethrow;
import static com.afollestad.aesthetic.Util.isColorLight;
import static com.afollestad.aesthetic.Util.resolveColor;
import static com.afollestad.aesthetic.Util.setLightStatusBarCompat;
import static com.afollestad.aesthetic.Util.setNavBarColorCompat;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.annotation.CheckResult;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.support.v4.content.ContextCompat;
import android.support.v4.util.ArrayMap;
import android.support.v4.util.Pair;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.f2prateek.rx.preferences2.RxSharedPreferences;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
import java.util.ArrayList;
import java.util.List;
/** @author Aidan Follestad (afollestad) */
@SuppressWarnings({"WeakerAccess", "unused"})
public class Aesthetic {
private static final String PREFS_NAME = "[aesthetic-prefs]";
private static final String KEY_FIRST_TIME = "first_time";
private static final String KEY_ACTIVITY_THEME = "activity_theme_%s";
private static final String KEY_IS_DARK = "is_dark";
private static final String KEY_PRIMARY_COLOR = "primary_color";
private static final String KEY_ACCENT_COLOR = "accent_color";
private static final String KEY_PRIMARY_TEXT_COLOR = "primary_text";
private static final String KEY_SECONDARY_TEXT_COLOR = "secondary_text";
private static final String KEY_PRIMARY_TEXT_INVERSE_COLOR = "primary_text_inverse";
private static final String KEY_SECONDARY_TEXT_INVERSE_COLOR = "secondary_text_inverse";
private static final String KEY_WINDOW_BG_COLOR = "window_bg_color";
private static final String KEY_STATUS_BAR_COLOR = "status_bar_color_%s";
private static final String KEY_NAV_BAR_COLOR = "nav_bar_color_%S";
private static final String KEY_LIGHT_STATUS_MODE = "light_status_mode";
private static final String KEY_TAB_LAYOUT_BG_MODE = "tab_layout_bg_mode";
private static final String KEY_TAB_LAYOUT_INDICATOR_MODE = "tab_layout_indicator_mode";
private static final String KEY_NAV_VIEW_MODE = "nav_view_mode";
private static final String KEY_BOTTOM_NAV_BG_MODE = "bottom_nav_bg_mode";
private static final String KEY_BOTTOM_NAV_ICONTEXT_MODE = "bottom_nav_icontext_mode";
private static final String KEY_CARD_VIEW_BG_COLOR = "card_view_bg_color";
private static final String KEY_ICON_TITLE_ACTIVE_COLOR = "icon_title_active_color";
private static final String KEY_ICON_TITLE_INACTIVE_COLOR = "icon_title_inactive_color";
private static final String KEY_SNACKBAR_TEXT = "snackbar_text_color";
private static final String KEY_SNACKBAR_ACTION_TEXT = "snackbar_action_text_color";
@SuppressLint("StaticFieldLeak")
private static Aesthetic instance;
private final ArrayMap<Object, List<ViewObservablePair>> backgroundSubscriberViews;
private CompositeDisposable backgroundSubscriptions;
private CompositeDisposable subs;
private AppCompatActivity context;
private SharedPreferences prefs;
private SharedPreferences.Editor editor;
private RxSharedPreferences rxPrefs;
private int lastActivityTheme;
private boolean isResumed;
@SuppressLint("CommitPrefEdits")
private Aesthetic(AppCompatActivity context) {
this.context = context;
prefs = context.getApplicationContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
editor = prefs.edit();
rxPrefs = RxSharedPreferences.create(prefs);
backgroundSubscriberViews = new ArrayMap<>(0);
}
private static String key(@Nullable AppCompatActivity activity) {
String key;
if (activity instanceof AestheticKeyProvider) {
key = ((AestheticKeyProvider) activity).key();
} else {
key = "default";
}
if (key == null) {
key = "default";
}
return key;
}
/** Should be called before super.onCreate() in each Activity. */
@NonNull
public static Aesthetic attach(@NonNull AppCompatActivity activity) {
if (instance == null) {
instance = new Aesthetic(activity);
}
instance.isResumed = false;
instance.context = activity;
LayoutInflater li = activity.getLayoutInflater();
Util.setInflaterFactory(li, activity);
String activityThemeKey = String.format(KEY_ACTIVITY_THEME, key(activity));
instance.lastActivityTheme = instance.prefs.getInt(activityThemeKey, 0);
if (instance.lastActivityTheme != 0) {
activity.setTheme(instance.lastActivityTheme);
}
return instance;
}
@NonNull
@CheckResult
public static Aesthetic get() {
if (instance == null) {
throw new IllegalStateException("Not attached!");
}
return instance;
}
/** Should be called in onPause() of each Activity. */
public static void pause(@NonNull AppCompatActivity activity) {
if (instance == null) {
return;
}
instance.isResumed = false;
if (instance.subs != null) {
instance.subs.clear();
}
if (instance.backgroundSubscriptions != null) {
instance.backgroundSubscriptions.clear();
}
if (activity.isFinishing()) {
instance.backgroundSubscriberViews.remove(activity);
if (instance.context != null
&& instance.context.getClass().getName().equals(activity.getClass().getName())) {
instance.context = null;
}
}
}
/** Should be called in onResume() of each Activity. */
public static void resume(@NonNull AppCompatActivity activity) {
if (instance == null) {
return;
}
instance.context = activity;
instance.isResumed = true;
if (instance.subs != null) {
instance.subs.clear();
}
instance.subs = new CompositeDisposable();
subscribeBackgroundListeners();
instance.subs.add(
instance
.colorPrimary()
.compose(Rx.<Integer>distinctToMainThread())
.subscribe(
new Consumer<Integer>() {
@Override
public void accept(@io.reactivex.annotations.NonNull Integer color) {
Util.setTaskDescriptionColor(instance.context, color);
}
},
onErrorLogAndRethrow()));
instance.subs.add(
instance
.activityTheme()
.compose(Rx.<Integer>distinctToMainThread())
.subscribe(
new Consumer<Integer>() {
@Override
public void accept(@io.reactivex.annotations.NonNull Integer themeId) {
if (instance.lastActivityTheme == themeId) {
return;
}
instance.lastActivityTheme = themeId;
instance.context.recreate();
}
},
onErrorLogAndRethrow()));
instance.subs.add(
Observable.combineLatest(
instance.colorStatusBar(),
instance.lightStatusBarMode(),
new BiFunction<Integer, Integer, Pair<Integer, Integer>>() {
@Override
public Pair<Integer, Integer> apply(Integer integer, Integer integer2) {
return Pair.create(integer, integer2);
}
})
.compose(Rx.<Pair<Integer, Integer>>distinctToMainThread())
.subscribe(
new Consumer<Pair<Integer, Integer>>() {
@Override
public void accept(
@io.reactivex.annotations.NonNull Pair<Integer, Integer> result) {
instance.invalidateStatusBar();
}
},
onErrorLogAndRethrow()));
instance.subs.add(
instance
.colorNavigationBar()
.compose(Rx.<Integer>distinctToMainThread())
.subscribe(
new Consumer<Integer>() {
@Override
public void accept(@io.reactivex.annotations.NonNull Integer color) {
setNavBarColorCompat(instance.context, color);
}
},
onErrorLogAndRethrow()));
instance.subs.add(
instance
.colorWindowBackground()
.compose(Rx.<Integer>distinctToMainThread())
.subscribe(
new Consumer<Integer>() {
@Override
public void accept(@io.reactivex.annotations.NonNull Integer color) {
instance.context.getWindow().setBackgroundDrawable(new ColorDrawable(color));
}
},
onErrorLogAndRethrow()));
}
/** Returns true if this method has never been called before. */
public static boolean isFirstTime() {
boolean firstTime = instance.prefs.getBoolean(KEY_FIRST_TIME, true);
instance.editor.putBoolean(KEY_FIRST_TIME, false).commit();
return firstTime;
}
private static void subscribeBackgroundListeners() {
if (instance.backgroundSubscriptions != null) {
instance.backgroundSubscriptions.clear();
}
instance.backgroundSubscriptions = new CompositeDisposable();
if (instance.backgroundSubscriberViews.size() > 0) {
List<ViewObservablePair> pairs = instance.backgroundSubscriberViews.get(instance.context);
if (pairs != null) {
for (ViewObservablePair pair : pairs) {
instance.backgroundSubscriptions.add(
pair.observable()
.compose(Rx.<Integer>distinctToMainThread())
.subscribeWith(ViewBackgroundSubscriber.create(pair.view())));
}
}
}
}
void addBackgroundSubscriber(@NonNull View view, @NonNull Observable<Integer> colorObservable) {
List<ViewObservablePair> subscribers = backgroundSubscriberViews.get(instance.context);
if (subscribers == null) {
subscribers = new ArrayList<>(1);
backgroundSubscriberViews.put(instance.context, subscribers);
}
subscribers.add(ViewObservablePair.create(view, colorObservable));
if (isResumed) {
instance.backgroundSubscriptions.add(
colorObservable
.compose(Rx.<Integer>distinctToMainThread())
.subscribeWith(ViewBackgroundSubscriber.create(view)));
}
}
private void invalidateStatusBar() {
String key = String.format(KEY_STATUS_BAR_COLOR, key(context));
final int color = prefs.getInt(key, resolveColor(context, R.attr.colorPrimaryDark));
ViewGroup rootView = Util.getRootView(context);
if (rootView instanceof DrawerLayout) {
// Color is set to DrawerLayout, Activity gets transparent status bar
setLightStatusBarCompat(context, false);
Util.setStatusBarColorCompat(
context, ContextCompat.getColor(context, android.R.color.transparent));
((DrawerLayout) rootView).setStatusBarBackgroundColor(color);
} else {
Util.setStatusBarColorCompat(context, color);
}
final int mode = prefs.getInt(KEY_LIGHT_STATUS_MODE, AutoSwitchMode.AUTO);
switch (mode) {
case AutoSwitchMode.OFF:
setLightStatusBarCompat(context, false);
break;
case AutoSwitchMode.ON:
setLightStatusBarCompat(context, true);
break;
default:
setLightStatusBarCompat(context, isColorLight(color));
break;
}
}
//
/////// GETTERS AND SETTERS OF THEME PROPERTIES
//
@CheckResult
public Aesthetic activityTheme(@StyleRes int theme) {
String key = String.format(KEY_ACTIVITY_THEME, key(context));
editor.putInt(key, theme);
return this;
}
@CheckResult
public Observable<Integer> activityTheme() {
String key = String.format(KEY_ACTIVITY_THEME, key(context));
return rxPrefs
.getInteger(key, 0)
.asObservable()
.filter(
new Predicate<Integer>() {
@Override
public boolean test(@io.reactivex.annotations.NonNull Integer next) throws Exception {
return next != 0 && next != lastActivityTheme;
}
});
}
@CheckResult
public Aesthetic isDark(boolean isDark) {
editor.putBoolean(KEY_IS_DARK, isDark).commit();
return this;
}
@CheckResult
public Observable<Boolean> isDark() {
return rxPrefs.getBoolean(KEY_IS_DARK, false).asObservable();
}
@CheckResult
public Aesthetic colorPrimary(@ColorInt int color) {
// needs to be committed immediately so that for statusBarColorAuto() and other auto methods
editor.putInt(KEY_PRIMARY_COLOR, color).commit();
return this;
}
@CheckResult
public Aesthetic colorPrimaryRes(@ColorRes int color) {
return colorPrimary(ContextCompat.getColor(context, color));
}
@CheckResult
public Observable<Integer> colorPrimary() {
return rxPrefs
.getInteger(KEY_PRIMARY_COLOR, resolveColor(context, R.attr.colorPrimary))
.asObservable();
}
@CheckResult
public Aesthetic colorAccent(@ColorInt int color) {
editor.putInt(KEY_ACCENT_COLOR, color).commit();
return this;
}
@CheckResult
public Aesthetic colorAccentRes(@ColorRes int color) {
return colorAccent(ContextCompat.getColor(context, color));
}
@CheckResult
public Observable<Integer> colorAccent() {
return rxPrefs
.getInteger(KEY_ACCENT_COLOR, resolveColor(context, R.attr.colorAccent))
.asObservable();
}
@CheckResult
public Aesthetic textColorPrimary(@ColorInt int color) {
editor.putInt(KEY_PRIMARY_TEXT_COLOR, color);
return this;
}
@CheckResult
public Aesthetic textColorPrimaryRes(@ColorRes int color) {
return textColorPrimary(ContextCompat.getColor(context, color));
}
@CheckResult
public Observable<Integer> textColorPrimary() {
return rxPrefs
.getInteger(KEY_PRIMARY_TEXT_COLOR, resolveColor(context, android.R.attr.textColorPrimary))
.asObservable();
}
@CheckResult
public Aesthetic textColorSecondary(@ColorInt int color) {
editor.putInt(KEY_SECONDARY_TEXT_COLOR, color);
return this;
}
@CheckResult
public Aesthetic textColorSecondaryRes(@ColorRes int color) {
return textColorSecondary(ContextCompat.getColor(context, color));
}
@CheckResult
public Observable<Integer> textColorSecondary() {
return rxPrefs
.getInteger(
KEY_SECONDARY_TEXT_COLOR, resolveColor(context, android.R.attr.textColorSecondary))
.asObservable();
}
@CheckResult
public Aesthetic textColorPrimaryInverse(@ColorInt int color) {
editor.putInt(KEY_PRIMARY_TEXT_INVERSE_COLOR, color);
return this;
}
@CheckResult
public Aesthetic textColorPrimaryInverseRes(@ColorRes int color) {
return textColorPrimaryInverse(ContextCompat.getColor(context, color));
}
@CheckResult
public Observable<Integer> textColorPrimaryInverse() {
return rxPrefs
.getInteger(
KEY_PRIMARY_TEXT_INVERSE_COLOR,
resolveColor(context, android.R.attr.textColorPrimaryInverse))
.asObservable();
}
@CheckResult
public Aesthetic textColorSecondaryInverse(@ColorInt int color) {
editor.putInt(KEY_SECONDARY_TEXT_INVERSE_COLOR, color);
return this;
}
@CheckResult
public Aesthetic textColorSecondaryInverseRes(@ColorRes int color) {
return textColorSecondaryInverse(ContextCompat.getColor(context, color));
}
@CheckResult
public Observable<Integer> textColorSecondaryInverse() {
return rxPrefs
.getInteger(
KEY_SECONDARY_TEXT_INVERSE_COLOR,
resolveColor(context, android.R.attr.textColorSecondaryInverse))
.asObservable();
}
@CheckResult
public Aesthetic colorWindowBackground(@ColorInt int color) {
editor.putInt(KEY_WINDOW_BG_COLOR, color).commit();
return this;
}
@CheckResult
public Aesthetic colorWindowBackgroundRes(@ColorRes int color) {
return colorWindowBackground(ContextCompat.getColor(context, color));
}
@CheckResult
public Observable<Integer> colorWindowBackground() {
return rxPrefs
.getInteger(KEY_WINDOW_BG_COLOR, resolveColor(context, android.R.attr.windowBackground))
.asObservable();
}
@CheckResult
public Aesthetic colorStatusBar(@ColorInt int color) {
String key = String.format(KEY_STATUS_BAR_COLOR, key(context));
editor.putInt(key, color);
return this;
}
@CheckResult
public Aesthetic colorStatusBarRes(@ColorRes int color) {
return colorStatusBar(ContextCompat.getColor(context, color));
}
@CheckResult
public Aesthetic colorStatusBarAuto() {
String key = String.format(KEY_STATUS_BAR_COLOR, key(context));
editor.putInt(
key,
Util.darkenColor(
prefs.getInt(KEY_PRIMARY_COLOR, resolveColor(context, R.attr.colorPrimary))));
return this;
}
@CheckResult
public Observable<Integer> colorStatusBar() {
String key = String.format(KEY_STATUS_BAR_COLOR, key(context));
return rxPrefs.getInteger(key, resolveColor(context, R.attr.colorPrimaryDark)).asObservable();
}
@CheckResult
public Aesthetic colorNavigationBar(@ColorInt int color) {
String key = String.format(KEY_NAV_BAR_COLOR, key(context));
editor.putInt(key, color);
return this;
}
@CheckResult
public Aesthetic colorNavigationBarRes(@ColorRes int color) {
return colorNavigationBar(ContextCompat.getColor(context, color));
}
@CheckResult
public Aesthetic colorNavigationBarAuto() {
int color = prefs.getInt(KEY_PRIMARY_COLOR, resolveColor(context, R.attr.colorPrimary));
String key = String.format(KEY_NAV_BAR_COLOR, key(context));
editor.putInt(key, isColorLight(color) ? Color.BLACK : color);
return this;
}
@CheckResult
public Observable<Integer> colorNavigationBar() {
String key = String.format(KEY_NAV_BAR_COLOR, key(context));
return rxPrefs.getInteger(key, Color.BLACK).asObservable();
}
@CheckResult
public Aesthetic lightStatusBarMode(@AutoSwitchMode int mode) {
editor.putInt(KEY_LIGHT_STATUS_MODE, mode);
return this;
}
@CheckResult
public Observable<Integer> lightStatusBarMode() {
return rxPrefs.getInteger(KEY_LIGHT_STATUS_MODE, AutoSwitchMode.AUTO).asObservable();
}
@CheckResult
public Aesthetic tabLayoutIndicatorMode(@TabLayoutIndicatorMode int mode) {
editor.putInt(KEY_TAB_LAYOUT_INDICATOR_MODE, mode).commit();
return this;
}
@CheckResult
public Observable<Integer> tabLayoutIndicatorMode() {
return rxPrefs
.getInteger(KEY_TAB_LAYOUT_INDICATOR_MODE, TabLayoutIndicatorMode.ACCENT)
.asObservable();
}
@CheckResult
public Aesthetic tabLayoutBackgroundMode(@TabLayoutBgMode int mode) {
editor.putInt(KEY_TAB_LAYOUT_BG_MODE, mode).commit();
return this;
}
@CheckResult
public Observable<Integer> tabLayoutBackgroundMode() {
return rxPrefs.getInteger(KEY_TAB_LAYOUT_BG_MODE, TabLayoutBgMode.PRIMARY).asObservable();
}
@CheckResult
public Aesthetic navigationViewMode(@NavigationViewMode int mode) {
editor.putInt(KEY_NAV_VIEW_MODE, mode).commit();
return this;
}
@CheckResult
public Observable<Integer> navigationViewMode() {
return rxPrefs
.getInteger(KEY_NAV_VIEW_MODE, NavigationViewMode.SELECTED_PRIMARY)
.asObservable();
}
@CheckResult
public Aesthetic bottomNavigationBackgroundMode(@BottomNavBgMode int mode) {
editor.putInt(KEY_BOTTOM_NAV_BG_MODE, mode).commit();
return this;
}
@CheckResult
public Observable<Integer> bottomNavigationBackgroundMode() {
return rxPrefs
.getInteger(KEY_BOTTOM_NAV_BG_MODE, BottomNavBgMode.BLACK_WHITE_AUTO)
.asObservable();
}
@CheckResult
public Aesthetic bottomNavigationIconTextMode(@BottomNavIconTextMode int mode) {
editor.putInt(KEY_BOTTOM_NAV_ICONTEXT_MODE, mode).commit();
return this;
}
@CheckResult
public Observable<Integer> bottomNavigationIconTextMode() {
return rxPrefs
.getInteger(KEY_BOTTOM_NAV_ICONTEXT_MODE, BottomNavIconTextMode.SELECTED_ACCENT)
.asObservable();
}
@CheckResult
public Observable<Integer> colorCardViewBackground() {
return isDark()
.flatMap(
new Function<Boolean, ObservableSource<Integer>>() {
@Override
public ObservableSource<Integer> apply(
@io.reactivex.annotations.NonNull Boolean isDark) throws Exception {
return rxPrefs
.getInteger(
KEY_CARD_VIEW_BG_COLOR,
ContextCompat.getColor(
context,
isDark ? R.color.ate_cardview_bg_dark : R.color.ate_cardview_bg_light))
.asObservable();
}
});
}
@CheckResult
public Aesthetic colorCardViewBackground(@ColorInt int color) {
editor.putInt(KEY_CARD_VIEW_BG_COLOR, color);
return this;
}
@CheckResult
public Aesthetic colorCardViewBackgroundRes(@ColorRes int color) {
return colorCardViewBackground(ContextCompat.getColor(context, color));
}
@CheckResult
public Observable<ActiveInactiveColors> colorIconTitle(
@Nullable Observable<Integer> backgroundObservable) {
if (backgroundObservable == null) {
backgroundObservable = Aesthetic.get().colorPrimary();
}
return backgroundObservable.flatMap(
new Function<Integer, ObservableSource<ActiveInactiveColors>>() {
@Override
public ObservableSource<ActiveInactiveColors> apply(
@io.reactivex.annotations.NonNull Integer primaryColor) throws Exception {
final boolean isDark = !isColorLight(primaryColor);
return Observable.zip(
rxPrefs
.getInteger(
KEY_ICON_TITLE_ACTIVE_COLOR,
ContextCompat.getColor(
context, isDark ? R.color.ate_icon_dark : R.color.ate_icon_light))
.asObservable(),
rxPrefs
.getInteger(
KEY_ICON_TITLE_INACTIVE_COLOR,
ContextCompat.getColor(
context,
isDark
? R.color.ate_icon_dark_inactive
: R.color.ate_icon_light_inactive))
.asObservable(),
new BiFunction<Integer, Integer, ActiveInactiveColors>() {
@Override
public ActiveInactiveColors apply(
@io.reactivex.annotations.NonNull Integer integer,
@io.reactivex.annotations.NonNull Integer integer2)
throws Exception {
return ActiveInactiveColors.create(integer, integer2);
}
});
}
});
}
@CheckResult
public Aesthetic colorIconTitleActive(@ColorInt int color) {
editor.putInt(KEY_ICON_TITLE_ACTIVE_COLOR, color);
return this;
}
@CheckResult
public Aesthetic colorIconTitleActiveRes(@ColorRes int color) {
return colorIconTitleActive(ContextCompat.getColor(context, color));
}
@CheckResult
public Aesthetic colorIconTitleInactive(@ColorInt int color) {
editor.putInt(KEY_ICON_TITLE_INACTIVE_COLOR, color);
return this;
}
@CheckResult
public Aesthetic colorIconTitleInactiveRes(@ColorRes int color) {
return colorIconTitleActive(ContextCompat.getColor(context, color));
}
@CheckResult
public Observable<Integer> snackbarTextColor() {
return isDark()
.flatMap(
new Function<Boolean, ObservableSource<Integer>>() {
@Override
public ObservableSource<Integer> apply(
@io.reactivex.annotations.NonNull Boolean isDark) throws Exception {
return (isDark ? textColorPrimary() : textColorPrimaryInverse())
.flatMap(
new Function<Integer, ObservableSource<Integer>>() {
@Override
public ObservableSource<Integer> apply(
@io.reactivex.annotations.NonNull Integer defaultTextColor)
throws Exception {
return rxPrefs
.getInteger(KEY_SNACKBAR_TEXT, defaultTextColor)
.asObservable();
}
});
}
});
}
@CheckResult
public Aesthetic snackbarTextColor(@ColorInt int color) {
editor.putInt(KEY_SNACKBAR_TEXT, color);
return this;
}
@CheckResult
public Aesthetic snackbarTextColorRes(@ColorRes int color) {
return colorCardViewBackground(ContextCompat.getColor(context, color));
}
@CheckResult
public Observable<Integer> snackbarActionTextColor() {
return colorAccent()
.flatMap(
new Function<Integer, ObservableSource<Integer>>() {
@Override
public ObservableSource<Integer> apply(
@io.reactivex.annotations.NonNull Integer accentColor) throws Exception {
return rxPrefs.getInteger(KEY_SNACKBAR_ACTION_TEXT, accentColor).asObservable();
}
});
}
@CheckResult
public Aesthetic snackbarActionTextColor(@ColorInt int color) {
editor.putInt(KEY_SNACKBAR_ACTION_TEXT, color);
return this;
}
@CheckResult
public Aesthetic snackbarActionTextColorRes(@ColorRes int color) {
return colorCardViewBackground(ContextCompat.getColor(context, color));
}
/** Notifies all listening views that theme properties have been updated. */
public void apply() {
editor.commit();
}
}