mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 07:57:39 +00:00
Use platform LocaleManager if possible
This commit is contained in:
parent
6b81716440
commit
7173693d1b
@ -2,7 +2,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application>
|
||||
<application android:localeConfig="@xml/locale_config">
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:exported="true"
|
||||
|
@ -16,7 +16,7 @@ import android.os.Build
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import androidx.core.os.ProcessCompat
|
||||
import com.topjohnwu.magisk.core.ktx.getLabel
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import java.util.Locale
|
||||
import java.util.TreeSet
|
||||
|
||||
class CmdlineListItem(line: String) {
|
||||
@ -102,7 +102,7 @@ class AppProcessInfo(
|
||||
|
||||
companion object {
|
||||
private val comparator = compareBy<AppProcessInfo>(
|
||||
{ it.label.lowercase(currentLocale) },
|
||||
{ it.label.lowercase(Locale.ROOT) },
|
||||
{ it.info.packageName }
|
||||
)
|
||||
}
|
||||
|
@ -14,18 +14,16 @@ import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.ktx.activity
|
||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
import com.topjohnwu.magisk.core.utils.availableLocales
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.databinding.DialogSettingsAppNameBinding
|
||||
import com.topjohnwu.magisk.databinding.DialogSettingsDownloadPathBinding
|
||||
import com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding
|
||||
import com.topjohnwu.magisk.databinding.set
|
||||
import com.topjohnwu.magisk.utils.TextHolder
|
||||
import com.topjohnwu.magisk.utils.asText
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import com.topjohnwu.magisk.core.R as CoreR
|
||||
|
||||
// --- Customization
|
||||
@ -35,37 +33,27 @@ object Customization : BaseSettingsItem.Section() {
|
||||
}
|
||||
|
||||
object Language : BaseSettingsItem.Selector() {
|
||||
private val names: Array<String> get() = LocaleSetting.available.names
|
||||
private val tags: Array<String> get() = LocaleSetting.available.tags
|
||||
|
||||
override var value
|
||||
get() = index
|
||||
get() = tags.indexOf(Config.locale)
|
||||
set(value) {
|
||||
index = value
|
||||
Config.locale = entryValues[value]
|
||||
Config.locale = tags[value]
|
||||
}
|
||||
|
||||
override val title = CoreR.string.language.asText()
|
||||
|
||||
private var entries = emptyArray<String>()
|
||||
private var entryValues = emptyArray<String>()
|
||||
private var index = -1
|
||||
override fun entries(res: Resources) = names
|
||||
override fun descriptions(res: Resources) = names
|
||||
}
|
||||
|
||||
override fun entries(res: Resources) = entries
|
||||
override fun descriptions(res: Resources) = entries
|
||||
|
||||
override fun onPressed(view: View, handler: Handler) {
|
||||
if (entries.isNotEmpty())
|
||||
super.onPressed(view, handler)
|
||||
}
|
||||
|
||||
suspend fun loadLanguages(scope: CoroutineScope) {
|
||||
scope.launch {
|
||||
availableLocales().let { (names, values) ->
|
||||
entries = names
|
||||
entryValues = values
|
||||
val selectedLocale = currentLocale.getDisplayName(currentLocale)
|
||||
index = names.indexOfFirst { it == selectedLocale }.let { if (it == -1) 0 else it }
|
||||
notifyPropertyChanged(BR.description)
|
||||
}
|
||||
}
|
||||
object LanguageSystem : BaseSettingsItem.Blank() {
|
||||
override val title = CoreR.string.language.asText()
|
||||
override val description: TextHolder
|
||||
get() {
|
||||
val locale = LocaleSetting.instance.appLocale
|
||||
return locale?.getDisplayName(locale)?.asText() ?: CoreR.string.system_default.asText()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
package com.topjohnwu.magisk.ui.settings
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
@ -16,6 +20,7 @@ import com.topjohnwu.magisk.core.isRunningAsStub
|
||||
import com.topjohnwu.magisk.core.ktx.activity
|
||||
import com.topjohnwu.magisk.core.ktx.toast
|
||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
import com.topjohnwu.magisk.databinding.bindExtra
|
||||
import com.topjohnwu.magisk.events.AddHomeIconEvent
|
||||
import com.topjohnwu.magisk.events.AuthEvent
|
||||
@ -30,12 +35,6 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
it.put(BR.handler, this)
|
||||
}
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
Language.loadLanguages(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createItems(): List<BaseSettingsItem> {
|
||||
val context = AppContext
|
||||
val hidden = context.packageName != BuildConfig.APP_PACKAGE_NAME
|
||||
@ -43,7 +42,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
// Customization
|
||||
val list = mutableListOf(
|
||||
Customization,
|
||||
Theme, Language
|
||||
Theme, if (LocaleSetting.useLocaleManager) LanguageSystem else Language
|
||||
)
|
||||
if (isRunningAsStub && ShortcutManagerCompat.isRequestPinShortcutSupported(context))
|
||||
list.add(AddShortcut)
|
||||
@ -98,6 +97,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
SystemlessHosts -> createHosts()
|
||||
Hide, Restore -> withInstallPermission(andThen)
|
||||
AddShortcut -> AddHomeIconEvent().publish()
|
||||
LanguageSystem -> launchAppLocaleSettings(view.activity)
|
||||
else -> andThen()
|
||||
}
|
||||
}
|
||||
@ -112,6 +112,12 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchAppLocaleSettings(activity: Activity) {
|
||||
val intent = Intent(Settings.ACTION_APP_LOCALE_SETTINGS)
|
||||
intent.data = Uri.fromParts("package", activity.packageName, null)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
private fun openUrlIfNecessary(view: View) {
|
||||
UpdateChannelUrl.refresh()
|
||||
if (UpdateChannelUrl.isEnabled && UpdateChannelUrl.value.isBlank()) {
|
||||
|
@ -16,7 +16,6 @@ import com.topjohnwu.magisk.core.R
|
||||
import com.topjohnwu.magisk.core.data.magiskdb.PolicyDao
|
||||
import com.topjohnwu.magisk.core.ktx.getLabel
|
||||
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.databinding.MergeObservableList
|
||||
import com.topjohnwu.magisk.databinding.RvItem
|
||||
import com.topjohnwu.magisk.databinding.bindExtra
|
||||
@ -30,6 +29,7 @@ import com.topjohnwu.magisk.view.TextItem
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.Locale
|
||||
|
||||
class SuperuserViewModel(
|
||||
private val db: PolicyDao
|
||||
@ -92,7 +92,7 @@ class SuperuserViewModel(
|
||||
policies.addAll(map)
|
||||
}
|
||||
policies.sortWith(compareBy(
|
||||
{ it.appName.lowercase(currentLocale) },
|
||||
{ it.appName.lowercase(Locale.ROOT) },
|
||||
{ it.packageName }
|
||||
))
|
||||
itemsPolicies.update(policies)
|
||||
|
@ -14,7 +14,7 @@ import androidx.interpolator.view.animation.FastOutSlowInInterpolator
|
||||
import com.google.android.material.circularreveal.CircularRevealCompat
|
||||
import com.google.android.material.circularreveal.CircularRevealWidget
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
import kotlin.math.hypot
|
||||
|
||||
object MotionRevealHelper {
|
||||
@ -63,7 +63,9 @@ object MotionRevealHelper {
|
||||
it.interpolator = FastOutSlowInInterpolator()
|
||||
it.addListener(onStart = { show() }, onEnd = { if (revealInfo.radius != 0f) hide() })
|
||||
|
||||
val rtlMod = if (currentLocale.layoutDirection == View.LAYOUT_DIRECTION_RTL) 1f else -1f
|
||||
val rtlMod =
|
||||
if (LocaleSetting.instance.currentLocale.layoutDirection == View.LAYOUT_DIRECTION_RTL)
|
||||
1f else -1f
|
||||
val maxX = revealInfo.centerX - marginEnd - measuredWidth / 2f
|
||||
val targetX = if (revealInfo.radius == 0f) 0f else maxX * rtlMod
|
||||
val moveX = ObjectAnimator.ofFloat(this, View.TRANSLATION_X, targetX)
|
||||
|
@ -2,6 +2,7 @@ package com.topjohnwu.magisk.core
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.app.LocaleManager
|
||||
import android.content.ComponentCallbacks2
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
@ -13,12 +14,11 @@ import android.system.Os
|
||||
import androidx.profileinstaller.ProfileInstaller
|
||||
import com.topjohnwu.magisk.StubApk
|
||||
import com.topjohnwu.magisk.core.base.UntrackedActivity
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
import com.topjohnwu.magisk.core.utils.NetworkObserver
|
||||
import com.topjohnwu.magisk.core.utils.ProcessLifecycle
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.magisk.core.utils.ShellInit
|
||||
import com.topjohnwu.magisk.core.utils.refreshLocale
|
||||
import com.topjohnwu.magisk.core.utils.setConfig
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
@ -31,6 +31,9 @@ import timber.log.Timber
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
lateinit var AppApkPath: String
|
||||
private set
|
||||
|
||||
object AppContext : ContextWrapper(null),
|
||||
Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
|
||||
|
||||
@ -52,7 +55,7 @@ object AppContext : ContextWrapper(null),
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
resources.setConfig(newConfig)
|
||||
LocaleSetting.instance.updateResource(resources)
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
@ -79,7 +82,6 @@ object AppContext : ContextWrapper(null),
|
||||
} else {
|
||||
base.packageResourcePath
|
||||
}
|
||||
refreshLocale()
|
||||
resources.patch()
|
||||
|
||||
val shellBuilder = Shell.Builder.create()
|
||||
@ -97,6 +99,11 @@ object AppContext : ContextWrapper(null),
|
||||
// Pre-heat the shell ASAP
|
||||
Shell.getShell(null) {}
|
||||
|
||||
if (SDK_INT >= 34 && isRunningAsStub) {
|
||||
// Send over the locale config manually
|
||||
val lm = getSystemService(LocaleManager::class.java)
|
||||
lm.overrideLocaleConfig = LocaleSetting.localeConfig
|
||||
}
|
||||
Notifications.setup()
|
||||
ProcessLifecycle.init(this)
|
||||
NetworkObserver.init(this)
|
||||
|
@ -6,7 +6,7 @@ import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
||||
import com.topjohnwu.magisk.core.repository.DBConfig
|
||||
import com.topjohnwu.magisk.core.repository.PreferenceConfig
|
||||
import com.topjohnwu.magisk.core.utils.refreshLocale
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@ -135,7 +135,7 @@ object Config : PreferenceConfig, DBConfig {
|
||||
get() = localePrefs
|
||||
set(value) {
|
||||
localePrefs = value
|
||||
refreshLocale()
|
||||
LocaleSetting.instance.setLocale(value)
|
||||
}
|
||||
|
||||
var zygisk by dbSettings(Key.ZYGISK, false)
|
||||
|
@ -6,22 +6,18 @@ import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.res.AssetManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.util.DisplayMetrics
|
||||
import com.topjohnwu.magisk.StubApk
|
||||
import com.topjohnwu.magisk.core.ktx.unwrap
|
||||
import com.topjohnwu.magisk.core.utils.syncLocale
|
||||
|
||||
lateinit var AppApkPath: String
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
|
||||
fun Resources.addAssetPath(path: String) = StubApk.addAssetPath(this, path)
|
||||
|
||||
fun Resources.patch(): Resources {
|
||||
if (isRunningAsStub)
|
||||
addAssetPath(AppApkPath)
|
||||
syncLocale()
|
||||
LocaleSetting.instance.updateResource(this)
|
||||
return this
|
||||
}
|
||||
|
||||
@ -40,16 +36,6 @@ fun Context.wrap(): Context {
|
||||
}
|
||||
}
|
||||
|
||||
fun createNewResources(): Resources {
|
||||
val asset = AssetManager::class.java.newInstance()
|
||||
val config = Configuration(AppContext.resources.configuration)
|
||||
val metrics = DisplayMetrics()
|
||||
metrics.setTo(AppContext.resources.displayMetrics)
|
||||
val res = Resources(asset, metrics, config)
|
||||
res.addAssetPath(AppApkPath)
|
||||
return res
|
||||
}
|
||||
|
||||
fun Class<*>.cmp(pkg: String) =
|
||||
ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name)
|
||||
|
||||
|
@ -6,7 +6,7 @@ import com.topjohnwu.magisk.ProviderInstaller
|
||||
import com.topjohnwu.magisk.core.BuildConfig
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
import okhttp3.Cache
|
||||
import okhttp3.ConnectionSpec
|
||||
import okhttp3.Dns
|
||||
@ -68,7 +68,7 @@ fun createOkHttpClient(context: Context): OkHttpClient {
|
||||
builder.addInterceptor { chain ->
|
||||
val request = chain.request().newBuilder()
|
||||
request.header("User-Agent", "Magisk/${BuildConfig.APP_VERSION_CODE}")
|
||||
request.header("Accept-Language", currentLocale.toLanguageTag())
|
||||
request.header("Accept-Language", LocaleSetting.instance.currentLocale.toLanguageTag())
|
||||
chain.proceed(request.build())
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@ import android.content.*
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.AdaptiveIconDrawable
|
||||
@ -19,8 +18,8 @@ import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.getSystemService
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.utils.APKInstall
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
import java.io.File
|
||||
@ -54,9 +53,7 @@ fun ApplicationInfo.getLabel(pm: PackageManager): String {
|
||||
runCatching {
|
||||
if (labelRes > 0) {
|
||||
val res = pm.getResourcesForApplication(this)
|
||||
val config = Configuration()
|
||||
config.setLocale(currentLocale)
|
||||
res.updateConfiguration(config, res.displayMetrics)
|
||||
LocaleSetting.instance.updateResource(res)
|
||||
return res.getString(labelRes)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.topjohnwu.magisk.core.ktx
|
||||
|
||||
import androidx.collection.SparseArrayCompat
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@ -17,6 +16,7 @@ import java.lang.reflect.Field
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Collections
|
||||
import java.util.Locale
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
@ -98,13 +98,13 @@ fun Long.toTime(format: DateFormat) = format.format(this).orEmpty()
|
||||
val timeFormatStandard by lazy {
|
||||
SimpleDateFormat(
|
||||
"yyyy-MM-dd'T'HH.mm.ss",
|
||||
currentLocale
|
||||
Locale.ROOT
|
||||
)
|
||||
}
|
||||
val timeDateFormat: DateFormat by lazy {
|
||||
DateFormat.getDateTimeInstance(
|
||||
DateFormat.DEFAULT,
|
||||
DateFormat.DEFAULT,
|
||||
currentLocale
|
||||
Locale.ROOT
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,183 @@
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.LocaleConfig
|
||||
import android.app.LocaleManager
|
||||
import android.content.ContextWrapper
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.os.LocaleList
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.topjohnwu.magisk.core.AppApkPath
|
||||
import com.topjohnwu.magisk.core.AppContext
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.R
|
||||
import com.topjohnwu.magisk.core.base.relaunch
|
||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import java.util.Locale
|
||||
|
||||
interface LocaleSetting {
|
||||
// The locale that is manually overridden, null if system default
|
||||
val appLocale: Locale?
|
||||
// The current active locale used in the application
|
||||
val currentLocale: Locale
|
||||
|
||||
fun setLocale(tag: String)
|
||||
fun updateResource(res: Resources)
|
||||
|
||||
private class Api23Impl : LocaleSetting {
|
||||
|
||||
private val systemLocale: Locale = Locale.getDefault()
|
||||
|
||||
override var currentLocale: Locale = systemLocale
|
||||
override var appLocale: Locale? = null
|
||||
|
||||
init {
|
||||
setLocale(Config.locale)
|
||||
}
|
||||
|
||||
override fun setLocale(tag: String) {
|
||||
val locale = when {
|
||||
tag.isEmpty() -> null
|
||||
else -> Locale.forLanguageTag(tag)
|
||||
}
|
||||
currentLocale = locale ?: systemLocale
|
||||
appLocale = locale
|
||||
Locale.setDefault(currentLocale)
|
||||
updateResource(AppContext.resources)
|
||||
AppContext.foregroundActivity?.relaunch()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun updateResource(res: Resources) {
|
||||
val config = res.configuration
|
||||
config.setLocale(currentLocale)
|
||||
res.updateConfiguration(config, null)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(24)
|
||||
private class Api24Impl : LocaleSetting {
|
||||
|
||||
private val systemLocaleList = LocaleList.getDefault()
|
||||
private var currentLocaleList: LocaleList = systemLocaleList
|
||||
|
||||
override var appLocale: Locale? = null
|
||||
override val currentLocale: Locale get() = currentLocaleList[0]
|
||||
|
||||
init {
|
||||
setLocale(Config.locale)
|
||||
}
|
||||
|
||||
override fun setLocale(tag: String) {
|
||||
val localeList = when {
|
||||
tag.isEmpty() -> null
|
||||
else -> LocaleList.forLanguageTags(tag)
|
||||
}
|
||||
currentLocaleList = localeList ?: systemLocaleList
|
||||
appLocale = localeList?.get(0)
|
||||
LocaleList.setDefault(currentLocaleList)
|
||||
updateResource(AppContext.resources)
|
||||
AppContext.foregroundActivity?.relaunch()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun updateResource(res: Resources) {
|
||||
val config = res.configuration
|
||||
config.setLocales(currentLocaleList)
|
||||
res.updateConfiguration(config, null)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(33)
|
||||
private class Api33Impl : LocaleSetting {
|
||||
|
||||
private val lm: LocaleManager = AppContext.getSystemService(LocaleManager::class.java)
|
||||
|
||||
override val appLocale: Locale?
|
||||
get() = lm.applicationLocales.let { if (it.isEmpty) null else it[0] }
|
||||
|
||||
override val currentLocale: Locale
|
||||
get() = appLocale ?: lm.systemLocales[0]
|
||||
|
||||
// These following methods should not be used
|
||||
override fun setLocale(tag: String) {}
|
||||
override fun updateResource(res: Resources) {}
|
||||
}
|
||||
|
||||
class AppLocaleList(
|
||||
val names: Array<String>,
|
||||
val tags: Array<String>
|
||||
)
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
companion object {
|
||||
val available: AppLocaleList by lazy {
|
||||
val names = ArrayList<String>()
|
||||
val tags = ArrayList<String>()
|
||||
|
||||
names.add(AppContext.getString(R.string.system_default))
|
||||
tags.add("")
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
// Use platform LocaleConfig parser
|
||||
val config = localeConfig
|
||||
val list = config.supportedLocales ?: LocaleList.getEmptyLocaleList()
|
||||
names.ensureCapacity(list.size() + 1)
|
||||
tags.ensureCapacity(list.size() + 1)
|
||||
for (i in 0 until list.size()) {
|
||||
val locale = list[i]
|
||||
names.add(locale.getDisplayName(locale))
|
||||
tags.add(locale.toLanguageTag())
|
||||
}
|
||||
} else {
|
||||
// Manually parse locale_config.xml
|
||||
val parser = AppContext.resources.getXml(R.xml.locale_config)
|
||||
while (true) {
|
||||
when (parser.next()) {
|
||||
XmlPullParser.START_TAG -> {
|
||||
if (parser.name == "locale") {
|
||||
val tag = parser.getAttributeValue(0)
|
||||
val locale = Locale.forLanguageTag(tag)
|
||||
names.add(locale.getDisplayName(locale))
|
||||
tags.add(tag)
|
||||
}
|
||||
}
|
||||
XmlPullParser.END_DOCUMENT -> break
|
||||
}
|
||||
}
|
||||
}
|
||||
AppLocaleList(names.toTypedArray(), tags.toTypedArray())
|
||||
}
|
||||
|
||||
@get:RequiresApi(34)
|
||||
val localeConfig: LocaleConfig by lazy {
|
||||
val context = if (isRunningAsStub) {
|
||||
val pkgInfo = AppContext.packageManager.getPackageArchiveInfo(AppApkPath, 0)!!
|
||||
object : ContextWrapper(AppContext) {
|
||||
override fun getApplicationInfo() = pkgInfo.applicationInfo
|
||||
}
|
||||
} else {
|
||||
AppContext
|
||||
}
|
||||
LocaleConfig.fromContextIgnoringOverride(context)
|
||||
}
|
||||
|
||||
val useLocaleManager get() =
|
||||
if (isRunningAsStub) Build.VERSION.SDK_INT >= 34
|
||||
else Build.VERSION.SDK_INT >= 33
|
||||
|
||||
val instance: LocaleSetting by lazy {
|
||||
// Initialize available locale list
|
||||
available
|
||||
if (useLocaleManager) {
|
||||
Api33Impl()
|
||||
} else if (Build.VERSION.SDK_INT <= 23) {
|
||||
Api23Impl()
|
||||
} else {
|
||||
Api24Impl()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import com.topjohnwu.magisk.core.AppContext
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.R
|
||||
import com.topjohnwu.magisk.core.base.relaunch
|
||||
import com.topjohnwu.magisk.core.createNewResources
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.Locale
|
||||
|
||||
var currentLocale: Locale = Locale.getDefault()
|
||||
|
||||
@SuppressLint("ConstantLocale")
|
||||
val defaultLocale: Locale = Locale.getDefault()
|
||||
|
||||
private var cachedLocales: Pair<Array<String>, Array<String>>? = null
|
||||
|
||||
suspend fun availableLocales() = cachedLocales ?:
|
||||
withContext(Dispatchers.Default) {
|
||||
val compareId = R.string.app_changelog
|
||||
|
||||
// Create a completely new resource to prevent cross talk over active configs
|
||||
val res = createNewResources()
|
||||
|
||||
fun changeLocale(locale: Locale) {
|
||||
res.configuration.setLocale(locale)
|
||||
res.updateConfiguration(res.configuration, res.displayMetrics)
|
||||
}
|
||||
|
||||
val locales = ArrayList<String>().apply {
|
||||
// Add default locale
|
||||
add("en")
|
||||
|
||||
// Add some special locales
|
||||
add("zh-TW")
|
||||
add("pt-BR")
|
||||
|
||||
// Then add all supported locales
|
||||
addAll(Resources.getSystem().assets.locales)
|
||||
}.map {
|
||||
Locale.forLanguageTag(it)
|
||||
}.distinctBy {
|
||||
changeLocale(it)
|
||||
res.getString(compareId)
|
||||
}.sortedWith { a, b ->
|
||||
a.getDisplayName(a).compareTo(b.getDisplayName(b), true)
|
||||
}
|
||||
|
||||
changeLocale(defaultLocale)
|
||||
val defName = res.getString(R.string.system_default)
|
||||
|
||||
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.toLanguageTag())
|
||||
}
|
||||
|
||||
(names.toTypedArray() to values.toTypedArray()).also { cachedLocales = it }
|
||||
}
|
||||
|
||||
fun Resources.setConfig(config: Configuration) {
|
||||
config.setLocale(currentLocale)
|
||||
updateConfiguration(config, null)
|
||||
}
|
||||
|
||||
fun Resources.syncLocale() = setConfig(configuration)
|
||||
|
||||
fun refreshLocale() {
|
||||
val localeConfig = Config.locale
|
||||
currentLocale = when {
|
||||
localeConfig.isEmpty() -> defaultLocale
|
||||
else -> Locale.forLanguageTag(localeConfig)
|
||||
}
|
||||
Locale.setDefault(currentLocale)
|
||||
AppContext.resources.syncLocale()
|
||||
AppContext.foregroundActivity?.relaunch()
|
||||
}
|
51
app/core/src/main/res/xml/locale_config.xml
Normal file
51
app/core/src/main/res/xml/locale_config.xml
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<locale android:name="ar" />
|
||||
<locale android:name="ast" />
|
||||
<locale android:name="az" />
|
||||
<locale android:name="be" />
|
||||
<locale android:name="bg" />
|
||||
<locale android:name="bn" />
|
||||
<locale android:name="ca" />
|
||||
<locale android:name="cs" />
|
||||
<locale android:name="de" />
|
||||
<locale android:name="el" />
|
||||
<locale android:name="en" />
|
||||
<locale android:name="es" />
|
||||
<locale android:name="et" />
|
||||
<locale android:name="fa" />
|
||||
<locale android:name="fr" />
|
||||
<locale android:name="hi" />
|
||||
<locale android:name="hr" />
|
||||
<locale android:name="hu" />
|
||||
<locale android:name="in" />
|
||||
<locale android:name="it" />
|
||||
<locale android:name="iw" />
|
||||
<locale android:name="ja" />
|
||||
<locale android:name="ka" />
|
||||
<locale android:name="kk" />
|
||||
<locale android:name="ko" />
|
||||
<locale android:name="lt" />
|
||||
<locale android:name="mk" />
|
||||
<locale android:name="ml" />
|
||||
<locale android:name="nb" />
|
||||
<locale android:name="nl" />
|
||||
<locale android:name="pa" />
|
||||
<locale android:name="pl" />
|
||||
<locale android:name="pt-BR" />
|
||||
<locale android:name="pt-PT" />
|
||||
<locale android:name="ro" />
|
||||
<locale android:name="ru" />
|
||||
<locale android:name="sk" />
|
||||
<locale android:name="sq" />
|
||||
<locale android:name="sr" />
|
||||
<locale android:name="sv" />
|
||||
<locale android:name="sw" />
|
||||
<locale android:name="ta" />
|
||||
<locale android:name="th" />
|
||||
<locale android:name="tr" />
|
||||
<locale android:name="uk" />
|
||||
<locale android:name="vi" />
|
||||
<locale android:name="zh-CN" />
|
||||
<locale android:name="zh-TW" />
|
||||
</locale-config>
|
Loading…
x
Reference in New Issue
Block a user