From 10ce11d671cf5c29fc2a237477666c20548fcce8 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 31 Oct 2019 17:13:06 -0400 Subject: [PATCH] Fix config/locale issues Close #1977 --- app/src/main/java/com/topjohnwu/magisk/App.kt | 11 +-- .../main/java/com/topjohnwu/magisk/Hacks.kt | 42 ++++------- .../magisk/ui/settings/SettingsFragment.kt | 20 +----- .../com/topjohnwu/magisk/utils/Locales.kt | 70 +++++++++++++++---- 4 files changed, 77 insertions(+), 66 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/App.kt b/app/src/main/java/com/topjohnwu/magisk/App.kt index 0569ac57a..391c2563f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/App.kt @@ -16,6 +16,7 @@ import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.unwrap import com.topjohnwu.magisk.utils.RootInit +import com.topjohnwu.magisk.utils.updateConfig import com.topjohnwu.superuser.Shell import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin @@ -58,15 +59,15 @@ open class App() : Application() { app = this impl = base } - ResourceMgr.init(impl) - super.attachBaseContext(impl.wrap()) + val wrapped = impl.wrap() + super.attachBaseContext(wrapped) // Normal startup startKoin { - androidContext(baseContext) + androidContext(wrapped) modules(koinModules) } - ResourceMgr.reload() + ResourceMgr.init(impl) app.registerActivityLifecycleCallbacks(get()) WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build()) } @@ -77,7 +78,7 @@ open class App() : Application() { } override fun onConfigurationChanged(newConfig: Configuration) { - ResourceMgr.reload(newConfig) + resources.updateConfig(newConfig) if (!isRunningAsStub) super.onConfigurationChanged(newConfig) } diff --git a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt index dbac949b9..272d299ae 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt @@ -25,6 +25,8 @@ import com.topjohnwu.magisk.ui.flash.FlashActivity import com.topjohnwu.magisk.ui.surequest.SuRequestActivity import com.topjohnwu.magisk.utils.currentLocale import com.topjohnwu.magisk.utils.defaultLocale +import com.topjohnwu.magisk.utils.refreshLocale +import com.topjohnwu.magisk.utils.updateConfig import java.util.* fun AssetManager.addAssetPath(path: String) { @@ -51,15 +53,6 @@ fun Context.wrapJob(): Context = object : GlobalResContext(this) { } } -// Override locale and inject resources from dynamic APK -private fun Resources.patch(config: Configuration = Configuration(configuration)): Resources { - config.setLocale(currentLocale) - updateConfiguration(config, displayMetrics) - if (isRunningAsStub) - assets.addAssetPath(ResourceMgr.resApk) - return this -} - fun Class<*>.cmp(pkg: String = BuildConfig.APPLICATION_ID): ComponentName { val name = ClassMap[this].name return ComponentName(pkg, Info.stub?.componentMap?.get(name) ?: name) @@ -92,35 +85,28 @@ private open class GlobalResContext(base: Context) : ContextWrapper(base) { private class ResContext(base: Context) : GlobalResContext(base) { override val mRes by lazy { base.resources.patch() } + + private fun Resources.patch(): Resources { + updateConfig() + if (isRunningAsStub) + assets.addAssetPath(ResourceMgr.resApk) + return this + } } object ResourceMgr { - internal lateinit var resource: Resources - internal lateinit var resApk: String + lateinit var resource: Resources + lateinit var resApk: String fun init(context: Context) { resource = context.resources - if (isRunningAsStub) + refreshLocale() + if (isRunningAsStub) { resApk = DynAPK.current(context).path - } - - fun reload(config: Configuration = Configuration(resource.configuration)) { - val localeConfig = Config.locale - currentLocale = when { - localeConfig.isEmpty() -> defaultLocale - else -> localeConfig.langTagToLocale() + resource.assets.addAssetPath(resApk) } - Locale.setDefault(currentLocale) - resource.patch(config) } - - fun getString(locale: Locale, @StringRes id: Int): String { - val config = Configuration() - config.setLocale(locale) - return Resources(resource.assets, resource.displayMetrics, config).getString(id) - } - } @RequiresApi(api = 28) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index 81fba3fff..fdcac23a5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -19,7 +19,6 @@ import com.topjohnwu.magisk.data.database.RepoDao import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding import com.topjohnwu.magisk.databinding.DialogCustomNameBinding import com.topjohnwu.magisk.extensions.subscribeK -import com.topjohnwu.magisk.extensions.toLangTag import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.DownloadSubject @@ -199,7 +198,7 @@ class SettingsFragment : BasePreferenceFragment() { Shell.su("magiskhide --disable").submit() } Config.Key.LOCALE -> { - ResourceMgr.reload() + refreshLocale() activity.recreate() } Config.Key.CHECK_UPDATES -> Utils.scheduleUpdateCheck(activity) @@ -223,22 +222,7 @@ class SettingsFragment : BasePreferenceFragment() { private fun setLocalePreference(lp: ListPreference) { lp.isEnabled = false - availableLocales.map { - val names = mutableListOf() - val values = mutableListOf() - - names.add( - ResourceMgr.getString(defaultLocale, R.string.system_default) - ) - values.add("") - - it.forEach { locale -> - names.add(locale.getDisplayName(locale)) - values.add(locale.toLangTag()) - } - - Pair(names.toTypedArray(), values.toTypedArray()) - }.subscribeK { (names, values) -> + availableLocales.subscribeK { (names, values) -> lp.isEnabled = true lp.entries = names lp.entryValues = values diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt b/app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt index f5fe0c8ea..f08bdb3d3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt @@ -1,24 +1,32 @@ +@file:Suppress("DEPRECATION") + package com.topjohnwu.magisk.utils import android.annotation.SuppressLint import android.content.res.Configuration import android.content.res.Resources +import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R import com.topjohnwu.magisk.ResourceMgr import com.topjohnwu.magisk.extensions.langTagToLocale +import com.topjohnwu.magisk.extensions.toLangTag import io.reactivex.Single import java.util.* import kotlin.Comparator +import kotlin.collections.ArrayList var currentLocale: Locale = Locale.getDefault() @SuppressLint("ConstantLocale") val defaultLocale: Locale = Locale.getDefault() -@Suppress("DEPRECATION") val availableLocales = Single.fromCallable { val compareId = R.string.app_changelog - mutableListOf().apply { + val config = ResourceMgr.resource.configuration + val metrics = ResourceMgr.resource.displayMetrics + val res = Resources(ResourceMgr.resource.assets, metrics, config) + + val locales = mutableListOf().apply { // Add default locale add(Locale.ENGLISH) @@ -26,24 +34,56 @@ val availableLocales = Single.fromCallable { add(Locale.TAIWAN) add(Locale("pt", "BR")) - val config = Configuration() - val metrics = ResourceMgr.resource.displayMetrics - val res = Resources(ResourceMgr.resource.assets, metrics, config) - // Other locales val otherLocales = ResourceMgr.resource.assets.locales - .map { it.langTagToLocale() } - .distinctBy { - config.setLocale(it) - res.updateConfiguration(config, metrics) - res.getString(compareId) - } - - listOf("", "").toTypedArray() + .map { it.langTagToLocale() } + .distinctBy { + config.setLocale(it) + res.updateConfiguration(config, metrics) + res.getString(compareId) + } addAll(otherLocales) }.sortedWith(Comparator { a, b -> a.getDisplayName(a).toLowerCase(a) - .compareTo(b.getDisplayName(b).toLowerCase(b)) + .compareTo(b.getDisplayName(b).toLowerCase(b)) }) + + config.setLocale(defaultLocale) + res.updateConfiguration(config, metrics) + val defName = res.getString(R.string.system_default) + + // Restore back to current locale + config.setLocale(currentLocale) + res.updateConfiguration(config, metrics) + + Pair(locales, defName) +}.map { (locales, defName) -> + val names = ArrayList(locales.size + 1) + val values = ArrayList(locales.size + 1) + + names.add(defName) + values.add("") + + locales.forEach { locale -> + names.add(locale.getDisplayName(locale)) + values.add(locale.toLangTag()) + } + + Pair(names.toTypedArray(), values.toTypedArray()) }.cache()!! + +fun Resources.updateConfig(config: Configuration = configuration) { + config.setLocale(currentLocale) + updateConfiguration(config, displayMetrics) +} + +fun refreshLocale() { + val localeConfig = Config.locale + currentLocale = when { + localeConfig.isEmpty() -> defaultLocale + else -> localeConfig.langTagToLocale() + } + Locale.setDefault(currentLocale) + ResourceMgr.resource.updateConfig() +}