From a7d672f6b407531298604d7caf3f6f8c13f508ac Mon Sep 17 00:00:00 2001 From: Fumiaki Yoshimatsu Date: Sat, 26 Sep 2020 10:35:18 -0400 Subject: [PATCH] Apply locale updates correctly for appcompat-v1.2.0. Fixes #9736 See https://developer.android.com/jetpack/androidx/releases/appcompat#1.2.0 for how the code is "correctly" applying a new configuration. Co-authored-by: Cody Henthorne --- .../securesms/ApplicationContext.java | 3 +- .../ApplicationPreferencesActivity.java | 27 +++++++++++-- .../thoughtcrime/securesms/BaseActivity.java | 29 +++++++++----- .../thoughtcrime/securesms/MainActivity.java | 12 ++++++ .../thoughtcrime/securesms/MainNavigator.java | 5 ++- .../mediasend/MediaSendActivity.java | 1 - .../DynamicLanguageActivityHelper.java | 40 ------------------- .../DynamicLanguageContextWrapper.java | 27 +++++-------- 8 files changed, 70 insertions(+), 74 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageActivityHelper.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index c5ff890c7..7d79bde7f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -408,7 +408,8 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi @Override protected void attachBaseContext(Context base) { - super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(base, TextSecurePreferences.getLanguage(base))); + DynamicLanguageContextWrapper.updateContext(base); + super.attachBaseContext(base); } private static class ProviderInitializationException extends RuntimeException { diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java index fc7fdc57c..02e1f61ce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java @@ -82,9 +82,13 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced"; private static final String PREFERENCE_CATEGORY_DONATE = "preference_category_donate"; + private static final String WAS_CONFIGURATION_UPDATED = "was_configuration_updated"; + private final DynamicTheme dynamicTheme = new DynamicTheme(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); + private boolean wasConfigurationUpdated = false; + @Override protected void onPreCreate() { dynamicTheme.onCreate(this); @@ -102,9 +106,17 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity initFragment(android.R.id.content, new BackupsPreferenceFragment()); } else if (icicle == null) { initFragment(android.R.id.content, new ApplicationPreferenceFragment()); + } else { + wasConfigurationUpdated = icicle.getBoolean(WAS_CONFIGURATION_UPDATED); } } + @Override + protected void onSaveInstanceState(@NonNull Bundle outState) { + outState.putBoolean(WAS_CONFIGURATION_UPDATED, wasConfigurationUpdated); + super.onSaveInstanceState(outState); + } + @Override public void onResume() { super.onResume(); @@ -126,21 +138,28 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity if (fragmentManager.getBackStackEntryCount() > 0) { fragmentManager.popBackStack(); } else { - // TODO [greyson] Navigation - Intent intent = new Intent(this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); + if (wasConfigurationUpdated) { + setResult(MainActivity.RESULT_CONFIG_CHANGED); + } else { + setResult(RESULT_OK); + } finish(); } return true; } + @Override + public void onBackPressed() { + onSupportNavigateUp(); + } + @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)) { + wasConfigurationUpdated = true; recreate(); Intent intent = new Intent(this, KeyCachingService.class); diff --git a/app/src/main/java/org/thoughtcrime/securesms/BaseActivity.java b/app/src/main/java/org/thoughtcrime/securesms/BaseActivity.java index 170dac472..83f7109c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BaseActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BaseActivity.java @@ -3,21 +3,20 @@ package org.thoughtcrime.securesms; import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; import android.os.Build; -import android.os.Build.VERSION_CODES; import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.ActionBar; -import androidx.core.app.ActivityCompat; -import androidx.core.app.ActivityOptionsCompat; -import androidx.appcompat.app.AppCompatActivity; import android.view.View; import android.view.WindowManager; +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.app.ActivityOptionsCompat; + import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageActivityHelper; import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper; import java.util.Objects; @@ -40,7 +39,6 @@ public abstract class BaseActivity extends AppCompatActivity { protected void onResume() { super.onResume(); initializeScreenshotSecurity(); - DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage(this, TextSecurePreferences.getLanguage(this)); } @Override @@ -84,7 +82,18 @@ public abstract class BaseActivity extends AppCompatActivity { @Override protected void attachBaseContext(Context newBase) { - super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(newBase, TextSecurePreferences.getLanguage(newBase))); + super.attachBaseContext(newBase); + + Configuration configuration = new Configuration(); + configuration.uiMode = (configuration.uiMode & ~Configuration.UI_MODE_NIGHT_MASK) | getDelegate().getLocalNightMode(); + + applyOverrideConfiguration(configuration); + } + + @Override + public void applyOverrideConfiguration(Configuration overrideConfiguration) { + DynamicLanguageContextWrapper.prepareOverrideConfiguration(this, overrideConfiguration); + super.applyOverrideConfiguration(overrideConfiguration); } private void logEvent(@NonNull String event) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java index fb3a6af8f..1d561f2be 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java @@ -1,10 +1,12 @@ package org.thoughtcrime.securesms; +import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.thoughtcrime.securesms.tracing.Trace; import org.thoughtcrime.securesms.util.CommunicationActions; @@ -14,6 +16,8 @@ import org.thoughtcrime.securesms.util.DynamicTheme; @Trace public class MainActivity extends PassphraseRequiredActivity { + public static final int RESULT_CONFIG_CHANGED = Activity.RESULT_FIRST_USER + 901; + private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme(); private final MainNavigator navigator = new MainNavigator(this); @@ -52,6 +56,14 @@ public class MainActivity extends PassphraseRequiredActivity { } } + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == MainNavigator.REQUEST_CONFIG_CHANGES && resultCode == RESULT_CONFIG_CHANGED) { + recreate(); + } + } + public @NonNull MainNavigator getNavigator() { return navigator; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java b/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java index 8742d30e6..de99a33b5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java @@ -18,6 +18,8 @@ import org.thoughtcrime.securesms.recipients.RecipientId; public class MainNavigator { + public static final int REQUEST_CONFIG_CHANGES = 901; + private final MainActivity activity; public MainNavigator(@NonNull MainActivity activity) { @@ -65,10 +67,9 @@ public class MainNavigator { public void goToAppSettings() { Intent intent = new Intent(activity, ApplicationPreferencesActivity.class); - activity.startActivity(intent); + activity.startActivityForResult(intent, REQUEST_CONFIG_CHANGES); } - public void goToArchiveList() { getFragmentManager().beginTransaction() .setCustomAnimations(R.anim.slide_from_end, R.anim.slide_to_start, R.anim.slide_from_start, R.anim.slide_to_end) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java index 2b2d91122..7713961ea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendActivity.java @@ -70,7 +70,6 @@ import org.thoughtcrime.securesms.util.IOFunction; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.concurrent.SimpleTask; diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageActivityHelper.java b/app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageActivityHelper.java deleted file mode 100644 index 8665377d4..000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageActivityHelper.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.thoughtcrime.securesms.util.dynamiclanguage; - -import android.app.Activity; - -import androidx.annotation.MainThread; -import androidx.core.os.ConfigurationCompat; - -import org.thoughtcrime.securesms.logging.Log; - -import java.util.Locale; - -public final class DynamicLanguageActivityHelper { - - private static final String TAG = Log.tag(DynamicLanguageActivityHelper.class); - - private static String reentryProtection; - - /** - * If the activity isn't in the specified language, it will restart the activity. - */ - @MainThread - public static void recreateIfNotInCorrectLanguage(Activity activity, String language) { - Locale currentActivityLocale = ConfigurationCompat.getLocales(activity.getResources().getConfiguration()).get(0); - Locale selectedLocale = LocaleParser.findBestMatchingLocaleForLanguage(language); - - if (currentActivityLocale.equals(selectedLocale)) { - reentryProtection = ""; - return; - } - - String reentryKey = activity.getClass().getName() + ":" + selectedLocale; - if (!reentryKey.equals(reentryProtection)) { - reentryProtection = reentryKey; - Log.d(TAG, String.format("Activity Locale %s, Selected locale %s, restarting", currentActivityLocale, selectedLocale)); - activity.recreate(); - } else { - Log.d(TAG, String.format("Skipping recreate as looks like looping, Activity Locale %s, Selected locale %s", currentActivityLocale, selectedLocale)); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper.java b/app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper.java index d4e227fb2..48fe775f4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/dynamiclanguage/DynamicLanguageContextWrapper.java @@ -2,7 +2,8 @@ package org.thoughtcrime.securesms.util.dynamiclanguage; import android.content.Context; import android.content.res.Configuration; -import android.content.res.Resources; + +import org.thoughtcrime.securesms.util.TextSecurePreferences; import java.util.Locale; @@ -10,25 +11,19 @@ import java.util.Locale; * Updates a context with an alternative language. */ public final class DynamicLanguageContextWrapper { + private DynamicLanguageContextWrapper() {} - public static Context updateContext(Context context, String language) { - final Locale newLocale = LocaleParser.findBestMatchingLocaleForLanguage(language); + public static void prepareOverrideConfiguration(Context context, Configuration base) { + String language = TextSecurePreferences.getLanguage(context); + Locale newLocale = LocaleParser.findBestMatchingLocaleForLanguage(language); Locale.setDefault(newLocale); - - final Resources resources = context.getResources(); - final Configuration config = resources.getConfiguration(); - final Configuration newConfig = copyWithNewLocale(config, newLocale); - - resources.updateConfiguration(newConfig, resources.getDisplayMetrics()); - - return context; + base.setLocale(newLocale); } - private static Configuration copyWithNewLocale(Configuration config, Locale locale) { - final Configuration copy = new Configuration(config); - copy.setLocale(locale); - return copy; - } + public static void updateContext(Context base) { + Configuration config = base.getResources().getConfiguration(); + prepareOverrideConfiguration(base, config); + } }