diff --git a/app/src/main/java/com/topjohnwu/magisk/core/App.kt b/app/src/main/java/com/topjohnwu/magisk/core/App.kt index b1f998589..56c3a05f0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/App.kt @@ -7,12 +7,8 @@ import android.content.Context import android.content.res.Configuration import android.os.Bundle import com.topjohnwu.magisk.DynAPK -import com.topjohnwu.magisk.core.utils.DispatcherExecutor -import com.topjohnwu.magisk.core.utils.RootRegistry -import com.topjohnwu.magisk.core.utils.ShellInit -import com.topjohnwu.magisk.core.utils.updateConfig +import com.topjohnwu.magisk.core.utils.* import com.topjohnwu.magisk.di.ServiceLocator -import com.topjohnwu.magisk.ktx.unwrap import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.internal.UiThreadHandler import com.topjohnwu.superuser.ipc.RootService @@ -40,28 +36,34 @@ open class App() : Application() { } } - override fun attachBaseContext(base: Context) { + override fun attachBaseContext(context: Context) { Shell.setDefaultBuilder(Shell.Builder.create() .setFlags(Shell.FLAG_MOUNT_MASTER) .setInitializers(ShellInit::class.java) .setTimeout(2)) Shell.EXECUTOR = DispatcherExecutor(Dispatchers.IO) - // Some context magic + // Get the actual ContextImpl val app: Application - val impl: Context - if (base is Application) { - app = base - impl = base.baseContext + val base: Context + if (context is Application) { + app = context + base = context.baseContext } else { app = this - impl = base + base = context } - val wrapped = impl.wrap() - super.attachBaseContext(wrapped) + super.attachBaseContext(base) + ServiceLocator.context = base - ServiceLocator.context = wrapped - AssetHack.init(impl) + refreshLocale() + AppApkPath = if (isRunningAsStub) { + DynAPK.current(base).path + } else { + base.packageResourcePath + } + + base.resources.patch() app.registerActivityLifecycleCallbacks(ForegroundTracker) } @@ -74,14 +76,9 @@ open class App() : Application() { ) } - // This is required as some platforms expect ContextImpl - override fun getBaseContext(): Context { - return super.getBaseContext().unwrap() - } - override fun onConfigurationChanged(newConfig: Configuration) { if (resources.configuration.diff(newConfig) != 0) { - resources.updateConfig(newConfig) + resources.setConfig(newConfig) } if (!isRunningAsStub) super.onConfigurationChanged(newConfig) diff --git a/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt b/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt index 039ebebc9..61ae01667 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/Hacks.kt @@ -13,15 +13,37 @@ import android.content.res.Resources import android.util.DisplayMetrics import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.core.utils.refreshLocale -import com.topjohnwu.magisk.core.utils.updateConfig +import com.topjohnwu.magisk.core.utils.syncLocale +import com.topjohnwu.magisk.di.AppContext -fun AssetManager.addAssetPath(path: String) { - DynAPK.addAssetPath(this, path) +lateinit var AppApkPath: String + +fun AssetManager.addAssetPath(path: String) = DynAPK.addAssetPath(this, path) + +fun Context.wrap(): Context = if (this is PatchedContext) this else PatchedContext(this) + +private class PatchedContext(base: Context) : ContextWrapper(base) { + init { base.resources.patch() } + override fun getClassLoader() = javaClass.classLoader!! + override fun createConfigurationContext(config: Configuration) = + super.createConfigurationContext(config).wrap() } -fun Context.wrap(inject: Boolean = false): Context = - if (inject) ReInjectedContext(this) else InjectedContext(this) +fun Resources.patch(): Resources { + syncLocale() + if (isRunningAsStub) + assets.addAssetPath(AppApkPath) + return this +} + +fun createNewResources(): Resources { + val asset = AssetManager::class.java.newInstance() + asset.addAssetPath(AppApkPath) + val config = Configuration(AppContext.resources.configuration) + val metrics = DisplayMetrics() + metrics.setTo(AppContext.resources.displayMetrics) + return Resources(asset, metrics, config) +} fun Class<*>.cmp(pkg: String) = ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name) @@ -32,52 +54,6 @@ inline fun Activity.redirect() = Intent(intent) inline fun Context.intent() = Intent().setComponent(T::class.java.cmp(packageName)) -private open class InjectedContext(base: Context) : ContextWrapper(base) { - open val res: Resources get() = AssetHack.resource - override fun getAssets(): AssetManager = res.assets - override fun getResources() = res - override fun getClassLoader() = javaClass.classLoader!! - override fun createConfigurationContext(config: Configuration): Context { - return super.createConfigurationContext(config).wrap(true) - } -} - -private class ReInjectedContext(base: Context) : InjectedContext(base) { - override val res by lazy { base.resources.patch() } - private fun Resources.patch(): Resources { - updateConfig() - if (isRunningAsStub) - assets.addAssetPath(AssetHack.apk) - return this - } -} - -object AssetHack { - - lateinit var resource: Resources - lateinit var apk: String - - fun init(context: Context) { - resource = context.resources - refreshLocale() - if (isRunningAsStub) { - apk = DynAPK.current(context).path - resource.assets.addAssetPath(apk) - } else { - 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) - } -} - // Keep a reference to these resources to prevent it from // being removed when running "remove unused resources" val shouldKeepResources = listOf( diff --git a/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt b/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt index a206b1592..ca0ba8222 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt @@ -36,7 +36,7 @@ abstract class BaseActivity : AppCompatActivity() { } override fun attachBaseContext(base: Context) { - super.attachBaseContext(base.wrap(true)) + super.attachBaseContext(base.wrap()) } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt b/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt index 9d5b6a564..0abe51e02 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt @@ -422,7 +422,7 @@ abstract class MagiskInstallImpl protected constructor( protected fun fixEnv() = extractFiles() && "fix_env $installDir".sh().isSuccess - protected fun uninstall() = "run_uninstaller ${AssetHack.apk}".sh().isSuccess + protected fun uninstall() = "run_uninstaller $AppApkPath".sh().isSuccess @WorkerThread protected abstract suspend fun operations(): Boolean diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/Locales.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/Locales.kt index 69f68fe52..7ec5691c5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/utils/Locales.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/Locales.kt @@ -6,8 +6,9 @@ import android.annotation.SuppressLint import android.content.res.Configuration import android.content.res.Resources import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.core.AssetHack import com.topjohnwu.magisk.core.Config +import com.topjohnwu.magisk.core.createNewResources +import com.topjohnwu.magisk.di.AppContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.util.* @@ -25,7 +26,7 @@ withContext(Dispatchers.Default) { val compareId = R.string.app_changelog // Create a completely new resource to prevent cross talk over active configs - val res = AssetHack.newResource() + val res = createNewResources() val locales = ArrayList().apply { // Add default locale @@ -40,13 +41,13 @@ withContext(Dispatchers.Default) { }.map { Locale.forLanguageTag(it) }.distinctBy { - res.updateLocale(it) + res.setLocale(it) res.getString(compareId) }.sortedWith { a, b -> a.getDisplayName(a).compareTo(b.getDisplayName(b), true) } - res.updateLocale(defaultLocale) + res.setLocale(defaultLocale) val defName = res.getString(R.string.system_default) val names = ArrayList(locales.size + 1) @@ -63,12 +64,14 @@ withContext(Dispatchers.Default) { (names.toTypedArray() to values.toTypedArray()).also { cachedLocales = it } } -fun Resources.updateConfig(config: Configuration = configuration) { +fun Resources.setConfig(config: Configuration) { config.setLocale(currentLocale) updateConfiguration(config, displayMetrics) } -fun Resources.updateLocale(locale: Locale) { +fun Resources.syncLocale() = setConfig(configuration) + +fun Resources.setLocale(locale: Locale) { configuration.setLocale(locale) updateConfiguration(configuration, displayMetrics) } @@ -80,5 +83,5 @@ fun refreshLocale() { else -> Locale.forLanguageTag(localeConfig) } Locale.setDefault(currentLocale) - AssetHack.resource.updateConfig() + AppContext.resources.syncLocale() } diff --git a/app/src/main/java/com/topjohnwu/magisk/ktx/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/ktx/XAndroid.kt index 875122013..d3e65c361 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ktx/XAndroid.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ktx/XAndroid.kt @@ -41,10 +41,10 @@ import androidx.lifecycle.lifecycleScope import androidx.transition.AutoTransition import androidx.transition.TransitionManager import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.core.AssetHack import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.utils.currentLocale +import com.topjohnwu.magisk.di.AppContext import com.topjohnwu.magisk.utils.DynamicClassLoader import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.superuser.Shell @@ -342,7 +342,7 @@ var TextView.precomputedText: CharSequence } fun Int.dpInPx(): Int { - val scale = AssetHack.resource.displayMetrics.density + val scale = AppContext.resources.displayMetrics.density return (this * scale + 0.5).toInt() } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/MarkwonImagePlugin.kt b/app/src/main/java/com/topjohnwu/magisk/utils/MarkwonImagePlugin.kt index 1e5a4e14f..f87de7c07 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/MarkwonImagePlugin.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/MarkwonImagePlugin.kt @@ -15,7 +15,7 @@ import android.widget.TextView import androidx.annotation.WorkerThread import com.caverock.androidsvg.SVG import com.caverock.androidsvg.SVGParseException -import com.topjohnwu.magisk.core.AssetHack +import com.topjohnwu.magisk.di.AppContext import com.topjohnwu.superuser.internal.WaitRunnable import io.noties.markwon.AbstractMarkwonPlugin import io.noties.markwon.MarkwonSpansFactory @@ -216,7 +216,7 @@ class MarkwonImagePlugin(okHttp: OkHttpClient) : AbstractMarkwonPlugin() { return PictureDrawable(picture) } - val density: Float = AssetHack.resource.displayMetrics.density + val density: Float = AppContext.resources.displayMetrics.density val width = (w * density + .5f).toInt() val height = (h * density + .5f).toInt() @@ -226,7 +226,7 @@ class MarkwonImagePlugin(okHttp: OkHttpClient) : AbstractMarkwonPlugin() { canvas.scale(density, density) svg.renderToCanvas(canvas) - return BitmapDrawable(AssetHack.resource, bitmap) + return BitmapDrawable(AppContext.resources, bitmap) } }