Fix config/locale issues

Close #1977
This commit is contained in:
topjohnwu 2019-10-31 17:13:06 -04:00
parent 0f34457a10
commit 10ce11d671
4 changed files with 77 additions and 66 deletions

View File

@ -16,6 +16,7 @@ import com.topjohnwu.magisk.di.koinModules
import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.unwrap import com.topjohnwu.magisk.extensions.unwrap
import com.topjohnwu.magisk.utils.RootInit import com.topjohnwu.magisk.utils.RootInit
import com.topjohnwu.magisk.utils.updateConfig
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
@ -58,15 +59,15 @@ open class App() : Application() {
app = this app = this
impl = base impl = base
} }
ResourceMgr.init(impl) val wrapped = impl.wrap()
super.attachBaseContext(impl.wrap()) super.attachBaseContext(wrapped)
// Normal startup // Normal startup
startKoin { startKoin {
androidContext(baseContext) androidContext(wrapped)
modules(koinModules) modules(koinModules)
} }
ResourceMgr.reload() ResourceMgr.init(impl)
app.registerActivityLifecycleCallbacks(get<ActivityTracker>()) app.registerActivityLifecycleCallbacks(get<ActivityTracker>())
WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build()) WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build())
} }
@ -77,7 +78,7 @@ open class App() : Application() {
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
ResourceMgr.reload(newConfig) resources.updateConfig(newConfig)
if (!isRunningAsStub) if (!isRunningAsStub)
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
} }

View File

@ -25,6 +25,8 @@ import com.topjohnwu.magisk.ui.flash.FlashActivity
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
import com.topjohnwu.magisk.utils.currentLocale import com.topjohnwu.magisk.utils.currentLocale
import com.topjohnwu.magisk.utils.defaultLocale import com.topjohnwu.magisk.utils.defaultLocale
import com.topjohnwu.magisk.utils.refreshLocale
import com.topjohnwu.magisk.utils.updateConfig
import java.util.* import java.util.*
fun AssetManager.addAssetPath(path: String) { 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 { fun Class<*>.cmp(pkg: String = BuildConfig.APPLICATION_ID): ComponentName {
val name = ClassMap[this].name val name = ClassMap[this].name
return ComponentName(pkg, Info.stub?.componentMap?.get(name) ?: 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) { private class ResContext(base: Context) : GlobalResContext(base) {
override val mRes by lazy { base.resources.patch() } override val mRes by lazy { base.resources.patch() }
private fun Resources.patch(): Resources {
updateConfig()
if (isRunningAsStub)
assets.addAssetPath(ResourceMgr.resApk)
return this
}
} }
object ResourceMgr { object ResourceMgr {
internal lateinit var resource: Resources lateinit var resource: Resources
internal lateinit var resApk: String lateinit var resApk: String
fun init(context: Context) { fun init(context: Context) {
resource = context.resources resource = context.resources
if (isRunningAsStub) refreshLocale()
if (isRunningAsStub) {
resApk = DynAPK.current(context).path resApk = DynAPK.current(context).path
} resource.assets.addAssetPath(resApk)
fun reload(config: Configuration = Configuration(resource.configuration)) {
val localeConfig = Config.locale
currentLocale = when {
localeConfig.isEmpty() -> defaultLocale
else -> localeConfig.langTagToLocale()
} }
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) @RequiresApi(api = 28)

View File

@ -19,7 +19,6 @@ import com.topjohnwu.magisk.data.database.RepoDao
import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding
import com.topjohnwu.magisk.databinding.DialogCustomNameBinding import com.topjohnwu.magisk.databinding.DialogCustomNameBinding
import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.extensions.toLangTag
import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.download.DownloadService
import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.Configuration
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
@ -199,7 +198,7 @@ class SettingsFragment : BasePreferenceFragment() {
Shell.su("magiskhide --disable").submit() Shell.su("magiskhide --disable").submit()
} }
Config.Key.LOCALE -> { Config.Key.LOCALE -> {
ResourceMgr.reload() refreshLocale()
activity.recreate() activity.recreate()
} }
Config.Key.CHECK_UPDATES -> Utils.scheduleUpdateCheck(activity) Config.Key.CHECK_UPDATES -> Utils.scheduleUpdateCheck(activity)
@ -223,22 +222,7 @@ class SettingsFragment : BasePreferenceFragment() {
private fun setLocalePreference(lp: ListPreference) { private fun setLocalePreference(lp: ListPreference) {
lp.isEnabled = false lp.isEnabled = false
availableLocales.map { availableLocales.subscribeK { (names, values) ->
val names = mutableListOf<String>()
val values = mutableListOf<String>()
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) ->
lp.isEnabled = true lp.isEnabled = true
lp.entries = names lp.entries = names
lp.entryValues = values lp.entryValues = values

View File

@ -1,24 +1,32 @@
@file:Suppress("DEPRECATION")
package com.topjohnwu.magisk.utils package com.topjohnwu.magisk.utils
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.ResourceMgr import com.topjohnwu.magisk.ResourceMgr
import com.topjohnwu.magisk.extensions.langTagToLocale import com.topjohnwu.magisk.extensions.langTagToLocale
import com.topjohnwu.magisk.extensions.toLangTag
import io.reactivex.Single import io.reactivex.Single
import java.util.* import java.util.*
import kotlin.Comparator import kotlin.Comparator
import kotlin.collections.ArrayList
var currentLocale: Locale = Locale.getDefault() var currentLocale: Locale = Locale.getDefault()
@SuppressLint("ConstantLocale") @SuppressLint("ConstantLocale")
val defaultLocale: Locale = Locale.getDefault() val defaultLocale: Locale = Locale.getDefault()
@Suppress("DEPRECATION")
val availableLocales = Single.fromCallable { val availableLocales = Single.fromCallable {
val compareId = R.string.app_changelog val compareId = R.string.app_changelog
mutableListOf<Locale>().apply { val config = ResourceMgr.resource.configuration
val metrics = ResourceMgr.resource.displayMetrics
val res = Resources(ResourceMgr.resource.assets, metrics, config)
val locales = mutableListOf<Locale>().apply {
// Add default locale // Add default locale
add(Locale.ENGLISH) add(Locale.ENGLISH)
@ -26,24 +34,56 @@ val availableLocales = Single.fromCallable {
add(Locale.TAIWAN) add(Locale.TAIWAN)
add(Locale("pt", "BR")) add(Locale("pt", "BR"))
val config = Configuration()
val metrics = ResourceMgr.resource.displayMetrics
val res = Resources(ResourceMgr.resource.assets, metrics, config)
// Other locales // Other locales
val otherLocales = ResourceMgr.resource.assets.locales val otherLocales = ResourceMgr.resource.assets.locales
.map { it.langTagToLocale() } .map { it.langTagToLocale() }
.distinctBy { .distinctBy {
config.setLocale(it) config.setLocale(it)
res.updateConfiguration(config, metrics) res.updateConfiguration(config, metrics)
res.getString(compareId) res.getString(compareId)
} }
listOf("", "").toTypedArray()
addAll(otherLocales) addAll(otherLocales)
}.sortedWith(Comparator { a, b -> }.sortedWith(Comparator { a, b ->
a.getDisplayName(a).toLowerCase(a) 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<String>(locales.size + 1)
val values = ArrayList<String>(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()!! }.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()
}