Add "Settings Presets"
This commit is contained in:
parent
567f625523
commit
c763b054f3
15 changed files with 351 additions and 4 deletions
|
|
@ -1,7 +1,6 @@
|
|||
package icu.nullptr.hidemyapplist.service
|
||||
|
||||
import android.os.IBinder
|
||||
import android.os.IBinder.DeathRecipient
|
||||
import android.os.Parcel
|
||||
import android.os.RemoteException
|
||||
import android.os.ServiceManager
|
||||
|
|
@ -12,7 +11,7 @@ import java.lang.reflect.InvocationHandler
|
|||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
object ServiceClient : IHMAService, DeathRecipient {
|
||||
object ServiceClient : IHMAService, IBinder.DeathRecipient {
|
||||
|
||||
private const val TAG = "ServiceClient"
|
||||
|
||||
|
|
@ -96,4 +95,4 @@ object ServiceClient : IHMAService, DeathRecipient {
|
|||
override fun stopService(cleanEnv: Boolean) {
|
||||
getServiceLegacy()?.stopService(cleanEnv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import by.kirich1409.viewbindingdelegate.viewBinding
|
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import icu.nullptr.hidemyapplist.common.AppPresets
|
||||
import icu.nullptr.hidemyapplist.common.JsonConfig
|
||||
import icu.nullptr.hidemyapplist.common.SettingsPresets
|
||||
import icu.nullptr.hidemyapplist.service.ConfigManager
|
||||
import icu.nullptr.hidemyapplist.ui.util.navController
|
||||
import icu.nullptr.hidemyapplist.ui.util.setupToolbar
|
||||
|
|
@ -110,6 +111,11 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
|
|||
getString(R.string.app_preset_using, pack.config.applyPresets.size)
|
||||
}
|
||||
|
||||
private fun updateApplySettingsPresets() {
|
||||
findPreference<Preference>("applySettingsPresets")?.title =
|
||||
getString(R.string.app_settings_preset_using, pack.config.applySettingsPresets.size)
|
||||
}
|
||||
|
||||
private fun updateExtraAppList(useWhiteList: Boolean) {
|
||||
findPreference<Preference>("extraAppList")?.title =
|
||||
if (useWhiteList) getString(R.string.app_extra_apps_visible_count, pack.config.extraAppList.size)
|
||||
|
|
@ -187,6 +193,40 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
|
|||
.show()
|
||||
true
|
||||
}
|
||||
findPreference<Preference>("applySettingsPresets")?.setOnPreferenceClickListener {
|
||||
val presetNames = SettingsPresets.instance.getAllPresetNames()
|
||||
val presetTranslations = presetNames.map { name ->
|
||||
try {
|
||||
val id = resources.getIdentifier(
|
||||
"settings_preset_${name}",
|
||||
"string",
|
||||
BuildConfig.APPLICATION_ID
|
||||
)
|
||||
|
||||
return@map if (id != 0) { getString(id) } else { name }
|
||||
} catch (ignore: Throwable) {}
|
||||
|
||||
name
|
||||
}
|
||||
|
||||
val presets = presetNames.zip(presetTranslations).toMap().toSortedMap()
|
||||
val checked = presets.keys.map {
|
||||
pack.config.applySettingsPresets.contains(it)
|
||||
}.toBooleanArray()
|
||||
val presetValues = presets.values.toTypedArray()
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.app_choose_preset)
|
||||
.setMultiChoiceItems(presetValues, checked) { _, i, value -> checked[i] = value }
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
pack.config.applySettingsPresets = presetValues.mapIndexedNotNullTo(mutableSetOf()) { i, name ->
|
||||
if (checked[i]) presets.filterValues { v -> v == name }.keys.first() else null
|
||||
}
|
||||
updateApplySettingsPresets()
|
||||
}
|
||||
.show()
|
||||
true
|
||||
}
|
||||
findPreference<Preference>("extraAppList")?.setOnPreferenceClickListener {
|
||||
parent.setFragmentResultListener("app_select") { _, bundle ->
|
||||
pack.config.extraAppList = bundle.getStringArrayList("checked")!!.toMutableSet()
|
||||
|
|
@ -203,6 +243,7 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
|
|||
}
|
||||
updateApplyTemplates()
|
||||
updateApplyPresets()
|
||||
updateApplySettingsPresets()
|
||||
updateExtraAppList(pack.config.useWhitelist)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@
|
|||
<string name="app_exclude_system_apps_summary">Turn off this option only when really necessary. It is likely to cause crash with it disabled</string>
|
||||
<string name="app_template_using">Using %d templates</string>
|
||||
<string name="app_preset_using">Using %d presets</string>
|
||||
<string name="app_settings_preset_using">Using %d settings presets</string>
|
||||
<string name="app_extra_apps_visible_count">%d additional apps visible</string>
|
||||
<string name="app_extra_apps_invisible_count">%d additional apps invisible</string>
|
||||
|
||||
|
|
@ -96,6 +97,10 @@
|
|||
<string name="preset_root_apps">Root managers/Rooted apps</string>
|
||||
<string name="preset_custom_rom">Custom ROM apps</string>
|
||||
|
||||
<!-- Settings preset names -->
|
||||
<string name="settings_preset_accessibility">Accessibility</string>
|
||||
<string name="settings_preset_dev_options">Developer options</string>
|
||||
|
||||
<!-- Logs -->
|
||||
<string name="logs_save">Save logs</string>
|
||||
<string name="logs_clear">Clear logs</string>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@
|
|||
android:icon="@drawable/outline_assignment_24"
|
||||
android:key="applyPresets"
|
||||
tools:title="@string/app_preset_using" />
|
||||
<Preference
|
||||
android:icon="@drawable/outline_settings_24"
|
||||
android:key="applySettingsPresets"
|
||||
tools:title="@string/app_settings_preset_using" />
|
||||
<Preference
|
||||
android:icon="@drawable/baseline_apps_24"
|
||||
android:key="extraAppList"
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ val targetSdkVer by extra(36)
|
|||
|
||||
val appVerCode = gitCommitCount + 0x6f7373
|
||||
val appVerName by extra("3.3.1-oss-${gitCommitCountAfterOss}${gitHasUncommittedSuffix}")
|
||||
val configVerCode by extra(91)
|
||||
val configVerCode by extra(92)
|
||||
val serviceVerCode by extra(98)
|
||||
val minBackupVerCode by extra(65)
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ data class JsonConfig(
|
|||
var excludeSystemApps: Boolean = true,
|
||||
var applyTemplates: MutableSet<String> = mutableSetOf(),
|
||||
var applyPresets: MutableSet<String> = mutableSetOf(),
|
||||
var applySettingsPresets: MutableSet<String> = mutableSetOf(),
|
||||
var extraAppList: MutableSet<String> = mutableSetOf()
|
||||
) {
|
||||
override fun toString() = encoder.encodeToString(this)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package icu.nullptr.hidemyapplist.common
|
||||
|
||||
import icu.nullptr.hidemyapplist.common.settings_presets.AccessibilityPreset
|
||||
import icu.nullptr.hidemyapplist.common.settings_presets.BasePreset
|
||||
import icu.nullptr.hidemyapplist.common.settings_presets.DeveloperOptionsPreset
|
||||
|
||||
class SettingsPresets private constructor() {
|
||||
private val presetList = mutableListOf<BasePreset>()
|
||||
|
||||
companion object {
|
||||
private var hiddenInstance: SettingsPresets? = null
|
||||
|
||||
val instance: SettingsPresets
|
||||
get() {
|
||||
if (hiddenInstance == null) {
|
||||
hiddenInstance = SettingsPresets()
|
||||
}
|
||||
|
||||
return hiddenInstance!!
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllPresetNames() = presetList.map { it.name }.toTypedArray()
|
||||
// fun filterPresetsByName(names: Array<String>) = presetList.filter { names.contains(it.name) }
|
||||
fun getPresetByName(name: String) = presetList.firstOrNull { it.name == name }
|
||||
|
||||
init {
|
||||
presetList.add(DeveloperOptionsPreset())
|
||||
presetList.add(AccessibilityPreset())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package icu.nullptr.hidemyapplist.common.settings_presets
|
||||
|
||||
import android.provider.Settings
|
||||
|
||||
class AccessibilityPreset : BasePreset(NAME) {
|
||||
companion object {
|
||||
const val NAME = "accessibility"
|
||||
}
|
||||
|
||||
override val settingsKVPairs = listOf(
|
||||
ReplacementItem(
|
||||
name = Settings.Secure.ACCESSIBILITY_ENABLED,
|
||||
value = "0",
|
||||
),
|
||||
ReplacementItem(
|
||||
name = Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
|
||||
value = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package icu.nullptr.hidemyapplist.common.settings_presets
|
||||
|
||||
abstract class BasePreset(val name: String) {
|
||||
protected abstract val settingsKVPairs: List<ReplacementItem>
|
||||
|
||||
fun getSpoofedValue(key: String) = settingsKVPairs.firstOrNull { it.name == key }
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package icu.nullptr.hidemyapplist.common.settings_presets
|
||||
|
||||
import android.provider.Settings
|
||||
|
||||
class DeveloperOptionsPreset : BasePreset("dev_options") {
|
||||
override val settingsKVPairs = listOf(
|
||||
ReplacementItem(
|
||||
name = Settings.Global.ADB_ENABLED,
|
||||
value = "0",
|
||||
),
|
||||
ReplacementItem(
|
||||
name = "adb_wifi_enabled",
|
||||
value = "0",
|
||||
),
|
||||
ReplacementItem(
|
||||
name = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
|
||||
value = "0",
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package icu.nullptr.hidemyapplist.common.settings_presets
|
||||
|
||||
data class ReplacementItem(
|
||||
val name: String,
|
||||
val value: String?
|
||||
) {
|
||||
override fun toString() = "ReplacementItem {" +
|
||||
" 'name': '$name'," +
|
||||
"'value': '$value'" +
|
||||
" }"
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import icu.nullptr.hidemyapplist.common.Constants
|
|||
import icu.nullptr.hidemyapplist.common.IHMAService
|
||||
import icu.nullptr.hidemyapplist.common.JsonConfig
|
||||
import icu.nullptr.hidemyapplist.common.Utils
|
||||
import icu.nullptr.hidemyapplist.xposed.hook.AccessibilityHook
|
||||
import icu.nullptr.hidemyapplist.xposed.hook.ActivityHook
|
||||
import icu.nullptr.hidemyapplist.xposed.hook.IFrameworkHook
|
||||
import icu.nullptr.hidemyapplist.xposed.hook.PlatformCompatHook
|
||||
|
|
@ -152,6 +153,7 @@ class HMAService(val pms: IPackageManager) : IHMAService.Stub() {
|
|||
|
||||
frameworkHooks.add(ActivityHook(this))
|
||||
frameworkHooks.add(PmsPackageEventsHook(this))
|
||||
frameworkHooks.add(AccessibilityHook(this))
|
||||
|
||||
frameworkHooks.forEach(IFrameworkHook::load)
|
||||
logI(TAG, "Hooks installed")
|
||||
|
|
@ -159,6 +161,11 @@ class HMAService(val pms: IPackageManager) : IHMAService.Stub() {
|
|||
|
||||
fun isHookEnabled(packageName: String) = config.scope.containsKey(packageName)
|
||||
|
||||
fun getEnabledSettingsPresets(caller: String?): Set<String> {
|
||||
if (caller == null) return setOf()
|
||||
return config.scope[caller]?.applySettingsPresets ?: return setOf()
|
||||
}
|
||||
|
||||
fun shouldHide(caller: String?, query: String?): Boolean {
|
||||
if (caller == null || query == null) return false
|
||||
if (caller in Constants.packagesShouldNotHide || query in Constants.packagesShouldNotHide) return false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
package icu.nullptr.hidemyapplist.xposed
|
||||
|
||||
import android.content.ContentProvider
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import com.github.kyuubiran.ezxhelper.utils.findMethod
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookBefore
|
||||
import de.robv.android.xposed.IXposedHookLoadPackage
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||
import icu.nullptr.hidemyapplist.common.JsonConfig
|
||||
import icu.nullptr.hidemyapplist.common.SettingsPresets
|
||||
import icu.nullptr.hidemyapplist.common.Utils
|
||||
import icu.nullptr.hidemyapplist.common.settings_presets.ReplacementItem
|
||||
import org.frknkrc44.hma_oss.common.BuildConfig
|
||||
import java.io.File
|
||||
|
||||
class HMASettingsProviderService : IXposedHookLoadPackage {
|
||||
companion object {
|
||||
private const val TAG = "SettingsProviderHook"
|
||||
}
|
||||
|
||||
init {
|
||||
searchDataDir()
|
||||
}
|
||||
|
||||
private lateinit var dataDir: String
|
||||
private lateinit var configFile: File
|
||||
|
||||
private val configLock = Any()
|
||||
private var configLastModified: Long = 0
|
||||
|
||||
var config = JsonConfig().apply { detailLog = true }
|
||||
private set
|
||||
|
||||
private fun searchDataDir() {
|
||||
File("/data/system").list()?.forEach {
|
||||
if (it.startsWith("hide_my_applist")) {
|
||||
if (!this::dataDir.isInitialized) {
|
||||
val newDir = File("/data/misc/$it")
|
||||
File("/data/system/$it").renameTo(newDir)
|
||||
dataDir = newDir.path
|
||||
} else {
|
||||
File("/data/system/$it").deleteRecursively()
|
||||
}
|
||||
}
|
||||
}
|
||||
File("/data/misc").list()?.forEach {
|
||||
if (it.startsWith("hide_my_applist")) {
|
||||
if (!this::dataDir.isInitialized) {
|
||||
dataDir = "/data/misc/$it"
|
||||
} else if (dataDir != "/data/misc/$it") {
|
||||
File("/data/misc/$it").deleteRecursively()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!this::dataDir.isInitialized) {
|
||||
dataDir = "/data/misc/hide_my_applist_" + Utils.generateRandomString(16)
|
||||
}
|
||||
|
||||
File("$dataDir/log").mkdirs()
|
||||
configFile = File("$dataDir/config.json")
|
||||
|
||||
logI(TAG, "Data dir: $dataDir")
|
||||
}
|
||||
|
||||
private fun loadConfig() {
|
||||
if (!configFile.exists()) {
|
||||
logI(TAG, "Config file not found")
|
||||
return
|
||||
}
|
||||
|
||||
if (configLastModified == configFile.lastModified()) {
|
||||
return
|
||||
}
|
||||
|
||||
configLastModified = configFile.lastModified()
|
||||
|
||||
val loading = runCatching {
|
||||
val json = configFile.readText()
|
||||
JsonConfig.parse(json)
|
||||
}.getOrElse {
|
||||
logE(TAG, "Failed to parse config.json", it)
|
||||
return
|
||||
}
|
||||
if (loading.configVersion != BuildConfig.CONFIG_VERSION) {
|
||||
logW(TAG, "Config version mismatch, need to reload")
|
||||
return
|
||||
}
|
||||
config = loading
|
||||
logI(TAG, "Config loaded")
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
|
||||
logI(TAG, "Load hook")
|
||||
|
||||
findMethod(
|
||||
"com.android.providers.settings.SettingsProvider",
|
||||
lpparam.classLoader,
|
||||
) {
|
||||
name == "call"
|
||||
}.hookBefore { param ->
|
||||
loadConfig()
|
||||
|
||||
val contentProvider = param.thisObject as ContentProvider
|
||||
val method = param.args[0] as String?
|
||||
val name = param.args[1] as String?
|
||||
|
||||
val caller = contentProvider.callingPackage
|
||||
if (!config.scope.containsKey(caller)) {
|
||||
// logD(TAG, "Not enabled app: $caller")
|
||||
return@hookBefore
|
||||
}
|
||||
|
||||
logD(TAG, "@call caller: $caller, method: $method, name: $name")
|
||||
|
||||
when (method) {
|
||||
"GET_global", "GET_secure", "GET_system" -> {
|
||||
val replacement = getSpoofedSetting(caller, name)
|
||||
if (replacement != null) {
|
||||
logD(TAG, "@getSettings returned replacement for $caller: ${replacement.value}")
|
||||
param.result = Bundle().apply {
|
||||
putString(Settings.NameValueTable.VALUE, replacement.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSpoofedSetting(caller: String?, name: String?): ReplacementItem? {
|
||||
synchronized(configLock) {
|
||||
if (caller == null || name == null) return null
|
||||
|
||||
val presets = config.scope[caller]?.applySettingsPresets
|
||||
if (presets?.isEmpty() ?: true) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (presetName in presets) {
|
||||
val preset = SettingsPresets.instance.getPresetByName(presetName)
|
||||
val value = preset?.getSpoofedValue(name)
|
||||
if (value != null) return value
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -50,6 +50,8 @@ class XposedEntry : IXposedHookZygoteInit, IXposedHookLoadPackage {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (lpparam.packageName == "com.android.providers.settings") {
|
||||
HMASettingsProviderService().handleLoadPackage(lpparam)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
package icu.nullptr.hidemyapplist.xposed.hook
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo
|
||||
import android.app.ActivityThread
|
||||
import com.github.kyuubiran.ezxhelper.utils.findMethod
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookBefore
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import icu.nullptr.hidemyapplist.common.settings_presets.AccessibilityPreset
|
||||
import icu.nullptr.hidemyapplist.xposed.HMAService
|
||||
import icu.nullptr.hidemyapplist.xposed.logE
|
||||
import icu.nullptr.hidemyapplist.xposed.logI
|
||||
|
||||
class AccessibilityHook(private val service: HMAService) : IFrameworkHook {
|
||||
companion object {
|
||||
private const val TAG = "AccessibilityHook"
|
||||
}
|
||||
|
||||
private val hookList = mutableSetOf<XC_MethodHook.Unhook>()
|
||||
|
||||
override fun load() {
|
||||
logI(TAG, "Load hook")
|
||||
|
||||
hookList += findMethod(
|
||||
"com.android.server.accessibility.AccessibilityManagerService"
|
||||
) {
|
||||
name == "getInstalledAccessibilityServiceList"
|
||||
}.hookBefore { param -> hookedMethod(param) }
|
||||
|
||||
hookList += findMethod(
|
||||
"com.android.server.accessibility.AccessibilityManagerService"
|
||||
) {
|
||||
name == "getEnabledAccessibilityServiceList"
|
||||
}.hookBefore { param -> hookedMethod(param) }
|
||||
}
|
||||
|
||||
private fun hookedMethod(param: XC_MethodHook.MethodHookParam) {
|
||||
try {
|
||||
val caller = ActivityThread.currentActivityThread().application.packageName
|
||||
if (service.getEnabledSettingsPresets(caller).contains(AccessibilityPreset.NAME)) {
|
||||
param.result = java.util.ArrayList<AccessibilityServiceInfo>()
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logE(TAG, "Fatal error occurred, ignore hooks", e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun unload() {
|
||||
hookList.forEach { it.unhook() }
|
||||
hookList.clear()
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue