Skip to content

Commit 08b960c

Browse files
authored
Merge pull request #3632 from B0pol/device_theme
Add settings to match device's theme (dark & black)
2 parents 4fac3cf + 731c65c commit 08b960c

8 files changed

Lines changed: 155 additions & 99 deletions

File tree

app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,40 +18,43 @@ public class AppearanceSettingsFragment extends BasePreferenceFragment {
1818
private static final boolean CAPTIONING_SETTINGS_ACCESSIBLE =
1919
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
2020

21-
/**
22-
* Theme that was applied when the settings was opened (or recreated after a theme change).
23-
*/
24-
private String startThemeKey;
25-
private final Preference.OnPreferenceChangeListener themePreferenceChange
26-
= new Preference.OnPreferenceChangeListener() {
27-
@Override
28-
public boolean onPreferenceChange(final Preference preference, final Object newValue) {
29-
defaultPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, true).apply();
30-
defaultPreferences.edit()
31-
.putString(getString(R.string.theme_key), newValue.toString()).apply();
32-
33-
if (!newValue.equals(startThemeKey) && getActivity() != null) {
34-
// If it's not the current theme
35-
ActivityCompat.recreate(requireActivity());
36-
}
37-
38-
return false;
39-
}
40-
};
4121
private String captionSettingsKey;
4222

4323
@Override
4424
public void onCreate(@Nullable final Bundle savedInstanceState) {
4525
super.onCreate(savedInstanceState);
26+
4627
final String themeKey = getString(R.string.theme_key);
47-
startThemeKey = defaultPreferences
28+
// the key of the active theme when settings were opened (or recreated after theme change)
29+
final String startThemeKey = defaultPreferences
4830
.getString(themeKey, getString(R.string.default_theme_value));
49-
findPreference(themeKey).setOnPreferenceChangeListener(themePreferenceChange);
31+
final String autoDeviceThemeKey = getString(R.string.auto_device_theme_key);
32+
findPreference(themeKey).setOnPreferenceChangeListener((preference, newValue) -> {
33+
if (newValue.toString().equals(autoDeviceThemeKey)) {
34+
Toast.makeText(getContext(), getString(R.string.select_night_theme_toast),
35+
Toast.LENGTH_LONG).show();
36+
}
37+
38+
applyThemeChange(startThemeKey, themeKey, newValue);
39+
return false;
40+
});
41+
42+
final String nightThemeKey = getString(R.string.night_theme_key);
43+
if (startThemeKey.equals(autoDeviceThemeKey)) {
44+
final String startNightThemeKey = defaultPreferences
45+
.getString(nightThemeKey, getString(R.string.default_night_theme_value));
46+
47+
findPreference(nightThemeKey).setOnPreferenceChangeListener((preference, newValue) -> {
48+
applyThemeChange(startNightThemeKey, nightThemeKey, newValue);
49+
return false;
50+
});
51+
} else {
52+
removePreference(nightThemeKey);
53+
}
5054

5155
captionSettingsKey = getString(R.string.caption_settings_key);
5256
if (!CAPTIONING_SETTINGS_ACCESSIBLE) {
53-
final Preference captionSettings = findPreference(captionSettingsKey);
54-
getPreferenceScreen().removePreference(captionSettings);
57+
removePreference(captionSettingsKey);
5558
}
5659
}
5760

@@ -72,4 +75,23 @@ public boolean onPreferenceTreeClick(final Preference preference) {
7275

7376
return super.onPreferenceTreeClick(preference);
7477
}
78+
79+
private void removePreference(final String preferenceKey) {
80+
final Preference preference = findPreference(preferenceKey);
81+
if (preference != null) {
82+
getPreferenceScreen().removePreference(preference);
83+
}
84+
}
85+
86+
private void applyThemeChange(final String beginningThemeKey,
87+
final String themeKey,
88+
final Object newValue) {
89+
defaultPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, true).apply();
90+
defaultPreferences.edit().putString(themeKey, newValue.toString()).apply();
91+
92+
if (!newValue.equals(beginningThemeKey) && getActivity() != null) {
93+
// if it's not the current theme
94+
ActivityCompat.recreate(getActivity());
95+
}
96+
}
7597
}

app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static void initSettings(final Context context) {
4848

4949
@Override
5050
protected void onCreate(final Bundle savedInstanceBundle) {
51-
setTheme(ThemeHelper.getSettingsThemeStyle(this));
51+
ThemeHelper.setTheme(this);
5252
assureCorrectAppLanguage(this);
5353
super.onCreate(savedInstanceBundle);
5454

app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java

Lines changed: 76 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121

2222
import android.app.Activity;
2323
import android.content.Context;
24+
import android.content.res.Configuration;
25+
import android.content.res.Resources;
2426
import android.content.res.TypedArray;
2527
import android.util.TypedValue;
26-
import android.view.ContextThemeWrapper;
2728

2829
import androidx.annotation.AttrRes;
2930
import androidx.annotation.Nullable;
@@ -39,7 +40,8 @@
3940
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
4041

4142
public final class ThemeHelper {
42-
private ThemeHelper() { }
43+
private ThemeHelper() {
44+
}
4345

4446
/**
4547
* Apply the selected theme (on NewPipe settings) in the context
@@ -70,31 +72,12 @@ public static void setTheme(final Context context, final int serviceId) {
7072
* @return whether the light theme is selected
7173
*/
7274
public static boolean isLightThemeSelected(final Context context) {
73-
return getSelectedThemeString(context).equals(context.getResources()
74-
.getString(R.string.light_theme_key));
75-
}
76-
77-
78-
/**
79-
* Create and return a wrapped context with the default selected theme set.
80-
*
81-
* @param baseContext the base context for the wrapper
82-
* @return a wrapped-styled context
83-
*/
84-
public static Context getThemedContext(final Context baseContext) {
85-
return new ContextThemeWrapper(baseContext, getThemeForService(baseContext, -1));
86-
}
75+
final String selectedThemeKey = getSelectedThemeKey(context);
76+
final Resources res = context.getResources();
8777

88-
/**
89-
* Return the selected theme without being styled to any service.
90-
* See {@link #getThemeForService(Context, int)}.
91-
*
92-
* @param context context to get the selected theme
93-
* @return the selected style (the default one)
94-
*/
95-
@StyleRes
96-
public static int getDefaultTheme(final Context context) {
97-
return getThemeForService(context, -1);
78+
return selectedThemeKey.equals(res.getString(R.string.light_theme_key))
79+
|| (selectedThemeKey.equals(res.getString(R.string.auto_device_theme_key))
80+
&& !isDeviceDarkThemeEnabled(context));
9881
}
9982

10083
/**
@@ -130,71 +113,60 @@ public static int getMinWidthDialogTheme(final Context context) {
130113
*/
131114
@StyleRes
132115
public static int getThemeForService(final Context context, final int serviceId) {
133-
final String lightTheme = context.getResources().getString(R.string.light_theme_key);
134-
final String darkTheme = context.getResources().getString(R.string.dark_theme_key);
135-
final String blackTheme = context.getResources().getString(R.string.black_theme_key);
136-
137-
final String selectedTheme = getSelectedThemeString(context);
138-
139-
int defaultTheme = R.style.DarkTheme;
140-
if (selectedTheme.equals(lightTheme)) {
141-
defaultTheme = R.style.LightTheme;
142-
} else if (selectedTheme.equals(blackTheme)) {
143-
defaultTheme = R.style.BlackTheme;
144-
} else if (selectedTheme.equals(darkTheme)) {
145-
defaultTheme = R.style.DarkTheme;
116+
final Resources res = context.getResources();
117+
final String lightThemeKey = res.getString(R.string.light_theme_key);
118+
final String blackThemeKey = res.getString(R.string.black_theme_key);
119+
final String automaticDeviceThemeKey = res.getString(R.string.auto_device_theme_key);
120+
121+
final String selectedThemeKey = getSelectedThemeKey(context);
122+
123+
int baseTheme = R.style.DarkTheme; // default to dark theme
124+
if (selectedThemeKey.equals(lightThemeKey)) {
125+
baseTheme = R.style.LightTheme;
126+
} else if (selectedThemeKey.equals(blackThemeKey)) {
127+
baseTheme = R.style.BlackTheme;
128+
} else if (selectedThemeKey.equals(automaticDeviceThemeKey)) {
129+
130+
if (isDeviceDarkThemeEnabled(context)) {
131+
// use the dark theme variant preferred by the user
132+
final String selectedNightThemeKey = getSelectedNightThemeKey(context);
133+
if (selectedNightThemeKey.equals(blackThemeKey)) {
134+
baseTheme = R.style.BlackTheme;
135+
} else {
136+
baseTheme = R.style.DarkTheme;
137+
}
138+
} else {
139+
// there is only one day theme
140+
baseTheme = R.style.LightTheme;
141+
}
146142
}
147143

148144
if (serviceId <= -1) {
149-
return defaultTheme;
145+
return baseTheme;
150146
}
151147

152148
final StreamingService service;
153149
try {
154150
service = NewPipe.getService(serviceId);
155151
} catch (final ExtractionException ignored) {
156-
return defaultTheme;
152+
return baseTheme;
157153
}
158154

159-
String themeName = "DarkTheme";
160-
if (selectedTheme.equals(lightTheme)) {
155+
String themeName = "DarkTheme"; // default
156+
if (baseTheme == R.style.LightTheme) {
161157
themeName = "LightTheme";
162-
} else if (selectedTheme.equals(blackTheme)) {
158+
} else if (baseTheme == R.style.BlackTheme) {
163159
themeName = "BlackTheme";
164-
} else if (selectedTheme.equals(darkTheme)) {
165-
themeName = "DarkTheme";
166160
}
167161

168162
themeName += "." + service.getServiceInfo().getName();
169-
final int resourceId = context
170-
.getResources()
163+
final int resourceId = context.getResources()
171164
.getIdentifier(themeName, "style", context.getPackageName());
172165

173166
if (resourceId > 0) {
174167
return resourceId;
175168
}
176-
177-
return defaultTheme;
178-
}
179-
180-
@StyleRes
181-
public static int getSettingsThemeStyle(final Context context) {
182-
final String lightTheme = context.getResources().getString(R.string.light_theme_key);
183-
final String darkTheme = context.getResources().getString(R.string.dark_theme_key);
184-
final String blackTheme = context.getResources().getString(R.string.black_theme_key);
185-
186-
final String selectedTheme = getSelectedThemeString(context);
187-
188-
if (selectedTheme.equals(lightTheme)) {
189-
return R.style.LightSettingsTheme;
190-
} else if (selectedTheme.equals(blackTheme)) {
191-
return R.style.BlackSettingsTheme;
192-
} else if (selectedTheme.equals(darkTheme)) {
193-
return R.style.DarkSettingsTheme;
194-
} else {
195-
// Fallback
196-
return R.style.DarkSettingsTheme;
197-
}
169+
return baseTheme;
198170
}
199171

200172
/**
@@ -229,18 +201,27 @@ public static int resolveColorFromAttr(final Context context, @AttrRes final int
229201
return value.data;
230202
}
231203

232-
private static String getSelectedThemeString(final Context context) {
204+
private static String getSelectedThemeKey(final Context context) {
233205
final String themeKey = context.getString(R.string.theme_key);
234206
final String defaultTheme = context.getResources().getString(R.string.default_theme_value);
235207
return PreferenceManager.getDefaultSharedPreferences(context)
236208
.getString(themeKey, defaultTheme);
237209
}
238210

211+
private static String getSelectedNightThemeKey(final Context context) {
212+
final String nightThemeKey = context.getString(R.string.night_theme_key);
213+
final String defaultNightTheme = context.getResources()
214+
.getString(R.string.default_night_theme_value);
215+
return PreferenceManager.getDefaultSharedPreferences(context)
216+
.getString(nightThemeKey, defaultNightTheme);
217+
}
218+
239219
/**
240220
* Sets the title to the activity, if the activity is an {@link AppCompatActivity} and has an
241221
* action bar.
222+
*
242223
* @param activity the activity to set the title of
243-
* @param title the title to set to the activity
224+
* @param title the title to set to the activity
244225
*/
245226
public static void setTitleToAppCompatActivity(@Nullable final Activity activity,
246227
final CharSequence title) {
@@ -251,4 +232,27 @@ public static void setTitleToAppCompatActivity(@Nullable final Activity activity
251232
}
252233
}
253234
}
235+
236+
/**
237+
* Get the device theme
238+
* <p>
239+
* It will return true if the device 's theme is dark, false otherwise.
240+
* <p>
241+
* From https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#java
242+
*
243+
* @param context the context to use
244+
* @return true:dark theme, false:light or unknown
245+
*/
246+
public static boolean isDeviceDarkThemeEnabled(final Context context) {
247+
final int deviceTheme = context.getResources().getConfiguration().uiMode
248+
& Configuration.UI_MODE_NIGHT_MASK;
249+
switch (deviceTheme) {
250+
case Configuration.UI_MODE_NIGHT_YES:
251+
return true;
252+
case Configuration.UI_MODE_NIGHT_UNDEFINED:
253+
case Configuration.UI_MODE_NIGHT_NO:
254+
default:
255+
return false;
256+
}
257+
}
254258
}

app/src/main/res/values-eo/strings.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<string name="theme_title">Etoso</string>
2424
<string name="dark_theme_title">Malluma</string>
2525
<string name="light_theme_title">Luma</string>
26+
<string name="black_theme_title">Nigra</string>
2627
<string name="download_dialog_title">Elŝuti</string>
2728
<string name="unsupported_url">Ligilo ne subtenita</string>
2829
<string name="content_language_title">Preferata enhavlingvo</string>
@@ -90,7 +91,6 @@
9091
<string name="show_higher_resolutions_title">Montri pli altajn rezoluciojn</string>
9192
<string name="show_higher_resolutions_summary">Nur kelkaj aparatoj povas ludi 2K / 4K filmetojn</string>
9293
<string name="default_video_format_title">Defaŭlta fomato de filmeto</string>
93-
<string name="black_theme_title">Nigra</string>
9494
<string name="popup_remember_size_pos_title">Memoru ŝprucfenestran grandecon kaj pozicion</string>
9595
<string name="popup_remember_size_pos_summary">Memoru lastan grandecon kaj pozicion de ŝprucfenestro</string>
9696
<string name="use_inexact_seek_title">Uzi rapide, ne precizan serĉon</string>

app/src/main/res/values-fr/strings.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@
4141
<string name="use_tor_title">Utiliser Tor</string>
4242
<string name="use_tor_summary">(Expérimental) Forcer la redirection du trafic de téléchargement via Tor pour plus de confidentialité (les flux vidéos ne sont pas encore pris en charge).</string>
4343
<string name="theme_title">Thème</string>
44+
<string name="night_theme_title">Thème nuit</string>
4445
<string name="dark_theme_title">Sombre</string>
4546
<string name="light_theme_title">Clair</string>
47+
<string name="black_theme_title">Noir</string>
4648
<string name="settings_category_appearance_title">Apparence</string>
4749
<string name="network_error">Erreur réseau</string>
4850
<string name="download_path_audio_title">Dossier de téléchargement audio</string>
@@ -103,7 +105,6 @@
103105
<string name="no_available_dir">Veuillez définir ultérieurement un dossier de téléchargement dans les paramètres</string>
104106
<string name="could_not_load_image">Impossible de charger l’image</string>
105107
<string name="app_ui_crash">L’application a planté</string>
106-
<string name="black_theme_title">Noir</string>
107108
<string name="all">Tout</string>
108109
<string name="channel">Chaîne</string>
109110
<string name="title_activity_recaptcha">Défi reCAPTCHA</string>
@@ -669,4 +670,7 @@
669670
<string name="paid_content">Ce contenu n\'est disponible que pour les abonnés, il ne peut donc pas être diffusé en continu ni téléchargé par NewPipe.</string>
670671
<string name="youtube_music_premium_content">Cette vidéo n\'est disponible que pour les membres de YouTube Music Premium, elle ne peut donc pas être diffusée en continu ni téléchargée par NewPipe.</string>
671672
<string name="private_content">Ce contenu est privé, il ne peut donc pas être diffusé en continu ni téléchargé par NewPipe.</string>
673+
<string name="auto_device_theme_title">Automatique (thème de l\'appareil)</string>
674+
<string name="night_theme_summary">Choisissez votre thème nuit favori — %s</string>
675+
<string name="select_night_theme_toast">Vous pouvez chosir votre thème nuit favori</string>
672676
</resources>

app/src/main/res/values/settings_keys.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,19 +176,32 @@
176176

177177
<!-- THEMES -->
178178
<string name="theme_key" translatable="false">theme</string>
179+
<string name="night_theme_key" translatable="false">night_theme</string>
179180
<string name="light_theme_key" translatable="false">light_theme</string>
180181
<string name="dark_theme_key" translatable="false">dark_theme</string>
181182
<string name="black_theme_key" translatable="false">black_theme</string>
183+
<string name="auto_device_theme_key" translatable="false">auto_device_theme</string>
182184
<string name="default_theme_value" translatable="false">@string/dark_theme_key</string>
185+
<string name="default_night_theme_value" translatable="false">@string/dark_theme_key</string>
183186
<string-array name="theme_values_list" translatable="false">
184187
<item>@string/light_theme_key</item>
185188
<item>@string/dark_theme_key</item>
186189
<item>@string/black_theme_key</item>
190+
<item>@string/auto_device_theme_key</item>
187191
</string-array>
188192
<string-array name="theme_description_list" translatable="false">
189193
<item>@string/light_theme_title</item>
190194
<item>@string/dark_theme_title</item>
191195
<item>@string/black_theme_title</item>
196+
<item>@string/auto_device_theme_title</item>
197+
</string-array>
198+
<string-array name="night_theme_values_list" translatable="false">
199+
<item>@string/dark_theme_key</item>
200+
<item>@string/black_theme_key</item>
201+
</string-array>
202+
<string-array name="night_theme_description_list" translatable="false">
203+
<item>@string/dark_theme_title</item>
204+
<item>@string/black_theme_title</item>
192205
</string-array>
193206

194207
<!-- Caption Size -->

0 commit comments

Comments
 (0)