add settings for dynamic colors and dark theme
This commit is contained in:
parent
b80fe9802a
commit
7567dcff5e
|
@ -1,8 +1,12 @@
|
|||
package im.conversations.android;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
import com.google.android.material.color.DynamicColorsOptions;
|
||||
import im.conversations.android.dns.Resolver;
|
||||
import im.conversations.android.notification.Channels;
|
||||
import im.conversations.android.xmpp.ConnectionPool;
|
||||
|
@ -30,8 +34,53 @@ public class Conversations extends Application {
|
|||
channels.initialize();
|
||||
Resolver.init(this);
|
||||
ConnectionPool.getInstance(this).reconfigure();
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); // For night mode theme
|
||||
DynamicColors.applyToActivitiesIfAvailable(this);
|
||||
applyThemeSettings();
|
||||
}
|
||||
|
||||
public void applyThemeSettings() {
|
||||
final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
if (sharedPreferences == null) {
|
||||
return;
|
||||
}
|
||||
applyThemeSettings(sharedPreferences);
|
||||
}
|
||||
|
||||
private void applyThemeSettings(final SharedPreferences sharedPreferences) {
|
||||
AppCompatDelegate.setDefaultNightMode(getDesiredNightMode(this, sharedPreferences));
|
||||
var dynamicColorsOptions =
|
||||
new DynamicColorsOptions.Builder()
|
||||
.setPrecondition((activity, t) -> isDynamicColorsDesired(activity))
|
||||
.build();
|
||||
DynamicColors.applyToActivitiesIfAvailable(this, dynamicColorsOptions);
|
||||
}
|
||||
|
||||
public static int getDesiredNightMode(final Context context) {
|
||||
final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (sharedPreferences == null) {
|
||||
return AppCompatDelegate.getDefaultNightMode();
|
||||
}
|
||||
return getDesiredNightMode(context, sharedPreferences);
|
||||
}
|
||||
|
||||
public static boolean isDynamicColorsDesired(final Context context) {
|
||||
final var preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return preferences.getBoolean("dynamic_colors", false);
|
||||
}
|
||||
|
||||
private static int getDesiredNightMode(
|
||||
final Context context, final SharedPreferences sharedPreferences) {
|
||||
final String theme =
|
||||
sharedPreferences.getString("theme", context.getString(R.string.theme));
|
||||
return getDesiredNightMode(theme);
|
||||
}
|
||||
|
||||
public static int getDesiredNightMode(final String theme) {
|
||||
if ("automatic".equals(theme)) {
|
||||
return AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
|
||||
} else if ("light".equals(theme)) {
|
||||
return AppCompatDelegate.MODE_NIGHT_NO;
|
||||
} else {
|
||||
return AppCompatDelegate.MODE_NIGHT_YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,20 @@ public final class Activities {
|
|||
|
||||
public static void setStatusAndNavigationBarColors(
|
||||
final AppCompatActivity activity, final View view) {
|
||||
setStatusAndNavigationBarColors(activity, view, false);
|
||||
}
|
||||
|
||||
public static void setStatusAndNavigationBarColors(
|
||||
final AppCompatActivity activity, final View view, final boolean raisedStatusBar) {
|
||||
final var isLightMode = isLightMode(activity);
|
||||
final var window = activity.getWindow();
|
||||
final var flags = view.getSystemUiVisibility();
|
||||
// an elevation of 4 matches the MaterialToolbar elevation
|
||||
if (raisedStatusBar) {
|
||||
window.setStatusBarColor(SurfaceColors.SURFACE_5.getColor(activity));
|
||||
} else {
|
||||
window.setStatusBarColor(SurfaceColors.SURFACE_0.getColor(activity));
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
window.setNavigationBarColor(SurfaceColors.SURFACE_1.getColor(activity));
|
||||
if (isLightMode) {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package im.conversations.android.ui.activity;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import im.conversations.android.Conversations;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class BaseActivity extends AppCompatActivity {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(BaseActivity.class);
|
||||
|
||||
private Boolean isDynamicColors;
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
final int desiredNightMode = Conversations.getDesiredNightMode(this);
|
||||
if (setDesiredNightMode(desiredNightMode)) {
|
||||
return;
|
||||
}
|
||||
final boolean isDynamicColors = Conversations.isDynamicColorsDesired(this);
|
||||
setDynamicColors(isDynamicColors);
|
||||
}
|
||||
|
||||
public void setDynamicColors(final boolean isDynamicColors) {
|
||||
if (this.isDynamicColors == null) {
|
||||
this.isDynamicColors = isDynamicColors;
|
||||
} else {
|
||||
if (this.isDynamicColors != isDynamicColors) {
|
||||
LOGGER.info(
|
||||
"Recreating {} because dynamic color setting has changed",
|
||||
getClass().getSimpleName());
|
||||
recreate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setDesiredNightMode(final int desiredNightMode) {
|
||||
if (desiredNightMode == AppCompatDelegate.getDefaultNightMode()) {
|
||||
return false;
|
||||
}
|
||||
AppCompatDelegate.setDefaultNightMode(desiredNightMode);
|
||||
LOGGER.info(
|
||||
"Recreating {} because desired night mode has changed", getClass().getSimpleName());
|
||||
recreate();
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,13 @@
|
|||
package im.conversations.android.ui.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import im.conversations.android.R;
|
||||
import im.conversations.android.databinding.ActivityMainBinding;
|
||||
import im.conversations.android.service.ForegroundService;
|
||||
import im.conversations.android.ui.Activities;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
public class MainActivity extends BaseActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package im.conversations.android.ui.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import im.conversations.android.R;
|
||||
import im.conversations.android.databinding.ActivitySettingsBinding;
|
||||
|
@ -9,19 +8,32 @@ import im.conversations.android.service.ForegroundService;
|
|||
import im.conversations.android.ui.Activities;
|
||||
import im.conversations.android.ui.fragment.settings.MainSettingsFragment;
|
||||
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
private ActivitySettingsBinding binding;
|
||||
public class SettingsActivity extends BaseActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ForegroundService.start(this);
|
||||
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_settings);
|
||||
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
|
||||
getSupportFragmentManager()
|
||||
final ActivitySettingsBinding binding =
|
||||
DataBindingUtil.setContentView(this, R.layout.activity_settings);
|
||||
setSupportActionBar(binding.materialToolbar);
|
||||
Activities.setStatusAndNavigationBarColors(this, binding.getRoot(), true);
|
||||
|
||||
final var fragmentManager = getSupportFragmentManager();
|
||||
final var currentFragment = fragmentManager.findFragmentById(R.id.fragment_container);
|
||||
if (currentFragment == null) {
|
||||
fragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.fragment_container, new MainSettingsFragment())
|
||||
.commit();
|
||||
}
|
||||
binding.materialToolbar.setNavigationOnClickListener(
|
||||
view -> {
|
||||
if (fragmentManager.getBackStackEntryCount() == 0) {
|
||||
finish();
|
||||
} else {
|
||||
fragmentManager.popBackStack();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package im.conversations.android.ui.activity;
|
|||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavController;
|
||||
|
@ -16,7 +15,7 @@ import im.conversations.android.ui.model.SetupViewModel;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SetupActivity extends AppCompatActivity {
|
||||
public class SetupActivity extends BaseActivity {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SetupActivity.class);
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package im.conversations.android.ui.fragment.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import im.conversations.android.Conversations;
|
||||
import im.conversations.android.R;
|
||||
import im.conversations.android.ui.activity.SettingsActivity;
|
||||
|
||||
public class InterfaceSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
|
||||
setPreferencesFromResource(R.xml.preferences_interface, rootKey);
|
||||
final var themePreference = findPreference("theme");
|
||||
final var dynamicColors = findPreference("dynamic_colors");
|
||||
if (themePreference != null) {
|
||||
themePreference.setOnPreferenceChangeListener(
|
||||
(preference, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
final String theme = (String) newValue;
|
||||
final int desiredNightMode = Conversations.getDesiredNightMode(theme);
|
||||
requireSettingsActivity().setDesiredNightMode(desiredNightMode);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (dynamicColors != null) {
|
||||
dynamicColors.setOnPreferenceChangeListener(
|
||||
new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(
|
||||
@NonNull Preference preference, Object newValue) {
|
||||
requireSettingsActivity()
|
||||
.setDynamicColors(Boolean.TRUE.equals(newValue));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public SettingsActivity requireSettingsActivity() {
|
||||
final var activity = requireActivity();
|
||||
if (activity instanceof SettingsActivity) {
|
||||
return (SettingsActivity) activity;
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"%s is not %s",
|
||||
activity.getClass().getName(), SettingsActivity.class.getName()));
|
||||
}
|
||||
}
|
11
app/src/main/res/drawable/ic_arrow_back_24dp.xml
Normal file
11
app/src/main/res/drawable/ic_arrow_back_24dp.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_dark_mode_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_dark_mode_24dp.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9s9,-4.03 9,-9c0,-0.46 -0.04,-0.92 -0.1,-1.36c-0.98,1.37 -2.58,2.26 -4.4,2.26c-2.98,0 -5.4,-2.42 -5.4,-5.4c0,-1.81 0.89,-3.42 2.26,-4.4C12.92,3.04 12.46,3 12,3L12,3z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_touch_app_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_touch_app_24dp.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M9,11.24V7.5C9,6.12 10.12,5 11.5,5S14,6.12 14,7.5v3.74c1.21,-0.81 2,-2.18 2,-3.74C16,5.01 13.99,3 11.5,3S7,5.01 7,7.5C7,9.06 7.79,10.43 9,11.24zM18.84,15.87l-4.54,-2.26c-0.17,-0.07 -0.35,-0.11 -0.54,-0.11H13v-6C13,6.67 12.33,6 11.5,6S10,6.67 10,7.5v10.74c-3.6,-0.76 -3.54,-0.75 -3.67,-0.75c-0.31,0 -0.59,0.13 -0.79,0.33l-0.79,0.8l4.94,4.94C9.96,23.83 10.34,24 10.75,24h6.79c0.75,0 1.33,-0.55 1.44,-1.28l0.75,-5.27c0.01,-0.07 0.02,-0.14 0.02,-0.2C19.75,16.63 19.37,16.09 18.84,15.87z" />
|
||||
</vector>
|
|
@ -25,13 +25,14 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:liftOnScroll="false"
|
||||
app:elevation="0dp">
|
||||
app:elevation="4dp">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/material_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:title="@string/title_activity_settings"/>
|
||||
app:title="@string/title_activity_settings"
|
||||
app:navigationIcon="@drawable/ic_arrow_back_24dp"/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:fillViewport="true"
|
||||
|
|
16
app/src/main/res/values-v29/theme-settings.xml
Normal file
16
app/src/main/res/values-v29/theme-settings.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="theme">automatic</string>
|
||||
<string-array name="themes" tools:ignore="InconsistentArrays">
|
||||
<item>@string/pref_theme_automatic</item>
|
||||
<item>@string/pref_theme_light</item>
|
||||
<item>@string/pref_theme_dark</item>
|
||||
</string-array>
|
||||
<string-array name="themes_values" tools:ignore="InconsistentArrays">
|
||||
<item>automatic</item>
|
||||
<item>light</item>
|
||||
<item>dark</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
|
@ -1017,10 +1017,14 @@
|
|||
<string name="accounts">Accounts</string>
|
||||
<string name="spaces">Spaces</string>
|
||||
<string name="chats">Chats</string>
|
||||
<string name="pref_title_appearance">Appearance</string>
|
||||
<string name="pref_title_interface">Interface</string>
|
||||
<string name="pref_summary_appearance"><![CDATA[Theme & Colors]]></string>
|
||||
<string name="pref_title_security">Security</string>
|
||||
<string name="pref_summary_security">Encryption, Blind Trust Before Verification, MIM Detection</string>
|
||||
<string name="unified_push_summary">Notification relay for UnifiedPush compatible third party apps</string>
|
||||
<string name="pref_dynamic_colors">Dynamic colors</string>
|
||||
<string name="pref_dynamic_colors_summary">System colors (Material You)</string>
|
||||
<string name="pref_light_dark_mode">Light/dark mode</string>
|
||||
<string name="appearance">Appearance</string>
|
||||
|
||||
</resources>
|
||||
|
|
14
app/src/main/res/values/theme-settings.xml
Normal file
14
app/src/main/res/values/theme-settings.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="theme">light</string>
|
||||
<string-array name="themes">
|
||||
<item>@string/pref_theme_light</item>
|
||||
<item>@string/pref_theme_dark</item>
|
||||
</string-array>
|
||||
<string-array name="themes_values">
|
||||
<item>light</item>
|
||||
<item>dark</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
|
@ -27,6 +27,5 @@
|
|||
<item name="colorOnSurfaceInverse">@color/md_theme_light_inverseOnSurface</item>
|
||||
<item name="colorSurfaceInverse">@color/md_theme_light_inverseSurface</item>
|
||||
<item name="colorPrimaryInverse">@color/md_theme_light_inversePrimary</item>
|
||||
<item name="android:statusBarColor">?colorSurface</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
19
app/src/main/res/xml/preferences_interface.xml
Normal file
19
app/src/main/res/xml/preferences_interface.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<PreferenceCategory android:title="@string/appearance">
|
||||
<SwitchPreferenceCompat
|
||||
android:icon="@drawable/ic_palette_24dp"
|
||||
android:key="dynamic_colors"
|
||||
android:title="@string/pref_dynamic_colors"
|
||||
android:summary="@string/pref_dynamic_colors_summary"/>
|
||||
<ListPreference
|
||||
android:defaultValue="@string/theme"
|
||||
android:entries="@array/themes"
|
||||
android:entryValues="@array/themes_values"
|
||||
android:icon="@drawable/ic_dark_mode_24dp"
|
||||
android:key="theme"
|
||||
android:title="@string/pref_light_dark_mode"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
|
@ -2,10 +2,10 @@
|
|||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<Preference
|
||||
app:title="@string/pref_title_appearance"
|
||||
app:title="@string/pref_title_interface"
|
||||
app:summary="@string/pref_summary_appearance"
|
||||
android:icon="@drawable/ic_palette_24dp"
|
||||
app:fragment="im.conversations.android.ui.fragment.MainSettingsFragment"/>
|
||||
android:icon="@drawable/ic_touch_app_24dp"
|
||||
app:fragment="im.conversations.android.ui.fragment.settings.InterfaceSettingsFragment"/>
|
||||
<Preference
|
||||
app:title="@string/pref_title_security"
|
||||
app:summary="@string/pref_summary_security"
|
||||
|
|
Loading…
Reference in a new issue