Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Custom themes via XML for light / dark mode #507

Merged
merged 4 commits into from
Nov 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public NavigationView(Context context, @Nullable AttributeSet attrs) {
public NavigationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);
ThemeSwitcher.setTheme(getContext());
ThemeSwitcher.setTheme(getContext(), attrs);
init();
}

Expand Down Expand Up @@ -359,7 +359,9 @@ private void resetBottomSheetState(int bottomSheetState) {
* route.
*/
private void initRoute() {
mapRoute = new NavigationMapRoute(mapView, map, NavigationConstants.ROUTE_BELOW_LAYER);
int routeStyleRes = ThemeSwitcher.retrieveNavigationViewRouteStyle(getContext());
mapRoute = new NavigationMapRoute(null, mapView, map,
routeStyleRes, NavigationConstants.ROUTE_BELOW_LAYER);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.mapbox.services.android.navigation.ui.v5;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.TypedValue;

import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.IconFactory;
Expand All @@ -18,35 +20,39 @@
*/
public class ThemeSwitcher {

/**
* Looks are current theme and retrieves the color attribute
* for the given set theme.
*
* @param context to retrieve the set theme and resolved attribute and then color res Id with {@link ContextCompat}
* @return color resource identifier for primary theme color
*/
public static int retrieveNavigationViewThemeColor(Context context, int resId) {
TypedValue outValue = obtainTypedValue(context, resId);
return ContextCompat.getColor(context, outValue.resourceId);
}

/**
* Called in onCreate() to check the UI Mode (day or night)
* and set the theme colors accordingly.
*
* @param context {@link NavigationView} where the theme will be set
* @param attrs holding custom styles if any are set
*/
static void setTheme(Context context) {
static void setTheme(Context context, AttributeSet attrs) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setTheme method is getting long.

int uiMode = context.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK;
boolean darkThemeEnabled = uiMode == Configuration.UI_MODE_NIGHT_YES;
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(context.getString(R.string.dark_theme_enabled), darkThemeEnabled);
editor.apply();
context.setTheme(darkThemeEnabled ? R.style.NavigationViewDark : R.style.NavigationViewLight);
}
updatePreferencesDarkEnabled(context, darkThemeEnabled);

/**
* Can be called to toggle the theme based on the current theme setting.
*
* @param activity {@link NavigationView} where the theme will be set
*/
static void toggleTheme(Activity activity) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
boolean darkThemeEnabled = preferences.getBoolean(activity.getString(R.string.dark_theme_enabled), false);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(activity.getString(R.string.dark_theme_enabled), !darkThemeEnabled);
editor.apply();
activity.recreate();
TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.NavigationView);
int lightTheme = styledAttributes.getResourceId(R.styleable.NavigationView_navigationLightTheme,
R.style.NavigationViewLight);
int darkTheme = styledAttributes.getResourceId(R.styleable.NavigationView_navigationDarkTheme,
R.style.NavigationViewDark);
styledAttributes.recycle();

context.setTheme(darkThemeEnabled ? darkTheme : lightTheme);
}

/**
Expand All @@ -56,11 +62,9 @@ static void toggleTheme(Activity activity) {
* @param map the style will be set on
*/
static void setMapStyle(Context context, MapboxMap map, MapboxMap.OnStyleLoadedListener listener) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean darkThemeEnabled = preferences.getBoolean(context.getString(R.string.dark_theme_enabled), false);
String nightThemeUrl = context.getString(R.string.navigation_guidance_night_v2);
String dayThemeUrl = context.getString(R.string.navigation_guidance_day_v2);
map.setStyleUrl(darkThemeEnabled ? nightThemeUrl : dayThemeUrl, listener);
TypedValue mapStyleAttr = obtainTypedValue(context, R.attr.navigationViewMapStyle);
String styleUrl = mapStyleAttr.string.toString();
map.setStyleUrl(styleUrl, listener);
}

/**
Expand All @@ -77,142 +81,28 @@ static Icon retrieveMapMarker(Context context) {
}

/**
* Looks are current theme and retrieves the primary color
* Looks are current theme and retrieves the route style
* for the given set theme.
*
* @param context to retrieve {@link SharedPreferences} and color with {@link ContextCompat}
* @return color resource identifier for primary theme color
* @param context to retrieve the resolved attribute
* @return style resource Id for the route
*/
public static int retrieveNavigationViewPrimaryColor(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean darkThemeEnabled = preferences.getBoolean(context.getString(R.string.dark_theme_enabled), false);
TypedArray styleArray = context.obtainStyledAttributes(
darkThemeEnabled ? R.style.NavigationViewDark : R.style.NavigationViewLight,
R.styleable.NavigationView
);
int navigationViewPrimary = styleArray.getColor(R.styleable.NavigationView_navigationViewPrimary,
ContextCompat.getColor(context, R.color.mapbox_navigation_view_color_primary));
styleArray.recycle();
return navigationViewPrimary;
static int retrieveNavigationViewRouteStyle(Context context) {
TypedValue outValue = obtainTypedValue(context, R.attr.navigationViewRouteStyle);
return outValue.resourceId;
}

/**
* Looks are current theme and retrieves the secondary color
* for the given set theme.
*
* @param context to retrieve {@link SharedPreferences} and color with {@link ContextCompat}
* @return color resource identifier for secondary theme color
*/
public static int retrieveNavigationViewSecondaryColor(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean darkThemeEnabled = preferences.getBoolean(context.getString(R.string.dark_theme_enabled), false);
TypedArray styleArray = context.obtainStyledAttributes(
darkThemeEnabled ? R.style.NavigationViewDark : R.style.NavigationViewLight,
R.styleable.NavigationView
);
int navigationViewSecondary = styleArray.getColor(R.styleable.NavigationView_navigationViewSecondary,
ContextCompat.getColor(context, R.color.mapbox_navigation_view_color_secondary));
styleArray.recycle();
return navigationViewSecondary;
@NonNull
private static TypedValue obtainTypedValue(Context context, int resId) {
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(resId, outValue, true);
return outValue;
}

/**
* Looks are current theme and retrieves the banner background color
* for the given set theme.
*
* @param context to retrieve {@link SharedPreferences} and color with {@link ContextCompat}
* @return color resource identifier for banner background color
*/
public static int retrieveNavigationViewBannerBackgroundColor(Context context) {
private static void updatePreferencesDarkEnabled(Context context, boolean darkThemeEnabled) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean darkThemeEnabled = preferences.getBoolean(context.getString(R.string.dark_theme_enabled), false);
TypedArray styleArray = context.obtainStyledAttributes(
darkThemeEnabled ? R.style.NavigationViewDark : R.style.NavigationViewLight,
R.styleable.NavigationView
);
int bannerBackground = styleArray.getColor(R.styleable.NavigationView_navigationViewBannerBackground,
ContextCompat.getColor(context, R.color.mapbox_navigation_view_color_banner_background));
styleArray.recycle();
return bannerBackground;
}

/**
* Looks are current theme and retrieves the banner maneuver primary color
* for the given set theme.
*
* @param context to retrieve {@link SharedPreferences} and color with {@link ContextCompat}
* @return color resource identifier for banner maneuver primary theme color
*/
public static int retrieveNavigationViewBannerManeuverPrimaryColor(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean darkThemeEnabled = preferences.getBoolean(context.getString(R.string.dark_theme_enabled), false);
TypedArray styleArray = context.obtainStyledAttributes(
darkThemeEnabled ? R.style.NavigationViewDark : R.style.NavigationViewLight,
R.styleable.NavigationView
);
int bannerManeuverPrimary = styleArray.getColor(R.styleable.NavigationView_navigationViewBannerManeuverPrimary,
ContextCompat.getColor(context, R.color.mapbox_navigation_view_color_banner_maneuver_primary));
styleArray.recycle();
return bannerManeuverPrimary;
}

/**
* Looks are current theme and retrieves the banner maneuver secondary color
* for the given set theme.
*
* @param context to retrieve {@link SharedPreferences} and color with {@link ContextCompat}
* @return color resource identifier for banner maneuver secondary theme color
*/
public static int retrieveNavigationViewBannerManeuverSecondaryColor(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean darkThemeEnabled = preferences.getBoolean(context.getString(R.string.dark_theme_enabled), false);
TypedArray styleArray = context.obtainStyledAttributes(
darkThemeEnabled ? R.style.NavigationViewDark : R.style.NavigationViewLight,
R.styleable.NavigationView
);
int bannerManeuverSecondary = styleArray.getColor(R.styleable.NavigationView_navigationViewBannerManeuverSecondary,
ContextCompat.getColor(context, R.color.mapbox_navigation_view_color_banner_maneuver_secondary));
styleArray.recycle();
return bannerManeuverSecondary;
}

/**
* Looks are current theme and retrieves the progress color
* for the given set theme.
*
* @param context to retrieve {@link SharedPreferences} and color with {@link ContextCompat}
* @return color resource identifier for progress color
*/
public static int retrieveNavigationViewProgressColor(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean darkThemeEnabled = preferences.getBoolean(context.getString(R.string.dark_theme_enabled), false);
TypedArray styleArray = context.obtainStyledAttributes(
darkThemeEnabled ? R.style.NavigationViewDark : R.style.NavigationViewLight,
R.styleable.NavigationView
);
int progress = styleArray.getColor(R.styleable.NavigationView_navigationViewProgress,
ContextCompat.getColor(context, R.color.mapbox_navigation_view_color_progress));
styleArray.recycle();
return progress;
}

/**
* Looks are current theme and retrieves the progress background color
* for the given set theme.
*
* @param context to retrieve {@link SharedPreferences} and color with {@link ContextCompat}
* @return color resource identifier for progress background color
*/
public static int retrieveNavigationViewProgressBackgroundColor(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean darkThemeEnabled = preferences.getBoolean(context.getString(R.string.dark_theme_enabled), false);
TypedArray styleArray = context.obtainStyledAttributes(
darkThemeEnabled ? R.style.NavigationViewDark : R.style.NavigationViewLight,
R.styleable.NavigationView
);
int progressBackground = styleArray.getColor(R.styleable.NavigationView_navigationViewProgressBackground,
ContextCompat.getColor(context, R.color.mapbox_navigation_view_color_progress_background));
styleArray.recycle();
return progressBackground;
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(context.getString(R.string.dark_theme_enabled), darkThemeEnabled);
editor.apply();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,10 @@ private void initAnimations() {

private void initBackground() {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
int progressColor = ThemeSwitcher.retrieveNavigationViewProgressColor(getContext());
int progressBackgroundColor = ThemeSwitcher.retrieveNavigationViewProgressBackgroundColor(getContext());
int progressColor = ThemeSwitcher.retrieveNavigationViewThemeColor(getContext(),
R.attr.navigationViewProgress);
int progressBackgroundColor = ThemeSwitcher.retrieveNavigationViewThemeColor(getContext(),
R.attr.navigationViewProgressBackground);

LayerDrawable progressBarDrawable = (LayerDrawable) alertProgressBar.getProgressDrawable();
// ProgressBar progress color
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,10 @@ public void onAnimationRepeat(Animator animation) {

private void initBackground(View view) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
int navigationViewPrimaryColor = ThemeSwitcher.retrieveNavigationViewPrimaryColor(getContext());
int navigationViewSecondaryColor = ThemeSwitcher.retrieveNavigationViewSecondaryColor(getContext());
int navigationViewPrimaryColor = ThemeSwitcher.retrieveNavigationViewThemeColor(getContext(),
R.attr.navigationViewPrimary);
int navigationViewSecondaryColor = ThemeSwitcher.retrieveNavigationViewThemeColor(getContext(),
R.attr.navigationViewSecondary);
// BottomSheet background
Drawable bottomSheetBackground = DrawableCompat.wrap(view.getBackground()).mutate();
DrawableCompat.setTint(bottomSheetBackground, navigationViewPrimaryColor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ void setFeedbackText(String feedbackText) {

private void initTextColor(TextView feedbackText) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
int navigationViewSecondaryColor = ThemeSwitcher.retrieveNavigationViewSecondaryColor(feedbackText.getContext());
int navigationViewSecondaryColor = ThemeSwitcher.retrieveNavigationViewThemeColor(feedbackText.getContext(),
R.attr.navigationViewSecondary);
// Text color
feedbackText.setTextColor(navigationViewSecondaryColor);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,10 @@ private void bind() {
*/
private void initBackground() {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
int navigationViewPrimaryColor = ThemeSwitcher.retrieveNavigationViewPrimaryColor(getContext());
int navigationViewBannerBackgroundColor = ThemeSwitcher.retrieveNavigationViewBannerBackgroundColor(getContext());
int navigationViewPrimaryColor = ThemeSwitcher.retrieveNavigationViewThemeColor(getContext(),
R.attr.navigationViewPrimary);
int navigationViewBannerBackgroundColor = ThemeSwitcher.retrieveNavigationViewThemeColor(getContext(),
R.attr.navigationViewBannerBackground);
// Instruction Layout landscape - banner background
if (getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
View instructionLayoutManeuver = findViewById(R.id.instructionManeuverLayout);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import android.util.AttributeSet;
import android.view.View;

import com.mapbox.services.android.navigation.ui.v5.R;
import com.mapbox.services.android.navigation.ui.v5.ThemeSwitcher;
import com.mapbox.services.android.navigation.v5.navigation.NavigationConstants;

Expand Down Expand Up @@ -65,8 +66,10 @@ protected void onFinishInflate() {
}

private void initManeuverColor() {
this.primaryColor = ThemeSwitcher.retrieveNavigationViewBannerManeuverPrimaryColor(getContext());
this.secondaryColor = ThemeSwitcher.retrieveNavigationViewBannerManeuverSecondaryColor(getContext());
this.primaryColor = ThemeSwitcher.retrieveNavigationViewThemeColor(getContext(),
R.attr.navigationViewBannerManeuverPrimary);
this.secondaryColor = ThemeSwitcher.retrieveNavigationViewThemeColor(getContext(),
R.attr.navigationViewBannerManeuverSecondary);
}

public void setManeuverType(String maneuverType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.view.View;

import com.mapbox.directions.v5.models.IntersectionLanes;
import com.mapbox.services.android.navigation.ui.v5.R;
import com.mapbox.services.android.navigation.ui.v5.ThemeSwitcher;
import com.mapbox.services.commons.utils.TextUtils;

Expand Down Expand Up @@ -114,7 +115,9 @@ public void updateLaneView(@NonNull IntersectionLanes lane, @NonNull String mane
}

private void initManeuverColor() {
this.primaryColor = ThemeSwitcher.retrieveNavigationViewBannerManeuverPrimaryColor(getContext());
this.secondaryColor = ThemeSwitcher.retrieveNavigationViewBannerManeuverSecondaryColor(getContext());
this.primaryColor = ThemeSwitcher.retrieveNavigationViewThemeColor(getContext(),
R.attr.navigationViewBannerManeuverPrimary);
this.secondaryColor = ThemeSwitcher.retrieveNavigationViewThemeColor(getContext(),
R.attr.navigationViewBannerManeuverSecondary);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
android:id="@+id/navigationView"
android:layout_width="0dp"
android:layout_height="0dp"
app:navigationLightTheme="@style/NavigationViewLight"
app:navigationDarkTheme="@style/NavigationViewDark"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
Expand Down
11 changes: 11 additions & 0 deletions libandroid-navigation-ui/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,19 @@
<attr name="navigationViewBannerManeuverPrimary" format="color"/>
<attr name="navigationViewBannerManeuverSecondary" format="color"/>

<!-- Progress Bars -->
<attr name="navigationViewProgress" format="color"/>
<attr name="navigationViewProgressBackground" format="color"/>

<!-- Map style -->
<attr name="navigationViewMapStyle" format="string"/>

<!-- Route style -->
<attr name="navigationViewRouteStyle" format="reference"/>

<!-- For setting the styles in XML -->
<attr name="navigationLightTheme" format="reference"/>
<attr name="navigationDarkTheme" format="reference"/>

</declare-styleable>
</resources>
Loading