Skip to content

Commit 1584ffe

Browse files
authored
Added Support for System Wide Dark Theme (#3460)
* Added Support for System Wide Dark Theme * changed methods to private * Moved Strings to strings.xml * Used Dagger to reduce code repetition * Changes made as per review suggestions * Minor Changes * Fixes as per suggestions * Minor Fixes as per suggestion * made the variables static * removed irrelevant code
1 parent 65ec071 commit 1584ffe

File tree

9 files changed

+139
-42
lines changed

9 files changed

+139
-42
lines changed

app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import fr.free.nrw.commons.kvstore.JsonKvStore;
5656
import fr.free.nrw.commons.theme.NavigationBaseActivity;
5757
import fr.free.nrw.commons.utils.ConfigUtils;
58+
import fr.free.nrw.commons.utils.SystemThemeUtils;
5859
import fr.free.nrw.commons.utils.ViewUtil;
5960
import io.reactivex.disposables.CompositeDisposable;
6061
import retrofit2.Call;
@@ -83,6 +84,9 @@ public class LoginActivity extends AccountAuthenticatorActivity {
8384
@Inject
8485
LoginClient loginClient;
8586

87+
@Inject
88+
SystemThemeUtils systemThemeUtils;
89+
8690
@BindView(R.id.login_button)
8791
Button loginButton;
8892

@@ -121,7 +125,7 @@ public void onCreate(Bundle savedInstanceState) {
121125
.getCommonsApplicationComponent()
122126
.inject(this);
123127

124-
boolean isDarkTheme = applicationKvStore.getBoolean("theme", false);
128+
boolean isDarkTheme = systemThemeUtils.isDeviceInNightMode();
125129
setTheme(isDarkTheme ? R.style.DarkAppTheme : R.style.LightAppTheme);
126130
getDelegate().installViewFactory();
127131
getDelegate().onCreate(savedInstanceState);
@@ -133,7 +137,7 @@ public void onCreate(Bundle savedInstanceState) {
133137
usernameEdit.addTextChangedListener(textWatcher);
134138
passwordEdit.addTextChangedListener(textWatcher);
135139
twoFactorEdit.addTextChangedListener(textWatcher);
136-
140+
137141
if (ConfigUtils.isBetaFlavour()) {
138142
loginCredentials.setText(getString(R.string.login_credential));
139143
} else {
@@ -264,7 +268,7 @@ private void doLogin(String username, String password, String twoFactorCode) {
264268
new Callback<MwQueryResponse>() {
265269
@Override
266270
public void onResponse(Call<MwQueryResponse> call,
267-
Response<MwQueryResponse> response) {
271+
Response<MwQueryResponse> response) {
268272
loginClient.login(commonsWikiSite, username, password, null, twoFactorCode,
269273
response.body().query().loginToken(), new LoginCallback() {
270274
@Override
@@ -275,7 +279,7 @@ public void success(@NonNull LoginResult result) {
275279

276280
@Override
277281
public void twoFactorPrompt(@NonNull Throwable caught,
278-
@Nullable String token) {
282+
@Nullable String token) {
279283
Timber.d("Requesting 2FA prompt");
280284
hideProgress();
281285
askUserForTwoFactorAuth();

app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java

+9-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import android.content.res.Configuration;
1010
import android.graphics.Bitmap;
1111
import android.os.Bundle;
12-
import android.text.TextUtils;
1312
import android.util.Log;
1413
import android.view.Gravity;
1514
import android.view.LayoutInflater;
@@ -93,6 +92,7 @@
9392
import fr.free.nrw.commons.utils.NearbyFABUtils;
9493
import fr.free.nrw.commons.utils.NetworkUtils;
9594
import fr.free.nrw.commons.utils.PermissionUtils;
95+
import fr.free.nrw.commons.utils.SystemThemeUtils;
9696
import fr.free.nrw.commons.utils.UiUtils;
9797
import fr.free.nrw.commons.utils.ViewUtil;
9898
import fr.free.nrw.commons.wikidata.WikidataEditListener;
@@ -156,6 +156,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
156156
@Inject ContributionController controller;
157157
@Inject WikidataEditListener wikidataEditListener;
158158

159+
@Inject
160+
SystemThemeUtils systemThemeUtils;
161+
159162
private NearbyFilterSearchRecyclerViewAdapter nearbyFilterSearchRecyclerViewAdapter;
160163

161164
private BottomSheetBehavior bottomSheetListBehavior;
@@ -210,7 +213,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
210213
@Override
211214
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
212215
super.onViewCreated(view, savedInstanceState);
213-
isDarkTheme = applicationKvStore.getBoolean("theme", false);
216+
isDarkTheme = systemThemeUtils.isDeviceInNightMode();
214217
cameraMoveListener= () -> presenter.onCameraMove(mapBox.getCameraPosition().target);
215218
addCheckBoxCallback();
216219
presenter.attachView(this);
@@ -821,10 +824,10 @@ public void animateFABs() {
821824
}
822825

823826
private void showFABs() {
824-
NearbyFABUtils.addAnchorToBigFABs(fabPlus, bottomSheetDetails.getId());
825-
fabPlus.show();
826-
NearbyFABUtils.addAnchorToSmallFABs(fabGallery, getView().findViewById(R.id.empty_view).getId());
827-
NearbyFABUtils.addAnchorToSmallFABs(fabCamera, getView().findViewById(R.id.empty_view1).getId());
827+
NearbyFABUtils.addAnchorToBigFABs(fabPlus, bottomSheetDetails.getId());
828+
fabPlus.show();
829+
NearbyFABUtils.addAnchorToSmallFABs(fabGallery, getView().findViewById(R.id.empty_view).getId());
830+
NearbyFABUtils.addAnchorToSmallFABs(fabCamera, getView().findViewById(R.id.empty_view1).getId());
828831
}
829832

830833
/**

app/src/main/java/fr/free/nrw/commons/settings/Prefs.java

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class Prefs {
99
public static final String IS_CONTRIBUTION_COUNT_CHANGED = "ccontributionCountChanged";
1010
public static final String MANAGED_EXIF_TAGS = "managed_exif_tags";
1111
public static final String KEY_LANGUAGE_VALUE = "languageDescription";
12+
public static final String KEY_THEME_VALUE = "appThemePref";
1213

1314
public static class Licenses {
1415
public static final String CC_BY_SA_3 = "CC BY-SA 3.0";

app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java

+44-23
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,15 @@
1212
import android.text.Editable;
1313
import android.text.TextWatcher;
1414

15-
import com.google.gson.reflect.TypeToken;
1615
import com.karumi.dexter.Dexter;
1716
import com.karumi.dexter.listener.PermissionGrantedResponse;
1817
import com.karumi.dexter.listener.single.BasePermissionListener;
1918

20-
import java.lang.reflect.Type;
21-
import java.util.Collections;
2219
import java.util.ArrayList;
20+
import java.util.Collections;
2321
import java.util.HashSet;
2422
import java.util.List;
2523
import java.util.Locale;
26-
import java.util.Set;
2724

2825
import javax.inject.Inject;
2926
import javax.inject.Named;
@@ -37,14 +34,19 @@
3734
import fr.free.nrw.commons.utils.PermissionUtils;
3835
import fr.free.nrw.commons.utils.ViewUtil;
3936

37+
import static fr.free.nrw.commons.utils.SystemThemeUtils.THEME_MODE_DEFAULT;
38+
4039
public class SettingsFragment extends PreferenceFragment {
4140

4241
@Inject
4342
@Named("default_preferences")
4443
JsonKvStore defaultKvStore;
44+
4545
@Inject
4646
CommonsLogSender commonsLogSender;
47-
private ListPreference listPreference;
47+
48+
private ListPreference themeListPreference;
49+
private ListPreference langListPreference;
4850

4951
@Override
5052
public void onCreate(Bundle savedInstanceState) {
@@ -57,11 +59,8 @@ public void onCreate(Bundle savedInstanceState) {
5759
// Load the preferences from an XML resource
5860
addPreferencesFromResource(R.xml.preferences);
5961

60-
SwitchPreference themePreference = (SwitchPreference) findPreference("theme");
61-
themePreference.setOnPreferenceChangeListener((preference, newValue) -> {
62-
getActivity().recreate();
63-
return true;
64-
});
62+
themeListPreference = (ListPreference) findPreference(Prefs.KEY_THEME_VALUE);
63+
prepareTheme();
6564

6665
//Check if the Author Name switch is enabled and appropriately handle the author name usage
6766
SwitchPreference useAuthorName = (SwitchPreference) findPreference("useAuthorName");
@@ -117,7 +116,7 @@ public void afterTextChanged(Editable s) {
117116
}
118117
});
119118

120-
listPreference = (ListPreference) findPreference("descriptionDefaultLanguagePref");
119+
langListPreference = (ListPreference) findPreference("descriptionDefaultLanguagePref");
121120
prepareLanguages();
122121
Preference betaTesterPreference = findPreference("becomeBetaTester");
123122
betaTesterPreference.setOnPreferenceClickListener(preference -> {
@@ -144,10 +143,32 @@ public void afterTextChanged(Editable s) {
144143
}
145144
}
146145

146+
/**
147+
* Uses previously saved theme if there is any, if not then uses default.
148+
*/
149+
private void prepareTheme() {
150+
151+
themeListPreference.setSummary(getThemeSummary(getCurrentTheme()));
152+
153+
themeListPreference.setOnPreferenceChangeListener((preference, newValue) -> {
154+
getActivity().recreate();
155+
return true;
156+
});
157+
}
158+
159+
private CharSequence getThemeSummary(String value) {
160+
int prefIndex = themeListPreference.findIndexOfValue(value);
161+
return themeListPreference.getEntries()[prefIndex];
162+
}
163+
164+
private String getCurrentTheme() {
165+
return defaultKvStore.getString(Prefs.KEY_THEME_VALUE, THEME_MODE_DEFAULT);
166+
}
167+
147168
/**
148169
* Prepares language summary and language codes list and adds them to list preference as pairs.
149170
* Uses previously saved language if there is any, if not uses phone local as initial language.
150-
* Adds preference changed listener and saves value choosen by user to shared preferences
171+
* Adds preference changed listener and saves value chosen by user to shared preferences
151172
* to remember later
152173
*/
153174
private void prepareLanguages() {
@@ -167,26 +188,26 @@ private void prepareLanguages() {
167188
CharSequence[] languageNames = languageNamesList.toArray(new CharSequence[0]);
168189
CharSequence[] languageCodes = languageCodesList.toArray(new CharSequence[0]);
169190
// Add all languages and languages codes to lists preference as pair
170-
listPreference.setEntries(languageNames);
171-
listPreference.setEntryValues(languageCodes);
191+
langListPreference.setEntries(languageNames);
192+
langListPreference.setEntryValues(languageCodes);
172193

173194
// Gets current language code from shared preferences
174195
String languageCode = getCurrentLanguageCode();
175-
if(languageCode.equals("")){
196+
if (languageCode.equals("")){
176197
// If current language code is empty, means none selected by user yet so use phone local
177-
listPreference.setSummary(Locale.getDefault().getDisplayLanguage());
178-
listPreference.setValue(Locale.getDefault().getLanguage());
198+
langListPreference.setSummary(Locale.getDefault().getDisplayLanguage());
199+
langListPreference.setValue(Locale.getDefault().getLanguage());
179200
} else {
180201
// If any language is selected by user previously, use it
181-
int prefIndex = listPreference.findIndexOfValue(languageCode);
182-
listPreference.setSummary(listPreference.getEntries()[prefIndex]);
183-
listPreference.setValue(languageCode);
202+
int prefIndex = langListPreference.findIndexOfValue(languageCode);
203+
langListPreference.setSummary(langListPreference.getEntries()[prefIndex]);
204+
langListPreference.setValue(languageCode);
184205
}
185206

186-
listPreference.setOnPreferenceChangeListener((preference, newValue) -> {
207+
langListPreference.setOnPreferenceChangeListener((preference, newValue) -> {
187208
String userSelectedValue = (String) newValue;
188-
int prefIndex = listPreference.findIndexOfValue(userSelectedValue);
189-
listPreference.setSummary(listPreference.getEntries()[prefIndex]);
209+
int prefIndex = langListPreference.findIndexOfValue(userSelectedValue);
210+
langListPreference.setSummary(langListPreference.getEntries()[prefIndex]);
190211
saveLanguageValue(userSelectedValue);
191212
return true;
192213
});

app/src/main/java/fr/free/nrw/commons/theme/BaseActivity.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,31 @@
88
import fr.free.nrw.commons.R;
99
import fr.free.nrw.commons.di.CommonsDaggerAppCompatActivity;
1010
import fr.free.nrw.commons.kvstore.JsonKvStore;
11+
import fr.free.nrw.commons.utils.SystemThemeUtils;
1112
import io.reactivex.disposables.CompositeDisposable;
1213

1314
public abstract class BaseActivity extends CommonsDaggerAppCompatActivity {
1415
@Inject
1516
@Named("default_preferences")
1617
public JsonKvStore defaultKvStore;
1718

19+
@Inject
20+
SystemThemeUtils systemThemeUtils;
21+
1822
protected CompositeDisposable compositeDisposable = new CompositeDisposable();
1923
protected boolean wasPreviouslyDarkTheme;
2024

2125
@Override
2226
protected void onCreate(Bundle savedInstanceState) {
2327
super.onCreate(savedInstanceState);
24-
wasPreviouslyDarkTheme = defaultKvStore.getBoolean("theme", false);
28+
wasPreviouslyDarkTheme = systemThemeUtils.isDeviceInNightMode();
2529
setTheme(wasPreviouslyDarkTheme ? R.style.DarkAppTheme : R.style.LightAppTheme);
2630
}
2731

2832
@Override
2933
protected void onResume() {
3034
// Restart activity if theme is changed
31-
if (wasPreviouslyDarkTheme != defaultKvStore.getBoolean("theme", false)) {
35+
if (wasPreviouslyDarkTheme != systemThemeUtils.isDeviceInNightMode()) {
3236
recreate();
3337
}
3438

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package fr.free.nrw.commons.utils;
2+
3+
import android.content.Context;
4+
import android.content.res.Configuration;
5+
6+
import javax.inject.Inject;
7+
import javax.inject.Named;
8+
9+
import fr.free.nrw.commons.kvstore.JsonKvStore;
10+
import fr.free.nrw.commons.settings.Prefs;
11+
12+
public class SystemThemeUtils {
13+
14+
private Context context;
15+
private JsonKvStore applicationKvStore;
16+
17+
public static final String THEME_MODE_DEFAULT = "0";
18+
public static final String THEME_MODE_DARK = "1";
19+
public static final String THEME_MODE_LIGHT = "2";
20+
21+
@Inject
22+
public SystemThemeUtils(Context context, @Named("default_preferences") JsonKvStore applicationKvStore) {
23+
this.context = context;
24+
this.applicationKvStore = applicationKvStore;
25+
}
26+
27+
// Return true is system wide dark theme is enabled else false
28+
public boolean getSystemDefaultThemeBool(String theme) {
29+
if (theme.equals(THEME_MODE_DARK)) {
30+
return true;
31+
} else if (theme.equals(THEME_MODE_DEFAULT)) {
32+
return getSystemDefaultThemeBool(getSystemDefaultTheme());
33+
}
34+
return false;
35+
}
36+
37+
// Returns the default system wide theme
38+
public String getSystemDefaultTheme() {
39+
return (context.getResources().getConfiguration().uiMode &
40+
Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES ? THEME_MODE_DARK : THEME_MODE_LIGHT;
41+
}
42+
43+
// Returns true if the device is in night mode or false otherwise
44+
public boolean isDeviceInNightMode() {
45+
return getSystemDefaultThemeBool(
46+
applicationKvStore.getString(Prefs.KEY_THEME_VALUE, getSystemDefaultTheme()));
47+
}
48+
49+
}

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

+11
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,15 @@
3535
<item>@string/exif_tag_software</item>
3636
</array>
3737

38+
<array name="pref_theme_entries">
39+
<item>@string/theme_default_name</item>
40+
<item>@string/theme_dark_name</item>
41+
<item>@string/theme_light_name</item>
42+
</array>
43+
44+
<string-array name="pref_theme_entries_values">
45+
<item>0</item>
46+
<item>1</item>
47+
<item>2</item>
48+
</string-array>
3849
</resources>

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,7 @@
113113
<string name="use_previous">Use previous title and description</string>
114114
<string name="allow_gps">Automatically get current location</string>
115115
<string name="allow_gps_summary">Retrieves current location if image is not geotagged, and geotags image with it. Warning: This will reveal your current location.</string>
116-
<string name="preference_theme">Night mode</string>
117-
<string name="preference_theme_summary">Use dark theme</string>
116+
<string name="preference_theme">Theme</string>
118117
<string name="license_name_cc_by_sa_four"> Attribution-ShareAlike 4.0</string>
119118
<string name="license_name_cc_by_four"> Attribution 4.0</string>
120119
<string name="license_name_cc_by_sa"> Attribution-ShareAlike 3.0</string>
@@ -599,4 +598,7 @@ Upload your first media by tapping on the add button.</string>
599598
<string name="wallpaper_set_unsuccessfully">Something went wrong. Could not set the wallpaper</string>
600599
<string name="setting_wallpaper_dialog_title">Set as Wallpaper</string>
601600
<string name="setting_wallpaper_dialog_message">Setting Wallpaper. Please wait…</string>
601+
<string name="theme_default_name">Default</string>
602+
<string name="theme_dark_name">Dark</string>
603+
<string name="theme_light_name">Light</string>
602604
</resources>

app/src/main/res/xml/preferences.xml

+7-5
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
<fr.free.nrw.commons.ui.LongTitlePreferences.LongTitlePreferenceCategory
66
android:title="@string/preference_category_appearance">
77

8-
<fr.free.nrw.commons.ui.LongTitlePreferences.LongTitleSwitchPreference
9-
android:title="@string/preference_theme"
10-
android:defaultValue="false"
11-
android:summary="@string/preference_theme_summary"
12-
android:key="theme" />
8+
<fr.free.nrw.commons.ui.LongTitlePreferences.LongTitleListPreference
9+
android:key="appThemePref"
10+
android:title= "@string/preference_theme"
11+
android:entries="@array/pref_theme_entries"
12+
android:entryValues="@array/pref_theme_entries_values"
13+
android:defaultValue="0"
14+
android:summary="@string/theme_default_name" />
1315

1416
</fr.free.nrw.commons.ui.LongTitlePreferences.LongTitlePreferenceCategory>
1517

0 commit comments

Comments
 (0)