Fix all locale issues
This commit is contained in:
parent
1e7e06d1cc
commit
0d7474cc88
@ -82,7 +82,7 @@ open class App() : Application() {
|
||||
androidContext(wrapped)
|
||||
modules(koinModules)
|
||||
}
|
||||
ResourceMgr.init(impl)
|
||||
ResMgr.init(impl)
|
||||
app.registerActivityLifecycleCallbacks(get<ActivityTracker>())
|
||||
WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build())
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ fun Class<*>.cmp(pkg: String): ComponentName {
|
||||
inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName))
|
||||
|
||||
private open class GlobalResContext(base: Context) : ContextWrapper(base) {
|
||||
open val mRes: Resources get() = ResourceMgr.resource
|
||||
open val mRes: Resources get() = ResMgr.resource
|
||||
|
||||
override fun getResources(): Resources {
|
||||
return mRes
|
||||
@ -78,22 +78,24 @@ private class ResContext(base: Context) : GlobalResContext(base) {
|
||||
private fun Resources.patch(): Resources {
|
||||
updateConfig()
|
||||
if (isRunningAsStub)
|
||||
assets.addAssetPath(ResourceMgr.resApk)
|
||||
assets.addAssetPath(ResMgr.apk)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
object ResourceMgr {
|
||||
object ResMgr {
|
||||
|
||||
lateinit var resource: Resources
|
||||
lateinit var resApk: String
|
||||
lateinit var apk: String
|
||||
|
||||
fun init(context: Context) {
|
||||
resource = context.resources
|
||||
refreshLocale()
|
||||
if (isRunningAsStub) {
|
||||
resApk = DynAPK.current(context).path
|
||||
resource.assets.addAssetPath(resApk)
|
||||
apk = DynAPK.current(context).path
|
||||
resource.assets.addAssetPath(apk)
|
||||
} else {
|
||||
apk = context.packageResourcePath
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -169,8 +171,5 @@ val shouldKeepResources = listOf(
|
||||
R.string.unsupport_magisk_title,
|
||||
R.string.install_inactive_slot_msg,
|
||||
R.string.invalid_update_channel,
|
||||
R.string.update_available,
|
||||
|
||||
/* Android Studio is dumb and cannot detect following usages in databinding */
|
||||
R.menu.menu_reboot
|
||||
R.string.update_available
|
||||
)
|
||||
|
@ -3,11 +3,14 @@
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.AssetManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.util.DisplayMetrics
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.ResourceMgr
|
||||
import com.topjohnwu.magisk.core.ResMgr
|
||||
import com.topjohnwu.magisk.core.addAssetPath
|
||||
import com.topjohnwu.magisk.extensions.langTagToLocale
|
||||
import com.topjohnwu.magisk.extensions.toLangTag
|
||||
import io.reactivex.Single
|
||||
@ -22,41 +25,37 @@ val defaultLocale: Locale = Locale.getDefault()
|
||||
|
||||
val availableLocales = Single.fromCallable {
|
||||
val compareId = R.string.app_changelog
|
||||
val config = ResourceMgr.resource.configuration
|
||||
val metrics = ResourceMgr.resource.displayMetrics
|
||||
val res = Resources(ResourceMgr.resource.assets, metrics, config)
|
||||
|
||||
val locales = mutableListOf<Locale>().apply {
|
||||
// Create a completely new resource to prevent cross talk over app's configs
|
||||
val asset = AssetManager::class.java.newInstance().apply { addAssetPath(ResMgr.apk) }
|
||||
val config = Configuration(ResMgr.resource.configuration)
|
||||
val metrics = DisplayMetrics().apply { setTo(ResMgr.resource.displayMetrics) }
|
||||
val res = Resources(asset, metrics, config)
|
||||
|
||||
val locales = ArrayList<String>().apply {
|
||||
// Add default locale
|
||||
add(Locale.ENGLISH)
|
||||
add("en")
|
||||
|
||||
// Add some special locales
|
||||
add(Locale.TAIWAN)
|
||||
add(Locale("pt", "BR"))
|
||||
add("zh-TW")
|
||||
add("pt-BR")
|
||||
|
||||
// Other locales
|
||||
val otherLocales = ResourceMgr.resource.assets.locales
|
||||
.map { it.langTagToLocale() }
|
||||
.distinctBy {
|
||||
config.setLocale(it)
|
||||
res.updateConfiguration(config, metrics)
|
||||
res.getString(compareId)
|
||||
}
|
||||
|
||||
addAll(otherLocales)
|
||||
// Then add all supported locales
|
||||
addAll(res.assets.locales)
|
||||
}.map {
|
||||
it.langTagToLocale()
|
||||
}.distinctBy {
|
||||
config.setLocale(it)
|
||||
res.updateConfiguration(config, metrics)
|
||||
res.getString(compareId)
|
||||
}.sortedWith(Comparator { a, b ->
|
||||
a.getDisplayName(a).toLowerCase(a)
|
||||
.compareTo(b.getDisplayName(b).toLowerCase(b))
|
||||
a.getDisplayName(a).compareTo(b.getDisplayName(b), true)
|
||||
})
|
||||
|
||||
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)
|
||||
@ -85,5 +84,5 @@ fun refreshLocale() {
|
||||
else -> localeConfig.langTagToLocale()
|
||||
}
|
||||
Locale.setDefault(currentLocale)
|
||||
ResourceMgr.resource.updateConfig()
|
||||
ResMgr.resource.updateConfig()
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.topjohnwu.magisk.core.ResourceMgr
|
||||
import com.topjohnwu.magisk.core.ResMgr
|
||||
import com.topjohnwu.magisk.utils.RxBus
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
@ -18,7 +18,7 @@ val Protected = named("protected")
|
||||
|
||||
val applicationModule = module {
|
||||
single { RxBus() }
|
||||
factory { ResourceMgr.resource }
|
||||
factory { ResMgr.resource }
|
||||
factory { get<Context>().packageManager }
|
||||
factory(Protected) { createDEContext(get()) }
|
||||
single(SUTimeout) { get<Context>(Protected).getSharedPreferences("su_timeout", 0) }
|
||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.annotation.ArrayRes
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.databinding.ViewDataBinding
|
||||
@ -145,13 +146,19 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
|
||||
|
||||
protected val resources get() = get<Resources>()
|
||||
|
||||
abstract val entries: Array<out CharSequence>
|
||||
abstract val entryValues: Array<out CharSequence>
|
||||
@ArrayRes open val entryRes = -1
|
||||
@ArrayRes open val entryValRes = -1
|
||||
|
||||
open val entries get() = resources.getArrayOrEmpty(entryRes)
|
||||
open val entryValues get() = resources.getArrayOrEmpty(entryValRes)
|
||||
|
||||
@get:Bindable
|
||||
val selectedEntry
|
||||
get() = entries.getOrNull(value)
|
||||
|
||||
private fun Resources.getArrayOrEmpty(id: Int): Array<String> =
|
||||
runCatching { getStringArray(id) }.getOrDefault(emptyArray())
|
||||
|
||||
override fun onPressed(view: View, callback: Callback) {
|
||||
if (entries.isEmpty() || entryValues.isEmpty()) return
|
||||
callback.onItemPressed(view, this)
|
||||
|
@ -39,8 +39,8 @@ object Language : SettingsItem.Selector() {
|
||||
}
|
||||
|
||||
override val title = R.string.language.asTransitive()
|
||||
override var entries = arrayOf<String>()
|
||||
override var entryValues = arrayOf<String>()
|
||||
override var entries = emptyArray<String>()
|
||||
override var entryValues = emptyArray<String>()
|
||||
|
||||
init {
|
||||
availableLocales.subscribeK { (names, values) ->
|
||||
@ -132,19 +132,19 @@ object GridSize : SettingsItem.Selector() {
|
||||
|
||||
override val title = R.string.settings_grid_column_count_title.asTransitive()
|
||||
override val description = R.string.settings_grid_column_count_summary.asTransitive()
|
||||
override val entries = resources.getStringArray(R.array.span_count)
|
||||
override val entryValues = resources.getStringArray(R.array.value_array)
|
||||
override val entryRes = R.array.span_count
|
||||
override val entryValRes = R.array.value_array
|
||||
}
|
||||
|
||||
object UpdateChannel : SettingsItem.Selector() {
|
||||
override var value by bindableValue(Config.updateChannel) { Config.updateChannel = it }
|
||||
|
||||
override val title = R.string.settings_update_channel_title.asTransitive()
|
||||
override val entries = resources.getStringArray(R.array.update_channel).let {
|
||||
override val entries get() = resources.getStringArray(R.array.update_channel).let {
|
||||
if (!isCanaryVersion && Config.updateChannel < Config.Value.CANARY_CHANNEL)
|
||||
it.take(it.size - 2).toTypedArray() else it
|
||||
}
|
||||
override val entryValues = resources.getStringArray(R.array.value_array)
|
||||
override val entryValRes = R.array.value_array
|
||||
}
|
||||
|
||||
object UpdateChannelUrl : SettingsItem.Input() {
|
||||
@ -247,8 +247,8 @@ object Superuser : SettingsItem.Section() {
|
||||
|
||||
object AccessMode : SettingsItem.Selector() {
|
||||
override val title = R.string.superuser_access.asTransitive()
|
||||
override val entries = resources.getStringArray(R.array.su_access)
|
||||
override val entryValues = resources.getStringArray(R.array.value_array)
|
||||
override val entryRes = R.array.su_access
|
||||
override val entryValRes = R.array.value_array
|
||||
|
||||
override var value by bindableValue(Config.rootMode) {
|
||||
Config.rootMode = entryValues[it].toInt()
|
||||
@ -257,16 +257,15 @@ object AccessMode : SettingsItem.Selector() {
|
||||
|
||||
object MultiuserMode : SettingsItem.Selector() {
|
||||
override val title = R.string.multiuser_mode.asTransitive()
|
||||
override val entries = resources.getStringArray(R.array.multiuser_mode)
|
||||
override val entryValues = resources.getStringArray(R.array.value_array)
|
||||
private val descArray = resources.getStringArray(R.array.multiuser_summary)
|
||||
override val entryRes = R.array.multiuser_mode
|
||||
override val entryValRes = R.array.value_array
|
||||
|
||||
override var value by bindableValue(Config.suMultiuserMode) {
|
||||
Config.suMultiuserMode = entryValues[it].toInt()
|
||||
}
|
||||
|
||||
override val description
|
||||
get() = descArray[value].asTransitive()
|
||||
get() = resources.getStringArray(R.array.multiuser_summary)[value].asTransitive()
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Const.USER_ID == 0
|
||||
@ -275,22 +274,21 @@ object MultiuserMode : SettingsItem.Selector() {
|
||||
|
||||
object MountNamespaceMode : SettingsItem.Selector() {
|
||||
override val title = R.string.mount_namespace_mode.asTransitive()
|
||||
override val entries = resources.getStringArray(R.array.namespace)
|
||||
override val entryValues = resources.getStringArray(R.array.value_array)
|
||||
private val descArray = resources.getStringArray(R.array.namespace_summary)
|
||||
override val entryRes = R.array.namespace
|
||||
override val entryValRes = R.array.value_array
|
||||
|
||||
override var value by bindableValue(Config.suMntNamespaceMode) {
|
||||
Config.suMntNamespaceMode = entryValues[it].toInt()
|
||||
}
|
||||
|
||||
override val description
|
||||
get() = descArray[value].asTransitive()
|
||||
get() = resources.getStringArray(R.array.namespace_summary)[value].asTransitive()
|
||||
}
|
||||
|
||||
object AutomaticResponse : SettingsItem.Selector() {
|
||||
override val title = R.string.auto_response.asTransitive()
|
||||
override val entries = resources.getStringArray(R.array.auto_response)
|
||||
override val entryValues = resources.getStringArray(R.array.value_array)
|
||||
override val entryRes = R.array.auto_response
|
||||
override val entryValRes = R.array.value_array
|
||||
|
||||
override var value by bindableValue(Config.suAutoReponse) {
|
||||
Config.suAutoReponse = entryValues[it].toInt()
|
||||
@ -299,8 +297,8 @@ object AutomaticResponse : SettingsItem.Selector() {
|
||||
|
||||
object RequestTimeout : SettingsItem.Selector() {
|
||||
override val title = R.string.request_timeout.asTransitive()
|
||||
override val entries = resources.getStringArray(R.array.request_timeout)
|
||||
override val entryValues = resources.getStringArray(R.array.request_timeout_value)
|
||||
override val entryRes = R.array.request_timeout
|
||||
override val entryValRes = R.array.request_timeout_value
|
||||
|
||||
override var value by bindableValue(selected) {
|
||||
Config.suDefaultTimeout = entryValues[it].toInt()
|
||||
@ -312,8 +310,8 @@ object RequestTimeout : SettingsItem.Selector() {
|
||||
|
||||
object SUNotification : SettingsItem.Selector() {
|
||||
override val title = R.string.superuser_notification.asTransitive()
|
||||
override val entries = resources.getStringArray(R.array.su_notification)
|
||||
override val entryValues = resources.getStringArray(R.array.value_array)
|
||||
override val entryRes = R.array.su_notification
|
||||
override val entryValRes = R.array.value_array
|
||||
|
||||
override var value by bindableValue(Config.suNotification) {
|
||||
Config.suNotification = entryValues[it].toInt()
|
||||
|
Loading…
Reference in New Issue
Block a user