From 74c9313c46efb43b41d19dc11ff040dea46a1495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Karc=C4=B1o=C4=9Flu?= <45714956+frknkrc44@users.noreply.github.com> Date: Sat, 25 Oct 2025 02:44:33 +0300 Subject: [PATCH] Redesign 2 (#31) * Begin design again * Improve Switch dimensions * Add empty settings preset fragment * Finish settings presets fragment * Add git branch to version name * Add sorting for preset keys, add new replaced settings * Remove unneeded string, fix force stop * Fix CI issue * Remove animation speed spoofing * Add "Use system wallpaper" option * Fix Switch colors * Add GitHub-only translators into translator list * Simplify translator getter * Fix About fragment button colors for wallpaper mode * Add new SwipeRefreshLayout colors * Add no transparency mode for home item background color, optimize imports * Replace ugly serif * Add enabled/disabled state colors for Switch * Dirty fix for system wallpaper mode is not activated * Remove ability to hide something from HMA-OSS itself * Replace Java codes with Kotlin * Move CommonUtils to PropertyUtils, move packages into Constants * Add localBuild as used --- app/build.gradle.kts | 28 +++--- .../java/icu/nullptr/hidemyapplist/MyApp.kt | 8 ++ .../hidemyapplist/service/ConfigManager.kt | 6 +- .../hidemyapplist/service/PrefManager.kt | 5 + .../hidemyapplist/service/ServiceClient.kt | 61 ++++-------- .../ui/adapter/AppManageAdapter.kt | 4 +- .../ui/fragment/AppSelectFragment.kt | 16 +++- .../ui/fragment/AppSettingsFragment.kt | 46 ++++++++- .../ui/fragment/SettingsFragment.kt | 36 +++++-- .../nullptr/hidemyapplist/ui/util/Fragment.kt | 4 +- .../hidemyapplist/ui/util/ThemeUtils.kt | 23 ++--- .../hidemyapplist/ui/view/AppItemView.kt | 3 +- .../hidemyapplist/ui/view/ListItemView.kt | 25 ++++- .../hma_oss/ui/activity/BaseActivity.kt | 2 +- .../ui/adapter/AppPresetListAdapter.kt | 81 +++++++++++++--- .../ui/adapter/SettingsPresetListAdapter.kt | 37 +++++++ .../hma_oss/ui/fragment/AboutFragment.kt | 6 ++ .../hma_oss/ui/fragment/HomeFragment.kt | 21 +++- .../ui/fragment/PresetManageFragment.kt | 12 ++- .../ui/fragment/SettingsPresetFragment.kt | 83 ++++++++++++++++ .../main/res/anim/activity_close_enter.xml | 40 ++++++++ app/src/main/res/anim/activity_close_exit.xml | 2 +- app/src/main/res/anim/activity_open_enter.xml | 2 +- app/src/main/res/anim/activity_open_exit.xml | 41 ++++++++ app/src/main/res/anim/fade_in.xml | 6 -- app/src/main/res/anim/fade_out.xml | 6 -- .../res/drawable/baseline_wallpaper_24.xml | 10 ++ app/src/main/res/drawable/switch_thumb.xml | 36 +++++++ app/src/main/res/drawable/switch_track.xml | 40 ++++++++ .../main/res/layout/fragment_app_select.xml | 1 + app/src/main/res/layout/fragment_home.xml | 1 + app/src/main/res/layout/fragment_logs.xml | 2 +- .../res/layout/fragment_preset_manage.xml | 1 + app/src/main/res/layout/fragment_settings.xml | 1 + .../res/layout/fragment_settings_preset.xml | 35 +++++++ .../res/layout/fragment_template_manage.xml | 1 + .../res/layout/fragment_template_settings.xml | 1 + app/src/main/res/layout/log_item_view.xml | 2 +- app/src/main/res/layout/switch_compat.xml | 10 +- .../main/res/navigation/home_nav_graph.xml | 33 +++++++ .../main/res/values-night/themes_overlay.xml | 33 +++++++ app/src/main/res/values/arrays.xml | 5 + app/src/main/res/values/colors.xml | 4 + app/src/main/res/values/strings.xml | 6 ++ app/src/main/res/values/themes_overlay.xml | 9 +- app/src/main/res/xml/app_settings.xml | 4 +- app/src/main/res/xml/settings.xml | 4 + build.gradle.kts | 17 +++- .../hidemyapplist/common/IHMAService.aidl | 4 + .../hidemyapplist/common/CommonUtils.kt | 17 ---- .../hidemyapplist/common/Constants.java | 39 -------- .../nullptr/hidemyapplist/common/Constants.kt | 32 +++++++ .../hidemyapplist/common/PropertyUtils.kt | 14 +++ .../common/settings_presets/BasePreset.kt | 2 +- .../DeveloperOptionsPreset.kt | 96 +++++++++++++++++-- .../hidemyapplist/xposed/HMAService.kt | 21 +++- .../nullptr/hidemyapplist/xposed/Logcat.kt | 10 +- .../xposed/hook/PlatformCompatHook.kt | 33 +++---- 58 files changed, 902 insertions(+), 226 deletions(-) create mode 100644 app/src/main/java/org/frknkrc44/hma_oss/ui/adapter/SettingsPresetListAdapter.kt create mode 100644 app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/SettingsPresetFragment.kt create mode 100644 app/src/main/res/anim/activity_close_enter.xml create mode 100644 app/src/main/res/anim/activity_open_exit.xml delete mode 100644 app/src/main/res/anim/fade_in.xml delete mode 100644 app/src/main/res/anim/fade_out.xml create mode 100644 app/src/main/res/drawable/baseline_wallpaper_24.xml create mode 100644 app/src/main/res/drawable/switch_thumb.xml create mode 100644 app/src/main/res/drawable/switch_track.xml create mode 100644 app/src/main/res/layout/fragment_settings_preset.xml create mode 100644 app/src/main/res/values-night/themes_overlay.xml delete mode 100644 common/src/main/java/icu/nullptr/hidemyapplist/common/CommonUtils.kt delete mode 100644 common/src/main/java/icu/nullptr/hidemyapplist/common/Constants.java create mode 100644 common/src/main/java/icu/nullptr/hidemyapplist/common/Constants.kt create mode 100644 common/src/main/java/icu/nullptr/hidemyapplist/common/PropertyUtils.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4df4f40..5439326 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -58,6 +58,18 @@ val officialBuild: Boolean by rootProject.extra @Suppress("deprecation") afterEvaluate { + val srcDir = android.sourceSets["main"].assets.srcDirs.first() + logger.lifecycle("Asset dir: $srcDir") + if (!srcDir.exists()) srcDir.mkdirs() + + val translatorsMap = mutableMapOf( + // I used GitHub to get translations before moving to Crowdin. + // Since nearly all of GitHub translators are listed in Crowdin + // too, I wanted to add one profile here. + + "cvnertnc" to "https://avatars.githubusercontent.com/u/148134890?v=4", + ) + if (localBuild || officialBuild) { val url = URL("https://crowdin.com/api/v2/projects/$crowdinProjectId/members") val urlConnection = url.openConnection() as HttpURLConnection @@ -70,7 +82,7 @@ afterEvaluate { val json = JsonParser.parseString(str).asJsonObject val translators = json.getAsJsonArray("data") - val translatorsMap = mutableMapOf() + for (item in translators) { val translator = item.asJsonObject.getAsJsonObject("data") val avatarUrl = translator.get("avatarUrl").asString @@ -87,18 +99,10 @@ afterEvaluate { translatorsMap[username] = avatarUrl } } - - val translatorJson = JSONObject(translatorsMap).toJSONString() - val srcDir = android.sourceSets["main"].assets.srcDirs.first() - logger.lifecycle("Asset dir: $srcDir") - if (!srcDir.exists()) srcDir.mkdirs() - File(srcDir, "translators.json").writeText(translatorJson) - } else { - val srcDir = android.sourceSets["main"].assets.srcDirs.first() - logger.lifecycle("Asset dir: $srcDir") - if (!srcDir.exists()) srcDir.mkdirs() - File(srcDir, "translators.json").writeText("{}") } + + val translatorJson = JSONObject(translatorsMap).toJSONString() + File(srcDir, "translators.json").writeText(translatorJson) } android { diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/MyApp.kt b/app/src/main/java/icu/nullptr/hidemyapplist/MyApp.kt index f9dbf11..e832624 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/MyApp.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/MyApp.kt @@ -2,10 +2,12 @@ package icu.nullptr.hidemyapplist import android.annotation.SuppressLint import android.app.Application +import android.util.Log import androidx.appcompat.app.AppCompatDelegate import icu.nullptr.hidemyapplist.receiver.AppChangeReceiver import icu.nullptr.hidemyapplist.service.ConfigManager import icu.nullptr.hidemyapplist.service.PrefManager +import icu.nullptr.hidemyapplist.service.ServiceClient import icu.nullptr.hidemyapplist.ui.util.showToast import icu.nullptr.hidemyapplist.util.ConfigUtils.Companion.getLocale import kotlinx.coroutines.CoroutineScope @@ -39,5 +41,11 @@ class MyApp : Application() { val config = resources.configuration config.setLocale(getLocale()) resources.updateConfiguration(config, resources.displayMetrics) + + val handler = Thread.getDefaultUncaughtExceptionHandler() + Thread.setDefaultUncaughtExceptionHandler { t, e -> + ServiceClient.log(Log.ERROR, t.name, e.stackTraceToString()) + handler?.uncaughtException(t, e) + } } } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt b/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt index b7cf770..b710d33 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt @@ -10,9 +10,13 @@ import org.frknkrc44.hma_oss.common.BuildConfig import java.io.File object ConfigManager { + enum class PresetType { + APP, + SETTINGS, + } data class TemplateInfo(val name: String?, val isWhiteList: Boolean) - data class PresetInfo(val name: String, val translation: String) + data class PresetInfo(val name: String, val type: PresetType?, val translation: String) private const val TAG = "ConfigManager" private lateinit var config: JsonConfig diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/service/PrefManager.kt b/app/src/main/java/icu/nullptr/hidemyapplist/service/PrefManager.kt index eef3c8e..bb8f817 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/service/PrefManager.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/service/PrefManager.kt @@ -13,6 +13,7 @@ object PrefManager { private const val PREF_LOCALE = "language" + private const val PREF_SYSTEM_WALLPAPER = "system_wallpaper" 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" @@ -45,6 +46,10 @@ object PrefManager { get() = pref.getInt(PREF_DARK_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) set(value) = pref.edit().putInt(PREF_DARK_THEME, value).apply() + var systemWallpaper: Boolean + get() = pref.getBoolean(PREF_SYSTEM_WALLPAPER, false) + set(value) = pref.edit().putBoolean(PREF_SYSTEM_WALLPAPER, value).apply() + var blackDarkTheme: Boolean get() = pref.getBoolean(PREF_BLACK_DARK_THEME, false) set(value) = pref.edit().putBoolean(PREF_BLACK_DARK_THEME, value).apply() diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/service/ServiceClient.kt b/app/src/main/java/icu/nullptr/hidemyapplist/service/ServiceClient.kt index 97d4be1..1ca1694 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/service/ServiceClient.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/service/ServiceClient.kt @@ -1,11 +1,7 @@ package icu.nullptr.hidemyapplist.service import android.os.IBinder -import android.os.Parcel -import android.os.RemoteException -import android.os.ServiceManager import android.util.Log -import icu.nullptr.hidemyapplist.common.Constants import icu.nullptr.hidemyapplist.common.IHMAService import java.lang.reflect.InvocationHandler import java.lang.reflect.Method @@ -36,37 +32,6 @@ object ServiceClient : IHMAService, IBinder.DeathRecipient { binder.linkToDeath(this, 0) } - private fun getServiceLegacy(): IHMAService? { - if (service != null) return service - val pm = ServiceManager.getService("package") - val data = Parcel.obtain() - val reply = Parcel.obtain() - val remote = try { - data.writeInterfaceToken(Constants.DESCRIPTOR) - data.writeInt(Constants.ACTION_GET_BINDER) - pm.transact(Constants.TRANSACTION, data, reply, 0) - reply.readException() - val binder = reply.readStrongBinder() - IHMAService.Stub.asInterface(binder) - } catch (e: RemoteException) { - Log.d(TAG, "Failed to get binder") - null - } finally { - data.recycle() - reply.recycle() - } - if (remote != null) { - Log.i(TAG, "Binder acquired") - remote.asBinder().linkToDeath(this, 0) - service = Proxy.newProxyInstance( - javaClass.classLoader, - arrayOf(IHMAService::class.java), - ServiceProxy(remote) - ) as IHMAService - } - return service - } - override fun binderDied() { service = null Log.e(TAG, "Binder died") @@ -74,30 +39,38 @@ object ServiceClient : IHMAService, IBinder.DeathRecipient { override fun asBinder() = service?.asBinder() - override fun getServiceVersion() = getServiceLegacy()?.serviceVersion ?: 0 + override fun getServiceVersion() = service?.serviceVersion ?: 0 - override fun getFilterCount() = getServiceLegacy()?.filterCount ?: 0 + override fun getFilterCount() = service?.filterCount ?: 0 - override fun getLogs() = getServiceLegacy()?.logs + override fun getLogs() = service?.logs override fun clearLogs() { - getServiceLegacy()?.clearLogs() + service?.clearLogs() } override fun handlePackageEvent(eventType: String?, packageName: String?) { - getServiceLegacy()?.handlePackageEvent(eventType, packageName) + service?.handlePackageEvent(eventType, packageName) } override fun getPackagesForPreset(presetName: String) = - getServiceLegacy()?.getPackagesForPreset(presetName) + service?.getPackagesForPreset(presetName) - override fun readConfig() = getServiceLegacy()?.readConfig() + override fun readConfig() = service?.readConfig() override fun writeConfig(json: String) { - getServiceLegacy()?.writeConfig(json) + service?.writeConfig(json) } override fun stopService(cleanEnv: Boolean) { - getServiceLegacy()?.stopService(cleanEnv) + service?.stopService(cleanEnv) + } + + override fun forceStop(packageName: String, userId: Int) { + service?.forceStop(packageName, userId) + } + + override fun log(level: Int, tag: String, message: String) { + service?.log(level, tag, message) } } \ No newline at end of file diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppManageAdapter.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppManageAdapter.kt index f13daf0..a472aef 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppManageAdapter.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppManageAdapter.kt @@ -2,7 +2,7 @@ package icu.nullptr.hidemyapplist.ui.adapter import android.view.ViewGroup import com.google.android.material.dialog.MaterialAlertDialogBuilder -import icu.nullptr.hidemyapplist.common.CommonUtils +import icu.nullptr.hidemyapplist.common.Constants import icu.nullptr.hidemyapplist.service.ConfigManager import icu.nullptr.hidemyapplist.service.PrefManager import icu.nullptr.hidemyapplist.ui.view.AppItemView @@ -15,7 +15,7 @@ class AppManageAdapter( inner class ViewHolder(view: AppItemView) : AppSelectAdapter.ViewHolder(view) { init { view.setOnClickListener { - if (!PrefManager.bypassRiskyPackageWarning && CommonUtils.riskyPackages.contains(view.binding.packageName.text)) { + if (!PrefManager.bypassRiskyPackageWarning && Constants.riskyPackages.contains(view.binding.packageName.text)) { MaterialAlertDialogBuilder(view.context) .setTitle(R.string.app_warning_risky_package_title) .setMessage(R.string.app_warning_risky_package_desc) diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt index f6e1285..e5f58b4 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt @@ -16,6 +16,8 @@ import androidx.recyclerview.widget.RecyclerView import by.kirich1409.viewbindingdelegate.viewBinding import icu.nullptr.hidemyapplist.service.PrefManager import icu.nullptr.hidemyapplist.ui.adapter.AppSelectAdapter +import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.homeItemBackgroundColor +import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.themeColor import icu.nullptr.hidemyapplist.ui.util.navController import icu.nullptr.hidemyapplist.ui.util.setupToolbar import icu.nullptr.hidemyapplist.util.PackageHelper @@ -118,8 +120,18 @@ abstract class AppSelectFragment : Fragment(R.layout.fragment_app_select) { } binding.list.layoutManager = LinearLayoutManager(context) binding.list.adapter = adapter - binding.swipeRefresh.setOnRefreshListener { - invalidateCache() + + with(binding.swipeRefresh) { + setProgressBackgroundColorSchemeColor(homeItemBackgroundColor(true)) + setColorSchemeColors( + themeColor(androidx.appcompat.R.attr.colorPrimary), + themeColor(com.google.android.material.R.attr.colorSecondary), + themeColor(com.google.android.material.R.attr.colorTertiary), + ) + + setOnRefreshListener { + invalidateCache() + } } adapter.registerAdapterDataObserver( diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSettingsFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSettingsFragment.kt index 9135725..c4b6e54 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSettingsFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSettingsFragment.kt @@ -3,6 +3,7 @@ package icu.nullptr.hidemyapplist.ui.fragment import android.annotation.SuppressLint import android.os.Build import android.os.Bundle +import android.util.Log import android.view.View import android.view.WindowInsets import android.widget.Toast @@ -23,10 +24,12 @@ 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.service.ServiceClient import icu.nullptr.hidemyapplist.ui.util.enabledString import icu.nullptr.hidemyapplist.ui.util.navController import icu.nullptr.hidemyapplist.ui.util.navigate import icu.nullptr.hidemyapplist.ui.util.setupToolbar +import icu.nullptr.hidemyapplist.ui.util.showToast import icu.nullptr.hidemyapplist.ui.viewmodel.AppSettingsViewModel import icu.nullptr.hidemyapplist.util.PackageHelper import org.frknkrc44.hma_oss.BuildConfig @@ -34,6 +37,9 @@ import org.frknkrc44.hma_oss.R import org.frknkrc44.hma_oss.databinding.FragmentSettingsBinding class AppSettingsFragment : Fragment(R.layout.fragment_settings) { + companion object { + private const val TAG = "AppSettingsFragment" + } private val binding by viewBinding() private val viewModel by viewModels() { @@ -157,6 +163,24 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) { else getString(R.string.app_extra_apps_invisible_count, pack.config.extraAppList.size) } + private fun launchMainActivity(packageName: String) { + try { + val pkgMgr = requireContext().packageManager + val pkgInfo = pkgMgr.getPackageInfo(packageName, 0) + if (pkgInfo.applicationInfo?.enabled == true) { + val resolvedIntent = pkgMgr.getLaunchIntentForPackage(packageName) + if (resolvedIntent != null) { + startActivity(resolvedIntent) + } + } else { + throw RuntimeException("Package is disabled") + } + } catch (e: Throwable) { + showToast(R.string.app_launch_failed) + ServiceClient.log(Log.ERROR, TAG, e.stackTraceToString()) + } + } + @SuppressLint("DiscouragedApi") override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { preferenceManager.preferenceDataStore = AppPreferenceDataStore(pack) @@ -164,7 +188,27 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) { findPreference("appInfo")?.let { it.icon = PackageHelper.loadAppIcon(pack.app).toDrawable(resources) it.title = PackageHelper.loadAppLabel(pack.app) - it.summary = PackageHelper.loadPackageInfo(pack.app).packageName + it.summary = pack.app + it.setOnPreferenceClickListener { pref -> + MaterialAlertDialogBuilder(pref.context).apply { + setTitle(it.title) + setItems( + R.array.app_action_texts, + ) { dialog, which -> + when (which) { + 0 -> { + ServiceClient.forceStop(pack.app, 0) + launchMainActivity(pack.app) + } + 1 -> { + launchMainActivity(pack.app) + } + } + } + }.show() + + true + } } findPreference("hideInstallationSource")?.setOnPreferenceChangeListener { _, newValue -> Toast.makeText(requireContext(), diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt index be2d244..35f7b33 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt @@ -17,7 +17,7 @@ import androidx.preference.SwitchPreferenceCompat import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.color.DynamicColors import com.google.android.material.dialog.MaterialAlertDialogBuilder -import icu.nullptr.hidemyapplist.common.CommonUtils +import icu.nullptr.hidemyapplist.common.PropertyUtils import icu.nullptr.hidemyapplist.common.Constants import icu.nullptr.hidemyapplist.hmaApp import icu.nullptr.hidemyapplist.service.ConfigManager @@ -92,6 +92,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen override fun getBoolean(key: String, defValue: Boolean): Boolean { return when (key) { "followSystemAccent" -> PrefManager.followSystemAccent + "systemWallpaper" -> PrefManager.systemWallpaper "blackDarkTheme" -> PrefManager.blackDarkTheme "detailLog" -> ConfigManager.detailLog "hideIcon" -> PrefManager.hideIcon @@ -118,6 +119,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen override fun putBoolean(key: String, value: Boolean) { when (key) { "followSystemAccent" -> PrefManager.followSystemAccent = value + "systemWallpaper" -> PrefManager.systemWallpaper = value "blackDarkTheme" -> PrefManager.blackDarkTheme = value "detailLog" -> ConfigManager.detailLog = value "forceMountData" -> ConfigManager.forceMountData = value @@ -151,7 +153,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen it.summary = getString(R.string.settings_need_reboot) + "\n\n" + getString( R.string.settings_default_value, - CommonUtils.isAppDataIsolationEnabled.enabledString(resources) + PropertyUtils.isAppDataIsolationEnabled.enabledString(resources) ) } @@ -159,7 +161,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen it.summary = getString(R.string.settings_need_reboot) + "\n\n" + getString( R.string.settings_default_value, - CommonUtils.isVoldAppDataIsolationEnabled.enabledString(resources) + PropertyUtils.isVoldAppDataIsolationEnabled.enabledString(resources) ) it.setOnPreferenceChangeListener { _, newValue -> @@ -191,9 +193,9 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen it.isEnabled -> getString( R.string.settings_data_isolation_summary, if (ConfigManager.altAppDataIsolation) getString(R.string.settings_overwritten) - else CommonUtils.isAppDataIsolationEnabled.enabledString(resources), + else PropertyUtils.isAppDataIsolationEnabled.enabledString(resources), if (ConfigManager.altVoldAppDataIsolation) getString(R.string.settings_overwritten) - else CommonUtils.isVoldAppDataIsolationEnabled.enabledString(resources), + else PropertyUtils.isVoldAppDataIsolationEnabled.enabledString(resources), ConfigManager.forceMountData.enabledString(resources) ) else -> getString(R.string.settings_data_isolation_unsupported) @@ -270,9 +272,27 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen true } - findPreference("blackDarkTheme")?.setOnPreferenceChangeListener { _, _ -> - activity?.recreate() - true + findPreference("systemWallpaper")?.apply { + isEnabled = findPreference("blackDarkTheme")?.isChecked != true + setOnPreferenceChangeListener { _, value -> + val activity = requireActivity() + if (value as Boolean) { + activity.finish() + startActivity(Intent(activity, activity.javaClass)) + } else { + activity.recreate() + } + + true + } + } + + findPreference("blackDarkTheme")?.apply { + isEnabled = findPreference("systemWallpaper")?.isChecked != true + setOnPreferenceChangeListener { _, _ -> + activity?.recreate() + true + } } configureDataIsolation() diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/util/Fragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/util/Fragment.kt index 81f85ae..f1db7f0 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/util/Fragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/util/Fragment.kt @@ -21,8 +21,8 @@ val Fragment.navController get() = NavHostFragment.findNavController(this) private val navOptions by lazy { NavOptions.Builder().apply { setEnterAnim(R.anim.activity_open_enter) - setExitAnim(R.anim.fade_out) - setPopEnterAnim(R.anim.fade_in) + setExitAnim(R.anim.activity_open_exit) + setPopEnterAnim(R.anim.activity_close_enter) setPopExitAnim(R.anim.activity_close_exit) }.build() } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/util/ThemeUtils.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/util/ThemeUtils.kt index 56b49a5..0b183bd 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/util/ThemeUtils.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/util/ThemeUtils.kt @@ -58,24 +58,18 @@ object ThemeUtils { "MATERIAL_BLUE_GREY" to R.style.ThemeOverlay_Dark_MaterialBlueGrey ) - private const val THEME_DEFAULT = "DEFAULT" - private const val THEME_BLACK = "BLACK" - val isSystemAccent get() = DynamicColors.isDynamicColorAvailable() && PrefManager.followSystemAccent fun isNightMode(context: Context) = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES fun isUsingBlackTheme(context: Context) = PrefManager.blackDarkTheme && isNightMode(context) - fun getNightTheme(context: Context): String { - return if (isUsingBlackTheme(context)) - THEME_BLACK else THEME_DEFAULT - } - @StyleRes - fun getNightThemeStyleRes(context: Context): Int { - return if (isUsingBlackTheme(context)) - R.style.ThemeOverlay_Black else R.style.ThemeOverlay + fun getOverlayThemeStyleRes(context: Context): Int { + if (isUsingBlackTheme(context)) return R.style.ThemeOverlay_Black + if (PrefManager.systemWallpaper) return R.style.ThemeOverlay_Wallpaper + + return R.style.ThemeOverlay } val colorTheme get() = if (isSystemAccent) "SYSTEM" else PrefManager.themeColor @@ -125,11 +119,14 @@ object ThemeUtils { @ColorRes colorId: Int ) = requireContext().getColor(colorId) - fun Context.homeItemBackgroundColor() = if (isNightMode(this)) { + fun Context.homeItemBackgroundColor(forceNoTrans: Boolean = false) = (if (isNightMode(this)) { themeColor(com.google.android.material.R.attr.colorSurfaceContainerHighest) } else { themeColor(com.google.android.material.R.attr.colorSurfaceContainer) + }).let { + if (!forceNoTrans && PrefManager.systemWallpaper) return@let it - 0x55000000 + return@let it } - fun Fragment.homeItemBackgroundColor() = requireContext().homeItemBackgroundColor() + fun Fragment.homeItemBackgroundColor(forceNoTrans: Boolean = false) = requireContext().homeItemBackgroundColor(forceNoTrans) } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/AppItemView.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/AppItemView.kt index 1589e39..7a35222 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/AppItemView.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/AppItemView.kt @@ -3,6 +3,7 @@ package icu.nullptr.hidemyapplist.ui.view import android.content.Context import android.util.AttributeSet import android.widget.LinearLayout +import androidx.core.view.isVisible import by.kirich1409.viewbindingdelegate.CreateMethod import by.kirich1409.viewbindingdelegate.viewBinding import icu.nullptr.hidemyapplist.util.PackageHelper @@ -18,7 +19,7 @@ class AppItemView @JvmOverloads constructor( val binding by viewBinding(createMethod = CreateMethod.INFLATE) var showEnabled: Boolean - get() = binding.enabled.visibility == VISIBLE + get() = binding.enabled.isVisible set(value) { binding.enabled.visibility = if (value) VISIBLE else GONE } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/ListItemView.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/ListItemView.kt index 244e0f6..f1efdad 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/ListItemView.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/ListItemView.kt @@ -1,11 +1,14 @@ package icu.nullptr.hidemyapplist.ui.view import android.content.Context +import android.graphics.Typeface import android.util.AttributeSet import android.widget.LinearLayout import androidx.annotation.DrawableRes +import androidx.core.view.isVisible import by.kirich1409.viewbindingdelegate.CreateMethod import by.kirich1409.viewbindingdelegate.viewBinding +import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.themeColor import org.frknkrc44.hma_oss.R import org.frknkrc44.hma_oss.databinding.ListItemViewBinding @@ -42,8 +45,28 @@ class ListItemView @JvmOverloads constructor( binding.icon.setImageResource(icon) } + fun showIcon(show: Boolean) { + binding.icon.isVisible = show + } + + fun showAsHeader() { + showIcon(false) + + with(binding.text) { + typeface = Typeface.DEFAULT_BOLD + + val textColor = context.themeColor( + androidx.appcompat.R.attr.colorPrimary, + ) + setTextColor(textColor) + + val padding = (resources.displayMetrics.density * 8f).toInt() + setPaddingRelative(paddingStart, padding, paddingEnd, padding) + } + } + override fun setOnClickListener(l: OnClickListener?) { - if (binding.button.visibility == VISIBLE) { + if (binding.button.isVisible) { binding.button.setOnClickListener(l) } else { super.setOnClickListener(l) diff --git a/app/src/main/java/org/frknkrc44/hma_oss/ui/activity/BaseActivity.kt b/app/src/main/java/org/frknkrc44/hma_oss/ui/activity/BaseActivity.kt index 1375a2d..c7b6f08 100644 --- a/app/src/main/java/org/frknkrc44/hma_oss/ui/activity/BaseActivity.kt +++ b/app/src/main/java/org/frknkrc44/hma_oss/ui/activity/BaseActivity.kt @@ -32,7 +32,7 @@ open class BaseActivity : AppCompatActivity() { theme.applyStyle(ThemeUtils.getColorThemeStyleRes(this), true) } - theme.applyStyle(ThemeUtils.getNightThemeStyleRes(this), true) + theme.applyStyle(ThemeUtils.getOverlayThemeStyleRes(this), true) } override fun attachBaseContext(newBase: Context?) { diff --git a/app/src/main/java/org/frknkrc44/hma_oss/ui/adapter/AppPresetListAdapter.kt b/app/src/main/java/org/frknkrc44/hma_oss/ui/adapter/AppPresetListAdapter.kt index d5445b1..37f0701 100644 --- a/app/src/main/java/org/frknkrc44/hma_oss/ui/adapter/AppPresetListAdapter.kt +++ b/app/src/main/java/org/frknkrc44/hma_oss/ui/adapter/AppPresetListAdapter.kt @@ -5,6 +5,7 @@ import android.content.Context import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import icu.nullptr.hidemyapplist.common.AppPresets +import icu.nullptr.hidemyapplist.common.SettingsPresets import icu.nullptr.hidemyapplist.service.ConfigManager import icu.nullptr.hidemyapplist.ui.view.ListItemView import org.frknkrc44.hma_oss.BuildConfig @@ -15,23 +16,31 @@ class AppPresetListAdapter( private val onClickListener: ((ConfigManager.PresetInfo) -> Unit)? ) : RecyclerView.Adapter() { - private lateinit var list: List + private var list = mutableListOf() init { updateList(context) } inner class ViewHolder(view: ListItemView) : RecyclerView.ViewHolder(view) { - init { - view.setOnClickListener { - onClickListener?.invoke(list[absoluteAdapterPosition]) - } - } - - fun bind(presetName: String) { + fun bind(item: ConfigManager.PresetInfo) { with(itemView as ListItemView) { - setIcon(R.drawable.baseline_assignment_24) - text = presetName + if (item.type == null) { + showAsHeader() + } else { + setIcon( + when (item.type) { + ConfigManager.PresetType.APP -> R.drawable.baseline_assignment_24 + ConfigManager.PresetType.SETTINGS -> R.drawable.baseline_settings_24 + } + ) + + itemView.setOnClickListener { + onClickListener?.invoke(item) + } + } + + text = item.translation } } } @@ -49,12 +58,14 @@ class AppPresetListAdapter( override fun getItemId(position: Int) = list[position].name.hashCode().toLong() - override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(list[position].translation) + override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(list[position]) @SuppressLint("NotifyDataSetChanged", "DiscouragedApi") private fun updateList(context: Context) { - val presetNames = AppPresets.instance.getAllPresetNames() - val presetTranslations = presetNames.map { name -> + list.clear() + + val appPresetNames = AppPresets.instance.getAllPresetNames() + val appPresetTranslations = appPresetNames.map { name -> try { val id = context.resources.getIdentifier( "preset_${name}", @@ -68,9 +79,49 @@ class AppPresetListAdapter( name } - list = presetNames - .map { ConfigManager.PresetInfo(it, presetTranslations[presetNames.indexOf(it)]) } + list += ConfigManager.PresetInfo( + "preset", + null, + context.getString(R.string.title_preset), + ) + + list += appPresetNames + .map { ConfigManager.PresetInfo( + it, + ConfigManager.PresetType.APP, + appPresetTranslations[appPresetNames.indexOf(it)] + ) } .sortedWith { a, b -> a.translation.lowercase().compareTo(b.translation.lowercase()) } + + list += ConfigManager.PresetInfo( + "settings_preset", + null, + context.getString(R.string.title_settings_preset), + ) + + val settingsPresetNames = SettingsPresets.instance.getAllPresetNames() + val settingsPresetTranslations = settingsPresetNames.map { name -> + try { + val id = context.resources.getIdentifier( + "settings_preset_${name}", + "string", + BuildConfig.APPLICATION_ID + ) + + return@map if (id != 0) { context.resources.getString(id) } else { name } + } catch (_: Throwable) {} + + name + } + + list += settingsPresetNames + .map { ConfigManager.PresetInfo( + it, + ConfigManager.PresetType.SETTINGS, + settingsPresetTranslations[settingsPresetNames.indexOf(it)] + ) } + .sortedWith { a, b -> a.translation.lowercase().compareTo(b.translation.lowercase()) } + notifyDataSetChanged() } } diff --git a/app/src/main/java/org/frknkrc44/hma_oss/ui/adapter/SettingsPresetListAdapter.kt b/app/src/main/java/org/frknkrc44/hma_oss/ui/adapter/SettingsPresetListAdapter.kt new file mode 100644 index 0000000..ce43d35 --- /dev/null +++ b/app/src/main/java/org/frknkrc44/hma_oss/ui/adapter/SettingsPresetListAdapter.kt @@ -0,0 +1,37 @@ +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() { + private val presetItems by lazy { + SettingsPresets.instance.getPresetByName(presetName)!!.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" + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/AboutFragment.kt b/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/AboutFragment.kt index f1fe967..f65a8ff 100644 --- a/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/AboutFragment.kt +++ b/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/AboutFragment.kt @@ -12,6 +12,7 @@ import androidx.core.net.toUri import androidx.fragment.app.Fragment import by.kirich1409.viewbindingdelegate.viewBinding import com.bumptech.glide.Glide +import icu.nullptr.hidemyapplist.service.PrefManager import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.homeItemBackgroundColor import icu.nullptr.hidemyapplist.ui.util.navController import org.frknkrc44.hma_oss.R @@ -73,6 +74,11 @@ class AboutFragment : Fragment(R.layout.fragment_about) { appName.setText(R.string.app_name) appVersion.text = BuildConfig.APP_VERSION_NAME + if (PrefManager.systemWallpaper) { + linkGithub.background.alpha = 0xAA + linkTelegram.background.alpha = 0xAA + } + setOnClickUrl(linkGithub, "https://github.com/frknkrc44/HMA-OSS") setOnClickUrl(linkTelegram, "https://t.me/aerathfuns") diff --git a/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/HomeFragment.kt b/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/HomeFragment.kt index 8b2790a..faa64ed 100644 --- a/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/HomeFragment.kt +++ b/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/HomeFragment.kt @@ -17,6 +17,7 @@ import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.dialog.MaterialAlertDialogBuilder import icu.nullptr.hidemyapplist.hmaApp import icu.nullptr.hidemyapplist.service.ConfigManager +import icu.nullptr.hidemyapplist.service.PrefManager import icu.nullptr.hidemyapplist.service.ServiceClient import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.attrDrawable import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.getColor @@ -115,11 +116,13 @@ class HomeFragment : Fragment(R.layout.fragment_home) { super.onStart() val serviceVersion = ServiceClient.serviceVersion - val color = when { + var color = when { serviceVersion == 0 -> getColor(R.color.invalid) else -> themeColor(android.R.attr.colorPrimary) } + if (PrefManager.systemWallpaper) color -= 0x55000000 + with(binding.statusCard) { root.setCardBackgroundColor(color) root.outlineAmbientShadowColor = color @@ -294,12 +297,20 @@ class HomeFragment : Fragment(R.layout.fragment_home) { } } - binding.backupConfig.setOnClickListener { - backupSAFLauncher.launch("HMA_Config_${System.currentTimeMillis()}.json") + with(binding.backupConfig) { + if (PrefManager.systemWallpaper) background.alpha = 0xAA + + setOnClickListener { + backupSAFLauncher.launch("HMA_Config_${System.currentTimeMillis()}.json") + } } - binding.restoreConfig.setOnClickListener { - restoreSAFLauncher.launch("application/json") + with(binding.restoreConfig) { + if (PrefManager.systemWallpaper) background.alpha = 0xAA + + setOnClickListener { + restoreSAFLauncher.launch("application/json") + } } } diff --git a/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/PresetManageFragment.kt b/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/PresetManageFragment.kt index 11c3c50..d740b9c 100644 --- a/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/PresetManageFragment.kt +++ b/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/PresetManageFragment.kt @@ -60,7 +60,15 @@ class PresetManageFragment : Fragment(R.layout.fragment_preset_manage) { } private fun navigateToPreset(presetInfo: ConfigManager.PresetInfo) { - val args = AppPresetFragmentArgs(presetInfo.name, presetInfo.translation) - navigate(R.id.nav_preset_inner_manage, args.toBundle()) + when (presetInfo.type!!) { + ConfigManager.PresetType.APP -> { + val args = AppPresetFragmentArgs(presetInfo.name, presetInfo.translation) + navigate(R.id.nav_preset_inner_manage, args.toBundle()) + } + ConfigManager.PresetType.SETTINGS -> { + val args = SettingsPresetFragmentArgs(presetInfo.name, presetInfo.translation) + navigate(R.id.nav_settings_preset_inner_manage, args.toBundle()) + } + } } } diff --git a/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/SettingsPresetFragment.kt b/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/SettingsPresetFragment.kt new file mode 100644 index 0000000..c29bd46 --- /dev/null +++ b/app/src/main/java/org/frknkrc44/hma_oss/ui/fragment/SettingsPresetFragment.kt @@ -0,0 +1,83 @@ +package org.frknkrc44.hma_oss.ui.fragment + +import android.os.Build +import android.os.Bundle +import android.view.View +import android.view.WindowInsets +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.navArgs +import androidx.recyclerview.widget.LinearLayoutManager +import by.kirich1409.viewbindingdelegate.viewBinding +import icu.nullptr.hidemyapplist.ui.util.navController +import icu.nullptr.hidemyapplist.ui.util.setupToolbar +import org.frknkrc44.hma_oss.R +import org.frknkrc44.hma_oss.databinding.FragmentSettingsPresetBinding +import org.frknkrc44.hma_oss.ui.adapter.SettingsPresetListAdapter + +/** + * Use the [SettingsPresetFragment.newInstance] factory method to + * create an instance of this fragment. + */ +class SettingsPresetFragment : Fragment(R.layout.fragment_settings_preset) { + private val binding by viewBinding() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val args by navArgs() + + setupToolbar( + toolbar = binding.toolbar, + title = args.presetTitle, + navigationIcon = R.drawable.baseline_arrow_back_24, + navigationOnClick = { navController.navigateUp() } + ) + + binding.list.layoutManager = LinearLayoutManager(context) + binding.list.adapter = SettingsPresetListAdapter(args.presetName) + + binding.root.setOnApplyWindowInsetsListener { v, insets -> + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val barInsets = insets.getInsets(WindowInsets.Type.systemBars()) + binding.root.setPadding( + barInsets.left, + barInsets.top, + barInsets.right, + barInsets.bottom, + ) + } else { + @Suppress("deprecation") + binding.root.setPadding( + insets.systemWindowInsetLeft, + insets.systemWindowInsetTop, + insets.systemWindowInsetRight, + insets.systemWindowInsetBottom, + ) + } + + insets + } + } + + companion object { + private const val ARG_PRESET_NAME = "presetName" + private const val ARG_PRESET_TITLE = "presetTitle" + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param presetName Name of the preset. + * @param presetTitle Title of the preset. + * @return A new instance of fragment SettingsPresetFragment. + */ + @JvmStatic + fun newInstance(presetName: String, presetTitle: String) = + SettingsPresetFragment().apply { + arguments = Bundle().apply { + putString(ARG_PRESET_NAME, presetName) + putString(ARG_PRESET_TITLE, presetTitle) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/anim/activity_close_enter.xml b/app/src/main/res/anim/activity_close_enter.xml new file mode 100644 index 0000000..d13e7ad --- /dev/null +++ b/app/src/main/res/anim/activity_close_enter.xml @@ -0,0 +1,40 @@ + + + + + + + + diff --git a/app/src/main/res/anim/activity_close_exit.xml b/app/src/main/res/anim/activity_close_exit.xml index 4ac2365..517fd80 100644 --- a/app/src/main/res/anim/activity_close_exit.xml +++ b/app/src/main/res/anim/activity_close_exit.xml @@ -40,5 +40,5 @@ android:fillBefore="true" android:fillAfter="true" android:interpolator="@android:interpolator/fast_out_extra_slow_in" - android:duration="400"/> + android:duration="@android:integer/config_mediumAnimTime"/> diff --git a/app/src/main/res/anim/activity_open_enter.xml b/app/src/main/res/anim/activity_open_enter.xml index cb94dcf..900e54f 100644 --- a/app/src/main/res/anim/activity_open_enter.xml +++ b/app/src/main/res/anim/activity_open_enter.xml @@ -38,5 +38,5 @@ android:fillBefore="true" android:fillAfter="true" android:interpolator="@android:interpolator/fast_out_extra_slow_in" - android:duration="400"/> + android:duration="@android:integer/config_mediumAnimTime"/> diff --git a/app/src/main/res/anim/activity_open_exit.xml b/app/src/main/res/anim/activity_open_exit.xml new file mode 100644 index 0000000..5625585 --- /dev/null +++ b/app/src/main/res/anim/activity_open_exit.xml @@ -0,0 +1,41 @@ + + + + + + + + + diff --git a/app/src/main/res/anim/fade_in.xml b/app/src/main/res/anim/fade_in.xml deleted file mode 100644 index 6f9934e..0000000 --- a/app/src/main/res/anim/fade_in.xml +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/app/src/main/res/anim/fade_out.xml b/app/src/main/res/anim/fade_out.xml deleted file mode 100644 index 00c73ef..0000000 --- a/app/src/main/res/anim/fade_out.xml +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/app/src/main/res/drawable/baseline_wallpaper_24.xml b/app/src/main/res/drawable/baseline_wallpaper_24.xml new file mode 100644 index 0000000..6b7eb35 --- /dev/null +++ b/app/src/main/res/drawable/baseline_wallpaper_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/switch_thumb.xml b/app/src/main/res/drawable/switch_thumb.xml new file mode 100644 index 0000000..f74e617 --- /dev/null +++ b/app/src/main/res/drawable/switch_thumb.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/switch_track.xml b/app/src/main/res/drawable/switch_track.xml new file mode 100644 index 0000000..5a3fa18 --- /dev/null +++ b/app/src/main/res/drawable/switch_track.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_app_select.xml b/app/src/main/res/layout/fragment_app_select.xml index 54f71dd..169b3de 100644 --- a/app/src/main/res/layout/fragment_app_select.xml +++ b/app/src/main/res/layout/fragment_app_select.xml @@ -8,6 +8,7 @@ tools:context="icu.nullptr.hidemyapplist.ui.fragment.AppSelectFragment"> diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 0f4b38c..f3ae9d8 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -8,6 +8,7 @@ tools:context=".ui.fragment.HomeFragment"> diff --git a/app/src/main/res/layout/fragment_logs.xml b/app/src/main/res/layout/fragment_logs.xml index 769d282..f17cdd2 100644 --- a/app/src/main/res/layout/fragment_logs.xml +++ b/app/src/main/res/layout/fragment_logs.xml @@ -5,6 +5,7 @@ android:layout_height="match_parent"> @@ -20,7 +21,6 @@ android:id="@+id/service_off" android:layout_width="match_parent" android:layout_height="match_parent" - android:fontFamily="serif" android:gravity="center" android:text="@string/home_xposed_service_off" android:textAppearance="?textAppearanceTitleLarge" /> diff --git a/app/src/main/res/layout/fragment_preset_manage.xml b/app/src/main/res/layout/fragment_preset_manage.xml index 2544f26..9bccf39 100644 --- a/app/src/main/res/layout/fragment_preset_manage.xml +++ b/app/src/main/res/layout/fragment_preset_manage.xml @@ -5,6 +5,7 @@ android:layout_height="match_parent"> diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 0bbcd0f..75a4a65 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -5,6 +5,7 @@ android:layout_height="match_parent"> diff --git a/app/src/main/res/layout/fragment_settings_preset.xml b/app/src/main/res/layout/fragment_settings_preset.xml new file mode 100644 index 0000000..a7da69e --- /dev/null +++ b/app/src/main/res/layout/fragment_settings_preset.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_template_manage.xml b/app/src/main/res/layout/fragment_template_manage.xml index 92784e5..93f7681 100644 --- a/app/src/main/res/layout/fragment_template_manage.xml +++ b/app/src/main/res/layout/fragment_template_manage.xml @@ -6,6 +6,7 @@ android:layout_height="match_parent"> diff --git a/app/src/main/res/layout/fragment_template_settings.xml b/app/src/main/res/layout/fragment_template_settings.xml index 729eed5..305a555 100644 --- a/app/src/main/res/layout/fragment_template_settings.xml +++ b/app/src/main/res/layout/fragment_template_settings.xml @@ -7,6 +7,7 @@ tools:context="icu.nullptr.hidemyapplist.ui.fragment.TemplateSettingsFragment"> diff --git a/app/src/main/res/layout/log_item_view.xml b/app/src/main/res/layout/log_item_view.xml index dc0c67b..91e94a6 100644 --- a/app/src/main/res/layout/log_item_view.xml +++ b/app/src/main/res/layout/log_item_view.xml @@ -45,7 +45,7 @@ android:id="@+id/date" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:fontFamily="serif" + android:textStyle="bold" android:textAppearance="?textAppearanceBodySmall" tools:text="19-19 11:45:14" /> diff --git a/app/src/main/res/layout/switch_compat.xml b/app/src/main/res/layout/switch_compat.xml index 5b4cd2b..1636378 100644 --- a/app/src/main/res/layout/switch_compat.xml +++ b/app/src/main/res/layout/switch_compat.xml @@ -1,11 +1,15 @@ - + - + android:thumb="@drawable/switch_thumb" + android:track="@drawable/switch_track" + android:background="@null" + tools:ignore="UseSwitchCompatOrMaterialXml" /> diff --git a/app/src/main/res/navigation/home_nav_graph.xml b/app/src/main/res/navigation/home_nav_graph.xml index ce6ed8e..be77128 100644 --- a/app/src/main/res/navigation/home_nav_graph.xml +++ b/app/src/main/res/navigation/home_nav_graph.xml @@ -3,6 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/home_nav_graph" app:startDestination="@id/nav_home"> + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-night/themes_overlay.xml b/app/src/main/res/values-night/themes_overlay.xml new file mode 100644 index 0000000..1a254cc --- /dev/null +++ b/app/src/main/res/values-night/themes_overlay.xml @@ -0,0 +1,33 @@ + + + + + + diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 50513c8..db8e8b3 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -70,4 +70,9 @@ @string/color_brown @string/color_blue_grey + + + @string/app_force_stop_launch + @string/app_launch + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 57f648d..626698b 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -6,4 +6,8 @@ #154A74 #375634 #8F3232 + + ?colorPrimary + ?colorOnSurfaceInverse + ?colorSurfaceContainer diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5044e5e..e009b5b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -107,8 +107,13 @@ Invert Activity launch protection This option will invert \"Disable Activity launch protection\" selection in Settings. Protection is %s globally + Force stop and launch + Launch + App launch failed, check logs for more details + Presets + Settings presets Show presets LSPosed/Xposed modules Root managers/Rooted apps @@ -152,6 +157,7 @@ Note that the devs are not responsible for risky package related issues before or after you enabled this feature Disable Activity launch protection This toggle will lift all startActivity protections. If you\'re unsure, don\'t enable it. + Use system wallpaper Language Participate in translation diff --git a/app/src/main/res/values/themes_overlay.xml b/app/src/main/res/values/themes_overlay.xml index 62a4a7e..c3ca1c8 100644 --- a/app/src/main/res/values/themes_overlay.xml +++ b/app/src/main/res/values/themes_overlay.xml @@ -20,9 +20,10 @@ diff --git a/app/src/main/res/xml/app_settings.xml b/app/src/main/res/xml/app_settings.xml index 13d83fb..bd381ca 100644 --- a/app/src/main/res/xml/app_settings.xml +++ b/app/src/main/res/xml/app_settings.xml @@ -2,7 +2,9 @@ - + + packagesShouldNotHide = Set.of( - "android", - "android.media", - "android.uid.system", - "android.uid.shell", - "android.uid.systemui", - "com.android.permissioncontroller", - "com.android.providers.downloads", - "com.android.providers.downloads.ui", - "com.android.providers.media", - "com.android.providers.media.module", - "com.android.providers.settings", - "com.google.android.webview", - "com.google.android.providers.media.module" - ); -} diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/Constants.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/Constants.kt new file mode 100644 index 0000000..e0e8274 --- /dev/null +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/Constants.kt @@ -0,0 +1,32 @@ +package icu.nullptr.hidemyapplist.common + +import org.frknkrc44.hma_oss.common.BuildConfig + +object Constants { + const val PROVIDER_AUTHORITY = "${BuildConfig.APP_PACKAGE_NAME}.ServiceProvider" + private const val GMS_PACKAGE_NAME = "com.google.android.gms" + private const val GSF_PACKAGE_NAME = "com.google.android.gsf" + const val VENDING_PACKAGE_NAME = "com.android.vending" + const val TRANSLATE_URL = "https://crowdin.com/project/frknkrc44-hma-oss" + + const val UID_SYSTEM = 1000 + + val gmsPackages = arrayOf(GMS_PACKAGE_NAME, GSF_PACKAGE_NAME) + val riskyPackages = arrayOf(VENDING_PACKAGE_NAME) + gmsPackages + + val packagesShouldNotHide = setOf( + "android", + "android.media", + "android.uid.system", + "android.uid.shell", + "android.uid.systemui", + "com.android.permissioncontroller", + "com.android.providers.downloads", + "com.android.providers.downloads.ui", + "com.android.providers.media", + "com.android.providers.media.module", + "com.android.providers.settings", + "com.google.android.webview", + "com.google.android.providers.media.module" + ) +} diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/PropertyUtils.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/PropertyUtils.kt new file mode 100644 index 0000000..61ccb9a --- /dev/null +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/PropertyUtils.kt @@ -0,0 +1,14 @@ +package icu.nullptr.hidemyapplist.common + +import android.os.SystemProperties + +object PropertyUtils { + private const val ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY = "persist.zygote.app_data_isolation" + private const val ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = "persist.sys.vold_app_data_isolation_enabled" + + val isAppDataIsolationEnabled: Boolean + get() = SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true) + + val isVoldAppDataIsolationEnabled: Boolean + get() = SystemProperties.getBoolean(ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false) +} diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/BasePreset.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/BasePreset.kt index 67ef420..29c679c 100644 --- a/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/BasePreset.kt +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/BasePreset.kt @@ -1,7 +1,7 @@ package icu.nullptr.hidemyapplist.common.settings_presets abstract class BasePreset(val name: String) { - protected abstract val settingsKVPairs: List + abstract val settingsKVPairs: List fun getSpoofedValue(key: String) = settingsKVPairs.firstOrNull { it.name == key } } diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/DeveloperOptionsPreset.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/DeveloperOptionsPreset.kt index d2de721..002c9a9 100644 --- a/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/DeveloperOptionsPreset.kt +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/DeveloperOptionsPreset.kt @@ -5,16 +5,100 @@ import android.provider.Settings class DeveloperOptionsPreset : BasePreset("dev_options") { override val settingsKVPairs = listOf( ReplacementItem( - name = Settings.Global.ADB_ENABLED, - value = "0", + Settings.Global.ADB_ENABLED, + "0", ), ReplacementItem( - name = "adb_wifi_enabled", - value = "0", + "adb_wifi_enabled", + "0", ), ReplacementItem( - name = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, - value = "0", + Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, + "0", + ), + ReplacementItem( + "debug.force_rtl", + "0" + ), + ReplacementItem( + "show_touches", + "0", + ), + ReplacementItem( + "force_resizable_activities", + "0", + ), + ReplacementItem( + "enable_freeform_support", + "0", + ), + ReplacementItem( + "override_desktop_mode_features", + "0", + ), + ReplacementItem( + "force_desktop_mode_on_external_displays", + "0", + ), + ReplacementItem( + "enable_non_resizable_multi_window", + "0", + ), + ReplacementItem( + "stay_on_while_plugged_in", + "0", + ), + ReplacementItem( + "emulate_display_cutout", + "0", + ), + ReplacementItem( + "anr_show_background", + "0", + ), + ReplacementItem( + "usb_audio_automatic_routing_disabled", + "0", + ), + ReplacementItem( + "enable_16k_pages", + "0", + ), + ReplacementItem( + "pointer_location", + "0", + ), + ReplacementItem( + "bugreport_in_power_menu", + "0", + ), + ReplacementItem( + "force_allow_on_external", + "0", + ), + ReplacementItem( + "tether_offload_disabled", + "0", + ), + ReplacementItem( + "ota_disable_automatic_update", + "0", + ), + ReplacementItem( + "enable_gpu_debug_layers", + "0", + ), + ReplacementItem( + "mobile_data_always_on", + "0", + ), + ReplacementItem( + "wifi_non_persistent_mac_randomization", + "0", + ), + ReplacementItem( + "non_persistent_mac_randomization_force_enabled", + "0", ), ) } diff --git a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt index 0d0a135..dfff0ce 100644 --- a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt +++ b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/HMAService.kt @@ -5,8 +5,8 @@ import android.content.pm.ApplicationInfo import android.content.pm.IPackageManager import android.os.Build import android.os.UserHandle +import android.util.Log import icu.nullptr.hidemyapplist.common.AppPresets -import icu.nullptr.hidemyapplist.common.CommonUtils import icu.nullptr.hidemyapplist.common.Constants import icu.nullptr.hidemyapplist.common.IHMAService import icu.nullptr.hidemyapplist.common.JsonConfig @@ -25,6 +25,7 @@ import icu.nullptr.hidemyapplist.xposed.hook.PmsHookTarget33 import icu.nullptr.hidemyapplist.xposed.hook.PmsHookTarget34 import icu.nullptr.hidemyapplist.xposed.hook.PmsPackageEventsHook import org.frknkrc44.hma_oss.common.BuildConfig +import rikka.hidden.compat.ActivityManagerApis import java.io.File import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @@ -177,10 +178,11 @@ class HMAService(val pms: IPackageManager) : IHMAService.Stub() { } fun isAppInGMSIgnoredPackages(caller: String, query: String) = - (caller in CommonUtils.gmsPackages) && appHasGMSConnection(query) + (caller in Constants.gmsPackages) && appHasGMSConnection(query) fun shouldHide(caller: String?, query: String?): Boolean { if (caller == null || query == null) return false + if (caller == BuildConfig.APP_PACKAGE_NAME) return false if (caller in Constants.packagesShouldNotHide || query in Constants.packagesShouldNotHide) return false if (caller == query) return false val appConfig = config.scope[caller] ?: return false @@ -224,6 +226,7 @@ class HMAService(val pms: IPackageManager) : IHMAService.Stub() { fun shouldHideInstallationSource(caller: String?, query: String?, user: UserHandle): Int { if (caller == null || query == null) return 0 + if (caller == BuildConfig.APP_PACKAGE_NAME) return 0 val appConfig = config.scope[caller] ?: return 0 if (!appConfig.hideInstallationSource) return 0 logD(TAG, "@shouldHideInstallationSource $caller: $query") @@ -316,4 +319,18 @@ class HMAService(val pms: IPackageManager) : IHMAService.Stub() { AppPresets.instance.getPresetByName(presetName)?.packages?.toTypedArray() override fun readConfig() = config.toString() + + override fun forceStop(packageName: String?, userId: Int) { + Utils.binderLocalScope { + runCatching { + ActivityManagerApis.forceStopPackage(packageName, userId) + }.onFailure { error -> + this.log(Log.ERROR, TAG, error.stackTraceToString()) + } + } + } + + override fun log(level: Int, tag: String, message: String) { + logWithLevel(level, tag, message) + } } diff --git a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/Logcat.kt b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/Logcat.kt index 6733117..8d1e3a9 100644 --- a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/Logcat.kt +++ b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/Logcat.kt @@ -21,7 +21,7 @@ private fun parseLog(level: Int, tag: String, msg: String, cause: Throwable? = n if (!endsWith('\n')) append('\n') } -private fun log(level: Int, tag: String, msg: String, cause: Throwable? = null) { +fun logWithLevel(level: Int, tag: String, msg: String, cause: Throwable? = null) { if (level <= Log.DEBUG && HMAService.instance?.config?.detailLog == false) return val parsedLog = parseLog(level, tag, msg, cause) val executor = HMAService.instance?.executor @@ -36,7 +36,7 @@ private fun log(level: Int, tag: String, msg: String, cause: Throwable? = null) } } -fun logD(tag: String, msg: String, cause: Throwable? = null) = log(Log.DEBUG, tag, msg, cause) -fun logI(tag: String, msg: String, cause: Throwable? = null) = log(Log.INFO, tag, msg, cause) -fun logW(tag: String, msg: String, cause: Throwable? = null) = log(Log.WARN, tag, msg, cause) -fun logE(tag: String, msg: String, cause: Throwable? = null) = log(Log.ERROR, tag, msg, cause) +fun logD(tag: String, msg: String, cause: Throwable? = null) = logWithLevel(Log.DEBUG, tag, msg, cause) +fun logI(tag: String, msg: String, cause: Throwable? = null) = logWithLevel(Log.INFO, tag, msg, cause) +fun logW(tag: String, msg: String, cause: Throwable? = null) = logWithLevel(Log.WARN, tag, msg, cause) +fun logE(tag: String, msg: String, cause: Throwable? = null) = logWithLevel(Log.ERROR, tag, msg, cause) diff --git a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PlatformCompatHook.kt b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PlatformCompatHook.kt index 35095b4..96afe27 100644 --- a/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PlatformCompatHook.kt +++ b/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PlatformCompatHook.kt @@ -6,12 +6,12 @@ import androidx.annotation.RequiresApi 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.CommonUtils -import icu.nullptr.hidemyapplist.common.Utils +import icu.nullptr.hidemyapplist.common.PropertyUtils import icu.nullptr.hidemyapplist.xposed.HMAService import icu.nullptr.hidemyapplist.xposed.logD import icu.nullptr.hidemyapplist.xposed.logE import icu.nullptr.hidemyapplist.xposed.logI +import org.frknkrc44.hma_oss.common.BuildConfig @RequiresApi(Build.VERSION_CODES.R) class PlatformCompatHook(private val service: HMAService) : IFrameworkHook { @@ -21,7 +21,7 @@ class PlatformCompatHook(private val service: HMAService) : IFrameworkHook { } private val sAppDataIsolationEnabled by lazy { - CommonUtils.isAppDataIsolationEnabled || service.config.altAppDataIsolation + PropertyUtils.isAppDataIsolationEnabled || service.config.altAppDataIsolation } private var hook: XC_MethodHook.Unhook? = null @@ -34,23 +34,18 @@ class PlatformCompatHook(private val service: HMAService) : IFrameworkHook { name == "isChangeEnabled" }.hookBefore { param -> runCatching { - val changeId = param.args[0] as Long - val appInfo = param.args[1] as ApplicationInfo - if (changeId.toInt() != 143937733) return@hookBefore - if (appInfo.packageName in service.systemApps) return@hookBefore - val apps = Utils.binderLocalScope { - service.pms.getPackagesForUid(appInfo.uid) - } ?: return@hookBefore - for (app in apps) { - if (service.isHookEnabled(app)) { - if (sAppDataIsolationEnabled) { - param.result = true - service.filterCount++ - logD(TAG, "force mount data: ${appInfo.uid} $app") - } + if (!sAppDataIsolationEnabled) return@hookBefore - return@hookBefore - } + val changeId = param.args[0] as Long + if (changeId != 143937733L) return@hookBefore + + val appInfo = param.args[1] as ApplicationInfo + val app = appInfo.packageName + if (app == BuildConfig.APP_PACKAGE_NAME || app in service.systemApps) return@hookBefore + if (service.isHookEnabled(app)) { + param.result = true + service.filterCount++ + logD(TAG, "force mount data: ${appInfo.uid} $app") } }.onFailure { logE(TAG, "Fatal error occurred, disable hooks", it)