Add foundation for using Android's DayNight theming system.

This commit is contained in:
Cody Henthorne
2020-11-09 09:29:11 -05:00
parent e9c7b120a0
commit 536e3139a2
95 changed files with 356 additions and 633 deletions

View File

@@ -70,6 +70,7 @@ import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@@ -146,6 +147,9 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
}
ApplicationDependencies.getJobManager().beginJobLoop();
DynamicTheme.setDefaultDayNightMode(this);
Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms");
}

View File

@@ -18,7 +18,6 @@
package org.thoughtcrime.securesms;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
@@ -29,7 +28,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.Preference;
import org.thoughtcrime.securesms.help.HelpFragment;
@@ -139,6 +137,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(TextSecurePreferences.THEME_PREF)) {
DynamicTheme.setDefaultDayNightMode(this);
recreate();
} else if (key.equals(TextSecurePreferences.LANGUAGE_PREF)) {
recreate();

View File

@@ -11,7 +11,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.logging.Log;
import java.util.Collections;
@@ -51,7 +50,7 @@ public class CachedInflater {
@MainThread
@SuppressWarnings("unchecked")
public <V extends View> V inflate(@LayoutRes int layoutRes, @Nullable ViewGroup parent, boolean attachToRoot) {
View cached = ViewCache.getInstance().pull(layoutRes);
View cached = ViewCache.getInstance().pull(layoutRes, ContextUtil.getNightModeConfiguration(context));
if (cached != null) {
if (parent != null && attachToRoot) {
parent.addView(cached);
@@ -87,12 +86,20 @@ public class CachedInflater {
private long lastClearTime;
private int nightModeConfiguration;
static ViewCache getInstance() {
return INSTANCE;
}
@MainThread
void cacheUntilLimit(Context context, @LayoutRes int layoutRes, @Nullable ViewGroup parent, int limit) {
void cacheUntilLimit(@NonNull Context context, @LayoutRes int layoutRes, @Nullable ViewGroup parent, int limit) {
int currentNightModeConfiguration = ContextUtil.getNightModeConfiguration(context);
if (nightModeConfiguration != currentNightModeConfiguration) {
clear();
nightModeConfiguration = currentNightModeConfiguration;
}
AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
int existingCount = Util.getOrDefault(cache, layoutRes, Collections.emptyList()).size();
@@ -118,7 +125,12 @@ public class CachedInflater {
}
@MainThread
@Nullable View pull(@LayoutRes int layoutRes) {
@Nullable View pull(@LayoutRes int layoutRes, int nightModeConfiguration) {
if (this.nightModeConfiguration != nightModeConfiguration) {
clear();
return null;
}
List<View> views = cache.get(layoutRes);
return views != null && !views.isEmpty() ? views.remove(0)
: null;

View File

@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.util;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import androidx.annotation.DrawableRes;
@@ -15,4 +16,8 @@ public final class ContextUtil {
public static @NonNull Drawable requireDrawable(@NonNull Context context, @DrawableRes int drawable) {
return Objects.requireNonNull(ContextCompat.getDrawable(context, drawable));
}
public static int getNightModeConfiguration(@NonNull Context context) {
return context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
}
}

View File

@@ -6,11 +6,7 @@ import org.thoughtcrime.securesms.R;
public class DynamicDarkActionBarTheme extends DynamicTheme {
protected @StyleRes int getLightThemeStyle() {
return R.style.TextSecure_LightTheme_Conversation;
}
protected @StyleRes int getDarkThemeStyle() {
return R.style.TextSecure_DarkTheme_Conversation;
protected @StyleRes int getTheme() {
return R.style.Signal_DayNight_DarkActionBar;
}
}

View File

@@ -6,11 +6,7 @@ import org.thoughtcrime.securesms.R;
public class DynamicDarkToolbarTheme extends DynamicTheme {
protected @StyleRes int getLightThemeStyle() {
return R.style.TextSecure_LightNoActionBar_DarkToolbar;
}
protected @StyleRes int getDarkThemeStyle() {
return R.style.TextSecure_DarkNoActionBar_DarkToolbar;
protected @StyleRes int getTheme() {
return R.style.Signal_DayNight_DarkNoActionBar;
}
}

View File

@@ -6,11 +6,7 @@ import org.thoughtcrime.securesms.R;
public class DynamicIntroTheme extends DynamicTheme {
protected @StyleRes int getLightThemeStyle() {
return R.style.TextSecure_LightIntroTheme;
}
protected @StyleRes int getDarkThemeStyle() {
return R.style.TextSecure_DarkIntroTheme;
protected @StyleRes int getTheme() {
return R.style.Signal_DayNight_IntroTheme;
}
}

View File

@@ -6,11 +6,7 @@ import org.thoughtcrime.securesms.R;
public class DynamicNoActionBarInviteTheme extends DynamicTheme {
protected @StyleRes int getLightThemeStyle() {
return R.style.Signal_Light_NoActionBar_Invite;
}
protected @StyleRes int getDarkThemeStyle() {
return R.style.Signal_NoActionBar_Invite;
protected @StyleRes int getTheme() {
return R.style.Signal_DayNight_Invite;
}
}

View File

@@ -6,11 +6,7 @@ import org.thoughtcrime.securesms.R;
public class DynamicNoActionBarTheme extends DynamicTheme {
protected @StyleRes int getLightThemeStyle() {
return R.style.TextSecure_LightNoActionBar;
}
protected @StyleRes int getDarkThemeStyle() {
return R.style.TextSecure_DarkNoActionBar;
protected @StyleRes int getTheme() {
return R.style.Signal_DayNight_NoActionBar;
}
}

View File

@@ -6,11 +6,7 @@ import org.thoughtcrime.securesms.R;
public class DynamicRegistrationTheme extends DynamicTheme {
protected @StyleRes int getLightThemeStyle() {
return R.style.TextSecure_LightRegistrationTheme;
}
protected @StyleRes int getDarkThemeStyle() {
return R.style.TextSecure_DarkRegistrationTheme;
protected @StyleRes int getTheme() {
return R.style.Signal_DayNight_Registration;
}
}

View File

@@ -2,12 +2,12 @@ package org.thoughtcrime.securesms.util;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.StyleRes;
import androidx.appcompat.app.AppCompatDelegate;
import org.thoughtcrime.securesms.R;
@@ -17,54 +17,51 @@ public class DynamicTheme {
public static final String LIGHT = "light";
public static final String SYSTEM = "system";
private static boolean isDarkTheme;
private static int globalNightModeConfiguration;
private int currentTheme;
private int onCreateNightModeConfiguration;
public void onCreate(Activity activity) {
boolean wasDarkTheme = isDarkTheme;
public void onCreate(@NonNull Activity activity) {
int previousGlobalConfiguration = globalNightModeConfiguration;
currentTheme = getSelectedTheme(activity);
isDarkTheme = isDarkTheme(activity);
onCreateNightModeConfiguration = ContextUtil.getNightModeConfiguration(activity);
globalNightModeConfiguration = onCreateNightModeConfiguration;
activity.setTheme(currentTheme);
activity.setTheme(getTheme());
if (isDarkTheme != wasDarkTheme) {
if (previousGlobalConfiguration != globalNightModeConfiguration) {
CachedInflater.from(activity).clear();
}
}
public void onResume(Activity activity) {
if (currentTheme != getSelectedTheme(activity)) {
Intent intent = activity.getIntent();
activity.finish();
OverridePendingTransition.invoke(activity);
activity.startActivity(intent);
OverridePendingTransition.invoke(activity);
public void onResume(@NonNull Activity activity) {
if (onCreateNightModeConfiguration != ContextUtil.getNightModeConfiguration(activity)) {
CachedInflater.from(activity).clear();
}
}
private @StyleRes int getSelectedTheme(Activity activity) {
if (isDarkTheme(activity)) {
return getDarkThemeStyle();
} else {
return getLightThemeStyle();
}
}
protected @StyleRes int getLightThemeStyle() {
return R.style.TextSecure_LightTheme;
}
protected @StyleRes int getDarkThemeStyle() {
return R.style.TextSecure_DarkTheme;
protected @StyleRes int getTheme() {
return R.style.Signal_DayNight;
}
public static boolean systemThemeAvailable() {
return Build.VERSION.SDK_INT >= 29;
}
public static void setDefaultDayNightMode(@NonNull Context context) {
String theme = TextSecurePreferences.getTheme(context);
if (theme.equals(SYSTEM)) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
} else if (DynamicTheme.isDarkTheme(context)) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
CachedInflater.from(context).clear();
}
/**
* Takes the system theme into account.
*/
@@ -81,10 +78,4 @@ public class DynamicTheme {
private static boolean isSystemInDarkTheme(@NonNull Context context) {
return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
}
private static final class OverridePendingTransition {
static void invoke(Activity activity) {
activity.overridePendingTransition(0, 0);
}
}
}