Random refactoring

This commit is contained in:
topjohnwu 2021-01-23 13:26:28 -08:00
parent 2e0f7a82fa
commit 1060dd2906
7 changed files with 62 additions and 89 deletions

View File

@ -73,7 +73,7 @@ open class App() : Application() {
androidContext(wrapped) androidContext(wrapped)
modules(koinModules) modules(koinModules)
} }
ResMgr.init(impl) AssetHack.init(impl)
app.registerActivityLifecycleCallbacks(ForegroundTracker) app.registerActivityLifecycleCallbacks(ForegroundTracker)
WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build()) WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build())
} }

View File

@ -14,6 +14,7 @@ import android.content.Intent
import android.content.res.AssetManager 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 androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
@ -27,22 +28,21 @@ fun AssetManager.addAssetPath(path: String) {
DynAPK.addAssetPath(this, path) DynAPK.addAssetPath(this, path)
} }
fun Context.wrap(global: Boolean = true): Context = fun Context.wrap(inject: Boolean = false): Context =
if (global) GlobalResContext(this) else ResContext(this) if (inject) ReInjectedContext(this) else InjectedContext(this)
fun Context.wrapJob(): Context = object : GlobalResContext(this) { fun Context.wrapJob(): Context = object : InjectedContext(this) {
override fun getApplicationContext(): Context { override fun getApplicationContext() = this
return this
}
@SuppressLint("NewApi") @SuppressLint("NewApi")
override fun getSystemService(name: String): Any? { override fun getSystemService(name: String): Any? {
return if (!isRunningAsStub) super.getSystemService(name) else return super.getSystemService(name).let {
when (name) { when {
Context.JOB_SCHEDULER_SERVICE -> !isRunningAsStub -> it
JobSchedulerWrapper(super.getSystemService(name) as JobScheduler) name == JOB_SCHEDULER_SERVICE -> JobSchedulerWrapper(it as JobScheduler)
else -> super.getSystemService(name) else -> it
}
} }
} }
} }
@ -58,34 +58,27 @@ inline fun <reified T> Activity.redirect() = Intent(intent)
inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName)) inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName))
private open class GlobalResContext(base: Context) : ContextWrapper(base) { private open class InjectedContext(base: Context) : ContextWrapper(base) {
open val mRes: Resources get() = ResMgr.resource open val res: Resources get() = AssetHack.resource
override fun getAssets(): AssetManager = res.assets
override fun getResources(): Resources { override fun getResources() = res
return mRes override fun getClassLoader() = javaClass.classLoader!!
}
override fun getClassLoader(): ClassLoader {
return javaClass.classLoader!!
}
override fun createConfigurationContext(config: Configuration): Context { override fun createConfigurationContext(config: Configuration): Context {
return ResContext(super.createConfigurationContext(config)) return super.createConfigurationContext(config).wrap(true)
} }
} }
private class ResContext(base: Context) : GlobalResContext(base) { private class ReInjectedContext(base: Context) : InjectedContext(base) {
override val mRes by lazy { base.resources.patch() } override val res by lazy { base.resources.patch() }
private fun Resources.patch(): Resources { private fun Resources.patch(): Resources {
updateConfig() updateConfig()
if (isRunningAsStub) if (isRunningAsStub)
assets.addAssetPath(ResMgr.apk) assets.addAssetPath(AssetHack.apk)
return this return this
} }
} }
object ResMgr { object AssetHack {
lateinit var resource: Resources lateinit var resource: Resources
lateinit var apk: String lateinit var apk: String
@ -100,37 +93,27 @@ object ResMgr {
apk = context.packageResourcePath apk = context.packageResourcePath
} }
} }
fun newResource(): Resources {
val asset = AssetManager::class.java.newInstance()
asset.addAssetPath(apk)
val config = Configuration(resource.configuration)
val metrics = DisplayMetrics()
metrics.setTo(resource.displayMetrics)
return Resources(asset, metrics, config)
}
} }
@RequiresApi(28) @RequiresApi(28)
private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() { private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() {
override fun schedule(job: JobInfo) = base.schedule(job.patch())
override fun schedule(job: JobInfo): Int { override fun enqueue(job: JobInfo, work: JobWorkItem) = base.enqueue(job.patch(), work)
return base.schedule(job.patch()) override fun cancel(jobId: Int) = base.cancel(jobId)
} override fun cancelAll() = base.cancelAll()
override fun getAllPendingJobs(): List<JobInfo> = base.allPendingJobs
override fun enqueue(job: JobInfo, work: JobWorkItem): Int { override fun getPendingJob(jobId: Int) = base.getPendingJob(jobId)
return base.enqueue(job.patch(), work)
}
override fun cancel(jobId: Int) {
base.cancel(jobId)
}
override fun cancelAll() {
base.cancelAll()
}
override fun getAllPendingJobs(): List<JobInfo> {
return base.allPendingJobs
}
override fun getPendingJob(jobId: Int): JobInfo? {
return base.getPendingJob(jobId)
}
private fun JobInfo.patch(): JobInfo { private fun JobInfo.patch(): JobInfo {
// We need to swap out the service of JobInfo // Swap out the service of JobInfo
val name = service.className val name = service.className
val component = ComponentName( val component = ComponentName(
service.packageName, service.packageName,

View File

@ -34,7 +34,7 @@ abstract class BaseActivity : AppCompatActivity() {
} }
override fun attachBaseContext(base: Context) { override fun attachBaseContext(base: Context) {
super.attachBaseContext(base.wrap(false)) super.attachBaseContext(base.wrap(true))
} }
fun withPermission(permission: String, builder: PermissionRequestBuilder.() -> Unit) { fun withPermission(permission: String, builder: PermissionRequestBuilder.() -> Unit) {

View File

@ -3,20 +3,16 @@
package com.topjohnwu.magisk.core.utils package com.topjohnwu.magisk.core.utils
import android.annotation.SuppressLint import android.annotation.SuppressLint
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.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.AssetHack
import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.ResMgr
import com.topjohnwu.magisk.core.addAssetPath
import com.topjohnwu.magisk.ktx.langTagToLocale import com.topjohnwu.magisk.ktx.langTagToLocale
import com.topjohnwu.magisk.ktx.toLangTag import com.topjohnwu.magisk.ktx.toLangTag
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.* import java.util.*
import kotlin.Comparator
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
var currentLocale: Locale = Locale.getDefault() var currentLocale: Locale = Locale.getDefault()
@ -30,11 +26,8 @@ suspend fun availableLocales() = cachedLocales ?:
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
val compareId = R.string.app_changelog val compareId = R.string.app_changelog
// Create a completely new resource to prevent cross talk over app's configs // Create a completely new resource to prevent cross talk over active configs
val asset = AssetManager::class.java.newInstance().apply { addAssetPath(ResMgr.apk) } val res = AssetHack.newResource()
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 { val locales = ArrayList<String>().apply {
// Add default locale // Add default locale
@ -49,15 +42,13 @@ withContext(Dispatchers.Default) {
}.map { }.map {
it.langTagToLocale() it.langTagToLocale()
}.distinctBy { }.distinctBy {
config.setLocale(it) res.updateLocale(it)
res.updateConfiguration(config, metrics)
res.getString(compareId) res.getString(compareId)
}.sortedWith(Comparator { a, b -> }.sortedWith { a, b ->
a.getDisplayName(a).compareTo(b.getDisplayName(b), true) a.getDisplayName(a).compareTo(b.getDisplayName(b), true)
}) }
config.setLocale(defaultLocale) res.updateLocale(defaultLocale)
res.updateConfiguration(config, metrics)
val defName = res.getString(R.string.system_default) val defName = res.getString(R.string.system_default)
val names = ArrayList<String>(locales.size + 1) val names = ArrayList<String>(locales.size + 1)
@ -79,6 +70,11 @@ fun Resources.updateConfig(config: Configuration = configuration) {
updateConfiguration(config, displayMetrics) updateConfiguration(config, displayMetrics)
} }
fun Resources.updateLocale(locale: Locale) {
configuration.setLocale(locale)
updateConfiguration(configuration, displayMetrics)
}
fun refreshLocale() { fun refreshLocale() {
val localeConfig = Config.locale val localeConfig = Config.locale
currentLocale = when { currentLocale = when {
@ -86,5 +82,5 @@ fun refreshLocale() {
else -> localeConfig.langTagToLocale() else -> localeConfig.langTagToLocale()
} }
Locale.setDefault(currentLocale) Locale.setDefault(currentLocale)
ResMgr.resource.updateConfig() AssetHack.resource.updateConfig()
} }

View File

@ -1,9 +1,9 @@
package com.topjohnwu.magisk.di package com.topjohnwu.magisk.di
import android.content.Context import android.content.Context
import android.os.Build
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.topjohnwu.magisk.core.ResMgr import com.topjohnwu.magisk.core.AssetHack
import com.topjohnwu.magisk.ktx.deviceProtectedContext
import org.koin.core.qualifier.named import org.koin.core.qualifier.named
import org.koin.dsl.module import org.koin.dsl.module
@ -11,15 +11,9 @@ val SUTimeout = named("su_timeout")
val Protected = named("protected") val Protected = named("protected")
val applicationModule = module { val applicationModule = module {
factory { ResMgr.resource } factory { AssetHack.resource }
factory { get<Context>().packageManager } factory { get<Context>().packageManager }
factory(Protected) { createDEContext(get()) } factory(Protected) { get<Context>().deviceProtectedContext }
single(SUTimeout) { get<Context>(Protected).getSharedPreferences("su_timeout", 0) } single(SUTimeout) { get<Context>(Protected).getSharedPreferences("su_timeout", 0) }
single { PreferenceManager.getDefaultSharedPreferences(get<Context>(Protected)) } single { PreferenceManager.getDefaultSharedPreferences(get(Protected)) }
}
private fun createDEContext(context: Context): Context {
return if (Build.VERSION.SDK_INT >= 24)
context.createDeviceProtectedStorageContext()
else context
} }

View File

@ -46,7 +46,7 @@ import androidx.transition.AutoTransition
import androidx.transition.TransitionManager import androidx.transition.TransitionManager
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.ResMgr import com.topjohnwu.magisk.core.AssetHack
import com.topjohnwu.magisk.core.utils.currentLocale import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.utils.DynamicClassLoader import com.topjohnwu.magisk.utils.DynamicClassLoader
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
@ -366,6 +366,6 @@ var TextView.precomputedText: CharSequence
} }
fun Int.dpInPx(): Int { fun Int.dpInPx(): Int {
val scale = ResMgr.resource.displayMetrics.density val scale = AssetHack.resource.displayMetrics.density
return (this * scale + 0.5).toInt() return (this * scale + 0.5).toInt()
} }

View File

@ -15,7 +15,7 @@ import android.widget.TextView
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import com.caverock.androidsvg.SVG import com.caverock.androidsvg.SVG
import com.caverock.androidsvg.SVGParseException import com.caverock.androidsvg.SVGParseException
import com.topjohnwu.magisk.core.ResMgr import com.topjohnwu.magisk.core.AssetHack
import com.topjohnwu.superuser.internal.WaitRunnable import com.topjohnwu.superuser.internal.WaitRunnable
import io.noties.markwon.AbstractMarkwonPlugin import io.noties.markwon.AbstractMarkwonPlugin
import io.noties.markwon.MarkwonSpansFactory import io.noties.markwon.MarkwonSpansFactory
@ -216,7 +216,7 @@ class MarkwonImagePlugin(okHttp: OkHttpClient) : AbstractMarkwonPlugin() {
return PictureDrawable(picture) return PictureDrawable(picture)
} }
val density: Float = ResMgr.resource.displayMetrics.density val density: Float = AssetHack.resource.displayMetrics.density
val width = (w * density + .5f).toInt() val width = (w * density + .5f).toInt()
val height = (h * density + .5f).toInt() val height = (h * density + .5f).toInt()
@ -226,7 +226,7 @@ class MarkwonImagePlugin(okHttp: OkHttpClient) : AbstractMarkwonPlugin() {
canvas.scale(density, density) canvas.scale(density, density)
svg.renderToCanvas(canvas) svg.renderToCanvas(canvas)
return BitmapDrawable(ResMgr.resource, bitmap) return BitmapDrawable(AssetHack.resource, bitmap)
} }
} }