diff --git a/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt b/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt index 47794b4c4e30..e3b7fb142355 100644 --- a/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt +++ b/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt @@ -950,13 +950,8 @@ class AppInitializer @Inject constructor( */ private inner class MemoryAndConfigChangeMonitor : ComponentCallbacks2 { override fun onConfigurationChanged(newConfig: Configuration) { - // If per-app locale is enabled make sure the in-app locale is correct, - // otherwise reapply in-app locale on configuration change - if (perAppLocaleManager.isPerAppLanguagePrefsEnabled()) { - perAppLocaleManager.checkAndUpdateOldLanguagePrefKey() - } else { - LocaleManager.setLocale(context) - } + // Make sure the in-app locale is correct + perAppLocaleManager.checkAndUpdateOldLanguagePrefKey() } override fun onLowMemory() { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/LocaleAwareActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/LocaleAwareActivity.kt index a5ec8bca5b63..15afe4f33862 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/LocaleAwareActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/LocaleAwareActivity.kt @@ -3,53 +3,24 @@ package org.wordpress.android.ui import android.content.Context import android.content.res.Configuration import androidx.appcompat.app.AppCompatActivity -import org.wordpress.android.ui.prefs.AppPrefs -import org.wordpress.android.util.LocaleManager -import org.wordpress.android.util.PerAppLocaleManager /** - * Newer versions of the AppCompat library no longer support locale changes at application level, - * so this activity is used to help handle those changes at activity level. - * Reference: https://issuetracker.google.com/issues/141869006#comment9 - * - * All the actual logic is inside the LocaleManager class, which should be used directly in cases where - * extending from this class is not possible/preferable. - * - * Note: please be mindful of the principle of favoring composition over inheritance and refrain from - * building upon this class unless it's absolutely necessary. - * - * Update Dec 2024: We've added experimental support for per-app language preferences which - * will eventually negate the need for this class. Instead of extending from this class, we - * should extend from AppCompatActivity once this feature is out of the experimental phase. + * Update Dec 2024: We've added support for per-app language preferences which negate + * the need for this class. Instead of extending from this class, we should extend + * from AppCompatActivity. */ abstract class LocaleAwareActivity : AppCompatActivity() { /** * Used to update locales on API 21 to API 25. */ override fun attachBaseContext(newBase: Context?) { - if (isPerAppLocaleEnabled()) { - super.attachBaseContext(newBase) - } else { - super.attachBaseContext(LocaleManager.setLocale(newBase)) - } + super.attachBaseContext(newBase) } /** * Used to update locales on API 26 and beyond. */ override fun applyOverrideConfiguration(overrideConfiguration: Configuration?) { - if (isPerAppLocaleEnabled()) { - super.applyOverrideConfiguration(overrideConfiguration) - } else { - super.applyOverrideConfiguration(LocaleManager.updatedConfigLocale(baseContext, overrideConfiguration)) - } - } - - /** - * Ideally we would use [PerAppLocaleManager.isPerAppLanguagePrefsEnabled] here, but we - * can't inject [PerAppLocaleManager] into an abstract class - */ - private fun isPerAppLocaleEnabled(): Boolean { - return AppPrefs.getManualFeatureConfig(PerAppLocaleManager.EXPERIMENTAL_PER_APP_LANGUAGE_PREF_KEY) + super.applyOverrideConfiguration(overrideConfiguration) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppSettingsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppSettingsFragment.java index f7226a5d2a3a..2bc319f1c6c2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppSettingsFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppSettingsFragment.java @@ -13,7 +13,6 @@ import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; import android.preference.SwitchPreference; -import android.text.TextUtils; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -49,11 +48,8 @@ import org.wordpress.android.ui.deeplinks.DeepLinkOpenWebLinksWithJetpackHelper; import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalPhaseHelper; import org.wordpress.android.ui.mysite.jetpackbadge.JetpackPoweredBottomSheetFragment; -import org.wordpress.android.util.PerAppLocaleManager; import org.wordpress.android.ui.prefs.language.LocalePickerBottomSheet; import org.wordpress.android.ui.prefs.language.LocalePickerBottomSheet.LocalePickerCallback; -import org.wordpress.android.ui.reader.services.update.ReaderUpdateLogic; -import org.wordpress.android.ui.reader.services.update.ReaderUpdateServiceStarter; import org.wordpress.android.ui.utils.UiHelpers; import org.wordpress.android.ui.whatsnew.FeatureAnnouncementDialogFragment; import org.wordpress.android.ui.whatsnew.FeatureAnnouncementProvider; @@ -61,9 +57,8 @@ import org.wordpress.android.util.AppThemeUtils; import org.wordpress.android.util.BuildConfigWrapper; import org.wordpress.android.util.JetpackBrandingUtils; -import org.wordpress.android.util.LocaleManager; -import org.wordpress.android.util.LocaleProvider; import org.wordpress.android.util.NetworkUtils; +import org.wordpress.android.util.PerAppLocaleManager; import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.WPActivityUtils; import org.wordpress.android.util.WPPrefUtils; @@ -71,9 +66,7 @@ import org.wordpress.android.viewmodel.ContextProvider; import java.util.Collections; -import java.util.EnumSet; import java.util.HashMap; -import java.util.Locale; import java.util.Map; import javax.inject.Inject; @@ -101,7 +94,6 @@ public class AppSettingsFragment extends PreferenceFragment private WPSwitchPreference mOpenWebLinksWithJetpack; private Preference mWhatsNew; - private Boolean mIsPerAppLanguagePrefsEnabled; @Inject SiteStore mSiteStore; @Inject AccountStore mAccountStore; @@ -110,7 +102,6 @@ public class AppSettingsFragment extends PreferenceFragment @Inject FeatureAnnouncementProvider mFeatureAnnouncementProvider; @Inject BuildConfigWrapper mBuildConfigWrapper; @Inject JetpackBrandingUtils mJetpackBrandingUtils; - @Inject LocaleProvider mLocaleProvider; @Inject DeepLinkOpenWebLinksWithJetpackHelper mOpenWebLinksWithJetpackHelper; @Inject UiHelpers mUiHelpers; @Inject JetpackFeatureRemovalPhaseHelper mJetpackFeatureRemovalPhaseHelper; @@ -125,8 +116,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) { ((WordPress) getActivity().getApplication()).component().inject(this); mDispatcher.register(this); - mIsPerAppLanguagePrefsEnabled = mPerAppLocaleManager.isPerAppLanguagePrefsEnabled(); - addPreferencesFromResource(R.xml.app_settings); findPreference(getString(R.string.pref_key_send_usage)).setOnPreferenceChangeListener( @@ -259,11 +248,7 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, mLanguagePreference = (WPPreference) findPreference(getString(R.string.pref_key_language)); mLanguagePreference.setOnPreferenceChangeListener(this); mLanguagePreference.setOnPreferenceClickListener(this); - if (mIsPerAppLanguagePrefsEnabled) { - mLanguagePreference.setSummary(mPerAppLocaleManager.getCurrentLocaleDisplayName()); - } else { - mLanguagePreference.setSummary(mLocaleProvider.getAppLanguageDisplayString()); - } + mLanguagePreference.setSummary(mPerAppLocaleManager.getCurrentLocaleDisplayName()); return view; } @@ -436,10 +421,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { return false; } - if (preference == mLanguagePreference) { - changeLanguage(newValue.toString()); - return false; - } else if (preference == mOptimizedImage) { + if (preference == mOptimizedImage) { AppPrefs.setImageOptimize((Boolean) newValue); mImageMaxSizePref.setEnabled((Boolean) newValue); Map properties = new HashMap<>(); @@ -503,39 +485,6 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { return super.onOptionsItemSelected(item); } - private void changeLanguage(String languageCode) { - if (mLanguagePreference == null || TextUtils.isEmpty(languageCode)) { - return; - } - - if (LocaleManager.isSameLanguage(languageCode)) { - return; - } - - LocaleManager.setNewLocale(WordPress.getContext(), languageCode); - WordPress.updateContextLocale(); - mContextProvider.refreshContext(); - - // Track language change on Analytics because we have both the device language and app selected language - // data in Tracks metadata. - Map properties = new HashMap<>(); - properties.put("app_locale", Locale.getDefault()); - AnalyticsTracker.track(Stat.ACCOUNT_SETTINGS_LANGUAGE_CHANGED, properties); - - // Language is now part of metadata, so we need to refresh them - AnalyticsUtils.refreshMetadata(mAccountStore, mSiteStore); - - // Refresh the app - Intent refresh = new Intent(getActivity(), getActivity().getClass()); - startActivity(refresh); - getActivity().setResult(LANGUAGE_CHANGED); - getActivity().overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); - getActivity().finish(); - - // update Reader tags as they need be localized - ReaderUpdateServiceStarter.startService(WordPress.getContext(), EnumSet.of(ReaderUpdateLogic.UpdateTask.TAGS)); - } - private boolean handleDevicePreferenceClick() { try { // open specific app info screen @@ -648,9 +597,8 @@ private boolean handleFeatureAnnouncementClick() { } private boolean handleAppLocalePickerClick() { - // if per-app language preferences are enabled and the device is on API 33+, take the user to the - // system app settings to change the language - if (mIsPerAppLanguagePrefsEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // if the device is on API 33+, take the user to the system app settings to change the language + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { mPerAppLocaleManager.openAppLanguageSettings(getContext()); return true; } else if (getActivity() instanceof AppCompatActivity) { @@ -678,11 +626,7 @@ private void reattachLocalePickerCallback() { @Override public void onLocaleSelected(@NonNull String languageCode) { - if (mIsPerAppLanguagePrefsEnabled) { - mPerAppLocaleManager.setCurrentLocaleByLanguageCode(languageCode); - } else { - onPreferenceChange(mLanguagePreference, languageCode); - } + mPerAppLocaleManager.onLanguageChanged(languageCode); } private void handleOpenLinksInJetpack(Boolean newValue) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/ExperimentalFeaturesActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/ExperimentalFeaturesActivity.kt index 5ab5227464f4..736f7baf388a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/ExperimentalFeaturesActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/ExperimentalFeaturesActivity.kt @@ -32,7 +32,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.ViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint -import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -40,14 +39,11 @@ import kotlinx.coroutines.flow.update import org.wordpress.android.R import org.wordpress.android.ui.compose.theme.AppThemeM3 import org.wordpress.android.ui.compose.unit.Margin -import org.wordpress.android.util.PerAppLocaleManager import org.wordpress.android.util.extensions.setContent -import javax.inject.Inject val experimentalFeatures = listOf( Feature(key = "experimental_block_editor"), Feature(key = "experimental_block_editor_theme_styles"), - Feature(key = PerAppLocaleManager.EXPERIMENTAL_PER_APP_LANGUAGE_PREF_KEY) ) data class Feature( @@ -55,10 +51,7 @@ data class Feature( val key: String, ) -@HiltViewModel -class FeatureViewModel @Inject constructor( - private val perAppLocaleManager: PerAppLocaleManager -) : ViewModel() { +class FeatureViewModel : ViewModel() { private val _switchStates = MutableStateFlow>(emptyMap()) val switchStates: StateFlow> = _switchStates.asStateFlow() @@ -76,18 +69,6 @@ class FeatureViewModel @Inject constructor( AppPrefs.setManualFeatureConfig(enabled, key) } } - - featureToggled(key, enabled) - } - - private fun featureToggled(key: String, enabled: Boolean) { - if (key == PerAppLocaleManager.EXPERIMENTAL_PER_APP_LANGUAGE_PREF_KEY) { - if (enabled) { - perAppLocaleManager.performMigrationIfNecessary() - } else { - perAppLocaleManager.resetApplicationLocale() - } - } } } diff --git a/WordPress/src/main/java/org/wordpress/android/util/PerAppLocaleManager.kt b/WordPress/src/main/java/org/wordpress/android/util/PerAppLocaleManager.kt index d6958dcde54b..5abfc8ef09ab 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/PerAppLocaleManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/PerAppLocaleManager.kt @@ -8,8 +8,16 @@ import android.provider.Settings import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatDelegate import androidx.core.os.LocaleListCompat +import org.wordpress.android.WordPress.Companion.getContext +import org.wordpress.android.analytics.AnalyticsTracker +import org.wordpress.android.fluxc.store.AccountStore +import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.fluxc.utils.AppLogWrapper import org.wordpress.android.ui.prefs.AppPrefsWrapper +import org.wordpress.android.ui.reader.services.update.ReaderUpdateLogic.UpdateTask +import org.wordpress.android.ui.reader.services.update.ReaderUpdateServiceStarter +import org.wordpress.android.util.analytics.AnalyticsUtils +import java.util.EnumSet import java.util.Locale import javax.inject.Inject @@ -20,6 +28,8 @@ import javax.inject.Inject class PerAppLocaleManager @Inject constructor( private val appPrefsWrapper: AppPrefsWrapper, private val appLogWrapper: AppLogWrapper, + private val siteStore: SiteStore, + private val accountStore: AccountStore, ) { private fun getCurrentLocale(): Locale { return if (isApplicationLocaleEmpty()) { @@ -31,6 +41,8 @@ class PerAppLocaleManager @Inject constructor( fun getCurrentLocaleDisplayName(): String = getCurrentLocale().displayName + private fun getCurrentLocaleLanguageCode(): String = getCurrentLocale().language + /** * Important: this should only be called after Activity.onCreate() * https://developer.android.com/reference/androidx/appcompat/app/AppCompatDelegate#getApplicationLocales() @@ -65,7 +77,7 @@ class PerAppLocaleManager @Inject constructor( AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList()) } - fun setCurrentLocaleByLanguageCode(languageCode: String) { + private fun setCurrentLocaleByLanguageCode(languageCode: String) { // We shouldn't have to replace "_" with "-" but this is in order to work with our existing language picker // on pre-Android 13 devices val appLocale = LocaleListCompat.forLanguageTags(languageCode.replace("_", "-")) @@ -77,33 +89,27 @@ class PerAppLocaleManager @Inject constructor( * Previously the app locale was stored in SharedPreferences, so here we migrate to AndroidX per-app language prefs */ fun performMigrationIfNecessary() { - if (isPerAppLanguagePrefsEnabled()) { - if (isApplicationLocaleEmpty()) { - val prefKey = LocaleManager.getLocalePrefKeyString() - val previousLanguage = appPrefsWrapper.getPrefString(prefKey, "") - if (previousLanguage?.isNotEmpty() == true) { - appLogWrapper.d( - AppLog.T.SETTINGS, - "PerAppLocaleManager: performing migration to AndroidX per-app language prefs" - ) - setCurrentLocaleByLanguageCode(previousLanguage) - } else { - appLogWrapper.d( - AppLog.T.SETTINGS, - "PerAppLocaleManager: setting default locale" - ) - setCurrentLocaleByLanguageCode(Locale.getDefault().language) - } + if (isApplicationLocaleEmpty()) { + val prefKey = LocaleManager.getLocalePrefKeyString() + val previousLanguage = appPrefsWrapper.getPrefString(prefKey, "") + if (previousLanguage?.isNotEmpty() == true) { + appLogWrapper.d( + AppLog.T.SETTINGS, + "PerAppLocaleManager: performing migration to AndroidX per-app language prefs" + ) + setCurrentLocaleByLanguageCode(previousLanguage) } else { - checkAndUpdateOldLanguagePrefKey() + appLogWrapper.d( + AppLog.T.SETTINGS, + "PerAppLocaleManager: setting default locale" + ) + setCurrentLocaleByLanguageCode(Locale.getDefault().language) } + } else { + checkAndUpdateOldLanguagePrefKey() } } - fun isPerAppLanguagePrefsEnabled(): Boolean { - return appPrefsWrapper.getManualFeatureConfig(EXPERIMENTAL_PER_APP_LANGUAGE_PREF_KEY) - } - /** * Open the app settings dialog so the user can change the app language. * Note that the per-app language setting is only available in API 33+ @@ -118,7 +124,30 @@ class PerAppLocaleManager @Inject constructor( } } - companion object { - const val EXPERIMENTAL_PER_APP_LANGUAGE_PREF_KEY = "experimental_per_app_language_prefs" + /** + * Called when the device language is changed from our in-app language picker + * TODO Detect when language changed from app settings dialog + */ + fun onLanguageChanged(languageCode: String) { + if (languageCode.isEmpty()) { + return + } + + // Only update if the language is different + if (languageCode != getCurrentLocaleLanguageCode()) { + setCurrentLocaleByLanguageCode(languageCode) + } + + // Track language change on Analytics because we have both the device language and app selected language + // data in Tracks metadata. + val properties: MutableMap = HashMap() + properties["app_locale"] = languageCode + AnalyticsTracker.track(AnalyticsTracker.Stat.ACCOUNT_SETTINGS_LANGUAGE_CHANGED, properties) + + // Language is now part of metadata, so we need to refresh them + AnalyticsUtils.refreshMetadata(accountStore, siteStore) + + // update Reader tags as they need be localized + ReaderUpdateServiceStarter.startService(getContext(), EnumSet.of(UpdateTask.TAGS)) } } diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index f684997cc9d3..76db2f186275 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -963,7 +963,6 @@ Experimental Features Experimental block editor Experimental block editor styles - Experimental per-app language preferences Debug Settings