Simplify locale override (#5509)

This commit is contained in:
Ivan Iskandar 2021-07-08 05:11:52 +07:00 committed by GitHub
parent 49cdcc644c
commit 37d30eb887
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 45 additions and 110 deletions

View File

@ -7,7 +7,6 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.webkit.WebView import android.webkit.WebView
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
@ -28,7 +27,6 @@ import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.notification import eu.kanade.tachiyomi.util.system.notification
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -68,8 +66,6 @@ open class App : Application(), LifecycleObserver, ImageLoaderFactory {
setupAcra() setupAcra()
setupNotificationChannels() setupNotificationChannels()
LocaleHelper.updateConfiguration(this, resources.configuration)
ProcessLifecycleOwner.get().lifecycle.addObserver(this) ProcessLifecycleOwner.get().lifecycle.addObserver(this)
// Show notification to disable Incognito Mode when it's enabled // Show notification to disable Incognito Mode when it's enabled
@ -106,11 +102,6 @@ open class App : Application(), LifecycleObserver, ImageLoaderFactory {
MultiDex.install(this) MultiDex.install(this)
} }
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
LocaleHelper.updateConfiguration(this, newConfig, true)
}
override fun newImageLoader(): ImageLoader { override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(this).apply { return ImageLoader.Builder(this).apply {
componentRegistry { componentRegistry {

View File

@ -4,7 +4,6 @@ import android.os.Bundle
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.util.system.LocaleHelper
import nucleus.view.NucleusAppCompatActivity import nucleus.view.NucleusAppCompatActivity
abstract class BaseRxActivity<VB : ViewBinding, P : BasePresenter<*>> : NucleusAppCompatActivity<P>() { abstract class BaseRxActivity<VB : ViewBinding, P : BasePresenter<*>> : NucleusAppCompatActivity<P>() {
@ -14,11 +13,6 @@ abstract class BaseRxActivity<VB : ViewBinding, P : BasePresenter<*>> : NucleusA
lateinit var binding: VB lateinit var binding: VB
init {
@Suppress("LeakingThis")
LocaleHelper.updateConfiguration(this)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.base.activity package eu.kanade.tachiyomi.ui.base.activity
import android.content.Context
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
@ -8,6 +9,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferenceValues import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.asImmediateFlow import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.view.setSecureScreen import eu.kanade.tachiyomi.util.view.setSecureScreen
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -18,6 +20,10 @@ abstract class BaseThemedActivity : AppCompatActivity() {
val preferences: PreferencesHelper by injectLazy() val preferences: PreferencesHelper by injectLazy()
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LocaleHelper.createLocaleWrapper(newBase))
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
applyThemePreferences(preferences) applyThemePreferences(preferences)

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.base.activity package eu.kanade.tachiyomi.ui.base.activity
import android.content.Context
import android.os.Bundle import android.os.Bundle
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
@ -12,9 +13,8 @@ abstract class BaseViewBindingActivity<VB : ViewBinding> : BaseThemedActivity()
@Suppress("LeakingThis") @Suppress("LeakingThis")
private val secureActivityDelegate = SecureActivityDelegate(this) private val secureActivityDelegate = SecureActivityDelegate(this)
init { override fun attachBaseContext(newBase: Context) {
@Suppress("LeakingThis") super.attachBaseContext(LocaleHelper.createLocaleWrapper(newBase))
LocaleHelper.updateConfiguration(this)
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {

View File

@ -232,11 +232,7 @@ class SettingsGeneralController : SettingsController() {
summary = "%s" summary = "%s"
onChange { newValue -> onChange { newValue ->
val activity = activity ?: return@onChange false activity?.recreate()
val app = activity.application
LocaleHelper.changeLocale(newValue.toString())
LocaleHelper.updateConfiguration(app, app.resources.configuration)
activity.recreate()
true true
} }
} }

View File

@ -1,11 +1,11 @@
package eu.kanade.tachiyomi.util.system package eu.kanade.tachiyomi.util.system
import android.app.Application
import android.content.Context import android.content.Context
import android.content.ContextWrapper
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.view.ContextThemeWrapper import android.os.LocaleList
import androidx.core.os.ConfigurationCompat import androidx.core.os.LocaleListCompat
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.browse.source.SourcePresenter import eu.kanade.tachiyomi.ui.browse.source.SourcePresenter
@ -15,36 +15,10 @@ import java.util.Locale
/** /**
* Utility class to change the application's language in runtime. * Utility class to change the application's language in runtime.
*/ */
@Suppress("DEPRECATION")
object LocaleHelper { object LocaleHelper {
private val preferences: PreferencesHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy()
private var systemLocale: Locale? = null
/**
* The application's locale. When it's null, the system locale is used.
*/
private var appLocale = getLocaleFromString(preferences.lang().get())
/**
* The currently applied locale. Used to avoid losing the selected language after a non locale
* configuration change to the application.
*/
private var currentLocale: Locale? = null
/**
* Returns the locale for the value stored in preferences, or null if it's system language.
*
* @param pref the string value stored in preferences.
*/
fun getLocaleFromString(pref: String?): Locale? {
if (pref.isNullOrEmpty()) {
return null
}
return getLocale(pref)
}
/** /**
* Returns Display name of a string language code * Returns Display name of a string language code
*/ */
@ -60,18 +34,35 @@ object LocaleHelper {
/** /**
* Returns Display name of a string language code * Returns Display name of a string language code
*
* @param lang empty for system language
*/ */
fun getDisplayName(lang: String?): String { fun getDisplayName(lang: String?): String {
return when (lang) { if (lang == null) {
null -> "" return ""
"" -> {
systemLocale!!.getDisplayName(systemLocale).capitalize()
} }
else -> {
val locale = getLocale(lang) val locale = if (lang.isEmpty()) {
locale.getDisplayName(locale).capitalize() LocaleListCompat.getAdjustedDefault()[0]
} else {
getLocale(lang)
} }
return locale.getDisplayName(locale).replaceFirstChar { it.uppercase(locale) }
} }
/**
* Creates a ContextWrapper using selected Locale
*/
fun createLocaleWrapper(context: Context): ContextWrapper {
val appLocale = getLocaleFromString(preferences.lang().get())
val newConfiguration = Configuration(context.resources.configuration)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val localeList = LocaleList(appLocale)
newConfiguration.setLocales(localeList)
} else {
newConfiguration.setLocale(appLocale)
}
return ContextWrapper(context.createConfigurationContext(newConfiguration))
} }
/** /**
@ -87,57 +78,14 @@ object LocaleHelper {
} }
/** /**
* Changes the application's locale with a new preference. * Returns the locale for the value stored in preferences, defaults to main system language.
* *
* @param pref the new value stored in preferences. * @param pref the string value stored in preferences.
*/ */
fun changeLocale(pref: String) { private fun getLocaleFromString(pref: String?): Locale {
appLocale = getLocaleFromString(pref) if (pref.isNullOrEmpty()) {
return LocaleListCompat.getDefault()[0]
} }
return getLocale(pref)
/**
* Updates the app's language to an activity.
*/
fun updateConfiguration(wrapper: ContextThemeWrapper) {
if (appLocale != null) {
val config = Configuration(preferences.context.resources.configuration)
config.setLocale(appLocale)
wrapper.applyOverrideConfiguration(config)
}
}
/**
* Updates the app's language to the application.
*/
fun updateConfiguration(app: Application, config: Configuration, configChange: Boolean = false) {
if (systemLocale == null) {
systemLocale = ConfigurationCompat.getLocales(config)[0]
}
if (configChange) {
val configLocale = ConfigurationCompat.getLocales(config)[0]
if (currentLocale == configLocale) {
return
}
systemLocale = configLocale
}
currentLocale = appLocale ?: systemLocale ?: Locale.getDefault()
val newConfig = updateConfigLocale(config, currentLocale!!)
val resources = app.resources
resources.updateConfiguration(newConfig, resources.displayMetrics)
Locale.setDefault(currentLocale!!)
}
/**
* Returns a new configuration with the given locale applied.
*/
private fun updateConfigLocale(config: Configuration, locale: Locale): Configuration {
val newConfig = Configuration(config)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
newConfig.locale = locale
} else {
newConfig.setLocale(locale)
}
return newConfig
} }
} }