├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ └── bug_report.yml
├── FUNDING.yml
└── workflows
│ ├── crowdin.yml
│ ├── pull_request.yml
│ └── main.yml
├── xposed
├── src
│ └── main
│ │ ├── assets
│ │ └── xposed_init
│ │ └── java
│ │ └── icu
│ │ └── nullptr
│ │ └── hidemyapplist
│ │ └── xposed
│ │ ├── hook
│ │ ├── IFrameworkHook.kt
│ │ ├── PmsPackageEventsHook.kt
│ │ ├── ActivityHook.kt
│ │ ├── PlatformCompatHook.kt
│ │ ├── AccessibilityHook.kt
│ │ └── ContentProviderHook.kt
│ │ ├── Utils4Xposed.kt
│ │ ├── Logcat.kt
│ │ ├── XposedEntry.kt
│ │ └── UserService.kt
├── proguard-rules.pro
└── build.gradle.kts
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── app
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── cont_k.webp
│ │ │ ├── cont_fk.webp
│ │ │ ├── cont_author.webp
│ │ │ ├── cont_aviraxp.webp
│ │ │ ├── cont_cpp_master.webp
│ │ │ ├── cont_icon_designer.webp
│ │ │ ├── divider.xml
│ │ │ ├── ic_home_checkable.xml
│ │ │ ├── ic_logs_checkable.xml
│ │ │ ├── ic_settings_checkable.xml
│ │ │ ├── baseline_add_24.xml
│ │ │ ├── baseline_home_24.xml
│ │ │ ├── outline_home_24.xml
│ │ │ ├── baseline_arrow_back_24.xml
│ │ │ ├── outline_delete_24.xml
│ │ │ ├── outline_storage_24.xml
│ │ │ ├── ic_outline_layers_24.xml
│ │ │ ├── baseline_apps_24.xml
│ │ │ ├── baseline_call_split_24.xml
│ │ │ ├── outline_sd_storage_24.xml
│ │ │ ├── outline_info_24.xml
│ │ │ ├── outline_shield_24.xml
│ │ │ ├── outline_shop_24.xml
│ │ │ ├── outline_stop_circle_24.xml
│ │ │ ├── outline_save_24.xml
│ │ │ ├── outline_invert_colors_24.xml
│ │ │ ├── baseline_refresh_24.xml
│ │ │ ├── outline_edit_24.xml
│ │ │ ├── outline_settings_backup_restore_24.xml
│ │ │ ├── outline_hide_image_24.xml
│ │ │ ├── baseline_assignment_24.xml
│ │ │ ├── outline_format_color_fill_24.xml
│ │ │ ├── outline_cleaning_services_24.xml
│ │ │ ├── outline_translate_24.xml
│ │ │ ├── baseline_my_location_24.xml
│ │ │ ├── outline_speed_24.xml
│ │ │ ├── outline_dark_mode_24.xml
│ │ │ ├── outline_backup_24.xml
│ │ │ ├── outline_update_disabled_24.xml
│ │ │ ├── outline_android_24.xml
│ │ │ ├── outline_assignment_24.xml
│ │ │ ├── outline_discount_24.xml
│ │ │ ├── sentiment_very_dissatisfied_24px.xml
│ │ │ ├── outline_bug_report_24.xml
│ │ │ ├── baseline_settings_24.xml
│ │ │ ├── outline_language_24.xml
│ │ │ ├── outline_palette_24.xml
│ │ │ ├── sentiment_calm_24px.xml
│ │ │ └── outline_settings_24.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_background.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_background.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_background.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_background.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_background.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── layout
│ │ │ ├── line.xml
│ │ │ ├── switch_compat.xml
│ │ │ ├── fragment_settings.xml
│ │ │ ├── view_home_item.xml
│ │ │ ├── view_app_item.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── list_item_view.xml
│ │ │ ├── fragment_logs.xml
│ │ │ ├── fragment_app_select.xml
│ │ │ ├── log_item_view.xml
│ │ │ ├── app_item_view.xml
│ │ │ ├── view_status_card.xml
│ │ │ ├── fragment_template_settings.xml
│ │ │ └── fragment_template_manage.xml
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher_round.xml
│ │ │ └── ic_launcher.xml
│ │ ├── values
│ │ │ ├── attrs.xml
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── themes_overlay.xml
│ │ │ ├── arrays.xml
│ │ │ ├── styles.xml
│ │ │ └── themes.xml
│ │ ├── menu
│ │ │ ├── menu_delete.xml
│ │ │ ├── menu_about.xml
│ │ │ ├── menu_app_list.xml
│ │ │ └── menu_logs.xml
│ │ ├── values-night
│ │ │ └── styles.xml
│ │ ├── xml
│ │ │ ├── settings_data_isolation.xml
│ │ │ ├── app_settings.xml
│ │ │ └── settings.xml
│ │ └── navigation
│ │ │ └── home_nav_graph.xml
│ │ ├── ic_launcher-playstore.png
│ │ ├── java
│ │ ├── icu
│ │ │ └── nullptr
│ │ │ │ └── hidemyapplist
│ │ │ │ ├── util
│ │ │ │ ├── SuUtils.kt
│ │ │ │ └── ConfigUtils.kt
│ │ │ │ ├── ui
│ │ │ │ ├── util
│ │ │ │ │ ├── Toast.kt
│ │ │ │ │ ├── Fragment.kt
│ │ │ │ │ └── ThemeUtils.kt
│ │ │ │ ├── viewmodel
│ │ │ │ │ ├── AppSettingsViewModel.kt
│ │ │ │ │ └── TemplateSettingsViewModel.kt
│ │ │ │ ├── fragment
│ │ │ │ │ ├── AppManageFragment.kt
│ │ │ │ │ └── ScopeFragment.kt
│ │ │ │ ├── adapter
│ │ │ │ │ ├── AppScopeAdapter.kt
│ │ │ │ │ ├── AppManageAdapter.kt
│ │ │ │ │ ├── AppSelectAdapter.kt
│ │ │ │ │ └── TemplateAdapter.kt
│ │ │ │ ├── view
│ │ │ │ │ ├── AppItemView.kt
│ │ │ │ │ └── ListItemView.kt
│ │ │ │ └── activity
│ │ │ │ │ └── AboutActivity.kt
│ │ │ │ ├── service
│ │ │ │ ├── ServiceProvider.kt
│ │ │ │ └── ServiceClient.kt
│ │ │ │ ├── receiver
│ │ │ │ └── AppChangeReceiver.kt
│ │ │ │ ├── data
│ │ │ │ └── UpdateInfo.kt
│ │ │ │ ├── MyApp.kt
│ │ │ │ └── adapter
│ │ │ │ └── LogAdapter.kt
│ │ └── org
│ │ │ └── frknkrc44
│ │ │ └── hma_oss
│ │ │ └── ui
│ │ │ └── activity
│ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle.kts
├── crowdin.yml
├── gradle.properties
├── common
├── src
│ └── main
│ │ ├── java
│ │ └── icu
│ │ │ └── nullptr
│ │ │ └── hidemyapplist
│ │ │ └── common
│ │ │ ├── settings_presets
│ │ │ ├── BasePreset.kt
│ │ │ ├── ReplacementItem.kt
│ │ │ ├── AccessibilityPreset.kt
│ │ │ └── DeveloperOptionsPreset.kt
│ │ │ ├── CommonUtils.kt
│ │ │ ├── app_presets
│ │ │ ├── XposedModulesPreset.kt
│ │ │ ├── BasePreset.kt
│ │ │ └── CustomROMPreset.kt
│ │ │ ├── SettingsPresets.kt
│ │ │ ├── Constants.java
│ │ │ ├── JsonConfig.kt
│ │ │ ├── Utils.kt
│ │ │ └── AppPresets.kt
│ │ └── aidl
│ │ └── icu
│ │ └── nullptr
│ │ └── hidemyapplist
│ │ └── common
│ │ └── IHMAService.aidl
├── build.gradle.kts
└── proguard-rules.pro
├── .gitattributes
├── settings.gradle.kts
├── README_zh_CN.md
├── .gitignore
├── README.md
└── gradlew.bat
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/xposed/src/main/assets/xposed_init:
--------------------------------------------------------------------------------
1 | icu.nullptr.hidemyapplist.xposed.XposedEntry
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [frknkrc44]
2 | custom: ['https://github.com/sponsors/frknkrc44']
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cont_k.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/drawable/cont_k.webp
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cont_fk.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/drawable/cont_fk.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cont_author.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/drawable/cont_author.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cont_aviraxp.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/drawable/cont_aviraxp.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cont_cpp_master.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/drawable/cont_cpp_master.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cont_icon_designer.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/drawable/cont_icon_designer.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frknkrc44/HMA-fork-archived/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/IFrameworkHook.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.xposed.hook
2 |
3 | interface IFrameworkHook {
4 |
5 | fun load()
6 | fun unload()
7 | fun onConfigChanged() {}
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/line.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/crowdin.yml:
--------------------------------------------------------------------------------
1 | project_id_env: CROWDIN_PROJECT_ID
2 | api_token_env: CROWDIN_API_TOKEN
3 | preserve_hierarchy: 1
4 | files:
5 | - source: /app/src/main/res/values/strings.xml
6 | translation: /app/src/main/res/values-%android_code%/%original_file_name%
7 | type: android
8 | dest: /app/strings.xml
9 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | android.experimental.enableNewResourceShrinker.preciseShrinking=true
2 | android.enableAppCompileTimeRClass=true
3 | android.useAndroidX=true
4 | android.disableMinifyLocalDependenciesForLibraries=false
5 | android.enableJetifier=true
6 | org.gradle.jvmargs=-Xmx2048m "-XX:MaxMetaspaceSize=1024m"
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_home_checkable.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/util/SuUtils.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.util
2 |
3 | import com.topjohnwu.superuser.Shell
4 |
5 | object SuUtils {
6 |
7 | fun execPrivileged(cmd: String): Boolean {
8 | return Shell.cmd(cmd).exec().isSuccess && Shell.isAppGrantedRoot() == true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_logs_checkable.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_settings_checkable.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_delete.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/BasePreset.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common.settings_presets
2 |
3 | abstract class BasePreset(val name: String) {
4 | protected abstract val settingsKVPairs: List
5 |
6 | fun getSpoofedValue(key: String) = settingsKVPairs.firstOrNull { it.name == key }
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #607D8B
4 | #8F6B32
5 | #000000
6 | #154A74
7 | #375634
8 | #8F3232
9 |
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set the default behavior, in case people don't have core.autocrlf set.
2 | * text=auto eol=lf
3 |
4 | # Declare files that will always have CRLF line endings on checkout.
5 | *.cmd text eol=crlf
6 | *.bat text eol=crlf
7 |
8 | # Denote all files that are truly binary and should not be modified.
9 | *.so binary
10 | *.dex binary
11 | *.jar binary
12 | *.png binary
13 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/ReplacementItem.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common.settings_presets
2 |
3 | data class ReplacementItem(
4 | val name: String,
5 | val value: String?
6 | ) {
7 | override fun toString() = "ReplacementItem {" +
8 | " 'name': '$name'," +
9 | "'value': '$value'" +
10 | " }"
11 | }
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_add_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_home_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/util/Toast.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.util
2 |
3 | import android.widget.Toast
4 | import androidx.annotation.StringRes
5 | import icu.nullptr.hidemyapplist.hmaApp
6 |
7 | fun makeToast(@StringRes resId: Int) {
8 | Toast.makeText(hmaApp, resId, Toast.LENGTH_SHORT).show()
9 | }
10 |
11 | fun makeToast(text: CharSequence) {
12 | Toast.makeText(hmaApp, text, Toast.LENGTH_SHORT).show()
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_home_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/util/ConfigUtils.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.util
2 |
3 | import icu.nullptr.hidemyapplist.service.PrefManager
4 | import java.util.Locale
5 |
6 | class ConfigUtils private constructor() {
7 | companion object {
8 | fun getLocale(): Locale {
9 | val tag = PrefManager.locale
10 | return if (tag == "SYSTEM") Locale.getDefault()
11 | else Locale.forLanguageTag(tag)
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/common/src/main/aidl/icu/nullptr/hidemyapplist/common/IHMAService.aidl:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common;
2 |
3 | interface IHMAService {
4 |
5 | void stopService(boolean cleanEnv) = 0;
6 |
7 | void syncConfig(String json) = 1;
8 |
9 | int getServiceVersion() = 2;
10 |
11 | int getFilterCount() = 3;
12 |
13 | String getLogs() = 4;
14 |
15 | void clearLogs() = 5;
16 |
17 | void handlePackageEvent(String eventType, String packageName) = 6;
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_arrow_back_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_delete_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_storage_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/CommonUtils.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common
2 |
3 | import android.os.SystemProperties
4 |
5 | object CommonUtils {
6 |
7 | val isAppDataIsolationEnabled: Boolean
8 | get() = SystemProperties.getBoolean(Constants.ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)
9 |
10 | val isVoldAppDataIsolationEnabled: Boolean
11 | get() = SystemProperties.getBoolean(Constants.ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_outline_layers_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2dp
4 | 12dp
5 | 20dp
6 | 16dp
7 | 28dp
8 | 42dp
9 |
10 | 6dp
11 | 1dp
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_apps_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_call_split_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_sd_storage_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_info_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_shield_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_shop_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_stop_circle_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_save_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_invert_colors_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Xposed
2 | -keepclassmembers class icu.nullptr.hidemyapplist.MyApp {
3 | boolean isHooked;
4 | }
5 |
6 | # Enum class
7 | -keepclassmembers,allowoptimization enum * {
8 | public static **[] values();
9 | public static ** valueOf(java.lang.String);
10 | }
11 |
12 | -keep class icu.nullptr.hidemyapplist.data.UpdateData { *; }
13 | -keep class icu.nullptr.hidemyapplist.data.UpdateData$* { *; }
14 |
15 | -keep,allowoptimization class * extends androidx.preference.PreferenceFragmentCompat
16 | -keepclassmembers class org.frknkrc44.hma_oss.databinding.** {
17 | public ;
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_refresh_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
2 |
3 | pluginManagement {
4 | repositories {
5 | gradlePluginPortal()
6 | google()
7 | mavenCentral()
8 | }
9 | }
10 |
11 | dependencyResolutionManagement {
12 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
13 | repositories {
14 | google()
15 | mavenCentral()
16 | maven("https://jitpack.io")
17 | maven("https://api.xposed.info/")
18 | }
19 | }
20 |
21 | rootProject.name = "HMA-OSS"
22 |
23 | include(
24 | ":app",
25 | ":common",
26 | ":xposed"
27 | )
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_edit_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_settings_backup_restore_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/AccessibilityPreset.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common.settings_presets
2 |
3 | import android.provider.Settings
4 |
5 | class AccessibilityPreset : BasePreset(NAME) {
6 | companion object {
7 | const val NAME = "accessibility"
8 | }
9 |
10 | override val settingsKVPairs = listOf(
11 | ReplacementItem(
12 | name = Settings.Secure.ACCESSIBILITY_ENABLED,
13 | value = "0",
14 | ),
15 | ReplacementItem(
16 | name = Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
17 | value = "",
18 | ),
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/switch_compat.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_hide_image_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_assignment_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/DeveloperOptionsPreset.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common.settings_presets
2 |
3 | import android.provider.Settings
4 |
5 | class DeveloperOptionsPreset : BasePreset("dev_options") {
6 | override val settingsKVPairs = listOf(
7 | ReplacementItem(
8 | name = Settings.Global.ADB_ENABLED,
9 | value = "0",
10 | ),
11 | ReplacementItem(
12 | name = "adb_wifi_enabled",
13 | value = "0",
14 | ),
15 | ReplacementItem(
16 | name = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
17 | value = "0",
18 | ),
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_format_color_fill_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_cleaning_services_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_translate_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_my_location_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_speed_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_dark_mode_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_backup_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/workflows/crowdin.yml:
--------------------------------------------------------------------------------
1 | name: Crowdin Action
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches: [ master ]
7 | paths:
8 | - app/src/main/res/values/strings.xml
9 |
10 | jobs:
11 | synchronize-with-crowdin:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v4
16 |
17 | - name: crowdin action
18 | uses: crowdin/github-action@master
19 | with:
20 | upload_translations: false
21 | download_translations: false
22 | upload_sources: true
23 | config: 'crowdin.yml'
24 | crowdin_branch_name: master
25 | env:
26 | CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
27 | CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }}
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_update_disabled_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_android_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/viewmodel/AppSettingsViewModel.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import icu.nullptr.hidemyapplist.common.JsonConfig
6 |
7 | class AppSettingsViewModel(val pack: Pack) : ViewModel() {
8 |
9 | class Factory(private val pack: Pack) : ViewModelProvider.Factory {
10 | override fun create(modelClass: Class): T {
11 | if (modelClass.isAssignableFrom(AppSettingsViewModel::class.java)) {
12 | @Suppress("UNCHECKED_CAST")
13 | return AppSettingsViewModel(pack) as T
14 | } else throw IllegalArgumentException("Unknown ViewModel class")
15 | }
16 | }
17 |
18 | class Pack(
19 | val app: String,
20 | var enabled: Boolean,
21 | val config: JsonConfig.AppConfig
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_assignment_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.agp.lib)
3 | alias(libs.plugins.refine)
4 | alias(libs.plugins.kotlin)
5 | alias(libs.plugins.kotlin.serialization)
6 | }
7 |
8 | val configVerCode: Int by rootProject.extra
9 | val serviceVerCode: Int by rootProject.extra
10 | val minBackupVerCode: Int by rootProject.extra
11 |
12 | android {
13 | namespace = "org.frknkrc44.hma_oss.common"
14 |
15 | buildFeatures {
16 | aidl = true
17 | buildConfig = true
18 | }
19 |
20 | defaultConfig {
21 | buildConfigField("int", "CONFIG_VERSION", configVerCode.toString())
22 | buildConfigField("int", "SERVICE_VERSION", serviceVerCode.toString())
23 | buildConfigField("int", "MIN_BACKUP_VERSION", minBackupVerCode.toString())
24 | }
25 | }
26 |
27 | kotlin {
28 | jvmToolchain(21)
29 | }
30 |
31 | dependencies {
32 | api(libs.kotlinx.serialization.json)
33 | compileOnly(libs.dev.rikka.hidden.stub)
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppManageFragment.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.fragment
2 |
3 | import android.os.Bundle
4 | import icu.nullptr.hidemyapplist.service.ConfigManager
5 | import icu.nullptr.hidemyapplist.ui.adapter.AppManageAdapter
6 | import icu.nullptr.hidemyapplist.ui.util.navController
7 | import org.frknkrc44.hma_oss.R
8 |
9 | class AppManageFragment : AppSelectFragment() {
10 |
11 | override val firstComparator: Comparator = Comparator.comparing(ConfigManager::isHideEnabled).reversed()
12 |
13 | override val adapter = AppManageAdapter {
14 | val args = AppSettingsFragmentArgs(it)
15 | navController.navigate(R.id.nav_app_settings, args.toBundle())
16 | }
17 |
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 | // enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
21 | // returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_discount_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/service/ServiceProvider.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.service
2 |
3 | import android.content.ContentProvider
4 | import android.content.ContentValues
5 | import android.net.Uri
6 | import android.os.Bundle
7 |
8 | class ServiceProvider : ContentProvider() {
9 |
10 | override fun onCreate() = false
11 |
12 | override fun query(p0: Uri, p1: Array?, p2: String?, p3: Array?, p4: String?) = null
13 |
14 | override fun getType(p0: Uri) = null
15 |
16 | override fun insert(p0: Uri, p1: ContentValues?) = null
17 |
18 | override fun delete(p0: Uri, p1: String?, p2: Array?) = 0
19 |
20 | override fun update(p0: Uri, p1: ContentValues?, p2: String?, p3: Array?) = 0
21 |
22 | override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
23 | if (callingPackage != "android" || extras == null) return null
24 | val binder = extras.getBinder("binder") ?: return null
25 | ServiceClient.linkService(binder)
26 | return Bundle()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/Utils4Xposed.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.xposed
2 |
3 | import android.os.Binder
4 | import android.os.Build
5 | import com.github.kyuubiran.ezxhelper.utils.findField
6 | import icu.nullptr.hidemyapplist.common.Constants
7 | import icu.nullptr.hidemyapplist.common.Utils
8 |
9 | class Utils4Xposed {
10 | companion object {
11 | fun getPackageNameFromPackageSettings(packageSettings: Any): String? {
12 | return runCatching {
13 | findField(packageSettings::class.java, true) {
14 | name == if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) "mName" else "name"
15 | }.get(packageSettings) as? String
16 | }.getOrNull()
17 | }
18 |
19 | fun getCallingApps(service: HMAService): Array {
20 | val callingUid = Binder.getCallingUid()
21 | if (callingUid == Constants.UID_SYSTEM) return arrayOf()
22 | return Utils.binderLocalScope {
23 | service.pms.getPackagesForUid(callingUid)
24 | } ?: arrayOf()
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
17 |
18 |
19 |
24 |
25 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/XposedModulesPreset.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common.app_presets
2 |
3 | import android.content.pm.ApplicationInfo
4 | import java.util.zip.ZipFile
5 |
6 | class XposedModulesPreset() : BasePreset("xposed") {
7 | override val exactPackageNames = setOf()
8 |
9 | override fun canBeAddedIntoPreset(appInfo: ApplicationInfo): Boolean {
10 | ZipFile(appInfo.sourceDir).use { zipFile ->
11 | val manifestFile = zipFile.getInputStream(
12 | zipFile.getEntry("AndroidManifest.xml"))
13 | val manifestBytes = manifestFile.use { it.readBytes() }
14 | val manifestStr = String(manifestBytes, Charsets.US_ASCII)
15 |
16 | // Checking with binary because the Android system sucks
17 | if (manifestStr.contains("\u0000x\u0000p\u0000o\u0000s\u0000e\u0000d\u0000m\u0000o\u0000d\u0000u\u0000l\u0000e")) {
18 | return true
19 | } else if (zipFile.getEntry("META-INF/xposed")?.isDirectory ?: false) {
20 | return true
21 | }
22 | }
23 |
24 | return false
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/common/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Keep `Companion` object fields of serializable classes.
2 | # This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
3 | -if @kotlinx.serialization.Serializable class **
4 | -keepclassmembers class <1> {
5 | static <1>$Companion Companion;
6 | }
7 |
8 | # Keep `serializer()` on companion objects (both default and named) of serializable classes.
9 | -if @kotlinx.serialization.Serializable class ** {
10 | static **$* *;
11 | }
12 | -keepclassmembers class <2>$<3> {
13 | kotlinx.serialization.KSerializer serializer(...);
14 | }
15 |
16 | # Keep `INSTANCE.serializer()` of serializable objects.
17 | -if @kotlinx.serialization.Serializable class ** {
18 | public static ** INSTANCE;
19 | }
20 | -keepclassmembers class <1> {
21 | public static <1> INSTANCE;
22 | kotlinx.serialization.KSerializer serializer(...);
23 | }
24 |
25 | # @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
26 | -keepattributes RuntimeVisibleAnnotations,AnnotationDefault
27 |
28 | -keep class icu.nullptr.hidemyapplist.common.JsonConfig { *; }
29 | -keep class icu.nullptr.hidemyapplist.common.JsonConfig$* { *; }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/ScopeFragment.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.fragment
2 |
3 | import android.os.Bundle
4 | import androidx.fragment.app.setFragmentResult
5 | import androidx.navigation.fragment.navArgs
6 | import icu.nullptr.hidemyapplist.service.ConfigManager
7 | import icu.nullptr.hidemyapplist.ui.adapter.AppScopeAdapter
8 | import icu.nullptr.hidemyapplist.ui.util.navController
9 |
10 | class ScopeFragment : AppSelectFragment() {
11 |
12 | private lateinit var checked: MutableSet
13 |
14 | override val firstComparator: Comparator = Comparator.comparing { !checked.contains(it) }
15 |
16 | override val adapter by lazy {
17 | val args by navArgs()
18 | checked = args.checked.toMutableSet()
19 | if (!args.filterOnlyEnabled) AppScopeAdapter(checked, null)
20 | else AppScopeAdapter(checked) { ConfigManager.getAppConfig(it)?.useWhitelist == args.isWhiteList }
21 | }
22 |
23 | override fun onBack() {
24 | setFragmentResult("app_select", Bundle().apply {
25 | putStringArrayList("checked", ArrayList(checked))
26 | })
27 | navController.navigateUp()
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sentiment_very_dissatisfied_24px.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes_overlay.xml:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
23 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_bug_report_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/settings_data_isolation.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
14 |
15 |
16 |
17 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/SettingsPresets.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common
2 |
3 | import icu.nullptr.hidemyapplist.common.settings_presets.AccessibilityPreset
4 | import icu.nullptr.hidemyapplist.common.settings_presets.BasePreset
5 | import icu.nullptr.hidemyapplist.common.settings_presets.DeveloperOptionsPreset
6 |
7 | class SettingsPresets private constructor() {
8 | private val presetList = mutableListOf()
9 |
10 | companion object {
11 | private var hiddenInstance: SettingsPresets? = null
12 |
13 | val instance: SettingsPresets
14 | get() {
15 | if (hiddenInstance == null) {
16 | hiddenInstance = SettingsPresets()
17 | }
18 |
19 | return hiddenInstance!!
20 | }
21 | }
22 |
23 | fun getAllPresetNames() = presetList.map { it.name }.toTypedArray()
24 | // fun filterPresetsByName(names: Array) = presetList.filter { names.contains(it.name) }
25 | fun getPresetByName(name: String) = presetList.firstOrNull { it.name == name }
26 |
27 | init {
28 | presetList.add(DeveloperOptionsPreset())
29 | presetList.add(AccessibilityPreset())
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_home_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
21 |
22 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_settings_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_language_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_app_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
26 |
27 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/README_zh_CN.md:
--------------------------------------------------------------------------------
1 | # HMA-OSS
2 |
3 | [](https://github.com/frknkrc44)
4 | [](https://github.com/frknkrc44/HMA-OSS/actions)
5 | [](https://github.com/frknkrc44/HMA-OSS/releases/latest)
6 | [](https://github.com/frknkrc44/HMA-OSS/releases/latest)
7 | [](https://t.me/aerathfuns)
8 | [](https://choosealicense.com/licenses/gpl-3.0/)
9 |
10 | - [English](README.md)
11 | - 中文(简体)
12 |
13 | ## 关于该模块
14 | 虽然“检测安装的应用”是不正确的做法,但是并不是所有的与 root 相关联的插件类应用都提供了随机包名支持。这就意味着检测到安装了此类应用(如 Fake Location 、存储空间隔离)与检测到了 root 本身区别不大。(会使用检测手段的 app 可不会认为你是在“我就蹭蹭不进去”)
15 | 与此同时,部分“不安分”的应用会使用各种漏洞绕过系统权限来获取你的应用列表,从而对你建立用户画像。(如陈叔叔将安装了 V2Ray 的用户分为一类),或是类似于某某校园某某乐跑的软件会要求你卸载作弊软件。
16 | 该模块提供了一些检测方式用于测试您是否成功地隐藏了某些特定的包名,如 Magisk/Edxposed Manager;同时可作为 Xposed 模块用于隐藏应用列表或特定应用,保护隐私。
17 |
18 | ## 更新日志
19 | [参考发布页面](https://github.com/frknkrc44/HMA-OSS/releases)
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/xposed/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | -keep class com.github.kyuubiran.ezxhelper.utils.** { *; }
2 | -keep class icu.nullptr.hidemyapplist.xposed.XposedEntry { *; }
3 | -dontwarn java.lang.invoke.StringConcatFactory
4 | -dontwarn android.content.res.XModuleResources
5 | -dontwarn android.content.res.XResources
6 | -dontwarn de.robv.android.xposed.IXposedHookLoadPackage
7 | -dontwarn de.robv.android.xposed.IXposedHookZygoteInit$StartupParam
8 | -dontwarn de.robv.android.xposed.IXposedHookZygoteInit
9 | -dontwarn de.robv.android.xposed.XC_MethodHook$MethodHookParam
10 | -dontwarn de.robv.android.xposed.XC_MethodHook$Unhook
11 | -dontwarn de.robv.android.xposed.XC_MethodHook
12 | -dontwarn de.robv.android.xposed.XC_MethodReplacement
13 | -dontwarn de.robv.android.xposed.XposedBridge
14 | -dontwarn de.robv.android.xposed.XposedHelpers
15 | -dontwarn de.robv.android.xposed.callbacks.XC_LoadPackage$LoadPackageParam
16 | -dontwarn org.bouncycastle.jsse.BCSSLParameters
17 | -dontwarn org.bouncycastle.jsse.BCSSLSocket
18 | -dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
19 | -dontwarn org.conscrypt.Conscrypt$Version
20 | -dontwarn org.conscrypt.Conscrypt
21 | -dontwarn org.conscrypt.ConscryptHostnameVerifier
22 | -dontwarn org.openjsse.javax.net.ssl.SSLParameters
23 | -dontwarn org.openjsse.javax.net.ssl.SSLSocket
24 | -dontwarn org.openjsse.net.ssl.OpenJSSE
25 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/receiver/AppChangeReceiver.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.receiver
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.content.IntentFilter
7 | import android.util.Log
8 | import icu.nullptr.hidemyapplist.util.PackageHelper
9 |
10 | class AppChangeReceiver : BroadcastReceiver() {
11 |
12 | companion object {
13 | private const val TAG = "AppChangeReceiver"
14 |
15 | private val actions = setOf(
16 | Intent.ACTION_PACKAGE_ADDED,
17 | Intent.ACTION_PACKAGE_REMOVED,
18 | Intent.ACTION_PACKAGE_REPLACED
19 | )
20 |
21 | fun register(context: Context) {
22 | val filter = IntentFilter().apply {
23 | actions.forEach(::addAction)
24 | addDataScheme("package")
25 | }
26 | context.registerReceiver(AppChangeReceiver(), filter)
27 | }
28 | }
29 |
30 | override fun onReceive(context: Context, intent: Intent) {
31 | if (intent.action in actions) {
32 | Log.i(TAG, "Received intent: $intent")
33 | PackageHelper.invalidateCache()
34 | // ServiceClient.handlePackageEvent(intent.action, intent.data?.encodedSchemeSpecificPart)
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/.github/workflows/pull_request.yml:
--------------------------------------------------------------------------------
1 | name: Pull Request
2 |
3 | on: pull_request
4 |
5 | jobs:
6 | build:
7 | name: Build on ${{ matrix.os }}
8 | runs-on: ${{ matrix.os }}
9 | strategy:
10 | fail-fast: false
11 | matrix:
12 | os: [ ubuntu-latest ]
13 | if: ${{ !startsWith(github.event.head_commit.message, 'docs:') }}
14 |
15 | steps:
16 | - name: Check out
17 | uses: actions/checkout@v4
18 | with:
19 | submodules: 'recursive'
20 | fetch-depth: 0
21 |
22 | - name: Gradle wrapper validation
23 | uses: gradle/wrapper-validation-action@v3
24 |
25 | - name: Set up JDK 21
26 | uses: actions/setup-java@v4
27 | with:
28 | java-version: '21'
29 | distribution: 'temurin'
30 |
31 | - name: Write properties
32 | run: |
33 | echo buildWithGitSuffix=true >> local.properties
34 |
35 | - name: Gradle prebuild
36 | run: |
37 | echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties
38 | ./gradlew prebuild
39 |
40 | - name: Build debug
41 | id: buildDebug
42 | run: |
43 | ./gradlew :app:assembleDebug
44 | echo "debugName=$(ls app/build/apk/debug/HMA*-debug.apk | awk -F '(/|.apk)' '{print $6}')" >> $GITHUB_OUTPUT
45 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_palette_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
19 |
22 |
23 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/BasePreset.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common.app_presets
2 |
3 | import android.content.pm.ApplicationInfo
4 |
5 | abstract class BasePreset(val name: String) {
6 | protected abstract val exactPackageNames: Set
7 | protected val packageNames = mutableSetOf()
8 |
9 | protected abstract fun canBeAddedIntoPreset(appInfo: ApplicationInfo): Boolean
10 |
11 | fun isDynamicListEmpty() = packageNames.isEmpty()
12 |
13 | fun containsPackage(packageName: String) = exactPackageNames.contains(packageName) || packageNames.contains(packageName)
14 |
15 | fun addPackageInfoPreset(appInfo: ApplicationInfo): Boolean {
16 | val packageName = appInfo.packageName
17 | if (!exactPackageNames.contains(packageName) && canBeAddedIntoPreset(appInfo)) {
18 | packageNames.add(packageName)
19 | return true
20 | }
21 |
22 | return false
23 | }
24 |
25 | fun removePackageFromPreset(packageName: String): Boolean {
26 | if (exactPackageNames.contains(packageName)) return false
27 | return packageNames.remove(packageName)
28 | }
29 |
30 | override fun toString() = "${javaClass.simpleName} {" +
31 | " \"exactPackageNames\": $exactPackageNames," +
32 | " \"packageNames\": $packageNames" +
33 | " }"
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sentiment_calm_24px.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/data/UpdateInfo.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.data
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | class UpdateInfo(
6 | val versionName: String,
7 | val versionCode: Int,
8 | val content: String,
9 | val downloadUrl: String
10 | )
11 |
12 | @Serializable
13 | private data class UpdateData(
14 | val release: Item?,
15 | val beta: Item?
16 | ) {
17 | @Serializable
18 | data class Item(
19 | val versionName: String,
20 | val versionCode: Int,
21 | val downloadUrl: String
22 | )
23 | }
24 |
25 | suspend fun fetchLatestUpdate(): UpdateInfo? {
26 | /*
27 | val updateData = RxHttp.get(Constants.UPDATE_URL_BASE + "updates.json")
28 | .toAwait()
29 | .tryAwait() ?: return null
30 | val isBeta = PrefManager.receiveBetaUpdate && updateData.beta != null
31 | val item = (if (isBeta) updateData.beta else updateData.release) ?: return null
32 | val variantPrefix = if (isBeta) "beta" else "release"
33 | val languagePrefix = if (Locale.getDefault().language.contains("zh")) "zh" else "en"
34 | val content = RxHttp.get(Constants.UPDATE_URL_BASE + variantPrefix + "-" + languagePrefix + ".html")
35 | .toAwaitString()
36 | .tryAwait() ?: return null
37 | return UpdateInfo(item.versionName, item.versionCode, content, item.downloadUrl)
38 | */
39 | return null
40 | }
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | # Uncomment the following line in case you need and you don't have the release build type files in your app
18 | release/
19 | app/debug
20 |
21 | # Gradle files
22 | .gradle/
23 | build/
24 |
25 | # Local configuration file (sdk path, etc)
26 | local.properties
27 |
28 | # Proguard folder generated by Eclipse
29 | proguard/
30 |
31 | # Log Files
32 | *.log
33 |
34 | # Android Studio Navigation editor temp files
35 | .navigation/
36 |
37 | # Android Studio captures folder
38 | captures/
39 |
40 | # IntelliJ
41 | *.iml
42 | .idea/
43 |
44 | # Keystore files
45 | # Uncomment the following lines if you do not want to check your keystore files in.
46 | #*.jks
47 | #*.keystore
48 |
49 | # External native build folder generated in Android Studio 2.2 and later
50 | .externalNativeBuild
51 | .cxx/
52 |
53 | # Google Services (e.g. APIs or Firebase)
54 | google-services.json
55 |
56 | # Freeline
57 | freeline.py
58 | freeline/
59 | freeline_project_description.json
60 |
61 | # fastlane
62 | fastlane/report.xml
63 | fastlane/Preview.html
64 | fastlane/screenshots
65 | fastlane/test_output
66 | fastlane/readme.md
67 |
68 | # Version control
69 | vcs.xml
70 |
71 | # lint
72 | lint/intermediates/
73 | lint/generated/
74 | lint/outputs/
75 | lint/tmp/
76 | # lint/reports/
77 |
78 | # Android Profiling
79 | *.hprof
80 |
81 | updates/
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/viewmodel/TemplateSettingsViewModel.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import icu.nullptr.hidemyapplist.service.ConfigManager
6 | import icu.nullptr.hidemyapplist.ui.fragment.TemplateSettingsFragmentArgs
7 | import kotlinx.coroutines.flow.MutableStateFlow
8 |
9 | class TemplateSettingsViewModel(
10 | val originalName: String?,
11 | val isWhiteList: Boolean,
12 | var name: String?
13 | ) : ViewModel() {
14 |
15 | class Factory(private val args: TemplateSettingsFragmentArgs) : ViewModelProvider.Factory {
16 | override fun create(modelClass: Class): T {
17 | if (modelClass.isAssignableFrom(TemplateSettingsViewModel::class.java)) {
18 | val viewModel = TemplateSettingsViewModel(args.name, args.isWhiteList, args.name)
19 | args.name?.let {
20 | viewModel.appliedAppList.value = ConfigManager.getTemplateAppliedAppList(it)
21 | viewModel.targetAppList.value = ConfigManager.getTemplateTargetAppList(it)
22 | }
23 | @Suppress("UNCHECKED_CAST")
24 | return viewModel as T
25 | } else throw IllegalArgumentException("Unknown ViewModel class")
26 | }
27 | }
28 |
29 | val appliedAppList = MutableStateFlow>(ArrayList())
30 | val targetAppList = MutableStateFlow>(ArrayList())
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
26 |
27 |
35 |
36 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/CustomROMPreset.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common.app_presets
2 |
3 | import android.content.pm.ApplicationInfo
4 | import icu.nullptr.hidemyapplist.common.Utils
5 |
6 | class CustomROMPreset() : BasePreset("custom_rom") {
7 | override val exactPackageNames = setOf(
8 | "io.chaldeaprjkt.gamespace"
9 | )
10 |
11 | override fun canBeAddedIntoPreset(appInfo: ApplicationInfo): Boolean {
12 | val packageName = appInfo.packageName
13 |
14 | // LineageOS overlays
15 | if (appInfo.sourceDir.contains("_lineage_")) {
16 | return true
17 | }
18 |
19 | // LineageOS apps
20 | if (Utils.startsWithMultiple(packageName, "lineageos.", "org.lineageos.")) {
21 | return true
22 | }
23 |
24 | // AOSPA
25 | if (packageName.startsWith("co.aospa.")) {
26 | return true
27 | }
28 |
29 | // OmniROM
30 | if (packageName.startsWith("org.omnirom.")) {
31 | return true
32 | }
33 |
34 | // ProtonAOSP
35 | if (packageName.startsWith("org.protonaosp.")) {
36 | return true
37 | }
38 |
39 | // EvoX (just added by the community request)
40 | if (Utils.startsWithMultiple("org.evolution.", "org.evolutionx.") ||
41 | Utils.endsWithMultiple(".evolution", ".evolutionx")) {
42 | return true
43 | }
44 |
45 | // TODO: Add more custom ROM apps
46 | return false
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_logs.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
17 |
18 |
19 |
27 |
28 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppScopeAdapter.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.adapter
2 |
3 | import android.view.ViewGroup
4 | import icu.nullptr.hidemyapplist.ui.view.AppItemView
5 |
6 | class AppScopeAdapter(
7 | private val checked: MutableSet,
8 | firstFilter: ((String) -> Boolean)?,
9 | ) : AppSelectAdapter(firstFilter) {
10 |
11 | private inline var String.isChecked
12 | get() = checked.contains(this)
13 | set(value) {
14 | if (value) checked.add(this) else checked.remove(this)
15 | }
16 |
17 | inner class ViewHolder(view: AppItemView) : AppSelectAdapter.ViewHolder(view) {
18 | init {
19 | view.setOnClickListener {
20 | val packageName = filteredList[absoluteAdapterPosition]
21 | packageName.isChecked = !packageName.isChecked
22 | view.isChecked = packageName.isChecked
23 | }
24 | }
25 |
26 | override fun bind(packageName: String) {
27 | (itemView as AppItemView).let {
28 | it.load(packageName)
29 | it.isChecked = packageName.isChecked
30 | }
31 | }
32 | }
33 |
34 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
35 | val view = AppItemView(parent.context, true)
36 | view.layoutParams = ViewGroup.LayoutParams(
37 | ViewGroup.LayoutParams.MATCH_PARENT,
38 | ViewGroup.LayoutParams.WRAP_CONTENT
39 | )
40 | return ViewHolder(view)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/AppItemView.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.view
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.widget.LinearLayout
6 | import by.kirich1409.viewbindingdelegate.CreateMethod
7 | import by.kirich1409.viewbindingdelegate.viewBinding
8 | import icu.nullptr.hidemyapplist.util.PackageHelper
9 | import org.frknkrc44.hma_oss.databinding.AppItemViewBinding
10 |
11 | class AppItemView @JvmOverloads constructor(
12 | context: Context,
13 | attrs: AttributeSet? = null,
14 | defStyleAttr: Int = 0,
15 | defStyleRes: Int = 0
16 | ) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
17 |
18 | val binding by viewBinding(createMethod = CreateMethod.INFLATE)
19 |
20 | var showEnabled: Boolean
21 | get() = binding.enabled.visibility == VISIBLE
22 | set(value) {
23 | binding.enabled.visibility = if (value) VISIBLE else GONE
24 | }
25 |
26 | var isChecked: Boolean
27 | get() = binding.checkbox.isChecked
28 | set(value) {
29 | binding.checkbox.isChecked = value
30 | }
31 |
32 | constructor(context: Context, isCheckable: Boolean) : this(context) {
33 | binding.checkbox.visibility = if (isCheckable) VISIBLE else GONE
34 | }
35 |
36 | fun load(packageName: String) {
37 | binding.packageName.text = packageName
38 | binding.label.text = PackageHelper.loadAppLabel(packageName)
39 | binding.icon.setImageBitmap(PackageHelper.loadAppIcon(packageName))
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HMA-OSS
2 |
3 | [](https://github.com/frknkrc44)
4 | [](https://github.com/frknkrc44/HMA-OSS/actions)
5 | [](https://github.com/frknkrc44/HMA-OSS/releases/latest)
6 | [](https://github.com/frknkrc44/HMA-OSS/releases/latest)
7 | [](https://t.me/aerathfuns)
8 | [](https://choosealicense.com/licenses/gpl-3.0/)
9 |
10 | - English
11 | - [中文(简体)](README_zh_CN.md)
12 |
13 | ## About this module
14 |
15 | Although it's bad practice to detect the installation of specific apps, not every app using root provides random package name support. In this case, if apps related to root (such as Fake Location and Storage Isolation) are detected, it is tantamount to detecting that the device is rooted.
16 |
17 | Additionally, some apps use various loopholes to acquire your app list, in order to use it as fingerprinting data or for other nefarious purposes.
18 |
19 | This module can work as an Xposed module to hide apps or reject app list requests, and provides some methods to test whether you have hidden your app list properly.
20 |
21 | ## Update Log
22 | [Reference to the release page](https://github.com/frknkrc44/HMA-OSS/releases)
23 |
--------------------------------------------------------------------------------
/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/Logcat.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.xposed
2 |
3 | import android.util.Log
4 | import de.robv.android.xposed.XposedBridge
5 | import java.text.SimpleDateFormat
6 | import java.util.Date
7 | import java.util.Locale
8 |
9 | private fun parseLog(level: Int, tag: String, msg: String, cause: Throwable? = null) = buildString {
10 | val levelStr = when (level) {
11 | Log.DEBUG -> "DEBUG"
12 | Log.INFO -> " INFO"
13 | Log.WARN -> " WARN"
14 | Log.ERROR -> "ERROR"
15 | else -> "?????"
16 | }
17 | val date = SimpleDateFormat("MM-dd HH:mm:ss", Locale.getDefault()).format(Date())
18 | append("[$levelStr] $date ($tag) $msg")
19 | if (!endsWith('\n')) append('\n')
20 | if (cause != null) append(Log.getStackTraceString(cause))
21 | if (!endsWith('\n')) append('\n')
22 | }
23 |
24 | private fun log(level: Int, tag: String, msg: String, cause: Throwable? = null) {
25 | if (level <= Log.DEBUG && HMAService.instance?.config?.detailLog == false) return
26 | val parsedLog = parseLog(level, tag, msg, cause)
27 | HMAService.instance?.executor?.execute {
28 | HMAService.instance?.addLog(parsedLog)
29 | }
30 | XposedBridge.log(parsedLog)
31 | }
32 |
33 | fun logD(tag: String, msg: String, cause: Throwable? = null) = log(Log.DEBUG, tag, msg, cause)
34 | fun logI(tag: String, msg: String, cause: Throwable? = null) = log(Log.INFO, tag, msg, cause)
35 | fun logW(tag: String, msg: String, cause: Throwable? = null) = log(Log.WARN, tag, msg, cause)
36 | fun logE(tag: String, msg: String, cause: Throwable? = null) = log(Log.ERROR, tag, msg, cause)
37 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_app_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/MyApp.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Application
5 | import androidx.appcompat.app.AppCompatDelegate
6 | import icu.nullptr.hidemyapplist.receiver.AppChangeReceiver
7 | import icu.nullptr.hidemyapplist.service.ConfigManager
8 | import icu.nullptr.hidemyapplist.service.PrefManager
9 | import icu.nullptr.hidemyapplist.ui.util.makeToast
10 | import icu.nullptr.hidemyapplist.util.ConfigUtils.Companion.getLocale
11 | import kotlinx.coroutines.CoroutineScope
12 | import kotlinx.coroutines.Dispatchers
13 | import me.zhanghai.android.appiconloader.AppIconLoader
14 | import org.frknkrc44.hma_oss.R
15 | import kotlin.system.exitProcess
16 |
17 | lateinit var hmaApp: MyApp
18 |
19 | class MyApp : Application() {
20 |
21 | @JvmField
22 | var isHooked = false
23 |
24 | val globalScope = CoroutineScope(Dispatchers.Default)
25 | val appIconLoader by lazy {
26 | val iconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size)
27 | AppIconLoader(iconSize, false, this)
28 | }
29 |
30 | @Suppress("DEPRECATION")
31 | @SuppressLint("SdCardPath")
32 | override fun onCreate() {
33 | super.onCreate()
34 | hmaApp = this
35 | if (!filesDir.absolutePath.startsWith("/data/user/0/")) {
36 | makeToast(R.string.do_not_dual)
37 | exitProcess(0)
38 | }
39 | AppChangeReceiver.register(this)
40 | ConfigManager.init()
41 |
42 | AppCompatDelegate.setDefaultNightMode(PrefManager.darkTheme)
43 | val config = resources.configuration
44 | config.setLocale(getLocale())
45 | resources.updateConfiguration(config, resources.displayMetrics)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/Constants.java:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common;
2 |
3 | import java.util.Set;
4 |
5 | public class Constants {
6 |
7 | public static final String APP_PACKAGE_NAME = "org.frknkrc44.hma_oss";
8 |
9 | public static final String PROVIDER_AUTHORITY = APP_PACKAGE_NAME + ".ServiceProvider";
10 | public static final String GMS_PACKAGE_NAME = "com.google.android.gms";
11 | public static final String GSF_PACKAGE_NAME = "com.google.android.gsf";
12 | public static final String VENDING_PACKAGE_NAME = "com.android.vending";
13 |
14 | public static final String ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY = "persist.zygote.app_data_isolation";
15 | public static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = "persist.sys.vold_app_data_isolation_enabled";
16 |
17 | public static final String DESCRIPTOR = "android.content.pm.IPackageManager";
18 | public static final int TRANSACTION = 'H' << 24 | 'M' << 16 | 'A' << 8 | 'D';
19 | public static final int ACTION_GET_BINDER = 1;
20 |
21 | public static final int UID_SYSTEM = 1000;
22 |
23 | public static final Set packagesShouldNotHide = Set.of(
24 | "android",
25 | "android.media",
26 | "android.uid.system",
27 | "android.uid.shell",
28 | "android.uid.systemui",
29 | "com.android.permissioncontroller",
30 | "com.android.providers.downloads",
31 | "com.android.providers.downloads.ui",
32 | "com.android.providers.media",
33 | "com.android.providers.media.module",
34 | "com.android.providers.settings",
35 | "com.google.android.webview",
36 | "com.google.android.providers.media.module"
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/JsonConfig.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common
2 |
3 | import kotlinx.serialization.Serializable
4 | import kotlinx.serialization.encodeToString
5 | import kotlinx.serialization.json.Json
6 | import org.frknkrc44.hma_oss.common.BuildConfig
7 |
8 | private val encoder = Json {
9 | encodeDefaults = true
10 | ignoreUnknownKeys = true
11 | }
12 |
13 | @Serializable
14 | data class JsonConfig(
15 | var configVersion: Int = BuildConfig.CONFIG_VERSION,
16 | var detailLog: Boolean = false,
17 | var maxLogSize: Int = 512,
18 | var forceMountData: Boolean = true,
19 | val templates: MutableMap = mutableMapOf(),
20 | val scope: MutableMap = mutableMapOf()
21 | ) {
22 | @Serializable
23 | data class Template(
24 | val isWhitelist: Boolean,
25 | val appList: Set
26 | ) {
27 | override fun toString() = encoder.encodeToString(this)
28 | }
29 |
30 | @Serializable
31 | data class AppConfig(
32 | var useWhitelist: Boolean = false,
33 | var excludeSystemApps: Boolean = true,
34 | var hideInstallationSource: Boolean = false,
35 | var hideSystemInstallationSource: Boolean = false,
36 | var excludeTargetInstallationSource: Boolean = false,
37 | var applyTemplates: MutableSet = mutableSetOf(),
38 | var applyPresets: MutableSet = mutableSetOf(),
39 | var applySettingsPresets: MutableSet = mutableSetOf(),
40 | var extraAppList: MutableSet = mutableSetOf()
41 | ) {
42 | override fun toString() = encoder.encodeToString(this)
43 | }
44 |
45 | companion object {
46 | fun parse(json: String) = encoder.decodeFromString(json)
47 | }
48 |
49 | override fun toString() = encoder.encodeToString(this)
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/org/frknkrc44/hma_oss/ui/activity/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package org.frknkrc44.hma_oss.ui.activity
2 |
3 | import android.content.Context
4 | import android.content.res.Resources
5 | import android.os.Bundle
6 | import androidx.activity.enableEdgeToEdge
7 | import androidx.appcompat.app.AppCompatActivity
8 | import androidx.navigation.findNavController
9 | import com.google.android.material.color.DynamicColors
10 | import icu.nullptr.hidemyapplist.hmaApp
11 | import icu.nullptr.hidemyapplist.ui.util.ThemeUtils
12 | import icu.nullptr.hidemyapplist.util.ConfigUtils.Companion.getLocale
13 | import org.frknkrc44.hma_oss.R
14 | import org.frknkrc44.hma_oss.databinding.ActivityMainBinding
15 |
16 | class MainActivity : AppCompatActivity() {
17 | override fun onCreate(savedInstanceState: Bundle?) {
18 | super.onCreate(savedInstanceState)
19 |
20 | enableEdgeToEdge()
21 |
22 | val binding = ActivityMainBinding.inflate(layoutInflater)
23 | setContentView(binding.root)
24 |
25 | DynamicColors.applyToActivityIfAvailable(this)
26 | }
27 |
28 | override fun onApplyThemeResource(theme: Resources.Theme, resid: Int, first: Boolean) {
29 | super.onApplyThemeResource(theme, resid, first)
30 | theme.applyStyle(ThemeUtils.getNightThemeStyleRes(this), true)
31 | }
32 |
33 | override fun onSupportNavigateUp(): Boolean {
34 | val navController = this.findNavController(R.id.nav_host_fragment)
35 | return navController.navigateUp() || super.onSupportNavigateUp()
36 | }
37 |
38 | override fun attachBaseContext(newBase: Context?) {
39 | super.attachBaseContext(getLocaleAppliedContext(newBase))
40 | }
41 |
42 | fun getLocaleAppliedContext(context: Context?): Context? {
43 | val config = hmaApp.resources.configuration
44 | config.setLocale(getLocale())
45 |
46 | return context?.createConfigurationContext(config)
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_logs.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
9 |
14 |
19 | -
22 |
23 |
24 |
28 |
32 |
36 |
40 |
41 |
42 |
43 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_app_select.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
20 |
21 |
22 |
27 |
28 |
34 |
35 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/view/ListItemView.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.view
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.widget.LinearLayout
6 | import androidx.annotation.DrawableRes
7 | import by.kirich1409.viewbindingdelegate.CreateMethod
8 | import by.kirich1409.viewbindingdelegate.viewBinding
9 | import org.frknkrc44.hma_oss.R
10 | import org.frknkrc44.hma_oss.databinding.ListItemViewBinding
11 |
12 | class ListItemView @JvmOverloads constructor(
13 | context: Context,
14 | attrs: AttributeSet? = null,
15 | defStyleAttr: Int = 0,
16 | defStyleRes: Int = 0
17 | ) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
18 |
19 | private val binding by viewBinding(createMethod = CreateMethod.INFLATE)
20 |
21 | init {
22 | val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.ListItemView, defStyleAttr, defStyleRes)
23 | val icon = typedArray.getResourceId(R.styleable.ListItemView_icon, 0)
24 | val text = typedArray.getString(R.styleable.ListItemView_text)
25 | val buttonText = typedArray.getText(R.styleable.ListItemView_buttonText)
26 | typedArray.recycle()
27 | binding.icon.setImageResource(icon)
28 | binding.text.text = text
29 | if (buttonText != null) {
30 | binding.button.visibility = VISIBLE
31 | binding.button.text = buttonText
32 | }
33 | }
34 |
35 | var text: CharSequence?
36 | get() = binding.text.text
37 | set(value) {
38 | binding.text.text = value
39 | }
40 |
41 | fun setIcon(@DrawableRes icon: Int) {
42 | binding.icon.setImageResource(icon)
43 | }
44 |
45 | override fun setOnClickListener(l: OnClickListener?) {
46 | if (binding.button.visibility == VISIBLE) {
47 | binding.button.setOnClickListener(l)
48 | } else {
49 | super.setOnClickListener(l)
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_settings_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PmsPackageEventsHook.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.xposed.hook
2 |
3 | import android.content.Intent
4 | import android.os.Build
5 | import com.github.kyuubiran.ezxhelper.utils.findMethod
6 | import com.github.kyuubiran.ezxhelper.utils.hookBefore
7 | import de.robv.android.xposed.XC_MethodHook
8 | import icu.nullptr.hidemyapplist.xposed.HMAService
9 |
10 | class PmsPackageEventsHook(private val service: HMAService) : IFrameworkHook {
11 | private var hook: XC_MethodHook.Unhook? = null
12 |
13 | override fun load() {
14 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
15 | try {
16 | hook = findMethod("com.android.server.pm.BroadcastHelper") {
17 | name == "sendPackageBroadcastAndNotify"
18 | }.hookBefore { param ->
19 | service.handlePackageEvent(
20 | param.args[0] as String?,
21 | param.args[1] as String?
22 | )
23 | }
24 | } catch (e: Throwable) {
25 | hook = findMethod("com.android.internal.content.PackageMonitor") {
26 | name == "onReceive"
27 | }.hookBefore { param ->
28 | val intent = param.args[1] as Intent? ?: return@hookBefore
29 |
30 | service.handlePackageEvent(
31 | intent.action,
32 | intent.data?.encodedSchemeSpecificPart
33 | )
34 | }
35 | }
36 | } else {
37 | hook = findMethod("com.android.server.pm.PackageManagerService") {
38 | name == "sendPackageBroadcast"
39 | }.hookBefore { param ->
40 | service.handlePackageEvent(
41 | param.args[0] as String?,
42 | param.args[1] as String?
43 | )
44 | }
45 | }
46 | }
47 |
48 | override fun unload() {
49 | hook?.unhook()
50 | hook = null
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/log_item_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
26 |
27 |
34 |
35 |
43 |
44 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppManageAdapter.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.adapter
2 |
3 | import android.view.ViewGroup
4 | import com.google.android.material.dialog.MaterialAlertDialogBuilder
5 | import icu.nullptr.hidemyapplist.common.Constants.GMS_PACKAGE_NAME
6 | import icu.nullptr.hidemyapplist.common.Constants.GSF_PACKAGE_NAME
7 | import icu.nullptr.hidemyapplist.service.ConfigManager
8 | import icu.nullptr.hidemyapplist.service.PrefManager
9 | import icu.nullptr.hidemyapplist.ui.view.AppItemView
10 | import org.frknkrc44.hma_oss.R
11 |
12 | class AppManageAdapter(
13 | private val onItemClickListener: (String) -> Unit
14 | ) : AppSelectAdapter() {
15 | private val riskyPackages = arrayOf(GMS_PACKAGE_NAME, GSF_PACKAGE_NAME)
16 |
17 | inner class ViewHolder(view: AppItemView) : AppSelectAdapter.ViewHolder(view) {
18 | init {
19 | view.setOnClickListener {
20 | if (!PrefManager.bypassRiskyPackageWarning && riskyPackages.contains(view.binding.packageName.text)) {
21 | MaterialAlertDialogBuilder(view.context)
22 | .setTitle(R.string.app_warning_risky_package_title)
23 | .setMessage(R.string.app_warning_risky_package_desc)
24 | .setNegativeButton(android.R.string.cancel, null)
25 | .setPositiveButton(android.R.string.ok) { _, _ ->
26 | onItemClickListener.invoke(filteredList[absoluteAdapterPosition])
27 | }
28 | .show()
29 |
30 | return@setOnClickListener
31 | }
32 |
33 | onItemClickListener.invoke(filteredList[absoluteAdapterPosition])
34 | }
35 | }
36 |
37 | override fun bind(packageName: String) {
38 | (itemView as AppItemView).let {
39 | it.load(packageName)
40 | it.showEnabled = ConfigManager.isHideEnabled(packageName)
41 | }
42 | }
43 | }
44 |
45 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
46 | val view = AppItemView(parent.context, false)
47 | view.layoutParams = ViewGroup.LayoutParams(
48 | ViewGroup.LayoutParams.MATCH_PARENT,
49 | ViewGroup.LayoutParams.WRAP_CONTENT
50 | )
51 | return ViewHolder(view)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_item_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
23 |
24 |
30 |
31 |
40 |
41 |
47 |
48 |
49 |
50 |
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/util/Fragment.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.util
2 |
3 | import android.os.Bundle
4 | import android.view.Menu
5 | import android.view.MenuInflater
6 | import android.view.MenuItem
7 | import android.view.View
8 | import androidx.annotation.DrawableRes
9 | import androidx.annotation.IdRes
10 | import androidx.annotation.MenuRes
11 | import androidx.appcompat.widget.Toolbar
12 | import androidx.core.view.MenuProvider
13 | import androidx.fragment.app.Fragment
14 | import androidx.navigation.NavOptions
15 | import androidx.navigation.fragment.NavHostFragment
16 |
17 | val Fragment.navController get() = NavHostFragment.findNavController(this)
18 |
19 | fun Fragment.navigate(@IdRes resId: Int, args: Bundle? = null) {
20 | val navOptions = NavOptions.Builder().apply {
21 | setEnterAnim(androidx.navigation.ui.R.anim.nav_default_enter_anim)
22 | setExitAnim(androidx.navigation.ui.R.anim.nav_default_exit_anim)
23 | setPopEnterAnim(androidx.navigation.ui.R.anim.nav_default_pop_enter_anim)
24 | setPopExitAnim(androidx.navigation.ui.R.anim.nav_default_pop_exit_anim)
25 | }.build()
26 |
27 | navController.navigate(resId, args, navOptions)
28 | }
29 |
30 | fun Fragment.setupToolbar(
31 | toolbar: Toolbar,
32 | title: String,
33 | @DrawableRes navigationIcon: Int? = null,
34 | navigationOnClick: View.OnClickListener? = null,
35 | @MenuRes menuRes: Int? = null,
36 | onMenuOptionSelected: ((MenuItem) -> Unit)? = null
37 | ) {
38 | navigationOnClick?.let { toolbar.setNavigationOnClickListener(it) }
39 | navigationIcon?.let { toolbar.setNavigationIcon(navigationIcon) }
40 | toolbar.title = title
41 | toolbar.tooltipText = title
42 | if (menuRes != null) {
43 | val menuProvider = object : MenuProvider {
44 | override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
45 | menuInflater.inflate(menuRes, menu)
46 | }
47 |
48 | override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
49 | return onMenuOptionSelected?.let {
50 | it(menuItem); true
51 | } ?: false
52 | }
53 | }
54 | toolbar.inflateMenu(menuRes)
55 | toolbar.setOnMenuItemClickListener(menuProvider::onMenuItemSelected)
56 | requireActivity().addMenuProvider(menuProvider)
57 | menuProvider.onPrepareMenu(toolbar.menu)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/AppSelectAdapter.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.adapter
2 |
3 | import android.widget.Filter
4 | import android.widget.Filterable
5 | import androidx.recyclerview.widget.RecyclerView
6 | import icu.nullptr.hidemyapplist.service.PrefManager
7 | import icu.nullptr.hidemyapplist.ui.view.AppItemView
8 | import icu.nullptr.hidemyapplist.util.PackageHelper
9 | import kotlinx.coroutines.flow.first
10 | import kotlinx.coroutines.runBlocking
11 |
12 | abstract class AppSelectAdapter(
13 | private val firstFilter: ((String) -> Boolean)? = null
14 | ) : RecyclerView.Adapter(), Filterable {
15 |
16 | abstract class ViewHolder(view: AppItemView) : RecyclerView.ViewHolder(view) {
17 | abstract fun bind(packageName: String)
18 | }
19 |
20 | private inner class AppFilter : Filter() {
21 | override fun performFiltering(constraint: CharSequence): FilterResults {
22 | return runBlocking {
23 | val constraintLowered = constraint.toString().lowercase()
24 | val filteredList = PackageHelper.appList.first().filter {
25 | if (firstFilter?.invoke(it) == false) return@filter false
26 | if (!PrefManager.appFilter_showSystem && PackageHelper.isSystem(it)) return@filter false
27 | val label = PackageHelper.loadAppLabel(it)
28 | val packageInfo = PackageHelper.loadPackageInfo(it)
29 | label.lowercase().contains(constraintLowered) || packageInfo.packageName.lowercase().contains(constraintLowered)
30 | }
31 |
32 | FilterResults().also { it.values = filteredList }
33 | }
34 | }
35 |
36 | @Suppress("UNCHECKED_CAST", "NotifyDataSetChanged")
37 | override fun publishResults(constraint: CharSequence, results: FilterResults) {
38 | filteredList = results.values as List
39 | notifyDataSetChanged()
40 | }
41 | }
42 |
43 | private val mFilter = AppFilter()
44 |
45 | protected var filteredList: List = listOf()
46 |
47 | override fun getItemCount() = filteredList.size
48 |
49 | override fun getItemId(position: Int) = filteredList[position].hashCode().toLong()
50 |
51 | override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(filteredList[position])
52 |
53 | override fun getFilter(): Filter = mFilter
54 | }
55 |
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.android.build.gradle.internal.api.BaseVariantOutputImpl
2 |
3 | plugins {
4 | alias(libs.plugins.agp.app)
5 | alias(libs.plugins.autoresconfig)
6 | alias(libs.plugins.refine)
7 | alias(libs.plugins.kotlin)
8 | alias(libs.plugins.kotlin.serialization)
9 | alias(libs.plugins.ksp)
10 | alias(libs.plugins.nav.safeargs.kotlin)
11 | }
12 |
13 | android {
14 | namespace = "org.frknkrc44.hma_oss"
15 |
16 | buildFeatures {
17 | buildConfig = true
18 | viewBinding = true
19 | }
20 |
21 | packaging {
22 | dex.useLegacyPackaging = true
23 | resources {
24 | excludes += arrayOf(
25 | "/META-INF/*",
26 | "/META-INF/androidx/**",
27 | "/kotlin/**",
28 | "/okhttp3/**",
29 | )
30 | }
31 | }
32 | }
33 |
34 | kotlin {
35 | jvmToolchain(21)
36 | }
37 |
38 | autoResConfig {
39 | generateClass.set(true)
40 | generateRes.set(false)
41 | generatedClassFullName.set("icu.nullptr.hidemyapplist.util.LangList")
42 | generatedArrayFirstItem.set("SYSTEM")
43 | }
44 |
45 | dependencies {
46 | implementation(projects.common)
47 | runtimeOnly(projects.xposed)
48 |
49 | implementation(libs.androidx.navigation.fragment.ktx)
50 | implementation(libs.androidx.navigation.ui.ktx)
51 | implementation(libs.androidx.preference.ktx)
52 | implementation(libs.androidx.swiperefreshlayout)
53 | implementation(libs.com.drakeet.about)
54 | implementation(libs.com.drakeet.multitype)
55 | implementation(libs.com.github.kirich1409.viewbindingpropertydelegate)
56 | implementation(libs.com.github.liujingxing.rxhttp)
57 | implementation(libs.com.github.liujingxing.rxhttp.converter.serialization)
58 | implementation(libs.com.github.topjohnwu.libsu.core)
59 | implementation(libs.com.squareup.okhttp3)
60 | implementation(libs.dev.rikka.hidden.compat)
61 | implementation(libs.me.zhanghai.android.appiconloader)
62 | compileOnly(libs.dev.rikka.hidden.stub)
63 | ksp(libs.com.github.liujingxing.rxhttp.compiler)
64 |
65 | implementation(libs.androidx.appcompat.appcompat)
66 | implementation(libs.material)
67 | }
68 |
69 | android.applicationVariants.all {
70 | outputs.all {
71 | (this as BaseVariantOutputImpl).apply {
72 | outputFileName = "${rootProject.name.replace(" ", "_")}-${versionName}-${buildType.name}.apk"
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/ActivityHook.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.xposed.hook
2 |
3 | import android.content.Intent
4 | import com.github.kyuubiran.ezxhelper.init.InitFields
5 | import com.github.kyuubiran.ezxhelper.utils.findMethod
6 | import com.github.kyuubiran.ezxhelper.utils.hookBefore
7 | import de.robv.android.xposed.XC_MethodHook
8 | import de.robv.android.xposed.XposedHelpers.findClass
9 | import de.robv.android.xposed.XposedHelpers.getObjectField
10 | import de.robv.android.xposed.XposedHelpers.getStaticIntField
11 | import icu.nullptr.hidemyapplist.xposed.HMAService
12 | import icu.nullptr.hidemyapplist.xposed.logD
13 | import icu.nullptr.hidemyapplist.xposed.logE
14 | import icu.nullptr.hidemyapplist.xposed.logI
15 |
16 | class ActivityHook(private val service: HMAService) : IFrameworkHook {
17 | companion object {
18 | private const val TAG = "ActivityHook"
19 | private val fakeReturnCode = getStaticIntField(
20 | findClass(
21 | "android.app.ActivityManager",
22 | InitFields.ezXClassLoader
23 | ),
24 | "START_INTENT_NOT_RESOLVED"
25 | )
26 | }
27 |
28 | private var hook: XC_MethodHook.Unhook? = null
29 |
30 | override fun load() {
31 | logI(TAG, "Load hook")
32 |
33 | hook = findMethod(
34 | "com.android.server.wm.ActivityStarter"
35 | ) {
36 | name == "execute"
37 | }.hookBefore { param ->
38 | runCatching {
39 | val request = getObjectField(param.thisObject, "mRequest")
40 | val caller = getObjectField(request, "callingPackage") as String?
41 | val intent = getObjectField(request, "intent") as Intent?
42 | val targetApp = intent?.component?.packageName
43 |
44 | if (service.shouldHide(caller, targetApp)) {
45 | logD(
46 | TAG,
47 | "@executeRequest: insecure query from $caller, target: ${intent?.component}"
48 | )
49 | param.result = fakeReturnCode
50 | service.filterCount++
51 | }
52 | }.onFailure {
53 | logE(TAG, "Fatal error occurred, ignore hook\n", it)
54 | // unload()
55 | }
56 | }
57 | }
58 |
59 | override fun unload() {
60 | hook?.unhook()
61 | hook = null
62 | }
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/adapter/TemplateAdapter.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.adapter
2 |
3 | import android.annotation.SuppressLint
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.RecyclerView
6 | import icu.nullptr.hidemyapplist.service.ConfigManager
7 | import icu.nullptr.hidemyapplist.ui.view.ListItemView
8 | import org.frknkrc44.hma_oss.R
9 | import java.text.Collator
10 | import java.util.Locale
11 |
12 | class TemplateAdapter(
13 | private val onClickListener: ((ConfigManager.TemplateInfo) -> Unit)?
14 | ) : RecyclerView.Adapter() {
15 |
16 | private lateinit var list: List
17 |
18 | init {
19 | updateList()
20 | }
21 |
22 | inner class ViewHolder(view: ListItemView) : RecyclerView.ViewHolder(view) {
23 | init {
24 | view.setOnClickListener {
25 | onClickListener?.invoke(list[absoluteAdapterPosition])
26 | }
27 | }
28 |
29 | fun bind(info: ConfigManager.TemplateInfo) {
30 | with(itemView as ListItemView) {
31 | setIcon(
32 | if (info.isWhiteList) R.drawable.outline_assignment_24
33 | else R.drawable.baseline_assignment_24
34 | )
35 | text = info.name
36 | }
37 | }
38 | }
39 |
40 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
41 | val view = ListItemView(parent.context)
42 | view.layoutParams = ViewGroup.LayoutParams(
43 | ViewGroup.LayoutParams.MATCH_PARENT,
44 | ViewGroup.LayoutParams.WRAP_CONTENT
45 | )
46 | return ViewHolder(view)
47 | }
48 |
49 | override fun getItemCount() = list.size
50 |
51 | override fun getItemId(position: Int) = list[position].name.hashCode().toLong()
52 |
53 | override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(list[position])
54 |
55 | @SuppressLint("NotifyDataSetChanged")
56 | fun updateList() {
57 | list = ConfigManager.getTemplateList().apply {
58 | sortWith { o1, o2 ->
59 | if (o1.isWhiteList != o2.isWhiteList) {
60 | o1.isWhiteList.compareTo(o2.isWhiteList)
61 | } else {
62 | Collator.getInstance(Locale.getDefault()).compare(o1.name, o2.name)
63 | }
64 | }
65 | }
66 | notifyDataSetChanged()
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/XposedEntry.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.xposed
2 |
3 | import android.content.pm.IPackageManager
4 | import com.github.kyuubiran.ezxhelper.init.EzXHelperInit
5 | import com.github.kyuubiran.ezxhelper.utils.findMethod
6 | import com.github.kyuubiran.ezxhelper.utils.getFieldByDesc
7 | import com.github.kyuubiran.ezxhelper.utils.hookAllConstructorAfter
8 | import com.github.kyuubiran.ezxhelper.utils.hookBefore
9 | import de.robv.android.xposed.IXposedHookLoadPackage
10 | import de.robv.android.xposed.IXposedHookZygoteInit
11 | import de.robv.android.xposed.XC_MethodHook
12 | import de.robv.android.xposed.callbacks.XC_LoadPackage
13 | import icu.nullptr.hidemyapplist.common.Constants
14 | import kotlin.concurrent.thread
15 |
16 | private const val TAG = "HMA-XposedEntry"
17 |
18 | @Suppress("unused")
19 | class XposedEntry : IXposedHookZygoteInit, IXposedHookLoadPackage {
20 |
21 | override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
22 | EzXHelperInit.initZygote(startupParam)
23 | }
24 |
25 | override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
26 | if (lpparam.packageName == Constants.APP_PACKAGE_NAME) {
27 | EzXHelperInit.initHandleLoadPackage(lpparam)
28 | hookAllConstructorAfter("icu.nullptr.hidemyapplist.MyApp") {
29 | getFieldByDesc("Licu/nullptr/hidemyapplist/MyApp;->isHooked:Z").setBoolean(it.thisObject, true)
30 | }
31 | } else if (lpparam.packageName == "android") {
32 | EzXHelperInit.initHandleLoadPackage(lpparam)
33 | logI(TAG, "Hook entry")
34 |
35 | var serviceManagerHook: XC_MethodHook.Unhook? = null
36 | serviceManagerHook = findMethod("android.os.ServiceManager") {
37 | name == "addService"
38 | }.hookBefore { param ->
39 | if (param.args[0] == "package") {
40 | serviceManagerHook?.unhook()
41 | val pms = param.args[1] as IPackageManager
42 | logD(TAG, "Got pms: $pms")
43 | thread {
44 | runCatching {
45 | UserService.register(pms)
46 | logI(TAG, "User service started")
47 | }.onFailure {
48 | logE(TAG, "System service crashed", it)
49 | }
50 | }
51 | }
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_status_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
23 |
24 |
30 |
31 |
38 |
39 |
46 |
47 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/PlatformCompatHook.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.xposed.hook
2 |
3 | import android.content.pm.ApplicationInfo
4 | import android.os.Build
5 | import androidx.annotation.RequiresApi
6 | import com.github.kyuubiran.ezxhelper.utils.findMethod
7 | import com.github.kyuubiran.ezxhelper.utils.hookBefore
8 | import de.robv.android.xposed.XC_MethodHook
9 | import icu.nullptr.hidemyapplist.common.CommonUtils
10 | import icu.nullptr.hidemyapplist.common.Utils
11 | import icu.nullptr.hidemyapplist.xposed.HMAService
12 | import icu.nullptr.hidemyapplist.xposed.logE
13 | import icu.nullptr.hidemyapplist.xposed.logI
14 |
15 | @RequiresApi(Build.VERSION_CODES.R)
16 | class PlatformCompatHook(private val service: HMAService) : IFrameworkHook {
17 |
18 | companion object {
19 | private const val TAG = "PlatformCompatHook"
20 | private val sAppDataIsolationEnabled = CommonUtils.isAppDataIsolationEnabled
21 | }
22 |
23 | private var hook: XC_MethodHook.Unhook? = null
24 |
25 | override fun load() {
26 | if (!service.config.forceMountData) return
27 | logI(TAG, "Load hook")
28 | logI(TAG, "App data isolation enabled: $sAppDataIsolationEnabled")
29 | hook = findMethod("com.android.server.compat.PlatformCompat") {
30 | name == "isChangeEnabled"
31 | }.hookBefore { param ->
32 | runCatching {
33 | val changeId = param.args[0] as Long
34 | val appInfo = param.args[1] as ApplicationInfo
35 | if (changeId.toInt() != 143937733) return@hookBefore
36 | val apps = Utils.binderLocalScope {
37 | service.pms.getPackagesForUid(appInfo.uid)
38 | } ?: return@hookBefore
39 | for (app in apps) {
40 | if (service.isHookEnabled(app)) {
41 | if (sAppDataIsolationEnabled) param.result = true
42 | logI(TAG, "force mount data: ${appInfo.uid} $app")
43 | return@hookBefore
44 | }
45 | }
46 | }.onFailure {
47 | logE(TAG, "Fatal error occurred, disable hooks", it)
48 | unload()
49 | }
50 | }
51 | }
52 |
53 | override fun unload() {
54 | hook?.unhook()
55 | hook = null
56 | }
57 |
58 | override fun onConfigChanged() {
59 | if (service.config.forceMountData) {
60 | if (hook == null) load()
61 | } else {
62 | if (hook != null) unload()
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
18 |
19 |
22 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
48 |
49 |
52 |
55 |
58 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/app/src/main/res/navigation/home_nav_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
11 |
15 |
16 |
20 |
21 |
25 |
29 |
33 |
36 |
37 |
41 |
45 |
48 |
49 |
53 |
56 |
60 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/util/ThemeUtils.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.util
2 |
3 | import android.content.Context
4 | import android.content.res.Configuration
5 | import android.graphics.Color
6 | import android.graphics.drawable.Drawable
7 | import androidx.annotation.AttrRes
8 | import androidx.annotation.ColorInt
9 | import androidx.annotation.ColorRes
10 | import androidx.annotation.StyleRes
11 | import androidx.fragment.app.Fragment
12 | import icu.nullptr.hidemyapplist.service.PrefManager
13 | import org.frknkrc44.hma_oss.R
14 |
15 | object ThemeUtils {
16 | private const val THEME_DEFAULT = "DEFAULT"
17 | private const val THEME_BLACK = "BLACK"
18 |
19 | fun isNightMode(context: Context) = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
20 |
21 | fun isUsingBlackTheme(context: Context) = PrefManager.blackDarkTheme && isNightMode(context)
22 |
23 | fun getNightTheme(context: Context): String {
24 | return if (isUsingBlackTheme(context))
25 | THEME_BLACK else THEME_DEFAULT
26 | }
27 |
28 | @StyleRes
29 | fun getNightThemeStyleRes(context: Context): Int {
30 | return if (isUsingBlackTheme(context))
31 | R.style.ThemeOverlay_Black else R.style.ThemeOverlay
32 | }
33 |
34 | /**
35 | * Retrieve a color from the current [android.content.res.Resources.Theme].
36 | */
37 | @ColorInt
38 | fun Context.themeColor(
39 | @AttrRes themeAttrId: Int
40 | ): Int {
41 | val style = obtainStyledAttributes(intArrayOf(themeAttrId))
42 | val color = style.getColor(0, Color.MAGENTA)
43 | style.recycle()
44 | return color
45 | }
46 |
47 | @ColorInt
48 | fun Fragment.themeColor(
49 | @AttrRes themeAttrId: Int
50 | ) = requireContext().themeColor(themeAttrId)
51 |
52 | fun Context.attrDrawable(
53 | @AttrRes themeAttrId: Int
54 | ): Drawable? {
55 | val style = obtainStyledAttributes(intArrayOf(themeAttrId))
56 | val drawable = style.getDrawable(0)
57 | style.recycle()
58 | return drawable
59 | }
60 |
61 | fun Fragment.attrDrawable(
62 | @AttrRes themeAttrId: Int
63 | ) = requireContext().attrDrawable(themeAttrId)
64 |
65 | @ColorInt
66 | fun Fragment.getColor(
67 | @ColorRes colorId: Int
68 | ) = requireContext().getColor(colorId)
69 |
70 | fun Fragment.homeItemBackgroundColor() = if (isNightMode(requireContext())) {
71 | themeColor(com.google.android.material.R.attr.colorSurfaceContainerHighest)
72 | } else {
73 | themeColor(com.google.android.material.R.attr.colorSurfaceContainer)
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_template_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
19 |
20 |
21 |
26 |
27 |
35 |
36 |
40 |
41 |
42 |
48 |
49 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | - android
4 |
5 |
6 |
7 | - 256K
8 | - 512K
9 | - 1M
10 |
11 |
12 |
13 | - 256
14 | - 512
15 | - 1024
16 |
17 |
18 |
19 | - @string/dark_theme_off
20 | - @string/dark_theme_on
21 | - @string/follow_system
22 |
23 |
24 |
25 | - 1
26 | - 2
27 | - -1
28 |
29 |
30 |
31 | - SAKURA
32 | - MATERIAL_RED
33 | - MATERIAL_PINK
34 | - MATERIAL_PURPLE
35 | - MATERIAL_DEEP_PURPLE
36 | - MATERIAL_INDIGO
37 | - MATERIAL_BLUE
38 | - MATERIAL_LIGHT_BLUE
39 | - MATERIAL_CYAN
40 | - MATERIAL_TEAL
41 | - MATERIAL_GREEN
42 | - MATERIAL_LIGHT_GREEN
43 | - MATERIAL_LIME
44 | - MATERIAL_YELLOW
45 | - MATERIAL_AMBER
46 | - MATERIAL_ORANGE
47 | - MATERIAL_DEEP_ORANGE
48 | - MATERIAL_BROWN
49 | - MATERIAL_BLUE_GREY
50 |
51 |
52 |
53 | - @string/color_sakura
54 | - @string/color_red
55 | - @string/color_pink
56 | - @string/color_purple
57 | - @string/color_deep_purple
58 | - @string/color_indigo
59 | - @string/color_blue
60 | - @string/color_light_blue
61 | - @string/color_cyan
62 | - @string/color_teal
63 | - @string/color_green
64 | - @string/color_light_green
65 | - @string/color_lime
66 | - @string/color_yellow
67 | - @string/color_amber
68 | - @string/color_orange
69 | - @string/color_deep_orange
70 | - @string/color_brown
71 | - @string/color_blue_grey
72 |
73 |
74 |
--------------------------------------------------------------------------------
/xposed/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.android.ide.common.signing.KeystoreHelper
2 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
3 | import java.io.PrintStream
4 | import java.util.Locale
5 |
6 | plugins {
7 | alias(libs.plugins.agp.lib)
8 | alias(libs.plugins.refine)
9 | alias(libs.plugins.kotlin)
10 | }
11 |
12 | android {
13 | namespace = "org.frknkrc44.hma_oss.xposed"
14 |
15 | buildFeatures {
16 | buildConfig = false
17 | }
18 | }
19 |
20 | kotlin {
21 | jvmToolchain(21)
22 | }
23 |
24 | afterEvaluate {
25 | //noinspection WrongGradleMethod
26 | android.libraryVariants.forEach { variant ->
27 | val variantCapped = variant.name.replaceFirstChar { it.titlecase(Locale.ROOT) }
28 | val variantLowered = variant.name.lowercase(Locale.ROOT)
29 |
30 | val outSrcDir = layout.buildDirectory.dir("generated/source/signInfo/${variantLowered}")
31 | val outSrc = outSrcDir.get().file("icu/nullptr/hidemyapplist/Magic.java")
32 | val signInfoTask = tasks.register("generate${variantCapped}SignInfo") {
33 | outputs.file(outSrc)
34 | doLast {
35 | val sign = android.buildTypes[variantLowered].signingConfig
36 | outSrc.asFile.parentFile.mkdirs()
37 | val certificateInfo = KeystoreHelper.getCertificateInfo(
38 | sign?.storeType,
39 | sign?.storeFile,
40 | sign?.storePassword,
41 | sign?.keyPassword,
42 | sign?.keyAlias
43 | )
44 | PrintStream(outSrc.asFile).apply {
45 | println("package icu.nullptr.hidemyapplist;")
46 | println("public final class Magic {")
47 | print("public static final byte[] magicNumbers = {")
48 | val bytes = certificateInfo.certificate.encoded
49 | print(bytes.joinToString(",") { it.toString() })
50 | println("};")
51 | println("}")
52 | }
53 | }
54 | }
55 | variant.registerJavaGeneratingTask(signInfoTask, outSrcDir.get().asFile)
56 |
57 | val kotlinCompileTask = tasks.findByName("compile${variantCapped}Kotlin") as KotlinCompile
58 | kotlinCompileTask.dependsOn(signInfoTask)
59 | val srcSet = objects.sourceDirectorySet("magic", "magic").srcDir(outSrcDir)
60 | kotlinCompileTask.source(srcSet)
61 | }
62 | }
63 |
64 | dependencies {
65 | implementation(projects.common)
66 |
67 | implementation(libs.androidx.annotation.jvm)
68 | implementation(libs.com.android.tools.build.apksig)
69 | implementation(libs.com.github.kyuubiran.ezxhelper)
70 | implementation(libs.dev.rikka.hidden.compat)
71 | compileOnly(libs.de.robv.android.xposed.api)
72 | compileOnly(libs.dev.rikka.hidden.stub)
73 | }
74 |
--------------------------------------------------------------------------------
/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/AccessibilityHook.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.xposed.hook
2 |
3 | import android.accessibilityservice.AccessibilityServiceInfo
4 | import com.github.kyuubiran.ezxhelper.utils.findMethod
5 | import com.github.kyuubiran.ezxhelper.utils.hookBefore
6 | import de.robv.android.xposed.XC_MethodHook
7 | import icu.nullptr.hidemyapplist.common.settings_presets.AccessibilityPreset
8 | import icu.nullptr.hidemyapplist.xposed.HMAService
9 | import icu.nullptr.hidemyapplist.xposed.Utils4Xposed
10 | import icu.nullptr.hidemyapplist.xposed.logE
11 | import icu.nullptr.hidemyapplist.xposed.logI
12 |
13 | // Big credits: https://github.com/Nitsuya/DoNotTryAccessibility/blob/main/app/src/main/java/io/github/nitsuya/donottryaccessibility/hook/AndroidFrameworkHooker.kt
14 | class AccessibilityHook(private val service: HMAService) : IFrameworkHook {
15 | companion object {
16 | private const val TAG = "AccessibilityHook"
17 | private const val ACCESSIBILITY_SERVICE = "com.android.server.accessibility.AccessibilityManagerService"
18 | }
19 |
20 | private val hookList = mutableSetOf()
21 |
22 | override fun load() {
23 | logI(TAG, "Load hook")
24 |
25 | hookList += findMethod(ACCESSIBILITY_SERVICE) {
26 | name == "getInstalledAccessibilityServiceList"
27 | }.hookBefore { param -> hookedMethod(param) }
28 |
29 | hookList += findMethod(ACCESSIBILITY_SERVICE) {
30 | name == "getEnabledAccessibilityServiceList"
31 | }.hookBefore { param -> hookedMethod(param) }
32 |
33 | hookList += findMethod(ACCESSIBILITY_SERVICE) {
34 | name == "addClient"
35 | }.hookBefore { param ->
36 | val callingApps = Utils4Xposed.getCallingApps(service)
37 | if (callingApps.isEmpty()) return@hookBefore
38 |
39 | for (caller in callingApps) {
40 | if (service.getEnabledSettingsPresets(caller).contains(AccessibilityPreset.NAME)) {
41 | param.result = 0L
42 | service.filterCount++
43 | break
44 | }
45 | }
46 | }
47 | }
48 |
49 | private fun hookedMethod(param: XC_MethodHook.MethodHookParam) {
50 | try {
51 | val callingApps = Utils4Xposed.getCallingApps(service)
52 | if (callingApps.isEmpty()) return
53 |
54 | for (caller in callingApps) {
55 | if (service.getEnabledSettingsPresets(caller).contains(AccessibilityPreset.NAME)) {
56 | param.result = java.util.ArrayList()
57 | service.filterCount++
58 | break
59 | }
60 | }
61 | } catch (e: Throwable) {
62 | logE(TAG, "Fatal error occurred, ignore hooks", e)
63 | }
64 | }
65 |
66 | override fun unload() {
67 | hookList.forEach(XC_MethodHook.Unhook::unhook)
68 | hookList.clear()
69 | }
70 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
39 |
46 |
53 |
54 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Main
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | paths-ignore:
7 | - '.github/ISSUE_TEMPLATE/**'
8 | - '.github/workflows/issue.yml'
9 | - '**.md'
10 |
11 | jobs:
12 | build:
13 | name: Build on ${{ matrix.os }}
14 | runs-on: ${{ matrix.os }}
15 | strategy:
16 | fail-fast: false
17 | matrix:
18 | os: [ ubuntu-latest ]
19 | if: ${{ !startsWith(github.event.head_commit.message, '[skip ci]') }}
20 |
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v4
24 | with:
25 | submodules: 'recursive'
26 | fetch-depth: 0
27 |
28 | - name: Gradle wrapper validation
29 | uses: gradle/wrapper-validation-action@v3
30 |
31 | - name: Set up JDK 21
32 | uses: actions/setup-java@v4
33 | with:
34 | distribution: 'temurin'
35 | java-version: '21'
36 |
37 | - name: Write key
38 | if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master'
39 | run: |
40 | echo officialBuild=true >> local.properties
41 | echo buildWithGitSuffix=true >> local.properties
42 | echo storePassword='${{ secrets.KEY_STORE_PASSWORD }}' >> local.properties
43 | echo keyAlias='${{ secrets.ALIAS }}' >> local.properties
44 | echo keyPassword='${{ secrets.ALIAS_KEY_PASSWORD }}' >> local.properties
45 | echo fileDir=`pwd`/key.jks >> local.properties
46 | echo "${{ secrets.KEY_STORE }}" | base64 --decode > key.jks
47 |
48 | - name: Cache gradle
49 | uses: actions/cache@v4
50 | with:
51 | path: |
52 | ~/.gradle/caches
53 | ~/.gradle/wrapper
54 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
55 | restore-keys: ${{ runner.os }}-gradle-
56 |
57 | - name: Gradle prebuild
58 | run: |
59 | ./gradlew prebuild
60 |
61 | - name: Build all
62 | id: buildAll
63 | run: |
64 | ./gradlew assemble
65 | echo "releaseName=$(ls app/build/outputs/apk/release/*.apk | awk -F '(/|.apk)' '{print $5}')" >> $GITHUB_OUTPUT
66 | echo "debugName=$(ls app/build/outputs/apk/debug/*.apk | awk -F '(/|.apk)' '{print $5}')" >> $GITHUB_OUTPUT
67 |
68 | - name: Upload release
69 | if: success()
70 | uses: actions/upload-artifact@v4
71 | with:
72 | name: ${{ steps.buildAll.outputs.releaseName }}
73 | path: "app/build/outputs/apk/release/*.apk"
74 |
75 | - name: Upload debug
76 | if: success()
77 | uses: actions/upload-artifact@v4
78 | with:
79 | name: ${{ steps.buildAll.outputs.debugName }}
80 | path: "app/build/outputs/apk/debug/*.apk"
81 |
82 | - name: Upload mappings
83 | uses: actions/upload-artifact@v4
84 | with:
85 | name: mappings
86 | path: "app/build/outputs/mapping/release"
87 |
--------------------------------------------------------------------------------
/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/hook/ContentProviderHook.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.xposed.hook
2 |
3 | import android.os.Bundle
4 | import android.provider.Settings
5 | import com.github.kyuubiran.ezxhelper.utils.findMethod
6 | import com.github.kyuubiran.ezxhelper.utils.hookBefore
7 | import de.robv.android.xposed.XC_MethodHook
8 | import icu.nullptr.hidemyapplist.common.SettingsPresets
9 | import icu.nullptr.hidemyapplist.common.settings_presets.ReplacementItem
10 | import icu.nullptr.hidemyapplist.xposed.HMAService
11 | import icu.nullptr.hidemyapplist.xposed.Utils4Xposed
12 | import icu.nullptr.hidemyapplist.xposed.logD
13 |
14 | class ContentProviderHook(private val service: HMAService): IFrameworkHook {
15 | companion object {
16 | private const val TAG = "ContentProviderHook"
17 | }
18 |
19 | private var hook: XC_MethodHook.Unhook? = null
20 |
21 | // Credit: https://github.com/Nitsuya/DoNotTryAccessibility/blob/main/app/src/main/java/io/github/nitsuya/donottryaccessibility/hook/AndroidFrameworkHooker.kt
22 | override fun load() {
23 | hook = findMethod(
24 | "android.content.ContentProvider\$Transport"
25 | ) {
26 | name == "call"
27 | }.hookBefore { param ->
28 | val callingApps = Utils4Xposed.getCallingApps(service)
29 | if (callingApps.isEmpty()) return@hookBefore
30 |
31 | val method = param.args[2] as String?
32 | val name = param.args[3] as String?
33 |
34 | for (caller in callingApps) {
35 | // logD(TAG, "@call caller: $caller, method: $method, name: $name")
36 |
37 | when (method) {
38 | "GET_global", "GET_secure", "GET_system" -> {
39 | val replacement = getSpoofedSetting(caller, name)
40 | if (replacement != null) {
41 | logD(TAG, "@getSettings returned replacement for $caller: ${replacement.value}")
42 | param.result = Bundle().apply {
43 | putString(Settings.NameValueTable.VALUE, replacement.value)
44 | putInt("_generation_index", -1)
45 | }
46 | service.filterCount++
47 | }
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
54 | fun getSpoofedSetting(caller: String?, name: String?): ReplacementItem? {
55 | if (caller == null || name == null) return null
56 |
57 | val presets = service.getEnabledSettingsPresets(caller)
58 | if (presets.isEmpty()) {
59 | return null
60 | }
61 |
62 | for (presetName in presets) {
63 | val preset = SettingsPresets.instance.getPresetByName(presetName)
64 | val value = preset?.getSpoofedValue(name)
65 | if (value != null) return value
66 | }
67 |
68 | return null
69 | }
70 |
71 | override fun unload() {
72 | hook?.unhook()
73 | hook = null
74 | }
75 | }
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.12.1"
3 | kotlin = "2.1.10"
4 | material = "1.13.0-rc01"
5 | rxhttp = "3.3.1"
6 | hidden-api = "4.3.3"
7 |
8 | [plugins]
9 | agp-app = { id = "com.android.application", version.ref = "agp" }
10 | agp-lib = { id = "com.android.library", version.ref = "agp" }
11 | kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
12 | kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
13 | ksp = { id = "com.google.devtools.ksp", version = "2.1.10-1.0.31" }
14 | nav-safeargs-kotlin = { id = "androidx.navigation.safeargs.kotlin", version = "2.8.8" }
15 | autoresconfig = { id = "dev.rikka.tools.autoresconfig", version = "1.2.2" }
16 | materialthemebuilder = { id = "dev.rikka.tools.materialthemebuilder", version = "1.5.1" }
17 | refine = { id = "dev.rikka.tools.refine", version = "4.4.0" }
18 |
19 | [libraries]
20 | androidx-appcompat-appcompat = { group = "androidx.appcompat", name = "appcompat", version = "1.7.1" }
21 | androidx-annotation-jvm = { group = "androidx.annotation", name = "annotation-jvm", version = "1.9.1" }
22 | androidx-navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version = "2.8.8" }
23 | androidx-navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version = "2.8.8" }
24 | androidx-preference-ktx = { module = "androidx.preference:preference-ktx", version = "1.2.1" }
25 | androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version = "1.1.0" }
26 | com-android-tools-build-apksig = { module = "com.android.tools.build:apksig", version.ref = "agp" }
27 | com-drakeet-about = { module = "com.drakeet.about:about", version = "2.5.2" }
28 | com-drakeet-multitype = { module = "com.drakeet.multitype:multitype", version = "4.3.0" }
29 | com-github-kirich1409-viewbindingpropertydelegate = { module = "com.github.kirich1409:viewbindingpropertydelegate", version = "1.5.9" }
30 | com-github-kyuubiran-ezxhelper = { module = "com.github.kyuubiran:EzXHelper", version = "1.0.3" }
31 | com-github-liujingxing-rxhttp = { module = "com.github.liujingxing.rxhttp:rxhttp", version.ref = "rxhttp" }
32 | com-github-liujingxing-rxhttp-compiler = { module = "com.github.liujingxing.rxhttp:rxhttp-compiler", version.ref = "rxhttp" }
33 | com-github-liujingxing-rxhttp-converter-serialization = { module = "com.github.liujingxing.rxhttp:converter-serialization", version.ref = "rxhttp" }
34 | com-github-topjohnwu-libsu-core = { module = "com.github.topjohnwu.libsu:core", version = "6.0.0" }
35 | com-squareup-okhttp3 = { module = "com.squareup.okhttp3:okhttp", version = "4.12.0" }
36 | de-robv-android-xposed-api = { module = "de.robv.android.xposed:api", version = "82" }
37 | dev-rikka-hidden-compat = { module = "dev.rikka.hidden:compat", version.ref = "hidden-api" }
38 | dev-rikka-hidden-stub = { module = "dev.rikka.hidden:stub", version.ref = "hidden-api" }
39 | kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.7.3" }
40 | material = { module = "com.google.android.material:material", version.ref = "material" }
41 | me-zhanghai-android-appiconloader = { module = "me.zhanghai.android.appiconloader:appiconloader", version = "1.5.0" }
42 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo. 1>&2
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
48 | echo. 1>&2
49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
50 | echo location of your Java installation. 1>&2
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo. 1>&2
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
62 | echo. 1>&2
63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
64 | echo location of your Java installation. 1>&2
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug report/反馈 Bug
2 | description: Report errors or unexpected behavior./反馈错误或异常行为。
3 | labels: [bug]
4 | title: "[Bug] "
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for reporting issues of HMA-OSS!
10 |
11 | To prevent spam, please fill in the title after "[Bug]".
12 |
13 | To make it easier for us to help you please enter detailed information below.
14 |
15 | 感谢给 HMA-OSS 汇报问题!
16 | 为了使我们更好地帮助你,请提供以下信息。
17 | 为了防止重复汇报与垃圾信息,标题请务必使用英文,在“[Bug]”之后填写内容。
18 | - type: textarea
19 | attributes:
20 | label: Steps to reproduce/复现步骤
21 | placeholder: |
22 | 1.
23 | 2.
24 | 3.
25 | validations:
26 | required: true
27 | - type: textarea
28 | attributes:
29 | label: Expected behaviour/预期行为
30 | placeholder: Tell us what should happen/正常情况下应该发生什么
31 | validations:
32 | required: true
33 | - type: textarea
34 | attributes:
35 | label: Actual behaviour/实际行为
36 | placeholder: Tell us what happens instead/实际上发生了什么
37 | validations:
38 | required: true
39 | - type: textarea
40 | attributes:
41 | label: Xposed Module List/Xposed 模块列表
42 | render: Shell
43 | validations:
44 | required: true
45 | - type: textarea
46 | attributes:
47 | label: Magisk Module List/Magisk 模块列表
48 | render: Shell
49 | validations:
50 | required: true
51 | - type: input
52 | attributes:
53 | label: HMA-OSS version/HMA-OSS 版本
54 | description: Please check [Actions](https://github.com/frknkrc44/HMA-OSS/actions/workflows/main.yml) for the latest CI debug build. Don't just type "latest". Specify actual version (**MUST** contains `.rXXX.XXX-debug`), otherwise your issue will be closed./请前往 [Actions](https://github.com/frknkrc44/HMA-OSS/actions/workflows/main.yml) 获取最新的 CI debug 版本。不要直接填用“最新版”。版本号**必须**包含 `·rXXX.XXX-debug`,否则 issue 会被自动关闭。
55 | placeholder: V_._._(-Beta).r___._______-debug
56 | validations:
57 | required: true
58 | - type: input
59 | attributes:
60 | label: Android version/Android 版本
61 | validations:
62 | required: true
63 | - type: input
64 | attributes:
65 | label: Magisk version/Magisk 版本
66 | validations:
67 | required: true
68 | - type: dropdown
69 | id: latest
70 | attributes:
71 | label: Version requirement/版本要求
72 | description: Select the version being used for feedback./选择反馈时正使用的版本。
73 | multiple: false
74 | options:
75 | - Latest CI debug build/最新 CI debug 构建
76 | - Version required by the developer/开发者要求的版本
77 | - Public release/beta version/公开发布/测试版
78 | - Other/其他
79 | validations:
80 | required: true
81 | - type: textarea
82 | attributes:
83 | label: Logs/日志
84 | description: For usage issues, please provide the log zip saved from manager; for activation issues, please provide [bugreport](https://developer.android.com/studio/debug/bug-report). Without log, the issue will be closed. /使用问题请提供从管理器保存的日志压缩包;激活问题请提供 [bugreport](https://developer.android.google.cn/studio/debug/bug-report?hl=zh-cn) 日志。无日志提交会被关闭。
85 | placeholder: Upload logs by clicking the bar on the bottom. /点击文本框底栏上传日志文件。
86 | validations:
87 | required: true
88 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/Utils.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common
2 |
3 | import android.content.pm.ApplicationInfo
4 | import android.content.pm.IPackageManager
5 | import android.content.pm.PackageInfo
6 | import android.os.Binder
7 | import android.os.Build
8 | import java.util.Random
9 |
10 | object Utils {
11 |
12 | fun generateRandomString(length: Int): String {
13 | val leftLimit = 97 // letter 'a'
14 | val rightLimit = 122 // letter 'z'
15 | val random = Random()
16 | val buffer = StringBuilder(length)
17 | for (i in 0 until length) {
18 | val randomLimitedInt = leftLimit + (random.nextFloat() * (rightLimit - leftLimit + 1)).toInt()
19 | buffer.append(randomLimitedInt.toChar())
20 | }
21 | return buffer.toString()
22 | }
23 |
24 | fun verifyAppSignature(path: String): Boolean {
25 | /*
26 | val verifier = ApkVerifier.Builder(File(path))
27 | .setMinCheckedPlatformVersion(24)
28 | .build()
29 | val result = verifier.verify()
30 | if (!result.isVerified) return false
31 | val mainCert = result.signerCertificates[0]
32 | return mainCert.encoded.contentEquals(Magic.magicNumbers)
33 | */
34 | return true
35 | }
36 |
37 | fun binderLocalScope(block: () -> T): T {
38 | val identity = Binder.clearCallingIdentity()
39 | val result = block()
40 | Binder.restoreCallingIdentity(identity)
41 | return result
42 | }
43 |
44 | fun getInstalledApplicationsCompat(pms: IPackageManager, flags: Long, userId: Int): List {
45 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
46 | pms.getInstalledApplications(flags, userId)
47 | } else {
48 | pms.getInstalledApplications(flags.toInt(), userId)
49 | }.list
50 | }
51 |
52 | fun getPackageUidCompat(pms: IPackageManager, packageName: String, flags: Long, userId: Int): Int {
53 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
54 | pms.getPackageUid(packageName, flags, userId)
55 | } else {
56 | pms.getPackageUid(packageName, flags.toInt(), userId)
57 | }
58 | }
59 |
60 | fun getPackageInfoCompat(pms: IPackageManager, packageName: String, flags: Long, userId: Int): PackageInfo {
61 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
62 | pms.getPackageInfo(packageName, flags, userId)
63 | } else {
64 | pms.getPackageInfo(packageName, flags.toInt(), userId)
65 | }
66 | }
67 |
68 | fun startsWithMultiple(source: String, vararg targets: String): Boolean {
69 | assert(source.isNotEmpty() && targets.isNotEmpty())
70 |
71 | for (target in targets) {
72 | if (source.startsWith(target)) {
73 | return true
74 | }
75 | }
76 |
77 | return false
78 | }
79 |
80 | fun endsWithMultiple(source: String, vararg targets: String): Boolean {
81 | assert(source.isNotEmpty() && targets.isNotEmpty())
82 |
83 | for (target in targets) {
84 | if (source.endsWith(target)) {
85 | return true
86 | }
87 | }
88 |
89 | return false
90 | }
91 | }
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/adapter/LogAdapter.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.adapter
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.view.LayoutInflater
6 | import android.view.ViewGroup
7 | import androidx.recyclerview.widget.RecyclerView
8 | import icu.nullptr.hidemyapplist.service.PrefManager
9 | import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.themeColor
10 | import org.frknkrc44.hma_oss.R
11 | import org.frknkrc44.hma_oss.databinding.LogItemViewBinding
12 | import java.util.regex.Pattern
13 |
14 | class LogAdapter(context: Context) : RecyclerView.Adapter() {
15 |
16 | class LogItem(
17 | val level: String,
18 | val date: String,
19 | val tag: String,
20 | val message: String
21 | )
22 |
23 | companion object {
24 | private val pattern = Pattern.compile("\\[ ?(.*)] (.*) \\((.*)\\) (.*)", Pattern.DOTALL)
25 |
26 | fun parseLog(text: String): LogItem? {
27 | val matcher = pattern.matcher(text)
28 | matcher.find()
29 | val level = matcher.group(1) ?: return null
30 | if (level == "DEBUG" && PrefManager.logFilter_level > 0 ||
31 | level == "INFO" && PrefManager.logFilter_level > 1 ||
32 | level == "WARN" && PrefManager.logFilter_level > 2
33 | ) return null
34 | val date = matcher.group(2) ?: return null
35 | val tag = matcher.group(3) ?: return null
36 | val message = matcher.group(4) ?: return null
37 | return LogItem(level, date, tag, message)
38 | }
39 | }
40 |
41 | private val colorDebug = context.getColor(R.color.debug)
42 | private val colorInfo = context.getColor(R.color.info)
43 | private val colorWarn = context.getColor(R.color.warn)
44 | private val colorError =
45 | context.themeColor(android.R.attr.colorError)
46 |
47 | var logs = listOf()
48 | @SuppressLint("NotifyDataSetChanged")
49 | set(value) {
50 | field = value
51 | notifyDataSetChanged()
52 | }
53 |
54 | inner class ViewHolder(private val binding: LogItemViewBinding) : RecyclerView.ViewHolder(binding.root) {
55 | fun bind(logItem: LogItem) {
56 | val color = when (logItem.level) {
57 | "DEBUG" -> colorDebug
58 | "INFO" -> colorInfo
59 | "WARN" -> colorWarn
60 | "ERROR" -> colorError
61 | else -> throw IllegalArgumentException("Unknown level: ${logItem.level}")
62 | }
63 |
64 | binding.level.setBackgroundColor(color)
65 | binding.level.text = logItem.level.substring(0, 1)
66 | binding.date.text = logItem.date
67 | binding.tag.text = logItem.tag
68 | binding.message.text = logItem.message
69 | }
70 | }
71 |
72 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
73 | val binding = LogItemViewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
74 | return ViewHolder(binding)
75 | }
76 |
77 | override fun getItemCount() = logs.size
78 |
79 | override fun getItemId(position: Int) = logs[position].hashCode().toLong()
80 |
81 | override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(logs[position])
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/app_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
10 |
11 |
12 |
15 |
20 |
26 |
31 |
32 |
33 |
36 |
42 |
49 |
50 |
51 |
54 |
58 |
62 |
66 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
32 |
33 |
34 |
35 |
39 |
40 |
46 |
47 |
53 |
54 |
58 |
59 |
63 |
64 |
68 |
69 |
72 |
73 |
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/ui/activity/AboutActivity.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.ui.activity
2 |
3 | import android.annotation.SuppressLint
4 | import android.widget.ImageView
5 | import android.widget.TextView
6 | import com.drakeet.about.AbsAboutActivity
7 | import com.drakeet.about.Card
8 | import com.drakeet.about.Category
9 | import com.drakeet.about.Contributor
10 | import com.drakeet.about.License
11 | import com.drakeet.about.Line
12 | import org.frknkrc44.hma_oss.BuildConfig
13 | import org.frknkrc44.hma_oss.R
14 |
15 | @Suppress("deprecation")
16 | class AboutActivity : AbsAboutActivity() {
17 | @SuppressLint("SetTextI18n")
18 | override fun onCreateHeader(icon: ImageView, slogan: TextView, version: TextView) {
19 | icon.setImageResource(R.mipmap.ic_launcher)
20 | slogan.text = applicationInfo.loadLabel(packageManager)
21 | version.text = BuildConfig.VERSION_NAME
22 | }
23 |
24 | override fun onItemsCreated(items: MutableList) {
25 | items.add(Category(getString(R.string.title_about_fork)))
26 | items.add(Card(getString(R.string.about_fork_description)))
27 |
28 | items.add(Category(getString(R.string.title_about)))
29 | items.add(Card(getString(R.string.about_description)))
30 |
31 | items.add(Category(getString(R.string.about_how_to_use_title)))
32 | items.add(Card(getString(R.string.about_how_to_use_description_1)))
33 | items.add(Line())
34 | items.add(Card(getString(R.string.about_how_to_use_description_2)))
35 |
36 | items.add(Category(getString(R.string.about_developer)))
37 | items.add(Contributor(R.drawable.cont_author, "\uD835\uDD93\uD835\uDD9A\uD835\uDD91\uD835\uDD91\uD835\uDD95\uD835\uDD99\uD835\uDD97", "Developer", "https://github.com/Dr-TSNG"))
38 | items.add(Line())
39 | items.add(Contributor(R.drawable.cont_fk, "frknkrc44", "HMA-OSS Developer", "https://github.com/frknkrc44"))
40 | items.add(Line())
41 | items.add(Contributor(R.drawable.cont_k, "Ketal", "Collaborator", "https://github.com/keta1"))
42 | items.add(Line())
43 | items.add(Contributor(R.drawable.cont_aviraxp, "aviraxp", "Collaborator", "https://github.com/aviraxp"))
44 | items.add(Line())
45 | items.add(Contributor(R.drawable.cont_icon_designer, "辉少菌", "Icon designer", "http://www.coolapk.com/u/1560270"))
46 | items.add(Line())
47 | items.add(Contributor(R.drawable.cont_cpp_master, "LoveSy", "Idea provider", "https://github.com/yujincheng08"))
48 |
49 | items.add(Category(getString(R.string.about_support)))
50 | items.add(Card("Github\nhttps://github.com/frknkrc44/HMA-OSS"))
51 | items.add(Line())
52 | items.add(Card("Telegram\nhttps://t.me/aerathfuns"))
53 |
54 | items.add(Category(getString(R.string.about_open_source)))
55 | items.add(License("MultiType", "drakeet", License.APACHE_2, "https://github.com/drakeet/MultiType"))
56 | items.add(License("about-page", "drakeet", License.APACHE_2, "https://github.com/drakeet/about-page"))
57 | items.add(License("EzXHelper", "KyuubiRan", License.APACHE_2, "https://github.com/KyuubiRan/EzXHelper"))
58 | items.add(License("libsu", "topjohnwu", License.APACHE_2, "https://github.com/topjohnwu/libsu"))
59 | items.add(License("okhttp", "square", License.APACHE_2, "https://github.com/square/okhttp"))
60 | items.add(License("rxhttp", "liujingxing", License.APACHE_2, "https://github.com/liujingxing/rxhttp"))
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/xposed/src/main/java/icu/nullptr/hidemyapplist/xposed/UserService.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.xposed
2 |
3 | import android.app.ActivityManagerHidden
4 | import android.content.AttributionSource
5 | import android.content.pm.IPackageManager
6 | import android.os.Build
7 | import android.os.Bundle
8 | import android.os.ServiceManager
9 | import icu.nullptr.hidemyapplist.common.Constants
10 | import icu.nullptr.hidemyapplist.common.Utils
11 | import org.frknkrc44.hma_oss.common.BuildConfig
12 | import rikka.hidden.compat.ActivityManagerApis
13 | import rikka.hidden.compat.adapter.UidObserverAdapter
14 |
15 | object UserService {
16 |
17 | private const val TAG = "HMA-UserService"
18 |
19 | private var appUid = 0
20 |
21 | private val uidObserver = object : UidObserverAdapter() {
22 | override fun onUidActive(uid: Int) {
23 | if (HMAService.instance == null) {
24 | logE(TAG, "HMAService instance is not available, maybe stopped")
25 | return
26 | }
27 |
28 | if (uid != appUid) return
29 | try {
30 | val provider = ActivityManagerApis.getContentProviderExternal(Constants.PROVIDER_AUTHORITY, 0, null, null)
31 | if (provider == null) {
32 | logE(TAG, "Failed to get provider")
33 | return
34 | }
35 | val extras = Bundle()
36 | extras.putBinder("binder", HMAService.instance)
37 | val reply = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
38 | val attr = AttributionSource.Builder(1000).setPackageName("android").build()
39 | provider.call(attr, Constants.PROVIDER_AUTHORITY, "", null, extras)
40 | } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
41 | provider.call("android", null, Constants.PROVIDER_AUTHORITY, "", null, extras)
42 | } else {
43 | provider.call("android", Constants.PROVIDER_AUTHORITY, "", null, extras)
44 | }
45 | if (reply == null) {
46 | logE(TAG, "Failed to send binder to app")
47 | return
48 | }
49 | logI(TAG, "Send binder to app")
50 | } catch (e: Throwable) {
51 | logE(TAG, "onUidActive", e)
52 | }
53 | }
54 | }
55 |
56 | fun register(pms: IPackageManager) {
57 | logI(TAG, "Initialize HMAService - Version ${BuildConfig.SERVICE_VERSION}")
58 | val service = HMAService(pms)
59 | appUid = Utils.getPackageUidCompat(service.pms, Constants.APP_PACKAGE_NAME, 0, 0)
60 | val appPackage = Utils.getPackageInfoCompat(service.pms, Constants.APP_PACKAGE_NAME, 0, 0)
61 | if (!Utils.verifyAppSignature(appPackage.applicationInfo?.sourceDir.toString())) {
62 | logE(TAG, "Fatal: App signature mismatch")
63 | return
64 | }
65 | logD(TAG, "Client uid: $appUid")
66 | logI(TAG, "Register observer")
67 |
68 | waitSystemService("activity")
69 | ActivityManagerApis.registerUidObserver(
70 | uidObserver,
71 | ActivityManagerHidden.UID_OBSERVER_ACTIVE,
72 | ActivityManagerHidden.PROCESS_STATE_UNKNOWN,
73 | null
74 | )
75 | }
76 |
77 | private fun waitSystemService(name: String) {
78 | while (ServiceManager.getService(name) == null) {
79 | Thread.sleep(1000)
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/icu/nullptr/hidemyapplist/service/ServiceClient.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.service
2 |
3 | import android.os.IBinder
4 | import android.os.Parcel
5 | import android.os.RemoteException
6 | import android.os.ServiceManager
7 | import android.util.Log
8 | import icu.nullptr.hidemyapplist.common.Constants
9 | import icu.nullptr.hidemyapplist.common.IHMAService
10 | import java.lang.reflect.InvocationHandler
11 | import java.lang.reflect.Method
12 | import java.lang.reflect.Proxy
13 |
14 | object ServiceClient : IHMAService, IBinder.DeathRecipient {
15 |
16 | private const val TAG = "ServiceClient"
17 |
18 | private class ServiceProxy(private val obj: IHMAService) : InvocationHandler {
19 | override fun invoke(proxy: Any?, method: Method, args: Array?): Any? {
20 | val result = method.invoke(obj, *args.orEmpty())
21 | if (result == null) Log.i(TAG, "Call service method ${method.name}")
22 | else Log.i(TAG, "Call service method ${method.name} with result " + result.toString().take(20))
23 | return result
24 | }
25 | }
26 |
27 | @Volatile
28 | private var service: IHMAService? = null
29 |
30 | fun linkService(binder: IBinder) {
31 | service = Proxy.newProxyInstance(
32 | javaClass.classLoader,
33 | arrayOf(IHMAService::class.java),
34 | ServiceProxy(IHMAService.Stub.asInterface(binder))
35 | ) as IHMAService
36 | binder.linkToDeath(this, 0)
37 | }
38 |
39 | private fun getServiceLegacy(): IHMAService? {
40 | if (service != null) return service
41 | val pm = ServiceManager.getService("package")
42 | val data = Parcel.obtain()
43 | val reply = Parcel.obtain()
44 | val remote = try {
45 | data.writeInterfaceToken(Constants.DESCRIPTOR)
46 | data.writeInt(Constants.ACTION_GET_BINDER)
47 | pm.transact(Constants.TRANSACTION, data, reply, 0)
48 | reply.readException()
49 | val binder = reply.readStrongBinder()
50 | IHMAService.Stub.asInterface(binder)
51 | } catch (e: RemoteException) {
52 | Log.d(TAG, "Failed to get binder")
53 | null
54 | } finally {
55 | data.recycle()
56 | reply.recycle()
57 | }
58 | if (remote != null) {
59 | Log.i(TAG, "Binder acquired")
60 | remote.asBinder().linkToDeath(this, 0)
61 | service = Proxy.newProxyInstance(
62 | javaClass.classLoader,
63 | arrayOf(IHMAService::class.java),
64 | ServiceProxy(remote)
65 | ) as IHMAService
66 | }
67 | return service
68 | }
69 |
70 | override fun binderDied() {
71 | service = null
72 | Log.e(TAG, "Binder died")
73 | }
74 |
75 | override fun asBinder() = service?.asBinder()
76 |
77 | override fun getServiceVersion() = getServiceLegacy()?.serviceVersion ?: 0
78 |
79 | override fun getFilterCount() = getServiceLegacy()?.filterCount ?: 0
80 |
81 | override fun getLogs() = getServiceLegacy()?.logs
82 |
83 | override fun clearLogs() {
84 | getServiceLegacy()?.clearLogs()
85 | }
86 |
87 | override fun handlePackageEvent(eventType: String?, packageName: String?) {
88 | getServiceLegacy()?.handlePackageEvent(eventType, packageName)
89 | }
90 |
91 | override fun syncConfig(json: String) {
92 | getServiceLegacy()?.syncConfig(json)
93 | }
94 |
95 | override fun stopService(cleanEnv: Boolean) {
96 | getServiceLegacy()?.stopService(cleanEnv)
97 | }
98 | }
--------------------------------------------------------------------------------
/app/src/main/res/xml/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
17 |
18 |
19 |
20 |
28 |
33 |
34 |
35 |
36 |
41 |
46 |
50 |
58 |
63 |
64 |
65 |
66 |
71 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_template_manage.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
18 |
19 |
20 |
25 |
26 |
34 |
35 |
42 |
43 |
47 |
48 |
54 |
55 |
56 |
57 |
64 |
65 |
71 |
72 |
73 |
74 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/common/src/main/java/icu/nullptr/hidemyapplist/common/AppPresets.kt:
--------------------------------------------------------------------------------
1 | package icu.nullptr.hidemyapplist.common
2 |
3 | import android.content.pm.ApplicationInfo
4 | import android.content.pm.IPackageManager
5 | import icu.nullptr.hidemyapplist.common.Utils.getInstalledApplicationsCompat
6 | import icu.nullptr.hidemyapplist.common.Utils.getPackageInfoCompat
7 | import icu.nullptr.hidemyapplist.common.app_presets.BasePreset
8 | import icu.nullptr.hidemyapplist.common.app_presets.CustomROMPreset
9 | import icu.nullptr.hidemyapplist.common.app_presets.RootAppsPreset
10 | import icu.nullptr.hidemyapplist.common.app_presets.XposedModulesPreset
11 |
12 | // TODO: Update presets when package added/removed
13 | class AppPresets private constructor() {
14 | private val presetList = mutableListOf()
15 | var loggerFunction: ((String) -> Unit)? = null
16 |
17 | companion object {
18 | private var hiddenInstance: AppPresets? = null
19 |
20 | val instance: AppPresets
21 | get() {
22 | if (hiddenInstance == null) {
23 | hiddenInstance = AppPresets()
24 | }
25 |
26 | return hiddenInstance!!
27 | }
28 | }
29 |
30 | fun getAllPresetNames() = presetList.map { it.name }.toTypedArray()
31 | // fun filterPresetsByName(names: Array) = presetList.filter { names.contains(it.name) }
32 | fun getPresetByName(name: String) = presetList.firstOrNull { it.name == name }
33 |
34 | fun reloadPresetsIfEmpty(pms: IPackageManager) {
35 | var appsList: List? = null
36 |
37 | presetList.forEach {
38 | if (it.isDynamicListEmpty()) {
39 | if (appsList == null) {
40 | appsList = getInstalledApplicationsCompat(pms, 0, 0)
41 | }
42 |
43 | for (appInfo in appsList) {
44 | runCatching {
45 | it.addPackageInfoPreset(appInfo)
46 | }.onFailure { fail ->
47 | loggerFunction?.invoke(fail.toString())
48 | }
49 | }
50 | }
51 |
52 | loggerFunction?.invoke(it.toString())
53 | }
54 | }
55 |
56 | fun handlePackageAdded(pms: IPackageManager, packageName: String) {
57 | var appInfo: ApplicationInfo? = null
58 | var addedInAList = false
59 |
60 | presetList.forEach {
61 | if (!it.containsPackage(packageName)) {
62 | if (appInfo == null)
63 | appInfo = getPackageInfoCompat(pms, packageName, 0, 0).applicationInfo
64 |
65 | if (appInfo != null) {
66 | runCatching {
67 | if (it.addPackageInfoPreset(appInfo!!)) {
68 | loggerFunction?.invoke("Package $packageName added into ${it.name}!")
69 | addedInAList = true
70 | }
71 | }.onFailure { fail ->
72 | loggerFunction?.invoke(fail.toString())
73 | }
74 | }
75 | }
76 | }
77 |
78 | if (addedInAList)
79 | loggerFunction?.invoke("Package add event handled for $packageName!")
80 | }
81 |
82 | fun handlePackageRemoved(packageName: String) {
83 | var itWasInAList = false
84 |
85 | presetList.forEach {
86 | if (it.removePackageFromPreset(packageName))
87 | itWasInAList = true
88 | }
89 |
90 | if (itWasInAList)
91 | loggerFunction?.invoke("Package remove event handled for $packageName!")
92 | }
93 |
94 | init {
95 | presetList.add(CustomROMPreset())
96 | presetList.add(RootAppsPreset())
97 | presetList.add(XposedModulesPreset())
98 | }
99 | }
100 |
101 |
102 |
--------------------------------------------------------------------------------