From 3d87a167c8c083b9d4b8af7f4d7d227e8f1f4a61 Mon Sep 17 00:00:00 2001 From: Adrian Ulrich Date: Sun, 10 May 2015 20:34:57 +0200 Subject: [PATCH] Dark theme support for Android 5.0 --- AndroidManifest.xml | 16 +-- res/layout-v21/draggable_row.xml | 2 +- res/layout/draggable_row.xml | 2 +- res/layout/filebrowser_content.xml | 4 +- res/layout/full_playback.xml | 4 +- res/layout/full_playback_alt.xml | 40 +++--- res/layout/library_row_expandable.xml | 2 +- res/layout/listview.xml | 2 +- res/layout/playlist_activity.xml | 2 +- res/layout/showqueue_listview.xml | 2 +- res/layout/tab_order.xml | 2 +- res/values-v21/theme.xml | 49 ++++++-- res/values/attrs.xml | 6 + res/values/theme.xml | 28 ++--- res/values/translatable.xml | 2 + res/xml/preference_playback.xml | 5 + .../android/vanilla/CoverBitmap.java | 9 +- .../vanilla/FilebrowserStartActivity.java | 3 +- .../android/vanilla/FullPlaybackActivity.java | 4 +- .../android/vanilla/LibraryActivity.java | 1 + .../android/vanilla/PlaybackActivity.java | 17 ++- .../android/vanilla/PlaybackService.java | 8 +- .../android/vanilla/PlaylistActivity.java | 1 + .../android/vanilla/PrefKeys.java | 2 +- .../android/vanilla/PreferencesActivity.java | 18 ++- .../android/vanilla/ShowQueueActivity.java | 5 +- .../android/vanilla/TabOrderActivity.java | 2 + .../android/vanilla/ThemeHelper.java | 115 ++++++++++++++++++ 28 files changed, 263 insertions(+), 90 deletions(-) create mode 100644 res/values/attrs.xml create mode 100644 src/ch/blinkenlights/android/vanilla/ThemeHelper.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index dded9614..f537c83a 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,6 +1,6 @@ - @color/button_material_light - #ffefefef - #f000 - @color/button_material_light + #ff5a595b #ff37474f @@ -40,17 +35,15 @@ Copyright (C) 2015 Adrian Ulrich @color/vanillaAccent @color/material_grey_600 - - @color/overlay_background_color - @color/divider_color - @color/divider_color - #55ffffff - + + + + + + + + + + + + + diff --git a/res/values/attrs.xml b/res/values/attrs.xml new file mode 100644 index 00000000..6089011d --- /dev/null +++ b/res/values/attrs.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/res/values/theme.xml b/res/values/theme.xml index 0349d25e..78d34f3e 100644 --- a/res/values/theme.xml +++ b/res/values/theme.xml @@ -21,19 +21,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> - - #f444 - #a000 - - #ffb2b2b2 - #f222 - - - #ff000000 - #ff606060 - #ff404040 - #88000000 - @android:color/holo_blue_dark #fff5f5f5 @@ -44,17 +31,26 @@ THE SOFTWARE. - + + - - diff --git a/res/values/translatable.xml b/res/values/translatable.xml index 69ca9aed..44382fde 100644 --- a/res/values/translatable.xml +++ b/res/values/translatable.xml @@ -207,6 +207,8 @@ THE SOFTWARE. Shake Force Threshold Miscellaneous Features + Use dark theme + Enables the dark material theme (requires restart) Disable Lockscreen Prevent the lockscreen from activating when in the library or playback screen Enable Idle Timeout diff --git a/res/xml/preference_playback.xml b/res/xml/preference_playback.xml index ed2c95b2..06c74b10 100644 --- a/res/xml/preference_playback.xml +++ b/res/xml/preference_playback.xml @@ -28,6 +28,11 @@ THE SOFTWARE. android:title="@string/playback_on_startup_title" android:summary="@string/playback_on_startup_summary" android:defaultValue="false" /> + - * Copyright (C) 2014 Adrian Ulrich + * Copyright (C) 2014-2015 Adrian Ulrich * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -55,7 +55,8 @@ import android.widget.Toast; public abstract class PlaybackActivity extends Activity implements Handler.Callback, View.OnClickListener, - CoverView.Callback + CoverView.Callback, + SharedPreferences.OnSharedPreferenceChangeListener { private Action mUpAction; private Action mDownAction; @@ -118,6 +119,7 @@ public abstract class PlaybackActivity extends Activity startService(new Intent(this, PlaybackService.class)); SharedPreferences prefs = PlaybackService.getSettings(this); + prefs.registerOnSharedPreferenceChangeListener(this); mUpAction = Action.getAction(prefs, PrefKeys.SWIPE_UP_ACTION, Action.Nothing); mDownAction = Action.getAction(prefs, PrefKeys.SWIPE_DOWN_ACTION, Action.Nothing); @@ -139,8 +141,19 @@ public abstract class PlaybackActivity extends Activity PlaybackService service = PlaybackService.get(this); service.userActionTriggered(); } + } + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (key.equals(PrefKeys.USE_DARK_THEME)) { + // Terminate this activity as the currently used theme is outdated + finish(); + } + } + + + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { diff --git a/src/ch/blinkenlights/android/vanilla/PlaybackService.java b/src/ch/blinkenlights/android/vanilla/PlaybackService.java index 4708d7de..49521b83 100644 --- a/src/ch/blinkenlights/android/vanilla/PlaybackService.java +++ b/src/ch/blinkenlights/android/vanilla/PlaybackService.java @@ -1944,13 +1944,7 @@ public final class PlaybackService extends Service String title = song.title; - int playButton = 0; - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // Android >= 5.0 uses the dark version of this drawable - playButton = playing ? R.drawable.widget_pause : R.drawable.widget_play; - } else { - playButton = playing ? R.drawable.pause : R.drawable.play; - } + int playButton = ThemeHelper.getPlayButtonResource(playing); views.setImageViewResource(R.id.play_pause, playButton); expanded.setImageViewResource(R.id.play_pause, playButton); diff --git a/src/ch/blinkenlights/android/vanilla/PlaylistActivity.java b/src/ch/blinkenlights/android/vanilla/PlaylistActivity.java index 71dff042..4cbc7214 100644 --- a/src/ch/blinkenlights/android/vanilla/PlaylistActivity.java +++ b/src/ch/blinkenlights/android/vanilla/PlaylistActivity.java @@ -91,6 +91,7 @@ public class PlaylistActivity extends Activity @Override public void onCreate(Bundle state) { + ThemeHelper.setTheme(this, R.style.BackActionBar); super.onCreate(state); HandlerThread thread = new HandlerThread(getClass().getName()); diff --git a/src/ch/blinkenlights/android/vanilla/PrefKeys.java b/src/ch/blinkenlights/android/vanilla/PrefKeys.java index 26931807..be71d7ec 100644 --- a/src/ch/blinkenlights/android/vanilla/PrefKeys.java +++ b/src/ch/blinkenlights/android/vanilla/PrefKeys.java @@ -63,5 +63,5 @@ public class PrefKeys { public static final String REPLAYGAIN_BUMP = "replaygain_bump"; public static final String REPLAYGAIN_UNTAGGED_DEBUMP = "replaygain_untagged_debump"; public static final String ENABLE_READAHEAD = "enable_readahead"; - public static final String USE_LIGHT_THEME = "light_theme"; + public static final String USE_DARK_THEME = "use_dark_theme"; } diff --git a/src/ch/blinkenlights/android/vanilla/PreferencesActivity.java b/src/ch/blinkenlights/android/vanilla/PreferencesActivity.java index a65b214f..c99f8c80 100644 --- a/src/ch/blinkenlights/android/vanilla/PreferencesActivity.java +++ b/src/ch/blinkenlights/android/vanilla/PreferencesActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Adrian Ulrich + * Copyright (C) 2012-2015 Adrian Ulrich * Copyright (C) 2012 Christopher Eby * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -31,6 +31,7 @@ import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; +import android.preference.PreferenceScreen; import android.preference.CheckBoxPreference; import android.view.LayoutInflater; import android.view.MenuItem; @@ -42,6 +43,7 @@ import android.content.Context; import android.content.Intent; import android.media.audiofx.AudioEffect; import android.net.Uri; +import android.util.TypedValue; import java.util.List; /** @@ -55,6 +57,7 @@ public class PreferencesActivity extends PreferenceActivity { @Override protected void onCreate(Bundle savedInstanceState) { + ThemeHelper.setTheme(this, R.style.BackActionBar); super.onCreate(savedInstanceState); } @@ -152,6 +155,14 @@ public class PreferencesActivity extends PreferenceActivity { { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preference_playback); + + // Hide the dark theme preference if this device + // does not support multiple themes + PreferenceScreen screen = getPreferenceScreen(); + CheckBoxPreference dark_theme_pref = (CheckBoxPreference)findPreference("use_dark_theme"); + if (ThemeHelper.couldUseDarkTheme() == false) + screen.removePreference(dark_theme_pref); + } } @@ -206,7 +217,10 @@ public class PreferencesActivity extends PreferenceActivity { { WebView view = (WebView)super.onCreateView(inflater, container, savedInstanceState); view.getSettings().setJavaScriptEnabled(true); - String fontColor = getResources().getString(R.color.overlay_foreground_color); + + TypedValue value = new TypedValue(); + getActivity().getTheme().resolveAttribute(R.attr.overlay_foreground_color, value, true); + String fontColor = TypedValue.coerceToString(value.type, value.data); view.loadUrl("file:///android_asset/about.html?"+Uri.encode(fontColor)); view.setBackgroundColor(Color.TRANSPARENT); return view; diff --git a/src/ch/blinkenlights/android/vanilla/ShowQueueActivity.java b/src/ch/blinkenlights/android/vanilla/ShowQueueActivity.java index ada4b799..be6315bd 100644 --- a/src/ch/blinkenlights/android/vanilla/ShowQueueActivity.java +++ b/src/ch/blinkenlights/android/vanilla/ShowQueueActivity.java @@ -40,11 +40,12 @@ public class ShowQueueActivity extends PlaybackActivity @Override public void onCreate(Bundle savedInstanceState) { + ThemeHelper.setTheme(this, R.style.BackActionBar); super.onCreate(savedInstanceState); - + setTitle(R.string.queue); setContentView(R.layout.showqueue_listview); - + mService = PlaybackService.get(this); mListView = (DragSortListView) findViewById(R.id.list); listAdapter = new ShowQueueAdapter(this, R.layout.draggable_row); diff --git a/src/ch/blinkenlights/android/vanilla/TabOrderActivity.java b/src/ch/blinkenlights/android/vanilla/TabOrderActivity.java index 1a334803..ecb07f9a 100644 --- a/src/ch/blinkenlights/android/vanilla/TabOrderActivity.java +++ b/src/ch/blinkenlights/android/vanilla/TabOrderActivity.java @@ -44,7 +44,9 @@ public class TabOrderActivity extends Activity implements View.OnClickListener, @Override protected void onCreate(Bundle savedInstanceState) { + ThemeHelper.setTheme(this, R.style.BackActionBar); super.onCreate(savedInstanceState); + setTitle(R.string.tabs); setContentView(R.layout.tab_order); diff --git a/src/ch/blinkenlights/android/vanilla/ThemeHelper.java b/src/ch/blinkenlights/android/vanilla/ThemeHelper.java new file mode 100644 index 00000000..288b7228 --- /dev/null +++ b/src/ch/blinkenlights/android/vanilla/ThemeHelper.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2015 Adrian Ulrich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package ch.blinkenlights.android.vanilla; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Build; + +public class ThemeHelper { + + /** + * Calls context.setTheme() with given theme. + * Will automatically swap the theme with an alternative + * version if the user requested us to use it + */ + final public static int setTheme(Context context, int theme) + { + if (usesDarkTheme(context)) { + switch (theme) { + case R.style.Playback: + theme = R.style.Dark_Playback; + break; + case R.style.Library: + theme = R.style.Dark_Library; + break; + case R.style.BackActionBar: + theme = R.style.Dark_BackActionBar; + break; + default: + break; + } + } + context.setTheme(theme); + return theme; + } + + /** + * Helper function to get the correct play button drawable for our + * notification: The notification does not depend on the theme but + * depends on the API level + */ + final public static int getPlayButtonResource(boolean playing) + { + int playButton = 0; + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // Android >= 5.0 uses the dark version of this drawable + playButton = playing ? R.drawable.widget_pause : R.drawable.widget_play; + } else { + playButton = playing ? R.drawable.pause : R.drawable.play; + } + return playButton; + } + + /** + * Configures (or unconfigures) the use of the black theme + */ + final public static void setDarkTheme(boolean enable) + { + } + + /** + * Returns TRUE if we should use the dark material theme, + * Returns FALSE otherwise - always returns FALSE on pre-5.x devices + */ + final private static boolean usesDarkTheme(Context context) + { + boolean useDark = false; + if(couldUseDarkTheme()) { + SharedPreferences settings = PlaybackService.getSettings(context); + useDark = settings.getBoolean(PrefKeys.USE_DARK_THEME, false); + } + return useDark; + } + + /** + * Returns TRUE if this device may use the dark theme + * (eg: running api v21 or later) + */ + final public static boolean couldUseDarkTheme() { + return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP); + } + + /** + * Hacky function to get the colors needed to draw the default cover + * These colors should actually be attributes, but getting them programatically + * is a big mess + */ + final public static int[] getDefaultCoverColors(Context context) { + int[] colors_holo_yolo = { 0xff000000, 0xff606060, 0xff404040, 0x88000000 }; + int[] colors_material_light = { 0xffeeeeee, 0xffd6d7d7, 0xffd6d7d7, 0x55ffffff }; + int[] colors_material_dark = { 0xff303030, 0xff606060, 0xff404040, 0x33ffffff }; + if (couldUseDarkTheme() == false) + return colors_holo_yolo; + if (usesDarkTheme(context)) + return colors_material_dark; + // else + return colors_material_light; + } + +}