Prepare for a new version (#35)

* Prepare for a new version

- Add "Clear package list" functionality for presets
- Add Xiaomi.EU apps in Custom ROM preset
- Add new apps into presets
- Create new preset, called as "Shizuku/Dhizuku apps"
- Move Shizuku to new preset
- Add manifest read cache, it can improve performance
- Add existence check for package add listener
- Add "Enable package query workaround" switch to help MIUI/HyperOS users (needs test)
- Other fixes and improvements
This commit is contained in:
Furkan Karcıoğlu 2025-11-17 23:04:44 +03:00 • committed by GitHub
commit 1da27627c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
105 changed files with 1798 additions and 402 deletions

View file

@ -31,7 +31,6 @@
<activity-alias
android:name=".MainActivityLauncher"
android:exported="true"
android:label="@string/app_name"
android:targetActivity="org.frknkrc44.hma_oss.ui.activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Before After
Before After

View file

@ -3,7 +3,10 @@ package icu.nullptr.hidemyapplist.service
import android.os.Build
import android.util.Log
import icu.nullptr.hidemyapplist.common.JsonConfig
import icu.nullptr.hidemyapplist.common.settings_presets.ReplacementItem
import icu.nullptr.hidemyapplist.hmaApp
import icu.nullptr.hidemyapplist.service.ConfigManager.PTType.APP
import icu.nullptr.hidemyapplist.service.ConfigManager.PTType.SETTINGS
import icu.nullptr.hidemyapplist.ui.util.showToast
import icu.nullptr.hidemyapplist.util.PackageHelper
import org.frknkrc44.hma_oss.R
@ -11,13 +14,26 @@ import org.frknkrc44.hma_oss.common.BuildConfig
import java.io.File
object ConfigManager {
enum class PresetType {
/**
* Indicates the type of preset/template.
*
* @see APP
* @see SETTINGS
*/
enum class PTType {
/**
* This preset/template type is used for app filtering.
*/
APP,
/**
* This preset/template type is used for settings filtering.
*/
SETTINGS,
}
data class TemplateInfo(val name: String?, val isWhiteList: Boolean)
data class PresetInfo(val name: String, val type: PresetType?, val translation: String)
data class TemplateInfo(val name: String?, val type: PTType, val isWhiteList: Boolean)
data class PresetInfo(val name: String, val type: PTType?, val translation: String)
private const val TAG = "ConfigManager"
private lateinit var config: JsonConfig
@ -107,6 +123,14 @@ object ConfigManager {
saveConfig()
}
var packageQueryWorkaround: Boolean
get() = config.packageQueryWorkaround
set(value) {
config.packageQueryWorkaround = value
saveConfig()
PackageHelper.invalidateCache()
}
fun importConfig(json: String) {
config = JsonConfig.parse(json)
config.configVersion = BuildConfig.CONFIG_VERSION
@ -118,7 +142,7 @@ object ConfigManager {
}
fun getTemplateList(): MutableList<TemplateInfo> {
return config.templates.mapTo(mutableListOf()) { TemplateInfo(it.key, it.value.isWhitelist) }
return config.templates.mapTo(mutableListOf()) { TemplateInfo(it.key, PTType.APP, it.value.isWhitelist) }
}
fun getTemplateAppliedAppList(name: String): ArrayList<String> {
@ -167,6 +191,56 @@ object ConfigManager {
saveConfig()
}
fun getSettingTemplateList(): MutableList<TemplateInfo> {
return config.settingsTemplates.mapTo(mutableListOf()) { TemplateInfo(it.key, PTType.SETTINGS, false) }
}
fun getSettingTemplateAppliedAppList(name: String): ArrayList<String> {
return config.scope.mapNotNullTo(ArrayList()) {
if (it.value.applySettingTemplates.contains(name)) it.key else null
}
}
fun getSettingTemplateTargetSettingList(name: String): ArrayList<ReplacementItem> {
return ArrayList(config.settingsTemplates[name]?.settingsList ?: emptyList())
}
fun deleteSettingTemplate(name: String) {
config.scope.forEach { (_, appInfo) ->
appInfo.applySettingTemplates.remove(name)
}
config.settingsTemplates.remove(name)
saveConfig()
}
fun renameSettingTemplate(oldName: String, newName: String) {
if (oldName == newName) return
config.scope.forEach { (_, appInfo) ->
if (appInfo.applySettingTemplates.contains(oldName)) {
appInfo.applySettingTemplates.remove(oldName)
appInfo.applySettingTemplates.add(newName)
}
}
config.settingsTemplates[newName] = config.settingsTemplates[oldName]!!
config.settingsTemplates.remove(oldName)
saveConfig()
}
fun updateSettingTemplate(name: String, template: JsonConfig.SettingsTemplate) {
Log.d(TAG, "updateSettingTemplate: $name list = ${template.settingsList}")
config.settingsTemplates[name] = template
saveConfig()
}
fun updateSettingTemplateAppliedApps(name: String, appliedList: List<String>) {
Log.d(TAG, "updateSettingTemplateAppliedApps: $name list = $appliedList")
config.scope.forEach { (app, appInfo) ->
if (appliedList.contains(app)) appInfo.applySettingTemplates.add(name)
else appInfo.applySettingTemplates.remove(name)
}
saveConfig()
}
fun isHideEnabled(packageName: String): Boolean {
return config.scope.containsKey(packageName)
}

View file

@ -4,6 +4,7 @@ import android.content.ComponentName
import android.content.Context.MODE_PRIVATE
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.edit
import icu.nullptr.hidemyapplist.hmaApp
import org.frknkrc44.hma_oss.BuildConfig
@ -14,6 +15,7 @@ object PrefManager {
private const val PREF_LOCALE = "language"
private const val PREF_SYSTEM_WALLPAPER = "system_wallpaper"
private const val PREF_SYSTEM_WALLPAPER_ALPHA = "system_wallpaper_alpha"
private const val PREF_DARK_THEME = "dark_theme"
private const val PREF_BLACK_DARK_THEME = "black_dark_theme"
private const val PREF_FOLLOW_SYSTEM_ACCENT = "follow_system_accent"
@ -36,36 +38,40 @@ object PrefManager {
var lastVersion: Int
get() = pref.getInt(PREF_LAST_VERSION, 0)
set(value) = pref.edit().putInt(PREF_LAST_VERSION, value).apply()
set(value) = pref.edit { putInt(PREF_LAST_VERSION, value) }
var locale: String
get() = pref.getString(PREF_LOCALE, "SYSTEM")!!
set(value) = pref.edit().putString(PREF_LOCALE, value).apply()
set(value) = pref.edit { putString(PREF_LOCALE, value) }
var darkTheme: Int
get() = pref.getInt(PREF_DARK_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
set(value) = pref.edit().putInt(PREF_DARK_THEME, value).apply()
set(value) = pref.edit { putInt(PREF_DARK_THEME, value) }
var systemWallpaper: Boolean
get() = pref.getBoolean(PREF_SYSTEM_WALLPAPER, false)
set(value) = pref.edit().putBoolean(PREF_SYSTEM_WALLPAPER, value).apply()
set(value) = pref.edit { putBoolean(PREF_SYSTEM_WALLPAPER, value) }
var systemWallpaperAlpha: Int
get() = pref.getInt(PREF_SYSTEM_WALLPAPER_ALPHA, 0xAA)
set(value) = pref.edit { putInt(PREF_SYSTEM_WALLPAPER_ALPHA, value) }
var blackDarkTheme: Boolean
get() = pref.getBoolean(PREF_BLACK_DARK_THEME, false)
set(value) = pref.edit().putBoolean(PREF_BLACK_DARK_THEME, value).apply()
set(value) = pref.edit { putBoolean(PREF_BLACK_DARK_THEME, value) }
var followSystemAccent: Boolean
get() = pref.getBoolean(PREF_FOLLOW_SYSTEM_ACCENT, true)
set(value) = pref.edit().putBoolean(PREF_FOLLOW_SYSTEM_ACCENT, value).apply()
set(value) = pref.edit { putBoolean(PREF_FOLLOW_SYSTEM_ACCENT, value) }
var themeColor: String
get() = pref.getString(PREF_THEME_COLOR, "MATERIAL_BLUE")!!
set(value) = pref.edit().putString(PREF_THEME_COLOR, value).apply()
set(value) = pref.edit { putString(PREF_THEME_COLOR, value) }
var hideIcon: Boolean
get() = pref.getBoolean(PREF_HIDE_ICON, false)
set(value) {
pref.edit().putBoolean(PREF_HIDE_ICON, value).apply()
pref.edit { putBoolean(PREF_HIDE_ICON, value) }
val component = ComponentName(hmaApp, "${BuildConfig.APPLICATION_ID}.MainActivityLauncher")
val status =
if (value) PackageManager.COMPONENT_ENABLED_STATE_DISABLED
@ -75,25 +81,25 @@ object PrefManager {
var bypassRiskyPackageWarning: Boolean
get() = pref.getBoolean(PREF_BYPASS_RISKY_PACKAGE_WARNING, false)
set(value) = pref.edit().putBoolean(PREF_BYPASS_RISKY_PACKAGE_WARNING, value).apply()
set(value) = pref.edit { putBoolean(PREF_BYPASS_RISKY_PACKAGE_WARNING, value) }
var appFilter_showSystem: Boolean
get() = pref.getBoolean(PREF_APP_FILTER_SHOW_SYSTEM, false)
set(value) = pref.edit().putBoolean(PREF_APP_FILTER_SHOW_SYSTEM, value).apply()
set(value) = pref.edit { putBoolean(PREF_APP_FILTER_SHOW_SYSTEM, value) }
var appFilter_sortMethod: SortMethod
get() = SortMethod.values()[pref.getInt(PREF_APP_FILTER_SORT_METHOD, SortMethod.BY_LABEL.ordinal)]
set(value) = pref.edit().putInt(PREF_APP_FILTER_SORT_METHOD, value.ordinal).apply()
get() = SortMethod.entries[pref.getInt(PREF_APP_FILTER_SORT_METHOD, SortMethod.BY_LABEL.ordinal)]
set(value) = pref.edit { putInt(PREF_APP_FILTER_SORT_METHOD, value.ordinal) }
var appFilter_reverseOrder: Boolean
get() = pref.getBoolean(PREF_APP_FILTER_REVERSE_ORDER, false)
set(value) = pref.edit().putBoolean(PREF_APP_FILTER_REVERSE_ORDER, value).apply()
set(value) = pref.edit { putBoolean(PREF_APP_FILTER_REVERSE_ORDER, value) }
var logFilter_level: Int
get() = pref.getInt(PREF_LOG_FILTER_LEVEL, 0)
set(value) = pref.edit().putInt(PREF_LOG_FILTER_LEVEL, value).apply()
set(value) = pref.edit { putInt(PREF_LOG_FILTER_LEVEL, value) }
var logFilter_reverseOrder: Boolean
get() = pref.getBoolean(PREF_LOG_FILTER_REVERSE_ORDER, false)
set(value) = pref.edit().putBoolean(PREF_LOG_FILTER_REVERSE_ORDER, value).apply()
set(value) = pref.edit { putBoolean(PREF_LOG_FILTER_REVERSE_ORDER, value) }
}

View file

@ -73,4 +73,13 @@ object ServiceClient : IHMAService, IBinder.DeathRecipient {
override fun log(level: Int, tag: String, message: String) {
service?.log(level, tag, message)
}
override fun getPackageNames(userId: Int) = service?.getPackageNames(userId)
override fun getPackageInfo(
packageName: String?,
userId: Int
) = service?.getPackageInfo(packageName, userId)
override fun listAllSettings(databaseName: String) = service?.listAllSettings(databaseName) ?: arrayOf()
}

View file

@ -29,8 +29,13 @@ class TemplateAdapter(
fun bind(info: ConfigManager.TemplateInfo) {
with(itemView as ListItemView) {
setIcon(
if (info.isWhiteList) R.drawable.outline_assignment_24
else R.drawable.baseline_assignment_24
when (info.type) {
ConfigManager.PTType.APP -> {
if (info.isWhiteList) R.drawable.outline_assignment_24
else R.drawable.baseline_assignment_24
}
ConfigManager.PTType.SETTINGS -> R.drawable.baseline_settings_24
}
)
text = info.name
}
@ -54,9 +59,13 @@ class TemplateAdapter(
@SuppressLint("NotifyDataSetChanged")
fun updateList() {
list = ConfigManager.getTemplateList().apply {
val allItems = (ConfigManager.getTemplateList() + ConfigManager.getSettingTemplateList()).toMutableList()
list = allItems.apply {
sortWith { o1, o2 ->
if (o1.isWhiteList != o2.isWhiteList) {
if (o1.type != o2.type) {
o1.type.compareTo(o2.type)
} else if (o1.isWhiteList != o2.isWhiteList) {
o1.isWhiteList.compareTo(o2.isWhiteList)
} else {
Collator.getInstance(Locale.getDefault()).compare(o1.name, o2.name)

View file

@ -138,7 +138,7 @@ abstract class AppSelectFragment : Fragment(R.layout.fragment_app_select) {
adapter.registerAdapterDataObserver(
EmptyDataObserver(
binding.list,
binding.listEmptyContainer
binding.listEmptyContainer.root
)
)
@ -159,7 +159,7 @@ abstract class AppSelectFragment : Fragment(R.layout.fragment_app_select) {
binding.root.setOnApplyWindowInsetsListener { v, insets ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val barInsets = insets.getInsets(WindowInsets.Type.systemBars())
binding.root.setPadding(
v.setPadding(
barInsets.left,
barInsets.top,
barInsets.right,
@ -167,7 +167,7 @@ abstract class AppSelectFragment : Fragment(R.layout.fragment_app_select) {
)
} else {
@Suppress("deprecation")
binding.root.setPadding(
v.setPadding(
insets.systemWindowInsetLeft,
insets.systemWindowInsetTop,
insets.systemWindowInsetRight,

View file

@ -9,6 +9,7 @@ import android.view.WindowInsets
import android.widget.Toast
import androidx.activity.addCallback
import androidx.core.graphics.drawable.toDrawable
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.clearFragmentResultListener
import androidx.fragment.app.setFragmentResultListener
@ -35,6 +36,7 @@ import icu.nullptr.hidemyapplist.util.PackageHelper
import org.frknkrc44.hma_oss.BuildConfig
import org.frknkrc44.hma_oss.R
import org.frknkrc44.hma_oss.databinding.FragmentSettingsBinding
import org.frknkrc44.hma_oss.databinding.LayoutListEmptyBinding
class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
companion object {
@ -65,11 +67,6 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
saveConfig()
}
override fun onDestroyView() {
super.onDestroyView()
saveConfig()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { onBack() }
setupToolbar(
@ -88,7 +85,7 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
binding.root.setOnApplyWindowInsetsListener { v, insets ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val barInsets = insets.getInsets(WindowInsets.Type.systemBars())
binding.root.setPadding(
v.setPadding(
barInsets.left,
barInsets.top,
barInsets.right,
@ -96,7 +93,7 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
)
} else {
@Suppress("deprecation")
binding.root.setPadding(
v.setPadding(
insets.systemWindowInsetLeft,
insets.systemWindowInsetTop,
insets.systemWindowInsetRight,
@ -119,6 +116,7 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
"hideSystemInstallationSource" -> pack.config.hideSystemInstallationSource
"excludeTargetInstallationSource" -> pack.config.excludeTargetInstallationSource
"invertActivityLaunchProtection" -> pack.config.invertActivityLaunchProtection
"excludeVoldIsolation" -> pack.config.excludeVoldIsolation
else -> throw IllegalArgumentException("Invalid key: $key")
}
}
@ -132,6 +130,7 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
"hideSystemInstallationSource" -> pack.config.hideSystemInstallationSource = value
"excludeTargetInstallationSource" -> pack.config.excludeTargetInstallationSource = value
"invertActivityLaunchProtection" -> pack.config.invertActivityLaunchProtection = value
"excludeVoldIsolation" -> pack.config.excludeVoldIsolation = value
else -> throw IllegalArgumentException("Invalid key: $key")
}
}
@ -147,9 +146,11 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
getString(R.string.app_template_using, pack.config.applyTemplates.size)
}
private fun updateApplyPresets() {
findPreference<Preference>("applyPresets")?.title =
getString(R.string.app_preset_using, pack.config.applyPresets.size)
private fun updateApplyPresets(useWhitelist: Boolean = pack.config.useWhitelist) {
findPreference<Preference>("applyPresets")?.apply {
isVisible = !useWhitelist
title = getString(R.string.app_preset_using, pack.config.applyPresets.size)
}
}
private fun updateApplySettingsPresets() {
@ -157,6 +158,11 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
getString(R.string.app_settings_preset_using, pack.config.applySettingsPresets.size)
}
private fun updateApplySettingsTemplates() {
findPreference<Preference>("applySettingsTemplates")?.title =
getString(R.string.app_settings_template_using, pack.config.applySettingTemplates.size)
}
private fun updateExtraAppList(useWhiteList: Boolean) {
findPreference<Preference>("extraAppList")?.title =
if (useWhiteList) getString(R.string.app_extra_apps_visible_count, pack.config.extraAppList.size)
@ -225,6 +231,9 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
R.string.app_force_stop_warning, Toast.LENGTH_LONG).show()
true
}
findPreference<SwitchPreferenceCompat>("excludeVoldIsolation")?.let {
it.isEnabled = ConfigManager.altVoldAppDataIsolation
}
findPreference<SwitchPreferenceCompat>("invertActivityLaunchProtection")?.let {
it.summary = getString(R.string.app_invert_activity_launch_protection_desc) + "\n\n" +
getString(R.string.app_global_activity_launch_protection_state,
@ -232,11 +241,14 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
)
}
findPreference<SwitchPreferenceCompat>("useWhiteList")?.setOnPreferenceChangeListener { _, newValue ->
val useWhitelist = newValue as Boolean
pack.config.applyTemplates.clear()
pack.config.extraAppList.clear()
pack.config.applyPresets.clear()
updateApplyTemplates()
updateApplyPresets()
updateExtraAppList(newValue as Boolean)
updateApplyPresets(useWhitelist)
updateExtraAppList(useWhitelist)
true
}
findPreference<Preference>("applyTemplates")?.setOnPreferenceClickListener {
@ -246,9 +258,9 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
val checked = templates.map {
pack.config.applyTemplates.contains(it)
}.toBooleanArray()
MaterialAlertDialogBuilder(requireContext())
val dialog = MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.app_choose_template)
.setMultiChoiceItems(templates, checked) { _, i, value -> checked[i] = value }
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok) { _, _ ->
pack.config.applyTemplates = templates.mapIndexedNotNullTo(mutableSetOf()) { i, name ->
@ -256,7 +268,21 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
}
updateApplyTemplates()
}
.show()
if (templates.isNotEmpty()) {
dialog.setMultiChoiceItems(templates, checked) { _, i, value ->
checked[i] = value
}
} else {
val emptyView = LayoutListEmptyBinding.inflate(layoutInflater)
emptyView.root.isVisible = true
emptyView.listEmptyIcon.setImageResource(R.drawable.sentiment_very_dissatisfied_24px)
emptyView.listEmptyText.text = getString(R.string.title_template_manage)
dialog.setView(emptyView.root)
}
dialog.show()
true
}
findPreference<Preference>("applyPresets")?.setOnPreferenceClickListener {
@ -293,6 +319,37 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
.show()
true
}
findPreference<Preference>("applySettingsTemplates")?.apply {
setOnPreferenceClickListener {
val templates = ConfigManager.getSettingTemplateList().mapNotNull { it.name }.toTypedArray()
val checked = templates.map {
pack.config.applySettingTemplates.contains(it)
}.toBooleanArray()
val dialog = MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.app_choose_template)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok) { _, _ ->
pack.config.applySettingTemplates = templates.mapIndexedNotNullTo(mutableSetOf()) { i, name ->
if (checked[i]) name else null
}
updateApplySettingsTemplates()
}
if (templates.isNotEmpty()) {
dialog.setMultiChoiceItems(templates, checked) { _, i, value -> checked[i] = value }
} else {
val emptyView = LayoutListEmptyBinding.inflate(layoutInflater)
emptyView.root.isVisible = true
emptyView.listEmptyIcon.setImageResource(R.drawable.sentiment_very_dissatisfied_24px)
emptyView.listEmptyText.text = getString(R.string.title_template_manage)
dialog.setView(emptyView.root)
}
dialog.show()
true
}
}
findPreference<Preference>("applySettingsPresets")?.setOnPreferenceClickListener {
val presetNames = SettingsPresets.instance.getAllPresetNames()
val presetTranslations = presetNames.map { name ->
@ -343,6 +400,7 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
}
updateApplyTemplates()
updateApplyPresets()
updateApplySettingsTemplates()
updateApplySettingsPresets()
updateExtraAppList(pack.config.useWhitelist)
}

View file

@ -146,7 +146,7 @@ class LogsFragment : Fragment(R.layout.fragment_logs) {
binding.root.setOnApplyWindowInsetsListener { v, insets ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val barInsets = insets.getInsets(WindowInsets.Type.systemBars())
binding.root.setPadding(
v.setPadding(
barInsets.left,
barInsets.top,
barInsets.right,
@ -154,7 +154,7 @@ class LogsFragment : Fragment(R.layout.fragment_logs) {
)
} else {
@Suppress("deprecation")
binding.root.setPadding(
v.setPadding(
insets.systemWindowInsetLeft,
insets.systemWindowInsetTop,
insets.systemWindowInsetRight,

View file

@ -14,6 +14,7 @@ import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceDataStore
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SeekBarPreference
import androidx.preference.SwitchPreferenceCompat
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.color.DynamicColors
@ -34,6 +35,7 @@ import icu.nullptr.hidemyapplist.util.SuUtils
import kotlinx.coroutines.launch
import org.frknkrc44.hma_oss.R
import org.frknkrc44.hma_oss.databinding.FragmentSettingsBinding
import org.frknkrc44.hma_oss.ui.activity.BaseActivity
import java.util.Locale
class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
@ -60,7 +62,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
binding.root.setOnApplyWindowInsetsListener { v, insets ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val barInsets = insets.getInsets(WindowInsets.Type.systemBars())
binding.root.setPadding(
v.setPadding(
barInsets.left,
barInsets.top,
barInsets.right,
@ -68,7 +70,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
)
} else {
@Suppress("deprecation")
binding.root.setPadding(
v.setPadding(
insets.systemWindowInsetLeft,
insets.systemWindowInsetTop,
insets.systemWindowInsetRight,
@ -104,6 +106,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
"skipSystemAppDataIsolation" -> ConfigManager.skipSystemAppDataIsolation
"disableActivityLaunchProtection" -> ConfigManager.disableActivityLaunchProtection
"forceMountData" -> ConfigManager.forceMountData
"packageQueryWorkaround" -> ConfigManager.packageQueryWorkaround
else -> throw IllegalArgumentException("Invalid key: $key")
}
}
@ -118,6 +121,13 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
}
}
override fun getInt(key: String, defValue: Int): Int {
return when (key) {
"systemWallpaperAlpha" -> PrefManager.systemWallpaperAlpha
else -> throw IllegalArgumentException("Invalid key: $key")
}
}
override fun putBoolean(key: String, value: Boolean) {
when (key) {
"followSystemAccent" -> PrefManager.followSystemAccent = value
@ -131,6 +141,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
"appDataIsolation" -> ConfigManager.altAppDataIsolation = value
"voldAppDataIsolation" -> ConfigManager.altVoldAppDataIsolation = value
"skipSystemAppDataIsolation" -> ConfigManager.skipSystemAppDataIsolation = value
"packageQueryWorkaround" -> ConfigManager.packageQueryWorkaround = value
else -> throw IllegalArgumentException("Invalid key: $key")
}
}
@ -144,6 +155,13 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
else -> throw IllegalArgumentException("Invalid key: $key")
}
}
override fun putInt(key: String, value: Int) {
when (key) {
"systemWallpaperAlpha" -> PrefManager.systemWallpaperAlpha = value
else -> throw IllegalArgumentException("Invalid key: $key")
}
}
}
class DataIsolationPreferenceFragment : PreferenceFragmentCompat() {
@ -289,6 +307,14 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
}
}
findPreference<SeekBarPreference>("systemWallpaperAlpha")?.apply {
setOnPreferenceChangeListener { _, value ->
(requireActivity() as BaseActivity).applyWallpaperBackgroundColor(value as Int)
true
}
}
findPreference<Preference>("clearUninstalledPackageConfigs")?.apply {
setOnPreferenceClickListener {
MaterialAlertDialogBuilder(requireContext())
@ -313,9 +339,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
}
}
}
.setNegativeButton(android.R.string.cancel) { _, _ ->
// do nothing
}
.setNegativeButton(android.R.string.cancel, null)
.setCancelable(false)
.show()

View file

@ -4,13 +4,12 @@ import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.WindowInsets
import androidx.core.view.doOnPreDraw
import androidx.fragment.app.Fragment
import androidx.fragment.app.clearFragmentResultListener
import androidx.fragment.app.setFragmentResultListener
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.recyclerview.widget.LinearLayoutManager
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import icu.nullptr.hidemyapplist.common.JsonConfig
import icu.nullptr.hidemyapplist.service.ConfigManager
import icu.nullptr.hidemyapplist.ui.adapter.TemplateAdapter
@ -19,6 +18,8 @@ import icu.nullptr.hidemyapplist.ui.util.navigate
import icu.nullptr.hidemyapplist.ui.util.setupToolbar
import org.frknkrc44.hma_oss.R
import org.frknkrc44.hma_oss.databinding.FragmentTemplateManageBinding
import org.frknkrc44.hma_oss.ui.fragment.SettingsTemplateConfFragmentArgs
import org.frknkrc44.hma_oss.ui.util.toTargetSettingList
class TemplateManageFragment : Fragment(R.layout.fragment_template_manage) {
@ -30,16 +31,29 @@ class TemplateManageFragment : Fragment(R.layout.fragment_template_manage) {
toolbar = binding.toolbar,
title = getString(R.string.title_template_manage),
navigationIcon = R.drawable.baseline_arrow_back_24,
navigationOnClick = { navController.navigateUp() }
navigationOnClick = { navController.navigateUp() },
menuRes = R.menu.menu_template_manage,
onMenuOptionSelected = {
when (it.itemId) {
R.id.menu_info -> {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.title_template_manage)
.setMessage(R.string.template_usage_hint)
.setNegativeButton(android.R.string.ok, null)
.show()
}
}
}
)
postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() }
binding.newBlacklistTemplate.setOnClickListener {
navigateToSettings(ConfigManager.TemplateInfo(null, false))
navigateToSettings(ConfigManager.TemplateInfo(null, ConfigManager.PTType.APP, false))
}
binding.newWhitelistTemplate.setOnClickListener {
navigateToSettings(ConfigManager.TemplateInfo(null, true))
navigateToSettings(ConfigManager.TemplateInfo(null, ConfigManager.PTType.APP, true))
}
binding.newSettingTemplate.setOnClickListener {
navigateToSettings(ConfigManager.TemplateInfo(null, ConfigManager.PTType.SETTINGS, false))
}
binding.templateList.layoutManager = LinearLayoutManager(context)
binding.templateList.adapter = adapter
@ -47,7 +61,7 @@ class TemplateManageFragment : Fragment(R.layout.fragment_template_manage) {
binding.root.setOnApplyWindowInsetsListener { v, insets ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val barInsets = insets.getInsets(WindowInsets.Type.systemBars())
binding.root.setPadding(
v.setPadding(
barInsets.left,
barInsets.top,
barInsets.right,
@ -55,7 +69,7 @@ class TemplateManageFragment : Fragment(R.layout.fragment_template_manage) {
)
} else {
@Suppress("deprecation")
binding.root.setPadding(
v.setPadding(
insets.systemWindowInsetLeft,
insets.systemWindowInsetTop,
insets.systemWindowInsetRight,
@ -68,6 +82,13 @@ class TemplateManageFragment : Fragment(R.layout.fragment_template_manage) {
}
private fun navigateToSettings(info: ConfigManager.TemplateInfo) {
when (info.type) {
ConfigManager.PTType.APP -> navigateToAppTemplateSettings(info)
ConfigManager.PTType.SETTINGS -> navigateToSettingTemplateSettings(info)
}
}
private fun navigateToAppTemplateSettings(info: ConfigManager.TemplateInfo) {
setFragmentResultListener("template_settings") { _, bundle ->
fun deal() {
var name = bundle.getString("name")
@ -94,7 +115,42 @@ class TemplateManageFragment : Fragment(R.layout.fragment_template_manage) {
}
val args = TemplateSettingsFragmentArgs(info.name, info.isWhiteList)
val extras = FragmentNavigatorExtras(binding.hintCard to "transition_manage")
navigate(R.id.nav_template_settings, args.toBundle(), extras)
navigate(R.id.nav_template_settings, args.toBundle())
}
private fun navigateToSettingTemplateSettings(info: ConfigManager.TemplateInfo) {
setFragmentResultListener("settings_template_conf") { _, bundle ->
fun deal() {
var name = bundle.getString("name")
val appliedList = bundle.getStringArrayList("appliedList")!!
val targetList = bundle.getBundle("settingList")!!.toTargetSettingList()
if (info.name == null) { // New template
if (name.isNullOrEmpty()) return
ConfigManager.updateSettingTemplate(
name,
JsonConfig.SettingsTemplate(targetList.toSet())
)
ConfigManager.updateSettingTemplateAppliedApps(name, appliedList)
} else { // Existing template
if (name == null) {
ConfigManager.deleteSettingTemplate(info.name)
} else {
if (name.isEmpty()) name = info.name
if (name != info.name) ConfigManager.renameSettingTemplate(info.name, name)
ConfigManager.updateSettingTemplate(
name,
JsonConfig.SettingsTemplate(targetList.toSet())
)
ConfigManager.updateSettingTemplateAppliedApps(name, appliedList)
}
}
}
deal()
adapter.updateList()
clearFragmentResultListener("settings_template_conf")
}
val args = SettingsTemplateConfFragmentArgs(info.name)
navigate(R.id.nav_setting_template_conf, args.toBundle())
}
}

View file

@ -114,7 +114,7 @@ class TemplateSettingsFragment : Fragment(R.layout.fragment_template_settings) {
binding.root.setOnApplyWindowInsetsListener { v, insets ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val barInsets = insets.getInsets(WindowInsets.Type.systemBars())
binding.root.setPadding(
v.setPadding(
barInsets.left,
barInsets.top,
barInsets.right,
@ -122,7 +122,7 @@ class TemplateSettingsFragment : Fragment(R.layout.fragment_template_settings) {
)
} else {
@Suppress("deprecation")
binding.root.setPadding(
v.setPadding(
insets.systemWindowInsetLeft,
insets.systemWindowInsetTop,
insets.systemWindowInsetRight,

View file

@ -12,7 +12,6 @@ import androidx.appcompat.widget.Toolbar
import androidx.core.view.MenuProvider
import androidx.fragment.app.Fragment
import androidx.navigation.NavOptions
import androidx.navigation.Navigator
import androidx.navigation.fragment.NavHostFragment
import org.frknkrc44.hma_oss.R
@ -31,10 +30,6 @@ fun Fragment.navigate(@IdRes resId: Int, args: Bundle? = null) {
navController.navigate(resId, args, navOptions)
}
fun Fragment.navigate(@IdRes resId: Int, args: Bundle? = null, navExtras: Navigator.Extras?) {
navController.navigate(resId, args, navOptions, navExtras)
}
fun Fragment.setupToolbar(
toolbar: Toolbar,
title: String,

View file

@ -19,7 +19,7 @@ class ListItemView @JvmOverloads constructor(
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
private val binding by viewBinding<ListItemViewBinding>(createMethod = CreateMethod.INFLATE)
val binding by viewBinding<ListItemViewBinding>(createMethod = CreateMethod.INFLATE)
init {
val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.ListItemView, defStyleAttr, defStyleRes)

View file

@ -6,7 +6,9 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory
import icu.nullptr.hidemyapplist.common.Constants
import icu.nullptr.hidemyapplist.hmaApp
import icu.nullptr.hidemyapplist.service.ConfigManager
import icu.nullptr.hidemyapplist.service.PrefManager
import icu.nullptr.hidemyapplist.service.ServiceClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.first
@ -74,14 +76,30 @@ object PackageHelper {
isRefreshing.emit(true)
val cache = withContext(Dispatchers.IO) {
val pm = hmaApp.packageManager
val packages = pm.getInstalledPackages(0)
mutableMapOf<String, PackageCache>().also {
for (packageInfo in packages) {
if (packageInfo.packageName in Constants.packagesShouldNotHide) continue
packageInfo.applicationInfo?.let { appInfo ->
val label = pm.getApplicationLabel(appInfo).toString()
val icon = hmaApp.appIconLoader.loadIcon(appInfo)
it[packageInfo.packageName] = PackageCache(packageInfo, label, icon)
if (ConfigManager.packageQueryWorkaround) {
val packages = ServiceClient.getPackageNames(0) ?: arrayOf<String>()
mutableMapOf<String, PackageCache>().also {
for (packageName in packages) {
val packageInfo = ServiceClient.getPackageInfo(packageName, 0)!!
if (packageInfo.packageName in Constants.packagesShouldNotHide) continue
packageInfo.applicationInfo?.let { appInfo ->
val label = pm.getApplicationLabel(appInfo).toString()
val icon = hmaApp.appIconLoader.loadIcon(appInfo)
it[packageInfo.packageName] = PackageCache(packageInfo, label, icon)
}
}
}
} else {
val packages = pm.getInstalledPackages(0)
mutableMapOf<String, PackageCache>().also {
for (packageInfo in packages) {
if (packageInfo.packageName in Constants.packagesShouldNotHide) continue
packageInfo.applicationInfo?.let { appInfo ->
val label = pm.getApplicationLabel(appInfo).toString()
val icon = hmaApp.appIconLoader.loadIcon(appInfo)
it[packageInfo.packageName] = PackageCache(packageInfo, label, icon)
}
}
}
}

View file

@ -5,9 +5,11 @@ import android.content.res.Resources
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.drawable.toDrawable
import com.google.android.material.color.DynamicColors
import com.google.android.material.color.DynamicColorsOptions
import icu.nullptr.hidemyapplist.hmaApp
import icu.nullptr.hidemyapplist.service.PrefManager
import icu.nullptr.hidemyapplist.ui.util.ThemeUtils
import icu.nullptr.hidemyapplist.util.ConfigUtils.Companion.getLocale
@ -17,6 +19,11 @@ open class BaseActivity : AppCompatActivity() {
enableEdgeToEdge()
// I add this manually because the E2E code is not working like I want
// They should give us a separate method to choice it for enableEdgeToEdge
// Source: https://github.com/androidx/androidx/blob/c0f9aabcf6f32029249ac7647711744b68e2a003/activity/activity/src/main/java/androidx/activity/EdgeToEdge.kt#L299
window.isNavigationBarContrastEnforced = !PrefManager.systemWallpaper
DynamicColors.applyToActivityIfAvailable(
this,
DynamicColorsOptions.Builder().also {
@ -33,6 +40,20 @@ open class BaseActivity : AppCompatActivity() {
}
theme.applyStyle(ThemeUtils.getOverlayThemeStyleRes(this), true)
applyWallpaperBackgroundColor()
}
fun applyWallpaperBackgroundColor(value: Int = PrefManager.systemWallpaperAlpha) {
if (PrefManager.systemWallpaper) {
val color = (value shl 24) + if (ThemeUtils.isNightMode(this)) {
0x00000000
} else {
0x00FFFFFF
}
window.setBackgroundDrawable(color.toDrawable())
}
}
override fun attachBaseContext(newBase: Context?) {

View file

@ -30,8 +30,8 @@ class AppPresetListAdapter(
} else {
setIcon(
when (item.type) {
ConfigManager.PresetType.APP -> R.drawable.baseline_assignment_24
ConfigManager.PresetType.SETTINGS -> R.drawable.baseline_settings_24
ConfigManager.PTType.APP -> R.drawable.baseline_assignment_24
ConfigManager.PTType.SETTINGS -> R.drawable.baseline_settings_24
}
)
@ -88,7 +88,7 @@ class AppPresetListAdapter(
list += appPresetNames
.map { ConfigManager.PresetInfo(
it,
ConfigManager.PresetType.APP,
ConfigManager.PTType.APP,
appPresetTranslations[appPresetNames.indexOf(it)]
) }
.sortedWith { a, b -> a.translation.lowercase().compareTo(b.translation.lowercase()) }
@ -117,7 +117,7 @@ class AppPresetListAdapter(
list += settingsPresetNames
.map { ConfigManager.PresetInfo(
it,
ConfigManager.PresetType.SETTINGS,
ConfigManager.PTType.SETTINGS,
settingsPresetTranslations[settingsPresetNames.indexOf(it)]
) }
.sortedWith { a, b -> a.translation.lowercase().compareTo(b.translation.lowercase()) }

View file

@ -0,0 +1,40 @@
package org.frknkrc44.hma_oss.ui.adapter
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import icu.nullptr.hidemyapplist.common.settings_presets.ReplacementItem
import icu.nullptr.hidemyapplist.ui.view.AppItemView
abstract class BaseSettingsPTAdapter() : RecyclerView.Adapter<BaseSettingsPTAdapter.ViewHolder>() {
abstract val items: List<ReplacementItem>
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = AppItemView(parent.context, false).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(items[position])
override fun getItemCount() = items.size
abstract fun onItemClick(item: ReplacementItem)
inner class ViewHolder(view: AppItemView) : RecyclerView.ViewHolder(view) {
fun bind(item: ReplacementItem) {
(itemView as AppItemView).apply {
binding.icon.isVisible = false
binding.label.text = item.name
binding.enabled.isVisible = true
binding.enabled.text = item.database
binding.packageName.text = item.value ?: "null"
binding.root.setOnClickListener { onItemClick(item) }
}
}
}
}

View file

@ -1,37 +1,14 @@
package org.frknkrc44.hma_oss.ui.adapter
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import icu.nullptr.hidemyapplist.common.SettingsPresets
import icu.nullptr.hidemyapplist.common.settings_presets.ReplacementItem
import icu.nullptr.hidemyapplist.ui.view.AppItemView
class SettingsPresetListAdapter(presetName: String) : RecyclerView.Adapter<SettingsPresetListAdapter.ViewHolder>() {
private val presetItems by lazy {
SettingsPresets.instance.getPresetByName(presetName)!!.settingsKVPairs.sortedBy { it.name }
class SettingsPresetListAdapter(name: String) : BaseSettingsPTAdapter() {
override val items by lazy {
SettingsPresets.instance.getPresetByName(name)!!.settingsKVPairs.sortedBy { it.name }
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = AppItemView(parent.context, false)
view.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(presetItems[position])
override fun getItemCount() = presetItems.size
class ViewHolder(view: AppItemView) : RecyclerView.ViewHolder(view) {
fun bind(item: ReplacementItem) {
(itemView as AppItemView).apply {
binding.icon.isVisible = false
binding.label.text = item.name
binding.packageName.text = item.value ?: "null"
}
}
override fun onItemClick(item: ReplacementItem) {
// do nothing
}
}

View file

@ -0,0 +1,21 @@
package org.frknkrc44.hma_oss.ui.adapter
import android.os.Bundle
import icu.nullptr.hidemyapplist.common.settings_presets.ReplacementItem
import org.frknkrc44.hma_oss.ui.util.targetSettingListToBundle
import org.frknkrc44.hma_oss.ui.util.toTargetSettingList
class SettingsTemplateListAdapter(
items: Bundle,
private val onItemClickListener: (SettingsTemplateListAdapter, ReplacementItem) -> Unit
) : BaseSettingsPTAdapter() {
override val items by lazy {
val list = mutableListOf<ReplacementItem>()
list.addAll(items.toTargetSettingList())
return@lazy list
}
fun targetSettingListToBundle() = items.targetSettingListToBundle()
override fun onItemClick(item: ReplacementItem) = onItemClickListener(this, item)
}

Some files were not shown because too many files have changed in this diff Show more