package com.afollestad.aesthetic;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.support.annotation.ColorInt;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import io.reactivex.Observable;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function3;
import static com.afollestad.aesthetic.Rx.onErrorLogAndRethrow;
/** @author Aidan Follestad (afollestad) */
public class AestheticBottomNavigationView extends BottomNavigationView {
private Disposable modesSubscription;
private CompositeDisposable colorSubscriptions;
private int lastTextIconColor;
public AestheticBottomNavigationView(Context context) {
super(context);
}
public AestheticBottomNavigationView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AestheticBottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void invalidateIconTextColor(int backgroundColor, int selectedColor) {
int baseColor =
ContextCompat.getColor(
getContext(),
Util.isColorLight(backgroundColor) ? R.color.ate_icon_light : R.color.ate_icon_dark);
int unselectedIconTextColor = Util.adjustAlpha(baseColor, .87f);
ColorStateList iconColor =
new ColorStateList(
new int[][] {
new int[] {-android.R.attr.state_checked}, new int[] {android.R.attr.state_checked}
},
new int[] {unselectedIconTextColor, selectedColor});
ColorStateList textColor =
new ColorStateList(
new int[][] {
new int[] {-android.R.attr.state_checked}, new int[] {android.R.attr.state_checked}
},
new int[] {unselectedIconTextColor, selectedColor});
setItemIconTintList(iconColor);
setItemTextColor(textColor);
}
@Override
public void setBackgroundColor(@ColorInt int color) {
super.setBackgroundColor(color);
if (lastTextIconColor == Color.TRANSPARENT) {
lastTextIconColor = Util.isColorLight(color) ? Color.BLACK : Color.WHITE;
}
invalidateIconTextColor(color, lastTextIconColor);
}
private void onState(State state) {
if (colorSubscriptions != null) {
colorSubscriptions.clear();
}
colorSubscriptions = new CompositeDisposable();
switch (state.iconTextMode) {
case BottomNavIconTextMode.SELECTED_PRIMARY:
colorSubscriptions.add(
Aesthetic.get()
.colorPrimary()
.compose(Rx.<Integer>distinctToMainThread())
.subscribe(
new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer color) {
lastTextIconColor = color;
}
},
onErrorLogAndRethrow()));
break;
case BottomNavIconTextMode.SELECTED_ACCENT:
colorSubscriptions.add(
Aesthetic.get()
.colorAccent()
.compose(Rx.<Integer>distinctToMainThread())
.subscribe(
new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer color) {
lastTextIconColor = color;
}
},
onErrorLogAndRethrow()));
break;
case BottomNavIconTextMode.BLACK_WHITE_AUTO:
// We will automatically set the icon/text color when the background color is set
lastTextIconColor = Color.TRANSPARENT;
break;
default:
throw new IllegalStateException("Unknown bottom nav icon/text mode: " + state.iconTextMode);
}
switch (state.bgMode) {
case BottomNavBgMode.PRIMARY:
colorSubscriptions.add(
Aesthetic.get()
.colorPrimary()
.compose(Rx.<Integer>distinctToMainThread())
.subscribe(ViewBackgroundAction.create(this), onErrorLogAndRethrow()));
break;
case BottomNavBgMode.PRIMARY_DARK:
colorSubscriptions.add(
Aesthetic.get()
.colorStatusBar()
.compose(Rx.<Integer>distinctToMainThread())
.subscribe(ViewBackgroundAction.create(this), onErrorLogAndRethrow()));
break;
case BottomNavBgMode.ACCENT:
colorSubscriptions.add(
Aesthetic.get()
.colorAccent()
.compose(Rx.<Integer>distinctToMainThread())
.subscribe(ViewBackgroundAction.create(this), onErrorLogAndRethrow()));
break;
case BottomNavBgMode.BLACK_WHITE_AUTO:
setBackgroundColor(
ContextCompat.getColor(
getContext(),
state.isDark
? R.color.ate_bottom_nav_default_dark_bg
: R.color.ate_bottom_nav_default_light_bg));
break;
default:
throw new IllegalStateException("Unknown bottom nav bg mode: " + state.bgMode);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
modesSubscription =
Observable.combineLatest(
Aesthetic.get().bottomNavigationBackgroundMode(),
Aesthetic.get().bottomNavigationIconTextMode(),
Aesthetic.get().isDark(),
State.creator())
.compose(Rx.<State>distinctToMainThread())
.subscribe(
new Consumer<State>() {
@Override
public void accept(@android.support.annotation.NonNull State state) {
onState(state);
}
},
onErrorLogAndRethrow());
}
@Override
protected void onDetachedFromWindow() {
modesSubscription.dispose();
colorSubscriptions.clear();
super.onDetachedFromWindow();
}
private static class State {
@BottomNavBgMode private final int bgMode;
@BottomNavIconTextMode private final int iconTextMode;
private final boolean isDark;
private State(int bgMode, int iconTextMode, boolean isDark) {
this.bgMode = bgMode;
this.iconTextMode = iconTextMode;
this.isDark = isDark;
}
static State create(
@BottomNavBgMode int bgMode, @BottomNavIconTextMode int iconTextMode, boolean isDark) {
return new State(bgMode, iconTextMode, isDark);
}
static Function3<Integer, Integer, Boolean, State> creator() {
return new Function3<Integer, Integer, Boolean, State>() {
@Override
public State apply(Integer integer, Integer integer2, Boolean aBoolean) {
return State.create(integer, integer2, aBoolean);
}
};
}
}
}