├── .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 | icon 2 | 3 | # 启动遮罩进化 4 | 5 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/ffbf5a0bdf954416a2e1d4347b1ea797)](https://app.codacy.com/gh/GSWXXN/RestoreSplashScreen?utm_source=github.com&utm_medium=referral&utm_content=GSWXXN/RestoreSplashScreen&utm_campaign=Badge_Grade) 6 | [![Xposed](https://img.shields.io/badge/-Xposed-green?style=flat&logo=Android&logoColor=white)](https://github.com/Xposed-Modules-Repo/com.gswxxn.restoresplashscreen/) 7 | [![GitHub](https://img.shields.io/github/license/GSWXXN/RestoreSplashScreen)](https://github.com/GSWXXN/RestoreSplashScreen/blob/master/LICENSE) 8 | [![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/GSWXXN/RestoreSplashScreen?label=version)](https://github.com/Xposed-Modules-Repo/com.gswxxn.restoresplashscreen/releases) 9 | [![GitHub all releases](https://img.shields.io/github/downloads/Xposed-Modules-Repo/com.gswxxn.restoresplashscreen/total?label=Downloads)](https://github.com/Xposed-Modules-Repo/com.gswxxn.restoresplashscreen/releases) 10 | [![Telegram CI](https://img.shields.io/badge/CI%20builds-Telegram-blue.svg?logo=telegram)](https://t.me/GSWXXN_Channel) 11 | [![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram)](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 | donate 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>("${packageName.replace('.', '_')}_hook_info_result") { 75 | val hookInfo = mutableMapOf() 76 | it.forEach { (key, hookInfoJson) -> 77 | hookInfo += key to Json.decodeFromString(hookInfoJson) 78 | } 79 | result(hookInfo) 80 | } 81 | dataChannel(packageName).put("${packageName.replace('.', '_')}_hook_info_get") 82 | } 83 | 84 | /** 85 | * 宿主注册接收获取HookInfo 的 DataChannel 通知 86 | */ 87 | fun YukiBaseHooker.registerHookInfo(membersObject: Any) { 88 | dataChannel.wait(key = "${packageName.replace('.', '_')}_hook_info_get") { 89 | val hookInfo: Map = membersObject.javaClass.declaredFields 90 | .filter { field -> field.apply { isAccessible = true }.get(null) is HookManager } 91 | .associateBy( 92 | { field -> field.name }, 93 | { field -> Json.encodeToString((field.get(null) as HookManager).getHookInfo()) }) 94 | dataChannel.put( 95 | key = "${packageName.replace('.', '_')}_hook_info_result", 96 | value = hookInfo 97 | ) 98 | } 99 | } 100 | 101 | /** 102 | * 打印日志 103 | */ 104 | fun YukiBaseHooker.printLog(vararg msg: String) = printLog(prefs, *msg) 105 | 106 | /** 107 | * 打印日志 108 | */ 109 | fun BaseHookHandler.printLog(vararg msg: String) = printLog(prefs, *msg) 110 | 111 | /** 112 | * 打印日志 113 | */ 114 | fun printLog(prefs: YukiHookPrefsBridge, vararg msg: String) { 115 | if (!prefs.get(DataConst.ENABLE_LOG)) return 116 | if (System.currentTimeMillis() - prefs.get(DataConst.ENABLE_LOG_TIMESTAMP) > 86400000) return 117 | msg.forEach { YLog.info(it) } 118 | } 119 | /** 120 | * 当前设备是否是 MIUI 定制 Android 系统 121 | * @return [Boolean] 是否符合条件 122 | */ 123 | val isMIUI by lazy { "android.miui.R".hasClass() } 124 | 125 | /** 126 | * 检测 MIUI 版本是否至少为 14 127 | * 128 | * @return [Boolean] 是否符合条件 129 | */ 130 | val atLeastMIUI14 by lazy { 131 | isMIUI && try { 132 | "android.os.SystemProperties".toClass().method { 133 | name = "get" 134 | param(StringClass) 135 | }.get().invoke("ro.miui.ui.version.code") ?: "" 136 | } catch (e: Throwable) { 137 | "" 138 | }.toInt() >= 14 139 | } 140 | 141 | /** 142 | * 当前设备是否是 ColorOS 定制 Android 系统 143 | * @return [Boolean] 是否符合条件 144 | */ 145 | val isColorOS by lazy { "oppo.R".hasClass() || "com.color.os.ColorBuild".hasClass() || "oplus.R".hasClass() } 146 | 147 | /** 148 | * 加载 HookHandler 149 | */ 150 | fun YukiBaseHooker.loadHookHandler(vararg hookHandler: BaseHookHandler) { 151 | hookHandler.forEach { 152 | it.baseHooker = this 153 | it.onHook() 154 | } 155 | } 156 | 157 | /** 158 | * 获取 开发者选项 Prefs 值 159 | */ 160 | inline fun BaseHookHandler.getDevPrefs(prefsData: PrefsData): T { 161 | if (prefs.get(DataConst.ENABLE_DEV_SETTINGS)) { 162 | return prefs.get(prefsData) 163 | } 164 | return prefsData.value 165 | } 166 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gswxxn/restoresplashscreen/view/BlockMIUIItemData.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("FunctionName") 2 | 3 | package com.gswxxn.restoresplashscreen.view 4 | 5 | import android.graphics.Typeface 6 | import android.graphics.drawable.Drawable 7 | import android.view.View 8 | import android.widget.TextView 9 | import cn.fkj233.ui.activity.data.DataBinding 10 | import cn.fkj233.ui.activity.data.Padding 11 | import cn.fkj233.ui.activity.view.* 12 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData 13 | 14 | /** 15 | * 复制自 [cn.fkj233.ui.activity.data.InitView], 后续可能会使用更优办法实现, 而不是复制整个类 16 | */ 17 | class BlockMIUIItemData { 18 | companion object { 19 | val datalist = mutableMapOf>() 20 | } 21 | val itemList: ArrayList = arrayListOf() 22 | private var bindingData = arrayListOf() 23 | 24 | fun GetDataBinding(defValue: () -> Any, recvCallbacks: (View, Int, Any) -> Unit): DataBinding.BindingData { 25 | return DataBinding.get(bindingData, defValue, recvCallbacks) 26 | } 27 | 28 | fun Author(authorHead: Drawable, authorName: String, authorTips: String? = null, round: Float = 30f, onClickListener: (() -> Unit)? = null, dataBindingRecv: DataBinding.Binding.Recv? = null) { 29 | itemList.add(ImageTextV(authorHead, authorName, authorTips, round, onClickListener, dataBindingRecv)) 30 | } 31 | 32 | fun Page(pageHead: Drawable, pageName: String?, pageNameId: Int?, round: Float = 0f, onClickListener: (() -> Unit)? = null, dataBindingRecv: DataBinding.Binding.Recv? = null) { 33 | itemList.add(PageV(pageHead, pageName, pageNameId, round, onClickListener, dataBindingRecv)) 34 | } 35 | 36 | fun Line() { 37 | itemList.add(LineV()) 38 | } 39 | 40 | fun SeekBar(key: String, min: Int, max: Int, defaultProgress: Int, dataSend: DataBinding.Binding.Send? = null, dataBindingRecv: DataBinding.Binding.Recv? = null, callBacks: ((Int, TextView) -> Unit)? = null) { 41 | itemList.add(SeekBarV(key, min, max, defaultProgress, dataSend, dataBindingRecv, callBacks)) 42 | } 43 | 44 | fun TextSummaryWithSpinner(textV: TextSummaryV, spinnerV: SpinnerV, dataBindingRecv: DataBinding.Binding.Recv? = null) { 45 | itemList.add(TextSummaryWithSpinnerV(textV, spinnerV, dataBindingRecv)) 46 | } 47 | 48 | fun Text(text: String? = null, textId: Int? = null, textSize: Float? = null, colorInt: Int? = null, colorId: Int? = null, padding: Padding? = null, dataBindingRecv: DataBinding.Binding.Recv? = null, typeface: Typeface? = null, onClickListener: (() -> Unit)? = null) { 49 | itemList.add(TextV(text, textId, textSize, colorInt, colorId, padding, dataBindingRecv, typeface, onClickListener)) 50 | } 51 | 52 | fun SeekBarWithText(key: String = "", min: Int, max: Int, defaultProgress: Int = 0, dataBindingRecv: DataBinding.Binding.Recv? = null, dataBindingSend: DataBinding.Binding.Send? = null, callBacks: ((Int, TextView) -> Unit)? = null) { 53 | itemList.add(SeekBarWithTextV(key, min, max, defaultProgress, dataBindingRecv, dataBindingSend, callBacks)) 54 | } 55 | 56 | fun TextSummaryArrow(textSummaryV: TextSummaryV, dataBindingRecv: DataBinding.Binding.Recv? = null) { 57 | itemList.add(TextSummaryWithArrowV(textSummaryV, dataBindingRecv)) 58 | } 59 | 60 | fun TextA(text: String? = null, textId: Int? = null, onClickListener: (() -> Unit)? = null, dataBindingRecv: DataBinding.Binding.Recv? = null) { 61 | itemList.add(TextSummaryWithArrowV(TextSummaryV(text, textId, onClickListener = onClickListener), dataBindingRecv)) 62 | } 63 | 64 | fun TextSummaryWithSwitch(textSummaryV: TextSummaryV, switchView: SwitchView, dataBindingRecv: DataBinding.Binding.Recv? = null) { 65 | itemList.add(TextSummaryWithSwitchView(textSummaryV, switchView, dataBindingRecv)) 66 | } 67 | 68 | fun TitleText(text: String? = null, textId: Int? = null,colorInt: Int? = null, colorId: Int? = null, dataBindingRecv: DataBinding.Binding.Recv? = null, onClickListener: (() -> Unit)? = null) { 69 | itemList.add(TitleTextV(text, textId,colorInt, colorId,dataBindingRecv, onClickListener)) 70 | } 71 | 72 | fun TextWithSwitch(textV: TextV, switchV: SwitchV, dataBindingRecv: DataBinding.Binding.Recv? = null) { 73 | itemList.add(TextWithSwitchV(textV, switchV, dataBindingRecv)) 74 | } 75 | 76 | fun TextS(text: String? = null, textId: Int? = null, key: String, defValue: Boolean=false, onClickListener: ((Boolean) -> Unit)? = null, dataBindingRecv: DataBinding.Binding.Recv? = null) { 77 | itemList.add(TextWithSwitchV(TextV(text, textId), SwitchV(key, defValue, onClickListener = onClickListener), dataBindingRecv)) 78 | } 79 | 80 | fun TextWithSpinner(textV: TextV, spinnerV: SpinnerV, dataBindingRecv: DataBinding.Binding.Recv? = null) { 81 | itemList.add(TextWithSpinnerV(textV, spinnerV, dataBindingRecv)) 82 | } 83 | 84 | fun CustomView(view: View, dataBindingRecv: DataBinding.Binding.Recv? = null) { 85 | itemList.add(CustomViewV(view, dataBindingRecv)) 86 | } 87 | 88 | fun RadioView(key: String, dataBindingRecv: DataBinding.Binding.Recv? = null, data: RadioViewV.RadioData.() -> Unit) { 89 | itemList.add(RadioViewV(key, dataBindingRecv, data)) 90 | } 91 | 92 | fun TextSummary(text: String? = null, textId: Int? = null, tips: String? = null, colorInt: Int? = null, colorId: Int? = null, tipsId: Int? = null, dataBindingRecv: DataBinding.Binding.Recv? = null, onClickListener: (() -> Unit)? = null) { 93 | itemList.add(TextSummaryV(text, textId, tips, colorInt, colorId, tipsId, dataBindingRecv, onClickListener)) 94 | } 95 | 96 | fun SeekBarWithStatus( 97 | titleID: Int, 98 | pref: PrefsData? = null, 99 | min: Int, 100 | max: Int, 101 | defaultProgress: Int = 0, 102 | isPercentage: Boolean = false, 103 | progressColor: Int = 0xFF0d7AEC.toInt(), 104 | drawHuePanel: Boolean = false, 105 | dataBindingRecv: DataBinding.Binding.Recv? = null, 106 | dataBindingSend: DataBinding.Binding.Send? = null, 107 | onProgressChanged: ((value: Int) -> Unit)? = null 108 | ) { 109 | itemList.add(SeekBarWithTitleView(titleID, pref, min, max, defaultProgress, isPercentage, progressColor, drawHuePanel, dataBindingRecv, dataBindingSend, onProgressChanged)) 110 | } 111 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gswxxn/restoresplashscreen/view/SwitchView.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * BlockMIUI 3 | * Copyright (C) 2022 fkj@fkj233.cn 4 | * https://github.com/577fkj/BlockMIUI 5 | * 6 | * This software is free opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Lesser General Public License v2.1 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version and our eula as published 10 | * by 577fkj. 11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * GNU Lesser General Public License v2.1 for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License v2.1 18 | * and eula along with this software. If not, see 19 | * 20 | * . 21 | */ 22 | 23 | package com.gswxxn.restoresplashscreen.view 24 | 25 | 26 | import android.content.Context 27 | import android.view.View 28 | import android.widget.Toast 29 | import cn.fkj233.ui.activity.data.DataBinding 30 | import cn.fkj233.ui.activity.view.BaseView 31 | import cn.fkj233.ui.switch.MIUISwitch 32 | import com.gswxxn.restoresplashscreen.R 33 | import com.highcapable.yukihookapi.YukiHookAPI 34 | import com.highcapable.yukihookapi.hook.factory.prefs 35 | import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData 36 | 37 | /** 38 | * 改自 BlockMIUI, 为了适配 YukiHookAPI 的配置存储方式, 后续可能通过反射实现而不是把这个类复制过来 39 | */ 40 | class SwitchView( 41 | private val pref: PrefsData, 42 | private val dataBindingRecv: DataBinding.Binding.Recv? = null, 43 | private val dataBindingSend: DataBinding.Binding.Send? = null, 44 | private val onClickListener: ((Boolean) -> Unit)? = null 45 | ): BaseView { 46 | private lateinit var context : Context 47 | 48 | lateinit var switch: MIUISwitch 49 | 50 | override fun getType(): BaseView = this 51 | 52 | override fun create(context: Context, callBacks: (() -> Unit)?): View { 53 | return MIUISwitch(context).also { 54 | switch = it 55 | dataBindingRecv?.setView(it) 56 | this.context = context 57 | it.isChecked = context.prefs().get(pref) 58 | it.setOnCheckedChangeListener { v, b -> 59 | if (!YukiHookAPI.Status.isXposedModuleActive) { 60 | v.isChecked = !b 61 | Toast.makeText(context, R.string.make_sure_active, Toast.LENGTH_SHORT).show() 62 | } else { 63 | dataBindingSend?.let { send -> 64 | send.send(b) 65 | } 66 | callBacks?.let { it1 -> it1() } 67 | onClickListener?.let { it(b) } 68 | context.prefs().edit { put(pref, b) } 69 | } 70 | } 71 | } 72 | } 73 | 74 | /** 切换开关状态 */ 75 | fun click() { switch.isChecked = !switch.isChecked } 76 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gswxxn/restoresplashscreen/view/TextSummaryWithSwitchView.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * BlockMIUI 3 | * Copyright (C) 2022 fkj@fkj233.cn 4 | * https://github.com/577fkj/BlockMIUI 5 | * 6 | * This software is free opensource software: you can redistribute it 7 | * and/or modify it under the terms of the GNU Lesser General Public License v2.1 8 | * as published by the Free Software Foundation; either 9 | * version 3 of the License, or any later version and our eula as published 10 | * by 577fkj. 11 | * 12 | * This software is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * GNU Lesser General Public License v2.1 for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License v2.1 18 | * and eula along with this software. If not, see 19 | * 20 | * . 21 | */ 22 | 23 | package com.gswxxn.restoresplashscreen.view 24 | 25 | import android.content.Context 26 | import android.view.Gravity 27 | import android.view.View 28 | import android.widget.LinearLayout 29 | import cn.fkj233.ui.activity.data.DataBinding 30 | import cn.fkj233.ui.activity.data.LayoutPair 31 | import cn.fkj233.ui.activity.dp2px 32 | import cn.fkj233.ui.activity.view.BaseView 33 | import cn.fkj233.ui.activity.view.LinearContainerV 34 | import cn.fkj233.ui.activity.view.TextSummaryV 35 | 36 | /** 37 | * 改自 BlockMIUI, 为了将适应 [SwitchView] 38 | */ 39 | class TextSummaryWithSwitchView(private val textV: TextSummaryV, val switchV: SwitchView, private val dataBindingRecv: DataBinding.Binding.Recv? = null): BaseView { 40 | 41 | override fun getType(): BaseView = this 42 | 43 | override fun create(context: Context, callBacks: (() -> Unit)?): View { 44 | textV.notShowMargins(true) 45 | return LinearContainerV( 46 | LinearContainerV.HORIZONTAL, 47 | arrayOf( 48 | LayoutPair(textV.create(context, callBacks), LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)), 49 | LayoutPair(switchV.create(context, callBacks), LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT).also { it.gravity = Gravity.CENTER_VERTICAL }) 50 | ), 51 | layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT).also { 52 | it.setMargins(0, dp2px(context, 17.75f),0, dp2px(context, 17.75f)) 53 | } 54 | ).create(context, callBacks).also { 55 | dataBindingRecv?.setView(it) 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gswxxn/restoresplashscreen/wrapper/SplashScreenViewBuilderWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.gswxxn.restoresplashscreen.wrapper 2 | 3 | import android.graphics.drawable.Drawable 4 | import com.highcapable.yukihookapi.hook.factory.current 5 | 6 | /** 7 | * SplashScreenView.Builder 的包装类 8 | */ 9 | class SplashScreenViewBuilderWrapper private constructor(private val builder: Any) { 10 | 11 | companion object { 12 | private val instances: MutableMap = mutableMapOf() 13 | 14 | /** 15 | * 获取给定的 SplashScreenView.Builder 对应的单例实例,如果已经存在则返回现有实例, 16 | * 如果不存在则创建新的实例并进行缓存。 17 | * 18 | * @param builder SplashScreenViewBuilder 对象,用于唯一标识需要的包装实例。 19 | * @return 对应的 SplashScreenViewBuilderWrapper 实例。 20 | * @throws IllegalArgumentException 如果传递的 builder 不是有效的 SplashScreenView.Builder 实例 21 | */ 22 | fun getInstance(builder: Any): SplashScreenViewBuilderWrapper { 23 | if (builder.javaClass.name != "android.window.SplashScreenView\$Builder") { 24 | throw IllegalArgumentException("Builder must be of type SplashScreenViewBuilder") 25 | } 26 | 27 | return instances.getOrPut(builder) { 28 | SplashScreenViewBuilderWrapper(builder) 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * Get the rectangle size for the center view. 35 | */ 36 | fun getIconSize() = 37 | builder.current().field { name = "mIconSize" }.int() 38 | 39 | /** 40 | * Get the background color for the view. 41 | */ 42 | fun getBackgroundColor() = 43 | builder.current().field { name = "mBackgroundColor" }.int() 44 | 45 | /** 46 | * Get the Drawable object to fill the entire view. 47 | */ 48 | fun getOverlayDrawable() = 49 | builder.current().field { name = "mOverlayDrawable" }.cast() 50 | 51 | /** 52 | * Get the Drawable object to fill the center view. 53 | */ 54 | fun getCenterViewDrawable() = 55 | builder.current().field { name = "mIconDrawable" }.cast() 56 | 57 | /** 58 | * Get the background color for the icon. 59 | */ 60 | fun getIconBackground() = 61 | builder.current().field { name = "mIconBackground" }.cast() 62 | 63 | /** 64 | * Get the Drawable object and size for the branding view. 65 | */ 66 | fun getBrandingDrawable() = 67 | builder.current().field { name = "mBrandingDrawable" }.cast() 68 | 69 | /** 70 | * Get whether this view can be copied and transferred to the client if the view is 71 | * an empty style splash screen. 72 | */ 73 | fun getAllowHandleSolidColor() = 74 | builder.current().field { name = "mAllowHandleSolidColor" }.boolean() 75 | 76 | /** 77 | * Set the rectangle size for the center view. 78 | */ 79 | fun setIconSize(iconSize: Int) = 80 | builder.current().method { name = "setIconSize" }.call(iconSize) 81 | 82 | /** 83 | * Set the background color for the view. 84 | */ 85 | fun setBackgroundColor(backgroundColor: Int) = 86 | builder.current().method { name = "setBackgroundColor" }.call(backgroundColor) 87 | 88 | /** 89 | * Set the Drawable object to fill the entire view. 90 | */ 91 | fun setOverlayDrawable(drawable: Drawable?) = 92 | builder.current().method { name = "setOverlayDrawable" }.call(drawable) 93 | 94 | /** 95 | * Set the Drawable object to fill the center view. 96 | */ 97 | fun setCenterViewDrawable(drawable: Drawable?) = 98 | builder.current().method { name = "setCenterViewDrawable" }.call(drawable) 99 | 100 | /** 101 | * Set the background color for the icon. 102 | */ 103 | fun setIconBackground(iconBackground: Drawable) = 104 | builder.current().method { name = "setIconBackground" }.call(iconBackground) 105 | 106 | /** 107 | * Set the Drawable object and size for the branding view. 108 | */ 109 | fun setBrandingDrawable(branding: Drawable?, width: Int, height: Int) = 110 | builder.current().method { name = "setBrandingDrawable" }.call(branding, width, height) 111 | 112 | /** 113 | * Sets whether this view can be copied and transferred to the client if the view is 114 | * an empty style splash screen. 115 | */ 116 | fun setAllowHandleSolidColor(allowHandleSolidColor: Boolean) = 117 | builder.current().method { name = "setAllowHandleSolidColor" }.call(allowHandleSolidColor) 118 | 119 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gswxxn/restoresplashscreen/wrapper/TransparentAdaptiveIconDrawable.kt: -------------------------------------------------------------------------------- 1 | package com.gswxxn.restoresplashscreen.wrapper 2 | 3 | import android.graphics.Bitmap 4 | import android.graphics.BitmapShader 5 | import android.graphics.Canvas 6 | import android.graphics.Color 7 | import android.graphics.Paint 8 | import android.graphics.Path 9 | import android.graphics.PorterDuff 10 | import android.graphics.Shader 11 | import android.graphics.drawable.AdaptiveIconDrawable 12 | import android.graphics.drawable.ColorDrawable 13 | import android.graphics.drawable.Drawable 14 | import com.highcapable.yukihookapi.hook.factory.current 15 | 16 | /** 17 | * 透明背景的 AdaptiveIconDrawable 18 | */ 19 | class TransparentAdaptiveIconDrawable( 20 | foregroundDrawable: Drawable 21 | ) : AdaptiveIconDrawable(ColorDrawable(Color.TRANSPARENT), foregroundDrawable) { 22 | private var mLayersShader: Shader? 23 | get() = this.current().field { 24 | name = "mLayersShader" 25 | superClass() 26 | }.cast() 27 | set(value) { 28 | this.current().field { 29 | name = "mLayersShader" 30 | superClass() 31 | }.set(value) 32 | } 33 | private val mCanvas 34 | get() = this.current().field { 35 | name = "mCanvas" 36 | superClass() 37 | }.cast()!! 38 | private val mLayersBitmap 39 | get() = this.current().field { 40 | name = "mLayersBitmap" 41 | superClass() 42 | }.cast() 43 | private val mPaint 44 | get() = this.current().field { 45 | name = "mPaint" 46 | superClass() 47 | }.cast()!! 48 | private val mMaskScaleOnly 49 | get() = this.current().field { 50 | name = "mMaskScaleOnly" 51 | superClass() 52 | }.cast() 53 | 54 | /** 55 | * 继承修改自 AdaptiveIconDrawable 56 | * 详见 [AdaptiveIconDrawable.draw] 57 | */ 58 | override fun draw(canvas: Canvas) { 59 | if (mLayersBitmap == null) { 60 | return 61 | } 62 | if (mLayersShader == null) { 63 | // 修改为透明色清空画布 64 | mCanvas.setBitmap(mLayersBitmap) 65 | mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) 66 | 67 | // 绘制背景图层 68 | background?.draw(mCanvas) 69 | 70 | // 绘制前景图层 71 | foreground?.setBounds(0, 0, bounds.width(), bounds.height()) 72 | foreground?.draw(mCanvas) 73 | 74 | // 创建位图着色器 75 | mLayersShader = 76 | BitmapShader(mLayersBitmap!!, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) 77 | mPaint.setShader(mLayersShader) 78 | } 79 | if (mMaskScaleOnly != null) { 80 | canvas.translate(bounds.left.toFloat(), bounds.top.toFloat()) 81 | canvas.drawPath(mMaskScaleOnly!!, mPaint) 82 | canvas.translate(-bounds.left.toFloat(), -bounds.top.toFloat()) 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /app/src/main/res/animator/dialog_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/animator/dialog_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_button_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_dark_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_demo_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_green_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_permotion_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_yellow_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/demo_background.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 17 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/demo_basic.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 17 | 20 | 23 | 26 | 29 | 32 | 35 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/demo_branding.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 17 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/demo_display.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 17 | 20 | 23 | 26 | 29 | 32 | 35 | 38 | 41 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/demo_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 17 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/demo_scope.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 17 | 20 | 23 | 26 | 29 | 32 | 35 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/demo_transparency.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_app.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bottom.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_collimation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_color.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_help.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_lab.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_monitor.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_more.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_picture.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_restart.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_setting.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_success.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_warn.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_github.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/thumb_seek.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_color_select.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 28 | 29 | 38 | 39 | 50 | 51 | 52 | 53 | 58 | 59 | 63 | 64 | 70 | 71 | 76 | 77 | 78 | 85 | 86 | 92 | 93 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 113 | 114 | 115 | 116 | 123 | 124 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_sub_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 28 | 29 | 38 | 39 | 50 | 51 | 52 | 59 | 60 | 64 | 65 | 66 | 67 | 68 | 76 | 77 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 22 | 23 | 29 | 30 | 38 | 39 | 48 | 49 | 50 | 59 | 60 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/menu/more_options_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/img_developer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GSWXXN/RestoreSplashScreen/c96c809a294721c8635c3a886ced754f5e9597a1/app/src/main/res/mipmap-xxhdpi/img_developer.jpg -------------------------------------------------------------------------------- /app/src/main/res/values-night/color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF000000 4 | #FF2D2D2D 5 | #FFCFCFCF 6 | #FFD3D3D3 7 | 8 | #FF1E70C2 9 | #FF636363 10 | #FF646464 11 | #FF585858 12 | #FF262626 13 | #FFD3D3D3 14 | #FFD3D3D3 15 | #FFFFFF 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/app_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | com.android.systemui 5 | android 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFFFF 4 | #FFF5F5F5 5 | #FF777777 6 | #FF323B42 7 | 8 | #FF69A5DA 9 | #FFD9DCE8 10 | #FFD3D6E4 11 | #FFC0C4D8 12 | #FFFFFFFF 13 | #FF666666 14 | #FF333333 15 | #FF000000 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFFFF 4 | #00000000 5 | #228B22 6 | #FF7043 7 | #9E9E9E 8 | #B22222 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #21A2EE 4 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | autowire(libs.plugins.com.android.application) apply false 3 | autowire(libs.plugins.org.jetbrains.kotlin.android) apply false 4 | } 5 | -------------------------------------------------------------------------------- /doc/Privacy.md: -------------------------------------------------------------------------------- 1 | # MIUI遮罩进化 隐私政策 2 | 3 | 欢迎您访问我们的产品。 **MIUI遮罩进化** (包括App等产品提供的服务,以下简称“产品和服务”)是由 **迷璐** (以下简称“我们”)开发并运营的。 4 | 确保用户的数据安全和隐私保护是我们的首要任务, 5 | 本隐私政策载明了您访问和使用我们的产品和服务时所收集的数据及其处理方式。 6 | 7 | 请您在继续使用我们的产品前务必认真仔细阅读并确认充分理解本隐私政策全部规则和要点, 8 | 一旦您选择使用,即视为您同意本隐私政策的全部内容,同意我们按其收集和使用您的相关信息。 9 | 如您在在阅读过程中,对本政策有任何疑问,可联系我们的客服咨询, 请通过 **mi.lu\@qq.com** 或产品中的反馈方式与我们取得联系。 10 | 如您不同意相关协议或其中的任何条款的,您应停止使用我们的产品和服务。 11 | 12 | 本隐私政策帮助您了解以下内容: 13 | 14 | 一、我们如何收集和使用您的个人信息; 15 | 16 | 二、我们如何存储和保护您的个人信息; 17 | 18 | 三、我们如何共享、转让、公开披露您的个人信息; 19 | 20 | ## 一、我们如何收集和使用您的个人信息 21 | 22 | 个人信息是指以电子或者其他方式记录的能够单独或者与其他信息, 结合识别特定自然人身份或者反映特定自然人活动情况的各种信息。 23 | 由于我们的产品和服务并不需要此类信息,因此很高兴的告知您, 我们不会收集关于您的任何个人信息。 24 | 25 | ## 二、我们如何存储和保护您的个人信息 26 | 27 | 作为一般规则,我们仅在实现信息收集目的所需的时间内保留您的个人信息。 我们会在对于管理与您之间的关系严格必要的时间内保留您的个人信息 28 | (例如,当您开立帐户,从我们的产品获取服务时)。 29 | 出于遵守法律义务或为证明某项权利或合同满足适用的诉讼时效要求的目的, 我们可能需要在上述期限到期后保留您存档的个人信息,并且无法按您的要求删除。 30 | 31 | ## 三、我们如何共享、转让、公开披露您的个人信息 32 | 33 | 在管理我们的日常业务活动所需要时,为追求合法利益以更好地服务客户, 我们将合规且恰当的使用您的个人信息。出于对业务和各个方面的综合考虑, 我们仅自身使用这些数据,不与任何第三方分享。 34 | 35 | 我们可能会根据法律法规规定,或按政府主管部门的强制性要求,对外共享您的个人信息。 在符合法律法规的前提下,当我们收到上述披露信息的请求时, 36 | 我们会要求必须出具与之相应的法律文件,如传票或调查函。 我们坚信,对于要求我们提供的信息,应该在法律允许的范围内尽可能保持透明。 37 | 38 | 在以下情形中,共享、转让、公开披露您的个人信息无需事先征得您的授权同意: 39 | 40 | 1、与国家安全、国防安全直接相关的; 41 | 42 | 2、与犯罪侦查、起诉、审判和判决执行等直接相关的; 43 | 44 | 3、出于维护您或其他个人的生命、财产等重大合法权益但又很难得到本人同意的; 45 | 46 | 4、您自行向社会公众公开的个人信息; 47 | 48 | 5、从合法公开披露的信息中收集个人信息的,如合法的新闻报道、政府信息公开等渠道。 49 | 50 | 6、根据个人信息主体要求签订和履行合同所必需的; 51 | 52 | 7、用于维护所提供的产品或服务的安全稳定运行所必需的,例如发现、处置产品或服务的故障; 53 | 54 | 8、法律法规规定的其他情形。 55 | -------------------------------------------------------------------------------- /doc/donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GSWXXN/RestoreSplashScreen/c96c809a294721c8635c3a886ced754f5e9597a1/doc/donate.png -------------------------------------------------------------------------------- /doc/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=false 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | #android.enableBuildConfigAsBytecode=true 23 | org.gradle.configuration-cache=true 24 | 25 | -------------------------------------------------------------------------------- /gradle/sweet-dependency/sweet-dependency-config.yaml: -------------------------------------------------------------------------------- 1 | # SweetDependency project configuration file 2 | # You can adjust your custom configuration to your liking here 3 | # You can visit https://github.com/HighCapable/SweetDependency for more help 4 | # 5 | # SweetDependency 项目配置文件 6 | # 你可以在这里调整你喜欢的自定义配置 7 | # 你可以前往 https://github.com/HighCapable/SweetDependency 以获得更多帮助 8 | 9 | preferences: 10 | autowire-on-sync-mode: UPDATE_OPTIONAL_DEPENDENCIES 11 | repositories-mode: FAIL_ON_PROJECT_REPOS 12 | 13 | repositories: 14 | gradle-plugin-portal: 15 | scope: PLUGINS 16 | google: 17 | maven-central: 18 | maven-xposed: 19 | url: https://api.xposed.info/ 20 | jitpack: 21 | url: https://jitpack.io 22 | 23 | plugins: 24 | com.android.application: 25 | version: 8.7.3 26 | org.jetbrains.kotlin.android: 27 | version: 2.1.0 28 | org.jetbrains.kotlin.plugin.serialization: 29 | version: 2.1.0 30 | com.google.devtools.ksp: 31 | version: 2.1.0-1.0.29 32 | 33 | libraries: 34 | de.robv.android.xposed: 35 | api: 36 | version: 82 37 | com.highcapable.yukihookapi: 38 | api: 39 | version: 1.2.1 40 | ksp-xposed: 41 | version: 1.2.1 42 | androidx.palette: 43 | palette-ktx: 44 | version: 1.0.0 45 | androidx.compose.material3: 46 | material3: 47 | version: 1.3.1 48 | org.jetbrains.kotlinx: 49 | kotlinx-coroutines-android: 50 | version: 1.9.0 51 | kotlinx-serialization-json: 52 | version: 1.7.3 53 | androidx.annotation: 54 | annotation: 55 | version: 1.9.1 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GSWXXN/RestoreSplashScreen/c96c809a294721c8635c3a886ced754f5e9597a1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 2 | 3 | pluginManagement { 4 | repositories { 5 | gradlePluginPortal() 6 | google() 7 | mavenCentral() 8 | } 9 | } 10 | plugins { 11 | id("com.highcapable.sweetdependency") version "1.0.4" 12 | id("com.highcapable.sweetproperty") version "1.0.5" 13 | } 14 | sweetProperty { 15 | project(":app") { 16 | sourcesCode { 17 | isEnable = false 18 | } 19 | buildScript { 20 | extensionName = "property" 21 | propertiesFileNames( 22 | "local.properties", 23 | isAddDefault = true 24 | ) 25 | permanentKeyValues( 26 | "KEYSTORE_PATH" to "", 27 | "KEYSTORE_PASS" to "", 28 | "KEY_ALIAS" to "", 29 | "KEY_PASSWORD" to "" 30 | ) 31 | generateFrom(SYSTEM_ENV, ROOT_PROJECT, CURRENT_PROJECT) 32 | } 33 | } 34 | } 35 | rootProject.name = "RestoreSplashScreen" 36 | include(":app", ":blockmiui") 37 | --------------------------------------------------------------------------------