mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 07:57:39 +00:00
Obfuscate WorkManager components
Remove unused components and hack the context sent into WorkManager
This commit is contained in:
parent
a910c8ccd8
commit
9f9de8c43b
@ -97,6 +97,12 @@
|
||||
android:name="com.google.android.gms.version"
|
||||
android:value="12451000" />
|
||||
|
||||
<!-- Initialize WorkManager on-demand -->
|
||||
<provider
|
||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||
android:authorities="${applicationId}.workmanager-init"
|
||||
tools:node="remove" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
@ -6,8 +6,8 @@ import androidx.annotation.NonNull;
|
||||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
|
||||
import com.topjohnwu.magisk.HacksKt;
|
||||
import com.topjohnwu.magisk.base.DelegateWorker;
|
||||
import com.topjohnwu.magisk.utils.ResourceMgrKt;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
|
||||
@ -19,7 +19,7 @@ public abstract class w<T extends DelegateWorker> extends Worker {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
w(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||
super(ResourceMgrKt.wrap(context, false), workerParams);
|
||||
super(HacksKt.wrap(context, false), workerParams);
|
||||
try {
|
||||
base = ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
|
||||
.getActualTypeArguments()[0]).newInstance();
|
||||
|
@ -6,6 +6,7 @@ import android.content.res.Configuration
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.multidex.MultiDex
|
||||
import androidx.room.Room
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.impl.WorkDatabase
|
||||
import androidx.work.impl.WorkDatabase_Impl
|
||||
import com.topjohnwu.magisk.data.database.RepoDatabase
|
||||
@ -14,10 +15,7 @@ import com.topjohnwu.magisk.di.ActivityTracker
|
||||
import com.topjohnwu.magisk.di.koinModules
|
||||
import com.topjohnwu.magisk.extensions.get
|
||||
import com.topjohnwu.magisk.extensions.unwrap
|
||||
import com.topjohnwu.magisk.utils.ResourceMgr
|
||||
import com.topjohnwu.magisk.utils.RootInit
|
||||
import com.topjohnwu.magisk.utils.isRunningAsStub
|
||||
import com.topjohnwu.magisk.utils.wrap
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.context.startKoin
|
||||
@ -72,6 +70,7 @@ open class App() : Application() {
|
||||
}
|
||||
ResourceMgr.reload()
|
||||
app.registerActivityLifecycleCallbacks(get<ActivityTracker>())
|
||||
WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build())
|
||||
}
|
||||
|
||||
// This is required as some platforms expect ContextImpl
|
||||
|
@ -1,44 +0,0 @@
|
||||
package com.topjohnwu.magisk
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.topjohnwu.magisk.model.download.DownloadService
|
||||
import com.topjohnwu.magisk.model.receiver.GeneralReceiver
|
||||
import com.topjohnwu.magisk.model.update.UpdateCheckService
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.ui.SplashActivity
|
||||
import com.topjohnwu.magisk.ui.flash.FlashActivity
|
||||
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
||||
|
||||
object ClassMap {
|
||||
|
||||
private val classMap = mapOf(
|
||||
App::class.java to a.e::class.java,
|
||||
MainActivity::class.java to a.b::class.java,
|
||||
SplashActivity::class.java to a.c::class.java,
|
||||
FlashActivity::class.java to a.f::class.java,
|
||||
UpdateCheckService::class.java to a.g::class.java,
|
||||
GeneralReceiver::class.java to a.h::class.java,
|
||||
DownloadService::class.java to a.j::class.java,
|
||||
SuRequestActivity::class.java to a.m::class.java
|
||||
)
|
||||
|
||||
// This will be set if running as guest app
|
||||
var componentMap: Map<String, String>? = null
|
||||
|
||||
operator fun get(c: Class<*>) = classMap.getOrElse(c) { throw IllegalArgumentException() }
|
||||
}
|
||||
|
||||
fun Class<*>.cmp(pkg: String = BuildConfig.APPLICATION_ID): ComponentName {
|
||||
val name = ClassMap[this].name
|
||||
return ComponentName(pkg, ClassMap.componentMap?.get(name) ?: name)
|
||||
}
|
||||
|
||||
fun Context.intent(c: Class<*>): Intent {
|
||||
val cls = ClassMap[c]
|
||||
return ClassMap.componentMap?.let {
|
||||
val className = it.getOrElse(cls.name) { cls.name }
|
||||
Intent().setComponent(ComponentName(this, className))
|
||||
} ?: Intent(this, cls)
|
||||
}
|
223
app/src/main/java/com/topjohnwu/magisk/Hacks.kt
Normal file
223
app/src/main/java/com/topjohnwu/magisk/Hacks.kt
Normal file
@ -0,0 +1,223 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.topjohnwu.magisk
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.job.JobInfo
|
||||
import android.app.job.JobScheduler
|
||||
import android.app.job.JobWorkItem
|
||||
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 androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StringRes
|
||||
import com.topjohnwu.magisk.extensions.langTagToLocale
|
||||
import com.topjohnwu.magisk.model.download.DownloadService
|
||||
import com.topjohnwu.magisk.model.receiver.GeneralReceiver
|
||||
import com.topjohnwu.magisk.model.update.UpdateCheckService
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.ui.SplashActivity
|
||||
import com.topjohnwu.magisk.ui.flash.FlashActivity
|
||||
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
||||
import com.topjohnwu.magisk.utils.DynAPK
|
||||
import com.topjohnwu.magisk.utils.currentLocale
|
||||
import com.topjohnwu.magisk.utils.defaultLocale
|
||||
import java.util.*
|
||||
|
||||
var isRunningAsStub = false
|
||||
|
||||
private val addAssetPath by lazy {
|
||||
AssetManager::class.java.getMethod("addAssetPath", String::class.java)
|
||||
}
|
||||
|
||||
fun AssetManager.addAssetPath(path: String) {
|
||||
addAssetPath.invoke(this, path)
|
||||
}
|
||||
|
||||
fun Context.wrap(global: Boolean = true): Context
|
||||
= if (!global) ResContext(this) else GlobalResContext(this)
|
||||
|
||||
fun Context.wrapJob(): Context = object : GlobalResContext(this) {
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
override fun getSystemService(name: String): Any? {
|
||||
return if (!isRunningAsStub) super.getSystemService(name) else
|
||||
when (name) {
|
||||
Context.JOB_SCHEDULER_SERVICE ->
|
||||
JobSchedulerWrapper(super.getSystemService(name) as JobScheduler)
|
||||
else -> super.getSystemService(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override locale and inject resources from dynamic APK
|
||||
private fun Resources.patch(config: Configuration = Configuration(configuration)): Resources {
|
||||
config.setLocale(currentLocale)
|
||||
updateConfiguration(config, displayMetrics)
|
||||
if (isRunningAsStub)
|
||||
assets.addAssetPath(ResourceMgr.resApk)
|
||||
return this
|
||||
}
|
||||
|
||||
fun Class<*>.cmp(pkg: String = BuildConfig.APPLICATION_ID): ComponentName {
|
||||
val name = ClassMap[this].name
|
||||
return ComponentName(pkg, ClassMap.componentMap?.get(name)
|
||||
?: name)
|
||||
}
|
||||
|
||||
fun Context.intent(c: Class<*>): Intent {
|
||||
val cls = ClassMap[c]
|
||||
return ClassMap.componentMap?.let {
|
||||
val className = it.getOrElse(cls.name) { cls.name }
|
||||
Intent().setComponent(ComponentName(this, className))
|
||||
} ?: Intent(this, cls)
|
||||
}
|
||||
|
||||
private open class GlobalResContext(base: Context) : ContextWrapper(base) {
|
||||
open val mRes: Resources get() = ResourceMgr.resource
|
||||
private val loader by lazy { javaClass.classLoader!! }
|
||||
|
||||
override fun getApplicationContext(): Context {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun getResources(): Resources {
|
||||
return mRes
|
||||
}
|
||||
|
||||
override fun getClassLoader(): ClassLoader {
|
||||
return loader
|
||||
}
|
||||
|
||||
override fun createConfigurationContext(config: Configuration): Context {
|
||||
return ResContext(super.createConfigurationContext(config))
|
||||
}
|
||||
}
|
||||
|
||||
private class ResContext(base: Context) : GlobalResContext(base) {
|
||||
override val mRes by lazy { base.resources.patch() }
|
||||
}
|
||||
|
||||
object ResourceMgr {
|
||||
|
||||
internal lateinit var resource: Resources
|
||||
internal lateinit var resApk: String
|
||||
|
||||
fun init(context: Context) {
|
||||
resource = context.resources
|
||||
if (isRunningAsStub)
|
||||
resApk = DynAPK.current(context).path
|
||||
}
|
||||
|
||||
fun reload(config: Configuration = Configuration(resource.configuration)) {
|
||||
val localeConfig = Config.locale
|
||||
currentLocale = when {
|
||||
localeConfig.isEmpty() -> defaultLocale
|
||||
else -> localeConfig.langTagToLocale()
|
||||
}
|
||||
Locale.setDefault(currentLocale)
|
||||
resource.patch(config)
|
||||
}
|
||||
|
||||
fun getString(locale: Locale, @StringRes id: Int): String {
|
||||
val config = Configuration()
|
||||
config.setLocale(locale)
|
||||
return Resources(resource.assets, resource.displayMetrics, config).getString(id)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@RequiresApi(api = 28)
|
||||
private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() {
|
||||
|
||||
override fun schedule(job: JobInfo): Int {
|
||||
return base.schedule(job.patch())
|
||||
}
|
||||
|
||||
override fun enqueue(job: JobInfo, work: JobWorkItem): Int {
|
||||
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)
|
||||
}
|
||||
|
||||
fun JobInfo.patch(): JobInfo {
|
||||
// We need to patch the component of JobInfo to access WorkManager SystemJobService
|
||||
|
||||
val name = service.className
|
||||
val component = ComponentName(service.packageName, ClassMap.componentMap?.get(name)
|
||||
?: name)
|
||||
|
||||
// Clone the JobInfo except component
|
||||
val builder = JobInfo.Builder(id, component)
|
||||
.setExtras(extras)
|
||||
.setTransientExtras(transientExtras)
|
||||
.setClipData(clipData, clipGrantFlags)
|
||||
.setRequiredNetwork(requiredNetwork)
|
||||
.setEstimatedNetworkBytes(estimatedNetworkDownloadBytes, estimatedNetworkUploadBytes)
|
||||
.setRequiresCharging(isRequireCharging)
|
||||
.setRequiresDeviceIdle(isRequireDeviceIdle)
|
||||
.setRequiresBatteryNotLow(isRequireBatteryNotLow)
|
||||
.setRequiresStorageNotLow(isRequireStorageNotLow)
|
||||
.also {
|
||||
triggerContentUris?.let { uris ->
|
||||
for (uri in uris)
|
||||
it.addTriggerContentUri(uri)
|
||||
}
|
||||
}
|
||||
.setTriggerContentUpdateDelay(triggerContentUpdateDelay)
|
||||
.setTriggerContentMaxDelay(triggerContentMaxDelay)
|
||||
.setImportantWhileForeground(isImportantWhileForeground)
|
||||
.setPrefetch(isPrefetch)
|
||||
.setPersisted(isPersisted)
|
||||
|
||||
if (isPeriodic) {
|
||||
builder.setPeriodic(intervalMillis, flexMillis)
|
||||
} else {
|
||||
if (minLatencyMillis > 0)
|
||||
builder.setMinimumLatency(minLatencyMillis)
|
||||
if (maxExecutionDelayMillis > 0)
|
||||
builder.setOverrideDeadline(maxExecutionDelayMillis)
|
||||
}
|
||||
if (!isRequireDeviceIdle)
|
||||
builder.setBackoffCriteria(initialBackoffMillis, backoffPolicy)
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
object ClassMap {
|
||||
|
||||
private val classMap = mapOf(
|
||||
App::class.java to a.e::class.java,
|
||||
MainActivity::class.java to a.b::class.java,
|
||||
SplashActivity::class.java to a.c::class.java,
|
||||
FlashActivity::class.java to a.f::class.java,
|
||||
UpdateCheckService::class.java to a.g::class.java,
|
||||
GeneralReceiver::class.java to a.h::class.java,
|
||||
DownloadService::class.java to a.j::class.java,
|
||||
SuRequestActivity::class.java to a.m::class.java
|
||||
)
|
||||
|
||||
// This will be set if running as guest app
|
||||
var componentMap: Map<String, String>? = null
|
||||
|
||||
operator fun get(c: Class<*>) = classMap.getOrElse(c) { throw IllegalArgumentException() }
|
||||
}
|
@ -21,7 +21,7 @@ import com.topjohnwu.magisk.extensions.set
|
||||
import com.topjohnwu.magisk.model.events.EventHandler
|
||||
import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder
|
||||
import com.topjohnwu.magisk.utils.currentLocale
|
||||
import com.topjohnwu.magisk.utils.wrap
|
||||
import com.topjohnwu.magisk.wrap
|
||||
import kotlin.random.Random
|
||||
|
||||
typealias RequestCallback = BaseActivity<*, *>.(Int, Intent?) -> Unit
|
||||
|
@ -4,7 +4,7 @@ import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import com.topjohnwu.magisk.utils.wrap
|
||||
import com.topjohnwu.magisk.wrap
|
||||
import org.koin.core.KoinComponent
|
||||
|
||||
abstract class BaseReceiver : BroadcastReceiver(), KoinComponent {
|
||||
|
@ -2,7 +2,7 @@ package com.topjohnwu.magisk.base
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import com.topjohnwu.magisk.utils.wrap
|
||||
import com.topjohnwu.magisk.wrap
|
||||
import org.koin.core.KoinComponent
|
||||
|
||||
abstract class BaseService : Service(), KoinComponent {
|
||||
|
@ -20,7 +20,7 @@ import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
|
||||
import com.topjohnwu.magisk.ui.flash.FlashActivity
|
||||
import com.topjohnwu.magisk.utils.APKInstall
|
||||
import com.topjohnwu.magisk.utils.DynAPK
|
||||
import com.topjohnwu.magisk.utils.isRunningAsStub
|
||||
import com.topjohnwu.magisk.isRunningAsStub
|
||||
import org.koin.core.get
|
||||
import java.io.File
|
||||
import kotlin.random.Random.Default.nextInt
|
||||
|
@ -12,7 +12,7 @@ import com.topjohnwu.magisk.extensions.writeTo
|
||||
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
|
||||
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
|
||||
import com.topjohnwu.magisk.utils.ProgressInputStream
|
||||
import com.topjohnwu.magisk.utils.isRunningAsStub
|
||||
import com.topjohnwu.magisk.isRunningAsStub
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import io.reactivex.Completable
|
||||
|
@ -7,7 +7,7 @@ import android.text.TextUtils
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.topjohnwu.magisk.*
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.wrap
|
||||
import com.topjohnwu.magisk.wrap
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import com.topjohnwu.magisk.view.Shortcuts
|
||||
import com.topjohnwu.superuser.Shell
|
||||
|
@ -13,10 +13,7 @@ import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.Config
|
||||
import com.topjohnwu.magisk.Const
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.*
|
||||
import com.topjohnwu.magisk.base.BasePreferenceFragment
|
||||
import com.topjohnwu.magisk.data.database.RepoDao
|
||||
import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding
|
||||
|
49
app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt
Normal file
49
app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt
Normal file
@ -0,0 +1,49 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.ResourceMgr
|
||||
import com.topjohnwu.magisk.extensions.langTagToLocale
|
||||
import io.reactivex.Single
|
||||
import java.util.*
|
||||
import kotlin.Comparator
|
||||
|
||||
var currentLocale: Locale = Locale.getDefault()
|
||||
|
||||
@SuppressLint("ConstantLocale")
|
||||
val defaultLocale: Locale = Locale.getDefault()
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val availableLocales = Single.fromCallable {
|
||||
val compareId = R.string.app_changelog
|
||||
mutableListOf<Locale>().apply {
|
||||
// Add default locale
|
||||
add(Locale.ENGLISH)
|
||||
|
||||
// Add some special locales
|
||||
add(Locale.TAIWAN)
|
||||
add(Locale("pt", "BR"))
|
||||
|
||||
val config = Configuration()
|
||||
val metrics = ResourceMgr.resource.displayMetrics
|
||||
val res = Resources(ResourceMgr.resource.assets, metrics, config)
|
||||
|
||||
// Other locales
|
||||
val otherLocales = ResourceMgr.resource.assets.locales
|
||||
.map { it.langTagToLocale() }
|
||||
.distinctBy {
|
||||
config.setLocale(it)
|
||||
res.updateConfiguration(config, metrics)
|
||||
res.getString(compareId)
|
||||
}
|
||||
|
||||
listOf("", "").toTypedArray()
|
||||
|
||||
addAll(otherLocales)
|
||||
}.sortedWith(Comparator { a, b ->
|
||||
a.getDisplayName(a).toLowerCase(a)
|
||||
.compareTo(b.getDisplayName(b).toLowerCase(b))
|
||||
})
|
||||
}.cache()!!
|
@ -1,126 +0,0 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.res.AssetManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.StringRes
|
||||
import com.topjohnwu.magisk.Config
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.extensions.langTagToLocale
|
||||
import io.reactivex.Single
|
||||
import java.util.*
|
||||
|
||||
var isRunningAsStub = false
|
||||
|
||||
var currentLocale: Locale = Locale.getDefault()
|
||||
private set
|
||||
|
||||
@SuppressLint("ConstantLocale")
|
||||
val defaultLocale: Locale = Locale.getDefault()
|
||||
|
||||
val availableLocales = Single.fromCallable {
|
||||
val compareId = R.string.app_changelog
|
||||
mutableListOf<Locale>().apply {
|
||||
// Add default locale
|
||||
add(Locale.ENGLISH)
|
||||
|
||||
// Add some special locales
|
||||
add(Locale.TAIWAN)
|
||||
add(Locale("pt", "BR"))
|
||||
|
||||
val config = Configuration()
|
||||
val metrics = ResourceMgr.resource.displayMetrics
|
||||
val res = Resources(ResourceMgr.resource.assets, metrics, config)
|
||||
|
||||
// Other locales
|
||||
val otherLocales = ResourceMgr.resource.assets.locales
|
||||
.map { it.langTagToLocale() }
|
||||
.distinctBy {
|
||||
config.setLocale(it)
|
||||
res.updateConfiguration(config, metrics)
|
||||
res.getString(compareId)
|
||||
}
|
||||
|
||||
listOf("", "").toTypedArray()
|
||||
|
||||
addAll(otherLocales)
|
||||
}.sortedWith(Comparator { a, b ->
|
||||
a.getDisplayName(a).toLowerCase(a)
|
||||
.compareTo(b.getDisplayName(b).toLowerCase(b))
|
||||
})
|
||||
}.cache()!!
|
||||
|
||||
private val addAssetPath by lazy {
|
||||
AssetManager::class.java.getMethod("addAssetPath", String::class.java)
|
||||
}
|
||||
|
||||
fun AssetManager.addAssetPath(path: String) {
|
||||
addAssetPath.invoke(this, path)
|
||||
}
|
||||
|
||||
fun Context.wrap(global: Boolean = true): Context
|
||||
= if (!global) ResourceMgr.ResContext(this) else ResourceMgr.GlobalResContext(this)
|
||||
|
||||
object ResourceMgr {
|
||||
|
||||
lateinit var resource: Resources
|
||||
private lateinit var resApk: String
|
||||
|
||||
fun init(context: Context) {
|
||||
resource = context.resources
|
||||
if (isRunningAsStub)
|
||||
resApk = DynAPK.current(context).path
|
||||
}
|
||||
|
||||
// Override locale and inject resources from dynamic APK
|
||||
private fun Resources.patch(config: Configuration = Configuration(configuration)): Resources {
|
||||
config.setLocale(currentLocale)
|
||||
updateConfiguration(config, displayMetrics)
|
||||
if (isRunningAsStub)
|
||||
assets.addAssetPath(resApk)
|
||||
return this
|
||||
}
|
||||
|
||||
fun reload(config: Configuration = Configuration(resource.configuration)) {
|
||||
val localeConfig = Config.locale
|
||||
currentLocale = when {
|
||||
localeConfig.isEmpty() -> defaultLocale
|
||||
else -> localeConfig.langTagToLocale()
|
||||
}
|
||||
Locale.setDefault(currentLocale)
|
||||
resource.patch(config)
|
||||
}
|
||||
|
||||
fun getString(locale: Locale, @StringRes id: Int): String {
|
||||
val config = Configuration()
|
||||
config.setLocale(locale)
|
||||
return Resources(resource.assets, resource.displayMetrics, config).getString(id)
|
||||
}
|
||||
|
||||
open class GlobalResContext(base: Context) : ContextWrapper(base) {
|
||||
open val mRes: Resources get() = resource
|
||||
private val loader by lazy { javaClass.classLoader!! }
|
||||
|
||||
override fun getResources(): Resources {
|
||||
return mRes
|
||||
}
|
||||
|
||||
override fun getClassLoader(): ClassLoader {
|
||||
return loader
|
||||
}
|
||||
|
||||
override fun createConfigurationContext(config: Configuration): Context {
|
||||
return ResContext(super.createConfigurationContext(config))
|
||||
}
|
||||
}
|
||||
|
||||
class ResContext(base: Context) : GlobalResContext(base) {
|
||||
override val mRes by lazy { base.resources.patch() }
|
||||
}
|
||||
|
||||
}
|
@ -5,6 +5,7 @@ import com.topjohnwu.magisk.Const
|
||||
import com.topjohnwu.magisk.Info
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.extensions.rawResource
|
||||
import com.topjohnwu.magisk.wrap
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import com.topjohnwu.superuser.io.SuFile
|
||||
|
@ -75,36 +75,14 @@
|
||||
android:name="com.google.android.gms.version"
|
||||
android:value="12451000" />
|
||||
|
||||
<!-- WorkManager -->
|
||||
|
||||
<provider
|
||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||
android:authorities="${applicationId}.workmanager-init"
|
||||
android:directBootAware="false"
|
||||
android:exported="false"
|
||||
android:multiprocess="true"
|
||||
tools:targetApi="n" />
|
||||
<!-- WorkManager SystemJobService -->
|
||||
|
||||
<service
|
||||
android:name="androidx.work.impl.background.systemjob.SystemJobService"
|
||||
android:name="a.j"
|
||||
android:directBootAware="false"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
tools:targetApi="n" />
|
||||
|
||||
<receiver
|
||||
android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
|
||||
android:directBootAware="false"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
tools:targetApi="n" />
|
||||
|
||||
<!-- Room -->
|
||||
|
||||
<service
|
||||
android:name="androidx.room.MultiInstanceInvalidationService"
|
||||
android:exported="false" />
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
</application>
|
||||
|
||||
|
@ -4,7 +4,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class ComponentMap {
|
||||
private static Map<String, String> map = new HashMap<>(6);
|
||||
private static Map<String, String> map = new HashMap<>();
|
||||
|
||||
// This mapping will be sent into the guest app
|
||||
static Map<String, String> inverseMap;
|
||||
@ -16,7 +16,7 @@ class ComponentMap {
|
||||
map.put("a.g", "a.m");
|
||||
map.put(a.w.class.getName(), "a.h");
|
||||
map.put("a.v", "a.j");
|
||||
map.put("a.s", "androidx.work.impl.WorkManagerInitializer");
|
||||
map.put("a.j", "androidx.work.impl.background.systemjob.SystemJobService");
|
||||
|
||||
inverseMap = new HashMap<>(map.size());
|
||||
for (Map.Entry<String, String> e : map.entrySet()) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user