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
This commit is contained in:
Seán Mac Gillicuddy 2020-03-05 14:33:57 +00:00 committed by GitHub
parent 65ec071493
commit 1584ffe0e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 139 additions and 42 deletions

View file

@ -55,6 +55,7 @@ import fr.free.nrw.commons.explore.categories.ExploreActivity;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.theme.NavigationBaseActivity;
import fr.free.nrw.commons.utils.ConfigUtils;
import fr.free.nrw.commons.utils.SystemThemeUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.disposables.CompositeDisposable;
import retrofit2.Call;
@ -83,6 +84,9 @@ public class LoginActivity extends AccountAuthenticatorActivity {
@Inject
LoginClient loginClient;
@Inject
SystemThemeUtils systemThemeUtils;
@BindView(R.id.login_button)
Button loginButton;
@ -121,7 +125,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
.getCommonsApplicationComponent()
.inject(this);
boolean isDarkTheme = applicationKvStore.getBoolean("theme", false);
boolean isDarkTheme = systemThemeUtils.isDeviceInNightMode();
setTheme(isDarkTheme ? R.style.DarkAppTheme : R.style.LightAppTheme);
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);

View file

@ -9,7 +9,6 @@ import android.content.IntentFilter;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@ -93,6 +92,7 @@ import fr.free.nrw.commons.utils.LocationUtils;
import fr.free.nrw.commons.utils.NearbyFABUtils;
import fr.free.nrw.commons.utils.NetworkUtils;
import fr.free.nrw.commons.utils.PermissionUtils;
import fr.free.nrw.commons.utils.SystemThemeUtils;
import fr.free.nrw.commons.utils.UiUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import fr.free.nrw.commons.wikidata.WikidataEditListener;
@ -156,6 +156,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Inject ContributionController controller;
@Inject WikidataEditListener wikidataEditListener;
@Inject
SystemThemeUtils systemThemeUtils;
private NearbyFilterSearchRecyclerViewAdapter nearbyFilterSearchRecyclerViewAdapter;
private BottomSheetBehavior bottomSheetListBehavior;
@ -210,7 +213,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
isDarkTheme = applicationKvStore.getBoolean("theme", false);
isDarkTheme = systemThemeUtils.isDeviceInNightMode();
cameraMoveListener= () -> presenter.onCameraMove(mapBox.getCameraPosition().target);
addCheckBoxCallback();
presenter.attachView(this);

View file

@ -9,6 +9,7 @@ public class Prefs {
public static final String IS_CONTRIBUTION_COUNT_CHANGED = "ccontributionCountChanged";
public static final String MANAGED_EXIF_TAGS = "managed_exif_tags";
public static final String KEY_LANGUAGE_VALUE = "languageDescription";
public static final String KEY_THEME_VALUE = "appThemePref";
public static class Licenses {
public static final String CC_BY_SA_3 = "CC BY-SA 3.0";

View file

@ -12,18 +12,15 @@ import android.preference.SwitchPreference;
import android.text.Editable;
import android.text.TextWatcher;
import com.google.gson.reflect.TypeToken;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.listener.PermissionGrantedResponse;
import com.karumi.dexter.listener.single.BasePermissionListener;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
@ -37,14 +34,19 @@ import fr.free.nrw.commons.upload.Language;
import fr.free.nrw.commons.utils.PermissionUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import static fr.free.nrw.commons.utils.SystemThemeUtils.THEME_MODE_DEFAULT;
public class SettingsFragment extends PreferenceFragment {
@Inject
@Named("default_preferences")
JsonKvStore defaultKvStore;
@Inject
CommonsLogSender commonsLogSender;
private ListPreference listPreference;
private ListPreference themeListPreference;
private ListPreference langListPreference;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -57,11 +59,8 @@ public class SettingsFragment extends PreferenceFragment {
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
SwitchPreference themePreference = (SwitchPreference) findPreference("theme");
themePreference.setOnPreferenceChangeListener((preference, newValue) -> {
getActivity().recreate();
return true;
});
themeListPreference = (ListPreference) findPreference(Prefs.KEY_THEME_VALUE);
prepareTheme();
//Check if the Author Name switch is enabled and appropriately handle the author name usage
SwitchPreference useAuthorName = (SwitchPreference) findPreference("useAuthorName");
@ -117,7 +116,7 @@ public class SettingsFragment extends PreferenceFragment {
}
});
listPreference = (ListPreference) findPreference("descriptionDefaultLanguagePref");
langListPreference = (ListPreference) findPreference("descriptionDefaultLanguagePref");
prepareLanguages();
Preference betaTesterPreference = findPreference("becomeBetaTester");
betaTesterPreference.setOnPreferenceClickListener(preference -> {
@ -144,10 +143,32 @@ public class SettingsFragment extends PreferenceFragment {
}
}
/**
* Uses previously saved theme if there is any, if not then uses default.
*/
private void prepareTheme() {
themeListPreference.setSummary(getThemeSummary(getCurrentTheme()));
themeListPreference.setOnPreferenceChangeListener((preference, newValue) -> {
getActivity().recreate();
return true;
});
}
private CharSequence getThemeSummary(String value) {
int prefIndex = themeListPreference.findIndexOfValue(value);
return themeListPreference.getEntries()[prefIndex];
}
private String getCurrentTheme() {
return defaultKvStore.getString(Prefs.KEY_THEME_VALUE, THEME_MODE_DEFAULT);
}
/**
* Prepares language summary and language codes list and adds them to list preference as pairs.
* Uses previously saved language if there is any, if not uses phone local as initial language.
* Adds preference changed listener and saves value choosen by user to shared preferences
* Adds preference changed listener and saves value chosen by user to shared preferences
* to remember later
*/
private void prepareLanguages() {
@ -167,26 +188,26 @@ public class SettingsFragment extends PreferenceFragment {
CharSequence[] languageNames = languageNamesList.toArray(new CharSequence[0]);
CharSequence[] languageCodes = languageCodesList.toArray(new CharSequence[0]);
// Add all languages and languages codes to lists preference as pair
listPreference.setEntries(languageNames);
listPreference.setEntryValues(languageCodes);
langListPreference.setEntries(languageNames);
langListPreference.setEntryValues(languageCodes);
// Gets current language code from shared preferences
String languageCode = getCurrentLanguageCode();
if (languageCode.equals("")){
// If current language code is empty, means none selected by user yet so use phone local
listPreference.setSummary(Locale.getDefault().getDisplayLanguage());
listPreference.setValue(Locale.getDefault().getLanguage());
langListPreference.setSummary(Locale.getDefault().getDisplayLanguage());
langListPreference.setValue(Locale.getDefault().getLanguage());
} else {
// If any language is selected by user previously, use it
int prefIndex = listPreference.findIndexOfValue(languageCode);
listPreference.setSummary(listPreference.getEntries()[prefIndex]);
listPreference.setValue(languageCode);
int prefIndex = langListPreference.findIndexOfValue(languageCode);
langListPreference.setSummary(langListPreference.getEntries()[prefIndex]);
langListPreference.setValue(languageCode);
}
listPreference.setOnPreferenceChangeListener((preference, newValue) -> {
langListPreference.setOnPreferenceChangeListener((preference, newValue) -> {
String userSelectedValue = (String) newValue;
int prefIndex = listPreference.findIndexOfValue(userSelectedValue);
listPreference.setSummary(listPreference.getEntries()[prefIndex]);
int prefIndex = langListPreference.findIndexOfValue(userSelectedValue);
langListPreference.setSummary(langListPreference.getEntries()[prefIndex]);
saveLanguageValue(userSelectedValue);
return true;
});

View file

@ -8,6 +8,7 @@ import javax.inject.Named;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.di.CommonsDaggerAppCompatActivity;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.utils.SystemThemeUtils;
import io.reactivex.disposables.CompositeDisposable;
public abstract class BaseActivity extends CommonsDaggerAppCompatActivity {
@ -15,20 +16,23 @@ public abstract class BaseActivity extends CommonsDaggerAppCompatActivity {
@Named("default_preferences")
public JsonKvStore defaultKvStore;
@Inject
SystemThemeUtils systemThemeUtils;
protected CompositeDisposable compositeDisposable = new CompositeDisposable();
protected boolean wasPreviouslyDarkTheme;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
wasPreviouslyDarkTheme = defaultKvStore.getBoolean("theme", false);
wasPreviouslyDarkTheme = systemThemeUtils.isDeviceInNightMode();
setTheme(wasPreviouslyDarkTheme ? R.style.DarkAppTheme : R.style.LightAppTheme);
}
@Override
protected void onResume() {
// Restart activity if theme is changed
if (wasPreviouslyDarkTheme != defaultKvStore.getBoolean("theme", false)) {
if (wasPreviouslyDarkTheme != systemThemeUtils.isDeviceInNightMode()) {
recreate();
}

View file

@ -0,0 +1,49 @@
package fr.free.nrw.commons.utils;
import android.content.Context;
import android.content.res.Configuration;
import javax.inject.Inject;
import javax.inject.Named;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.settings.Prefs;
public class SystemThemeUtils {
private Context context;
private JsonKvStore applicationKvStore;
public static final String THEME_MODE_DEFAULT = "0";
public static final String THEME_MODE_DARK = "1";
public static final String THEME_MODE_LIGHT = "2";
@Inject
public SystemThemeUtils(Context context, @Named("default_preferences") JsonKvStore applicationKvStore) {
this.context = context;
this.applicationKvStore = applicationKvStore;
}
// Return true is system wide dark theme is enabled else false
public boolean getSystemDefaultThemeBool(String theme) {
if (theme.equals(THEME_MODE_DARK)) {
return true;
} else if (theme.equals(THEME_MODE_DEFAULT)) {
return getSystemDefaultThemeBool(getSystemDefaultTheme());
}
return false;
}
// Returns the default system wide theme
public String getSystemDefaultTheme() {
return (context.getResources().getConfiguration().uiMode &
Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES ? THEME_MODE_DARK : THEME_MODE_LIGHT;
}
// Returns true if the device is in night mode or false otherwise
public boolean isDeviceInNightMode() {
return getSystemDefaultThemeBool(
applicationKvStore.getString(Prefs.KEY_THEME_VALUE, getSystemDefaultTheme()));
}
}

View file

@ -35,4 +35,15 @@
<item>@string/exif_tag_software</item>
</array>
<array name="pref_theme_entries">
<item>@string/theme_default_name</item>
<item>@string/theme_dark_name</item>
<item>@string/theme_light_name</item>
</array>
<string-array name="pref_theme_entries_values">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
</resources>

View file

@ -113,8 +113,7 @@
<string name="use_previous">Use previous title and description</string>
<string name="allow_gps">Automatically get current location</string>
<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>
<string name="preference_theme">Night mode</string>
<string name="preference_theme_summary">Use dark theme</string>
<string name="preference_theme">Theme</string>
<string name="license_name_cc_by_sa_four"> Attribution-ShareAlike 4.0</string>
<string name="license_name_cc_by_four"> Attribution 4.0</string>
<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>
<string name="wallpaper_set_unsuccessfully">Something went wrong. Could not set the wallpaper</string>
<string name="setting_wallpaper_dialog_title">Set as Wallpaper</string>
<string name="setting_wallpaper_dialog_message">Setting Wallpaper. Please wait…</string>
<string name="theme_default_name">Default</string>
<string name="theme_dark_name">Dark</string>
<string name="theme_light_name">Light</string>
</resources>

View file

@ -5,11 +5,13 @@
<fr.free.nrw.commons.ui.LongTitlePreferences.LongTitlePreferenceCategory
android:title="@string/preference_category_appearance">
<fr.free.nrw.commons.ui.LongTitlePreferences.LongTitleSwitchPreference
<fr.free.nrw.commons.ui.LongTitlePreferences.LongTitleListPreference
android:key="appThemePref"
android:title= "@string/preference_theme"
android:defaultValue="false"
android:summary="@string/preference_theme_summary"
android:key="theme" />
android:entries="@array/pref_theme_entries"
android:entryValues="@array/pref_theme_entries_values"
android:defaultValue="0"
android:summary="@string/theme_default_name" />
</fr.free.nrw.commons.ui.LongTitlePreferences.LongTitlePreferenceCategory>