├── .github
└── workflows
│ └── android.yml
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── gradle.properties
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── gswxxn
│ │ └── restoresplashscreen
│ │ ├── data
│ │ ├── ConstValue.kt
│ │ ├── DataConst.kt
│ │ ├── RoundDegree.kt
│ │ └── StartingWindowInfo.kt
│ │ ├── hook
│ │ ├── AndroidHooker.kt
│ │ ├── HookEntry.kt
│ │ ├── NewSystemUIHooker.kt
│ │ ├── SystemUIHooker.kt
│ │ ├── base
│ │ │ ├── BaseHookHandler.kt
│ │ │ └── HookManager.kt
│ │ └── systemui
│ │ │ ├── BgHookHandler.kt
│ │ │ ├── BottomHookHandler.kt
│ │ │ ├── ColorOSHookHandler.kt
│ │ │ ├── GenerateHookHandler.kt
│ │ │ ├── IconHookHandler.kt
│ │ │ ├── MIUIHookHandler.kt
│ │ │ └── ScopeHookHandler.kt
│ │ ├── ui
│ │ ├── AboutPageActivity.kt
│ │ ├── BaseActivity.kt
│ │ ├── ColorSelectActivity.kt
│ │ ├── ConfigAppsActivity.kt
│ │ ├── MainSettingsActivity.kt
│ │ ├── SubSettings.kt
│ │ ├── configapps
│ │ │ ├── BGColorIndividualConfig.kt
│ │ │ ├── BackgroundExcept.kt
│ │ │ ├── BrandingImage.kt
│ │ │ ├── CustomScope.kt
│ │ │ ├── DefaultStyle.kt
│ │ │ ├── ForceShowSplashScreen.kt
│ │ │ ├── HideSplashScreenIcon.kt
│ │ │ └── MinDuration.kt
│ │ ├── interface
│ │ │ ├── IConfigApps.kt
│ │ │ └── ISubSettings.kt
│ │ └── subsettings
│ │ │ ├── BackgroundSettings.kt
│ │ │ ├── BasicSettings.kt
│ │ │ ├── BottomSettings.kt
│ │ │ ├── CustomScopeSettings.kt
│ │ │ ├── DevSettings.kt
│ │ │ ├── DisplaySettings.kt
│ │ │ ├── HookInfo.kt
│ │ │ └── IconSettings.kt
│ │ ├── utils
│ │ ├── AppInfoHelper.kt
│ │ ├── BackupUtils.kt
│ │ ├── BlockMIUIHelper.kt
│ │ ├── CommonUtils.kt
│ │ ├── GraphicUtils.kt
│ │ ├── IconPackManager.kt
│ │ ├── MIUIIconsHelper.kt
│ │ └── YukiHelper.kt
│ │ ├── view
│ │ ├── BlockMIUIItemData.kt
│ │ ├── NewMIUIDialog.kt
│ │ ├── SeekBarWithTitleView.kt
│ │ ├── SwitchView.kt
│ │ └── TextSummaryWithSwitchView.kt
│ │ └── wrapper
│ │ ├── SplashScreenViewBuilderWrapper.kt
│ │ └── TransparentAdaptiveIconDrawable.kt
│ └── res
│ ├── animator
│ ├── dialog_enter.xml
│ └── dialog_exit.xml
│ ├── drawable
│ ├── bg_button_round.xml
│ ├── bg_dark_round.xml
│ ├── bg_demo_round.xml
│ ├── bg_green_round.xml
│ ├── bg_permotion_round.xml
│ ├── bg_yellow_round.xml
│ ├── demo_background.xml
│ ├── demo_basic.xml
│ ├── demo_branding.xml
│ ├── demo_display.xml
│ ├── demo_icon.xml
│ ├── demo_scope.xml
│ ├── demo_transparency.xml
│ ├── ic_app.xml
│ ├── ic_bottom.xml
│ ├── ic_brand_image_gswxxn.xml
│ ├── ic_collimation.xml
│ ├── ic_color.xml
│ ├── ic_help.xml
│ ├── ic_info.xml
│ ├── ic_lab.xml
│ ├── ic_launcher_foreground.xml
│ ├── ic_monitor.xml
│ ├── ic_more.xml
│ ├── ic_picture.xml
│ ├── ic_restart.xml
│ ├── ic_search.xml
│ ├── ic_setting.xml
│ ├── ic_success.xml
│ ├── ic_warn.xml
│ ├── img_github.xml
│ ├── img_iconfont.xml
│ ├── splash_icon.xml
│ └── thumb_seek.xml
│ ├── layout
│ ├── activity_about_page.xml
│ ├── activity_color_select.xml
│ ├── activity_config_apps.xml
│ ├── activity_main_settings.xml
│ ├── activity_sub_settings.xml
│ └── adapter_config.xml
│ ├── menu
│ └── more_options_menu.xml
│ ├── mipmap-anydpi-v26
│ └── ic_launcher.xml
│ ├── mipmap-xxhdpi
│ └── img_developer.jpg
│ ├── values-night
│ └── color.xml
│ └── values
│ ├── app_splash.xml
│ ├── arrays.xml
│ ├── color.xml
│ ├── colors.xml
│ ├── ic_launcher_background.xml
│ └── strings.xml
├── build.gradle.kts
├── doc
├── Privacy.md
├── donate.png
└── icon.svg
├── gradle.properties
├── gradle
├── sweet-dependency
│ └── sweet-dependency-config.yaml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches: ["master"]
7 | tags:
8 | - 'v*'
9 | paths-ignore:
10 | - 'README.md'
11 | - 'doc/*'
12 | - 'app/gradle.properties'
13 | - '.github/workflows/android.yml'
14 |
15 | jobs:
16 | build:
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - name: Checkout Repository
21 | uses: actions/checkout@v4
22 | with:
23 | submodules: true
24 |
25 | - name: Set up JDK 20
26 | uses: actions/setup-java@v4
27 | with:
28 | java-version: "20"
29 | distribution: "temurin"
30 |
31 | - name: Build with Gradle and Sign
32 | run: |
33 | echo ${{ secrets.SIGNING_KEY }} | base64 -d > keystore.jks
34 | if [[ ${{ github.ref }} == refs/tags/v* ]]; then
35 | bash ./gradlew :app:assembleAppRelease
36 | bash ./gradlew :app:assembleAppRelease
37 | echo "APK_FILE=$(find app/build/outputs/apk/app/release/ -name '*.apk' | head -n 1)" >> $GITHUB_ENV
38 | echo "Building Type: Release Version" >> $GITHUB_STEP_SUMMARY
39 | else
40 | bash ./gradlew :app:assembleCIRelease
41 | bash ./gradlew :app:assembleCIRelease
42 | echo "APK_FILE=$(find app/build/outputs/apk/CI/release/ -name '*.apk' | head -n 1)" >> $GITHUB_ENV
43 | echo "Building Type: CI Build Version" >> $GITHUB_STEP_SUMMARY
44 | fi
45 | env:
46 | KEYSTORE_PATH: "../keystore.jks"
47 | KEYSTORE_PASS: ${{ secrets.KEY_STORE_PASSWORD }}
48 | KEY_ALIAS: ${{ secrets.ALIAS }}
49 | KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
50 |
51 | - name: Upload CI Artifact
52 | if: ${{ !startsWith(github.ref, 'refs/tags/') }}
53 | uses: actions/upload-artifact@v4
54 | with:
55 | name: RestoreSplashScreen_CI_Release
56 | path: ${{ env.APK_FILE }}
57 |
58 | - name: Post Artifacts to Telegram
59 | if: ${{ !startsWith(github.ref, 'refs/tags/') }}
60 | env:
61 | TG_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
62 | TG_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
63 | COMMIT_MESSAGE: |+
64 | New push to GitHub\!
65 | ```
66 | ${{ github.event.head_commit.message }}
67 | ```by `${{ github.event.head_commit.author.name }}`
68 |
69 | See commit detail [here](${{ github.event.head_commit.url }})
70 | See build detail [here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
71 |
72 | \#启动遮罩进化
73 | COMMIT_URL: ${{ github.event.head_commit.url }}
74 | run: |
75 | ESCAPED=`python3 -c 'import json,os,urllib.parse; msg = json.dumps(os.environ["COMMIT_MESSAGE"]); print(urllib.parse.quote(msg if len(msg) <= 1024 else json.dumps(os.environ["COMMIT_URL"])))'`
76 | curl -v "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMediaGroup?chat_id=${TG_CHAT_ID}&media=%5B%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Fci%22%2C%22parse_mode%22%3A%22MarkdownV2%22%2C%22caption%22%3A${ESCAPED}%7D%5D" \
77 | -F ci="@${{ env.APK_FILE }}"
78 |
79 | - name: Get Release Tag Name
80 | if: ${{ startsWith(github.ref, 'refs/tags/v') }}
81 | run: |
82 | echo "RELEASE_TAG_NAME=$(bash ./gradlew -q getVersionCode 2>&1 | tail -n 1)" >> "$GITHUB_ENV"
83 |
84 | - name: Create LSPosed Repository Release
85 | if: ${{ startsWith(github.ref, 'refs/tags/v') }}
86 | uses: softprops/action-gh-release@v1
87 | with:
88 | prerelease: true
89 | name: ${{ github.ref_name }}
90 | tag_name: ${{ env.RELEASE_TAG_NAME }}
91 | body: "This is a pre-release version automatically created by GitHub Actions, awaiting developer's addition of the changelog."
92 | repository: Xposed-Modules-Repo/com.gswxxn.restoresplashscreen
93 | token: ${{ secrets.GH_TOKEN }}
94 | files: ${{ env.APK_FILE }}
95 |
96 | - name: Display APK Hashes
97 | run: |
98 | apk=${{ env.APK_FILE }}
99 | echo "### APK Hashes" >> $GITHUB_STEP_SUMMARY
100 | echo "| Hash Type | Hash Value |" >> $GITHUB_STEP_SUMMARY
101 | echo "| --- | --- |" >> $GITHUB_STEP_SUMMARY
102 | echo "| SHA256 | \`$(sha256sum $apk | awk '{ print $1 }')\` |" >> $GITHUB_STEP_SUMMARY
103 | echo "| SHA1 | \`$(sha1sum $apk | awk '{ print $1 }')\` |" >> $GITHUB_STEP_SUMMARY
104 | echo "| MD5 | \`$(md5sum $apk | awk '{ print $1 }')\` |" >> $GITHUB_STEP_SUMMARY
105 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 | .idea/*
17 | /app/src/main/assets/yukihookapi_init
18 | /app/src/main/assets/xposed_init
19 | /.kotlin
20 | app/src/main/resources/META-INF/yukihookapi_init
21 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "blockmiui"]
2 | path = blockmiui
3 | url = https://github.com/Block-Network/blockmiui.git
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # 启动遮罩进化
4 |
5 | [](https://app.codacy.com/gh/GSWXXN/RestoreSplashScreen?utm_source=github.com&utm_medium=referral&utm_content=GSWXXN/RestoreSplashScreen&utm_campaign=Badge_Grade)
6 | [](https://github.com/Xposed-Modules-Repo/com.gswxxn.restoresplashscreen/)
7 | [](https://github.com/GSWXXN/RestoreSplashScreen/blob/master/LICENSE)
8 | [](https://github.com/Xposed-Modules-Repo/com.gswxxn.restoresplashscreen/releases)
9 | [](https://github.com/Xposed-Modules-Repo/com.gswxxn.restoresplashscreen/releases)
10 | [](https://t.me/GSWXXN_Channel)
11 | [](https://t.me/GSWXXN_Chat)
12 |
13 | ~~尝试恢复被MIUI阉割的SplashScreen~~
14 | ~~自定义MIUI的Splash Screen~~
15 | 为 Splash Screen 添加自定义选项
16 |
17 | 模块适配大部分安卓系统, 但目前还是以运行在高通 SoC 的 MIUI/HyperOS 为主, 如果在其他系统中使用遇到问题也欢迎反馈
18 |
19 | ## 测试环境
20 |
21 | > 小米12 Ultra
22 | > Android 14
23 | > HyperOS 1.0
24 |
25 | ## 模块功能
26 |
27 | 1. 为所有应用显示原生 Splash Screen 界面
28 | 2. 对于主动适配 Splash Screen 的应用使用默认静态图标
29 | 3. 替换获取图标方式, 使 Splash Screen 的图标与桌面图标一致(多用于主题)
30 | 4. 根据图标自适应 Splash Screen 背景颜色
31 | 5. 彻底关闭 Splash Screen 特性
32 |
33 | ## 使用方法
34 |
35 | 1. 在 Xposed 管理器 (LSPosed) 中激活模块
36 | 2. 作用域勾选: 系统界面(`com.android.systemui`) 和 系统框架(`android`)
37 | 3. 重启手机
38 |
39 | ## 已知问题
40 |
41 | - ~~微信、QQ、支付宝的Splash Screen只能在MIUI内测22.4.25后显示~~ (v1.9 已支持)
42 |
43 | - 开启模块后出现启动应用卡顿可能与调度模块同时开启有关,这个我也在想办法优化 (v2.0 优化过,反馈似乎还不错)
44 |
45 | - ~~部分机型 (如Redmi Note 10 Pro、小米12Pro) 的系统版本 (如稳定版、开发版) 可能无法使用本模块,
46 | 请等待系统更新~~ (v1.9 已支持)
47 |
48 | ## 常见问题解答
49 |
50 | [点击跳转](https://gswxxn.coding.net/public/restoresplashscreen/faq/git)
51 |
52 | ## 无法使用
53 |
54 | 请先检查模块是否正常激活,并且作用域是否勾选。如果排查后仍有错误,请提交 issue,并附上 LSPosed 的日志,如有能力提取
55 | SystemUI, 最好一并提交。
56 | 也可以联系酷安 [@迷璐](http://www.coolapk.com/u/1189245)
57 |
58 | ## 捐赠支持
59 |
60 | 点个 **Star** 也是对我的支持。
61 |
62 | 如果你想捐赠,觉得这个模块好用的不得了,我会非常感谢你的!!!如果这个模块对你来说只要还差一点点意思,就不要捐赠啦
63 |
64 |
69 |
70 | ## 致谢
71 |
72 | 使用 [Yuki Hook API](https://github.com/fankes/YukiHookAPI) 构建模块
73 | UI界面改自 [MIUI 原生通知图标](https://github.com/fankes/MIUINativeNotifyIcon)
74 | 使用 [BlockMIUI](https://github.com/Block-Network/blockmiui) 的部分资源构建UI
75 | 获取应用列表方式参考 [Hide My Applist](https://github.com/Dr-TSNG/Hide-My-Applist)
76 | 曾使用 [libsu](https://github.com/topjohnwu/libsu) 执行Shell命令
77 | 参考 [MIUIHomeR](https://github.com/qqlittleice/MiuiHome_R) 优化部分代码
78 | 使用 [Sweet Dependency](https://github.com/HighCapable/SweetDependency) 自动装配和管理依赖
79 | 使用 [Sweet Property](https://github.com/HighCapable/SweetProperty) 管理项目属性
80 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @file:Suppress("UnstableApiUsage")
2 |
3 | plugins {
4 | autowire(libs.plugins.com.android.application)
5 | autowire(libs.plugins.org.jetbrains.kotlin.android)
6 | autowire(libs.plugins.org.jetbrains.kotlin.plugin.serialization)
7 | autowire(libs.plugins.com.google.devtools.ksp)
8 | }
9 |
10 | android {
11 | namespace = property.project.namespace
12 | compileSdk = property.project.compileSdk
13 |
14 | defaultConfig {
15 | applicationId = property.project.applicationId
16 | minSdk = property.project.minSdk
17 | targetSdk = property.project.targetSdk
18 | versionCode = property.project.versionCode
19 | versionName = property.project.versionName
20 | }
21 |
22 | packaging.resources {
23 | excludes += "**"
24 | merges += "META-INF/yukihookapi_init"
25 | }
26 |
27 | dependenciesInfo {
28 | includeInApk = false
29 | includeInBundle = false
30 | }
31 |
32 | val isKeyStoreAvailable = try {
33 | property.keystore.path.isNotBlank() && property.keystore.pass.isNotBlank() && property.key.alias.isNotBlank() && property.key.password.isNotBlank()
34 | } catch (_: Exception) {
35 | false
36 | }
37 | if (isKeyStoreAvailable) {
38 | signingConfigs {
39 | create("universal") {
40 | storeFile = file(property.keystore.path)
41 | storePassword = property.keystore.pass
42 | keyAlias = property.key.alias
43 | keyPassword = property.key.password
44 | enableV1Signing = true
45 | enableV2Signing = true
46 | enableV3Signing = true
47 | }
48 | }
49 | }
50 |
51 | buildTypes {
52 | all { if (isKeyStoreAvailable) signingConfig = signingConfigs.getByName("universal") }
53 | getByName("release") {
54 | isMinifyEnabled = true
55 | isShrinkResources = true
56 | vcsInfo.include = false
57 | proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
58 | }
59 | }
60 |
61 | flavorDimensionList.add("tier")
62 | productFlavors {
63 | create("CI") {
64 | dimension = "tier"
65 | versionCode = defaultConfig.versionCode?.plus(1)
66 | versionName = "${defaultConfig.versionName?.split(" - ")?.get(0)}-CI.${getGitHeadRefsSuffix(rootProject)}"
67 | }
68 | create("app") {
69 | dimension = "tier"
70 | }
71 | }
72 |
73 | buildFeatures {
74 | buildConfig = true
75 | viewBinding = true
76 | }
77 |
78 | applicationVariants.all {
79 | val buildType = buildType.name
80 | outputs.all {
81 | if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
82 | this.outputFileName = "RestoreSplashScreen_${versionName}${if (buildType == "debug") "_debug" else ""}.apk"
83 | }
84 | }
85 | }
86 |
87 | compileOptions {
88 | sourceCompatibility = JavaVersion.VERSION_17
89 | targetCompatibility = JavaVersion.VERSION_17
90 | }
91 |
92 | kotlinOptions {
93 | jvmTarget = JavaVersion.VERSION_17.majorVersion
94 | }
95 | }
96 |
97 | dependencies {
98 | implementation(projects.blockmiui)
99 | compileOnly(de.robv.android.xposed.api)
100 | implementation(com.highcapable.yukihookapi.api)
101 | ksp(com.highcapable.yukihookapi.ksp.xposed)
102 | implementation(androidx.palette.palette.ktx)
103 | implementation(androidx.compose.material3.material3)
104 | implementation(org.jetbrains.kotlinx.kotlinx.coroutines.android)
105 | implementation(org.jetbrains.kotlinx.kotlinx.serialization.json)
106 | }
107 |
108 | tasks.register("getVersionCode") {
109 | println("${property.project.versionCode}-${property.project.versionName}")
110 | }
111 |
112 | /**
113 | * from [MiuiHomeR](https://github.com/qqlittleice/MiuiHome_R/blob/main/app/build.gradle.kts)
114 | * 用于获取 git commit id
115 | */
116 | fun getGitHeadRefsSuffix(project: Project): String {
117 | // .git/HEAD描述当前目录所指向的分支信息,内容示例:"ref: refs/heads/master\n"
118 | val headFile = File(project.rootProject.projectDir, ".git" + File.separator + "HEAD")
119 | if (headFile.exists()) {
120 | val string: String = headFile.readText(Charsets.UTF_8)
121 | val string1 = string.replace(Regex("""ref:|\s"""), "")
122 | val result = if (string1.isNotBlank() && string1.contains('/')) {
123 | val refFilePath = ".git" + File.separator + string1
124 | // 根据HEAD读取当前指向的hash值,路径示例为:".git/refs/heads/master"
125 | val refFile = File(project.rootProject.projectDir, refFilePath)
126 | // 索引文件内容为hash值+"\n",
127 | // 示例:"90312cd9157587d11779ed7be776e3220050b308\n"
128 | refFile.readText(Charsets.UTF_8).replace(Regex("""\s"""), "").subSequence(0, 7)
129 | } else {
130 | string.substring(0, 7)
131 | }
132 | println("commit_id: $result")
133 | return result.toString()
134 | } else {
135 | println("WARN: .git/HEAD does NOT exist")
136 | return ""
137 | }
138 | }
--------------------------------------------------------------------------------
/app/gradle.properties:
--------------------------------------------------------------------------------
1 | project.applicationId = com.gswxxn.restoresplashscreen
2 | project.namespace = ${project.applicationId}
3 |
4 | project.compileSdk=${project.targetSdk}
5 | project.targetSdk=35
6 | project.minSdk=31
7 |
8 | project.versionCode = 3100
9 | project.versionName = "3.1"
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 | -keepclassmembers class * implements androidx.viewbinding.ViewBinding {
23 | *** inflate(android.view.LayoutInflater);
24 | }
25 | -keep class kotlin.Unit
26 | -keep class com.gswxxn.restoresplashscreen.ui.BaseActivity
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
12 |
13 |
16 |
17 |
22 |
23 |
26 |
29 |
33 |
37 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
63 |
66 |
69 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/data/ConstValue.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.data
2 |
3 | /** Constants used in the app. */
4 | object ConstValue {
5 | const val EXTRA_MESSAGE = "com.gswxxn.MainActivity.MESSAGE"
6 | const val EXTRA_MESSAGE_OVERALL_BG_COLOR = "OVERALL BG COLOR"
7 | const val EXTRA_MESSAGE_PACKAGE_NAME = "PACKAGE NAME"
8 | const val EXTRA_MESSAGE_APP_INDEX = "APP INDEX"
9 | const val EXTRA_MESSAGE_CURRENT_COLOR = "CURRENT COLOR"
10 | const val EXTRA_MESSAGE_SELECTED_COLOR = "SELECTED COLOR"
11 |
12 | const val CREATE_DOCUMENT_CODE = 0
13 | const val OPEN_DOCUMENT_CODE = 1
14 | const val SELECT_COLOR_CODE = 3
15 |
16 | const val DEFAULT_COLOR = 0
17 | const val UNDO_MODIFY = 1
18 | const val SELECTED_COLOR = 2
19 |
20 | const val CUSTOM_SCOPE = 100
21 | const val DEFAULT_STYLE = 200
22 | const val BACKGROUND_EXCEPT = 300
23 | const val BACKGROUND_INDIVIDUALLY_CONFIG = 310
24 | const val BRANDING_IMAGE = 400
25 | const val FORCE_SHOW_SPLASH_SCREEN = 500
26 | const val MIN_DURATION = 600
27 | const val HIDE_SPLASH_SCREEN_ICON = 700
28 |
29 | const val BASIC_SETTINGS = 1100
30 | const val CUSTOM_SCOPE_SETTINGS = 1200
31 | const val ICON_SETTINGS = 1300
32 | const val BOTTOM_SETTINGS = 1400
33 | const val BACKGROUND_SETTINGS = 1500
34 | const val DISPLAY_SETTINGS = 1600
35 | const val HOOK_INFO = 1700
36 | const val DEV_SETTINGS = 1800
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/data/DataConst.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.data
2 |
3 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
4 |
5 | /**
6 | * 模板类定义模块与宿主需要使用的键值数据
7 | *
8 | * [YukiHookAPI](https://fankes.github.io/YukiHookAPI/zh-cn/api/public/com/highcapable/yukihookapi/hook/xposed/prefs/data/PrefsData.html)
9 | */
10 | object DataConst {
11 | val ENABLE_NEW_SYSTEM_UI_HOOKER = PrefsData("enable_new_system_ui_hooker", true)
12 | val ENABLE_LOG = PrefsData("enable_log", false)
13 | val ENABLE_LOG_TIMESTAMP = PrefsData("enable_log_timestamp", 0L)
14 | val ENABLE_CUSTOM_SCOPE = PrefsData("enable_custom_scope", false)
15 | val IS_CUSTOM_SCOPE_EXCEPTION_MODE = PrefsData("is_custom_scope_exception_mode", true)
16 | val IS_REMOVE_BRANDING_IMAGE_EXCEPTION_MODE = PrefsData("is_remove_branding_image_exception_mode", false)
17 | val IS_DEFAULT_STYLE_LIST_EXCEPTION_MODE = PrefsData("is_default_style_list_exception_mode", false)
18 | val IS_HIDE_SPLASH_SCREEN_ICON_EXCEPTION_MODE = PrefsData("is_hide_splash_screen_icon_exception_mode", false)
19 | val REPLACE_TO_EMPTY_SPLASH_SCREEN = PrefsData("replace_to_empty_splash_screen", false)
20 | val ENABLE_DEFAULT_STYLE = PrefsData("enable_default_style", false) // 忽略应用主动设置的图标
21 | val ENABLE_HIDE_SPLASH_SCREEN_ICON = PrefsData("enable_hide_splash_screen_icon", false)
22 | val ENABLE_HIDE_ICON = PrefsData("enable_hide_icon", false)
23 | val ENABLE_REPLACE_ICON = PrefsData("enable_replace_icon", false)
24 | val ENABLE_USE_MIUI_LARGE_ICON = PrefsData("enable_use_miui_large_icon", false)
25 | val ENABLE_ADD_ICON_BLUR_BG = PrefsData("enable_add_blur_bg", false)
26 | val ICON_PACK_PACKAGE_NAME = PrefsData("icon_pack_package_name", "None")
27 | val OVERALL_BG_COLOR = PrefsData("overall_bg_color", "#FFFFFF")
28 | val OVERALL_BG_COLOR_NIGHT = PrefsData("overall_bg_color_night", "#000000")
29 | val IGNORE_DARK_MODE = PrefsData("ignore_dark_mode", false)
30 | val REMOVE_BG_DRAWABLE = PrefsData("remove_bg_drawable", false)
31 | val SKIP_APP_WITH_BG_COLOR = PrefsData("skip_app_with_bg_color", true)
32 | val REMOVE_BRANDING_IMAGE = PrefsData("remove_branding_image", false)
33 | val REDUCE_SPLASH_SCREEN = PrefsData("reduce_splash_screen", true)
34 | val FORCE_ENABLE_SPLASH_SCREEN = PrefsData("force_enable_splash_screen", false)
35 | val FORCE_SHOW_SPLASH_SCREEN = PrefsData("force_show_splash_screen", false)
36 | val DISABLE_SPLASH_SCREEN = PrefsData("disable_splash_screen", false)
37 | val ENABLE_HOT_START_COMPATIBLE = PrefsData("enable_hot_start_compatible", false)
38 | val ENABLE_DRAW_ROUND_CORNER = PrefsData("draw_round_corner", false)
39 | val SHRINK_ICON = PrefsData("shrink_icon", 0)
40 | val BG_COLOR_MODE = PrefsData("color_mode", 0)
41 | val CHANG_BG_COLOR_TYPE = PrefsData("change_bg_color_type", 0)
42 | val MIN_DURATION = PrefsData("min_duration", 0)
43 |
44 | val UNDEFINED_LIST = PrefsData("undefined_list", mutableSetOf())
45 | val CUSTOM_SCOPE_LIST = PrefsData("custom_scope_list", mutableSetOf())
46 | val DEFAULT_STYLE_LIST = PrefsData("default_style_list", mutableSetOf()) // 忽略应用主动设置的图标 应用列表
47 | val HIDE_SPLASH_SCREEN_ICON_LIST = PrefsData("hide_splash_screen_icon_list", mutableSetOf())
48 | val BG_EXCEPT_LIST =PrefsData("bg_except_list", mutableSetOf()) //自适应背景颜色排除列表
49 | val REMOVE_BRANDING_IMAGE_LIST = PrefsData("remove_branding_image_list", mutableSetOf())
50 | val FORCE_SHOW_SPLASH_SCREEN_LIST = PrefsData("force_show_splash_screen_list", mutableSetOf())
51 | val MIN_DURATION_LIST = PrefsData("min_duration_list", mutableSetOf())
52 | val MIN_DURATION_CONFIG_MAP = PrefsData("min_duration_config_map", mutableSetOf())
53 | val INDIVIDUAL_BG_COLOR_APP_MAP = PrefsData("individual_bg_color_app_map", mutableSetOf())
54 | val INDIVIDUAL_BG_COLOR_APP_MAP_DARK = PrefsData("individual_bg_color_app_map_dark", mutableSetOf())
55 |
56 | // 开发者设置
57 | val ENABLE_DEV_SETTINGS = PrefsData("enable_dev_settings", false)
58 | val DEV_ICON_ROUND_CORNER_RATE = PrefsData("dev_icon_round_corner", 25)
59 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/data/RoundDegree.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.data
2 |
3 | /**
4 | * 圆角程度
5 | *
6 | * [NotDrawRoundCorner] 不绘制圆角
7 | * [RoundCorner] 绘制圆角图标
8 | * [MIUIWidget] 绘制小部件圆角
9 | * [Circle] 绘制圆形图标
10 | */
11 | enum class RoundDegree {
12 | NotDrawRoundCorner,
13 | RoundCorner,
14 | MIUIWidget,
15 | Circle
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/data/StartingWindowInfo.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.data
2 |
3 | /**
4 | * 该对象定义了在应用程序中可用的不同类型启动窗口的常量
5 | */
6 | object StartingWindowInfo {
7 | /**
8 | * Prefer nothing or not care the type of starting window.
9 | */
10 | const val STARTING_WINDOW_TYPE_NONE = 0
11 |
12 | /**
13 | * Prefer splash screen starting window.
14 | */
15 | const val STARTING_WINDOW_TYPE_SPLASH_SCREEN = 1
16 |
17 | /**
18 | * Prefer snapshot starting window.
19 | */
20 | const val STARTING_WINDOW_TYPE_SNAPSHOT = 2
21 |
22 | /**
23 | * Prefer solid color splash screen starting window.
24 | */
25 | const val STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN = 3
26 |
27 | /**
28 | * Represents a preference for using a legacy splash screen as the starting window.
29 | */
30 | const val STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN = 4
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/hook/AndroidHooker.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.hook
2 |
3 | import com.gswxxn.restoresplashscreen.data.DataConst
4 | import com.gswxxn.restoresplashscreen.utils.CommonUtils.isAtLeastT
5 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.getField
6 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.printLog
7 | import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
8 | import com.highcapable.yukihookapi.hook.factory.current
9 | import com.highcapable.yukihookapi.hook.factory.method
10 |
11 | /**
12 | * Android 系统相关 Hook
13 | */
14 | object AndroidHooker : YukiBaseHooker() {
15 | override fun onHook() {
16 |
17 | val activityRecordClass = "com.android.server.wm.ActivityRecord".toClass()
18 |
19 | /**
20 | * 强制显示遮罩
21 | *
22 | * 类原始位置在 services.jar 中
23 | *
24 | * 此处在 evaluateStartingWindowTheme() 中被调用,最终将参数传递给 showStartingWindow()
25 | */
26 | activityRecordClass.method {
27 | name = "validateStartingWindowTheme"
28 | paramCount(3)
29 | }.hook {
30 | before {
31 | val pkgName = args(1).string()
32 | val isLaunchedFromSystemSurface = instance.current().method {
33 | name = "launchedFromSystemSurface"
34 | emptyParam()
35 | }.boolean()
36 | val isForceShowSS = prefs.get(DataConst.FORCE_SHOW_SPLASH_SCREEN)
37 | && pkgName in prefs.get(DataConst.FORCE_SHOW_SPLASH_SCREEN_LIST)
38 | && (!prefs.get(DataConst.REDUCE_SPLASH_SCREEN) || isLaunchedFromSystemSurface)
39 |
40 | if (isForceShowSS) resultTrue()
41 | printLog("[Android] validateStartingWindowTheme():" +
42 | "${if (isForceShowSS) "" else "Not"} force show $pkgName splash screen, " +
43 | "isLaunchedFromSystemSurface: $isLaunchedFromSystemSurface")
44 | }
45 | }
46 |
47 | // 彻底关闭 Splash Screen
48 | activityRecordClass.method {
49 | name = "showStartingWindow"
50 | paramCount(if (isAtLeastT) 7 else 5)
51 | }.hook {
52 | before {
53 | val currentPkgName = instance.getField("packageName")
54 |
55 | val isDisableSS = prefs.get(DataConst.DISABLE_SPLASH_SCREEN)
56 | printLog("[Android] addStartingWindow():${if (isDisableSS) "" else "Not"} disable $currentPkgName splash screen")
57 | if (isDisableSS) {
58 | resultNull()
59 | }
60 | }
61 | }
62 |
63 | // 热启动时生成启动遮罩
64 | activityRecordClass.method {
65 | name = "getStartingWindowType"
66 | paramCount(if (isAtLeastT) 7 else 6)
67 | }.hook {
68 | before {
69 | val isHotStartCompatible = prefs.get(DataConst.ENABLE_HOT_START_COMPATIBLE) && args(1).boolean()
70 | if (isHotStartCompatible) result = 2
71 | printLog("[Android] getStartingWindowType():${if (isHotStartCompatible) "" else "Not"} set result to 2")
72 | }
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/hook/HookEntry.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.hook
2 |
3 | import com.gswxxn.restoresplashscreen.data.DataConst
4 | import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
5 | import com.highcapable.yukihookapi.hook.factory.configs
6 | import com.highcapable.yukihookapi.hook.factory.encase
7 | import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit
8 |
9 | /** Hook 入口类 */
10 | @InjectYukiHookWithXposed(isUsingResourcesHook = false)
11 | object HookEntry : IYukiHookXposedInit {
12 | override fun onInit() = configs {
13 | debugLog { tag = "RestoreSplashScreen" }
14 | isDebug = false
15 | }
16 |
17 | override fun onHook() = encase {
18 | if (prefs.get(DataConst.ENABLE_NEW_SYSTEM_UI_HOOKER))
19 | loadApp("com.android.systemui", NewSystemUIHooker)
20 | else
21 | loadApp("com.android.systemui", SystemUIHooker)
22 | loadSystem(AndroidHooker)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/hook/base/BaseHookHandler.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.hook.base
2 |
3 | import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
4 |
5 | /**
6 | * Hook处理程序的抽象基类。
7 | */
8 | abstract class BaseHookHandler {
9 | lateinit var baseHooker: YukiBaseHooker
10 |
11 | val appContext get() = baseHooker.appContext
12 | val appResources get() = baseHooker.appResources
13 | val prefs get() = baseHooker.prefs
14 |
15 | /**
16 | * 在进行Hook时调用的方法。
17 | */
18 | abstract fun onHook()
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/hook/base/HookManager.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.hook.base
2 |
3 | import com.gswxxn.restoresplashscreen.hook.AndroidHooker.hook
4 | import com.highcapable.yukihookapi.hook.log.YLog
5 | import com.highcapable.yukihookapi.hook.param.HookParam
6 | import kotlinx.serialization.Serializable
7 | import java.lang.reflect.Member
8 | import java.lang.reflect.Method
9 |
10 | /**
11 | * 用于进行Hook操作的HookManager类。
12 | * 由于底层框架不支持重复 Hook, 所以在这里存储 Member 和对应的 BeforeHooks 和 AfterHooks, 等到加载完所有的 Hooker 后, 一并执行 Hook
13 | *
14 | * @param createCondition 创建 HookManager 实例的条件, 如对系统判断。
15 | * @param block 返回要进行 Hook 的成员对象的 lambda 表达式。
16 | */
17 | class HookManager(private val createCondition: Boolean = true, block: () -> Member?) {
18 |
19 | companion object {
20 | // 默认不执行 Hook, 后续获取到包名后, 可以将这里替换为只有当前为作用域内的应用, 才执行 Hook
21 | var defaultExecCondition: (() -> Boolean) = { false }
22 | }
23 |
24 | @Serializable
25 | data class HookInfo(
26 | val createCondition: Boolean,
27 | val hasReplaceHook: Boolean,
28 | val hasBeforeHooks: Boolean,
29 | val hasAfterHooks: Boolean,
30 | val isBeforeHookExecuted: Boolean,
31 | val isAfterHookExecuted: Boolean,
32 | val isReplaceHookExecuted: Boolean,
33 | val isMemberFound: Boolean,
34 | val isAbnormal: Boolean
35 | )
36 |
37 | private var member: Member? = null
38 |
39 | private val beforeHooks = mutableListOf Unit>()
40 | private val afterHooks = mutableListOf Unit>()
41 | private var replaceHook: (HookParam.() -> Any?)? = null
42 |
43 | private val hasReplaceHook get() = replaceHook != null
44 | private val hasBeforeHooks get() = beforeHooks.isNotEmpty()
45 | private val hasAfterHooks get() = afterHooks.isNotEmpty()
46 |
47 | private var isBeforeHookExecuted = false
48 | private var isAfterHookExecuted = false
49 | private var isReplaceHookExecuted = false
50 |
51 | private var isMemberFound = false
52 |
53 | private val isAbnormal get() = createCondition && (!isMemberFound ||
54 | (hasBeforeHooks && !isBeforeHookExecuted) ||
55 | (hasAfterHooks && !isAfterHookExecuted) ||
56 | (hasReplaceHook && !isReplaceHookExecuted))
57 |
58 | init {
59 | if (createCondition) try {
60 | member = block()
61 | isMemberFound = member != null
62 | } catch (e: Throwable) {
63 | YLog.error(e = e)
64 | }
65 | }
66 |
67 | /**
68 | * 获取方法的返回类型(如果 `member` 是方法)
69 | *
70 | * @return 如果 `member` 是方法,则返回该方法的返回类型;否则返回 `null`。
71 | */
72 | val returnType get() = (member as? Method)?.returnType
73 |
74 | /**
75 | * 添加一个在Hook前执行的回调函数。
76 | *
77 | * @param execCondition 判断是否执行回调函数的条件,默认为 [defaultExecCondition]。
78 | * @param block 在Hook前执行的回调函数。
79 | * @return 当前的HookManager实例。
80 | */
81 | fun addBeforeHook(execCondition: (() -> Boolean) = defaultExecCondition, block: HookParam.() -> Unit): HookManager {
82 | beforeHooks += { if (execCondition()) block() }
83 | return this
84 | }
85 |
86 | /**
87 | * 添加一个在Hook后执行的回调函数。
88 | *
89 | * @param execCondition 判断是否执行回调函数的条件,默认为 [defaultExecCondition]。
90 | * @param block 在Hook后执行的回调函数。
91 | * @return 当前的HookManager实例。
92 | */
93 | fun addAfterHook(execCondition: (() -> Boolean) = defaultExecCondition, block: HookParam.() -> Unit): HookManager {
94 | afterHooks += { if (execCondition()) block() }
95 | return this
96 | }
97 |
98 | /**
99 | * 添加一个替换原函数的回调函数, ReplaceHook 只能存在一个, 后面添加的会把之前的覆盖
100 | *
101 | * @param execCondition 判断是否执行回调函数的条件,默认为 [defaultExecCondition]。
102 | * @param block 替换原函数的回调函数, 注意回调函数的返回值即为替换原函数的返回值。
103 | * @return 当前的HookManager实例。
104 | */
105 | fun addReplaceHook(execCondition: (() -> Boolean) = defaultExecCondition, block: HookParam.() -> Any?): HookManager {
106 | replaceHook = { if (execCondition()) block() else callOriginal() }
107 | return this
108 | }
109 |
110 | /**
111 | * 开始进行Hook操作。
112 | *
113 | * 将 beforeHooks 和 afterHooks 分别整合到一起
114 | */
115 | fun startHook() {
116 | member?.hook {
117 | if (hasReplaceHook && (hasBeforeHooks || hasAfterHooks))
118 | YLog.warn("Conflict detected: ReplaceHook and Before/After Hooks cannot coexist. The before/after hooks will be ignored. Affected class: ${member?.declaringClass}. Affected member: ${member?.name}. Please ensure that only one type of hook is used at a time.")
119 |
120 | if (hasReplaceHook) replaceAny { replaceHook!!().also { isReplaceHookExecuted = true } }
121 | else {
122 | if (hasBeforeHooks) before { beforeHooks.forEach { it() }; isBeforeHookExecuted = true }
123 | if (hasAfterHooks) after { afterHooks.forEach { it() }; isAfterHookExecuted = true }
124 | }
125 | }
126 | }
127 |
128 | fun getHookInfo() = HookInfo(
129 | createCondition= createCondition,
130 | hasReplaceHook= hasReplaceHook,
131 | hasBeforeHooks= hasBeforeHooks,
132 | hasAfterHooks= hasAfterHooks,
133 | isBeforeHookExecuted= isBeforeHookExecuted,
134 | isAfterHookExecuted= isAfterHookExecuted,
135 | isReplaceHookExecuted= isReplaceHookExecuted,
136 | isMemberFound= isMemberFound,
137 | isAbnormal= isAbnormal
138 | )
139 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/BgHookHandler.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.hook.systemui
2 |
3 | import android.content.res.Configuration
4 | import android.graphics.Color
5 | import android.graphics.drawable.Drawable
6 | import androidx.compose.material3.dynamicDarkColorScheme
7 | import androidx.compose.material3.dynamicLightColorScheme
8 | import androidx.compose.ui.graphics.toArgb
9 | import com.gswxxn.restoresplashscreen.data.DataConst
10 | import com.gswxxn.restoresplashscreen.hook.NewSystemUIHooker
11 | import com.gswxxn.restoresplashscreen.hook.base.BaseHookHandler
12 | import com.gswxxn.restoresplashscreen.hook.systemui.GenerateHookHandler.currentPackageName
13 | import com.gswxxn.restoresplashscreen.utils.GraphicUtils
14 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.getMapPrefs
15 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.isMIUI
16 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.printLog
17 | import com.gswxxn.restoresplashscreen.wrapper.SplashScreenViewBuilderWrapper
18 | import com.highcapable.yukihookapi.hook.factory.current
19 |
20 | /**
21 | * 此对象用于处理 背景 Hook
22 | */
23 | object BgHookHandler: BaseHookHandler() {
24 | private var mTmpAttrsInstance: Any? = null
25 |
26 | /**
27 | * 重置当前应用的属性
28 | */
29 | fun resetCache() {
30 | mTmpAttrsInstance = null
31 | }
32 |
33 | /** 开始 Hook */
34 | override fun onHook() {
35 | NewSystemUIHooker.Members.getBGColorFromCache.addAfterHook {
36 | mTmpAttrsInstance = instance.current().field { name = "mTmpAttrs" }.any()
37 | }
38 | NewSystemUIHooker.Members.build_SplashScreenViewBuilder.addBeforeHook {
39 | val builder = SplashScreenViewBuilderWrapper.getInstance(instance)
40 |
41 | // 设置背景颜色
42 | getColor()?.let { builder.setBackgroundColor(it) }
43 | }
44 | }
45 |
46 | /**
47 | * 此处实现功能:
48 | * - 替换背景颜色
49 | * - 单独配置应用背景颜色
50 | */
51 | private fun getColor(): Int? {
52 | val isDarkMode = (appContext!!.resources
53 | .configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES)
54 | val bgColorMode = prefs.get(DataConst.BG_COLOR_MODE)
55 | val bgColorType = prefs.get(DataConst.CHANG_BG_COLOR_TYPE)
56 | val isInBGExceptList = currentPackageName in prefs.get(DataConst.BG_EXCEPT_LIST)
57 | val ignoreDarkMode = prefs.get(DataConst.IGNORE_DARK_MODE) || !isMIUI
58 | val individualBgColorAppMap = getMapPrefs(
59 | if (!isDarkMode) DataConst.INDIVIDUAL_BG_COLOR_APP_MAP
60 | else DataConst.INDIVIDUAL_BG_COLOR_APP_MAP_DARK)
61 | val skipAppWithBgColor = bgColorType != 0 &&
62 | currentPackageName !in individualBgColorAppMap.keys &&
63 | prefs.get(DataConst.SKIP_APP_WITH_BG_COLOR) &&
64 | mTmpAttrsInstance!!.current().field { name = "mWindowBgColor" }.int() != 0
65 |
66 | if (skipAppWithBgColor) {
67 | printLog("SplashScreenViewBuilder(): skip set bg color cuz app has been set bg color")
68 | return null
69 | }
70 |
71 | return if (currentPackageName in individualBgColorAppMap.keys) {
72 | printLog("SplashScreenViewBuilder(): set individual background color, ${individualBgColorAppMap[currentPackageName]}")
73 | Color.parseColor(individualBgColorAppMap[currentPackageName])
74 | } else if (!isInBGExceptList && (!isDarkMode || ignoreDarkMode))
75 | when (bgColorType) {
76 |
77 | // 从图标取色
78 | 1 -> {
79 | printLog("SplashScreenViewBuilder(): get adaptive background color")
80 | IconHookHandler.currentIconDominantColor ?:
81 | mTmpAttrsInstance!!.current().field { name = "mSplashScreenIcon" }.cast()?.let { drawable ->
82 | val bitmap = GraphicUtils.drawable2Bitmap(drawable, 100)
83 | val colorMode = prefs.get(DataConst.BG_COLOR_MODE)
84 | GraphicUtils.getBgColor(
85 | bitmap,
86 | when (colorMode) {
87 | 1 -> false
88 | 2 -> !isDarkMode
89 | else -> true
90 | }
91 | )
92 | }
93 | }
94 |
95 | // 从壁纸取色
96 | 2 -> {
97 | printLog("SplashScreenViewBuilder(): get monet background color")
98 | when (bgColorMode) {
99 | 0 -> dynamicLightColorScheme(appContext!!).primaryContainer.toArgb()
100 | 1 -> dynamicDarkColorScheme(appContext!!).surface.toArgb()
101 | else -> if (!isDarkMode)
102 | dynamicLightColorScheme(appContext!!).primaryContainer.toArgb()
103 | else
104 | dynamicDarkColorScheme(appContext!!).surface.toArgb()
105 | }
106 | }
107 |
108 | // 自定义颜色
109 | 3 -> {
110 | printLog("SplashScreenViewBuilder(): set overall background color")
111 | Color.parseColor(prefs.get(if (isDarkMode) DataConst.OVERALL_BG_COLOR_NIGHT else DataConst.OVERALL_BG_COLOR))
112 | }
113 |
114 | else -> {
115 | printLog("SplashScreenViewBuilder(): not replace background color"); null
116 | }
117 | } else {
118 | printLog("SplashScreenViewBuilder(): skip set bg color cuz app in except list"); null
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/BottomHookHandler.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.hook.systemui
2 |
3 | import com.gswxxn.restoresplashscreen.data.DataConst
4 | import com.gswxxn.restoresplashscreen.hook.NewSystemUIHooker
5 | import com.gswxxn.restoresplashscreen.hook.base.BaseHookHandler
6 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.printLog
7 | import com.gswxxn.restoresplashscreen.wrapper.SplashScreenViewBuilderWrapper
8 |
9 | /**
10 | * 此对象用于处理底部图片 Hook
11 | */
12 | object BottomHookHandler: BaseHookHandler() {
13 |
14 | /** 开始 Hook */
15 | override fun onHook() {
16 | /**
17 | * 移除底部图片
18 | */
19 | NewSystemUIHooker.Members.build_SplashScreenViewBuilder.addBeforeHook {
20 | val isRemoveBrandingImage = prefs.get(DataConst.REMOVE_BRANDING_IMAGE) &&
21 | if (prefs.get(DataConst.IS_REMOVE_BRANDING_IMAGE_EXCEPTION_MODE))
22 | GenerateHookHandler.currentPackageName !in prefs.get(DataConst.REMOVE_BRANDING_IMAGE_LIST)
23 | else
24 | GenerateHookHandler.currentPackageName in prefs.get(DataConst.REMOVE_BRANDING_IMAGE_LIST)
25 |
26 | if (isRemoveBrandingImage)
27 | SplashScreenViewBuilderWrapper.getInstance(instance).setBrandingDrawable(null, 0, 0)
28 | printLog("SplashScreenViewBuilder(): ${if (isRemoveBrandingImage) "" else "Not"} remove Branding Image")
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/ColorOSHookHandler.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.hook.systemui
2 |
3 | import android.graphics.drawable.Drawable
4 | import com.gswxxn.restoresplashscreen.hook.NewSystemUIHooker
5 | import com.gswxxn.restoresplashscreen.hook.base.BaseHookHandler
6 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.printLog
7 |
8 | /**
9 | * 此对象用于处理针对 ColorOS 的 Hook
10 | */
11 | object ColorOSHookHandler: BaseHookHandler() {
12 |
13 | /** 开始 Hook */
14 | override fun onHook() {
15 | NewSystemUIHooker.Members.setContentViewBackground_OplusShellStartingWindowManager.addBeforeHook {
16 | printLog("ColorOS: setContentViewBackground(): intercept!!")
17 | result = null
18 | }
19 |
20 | // 处理 Drawable 图标
21 | NewSystemUIHooker.Members.getIconExt_OplusShellStartingWindowManager.addAfterHook {
22 | printLog("getIconExt_OplusShellStartingWindowManager(): current method is getIconExt")
23 | result = IconHookHandler.processIconDrawable(result as Drawable)
24 | }
25 |
26 | // 禁止读取 WindowAttrs 缓存
27 | NewSystemUIHooker.Members.getWindowAttrsIfPresent_OplusShellStartingWindowManager.addBeforeHook {
28 | printLog("getWindowAttrsIfPresent_OplusShellStartingWindowManager(): return false")
29 | resultFalse()
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/GenerateHookHandler.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.hook.systemui
2 |
3 | import android.content.pm.ActivityInfo
4 | import com.gswxxn.restoresplashscreen.data.DataConst
5 | import com.gswxxn.restoresplashscreen.data.StartingWindowInfo
6 | import com.gswxxn.restoresplashscreen.hook.NewSystemUIHooker
7 | import com.gswxxn.restoresplashscreen.hook.base.BaseHookHandler
8 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.getMapPrefs
9 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.printLog
10 | import com.highcapable.yukihookapi.hook.factory.current
11 | import kotlinx.coroutines.MainScope
12 | import kotlinx.coroutines.delay
13 | import kotlinx.coroutines.launch
14 |
15 | /**
16 | * 此对象用于处理 基础设置 和 实验功能 中的 Hook
17 | */
18 | object GenerateHookHandler: BaseHookHandler() {
19 | var currentPackageName = ""
20 | var currentActivity = ""
21 | var exceptCurrentApp = false
22 | var isHooking = false
23 |
24 | /**
25 | * 重置当前应用信息的缓存
26 | */
27 | private fun resetCache() {
28 | currentPackageName = ""
29 | currentActivity = ""
30 | isHooking = false
31 | exceptCurrentApp = false
32 |
33 | IconHookHandler.resetCache()
34 | BgHookHandler.resetCache()
35 | }
36 |
37 | /** 开始 Hook */
38 | override fun onHook() {
39 |
40 | // Hook 起始位置, 获取应用信息
41 | NewSystemUIHooker.Members.makeSplashScreenContentView.addBeforeHook({ true }) {
42 | var activityInfo: ActivityInfo?
43 |
44 | if (args[1]!! is ActivityInfo)
45 | activityInfo = args[1] as ActivityInfo
46 | else {
47 | activityInfo = args[1]!!.current().field { name = "targetActivityInfo" }.cast()
48 | if (activityInfo == null) {
49 | activityInfo = args[1]!!.current().field { name = "taskInfo" }.any()!!.current()
50 | .field { superClass(); name = "topActivityInfo" }.cast()!!
51 | }
52 | }
53 |
54 | isHooking = true
55 | currentPackageName = activityInfo.packageName
56 | currentActivity = activityInfo.targetActivity ?: ""
57 | exceptCurrentApp = isExcept()
58 |
59 | printLog(
60 | "****** $currentPackageName; $currentActivity:",
61 | "makeSplashScreenContentView(): ${ if (exceptCurrentApp) "Except" else "Allow"} this app"
62 | )
63 |
64 | /**
65 | * 强制开启启动遮罩
66 | *
67 | * 直接干预 build() 中的 if 判断
68 | */
69 | val forceEnableSplashScreen = prefs.get(DataConst.FORCE_ENABLE_SPLASH_SCREEN)
70 | if (forceEnableSplashScreen) {
71 | if (!exceptCurrentApp) {
72 | args(args.indexOfFirst { it is Int }).set(StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN)
73 | printLog("makeSplashScreenContentView(): forceEnableSplashScreen, set mSuggestType to STARTING_WINDOW_TYPE_SPLASH_SCREEN(1)")
74 | }
75 | }
76 | }
77 |
78 | // 遮罩最小持续时间, 也是 Hook 结束位置, 清除缓存的应用信息
79 | NewSystemUIHooker.Members.removeStartingWindow.addReplaceHook({ true }) {
80 | if (exceptCurrentApp || !isHooking) callOriginal()
81 | else when (currentPackageName) {
82 | "" -> {
83 | callOriginal()
84 | }
85 |
86 | // 单独配置应用最小持续时长
87 | in prefs.get(DataConst.MIN_DURATION_LIST) -> {
88 | val configMap = getMapPrefs(DataConst.MIN_DURATION_CONFIG_MAP)
89 | try {
90 | val duration = configMap[currentPackageName].toString().toLong()
91 |
92 | if (duration == 0L) callOriginal()
93 | else {
94 | printLog("removeStartingWindow(): remove splash screen of $currentPackageName after $duration ms")
95 | MainScope().launch {
96 | delay(duration)
97 | callOriginal()
98 | }
99 | }
100 |
101 | } catch (_: NumberFormatException) {
102 | printLog("removeStartingWindow(): $currentPackageName: a NumberFormatException is threw, maybe it's MIN_DURATION config is incorrect")
103 | callOriginal()
104 | }
105 | }
106 |
107 | // 默认值
108 | else -> prefs.get(DataConst.MIN_DURATION).let { duration ->
109 | if (duration == 0) callOriginal()
110 | else {
111 | printLog("removeStartingWindow(): remove splash screen of $currentPackageName after $duration ms (default value)")
112 | MainScope().launch {
113 | delay(duration.toLong())
114 | callOriginal()
115 | }
116 | }
117 | }
118 | }
119 |
120 | // 清除缓存的应用信息
121 | resetCache()
122 | null
123 | }
124 | }
125 |
126 | /**
127 | * 判断是否应执行Hook操作。
128 | *
129 | * @return 是否应执行Hook操作。
130 | */
131 | private fun isExcept(): Boolean {
132 | return if (currentPackageName.isBlank())
133 | true
134 | else {
135 | val list = prefs.get(DataConst.CUSTOM_SCOPE_LIST)
136 | val isExceptionMode = prefs.get(DataConst.IS_CUSTOM_SCOPE_EXCEPTION_MODE)
137 | (prefs.get(DataConst.ENABLE_CUSTOM_SCOPE)
138 | && ((isExceptionMode && (currentPackageName in list))
139 | || (!isExceptionMode && currentPackageName !in list)))
140 | }
141 | }
142 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/MIUIHookHandler.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.hook.systemui
2 |
3 | import com.gswxxn.restoresplashscreen.data.DataConst
4 | import com.gswxxn.restoresplashscreen.hook.NewSystemUIHooker
5 | import com.gswxxn.restoresplashscreen.hook.base.BaseHookHandler
6 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.printLog
7 |
8 | /**
9 | * 此对象用于处理针对 MIUI 的 Hook
10 | */
11 | object MIUIHookHandler: BaseHookHandler() {
12 |
13 | /** 开始 Hook */
14 | override fun onHook() {
15 | /**
16 | * 背景 - 移除截图背景
17 | *
18 | * 类原始位置在 miui-framework.jar 中
19 | *
20 | * 此处在 com.android.wm.shell.startingsurface.SplashscreenContentDrawer
21 | * .$StartingWindowViewBuilder.fillViewWithIcon() 中被调用
22 | *
23 | * 原理为干预 fillViewWithIcon() 中的 if 判断,使其将启动器判断为不是 MIUI 桌面
24 | */
25 | NewSystemUIHooker.Members.isMiuiHome_TaskSnapshotHelperImpl.addBeforeHook {
26 | if (prefs.get(DataConst.REMOVE_BG_DRAWABLE)) {
27 | resultFalse()
28 | printLog("isMiuiHome(): set isMiuiHome() false")
29 | }
30 | }
31 |
32 | /**
33 | * 背景 - 忽略深色模式
34 | *
35 | * 类原始位置在 framework.jar 中
36 | *
37 | * 此处在 com.android.wm.shell.startingsurface.SplashscreenContentDrawer
38 | * .$StartingWindowViewBuilder.fillViewWithIcon() 中被调用
39 | */
40 | NewSystemUIHooker.Members.updateForceDarkSplashScreen_ForceDarkHelperStubImpl.addBeforeHook {
41 | if (prefs.get(DataConst.IGNORE_DARK_MODE)) {
42 | resultFalse()
43 | printLog("isStaringWindowUnderNightMode(): ignore dark mode")
44 | }
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/hook/systemui/ScopeHookHandler.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.hook.systemui
2 |
3 | import android.content.Context
4 | import com.gswxxn.restoresplashscreen.data.DataConst
5 | import com.gswxxn.restoresplashscreen.data.StartingWindowInfo
6 | import com.gswxxn.restoresplashscreen.hook.NewSystemUIHooker
7 | import com.gswxxn.restoresplashscreen.hook.base.BaseHookHandler
8 | import com.gswxxn.restoresplashscreen.hook.base.HookManager
9 | import com.gswxxn.restoresplashscreen.hook.systemui.GenerateHookHandler.currentPackageName
10 | import com.gswxxn.restoresplashscreen.hook.systemui.GenerateHookHandler.exceptCurrentApp
11 | import com.gswxxn.restoresplashscreen.hook.systemui.GenerateHookHandler.isHooking
12 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.isMIUI
13 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.printLog
14 | import com.highcapable.yukihookapi.hook.factory.current
15 |
16 | /**
17 | * 此对象用于处理作用域 Hook
18 | */
19 | object ScopeHookHandler: BaseHookHandler() {
20 |
21 | /** 开始 Hook */
22 | override fun onHook() {
23 | /**
24 | * 设置后续 Hooks 的默认执行条件, 只有 [currentPackageName] 在作用域内,
25 | * 并且当前 [isHooking] 才执行后续 Hooks
26 | */
27 | HookManager.defaultExecCondition = { isHooking && !exceptCurrentApp }
28 |
29 | // 将作用域外的应用替换为空白启动遮罩
30 | NewSystemUIHooker.Members.makeSplashScreenContentView.addBeforeHook({ true }) {
31 | val isReplaceToEmptySplashScreen = prefs.get(DataConst.REPLACE_TO_EMPTY_SPLASH_SCREEN)
32 |
33 | if (isReplaceToEmptySplashScreen && exceptCurrentApp) {
34 | args(args.indexOfFirst { it is Int }).set(StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN)
35 | }
36 | printLog("makeSplashScreenContentView(): ${if (isReplaceToEmptySplashScreen && exceptCurrentApp) "set mSuggestType to 4;" else "Not"} replace to empty splash screen")
37 | }
38 |
39 | /**
40 | * 绕过部分厂商为非主动适配 splash screen 的应用进行额外操作
41 | *
42 | * 原理为将 mIconBgColor 设置为一个固定值, 骗过厂商的额外判断, 后续再恢复成默认值
43 | *
44 | * 此操作在原生系统为非必要操作, 尤其在某些类原生系统执行此 Hook 会造成额外错误,
45 | * 所以这里手动指定为只在 MIUI 系统上执行该 Hook, 后续如有返回其他厂商系统需要类似操作, 再手动添加
46 | */
47 | if (isMIUI){
48 | NewSystemUIHooker.Members.getBGColorFromCache.addAfterHook {
49 | instance.current().field { name = "mTmpAttrs" }.any()!!.current().field { name = "mIconBgColor" }.set(1)
50 | printLog("getBGColorFromCache(): Set mIconBgColor to 1")
51 | }
52 |
53 | // 重置因实现自定义作用域而影响到的 mTmpAttrs
54 | NewSystemUIHooker.Members.startingWindowViewBuilderConstructor.addAfterHook {
55 | val mSplashscreenContentDrawer = instance.current().field { name = "this\$0" }.any()!!
56 | val mTmpAttrs = mSplashscreenContentDrawer.current().field { name = "mTmpAttrs" }.any()!!
57 | val context = args.first { it is Context }
58 |
59 | mSplashscreenContentDrawer.current().method {
60 | name = "getWindowAttrs"
61 | paramCount(2)
62 | }.call(context, mTmpAttrs)
63 | }
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui
2 |
3 | import android.app.Activity
4 | import android.graphics.Color
5 | import android.os.Bundle
6 | import android.view.View
7 | import androidx.core.view.WindowCompat
8 | import androidx.viewbinding.ViewBinding
9 | import com.gswxxn.restoresplashscreen.utils.CommonUtils.isDarkMode
10 | import com.highcapable.yukihookapi.hook.factory.method
11 | import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
12 | import java.lang.reflect.ParameterizedType
13 |
14 | /**
15 | * 改自 [MIUINativeNotifyIcon](https://github.com/fankes/MIUINativeNotifyIcon/blob/master/app/src/main/java/com/fankes/miui/notify/ui/activity/base/BaseActivity.kt)
16 | */
17 | abstract class BaseActivity : Activity() {
18 | lateinit var binding: VB
19 |
20 | /**
21 | * 批量显示或隐藏 [View]
22 | *
23 | * @param isShow [Boolean]
24 | * @param views [View]
25 | */
26 | open fun showView(isShow: Boolean = true, vararg views: View?) {
27 | for (element in views) {
28 | element?.visibility = if (isShow) View.VISIBLE else View.GONE
29 | }
30 | }
31 |
32 | override fun onCreate(savedInstanceState: Bundle?) {
33 | super.onCreate(savedInstanceState)
34 |
35 | // 安卓 15 以下仍需要
36 | window.statusBarColor = Color.TRANSPARENT
37 | window.navigationBarColor = Color.TRANSPARENT
38 | window.isNavigationBarContrastEnforced = false
39 | WindowCompat.setDecorFitsSystemWindows(window, false)
40 | WindowCompat.getInsetsController(window, window.decorView).apply {
41 | isAppearanceLightStatusBars = !isDarkMode(this@BaseActivity)
42 | isAppearanceLightNavigationBars = !isDarkMode(this@BaseActivity)
43 | }
44 |
45 | // 通过反射绑定布局
46 | javaClass.genericSuperclass.also { type ->
47 | if (type is ParameterizedType) {
48 | binding = (type.actualTypeArguments[0] as Class<*>).method {
49 | name = "inflate"
50 | param(LayoutInflaterClass)
51 | }.get().invoke(layoutInflater) ?: error("binding failed")
52 | setContentView(binding.root)
53 | } else error("binding but got wrong type")
54 | }
55 |
56 | /** 装载子类 */
57 | onCreate()
58 | }
59 |
60 | /** 回调 [onCreate] 方法 */
61 | abstract fun onCreate()
62 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/SubSettings.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui
2 |
3 | import android.content.Intent
4 | import android.view.View
5 | import com.gswxxn.restoresplashscreen.data.ConstValue
6 | import com.gswxxn.restoresplashscreen.databinding.ActivitySubSettingsBinding
7 | import com.gswxxn.restoresplashscreen.ui.`interface`.ISubSettings
8 | import com.gswxxn.restoresplashscreen.ui.subsettings.BackgroundSettings
9 | import com.gswxxn.restoresplashscreen.ui.subsettings.BasicSettings
10 | import com.gswxxn.restoresplashscreen.ui.subsettings.BottomSettings
11 | import com.gswxxn.restoresplashscreen.ui.subsettings.CustomScopeSettings
12 | import com.gswxxn.restoresplashscreen.ui.subsettings.DevSettings
13 | import com.gswxxn.restoresplashscreen.ui.subsettings.DisplaySettings
14 | import com.gswxxn.restoresplashscreen.ui.subsettings.HookInfo
15 | import com.gswxxn.restoresplashscreen.ui.subsettings.IconSettings
16 | import com.gswxxn.restoresplashscreen.utils.BlockMIUIHelper.addBlockMIUIView
17 |
18 | /** 子界面 */
19 | class SubSettings : BaseActivity() {
20 | private lateinit var instance: ISubSettings
21 |
22 | override fun onCreate() {
23 | val message = intent.getIntExtra(ConstValue.EXTRA_MESSAGE, 0)
24 |
25 | //返回按钮点击事件
26 | binding.titleBackIcon.setOnClickListener { finishAfterTransition() }
27 |
28 | when (message) {
29 | // 基础设置
30 | ConstValue.BASIC_SETTINGS -> BasicSettings
31 | // 作用域
32 | ConstValue.CUSTOM_SCOPE_SETTINGS -> CustomScopeSettings
33 | // 图标
34 | ConstValue.ICON_SETTINGS -> IconSettings
35 | // 底部
36 | ConstValue.BOTTOM_SETTINGS -> BottomSettings
37 | // 背景
38 | ConstValue.BACKGROUND_SETTINGS -> BackgroundSettings
39 | // 显示设置
40 | ConstValue.DISPLAY_SETTINGS -> DisplaySettings
41 | // Hook 信息
42 | ConstValue.HOOK_INFO -> HookInfo
43 | // 开发者选项
44 | ConstValue.DEV_SETTINGS -> DevSettings
45 |
46 | else -> null
47 | }?.apply {
48 | instance = this
49 | binding.appListTitle.text = getString(titleID)
50 | demoImageID?.let { binding.demoImage.setImageDrawable(getDrawable(it)) }
51 | ?: run {
52 | binding.demoImageLayout.visibility = View.GONE
53 | binding.mainStatus.background = null
54 | }
55 | binding.settingItems.addBlockMIUIView(this@SubSettings, itemData = create(this@SubSettings, binding))
56 | }
57 | }
58 |
59 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) =
60 | instance.onActivityResult(this, requestCode, resultCode, data)
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/configapps/BGColorIndividualConfig.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.configapps
2 |
3 | import android.content.Intent
4 | import android.graphics.Color
5 | import android.graphics.drawable.GradientDrawable
6 | import android.view.View
7 | import android.widget.CheckBox
8 | import android.widget.TextView
9 | import cn.fkj233.ui.activity.dp2px
10 | import com.gswxxn.restoresplashscreen.R
11 | import com.gswxxn.restoresplashscreen.data.ConstValue
12 | import com.gswxxn.restoresplashscreen.data.ConstValue.SELECT_COLOR_CODE
13 | import com.gswxxn.restoresplashscreen.data.DataConst
14 | import com.gswxxn.restoresplashscreen.databinding.AdapterConfigBinding
15 | import com.gswxxn.restoresplashscreen.ui.ColorSelectActivity
16 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity
17 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity.Companion.isDarkMode
18 | import com.gswxxn.restoresplashscreen.ui.`interface`.IConfigApps
19 | import com.gswxxn.restoresplashscreen.utils.AppInfoHelper
20 | import com.gswxxn.restoresplashscreen.utils.CommonUtils.toastL
21 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
22 |
23 | /** 单独配置背景颜色 */
24 | object BGColorIndividualConfig : IConfigApps {
25 | override val titleID: Int
26 | get() = R.string.configure_bg_colors_individually
27 | override val subSettingHint: Int
28 | get() = R.string.custom_bg_color_sub_setting_hint
29 | override val configMapPrefs: PrefsData>
30 | get() = if (isDarkMode) DataConst.INDIVIDUAL_BG_COLOR_APP_MAP_DARK else DataConst.INDIVIDUAL_BG_COLOR_APP_MAP
31 | override val submitSet: Boolean
32 | get() = false
33 | override val submitMap: Boolean
34 | get() = true
35 |
36 | override fun adpTextView(
37 | context: ConfigAppsActivity,
38 | holder: AdapterConfigBinding,
39 | item: AppInfoHelper.MyAppInfo
40 | ): TextView.() -> Unit = {
41 | if (item.config == null) {
42 | text = context.getString(R.string.default_value)
43 | background = null
44 | } else {
45 | text = ""
46 | width = dp2px(context, 30f)
47 | background = GradientDrawable().apply {
48 | setColor(Color.parseColor(item.config))
49 | setStroke(2, context.getColor(R.color.brandColor))
50 | cornerRadius = dp2px(context, 15f).toFloat()
51 | }
52 | }
53 | }
54 |
55 | override fun adpCheckBox(
56 | context: ConfigAppsActivity,
57 | holder: AdapterConfigBinding,
58 | item: AppInfoHelper.MyAppInfo
59 | ): CheckBox.() -> Unit = { visibility = View.GONE }
60 |
61 | override fun adpLinearLayout(
62 | context: ConfigAppsActivity,
63 | holder: AdapterConfigBinding,
64 | item: AppInfoHelper.MyAppInfo
65 | ): (View) -> Unit = {
66 | context.startActivityForResult(Intent(context, ColorSelectActivity::class.java).apply {
67 | putExtra(ConstValue.EXTRA_MESSAGE_PACKAGE_NAME, item.packageName)
68 | putExtra(ConstValue.EXTRA_MESSAGE_APP_INDEX, context.appInfo.getIndex(item))
69 | putExtra(ConstValue.EXTRA_MESSAGE_CURRENT_COLOR, item.config)
70 | }, SELECT_COLOR_CODE)
71 | }
72 |
73 | override fun onActivityResult(context: ConfigAppsActivity, requestCode: Int, resultCode: Int, data: Intent?) {
74 | val color = data?.getStringExtra(ConstValue.EXTRA_MESSAGE_SELECTED_COLOR)
75 | val pkgName = data?.getStringExtra(ConstValue.EXTRA_MESSAGE_PACKAGE_NAME)
76 | val index = data?.getIntExtra(ConstValue.EXTRA_MESSAGE_APP_INDEX, -1) ?: -1
77 | if (requestCode != SELECT_COLOR_CODE || color == null || index == -1 || pkgName == null) return
78 | try {
79 | when (resultCode) {
80 | ConstValue.SELECTED_COLOR -> {
81 | context.appInfo.setConfig(index, color)
82 | context.onRefreshList?.invoke()
83 | context.configMap[pkgName] = color
84 | }
85 |
86 | ConstValue.DEFAULT_COLOR -> {
87 | context.appInfo.setConfig(index, null)
88 | context.onRefreshList?.invoke()
89 | context.configMap.remove(pkgName)
90 | }
91 |
92 | ConstValue.UNDO_MODIFY -> {}
93 | }
94 | } catch (_: RuntimeException) {
95 | context.toastL(context.getString(R.string.mode_conflict))
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/configapps/BackgroundExcept.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.configapps
2 |
3 | import com.gswxxn.restoresplashscreen.R
4 | import com.gswxxn.restoresplashscreen.data.DataConst
5 | import com.gswxxn.restoresplashscreen.ui.`interface`.IConfigApps
6 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
7 |
8 | /**
9 | * 背景 - 替换背景颜色 - 排除列表
10 | */
11 | object BackgroundExcept : IConfigApps {
12 | override val titleID: Int
13 | get() = R.string.background_except_title
14 | override val checkedListPrefs: PrefsData>
15 | get() = DataConst.BG_EXCEPT_LIST
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/configapps/BrandingImage.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.configapps
2 |
3 | import android.widget.TextView
4 | import cn.fkj233.ui.activity.view.TextSummaryV
5 | import cn.fkj233.ui.activity.view.TextV
6 | import com.gswxxn.restoresplashscreen.R
7 | import com.gswxxn.restoresplashscreen.data.DataConst
8 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity
9 | import com.gswxxn.restoresplashscreen.ui.`interface`.IConfigApps
10 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
11 | import com.gswxxn.restoresplashscreen.view.SwitchView
12 | import com.highcapable.yukihookapi.hook.factory.prefs
13 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
14 |
15 | /**
16 | * 底部 - 移除底部图片 - 配置移除列表
17 | */
18 | object BrandingImage : IConfigApps {
19 | override val titleID: Int
20 | get() = R.string.background_image_title
21 |
22 | override val checkedListPrefs: PrefsData>
23 | get() = DataConst.REMOVE_BRANDING_IMAGE_LIST
24 |
25 | override fun blockMIUIView(context: ConfigAppsActivity): BlockMIUIItemData.() -> Unit = {
26 | fun getDataBinding(pref: Any) = GetDataBinding({ pref }) { view, flags, data ->
27 | when (flags) {
28 | 0 -> (view as TextView).text = context.getString(R.string.exception_mode_message, context.getString(if (data as Boolean) R.string.not_chosen else R.string.chosen))
29 | }
30 | }
31 |
32 | // 排除模式
33 | val exceptionModePrefs = DataConst.IS_REMOVE_BRANDING_IMAGE_EXCEPTION_MODE
34 | val exceptionModeBinding = getDataBinding(context.prefs().get(exceptionModePrefs))
35 | TextSummaryWithSwitch(TextSummaryV(textId = R.string.exception_mode), SwitchView(exceptionModePrefs, dataBindingSend = exceptionModeBinding.bindingSend))
36 |
37 | CustomView(
38 | TextV(
39 | textSize = 13F,
40 | colorId = R.color.colorTextRed,
41 | dataBindingRecv = exceptionModeBinding.getRecv(0)
42 | ).create(context, null).apply {
43 | setPadding(0, 0, 0, 0)
44 | },
45 | )
46 | }
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/configapps/CustomScope.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.configapps
2 |
3 | import android.widget.TextView
4 | import cn.fkj233.ui.activity.view.TextSummaryV
5 | import cn.fkj233.ui.activity.view.TextV
6 | import com.gswxxn.restoresplashscreen.R
7 | import com.gswxxn.restoresplashscreen.data.DataConst
8 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity
9 | import com.gswxxn.restoresplashscreen.ui.`interface`.IConfigApps
10 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
11 | import com.gswxxn.restoresplashscreen.view.SwitchView
12 | import com.highcapable.yukihookapi.hook.factory.prefs
13 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
14 |
15 | /**
16 | * 作用域 - 自定义模块作用域 - 配置应用列表
17 | */
18 | object CustomScope : IConfigApps {
19 | override val titleID: Int
20 | get() = R.string.custom_scope_title
21 |
22 | override val checkedListPrefs: PrefsData>
23 | get() = DataConst.CUSTOM_SCOPE_LIST
24 |
25 | override fun blockMIUIView(context: ConfigAppsActivity): BlockMIUIItemData.() -> Unit = {
26 | fun getDataBinding(pref: Any) = GetDataBinding({ pref }) { view, flags, data ->
27 | when (flags) {
28 | 0 -> (view as TextView).text = context.getString(R.string.custom_scope_exception_mode_message, context.getString(if (data as Boolean) R.string.will_not else R.string.will_only))
29 | }
30 | }
31 |
32 | // 排除模式
33 | val exceptionModePrefs = DataConst.IS_CUSTOM_SCOPE_EXCEPTION_MODE
34 | val exceptionModeBinding = getDataBinding(context.prefs().get(exceptionModePrefs))
35 | TextSummaryWithSwitch(TextSummaryV(textId = R.string.exception_mode), SwitchView(exceptionModePrefs, dataBindingSend = exceptionModeBinding.bindingSend))
36 |
37 | CustomView(
38 | TextV(
39 | textSize = 13F,
40 | colorId = R.color.colorTextRed,
41 | dataBindingRecv = exceptionModeBinding.getRecv(0)
42 | ).create(context, null).apply {
43 | setPadding(0, 0, 0, 0)
44 | },
45 | )
46 | }
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/configapps/DefaultStyle.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.configapps
2 |
3 | import android.widget.TextView
4 | import cn.fkj233.ui.activity.view.TextSummaryV
5 | import cn.fkj233.ui.activity.view.TextV
6 | import com.gswxxn.restoresplashscreen.R
7 | import com.gswxxn.restoresplashscreen.data.DataConst
8 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity
9 | import com.gswxxn.restoresplashscreen.ui.`interface`.IConfigApps
10 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
11 | import com.gswxxn.restoresplashscreen.view.SwitchView
12 | import com.highcapable.yukihookapi.hook.factory.prefs
13 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
14 |
15 | /**
16 | * 图标 - 忽略应用主动谁知的图标 - 配置应用列表
17 | */
18 | object DefaultStyle : IConfigApps {
19 | override val titleID: Int
20 | get() = R.string.default_style_title
21 |
22 | override val checkedListPrefs: PrefsData>
23 | get() = DataConst.DEFAULT_STYLE_LIST
24 |
25 | override fun blockMIUIView(context: ConfigAppsActivity): BlockMIUIItemData.() -> Unit = {
26 | fun getDataBinding(pref: Any) = GetDataBinding({ pref }) { view, flags, data ->
27 | when (flags) {
28 | 0 -> (view as TextView).text = context.getString(R.string.exception_mode_message, context.getString(if (data as Boolean) R.string.not_chosen else R.string.chosen))
29 | }
30 | }
31 |
32 | // 排除模式
33 | val exceptionModePrefs = DataConst.IS_DEFAULT_STYLE_LIST_EXCEPTION_MODE
34 | val exceptionModeBinding = getDataBinding(context.prefs().get(exceptionModePrefs))
35 | TextSummaryWithSwitch(TextSummaryV(textId = R.string.exception_mode), SwitchView(exceptionModePrefs, dataBindingSend = exceptionModeBinding.bindingSend))
36 |
37 | CustomView(
38 | TextV(
39 | textSize = 13F,
40 | colorId = R.color.colorTextRed,
41 | dataBindingRecv = exceptionModeBinding.getRecv(0)
42 | ).create(context, null).apply {
43 | setPadding(0, 0, 0, 0)
44 | },
45 | )
46 | }
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/configapps/ForceShowSplashScreen.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.configapps
2 |
3 | import com.gswxxn.restoresplashscreen.R
4 | import com.gswxxn.restoresplashscreen.data.DataConst
5 | import com.gswxxn.restoresplashscreen.ui.`interface`.IConfigApps
6 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
7 |
8 | /**
9 | * 实验功能 - 强制显示遮罩 - 配置应用列表
10 | */
11 | object ForceShowSplashScreen : IConfigApps {
12 | override val titleID: Int
13 | get() = R.string.force_show_splash_screen_title
14 | override val checkedListPrefs: PrefsData>
15 | get() = DataConst.FORCE_SHOW_SPLASH_SCREEN_LIST
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/configapps/HideSplashScreenIcon.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.configapps
2 |
3 | import android.widget.TextView
4 | import cn.fkj233.ui.activity.view.TextSummaryV
5 | import cn.fkj233.ui.activity.view.TextV
6 | import com.gswxxn.restoresplashscreen.R
7 | import com.gswxxn.restoresplashscreen.data.DataConst
8 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity
9 | import com.gswxxn.restoresplashscreen.ui.`interface`.IConfigApps
10 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
11 | import com.gswxxn.restoresplashscreen.view.SwitchView
12 | import com.highcapable.yukihookapi.hook.factory.prefs
13 |
14 | /** 图标 - 不显示图标 */
15 | object HideSplashScreenIcon : IConfigApps {
16 | override val titleID: Int
17 | get() = R.string.hide_splash_screen_icon_title
18 |
19 | override val checkedListPrefs = DataConst.HIDE_SPLASH_SCREEN_ICON_LIST
20 |
21 | override fun blockMIUIView(context: ConfigAppsActivity): BlockMIUIItemData.() -> Unit = {
22 | fun getDataBinding(pref: Any) = GetDataBinding({ pref }) { view, flags, data ->
23 | when (flags) {
24 | 0 -> (view as TextView).text = context.getString(R.string.exception_mode_message, context.getString(if (data as Boolean) R.string.not_chosen else R.string.chosen))
25 | }
26 | }
27 |
28 | // 排除模式
29 | val exceptionModePrefs = DataConst.IS_HIDE_SPLASH_SCREEN_ICON_EXCEPTION_MODE
30 | val exceptionModeBinding = getDataBinding(context.prefs().get(exceptionModePrefs))
31 | TextSummaryWithSwitch(TextSummaryV(textId = R.string.exception_mode), SwitchView(exceptionModePrefs, dataBindingSend = exceptionModeBinding.bindingSend))
32 |
33 | CustomView(
34 | TextV(
35 | textSize = 13F,
36 | colorId = R.color.colorTextRed,
37 | dataBindingRecv = exceptionModeBinding.getRecv(0)
38 | ).create(context, null).apply {
39 | setPadding(0, 0, 0, 0)
40 | },
41 | )
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/configapps/MinDuration.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.configapps
2 |
3 | import android.annotation.SuppressLint
4 | import android.os.Build
5 | import android.text.method.DigitsKeyListener
6 | import android.view.View
7 | import android.widget.ImageView
8 | import android.widget.TextView
9 | import cn.fkj233.ui.activity.view.MIUIEditText
10 | import cn.fkj233.ui.activity.view.TextSummaryV
11 | import cn.fkj233.ui.dialog.MIUIDialog
12 | import com.gswxxn.restoresplashscreen.BuildConfig
13 | import com.gswxxn.restoresplashscreen.R
14 | import com.gswxxn.restoresplashscreen.data.DataConst
15 | import com.gswxxn.restoresplashscreen.databinding.AdapterConfigBinding
16 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity
17 | import com.gswxxn.restoresplashscreen.ui.`interface`.IConfigApps
18 | import com.gswxxn.restoresplashscreen.utils.AppInfoHelper
19 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
20 | import com.highcapable.yukihookapi.hook.factory.current
21 | import com.highcapable.yukihookapi.hook.factory.prefs
22 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
23 |
24 | /**
25 | * 基础设置 - 遮罩最小持续时长
26 | */
27 | object MinDuration : IConfigApps {
28 | override val titleID: Int = R.string.min_duration_title
29 | override val subSettingHint: Int
30 | get() = R.string.min_duration_sub_setting_hint
31 | override val checkedListPrefs: PrefsData>
32 | get() = DataConst.MIN_DURATION_LIST
33 | override val configMapPrefs: PrefsData>
34 | get() = DataConst.MIN_DURATION_CONFIG_MAP
35 | override val submitMap: Boolean
36 | get() = true
37 |
38 | override fun moreOptions(context: ConfigAppsActivity): ImageView.() -> Unit = {
39 | visibility = View.GONE
40 | }
41 |
42 | override fun blockMIUIView(context: ConfigAppsActivity): BlockMIUIItemData.() -> Unit = {
43 | TextSummaryArrow(TextSummaryV(textId = R.string.set_default_min_duration) {
44 | MIUIDialog(context) {
45 | setTitle(R.string.set_default_min_duration)
46 | setMessage(R.string.set_min_duration_unit)
47 | setEditText(context.prefs().get(DataConst.MIN_DURATION).toString(), "", config = {
48 | it.keyListener = DigitsKeyListener.getInstance("1234567890")
49 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) it.isLocalePreferredLineHeightForMinimumUsed = false
50 | })
51 | setRButton(R.string.button_okay) {
52 | context.prefs().edit {
53 | if (getEditText().isNotBlank())
54 | put(DataConst.MIN_DURATION, getEditText().toInt())
55 | else
56 | put(DataConst.MIN_DURATION, 0)
57 | }
58 | dismiss()
59 | }
60 | setLButton(R.string.button_cancel) { dismiss() }
61 | }.show()
62 | })
63 | Line()
64 | TitleText(textId = R.string.min_duration_separate_configuration)
65 | }
66 |
67 | override fun adpTextView(
68 | context: ConfigAppsActivity,
69 | holder: AdapterConfigBinding,
70 | item: AppInfoHelper.MyAppInfo
71 | ): TextView.() -> Unit = {
72 | holder.adpAppTextView.text =
73 | if (item.config == null) context.getString(R.string.not_set_min_duration)
74 | else "${item.config} ms"
75 | }
76 |
77 | @SuppressLint("SetTextI18n")
78 | override fun adpLinearLayout(
79 | context: ConfigAppsActivity,
80 | holder: AdapterConfigBinding,
81 | item: AppInfoHelper.MyAppInfo
82 | ): (View) -> Unit = {
83 | holder.adpAppCheckBox.isChecked = true
84 | MIUIDialog(context) {
85 | setTitle(R.string.set_min_duration)
86 | setMessage(R.string.set_min_duration_unit)
87 | setEditText(item.config ?: "", "", config = {
88 | it.keyListener = DigitsKeyListener.getInstance("1234567890")
89 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) it.isLocalePreferredLineHeightForMinimumUsed = false
90 | })
91 | setRButton(R.string.button_okay) {
92 | if (getEditText().isEmpty() || getEditText() == "0") {
93 | context.appInfo.setConfig(item, null)
94 | holder.adpAppTextView.text = context.getString(R.string.not_set_min_duration)
95 | context.configMap.remove(item.packageName)
96 | } else {
97 | context.appInfo.setConfig(item, getEditText())
98 | holder.adpAppTextView.text = "${getEditText()} ms"
99 | context.configMap[item.packageName] = getEditText()
100 | }
101 | dismiss()
102 | }
103 | setLButton(R.string.button_cancel) { dismiss() }
104 | }.show()
105 | }
106 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/interface/IConfigApps.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.`interface`
2 |
3 | import android.content.Intent
4 | import android.view.View
5 | import android.widget.CheckBox
6 | import android.widget.ImageView
7 | import android.widget.LinearLayout
8 | import android.widget.PopupMenu
9 | import android.widget.TextView
10 | import com.gswxxn.restoresplashscreen.R
11 | import com.gswxxn.restoresplashscreen.data.DataConst
12 | import com.gswxxn.restoresplashscreen.databinding.AdapterConfigBinding
13 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity
14 | import com.gswxxn.restoresplashscreen.ui.SubSettings
15 | import com.gswxxn.restoresplashscreen.utils.AppInfoHelper
16 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
17 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
18 |
19 | /**
20 | * 配置应用界面接口
21 | *
22 | * @property titleID 标题文本 ID, 之后通过 getString(titleID) 获取标题文本
23 | * @property subSettingHint 子设置界面提示文本 ID, 之后通过 getString(subSettingHint) 获取子设置界面提示文本
24 | * @property submitSet 是否需要提交保存 [Set] 集合设置
25 | * @property submitMap 是否需要提交保存 [Map] 集合设置
26 | * @property checkedListPrefs 保存 [Set] 集合设置的 [PrefsData]
27 | * @property configMapPrefs 保存 [Map] 集合设置的 [PrefsData]
28 | */
29 | interface IConfigApps {
30 |
31 | val titleID: Int
32 | val subSettingHint: Int
33 | get() = R.string.save_hint
34 | val submitSet: Boolean
35 | get() = true
36 | val submitMap: Boolean
37 | get() = false
38 | val checkedListPrefs: PrefsData>
39 | get() = DataConst.UNDEFINED_LIST
40 | val configMapPrefs: PrefsData>
41 | get() = DataConst.UNDEFINED_LIST
42 |
43 | /**
44 | * 配置界面右上角的菜单栏事件
45 | *
46 | * @param context 当前配置界面的实例
47 | * @return 返回一个 lambda 表达式, 该表达式会在 ImageView 中被调用
48 | */
49 | fun moreOptions (context: ConfigAppsActivity): ImageView.() -> Unit = {
50 | setOnClickListener { it ->
51 | PopupMenu(context, it).apply {
52 | setOnMenuItemClickListener { item ->
53 | when (item.itemId) {
54 | R.id.select_system_apps -> {
55 | context.appInfo.getAppInfoList().forEach {
56 | if (it.isSystemApp) {
57 | context.appInfo.setChecked(it, true)
58 | context.checkedList.add(it.packageName)
59 | }
60 | }
61 | context.appInfoFilter = context.appInfo.getAppInfoList()
62 | context.onRefreshList?.invoke()
63 | true
64 | }
65 | R.id.clear_slection -> {
66 | context.appInfo.getAppInfoList().forEach {
67 | context.appInfo.setChecked(it, false)
68 | context.checkedList.remove(it.packageName)
69 | }
70 | context.appInfoFilter = context.appInfo.getAppInfoList()
71 | context.onRefreshList?.invoke()
72 | true
73 | }
74 | else -> false
75 | }
76 | }
77 | inflate(R.menu.more_options_menu)
78 | show()
79 | }
80 | }
81 | }
82 |
83 | /**
84 | * 在提示文本和列表之间添加一个自定义的 BlockMIUIView, 用于添加一些自定义的设置
85 | * @see BlockMIUIItemData
86 | *
87 | * @param context 当前配置界面的实例
88 | */
89 | fun blockMIUIView (context: ConfigAppsActivity): BlockMIUIItemData.() -> Unit = {
90 | context.findViewById(R.id.overall_settings).visibility = View.GONE
91 | }
92 |
93 | /**
94 | * 配置每个列表项目的布局
95 | *
96 | * @param context 当前配置界面的实例
97 | * @param holder 当前列表项的 [AdapterConfigBinding] 实例
98 | * @param item 当前列表项的 [AppInfoHelper.MyAppInfo] 实例
99 | * @return 返回一个 lambda 表达式, 该表达式会在 TextView 中被调用
100 | */
101 | fun adpTextView (
102 | context: ConfigAppsActivity,
103 | holder: AdapterConfigBinding,
104 | item: AppInfoHelper.MyAppInfo
105 | ): TextView.() -> Unit = { }
106 |
107 | /**
108 | * 配置每个列表项目的复选框
109 | *
110 | * @param context 当前配置界面的实例
111 | * @param holder 当前列表项的 [AdapterConfigBinding] 实例
112 | * @param item 当前列表项的 [AppInfoHelper.MyAppInfo] 实例
113 | * @return 返回一个 lambda 表达式, 该表达式会在 CheckBox 中被调用
114 | */
115 | fun adpCheckBox (
116 | context: ConfigAppsActivity,
117 | holder: AdapterConfigBinding,
118 | item: AppInfoHelper.MyAppInfo
119 | ): CheckBox.() -> Unit = {
120 | setOnCheckedChangeListener { _, isChecked ->
121 | context.appInfo.setChecked(item, isChecked)
122 | if (isChecked) {
123 | context.checkedList.add(item.packageName)
124 | } else {
125 | context.checkedList.remove(item.packageName)
126 | }
127 | }
128 | isChecked = item.packageName in context.checkedList
129 | }
130 |
131 | /**
132 | * 配置每个列表项目的线性布局
133 | *
134 | * @param context 当前配置界面的实例
135 | * @param holder 当前列表项的 [AdapterConfigBinding] 实例
136 | * @param item 当前列表项的 [AppInfoHelper.MyAppInfo] 实例
137 | * @return 返回一个 lambda 表达式, 该表达式会在 LinearLayout 中被调用
138 | */
139 | fun adpLinearLayout (
140 | context: ConfigAppsActivity,
141 | holder: AdapterConfigBinding,
142 | item: AppInfoHelper.MyAppInfo
143 | ): ((View) -> Unit) = {
144 | holder.adpAppCheckBox.isChecked = !holder.adpAppCheckBox.isChecked
145 | }
146 |
147 | /**
148 | * 重写以处理 onActivityResult
149 | *
150 | * @param context 当前子设置界面的实例
151 | * @param requestCode 请求码
152 | * @param resultCode 结果码
153 | * @param data 返回的 Intent
154 | *
155 | * @see SubSettings.onActivityResult
156 | */
157 | fun onActivityResult(context: ConfigAppsActivity, requestCode: Int, resultCode: Int, data: Intent?) { }
158 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/interface/ISubSettings.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.`interface`
2 |
3 | import android.content.Intent
4 | import com.gswxxn.restoresplashscreen.databinding.ActivitySubSettingsBinding
5 | import com.gswxxn.restoresplashscreen.ui.SubSettings
6 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
7 |
8 | /**
9 | * 创建子设置界面的接口, 需要在 [SubSettings.onCreate] 中注册
10 | *
11 | * @property titleID 标题文本 ID, 之后通过 getString(titleID) 获取标题文本
12 | * @property demoImageID 演示图片 ID, 之后通过 getDrawable(demoImageID) 获取字节面上方演示图片
13 | */
14 | interface ISubSettings {
15 | val titleID: Int
16 | val demoImageID: Int?
17 |
18 | /**
19 | * 创建子设置界面
20 | *
21 | * @param context 当前子设置界面的实例
22 | * @param binding 当前子设置界面的 DataBinding
23 | * @return 返回一个 lambda 表达式, 该表达式会在 BlockMIUIItemData 中被调用
24 | */
25 | fun create(context: SubSettings, binding: ActivitySubSettingsBinding): BlockMIUIItemData.() -> Unit
26 |
27 | /**
28 | * 重写以处理 onActivityResult
29 | *
30 | * @param context 当前子设置界面的实例
31 | * @param requestCode 请求码
32 | * @param resultCode 结果码
33 | * @param data 返回的 Intent
34 | *
35 | * @see SubSettings.onActivityResult
36 | */
37 | fun onActivityResult(context: SubSettings, requestCode: Int, resultCode: Int, data: Intent?) { }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/subsettings/BasicSettings.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.subsettings
2 |
3 | import android.content.ComponentName
4 | import android.content.Intent
5 | import android.content.pm.PackageManager
6 | import cn.fkj233.ui.activity.view.TextSummaryV
7 | import com.gswxxn.restoresplashscreen.BuildConfig
8 | import com.gswxxn.restoresplashscreen.R
9 | import com.gswxxn.restoresplashscreen.data.ConstValue.CREATE_DOCUMENT_CODE
10 | import com.gswxxn.restoresplashscreen.data.ConstValue.OPEN_DOCUMENT_CODE
11 | import com.gswxxn.restoresplashscreen.data.DataConst
12 | import com.gswxxn.restoresplashscreen.databinding.ActivitySubSettingsBinding
13 | import com.gswxxn.restoresplashscreen.ui.SubSettings
14 | import com.gswxxn.restoresplashscreen.ui.`interface`.ISubSettings
15 | import com.gswxxn.restoresplashscreen.utils.BackupUtils
16 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
17 | import com.gswxxn.restoresplashscreen.view.SwitchView
18 | import com.highcapable.yukihookapi.hook.factory.prefs
19 |
20 | /**
21 | * 基础设置 界面
22 | */
23 | object BasicSettings : ISubSettings {
24 | override val titleID = R.string.basic_settings
25 | override val demoImageID = R.drawable.demo_basic
26 |
27 | override fun create(context: SubSettings, binding: ActivitySubSettingsBinding): BlockMIUIItemData.() -> Unit = {
28 | // 启用日志
29 | if (System.currentTimeMillis() - context.prefs().get(DataConst.ENABLE_LOG_TIMESTAMP) > 86400000) {
30 | context.prefs().edit().put(DataConst.ENABLE_LOG, false).commit()
31 | }
32 | TextSummaryWithSwitch(
33 | TextSummaryV(textId = R.string.enable_log, tipsId = R.string.enable_log_tips),
34 | SwitchView(DataConst.ENABLE_LOG) {
35 | if (it) {
36 | context.prefs().edit { put(DataConst.ENABLE_LOG_TIMESTAMP, System.currentTimeMillis()) }
37 | }
38 | }
39 | )
40 |
41 | // 隐藏桌面图标
42 | TextSummaryWithSwitch(TextSummaryV(textId = R.string.hide_icon), SwitchView(DataConst.ENABLE_HIDE_ICON) {
43 | context.packageManager.setComponentEnabledSetting(
44 | ComponentName(context, "${BuildConfig.APPLICATION_ID}.Home"),
45 | if (it) PackageManager.COMPONENT_ENABLED_STATE_DISABLED else PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
46 | PackageManager.DONT_KILL_APP
47 | )
48 | })
49 |
50 | Line()
51 | TitleText(textId = R.string.backup_restore_title)
52 |
53 | // 备份设置项
54 | TextSummaryArrow(TextSummaryV(textId = R.string.backup) {
55 | BackupUtils.saveFile(context)
56 | })
57 |
58 | // 恢复设置项
59 | TextSummaryArrow(TextSummaryV(textId = R.string.restore) {
60 | BackupUtils.openFile(context)
61 | })
62 | }
63 |
64 | override fun onActivityResult(context: SubSettings, requestCode: Int, resultCode: Int, data: Intent?) {
65 | when (requestCode) {
66 | CREATE_DOCUMENT_CODE -> BackupUtils.handleCreateDocument(context, data?.data)
67 | OPEN_DOCUMENT_CODE -> BackupUtils.handleReadDocument(context, data?.data)
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/subsettings/BottomSettings.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.subsettings
2 |
3 | import android.content.Intent
4 | import android.view.View
5 | import cn.fkj233.ui.activity.view.TextSummaryV
6 | import com.gswxxn.restoresplashscreen.R
7 | import com.gswxxn.restoresplashscreen.data.ConstValue
8 | import com.gswxxn.restoresplashscreen.data.DataConst
9 | import com.gswxxn.restoresplashscreen.databinding.ActivitySubSettingsBinding
10 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity
11 | import com.gswxxn.restoresplashscreen.ui.SubSettings
12 | import com.gswxxn.restoresplashscreen.ui.`interface`.ISubSettings
13 | import com.gswxxn.restoresplashscreen.utils.CommonUtils.toast
14 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
15 | import com.gswxxn.restoresplashscreen.view.SwitchView
16 | import com.highcapable.yukihookapi.hook.factory.prefs
17 |
18 | /**
19 | * 底部 界面
20 | */
21 | object BottomSettings : ISubSettings {
22 | override val titleID = R.string.bottom_settings
23 | override val demoImageID = R.drawable.demo_branding
24 |
25 | override fun create(context: SubSettings, binding: ActivitySubSettingsBinding): BlockMIUIItemData.() -> Unit = {
26 | fun getDataBinding(pref : Any) = GetDataBinding({ pref }) { view, flags, data ->
27 | when (flags) {
28 | 0 -> view.visibility = if (data as Boolean) View.VISIBLE else View.GONE
29 | }
30 | }
31 |
32 | // 移除底部图片
33 | val removeBrandingImageBinding = getDataBinding(context.prefs().get(DataConst.REMOVE_BRANDING_IMAGE))
34 | TextSummaryWithSwitch(
35 | TextSummaryV(
36 | textId = R.string.remove_branding_image,
37 | tipsId = R.string.remove_branding_image_tips
38 | ),
39 | SwitchView(DataConst.REMOVE_BRANDING_IMAGE, dataBindingSend = removeBrandingImageBinding.bindingSend) {
40 | if (it) context.toast(context.getString(R.string.custom_scope_message))
41 | }
42 | )
43 |
44 | // 配置移除列表
45 | TextSummaryArrow(TextSummaryV(textId = R.string.remove_branding_image_list, onClickListener = {
46 | context.startActivity(Intent(context, ConfigAppsActivity::class.java).apply {
47 | putExtra(ConstValue.EXTRA_MESSAGE, ConstValue.BRANDING_IMAGE)
48 | })
49 | }), dataBindingRecv = removeBrandingImageBinding.getRecv(0))
50 |
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/subsettings/CustomScopeSettings.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.subsettings
2 |
3 | import android.content.Intent
4 | import android.view.View
5 | import cn.fkj233.ui.activity.view.TextSummaryV
6 | import com.gswxxn.restoresplashscreen.R
7 | import com.gswxxn.restoresplashscreen.data.ConstValue
8 | import com.gswxxn.restoresplashscreen.data.DataConst
9 | import com.gswxxn.restoresplashscreen.databinding.ActivitySubSettingsBinding
10 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity
11 | import com.gswxxn.restoresplashscreen.ui.SubSettings
12 | import com.gswxxn.restoresplashscreen.ui.`interface`.ISubSettings
13 | import com.gswxxn.restoresplashscreen.utils.CommonUtils.toast
14 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
15 | import com.gswxxn.restoresplashscreen.view.SwitchView
16 | import com.highcapable.yukihookapi.hook.factory.prefs
17 |
18 | /**
19 | * 作用域 界面
20 | */
21 | object CustomScopeSettings : ISubSettings {
22 | override val titleID = R.string.custom_scope_settings
23 | override val demoImageID = R.drawable.demo_scope
24 |
25 | override fun create(context: SubSettings, binding: ActivitySubSettingsBinding): BlockMIUIItemData.() -> Unit = {
26 | fun getDataBinding(pref : Any) = GetDataBinding({ pref }) { view, flags, data ->
27 | when (flags) {
28 | 0 -> view.visibility = if (data as Boolean) View.VISIBLE else View.GONE
29 | }
30 | }
31 |
32 | // 自定义模块作用域
33 | val customScopeBinding = getDataBinding(context.prefs().get(DataConst.ENABLE_CUSTOM_SCOPE))
34 | TextSummaryWithSwitch(TextSummaryV(textId = R.string.custom_scope), SwitchView(DataConst.ENABLE_CUSTOM_SCOPE, dataBindingSend = customScopeBinding.bindingSend) {
35 | if (it) context.toast(context.getString(R.string.custom_scope_message))
36 | })
37 |
38 | // 将作用域外的应用替换位空白启动遮罩
39 | TextSummaryWithSwitch(
40 | TextSummaryV(textId = R.string.replace_to_empty_splash_screen, tipsId = R.string.replace_to_empty_splash_screen_tips), SwitchView(
41 | DataConst.REPLACE_TO_EMPTY_SPLASH_SCREEN), dataBindingRecv = customScopeBinding.binding.getRecv(0))
42 |
43 | // 配置应用列表
44 | TextSummaryArrow(TextSummaryV(textId = R.string.exception_mode_list) {
45 | context.startActivity(Intent(context, ConfigAppsActivity::class.java).apply {
46 | putExtra(ConstValue.EXTRA_MESSAGE, ConstValue.CUSTOM_SCOPE)
47 | })
48 | }, dataBindingRecv = customScopeBinding.binding.getRecv(0))
49 | }
50 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/subsettings/DevSettings.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.subsettings
2 |
3 | import android.content.Intent
4 | import cn.fkj233.ui.activity.view.TextSummaryV
5 | import com.gswxxn.restoresplashscreen.R
6 | import com.gswxxn.restoresplashscreen.data.ConstValue
7 | import com.gswxxn.restoresplashscreen.data.DataConst
8 | import com.gswxxn.restoresplashscreen.databinding.ActivitySubSettingsBinding
9 | import com.gswxxn.restoresplashscreen.ui.SubSettings
10 | import com.gswxxn.restoresplashscreen.ui.`interface`.ISubSettings
11 | import com.gswxxn.restoresplashscreen.utils.CommonUtils.toast
12 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
13 | import com.gswxxn.restoresplashscreen.view.SwitchView
14 |
15 | /** 开发者选项 */
16 | object DevSettings: ISubSettings {
17 | override val titleID = R.string.dev_settings
18 | override val demoImageID = null
19 |
20 | /** onCreate 事件 */
21 | override fun create(
22 | context: SubSettings,
23 | binding: ActivitySubSettingsBinding
24 | ): BlockMIUIItemData.() -> Unit = {
25 | TextSummaryWithSwitch(
26 | TextSummaryV(
27 | textId = R.string.dev_settings
28 | ),
29 | SwitchView(DataConst.ENABLE_DEV_SETTINGS) {
30 | if (it) return@SwitchView
31 |
32 | context.toast(context.getString(R.string.disabled_dev_settings))
33 | context.finishAfterTransition()
34 | }
35 | )
36 |
37 | TextSummaryArrow(
38 | TextSummaryV(textId = R.string.hook_info) {
39 | context.startActivity(Intent(context, SubSettings::class.java).apply {
40 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
41 | putExtra(ConstValue.EXTRA_MESSAGE, ConstValue.HOOK_INFO)
42 | })
43 | }
44 | )
45 |
46 | Line()
47 | TitleText(textId = R.string.icon_settings)
48 |
49 | SeekBarWithStatus(
50 | titleID = R.string.dev_icon_round_corner_rate,
51 | pref = DataConst.DEV_ICON_ROUND_CORNER_RATE,
52 | min = 0,
53 | max = 50,
54 | isPercentage = true
55 | )
56 | }
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/subsettings/DisplaySettings.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.subsettings
2 |
3 | import android.content.Intent
4 | import android.view.View
5 | import android.widget.Switch
6 | import cn.fkj233.ui.activity.view.TextSummaryV
7 | import com.gswxxn.restoresplashscreen.R
8 | import com.gswxxn.restoresplashscreen.data.ConstValue
9 | import com.gswxxn.restoresplashscreen.data.DataConst
10 | import com.gswxxn.restoresplashscreen.databinding.ActivitySubSettingsBinding
11 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity
12 | import com.gswxxn.restoresplashscreen.ui.SubSettings
13 | import com.gswxxn.restoresplashscreen.ui.`interface`.ISubSettings
14 | import com.gswxxn.restoresplashscreen.utils.CommonUtils.toast
15 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
16 | import com.gswxxn.restoresplashscreen.view.SwitchView
17 | import com.highcapable.yukihookapi.hook.factory.prefs
18 |
19 | /**
20 | * 显示设置 界面
21 | */
22 | object DisplaySettings : ISubSettings {
23 | override val titleID = R.string.display_settings
24 | override val demoImageID = R.drawable.demo_display
25 |
26 | override fun create(context: SubSettings, binding: ActivitySubSettingsBinding): BlockMIUIItemData.() -> Unit = {
27 | fun getDataBinding(pref : Any) = GetDataBinding({ pref }) { view, flags, data ->
28 | when (flags) {
29 | 0 -> view.visibility = if (data as Boolean) View.VISIBLE else View.GONE
30 | 1 -> if (data as Boolean) (view as Switch).isChecked = true
31 | 2 -> if (!(data as Boolean)) (view as Switch).isChecked = false
32 | }
33 | }
34 |
35 | // 遮罩最小持续时间
36 | TextSummaryArrow(TextSummaryV(textId = R.string.min_duration, tipsId = R.string.min_duration_tips) {
37 | context.startActivity(Intent(context, ConfigAppsActivity::class.java).apply {
38 | putExtra(ConstValue.EXTRA_MESSAGE, ConstValue.MIN_DURATION)
39 | })
40 | })
41 |
42 | Line()
43 |
44 | // 强制显示遮罩
45 | val forceShowSplashScreenBinding = getDataBinding(context.prefs().get(DataConst.FORCE_SHOW_SPLASH_SCREEN))
46 | TextSummaryWithSwitch(
47 | TextSummaryV(
48 | textId = R.string.force_show_splash_screen,
49 | tipsId = R.string.force_show_splash_screen_tips
50 | ),
51 | SwitchView(DataConst.FORCE_SHOW_SPLASH_SCREEN, dataBindingSend = forceShowSplashScreenBinding.bindingSend) {
52 | if (it) context.toast(context.getString(R.string.custom_scope_message))
53 | }
54 | )
55 |
56 | // 配置应用列表
57 | TextSummaryArrow(TextSummaryV(textId = R.string.force_show_splash_screen_list, onClickListener = {
58 | context.startActivity(Intent(context, ConfigAppsActivity::class.java).apply {
59 | putExtra(ConstValue.EXTRA_MESSAGE, ConstValue.FORCE_SHOW_SPLASH_SCREEN)
60 | })
61 | }), dataBindingRecv = forceShowSplashScreenBinding.getRecv(0))
62 |
63 | // 减少不必要的启动遮罩
64 | TextSummaryWithSwitch(
65 | TextSummaryV(
66 | textId = R.string.reduce_splash_screen,
67 | tipsId = R.string.reduce_splash_screen_tips
68 | ),
69 | SwitchView(DataConst.REDUCE_SPLASH_SCREEN),
70 | dataBindingRecv = forceShowSplashScreenBinding.getRecv(0)
71 | )
72 |
73 | Line()
74 |
75 | // 强制开启启动遮罩
76 | val hotStartBinding = getDataBinding(context.prefs().get(DataConst.ENABLE_HOT_START_COMPATIBLE))
77 | val forceEnableSplashScreenBinding = getDataBinding(context.prefs().get(DataConst.FORCE_ENABLE_SPLASH_SCREEN))
78 | TextSummaryWithSwitch(
79 | TextSummaryV(
80 | textId = R.string.force_enable_splash_screen,
81 | tipsId = R.string.force_enable_splash_screen_tips
82 | ),
83 | SwitchView(
84 | DataConst.FORCE_ENABLE_SPLASH_SCREEN,
85 | dataBindingRecv = hotStartBinding.getRecv(1),
86 | dataBindingSend = forceEnableSplashScreenBinding.bindingSend
87 | )
88 | )
89 |
90 | // 将启动遮罩适用于热启动
91 | TextSummaryWithSwitch(
92 | TextSummaryV(
93 | textId = R.string.hot_start_compatible,
94 | tipsId = R.string.hot_start_compatible_tips
95 | ),
96 | SwitchView(
97 | DataConst.ENABLE_HOT_START_COMPATIBLE,
98 | dataBindingRecv = forceEnableSplashScreenBinding.getRecv(2),
99 | dataBindingSend = hotStartBinding.bindingSend
100 | )
101 | )
102 |
103 | // 彻底关闭 Splash Screen
104 | TextSummaryWithSwitch(
105 | TextSummaryV(textId = R.string.disable_splash_screen, tipsId = R.string.disable_splash_screen_tips), SwitchView(
106 | DataConst.DISABLE_SPLASH_SCREEN)
107 | )
108 |
109 | }
110 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/subsettings/HookInfo.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.subsettings
2 |
3 | import android.widget.LinearLayout
4 | import androidx.compose.ui.graphics.Color
5 | import androidx.compose.ui.graphics.toArgb
6 | import com.gswxxn.restoresplashscreen.R
7 | import com.gswxxn.restoresplashscreen.databinding.ActivitySubSettingsBinding
8 | import com.gswxxn.restoresplashscreen.hook.base.HookManager
9 | import com.gswxxn.restoresplashscreen.ui.SubSettings
10 | import com.gswxxn.restoresplashscreen.ui.`interface`.ISubSettings
11 | import com.gswxxn.restoresplashscreen.utils.BlockMIUIHelper.addBlockMIUIView
12 | import com.gswxxn.restoresplashscreen.utils.YukiHelper.getHookInfo
13 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
14 |
15 | /**
16 | * Hook 信息 界面
17 | */
18 | object HookInfo: ISubSettings {
19 | override val titleID = R.string.hook_info
20 | override val demoImageID = null
21 |
22 | /**
23 | * 创建一个新的 BlockMIUIItemData 实例
24 | */
25 | override fun create(context: SubSettings, binding: ActivitySubSettingsBinding): BlockMIUIItemData.() -> Unit = {
26 | context.getHookInfo("com.android.systemui") { hookInfo ->
27 | redrawView(context, binding.settingItems, hookInfo)
28 | }
29 | }
30 |
31 | /**
32 | * 重新绘制视图,使用给定的上下文和钩子管理器的映射。
33 | * 从线性布局中移除所有现有视图,并为映射中的每个条目添加新的 TextSummary 视图,
34 | * 按照 Hook 是否可能异常以及键进行排序
35 | *
36 | * @param context 用于创建视图的上下文。
37 | * @param linearLayout 要重新绘制的线性布局。
38 | * @param map 钩子管理器的映射。
39 | */
40 | private fun redrawView(context: SubSettings, linearLayout: LinearLayout, map: Map) {
41 | linearLayout.removeAllViews()
42 | linearLayout.addBlockMIUIView(context) {
43 | map.entries.sortedWith(compareBy({ !it.value.isAbnormal }, {it.key})).forEach { (key, hookInfo) ->
44 | TextSummary(
45 | text = key,
46 | colorInt = if (hookInfo.isAbnormal) Color.Red.toArgb() else null,
47 | tips = "createCondition: ${hookInfo.createCondition}\n" +
48 | "isMemberFound: ${hookInfo.isMemberFound}\n" +
49 | "hasBeforeHooks: ${hookInfo.hasBeforeHooks}\n" +
50 | "isBeforeHookExecuted: ${hookInfo.isBeforeHookExecuted}\n" +
51 | "hasAfterHooks: ${hookInfo.hasAfterHooks}\n" +
52 | "isAfterHookExecuted: ${hookInfo.isAfterHookExecuted}\n" +
53 | "hasReplaceHook: ${hookInfo.hasReplaceHook}\n" +
54 | "isReplaceHookExecuted: ${hookInfo.isReplaceHookExecuted}"
55 | )
56 | }
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/ui/subsettings/IconSettings.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.ui.subsettings
2 |
3 | import android.content.Intent
4 | import android.view.View
5 | import androidx.core.widget.NestedScrollView
6 | import cn.fkj233.ui.activity.view.SpinnerV
7 | import cn.fkj233.ui.activity.view.TextSummaryV
8 | import cn.fkj233.ui.activity.view.TextV
9 | import com.gswxxn.restoresplashscreen.R
10 | import com.gswxxn.restoresplashscreen.data.ConstValue
11 | import com.gswxxn.restoresplashscreen.data.DataConst
12 | import com.gswxxn.restoresplashscreen.databinding.ActivitySubSettingsBinding
13 | import com.gswxxn.restoresplashscreen.ui.ConfigAppsActivity
14 | import com.gswxxn.restoresplashscreen.ui.SubSettings
15 | import com.gswxxn.restoresplashscreen.ui.`interface`.ISubSettings
16 | import com.gswxxn.restoresplashscreen.utils.CommonUtils.toast
17 | import com.gswxxn.restoresplashscreen.utils.IconPackManager
18 | import com.gswxxn.restoresplashscreen.utils.YukiHelper
19 | import com.gswxxn.restoresplashscreen.view.BlockMIUIItemData
20 | import com.gswxxn.restoresplashscreen.view.SwitchView
21 | import com.highcapable.yukihookapi.hook.factory.prefs
22 | import kotlinx.coroutines.MainScope
23 | import kotlinx.coroutines.delay
24 | import kotlinx.coroutines.launch
25 |
26 | /**
27 | * 图标 界面
28 | */
29 | object IconSettings : ISubSettings {
30 | override val titleID = R.string.icon_settings
31 | override val demoImageID = R.drawable.demo_icon
32 |
33 | override fun create(context: SubSettings, binding: ActivitySubSettingsBinding): BlockMIUIItemData.() -> Unit = {
34 | fun getDataBinding(pref : Any) = GetDataBinding({ pref }) { view, flags, data ->
35 | when (flags) {
36 | 0 -> view.visibility = if (data as Boolean) View.VISIBLE else View.GONE
37 | 1 -> view.visibility = if (data as String != context.getString(R.string.not_shrink_icon)) View.VISIBLE else View.GONE
38 | }
39 | }
40 |
41 | // 绘制图标圆角
42 | TextSummaryWithSwitch(TextSummaryV(textId = R.string.draw_round_corner), SwitchView(DataConst.ENABLE_DRAW_ROUND_CORNER))
43 |
44 | // 缩小图标
45 | val shrinkIconItems = mapOf(
46 | 0 to context.getString(R.string.not_shrink_icon),
47 | 1 to context.getString(R.string.shrink_low_resolution_icon),
48 | 2 to context.getString(R.string.shrink_all_icon)
49 | )
50 | val shrinkIconBinding = getDataBinding(shrinkIconItems[context.prefs().get(DataConst.SHRINK_ICON)]!!)
51 | TextWithSpinner(TextV(textId = R.string.shrink_icon), SpinnerV(shrinkIconItems[context.prefs().get(DataConst.SHRINK_ICON)]!!, 180F, dataBindingSend = shrinkIconBinding.bindingSend) {
52 | for (item in shrinkIconItems) {
53 | add(item.value) {
54 | context.prefs().edit { put(DataConst.SHRINK_ICON, item.key) }
55 | }
56 | }
57 | })
58 |
59 | // 为缩小的图标添加模糊背景
60 | TextSummaryWithSwitch(
61 | TextSummaryV(textId = R.string.add_icon_blur_bg),
62 | SwitchView(DataConst.ENABLE_ADD_ICON_BLUR_BG),
63 | dataBindingRecv = shrinkIconBinding.getRecv(1)
64 | )
65 |
66 | // 替换图标获取方式
67 | TextSummaryWithSwitch(
68 | TextSummaryV(textId = R.string.replace_icon, tipsId = R.string.replace_icon_tips),
69 | SwitchView(DataConst.ENABLE_REPLACE_ICON)
70 | )
71 |
72 | // 使用 MIUI 大图标
73 | if (YukiHelper.atLeastMIUI14)
74 | TextSummaryWithSwitch(
75 | TextSummaryV(textId = R.string.use_miui_large_icon),
76 | SwitchView(DataConst.ENABLE_USE_MIUI_LARGE_ICON)
77 | )
78 |
79 | // 使用图标包
80 | val availableIconPacks = IconPackManager(context).getAvailableIconPacks()
81 | TextWithSpinner(
82 | TextV(textId = R.string.use_icon_pack), SpinnerV(availableIconPacks[context.prefs().get(DataConst.ICON_PACK_PACKAGE_NAME)]?:context.getString(
83 | R.string.icon_pack_is_removed)) {
84 | for (item in availableIconPacks) {
85 | add(item.value) {
86 | context.prefs().edit { put(DataConst.ICON_PACK_PACKAGE_NAME, item.key) }
87 | }
88 | }
89 | })
90 |
91 | Line()
92 |
93 | // 忽略应用主动设置的图标
94 | val defaultStyleBinding = getDataBinding(context.prefs().get(DataConst.ENABLE_DEFAULT_STYLE))
95 | TextSummaryWithSwitch(
96 | TextSummaryV(
97 | textId = R.string.default_style,
98 | tipsId = R.string.default_style_tips
99 | ),
100 | SwitchView(DataConst.ENABLE_DEFAULT_STYLE, dataBindingSend = defaultStyleBinding.bindingSend) {
101 | if (it) {
102 | context.toast(context.getString(R.string.custom_scope_message))
103 | }
104 | }
105 | )
106 |
107 | // 配置应用列表
108 | TextSummaryArrow(TextSummaryV(textId = R.string.default_style_list, onClickListener = {
109 | context.startActivity(Intent(context, ConfigAppsActivity::class.java).apply {
110 | putExtra(ConstValue.EXTRA_MESSAGE, ConstValue.DEFAULT_STYLE)
111 | })
112 | }), dataBindingRecv = defaultStyleBinding.getRecv(0))
113 |
114 | Line()
115 |
116 | // 不显示图标
117 | val hideSplashScreenIconBinding = getDataBinding(context.prefs().get(DataConst.ENABLE_HIDE_SPLASH_SCREEN_ICON))
118 | TextSummaryWithSwitch(
119 | TextSummaryV(textId = R.string.hide_splash_screen_icon),
120 | SwitchView(DataConst.ENABLE_HIDE_SPLASH_SCREEN_ICON, dataBindingSend = hideSplashScreenIconBinding.bindingSend) {
121 | if (it) {
122 | context.toast(context.getString(R.string.custom_scope_message))
123 | MainScope().launch {
124 | delay(100)
125 | binding.nestedScrollView.fullScroll(NestedScrollView.FOCUS_DOWN)
126 | }
127 | }
128 | }
129 | )
130 |
131 | // 配置应用列表
132 | TextSummaryArrow(TextSummaryV(textId = R.string.default_style_list, onClickListener = {
133 | context.startActivity(Intent(context, ConfigAppsActivity::class.java).apply {
134 | putExtra(ConstValue.EXTRA_MESSAGE, ConstValue.HIDE_SPLASH_SCREEN_ICON)
135 | })
136 | }), dataBindingRecv = hideSplashScreenIconBinding.getRecv(0))
137 | }
138 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/utils/AppInfoHelper.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.utils
2 |
3 | import android.content.Context
4 | import android.content.pm.ApplicationInfo
5 | import android.graphics.drawable.Drawable
6 |
7 | /**
8 | * 原始位置 [Hide-My-Applist](https://github.com/Dr-TSNG/Hide-My-Applist/blob/d377ab13ddf74a3f1b561fda1631a62e4fbc0ab0/app/src/main/java/com/tsng/hidemyapplist/app/helpers/AppInfoHelper.kt)
9 | */
10 | class AppInfoHelper(private val context: Context, private val checkedList: Set, private val configList: MutableMap) {
11 | private lateinit var appInfoList: MutableList
12 |
13 | /**
14 | * 数据类, 存储应用信息
15 | */
16 | data class MyAppInfo(
17 | val appName: String,
18 | val packageName: String,
19 | val icon: Drawable,
20 | // 该 config 用于存储具体应用配置, 例如最小持续时间, 单独设置的背景颜色等
21 | var config: String?,
22 | // 该 isChecked 用于存储应用是否被勾选, 0 为未勾选, 1 为勾选
23 | var isChecked: Int,
24 | val isSystemApp: Boolean
25 | )
26 |
27 | /**
28 | * 获取应用信息列表
29 | *
30 | * @return [MutableList] 应用信息列表
31 | */
32 | fun getAppInfoList(): MutableList {
33 | if (::appInfoList.isInitialized)
34 | return appInfoList.apply {
35 | sortBy { it.appName }
36 | sortByDescending { it.config }
37 | sortByDescending {it.isChecked }
38 | }.toMutableList()
39 | appInfoList = mutableListOf()
40 | val pm = context.packageManager
41 | for (appInfo in pm.getInstalledApplications(0)) {
42 | MyAppInfo(
43 | appInfo.loadLabel(pm).toString(),
44 | appInfo.packageName,
45 | appInfo.loadIcon(pm),
46 | configList[appInfo.packageName],
47 | if (appInfo.packageName in checkedList) 1 else 0,
48 | appInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0
49 | ).also { appInfoList.add(it) }
50 | }
51 | return appInfoList.apply {
52 | sortBy { it.appName }
53 | sortByDescending { it.config }
54 | sortByDescending { it.isChecked }
55 | }.toMutableList()
56 | }
57 |
58 | /**
59 | * 设置选项的勾选状态 ([MyAppInfo.isChecked])
60 | *
61 | * @param info 应用信息
62 | * @param status 勾选状态
63 | */
64 | fun setChecked(info : MyAppInfo, status : Boolean) {
65 | appInfoList[appInfoList.indexOf(info)].isChecked = if (status) 1 else 0
66 | }
67 |
68 | /**
69 | * 根据应用信息设置其参配置 ([MyAppInfo.config]),
70 | *
71 | * @param info 应用信息
72 | * @param config 参数
73 | */
74 | fun setConfig(info : MyAppInfo, config : String?) {
75 | appInfoList[appInfoList.indexOf(info)].config = config
76 | }
77 |
78 | /**
79 | * 根据索引设置其参配置 ([MyAppInfo.config]),
80 | *
81 | * @param index 应用信息的索引
82 | * @param config 参数
83 | */
84 | fun setConfig(index : Int, config : String?) {
85 | appInfoList[index].config = config
86 | }
87 |
88 | /**
89 | * 获取应用信息的索引
90 | *
91 | * @param info 应用信息
92 | * @return 应用信息的索引
93 | */
94 | fun getIndex(info : MyAppInfo) = appInfoList.indexOf(info)
95 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/utils/BackupUtils.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.utils
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import android.net.Uri
6 | import com.gswxxn.restoresplashscreen.R
7 | import com.gswxxn.restoresplashscreen.data.ConstValue.CREATE_DOCUMENT_CODE
8 | import com.gswxxn.restoresplashscreen.data.ConstValue.OPEN_DOCUMENT_CODE
9 | import com.gswxxn.restoresplashscreen.utils.CommonUtils.toast
10 | import com.highcapable.yukihookapi.hook.factory.prefs
11 | import org.json.JSONObject
12 | import java.io.BufferedReader
13 | import java.io.BufferedWriter
14 | import java.io.InputStreamReader
15 | import java.io.OutputStreamWriter
16 | import java.time.LocalDateTime
17 |
18 | /**
19 | * 改自 [MiuiHomeR](https://github.com/qqlittleice/MiuiHome_R/blob/9f3a298df6427b3a8ea6a47aaabfa0a56c4dd11e/app/src/main/java/com/yuk/miuiHomeR/utils/BackupUtils.kt#L47)
20 | * 用于备份和恢复数据
21 | */
22 | object BackupUtils {
23 |
24 | /**
25 | * 打开文件, 用于选择备份文件
26 | *
27 | * @param activity [Activity]
28 | * @return [Unit]
29 | */
30 | fun openFile(activity: Activity) =
31 | activity.startActivityForResult(Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
32 | addCategory(Intent.CATEGORY_OPENABLE)
33 | type = "application/json"
34 | }, OPEN_DOCUMENT_CODE)
35 |
36 | /**
37 | * 读取文件, 用于恢复数据
38 | *
39 | * @param activity [Activity]
40 | * @return [Unit]
41 | */
42 | fun saveFile(activity: Activity) =
43 | activity.startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
44 | addCategory(Intent.CATEGORY_OPENABLE)
45 | type = "application/json"
46 | putExtra(Intent.EXTRA_TITLE, "RestoreSplashScreen_${LocalDateTime.now()}.json")
47 | }, CREATE_DOCUMENT_CODE)
48 |
49 | /**
50 | * 处理打开文件, 处理并写出数据
51 | *
52 | * @param activity [Activity]
53 | * @param data [Uri]
54 | */
55 | fun handleReadDocument(activity: Activity, data: Uri?) {
56 | val uri = data ?: return
57 | try {
58 | activity.prefs().edit {
59 | clear()
60 | activity.contentResolver.openInputStream(uri)?.let { loadFile ->
61 | BufferedReader(InputStreamReader(loadFile)).apply {
62 | val sb = StringBuffer()
63 | var line = readLine()
64 | do {
65 | sb.append(line)
66 | line = readLine()
67 | } while (line != null)
68 | JSONObject(sb.toString()).apply {
69 | val key = keys()
70 | while (key.hasNext()) {
71 | val keys = key.next()
72 | when (val value = get(keys)) {
73 | is String -> {
74 | if (value.firstOrNull() == '[' && value.lastOrNull() == ']')
75 | putStringSet(
76 | keys,
77 | parseStringArray(value)
78 | )
79 | else putString(keys, value)
80 | }
81 | is Boolean -> putBoolean(keys, value)
82 | is Int -> putInt(keys, value)
83 | }
84 | }
85 | }
86 | close()
87 | }
88 | }
89 | }
90 | activity.finish()
91 | activity.toast(activity.getString(R.string.restore_successful))
92 | } catch (e: Throwable) { activity.toast(activity.getString(R.string.restore_failed)) }
93 | }
94 |
95 | /**
96 | * 处理保存文件, 写出数据
97 | *
98 | * @param activity [Activity]
99 | * @param data [Uri]
100 | */
101 | fun handleCreateDocument(activity: Activity, data: Uri?) {
102 | val uri = data ?: return
103 | try {
104 | activity.contentResolver.openOutputStream(uri)?.let { saveFile ->
105 | BufferedWriter(OutputStreamWriter(saveFile)).apply {
106 | write(JSONObject().also {
107 | for (entry: Map.Entry in activity.prefs().all()) {
108 | it.put(entry.key, entry.value)
109 | }
110 | }.toString())
111 | close()
112 | }
113 | }
114 | activity.toast(activity.getString(R.string.save_successful))
115 | } catch (_: Throwable) { activity.toast(activity.getString(R.string.save_failed)) }
116 | }
117 |
118 | /**
119 | * 解析字符串数组 "[value1, value2, value3]" 为 [MutableSet]
120 | *
121 | * @param value [String]
122 | * @return [MutableSet]<[String]>
123 | */
124 | private fun parseStringArray(value: String) =
125 | value.substring(1, value.lastIndex).split(", ").toMutableSet().apply { remove("") }
126 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/utils/CommonUtils.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.utils
2 |
3 | import android.content.Context
4 | import android.content.res.Configuration
5 | import android.os.Build
6 | import android.widget.Toast
7 | import java.io.DataOutputStream
8 |
9 | /**
10 | * 通用工具类
11 | */
12 | object CommonUtils {
13 |
14 | /**
15 | * 显示 Toast
16 | *
17 | * @receiver 需要显示 Toast 应用的 Context
18 | * @param message 显示文本内容
19 | * @return [Toast]
20 | */
21 | fun Context.toast(message: CharSequence): Toast = Toast
22 | .makeText(this, message, Toast.LENGTH_SHORT)
23 | .apply { show() }
24 |
25 | /**
26 | * 显示长时间 Toast
27 | *
28 | * @receiver [Context] 需要显示 Toast 应用的 Context
29 | * @param message 显示文本内容
30 | * @return [Toast]
31 | */
32 | fun Context.toastL(message: CharSequence): Toast = Toast
33 | .makeText(this, message, Toast.LENGTH_LONG)
34 | .apply { show() }
35 |
36 | /**
37 | * 执行 Shell 命令
38 | * @param command Shell 命令
39 | */
40 | fun execShell(command: String) {
41 | try {
42 | val p = Runtime.getRuntime().exec("su")
43 | val outputStream = p.outputStream
44 | val dataOutputStream = DataOutputStream(outputStream)
45 | dataOutputStream.writeBytes(command)
46 | dataOutputStream.flush()
47 | dataOutputStream.close()
48 | outputStream.close()
49 | } catch (t: Throwable) {
50 | t.printStackTrace()
51 | }
52 | }
53 |
54 | /**
55 | * 将值为类似 <[String]_[String]> 的 Set 转换成 <[String], [String]> 的 Map
56 | */
57 | fun Set.toMap(): MutableMap {
58 | val result = mutableMapOf()
59 | forEach { item ->
60 | val separatorIndex = item.lastIndexOf("_")
61 | if (separatorIndex != -1 && separatorIndex < item.length - 1) {
62 | val packageName = item.substring(0, separatorIndex)
63 | val duration = item.substring(separatorIndex + 1)
64 | result[packageName] = duration
65 | }
66 | }
67 | return result
68 | }
69 |
70 | /**
71 | * 将类似 <[String], [String]> 的 Map 转换成值为类似 <[String]_[String]> 的 Set
72 | */
73 | fun MutableMap.toSet(): MutableSet {
74 | val result = mutableSetOf()
75 | forEach { (key, value) ->
76 | result += "${key}_${value}"
77 | }
78 | return result
79 | }
80 |
81 | /**
82 | * 比较两个 [Collection] 的内容是否相同
83 | */
84 | infix fun Collection<*>.notEqualsTo(second: Collection<*>): Boolean = !(this equalTo second)
85 | private infix fun Collection<*>.equalTo(second: Collection<*>): Boolean {
86 | if (size != second.size) return false
87 | forEach { if (it !in second) return false }
88 | return true
89 | }
90 |
91 | /**
92 | * 检查 SDK 版本
93 | */
94 | val isAtLeastT = Build.VERSION.SDK_INT >= 33
95 |
96 | /**
97 | * 是否处于深色模式
98 | */
99 | fun isDarkMode(context: Context) = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
100 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/gswxxn/restoresplashscreen/utils/YukiHelper.kt:
--------------------------------------------------------------------------------
1 | package com.gswxxn.restoresplashscreen.utils
2 |
3 | import android.content.Context
4 | import com.gswxxn.restoresplashscreen.data.DataConst
5 | import com.gswxxn.restoresplashscreen.hook.NewSystemUIHooker.toClass
6 | import com.gswxxn.restoresplashscreen.hook.base.BaseHookHandler
7 | import com.gswxxn.restoresplashscreen.hook.base.HookManager
8 | import com.gswxxn.restoresplashscreen.utils.CommonUtils.toMap
9 | import com.highcapable.yukihookapi.hook.core.finder.members.FieldFinder.Result.Instance
10 | import com.highcapable.yukihookapi.hook.entity.YukiBaseHooker
11 | import com.highcapable.yukihookapi.hook.factory.current
12 | import com.highcapable.yukihookapi.hook.factory.dataChannel
13 | import com.highcapable.yukihookapi.hook.factory.hasClass
14 | import com.highcapable.yukihookapi.hook.factory.method
15 | import com.highcapable.yukihookapi.hook.log.YLog
16 | import com.highcapable.yukihookapi.hook.type.java.StringClass
17 | import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookPrefsBridge
18 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
19 | import kotlinx.serialization.encodeToString
20 | import kotlinx.serialization.json.Json
21 |
22 | /**
23 | * YukiHookAPI 工具类 */
24 | object YukiHelper {
25 | /**
26 | * 读取 MapPrefs
27 | *
28 | * @param p [PrefsData] 实例
29 | * @return [MutableMap]
30 | */
31 | fun YukiBaseHooker.getMapPrefs(p: PrefsData>) = prefs.get(p).toMap()
32 | /**
33 | * 读取 MapPrefs
34 | *
35 | * @param p [PrefsData] 实例
36 | * @return [MutableMap]
37 | */
38 | fun BaseHookHandler.getMapPrefs(p: PrefsData>) = prefs.get(p).toMap()
39 |
40 | /**
41 | * 根据名称获取实例 的 Field 实例处理类
42 | *
43 | * 需要获取 Field 的实例
44 | * @param fieldName Field 名称
45 | * @return [Instance]
46 | */
47 | @JvmName("getFieldAny")
48 | fun Any.getField(fieldName : String) = current().field { name = fieldName }.any()
49 |
50 | /**
51 | * 根据名称获取实例 的 Field 实例处理类, 并转换为指定类型
52 | *
53 | * 需要获取 Field 的实例
54 | * @param fieldName Field 名称
55 | * @return [Instance]
56 | */
57 | fun Any.getField(fieldName : String) = current().field { name = fieldName }.cast()
58 |
59 | /**
60 | * 根据名称设置实例 的 Field 实例内容
61 | *
62 | * 需要设置 Field 的实例
63 | * @param fieldName Field 名称
64 | * @param value 设置的实例内容
65 | */
66 | fun Any.setField(fieldName : String, value : Any?) = current().field { name = fieldName }.set(value)
67 |
68 | /**
69 | * 通过 DataChannel 发送消息,获取 Hook 信息
70 | * @param packageName 宿主包名
71 | * @param result 结果回调
72 | */
73 | fun Context.getHookInfo(packageName: String, result: (Map) -> Unit) {
74 | dataChannel(packageName).wait