mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 16:07: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"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<application>
|
<application android:localeConfig="@xml/locale_config">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
@ -16,7 +16,7 @@ import android.os.Build
|
|||||||
import android.os.Build.VERSION.SDK_INT
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import androidx.core.os.ProcessCompat
|
import androidx.core.os.ProcessCompat
|
||||||
import com.topjohnwu.magisk.core.ktx.getLabel
|
import com.topjohnwu.magisk.core.ktx.getLabel
|
||||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
import java.util.Locale
|
||||||
import java.util.TreeSet
|
import java.util.TreeSet
|
||||||
|
|
||||||
class CmdlineListItem(line: String) {
|
class CmdlineListItem(line: String) {
|
||||||
@ -102,7 +102,7 @@ class AppProcessInfo(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val comparator = compareBy<AppProcessInfo>(
|
private val comparator = compareBy<AppProcessInfo>(
|
||||||
{ it.label.lowercase(currentLocale) },
|
{ it.label.lowercase(Locale.ROOT) },
|
||||||
{ it.info.packageName }
|
{ it.info.packageName }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -14,18 +14,16 @@ import com.topjohnwu.magisk.core.Const
|
|||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.ktx.activity
|
import com.topjohnwu.magisk.core.ktx.activity
|
||||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
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.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.DialogSettingsAppNameBinding
|
||||||
import com.topjohnwu.magisk.databinding.DialogSettingsDownloadPathBinding
|
import com.topjohnwu.magisk.databinding.DialogSettingsDownloadPathBinding
|
||||||
import com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding
|
import com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding
|
||||||
import com.topjohnwu.magisk.databinding.set
|
import com.topjohnwu.magisk.databinding.set
|
||||||
|
import com.topjohnwu.magisk.utils.TextHolder
|
||||||
import com.topjohnwu.magisk.utils.asText
|
import com.topjohnwu.magisk.utils.asText
|
||||||
import com.topjohnwu.magisk.view.MagiskDialog
|
import com.topjohnwu.magisk.view.MagiskDialog
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import com.topjohnwu.magisk.core.R as CoreR
|
import com.topjohnwu.magisk.core.R as CoreR
|
||||||
|
|
||||||
// --- Customization
|
// --- Customization
|
||||||
@ -35,38 +33,28 @@ object Customization : BaseSettingsItem.Section() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object Language : BaseSettingsItem.Selector() {
|
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
|
override var value
|
||||||
get() = index
|
get() = tags.indexOf(Config.locale)
|
||||||
set(value) {
|
set(value) {
|
||||||
index = value
|
Config.locale = tags[value]
|
||||||
Config.locale = entryValues[value]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override val title = CoreR.string.language.asText()
|
override val title = CoreR.string.language.asText()
|
||||||
|
|
||||||
private var entries = emptyArray<String>()
|
override fun entries(res: Resources) = names
|
||||||
private var entryValues = emptyArray<String>()
|
override fun descriptions(res: Resources) = names
|
||||||
private var index = -1
|
}
|
||||||
|
|
||||||
override fun entries(res: Resources) = entries
|
object LanguageSystem : BaseSettingsItem.Blank() {
|
||||||
override fun descriptions(res: Resources) = entries
|
override val title = CoreR.string.language.asText()
|
||||||
|
override val description: TextHolder
|
||||||
override fun onPressed(view: View, handler: Handler) {
|
get() {
|
||||||
if (entries.isNotEmpty())
|
val locale = LocaleSetting.instance.appLocale
|
||||||
super.onPressed(view, handler)
|
return locale?.getDisplayName(locale)?.asText() ?: CoreR.string.system_default.asText()
|
||||||
}
|
|
||||||
|
|
||||||
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 Theme : BaseSettingsItem.Blank() {
|
object Theme : BaseSettingsItem.Blank() {
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package com.topjohnwu.magisk.ui.settings
|
package com.topjohnwu.magisk.ui.settings
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.provider.Settings
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.pm.ShortcutManagerCompat
|
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.activity
|
||||||
import com.topjohnwu.magisk.core.ktx.toast
|
import com.topjohnwu.magisk.core.ktx.toast
|
||||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
import com.topjohnwu.magisk.core.tasks.HideAPK
|
||||||
|
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||||
import com.topjohnwu.magisk.databinding.bindExtra
|
import com.topjohnwu.magisk.databinding.bindExtra
|
||||||
import com.topjohnwu.magisk.events.AddHomeIconEvent
|
import com.topjohnwu.magisk.events.AddHomeIconEvent
|
||||||
import com.topjohnwu.magisk.events.AuthEvent
|
import com.topjohnwu.magisk.events.AuthEvent
|
||||||
@ -30,12 +35,6 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
|||||||
it.put(BR.handler, this)
|
it.put(BR.handler, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
|
||||||
viewModelScope.launch {
|
|
||||||
Language.loadLanguages(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createItems(): List<BaseSettingsItem> {
|
private fun createItems(): List<BaseSettingsItem> {
|
||||||
val context = AppContext
|
val context = AppContext
|
||||||
val hidden = context.packageName != BuildConfig.APP_PACKAGE_NAME
|
val hidden = context.packageName != BuildConfig.APP_PACKAGE_NAME
|
||||||
@ -43,7 +42,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
|||||||
// Customization
|
// Customization
|
||||||
val list = mutableListOf(
|
val list = mutableListOf(
|
||||||
Customization,
|
Customization,
|
||||||
Theme, Language
|
Theme, if (LocaleSetting.useLocaleManager) LanguageSystem else Language
|
||||||
)
|
)
|
||||||
if (isRunningAsStub && ShortcutManagerCompat.isRequestPinShortcutSupported(context))
|
if (isRunningAsStub && ShortcutManagerCompat.isRequestPinShortcutSupported(context))
|
||||||
list.add(AddShortcut)
|
list.add(AddShortcut)
|
||||||
@ -98,6 +97,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
|||||||
SystemlessHosts -> createHosts()
|
SystemlessHosts -> createHosts()
|
||||||
Hide, Restore -> withInstallPermission(andThen)
|
Hide, Restore -> withInstallPermission(andThen)
|
||||||
AddShortcut -> AddHomeIconEvent().publish()
|
AddShortcut -> AddHomeIconEvent().publish()
|
||||||
|
LanguageSystem -> launchAppLocaleSettings(view.activity)
|
||||||
else -> andThen()
|
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) {
|
private fun openUrlIfNecessary(view: View) {
|
||||||
UpdateChannelUrl.refresh()
|
UpdateChannelUrl.refresh()
|
||||||
if (UpdateChannelUrl.isEnabled && UpdateChannelUrl.value.isBlank()) {
|
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.data.magiskdb.PolicyDao
|
||||||
import com.topjohnwu.magisk.core.ktx.getLabel
|
import com.topjohnwu.magisk.core.ktx.getLabel
|
||||||
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
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.MergeObservableList
|
||||||
import com.topjohnwu.magisk.databinding.RvItem
|
import com.topjohnwu.magisk.databinding.RvItem
|
||||||
import com.topjohnwu.magisk.databinding.bindExtra
|
import com.topjohnwu.magisk.databinding.bindExtra
|
||||||
@ -30,6 +29,7 @@ import com.topjohnwu.magisk.view.TextItem
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
class SuperuserViewModel(
|
class SuperuserViewModel(
|
||||||
private val db: PolicyDao
|
private val db: PolicyDao
|
||||||
@ -92,7 +92,7 @@ class SuperuserViewModel(
|
|||||||
policies.addAll(map)
|
policies.addAll(map)
|
||||||
}
|
}
|
||||||
policies.sortWith(compareBy(
|
policies.sortWith(compareBy(
|
||||||
{ it.appName.lowercase(currentLocale) },
|
{ it.appName.lowercase(Locale.ROOT) },
|
||||||
{ it.packageName }
|
{ it.packageName }
|
||||||
))
|
))
|
||||||
itemsPolicies.update(policies)
|
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.CircularRevealCompat
|
||||||
import com.google.android.material.circularreveal.CircularRevealWidget
|
import com.google.android.material.circularreveal.CircularRevealWidget
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
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
|
import kotlin.math.hypot
|
||||||
|
|
||||||
object MotionRevealHelper {
|
object MotionRevealHelper {
|
||||||
@ -63,7 +63,9 @@ object MotionRevealHelper {
|
|||||||
it.interpolator = FastOutSlowInInterpolator()
|
it.interpolator = FastOutSlowInInterpolator()
|
||||||
it.addListener(onStart = { show() }, onEnd = { if (revealInfo.radius != 0f) hide() })
|
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 maxX = revealInfo.centerX - marginEnd - measuredWidth / 2f
|
||||||
val targetX = if (revealInfo.radius == 0f) 0f else maxX * rtlMod
|
val targetX = if (revealInfo.radius == 0f) 0f else maxX * rtlMod
|
||||||
val moveX = ObjectAnimator.ofFloat(this, View.TRANSLATION_X, targetX)
|
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.Activity
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.app.LocaleManager
|
||||||
import android.content.ComponentCallbacks2
|
import android.content.ComponentCallbacks2
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.ContextWrapper
|
import android.content.ContextWrapper
|
||||||
@ -13,12 +14,11 @@ import android.system.Os
|
|||||||
import androidx.profileinstaller.ProfileInstaller
|
import androidx.profileinstaller.ProfileInstaller
|
||||||
import com.topjohnwu.magisk.StubApk
|
import com.topjohnwu.magisk.StubApk
|
||||||
import com.topjohnwu.magisk.core.base.UntrackedActivity
|
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.NetworkObserver
|
||||||
import com.topjohnwu.magisk.core.utils.ProcessLifecycle
|
import com.topjohnwu.magisk.core.utils.ProcessLifecycle
|
||||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||||
import com.topjohnwu.magisk.core.utils.ShellInit
|
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.magisk.view.Notifications
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
@ -31,6 +31,9 @@ import timber.log.Timber
|
|||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
lateinit var AppApkPath: String
|
||||||
|
private set
|
||||||
|
|
||||||
object AppContext : ContextWrapper(null),
|
object AppContext : ContextWrapper(null),
|
||||||
Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
|
Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
|
||||||
|
|
||||||
@ -52,7 +55,7 @@ object AppContext : ContextWrapper(null),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
resources.setConfig(newConfig)
|
LocaleSetting.instance.updateResource(resources)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResumed(activity: Activity) {
|
override fun onActivityResumed(activity: Activity) {
|
||||||
@ -79,7 +82,6 @@ object AppContext : ContextWrapper(null),
|
|||||||
} else {
|
} else {
|
||||||
base.packageResourcePath
|
base.packageResourcePath
|
||||||
}
|
}
|
||||||
refreshLocale()
|
|
||||||
resources.patch()
|
resources.patch()
|
||||||
|
|
||||||
val shellBuilder = Shell.Builder.create()
|
val shellBuilder = Shell.Builder.create()
|
||||||
@ -97,6 +99,11 @@ object AppContext : ContextWrapper(null),
|
|||||||
// Pre-heat the shell ASAP
|
// Pre-heat the shell ASAP
|
||||||
Shell.getShell(null) {}
|
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()
|
Notifications.setup()
|
||||||
ProcessLifecycle.init(this)
|
ProcessLifecycle.init(this)
|
||||||
NetworkObserver.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.ktx.writeTo
|
||||||
import com.topjohnwu.magisk.core.repository.DBConfig
|
import com.topjohnwu.magisk.core.repository.DBConfig
|
||||||
import com.topjohnwu.magisk.core.repository.PreferenceConfig
|
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.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
@ -135,7 +135,7 @@ object Config : PreferenceConfig, DBConfig {
|
|||||||
get() = localePrefs
|
get() = localePrefs
|
||||||
set(value) {
|
set(value) {
|
||||||
localePrefs = value
|
localePrefs = value
|
||||||
refreshLocale()
|
LocaleSetting.instance.setLocale(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
var zygisk by dbSettings(Key.ZYGISK, false)
|
var zygisk by dbSettings(Key.ZYGISK, false)
|
||||||
|
@ -6,22 +6,18 @@ import android.content.ComponentName
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.ContextWrapper
|
import android.content.ContextWrapper
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.AssetManager
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.util.DisplayMetrics
|
|
||||||
import com.topjohnwu.magisk.StubApk
|
import com.topjohnwu.magisk.StubApk
|
||||||
import com.topjohnwu.magisk.core.ktx.unwrap
|
import com.topjohnwu.magisk.core.ktx.unwrap
|
||||||
import com.topjohnwu.magisk.core.utils.syncLocale
|
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||||
|
|
||||||
lateinit var AppApkPath: String
|
|
||||||
|
|
||||||
fun Resources.addAssetPath(path: String) = StubApk.addAssetPath(this, path)
|
fun Resources.addAssetPath(path: String) = StubApk.addAssetPath(this, path)
|
||||||
|
|
||||||
fun Resources.patch(): Resources {
|
fun Resources.patch(): Resources {
|
||||||
if (isRunningAsStub)
|
if (isRunningAsStub)
|
||||||
addAssetPath(AppApkPath)
|
addAssetPath(AppApkPath)
|
||||||
syncLocale()
|
LocaleSetting.instance.updateResource(this)
|
||||||
return 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) =
|
fun Class<*>.cmp(pkg: String) =
|
||||||
ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name)
|
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.BuildConfig
|
||||||
import com.topjohnwu.magisk.core.Config
|
import com.topjohnwu.magisk.core.Config
|
||||||
import com.topjohnwu.magisk.core.Info
|
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.Cache
|
||||||
import okhttp3.ConnectionSpec
|
import okhttp3.ConnectionSpec
|
||||||
import okhttp3.Dns
|
import okhttp3.Dns
|
||||||
@ -68,7 +68,7 @@ fun createOkHttpClient(context: Context): OkHttpClient {
|
|||||||
builder.addInterceptor { chain ->
|
builder.addInterceptor { chain ->
|
||||||
val request = chain.request().newBuilder()
|
val request = chain.request().newBuilder()
|
||||||
request.header("User-Agent", "Magisk/${BuildConfig.APP_VERSION_CODE}")
|
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())
|
chain.proceed(request.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import android.content.*
|
|||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.drawable.AdaptiveIconDrawable
|
import android.graphics.drawable.AdaptiveIconDrawable
|
||||||
@ -19,8 +18,8 @@ import android.view.View
|
|||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.getSystemService
|
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.RootUtils
|
||||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
|
||||||
import com.topjohnwu.magisk.utils.APKInstall
|
import com.topjohnwu.magisk.utils.APKInstall
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -54,9 +53,7 @@ fun ApplicationInfo.getLabel(pm: PackageManager): String {
|
|||||||
runCatching {
|
runCatching {
|
||||||
if (labelRes > 0) {
|
if (labelRes > 0) {
|
||||||
val res = pm.getResourcesForApplication(this)
|
val res = pm.getResourcesForApplication(this)
|
||||||
val config = Configuration()
|
LocaleSetting.instance.updateResource(res)
|
||||||
config.setLocale(currentLocale)
|
|
||||||
res.updateConfiguration(config, res.displayMetrics)
|
|
||||||
return res.getString(labelRes)
|
return res.getString(labelRes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.core.ktx
|
package com.topjohnwu.magisk.core.ktx
|
||||||
|
|
||||||
import androidx.collection.SparseArrayCompat
|
import androidx.collection.SparseArrayCompat
|
||||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@ -17,6 +16,7 @@ import java.lang.reflect.Field
|
|||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
|
import java.util.Locale
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
|
|
||||||
@ -98,13 +98,13 @@ fun Long.toTime(format: DateFormat) = format.format(this).orEmpty()
|
|||||||
val timeFormatStandard by lazy {
|
val timeFormatStandard by lazy {
|
||||||
SimpleDateFormat(
|
SimpleDateFormat(
|
||||||
"yyyy-MM-dd'T'HH.mm.ss",
|
"yyyy-MM-dd'T'HH.mm.ss",
|
||||||
currentLocale
|
Locale.ROOT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val timeDateFormat: DateFormat by lazy {
|
val timeDateFormat: DateFormat by lazy {
|
||||||
DateFormat.getDateTimeInstance(
|
DateFormat.getDateTimeInstance(
|
||||||
DateFormat.DEFAULT,
|
DateFormat.DEFAULT,
|
||||||
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