├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ └── webpack.yml ├── .gitignore ├── LICENSE ├── README.md ├── autojs-deploy.js ├── babel.config.js ├── package.json ├── project.js ├── res └── drawable │ ├── ic_android.png │ ├── ic_check_list.png │ ├── ic_download.png │ ├── ic_exit.png │ ├── ic_help.png │ ├── ic_item_list.png │ ├── ic_key.png │ ├── ic_launcher.png │ ├── ic_log.png │ ├── ic_no_ad.png │ ├── ic_phone_recovery.png │ ├── ic_phone_settings.png │ ├── ic_recovery.png │ └── ic_settings.png ├── src ├── miui_cleaner_app │ ├── 123pan.js │ ├── appDesc.js │ ├── appManager.js │ ├── dialogs.js │ ├── downApp.js │ ├── downFile.js │ ├── emitItemShowEvent.js │ ├── fetch.js │ ├── findClickableParent.js │ ├── getApplicationInfo.js │ ├── getRemoteFileInfo.js │ ├── index.js │ ├── instApk.js │ ├── lanzou.js │ ├── multiChoice.js │ ├── offAppAd.js │ ├── project.json │ ├── recycle.js │ ├── serviceMgr.js │ ├── services.js │ ├── settings.js │ ├── singleChoice.js │ ├── startActivity.js │ ├── support.js │ ├── sysAppRm.js │ ├── test │ │ ├── getRemoteFileInfo.js │ │ └── services.js │ ├── update.js │ ├── waitForBack.js │ └── webView.js └── miui_cleaner_cmd │ └── main.cmd ├── types ├── adbkit.d.ts ├── auto.d.ts ├── autojs.d.ts └── modules │ ├── app.d.ts │ ├── colors.d.ts │ ├── console.d.ts │ ├── coordinate.d.ts │ ├── device.d.ts │ ├── dialogs.d.ts │ ├── engines.d.ts │ ├── events.d.ts │ ├── files.d.ts │ ├── floaty.d.ts │ ├── global.d.ts │ ├── http.d.ts │ ├── images.d.ts │ ├── keys.d.ts │ ├── media.d.ts │ ├── root.d.ts │ ├── sensors.d.ts │ ├── storages.d.ts │ ├── threads.d.ts │ ├── ui.d.ts │ └── widgets.d.ts └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = tab 8 | indent_size = 4 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [**/*.{cmd,bat}] 15 | end_of_line = crlf 16 | 17 | [**/*.{yml,yaml}] 18 | indent_style = space 19 | indent_size = 2 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | node: true, 7 | }, 8 | extends: [ 9 | "standard", 10 | ], 11 | parserOptions: { 12 | ecmaFeatures: { 13 | jsx: true, 14 | }, 15 | }, 16 | globals: { 17 | colors: true, 18 | com: true, 19 | org: true, 20 | importClass: true, 21 | storages: true, 22 | device: true, 23 | log: true, 24 | threads: true, 25 | exit: true, 26 | runtime: true, 27 | java: true, 28 | importPackage: true, 29 | ui: true, 30 | activity: true, 31 | context: true, 32 | sleep: true, 33 | android: true, 34 | toastLog: true, 35 | files: true, 36 | requestScreenCapture: true, 37 | http: true, 38 | toast: true, 39 | engines: true, 40 | random: true, 41 | events: true, 42 | press: true, 43 | gesture: true, 44 | getPackageName: true, 45 | shell: true, 46 | floaty: true, 47 | currentPackage: true, 48 | launch: true, 49 | app: true, 50 | images: true, 51 | launchApp: true, 52 | idEndsWith: true, 53 | textEndsWith: true, 54 | descEndsWith: true, 55 | back: true, 56 | dialogs: true, 57 | auto: true, 58 | setClip: true, 59 | getClip: true, 60 | javax: true, 61 | media: true, 62 | captureScreen: true, 63 | timers: true, 64 | selector: true, 65 | recents: true, 66 | swipe: true, 67 | waitForActivity: true, 68 | waitForPackage: true, 69 | currentActivity: true, 70 | JavaAdapter: true, 71 | __non_webpack_require__: true, 72 | DEBUG: true, 73 | }, 74 | rules: { 75 | "indent": ["error", "tab", { SwitchCase: 1 }], 76 | "quotes": ["error", "double"], 77 | "semi": ["error", "always"], 78 | "block-spacing": ["error", "always"], 79 | "array-bracket-spacing": ["error", "never"], 80 | "quote-props": ["error", "consistent-as-needed"], 81 | "comma-dangle": ["error", "always-multiline"], 82 | "no-tabs": ["off"], 83 | }, 84 | }; 85 | -------------------------------------------------------------------------------- /.github/workflows/webpack.yml: -------------------------------------------------------------------------------- 1 | name: NodeJS with Webpack 2 | 3 | on: 4 | push: 5 | branches: [ "*" ] 6 | pull_request: 7 | branches: [ "*" ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Use Node.js current 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: current 20 | 21 | - name: Build 22 | run: | 23 | npm install 24 | npm run build 25 | 26 | - name: Create Release 27 | id: create_release 28 | uses: actions/create-release@v1 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | with: 32 | tag_name: ${{ github.ref }} 33 | release_name: ${{ github.ref }} 34 | draft: true 35 | prerelease: true 36 | 37 | - name: Upload Batch 38 | uses: actions/upload-release-asset@v1 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | with: 42 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 43 | asset_path: ./dist/miui_cleaner_cmd/MiuiCleaner.cmd 44 | asset_name: MiuiCleaner.cmd 45 | asset_content_type: text/plain 46 | 47 | - name: Upload JavaScript Main 48 | uses: actions/upload-release-asset@v1 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | with: 52 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 53 | asset_path: ./dist/miui_cleaner_app/main.js 54 | asset_name: main.js 55 | asset_content_type: text/javascript 56 | 57 | - name: Upload JavaScript Service 58 | uses: actions/upload-release-asset@v1 59 | env: 60 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 61 | with: 62 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 63 | asset_path: ./dist/miui_cleaner_app/services.js 64 | asset_name: services.js 65 | asset_content_type: text/javascript 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | # Created by https://www.toptal.com/developers/gitignore/api/windows,visualstudiocode,node,linux,androidstudio,android 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows,visualstudiocode,node,linux,androidstudio,android 4 | 5 | ### Android ### 6 | # Gradle files 7 | .gradle/ 8 | build/ 9 | 10 | # Local configuration file (sdk path, etc) 11 | local.properties 12 | 13 | # Log/OS Files 14 | *.log 15 | 16 | # Android Studio generated files and folders 17 | captures/ 18 | .externalNativeBuild/ 19 | .cxx/ 20 | *.apk 21 | output.json 22 | 23 | # IntelliJ 24 | *.iml 25 | .idea/ 26 | misc.xml 27 | deploymentTargetDropDown.xml 28 | render.experimental.xml 29 | 30 | # Keystore files 31 | *.jks 32 | *.keystore 33 | 34 | # Google Services (e.g. APIs or Firebase) 35 | google-services.json 36 | 37 | # Android Profiling 38 | *.hprof 39 | 40 | ### Android Patch ### 41 | gen-external-apklibs 42 | 43 | # Replacement of .externalNativeBuild directories introduced 44 | # with Android Studio 3.5. 45 | 46 | ### Linux ### 47 | *~ 48 | 49 | # temporary files which can be created if a process still has a handle open of a deleted file 50 | .fuse_hidden* 51 | 52 | # KDE directory preferences 53 | .directory 54 | 55 | # Linux trash folder which might appear on any partition or disk 56 | .Trash-* 57 | 58 | # .nfs files are created when an open file is removed but is still being accessed 59 | .nfs* 60 | 61 | ### Node ### 62 | # Logs 63 | logs 64 | npm-debug.log* 65 | yarn-debug.log* 66 | yarn-error.log* 67 | lerna-debug.log* 68 | .pnpm-debug.log* 69 | 70 | # Diagnostic reports (https://nodejs.org/api/report.html) 71 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 72 | 73 | # Runtime data 74 | pids 75 | *.pid 76 | *.seed 77 | *.pid.lock 78 | 79 | # Directory for instrumented libs generated by jscoverage/JSCover 80 | lib-cov 81 | 82 | # Coverage directory used by tools like istanbul 83 | coverage 84 | *.lcov 85 | 86 | # nyc test coverage 87 | .nyc_output 88 | 89 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 90 | .grunt 91 | 92 | # Bower dependency directory (https://bower.io/) 93 | bower_components 94 | 95 | # node-waf configuration 96 | .lock-wscript 97 | 98 | # Compiled binary addons (https://nodejs.org/api/addons.html) 99 | build/Release 100 | 101 | # Dependency directories 102 | node_modules/ 103 | jspm_packages/ 104 | 105 | # Snowpack dependency directory (https://snowpack.dev/) 106 | web_modules/ 107 | 108 | # TypeScript cache 109 | *.tsbuildinfo 110 | 111 | # Optional npm cache directory 112 | .npm 113 | 114 | # Optional eslint cache 115 | .eslintcache 116 | 117 | # Optional stylelint cache 118 | .stylelintcache 119 | 120 | # Microbundle cache 121 | .rpt2_cache/ 122 | .rts2_cache_cjs/ 123 | .rts2_cache_es/ 124 | .rts2_cache_umd/ 125 | 126 | # Optional REPL history 127 | .node_repl_history 128 | 129 | # Output of 'npm pack' 130 | *.tgz 131 | 132 | # Yarn Integrity file 133 | .yarn-integrity 134 | 135 | # dotenv environment variable files 136 | .env 137 | .env.development.local 138 | .env.test.local 139 | .env.production.local 140 | .env.local 141 | 142 | # parcel-bundler cache (https://parceljs.org/) 143 | .cache 144 | .parcel-cache 145 | 146 | # Next.js build output 147 | .next 148 | out 149 | 150 | # Nuxt.js build / generate output 151 | .nuxt 152 | dist 153 | 154 | # Gatsby files 155 | .cache/ 156 | # Comment in the public line in if your project uses Gatsby and not Next.js 157 | # https://nextjs.org/blog/next-9-1#public-directory-support 158 | # public 159 | 160 | # vuepress build output 161 | .vuepress/dist 162 | 163 | # vuepress v2.x temp and cache directory 164 | .temp 165 | 166 | # Docusaurus cache and generated files 167 | .docusaurus 168 | 169 | # Serverless directories 170 | .serverless/ 171 | 172 | # FuseBox cache 173 | .fusebox/ 174 | 175 | # DynamoDB Local files 176 | .dynamodb/ 177 | 178 | # TernJS port file 179 | .tern-port 180 | 181 | # Stores VSCode versions used for testing VSCode extensions 182 | .vscode-test 183 | 184 | # yarn v2 185 | .yarn/cache 186 | .yarn/unplugged 187 | .yarn/build-state.yml 188 | .yarn/install-state.gz 189 | .pnp.* 190 | 191 | ### Node Patch ### 192 | # Serverless Webpack directories 193 | .webpack/ 194 | 195 | # Optional stylelint cache 196 | 197 | # SvelteKit build / generate output 198 | .svelte-kit 199 | 200 | ### VisualStudioCode ### 201 | .vscode/* 202 | !.vscode/settings.json 203 | !.vscode/tasks.json 204 | !.vscode/launch.json 205 | !.vscode/extensions.json 206 | !.vscode/*.code-snippets 207 | 208 | # Local History for Visual Studio Code 209 | .history/ 210 | 211 | # Built Visual Studio Code Extensions 212 | *.vsix 213 | 214 | ### VisualStudioCode Patch ### 215 | # Ignore all local history of files 216 | .history 217 | .ionide 218 | 219 | # Support for Project snippet scope 220 | .vscode/*.code-snippets 221 | 222 | # Ignore code-workspaces 223 | *.code-workspace 224 | 225 | ### Windows ### 226 | # Windows thumbnail cache files 227 | Thumbs.db 228 | Thumbs.db:encryptable 229 | ehthumbs.db 230 | ehthumbs_vista.db 231 | 232 | # Dump file 233 | *.stackdump 234 | 235 | # Folder config file 236 | [Dd]esktop.ini 237 | 238 | # Recycle Bin used on file shares 239 | $RECYCLE.BIN/ 240 | 241 | # Windows Installer files 242 | *.cab 243 | *.msi 244 | *.msix 245 | *.msm 246 | *.msp 247 | 248 | # Windows shortcuts 249 | *.lnk 250 | 251 | ### AndroidStudio ### 252 | # Covers files to be ignored for android development using Android Studio. 253 | 254 | # Built application files 255 | *.ap_ 256 | *.aab 257 | 258 | # Files for the ART/Dalvik VM 259 | *.dex 260 | 261 | # Java class files 262 | *.class 263 | 264 | # Generated files 265 | bin/ 266 | gen/ 267 | out/ 268 | 269 | # Gradle files 270 | .gradle 271 | 272 | # Signing files 273 | .signing/ 274 | 275 | # Local configuration file (sdk path, etc) 276 | 277 | # Proguard folder generated by Eclipse 278 | proguard/ 279 | 280 | # Log Files 281 | 282 | # Android Studio 283 | /*/build/ 284 | /*/local.properties 285 | /*/out 286 | /*/*/build 287 | /*/*/production 288 | .navigation/ 289 | *.ipr 290 | *.swp 291 | 292 | # Keystore files 293 | 294 | # Google Services (e.g. APIs or Firebase) 295 | # google-services.json 296 | 297 | # Android Patch 298 | 299 | # External native build folder generated in Android Studio 2.2 and later 300 | .externalNativeBuild 301 | 302 | # NDK 303 | obj/ 304 | 305 | # IntelliJ IDEA 306 | *.iws 307 | /out/ 308 | 309 | # User-specific configurations 310 | .idea/caches/ 311 | .idea/libraries/ 312 | .idea/shelf/ 313 | .idea/workspace.xml 314 | .idea/tasks.xml 315 | .idea/.name 316 | .idea/compiler.xml 317 | .idea/copyright/profiles_settings.xml 318 | .idea/encodings.xml 319 | .idea/misc.xml 320 | .idea/modules.xml 321 | .idea/scopes/scope_settings.xml 322 | .idea/dictionaries 323 | .idea/vcs.xml 324 | .idea/jsLibraryMappings.xml 325 | .idea/datasources.xml 326 | .idea/dataSources.ids 327 | .idea/sqlDataSources.xml 328 | .idea/dynamic.xml 329 | .idea/uiDesigner.xml 330 | .idea/assetWizardSettings.xml 331 | .idea/gradle.xml 332 | .idea/jarRepositories.xml 333 | .idea/navEditor.xml 334 | 335 | # Legacy Eclipse project files 336 | .classpath 337 | .project 338 | .cproject 339 | .settings/ 340 | 341 | # Mobile Tools for Java (J2ME) 342 | .mtj.tmp/ 343 | 344 | # Package Files # 345 | *.war 346 | *.ear 347 | 348 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 349 | hs_err_pid* 350 | 351 | ## Plugin-specific files: 352 | 353 | # mpeltonen/sbt-idea plugin 354 | .idea_modules/ 355 | 356 | # JIRA plugin 357 | atlassian-ide-plugin.xml 358 | 359 | # Mongo Explorer plugin 360 | .idea/mongoSettings.xml 361 | 362 | # Crashlytics plugin (for Android Studio and IntelliJ) 363 | com_crashlytics_export_strings.xml 364 | crashlytics.properties 365 | crashlytics-build.properties 366 | fabric.properties 367 | 368 | ### AndroidStudio Patch ### 369 | 370 | !/gradle/wrapper/gradle-wrapper.jar 371 | 372 | # End of https://www.toptal.com/developers/gitignore/api/windows,visualstudiocode,node,linux,androidstudio,android 373 | 374 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 375 | 376 | window_dump.xml 377 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Chaos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MiuiCleaner 2 | ---- 3 | MIUI广告清理工具 4 | 5 | ## 使用方法 6 | 7 | - [点击下载最新版本MiuiCleaner](https://github.com/gucong3000/MiuiCleaner/releases/latest),将`MiuiCleaner.apk`安装到手机即可 8 | - 使用“预装应用卸载”功能能时,需要root权限或者ADB权限(亦即“开发者选项”中“USB 调试”),未root用户在使用该功能时请在电脑上按以下步骤操作: 9 | - 在电脑上安装以下工具中任何一种,并确保其可以正常连接手机。 10 | - [小米手机助手](http://zhushou.xiaomi.com/) 11 | - [360手机助手](https://sj.360.cn/) 12 | - [豌豆荚](https://www.wandoujia.com/) 13 | - [Android SDK 平台工具](https://developer.android.google.cn/studio/releases/platform-tools?hl=zh-cn) 14 | - 在该工具的安装目录中搜索到`adb.exe`所在的子目录,将`MiuiCleaner.cmd`放入其中并运行(或者将这个目录加入环境变量`PATH`中) 15 | 16 | ## 功能介绍 17 | ### 预装应用卸载 18 | 19 | 勾选你想要卸载的APP,点击确定就可以一键删除了。支持以下62款应用的卸载: 20 |
21 | 点击查看详细名单 22 | 23 | - APP 外置开屏广告 24 | - 广告分析 25 | - 小米系统广告解决方案(智能服务) 26 | - 桌面广告 APP 27 | - 智能助理(负一屏) 28 | - 信息助手(负一屏) 29 | - 智能出行 30 | - 内容中心(趣看看) 31 | - 百度搜索框 32 | - 桌面搜索框(搜索/全局搜索) 33 | - 桌面搜索框(Google) 34 | - 过时的 APP 35 | - 悬浮球 36 | - 小米闻声 37 | - 智慧生活 38 | - 影音类 APP 39 | - 音乐 40 | - Mi Video 41 | - 小米视频 42 | - 腾讯视频小米版 43 | - 爱奇艺播放器 44 | - 天气 45 | - 小米天气 46 | - 支付、电商、理财类 APP 47 | - 小米商城 48 | - 小米商城系统组件(电商助手) 49 | - 小米钱包 50 | - 米币支付 51 | - 小米支付 52 | - 小米卡包 53 | - 小米金融(天星金融) 54 | - 小米金融(天星金融)- 安全组件 55 | - 小米金服安全组件 56 | - 银联可信服务安全组件小米版 57 | - 低使用频率 APP 58 | - 小米换机 59 | - 小米社区 60 | - 用户反馈 61 | - KLO bug反馈 62 | - 服务与反馈 63 | - 我的服务 64 | - 小米画报 65 | - 动态壁纸 66 | - 动态壁纸获取 67 | - 壁纸备份 68 | - 壁纸编辑器 69 | - 收音机(蜻蜓FM) 70 | - WPS Office Lite 71 | - 阅读(番茄免费小说) 72 | - 阅读(多看阅读器) 73 | - 小米运动健康 74 | - 浏览器 75 | - 小米浏览器 76 | - 小米浏览器(国际版) 77 | - Chrome 78 | - 内置输入法 79 | - 百度输入法-小米版 80 | - 搜狗输入法-小米版 81 | - 讯飞输入法-小米版 82 | - 小米安全键盘 83 | - 小米游戏中心 84 | - 游戏中心(旧版) 85 | - 游戏中心 86 | - 游戏服务 87 | - 游戏中心 - pad 版 88 | - Joyose 89 | - SIM 卡应用 90 | - 小米移动 91 | - 全球上网 92 | - 小米云流量 93 | - 全球上网工具插件 94 | - SIM卡应用 95 | - 快应用 96 | - 快应用中心 97 | - 快应用服务框架 98 | - 语音助手 99 | - 语音唤醒 100 | - 小爱语音(小爱同学) 101 | - 小爱视觉(扫一扫) 102 | - 小爱翻译 103 | - 小爱通话(AI虚拟助手) 104 |
105 | 106 | ### 去广告应用 107 | 108 | 内置多款去广告应用的下载链接: 109 |
110 | 点击查看详细名单 111 | 112 | - [李跳跳](https://www.123pan.com/s/A6cA-edAJh) 113 | > 广告自动跳过工具 114 | - [Edge](https://www.coolapk.com/apk/com.microsoft.emmx) 115 | > 广告可关,可与Windows的Edge互动,有网页广告屏蔽功能 116 | - [小米浏览器](https://com-globalbrowser.cn.aptoide.com/app) 117 | > 国际版,广告可关,有网页广告屏蔽功能 118 | - [讯飞输入法](https://423down.lanzouv.com/b0f24av5i) 119 | > Google Play版,无广告 120 | - 软件包安装程序 121 | > Google版,代替MIUI的“应用包管理程序”,无广告和审查功能 122 | - [应用包管理组件](https://zisu.lanzoum.com/iI7LGwn5xjc) 123 | > MIUI软件包安装程序v3.8.0,不含“纯净模式” 124 | - [QQ音乐简洁版](https://www.coolapk.com/apk/com.tencent.qqmusiclite) 125 | > MIUI 音乐APP套壳的产品 126 | - [Holi 天气](https://www.coolapk.com/apk/com.joe.holi) 127 | > 无广告,体较小,更漂亮,替代“小米天气” 128 | - [ES文件浏览器](https://423down.lanzouv.com/b0f1d7s2h) 129 | > 修改版,去广告,代替“小米视频”和“小米音乐” 130 | - [WPS Office Lite](https://www.32r.com/app/109976.html) 131 | > 国际版,无广告,替代“文档查看器” 132 | - [知乎](https://423down.lanzouo.com/b0f2lkafe) 133 | > 集成“知了”,可在“知了”中关闭所有广告 134 | - [哔哩哔哩](https://423down.lanzouv.com/b0f1gksne) 135 | > 集成“哔哩漫游”,可在“哔哩漫游”中关闭所有广告(需点击其版本号7次) 136 | - [优酷视频](https://423down.lanzouv.com/b0f1avpib) 137 | > 修改版,去广告 138 | - [百度贴吧](https://423down.lanzouv.com/b0f1b6q8d) 139 | > 修改版,去广告 140 | - [酷安](https://423down.lanzouv.com/b0f2uzq2b) 141 | > 应用商店,修改版,去广告 142 | - [AppShare](https://appshare.muge.info/) 143 | > 应用商店,可下载MIUI国际版中提取的APP 144 | 145 |
146 | 147 | ### 关闭各应用广告 148 | 149 | 支持在以下17款应用中,自动搜索50多个广告开关的具体位置,并自动给予处置。 150 |
151 | 点击查看详细名单 152 | 153 | - 小米帐号 154 | - 关于小米帐号 155 | - 系统广告 156 | - 系统工具广告:`关闭` 157 | - 系统安全 158 | - 加入“用户体验改进计划”:`关闭` 159 | - 自动发送诊断数据:`关闭` 160 | - 广告服务 161 | - 个性化广告推荐:`关闭` 162 | - 网页链接调用服务 163 | - 网页链接调用服务:`关闭` 164 | - 手机管家 165 | - 在通知栏显示:`关闭` 166 | - 在线服务:`关闭` 167 | - 隐私设置 168 | - 仅在WLAN下推荐:`打开` 169 | - 个性化推荐:`关闭` 170 | - 应用管理 171 | - 资源推荐:`关闭` 172 | - 垃圾清理 173 | - 扫描内存:`关闭` 174 | - 推荐内容:`关闭` 175 | - 仅在WLAN下推荐:`打开` 176 | - 应用商店 177 | - 通知设置 178 | - 新手帮助:`关闭` 179 | - 应用更新通知:`关闭` 180 | - 点赞消息:`关闭` 181 | - 评论消息:`关闭` 182 | - 通知栏快捷入口:`关闭` 183 | - 隐私设置 184 | - 个性化服务 185 | - 个性化服务:`关闭` 186 | - 功能设置 187 | - 显示福利活动:`关闭` 188 | - 下载管理 189 | - 信息流设置 190 | - 仅在WLAN下加载:`打开` 191 | - 资源推荐:`关闭` 192 | - 热榜推荐:`关闭` 193 | - 日历 194 | - 功能设置 195 | - 显示天气服务:`关闭` 196 | - 用户体验计划 197 | - 内容推广:`关闭` 198 | - 时钟 199 | - 更多闹钟设置 200 | - 显示生活早报:`关闭` 201 | - 小米社区 202 | - 隐私管理 203 | - 详情页相似推荐:`关闭` 204 | - 个性化广告:`关闭` 205 | - 信息流推荐:`关闭` 206 | - 关闭私信消息提醒:`打开` 207 | - 小米天气 208 | - 用户体验计划 209 | - 天气视频卡片:`关闭` 210 | - 内容推广:`关闭` 211 | - 小米视频 212 | - 隐私设置 213 | - 个性化内容推荐:`关闭` 214 | - 个性化广告推荐:`关闭` 215 | - 消息与推送 216 | - 未读消息提醒:`关闭` 217 | - 接收小米推送:`关闭` 218 | - 其他 219 | - 在线服务:`关闭` 220 | - 音乐 221 | - 在线内容服务:`关闭` 222 | - 小爱语音 223 | - 隐私管理 224 | - 隐私设置 225 | - 加入用户体验改进计划:`关闭` 226 | - 小爱技巧推送服务:`关闭` 227 | - 个性化推荐:`关闭` 228 | - 个性化广告推荐:`关闭` 229 | - 搜索 230 | - 搜索快捷方式 231 | - 桌面搜索框:`关闭` 232 | - 首页展示模块 233 | - 热搜榜单 234 | - 热搜榜s:`关闭` 235 | - 搜索提示词:`关闭` 236 | - 搜索项 237 | - 搜索精选:`关闭` 238 | - 网站广告过滤:`打开` 239 | - 浏览器 240 | - 主页设置 241 | - 简洁版:`打开` 242 | - 宫格位推送:`关闭` 243 | - 隐私防护 244 | - 广告过滤 245 | - 广告过滤:`打开` 246 | - 消息通知管理 247 | - 接收消息通知:`关闭` 248 | - 小米浏览器 249 | - 首页设置 250 | - 简洁版:`打开` 251 | - 隐私保护 252 | - 广告过滤 253 | - 广告过滤:`打开` 254 | - 高级 255 | - 浏览器广告:`关闭` 256 | - 通知栏快捷入口:`关闭` 257 | - Facebook快捷通知:`关闭` 258 | 259 |
260 | 261 | [演示视频:MiuiCleaner新功能演示-广告全自动关闭](https://www.zhihu.com/zvideo/1555993019102552064) 262 | ### 应用管家 263 | 264 | - 自启动管理 265 | > 自启动及后台运行权限管理 266 | - 通知管理 267 | > 通知栏、悬浮提示、图标角标的管理 268 | - APP卸载 269 | > APP的批量卸载 270 | - APP管理 271 | > 手机管家的应用管理功能 272 | - APP信息 273 | > 权限管理模块 274 | 275 | ### 回收站 276 | 277 | 你可以在这里重新安装已卸载的预装应用 278 | 279 | ## 常见问题 280 | - 电脑连不上手机,咋办? 281 | > 确保数据线正常,确保驱动安装正常,可以用“360手机助手”等工具自动安装 282 | - 删错了“XXX”,咋恢复? 283 | > 进入“回收站”或者“应用商店”,重新安装。 284 | 285 | ## CHANGELOG 286 | - v2023.4.23.8 287 | - 预装应用卸载 288 | - 去除卸载“纯净模式”功能 289 | - 去广告APP 290 | - 李跳跳更新至v2.2 291 | - 关闭各应用广告 292 | - 新增替换软件包安装器功能 293 | - 回收站 294 | - 使用原生圆形进度条,不再卡UI 295 | - 修正MIUI 13+ 报错,无法找到设置项 296 | - 新增下载管理 297 | - 新增在线升级功能 298 | - 新增帮助与反馈功能 299 | - v2022.10.20.7 300 | - 重构UI,减少对弹出框权限的依赖,菜单项加入描述信息、图标 301 | - 权限获取功能重构,修复不能正确请求权限的bug 302 | - 增加若干可卸载APP 303 | - 增加若干去广告APP 304 | - v2022.9.25.5 305 | - PC端修复bug #1 306 | - 预装应用卸载 307 | - 修正:卸载多个应用时报“超时”的bug 308 | - 修正:单独卸载系统应用时流程卡死的bug 309 | - 新增:无`USB调试`权限时,自动获取授权的功能 310 | - 去广告应用 311 | - 新增:应用包管理组件 312 | - 新增:QQ音乐简洁版 313 | - 关闭各应用广告 314 | - 增强:小米帐号 315 | - 新增:系统安全 316 | - 新增:广告服务 317 | - 新增:小米帐号、系统安全、广告服务三个选项按需显示功能、相关广告已关,或者已通过“修改系统”权限自动关闭后,不显示 318 | - 删除:音乐APP的广告关闭功能 319 | - 应用管家 320 | - 新增:自启动管理 321 | - 新增:应用卸载 322 | - 删除:应用管理 323 | - v2022.9.21.4 324 | - 新增手机端 GUI 325 | - 新增广告全自动关闭功能 326 | - 新增应用管家入口 327 | - 通知管理 328 | - 应用管理 329 | - 应用信息 330 | - 增加若干可卸载APP 331 | - 动态壁纸 332 | - 动态壁纸获取 333 | - 用户反馈 334 | - 服务与反馈 335 | - 小米运动健康 336 | - 小米云流量 337 | - Chrome 338 | - 大部分功能从电脑端实现转为用手机端实现 339 | - 删除卸载米家功能 340 | - v2022.9.10.2 341 | - 修正文案丢字 342 | - “⋮”无法在Windows默认终端显示,改为“三个点”来迁就 343 | - v2022.9.9.1 344 | - 新增内置 APP 广告关闭引导功能 345 | - 部分文案修改 346 | - APP卸载功能新增9款 APP 的支持 347 | - 软件推送向手机前,增加了判断是否已装的逻辑 348 | - 删除MIUI内置应用前,增加了是否MIUI环境的判断 349 | - v2022.9.7.0 350 | - 首个版本 351 | - 提供若干内置APP卸载和恢复功能 352 | - 提供第三方APP替换功能 353 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | sourceType: "script", 3 | targets: { 4 | rhino: "1.7.13", 5 | }, 6 | presets: [ 7 | "@babel/preset-env", 8 | ], 9 | plugins: [ 10 | "@babel/plugin-transform-runtime", 11 | "@babel/plugin-syntax-jsx", 12 | ], 13 | }; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miui_cleaner", 3 | "version": "2023.4.23.8", 4 | "private": true, 5 | "description": "MIUI广告清理工具", 6 | "scripts": { 7 | "test": "npx eslint *.js src/**/*.js", 8 | "test:fix": "npm run test -- --fix", 9 | "start": "npx webpack --config webpack.config.js --watch --mode=development", 10 | "build:pack": "npx webpack --config webpack.config.js --mode=production", 11 | "build:pull": "adb pull sdcard/脚本/com.github.gucong3000.miui.cleaner/build ./dist/", 12 | "build:init": "sh -c \"mkdir -p dist/miui_cleaner_cmd\" && node project.js", 13 | "build": "npm run build:init && npm run build:pack", 14 | "deploy:res": "adb push ./res /sdcard/脚本/com.github.gucong3000.miui.cleaner/", 15 | "deploy": "npm run build & npm run deploy:res", 16 | "dump:ui": "adb shell uiautomator dump && (adb shell cat /sdcard/window_dump.xml | xmllint --format - > window_dump.xml) && code window_dump.xml", 17 | "dump:act": "adb shell dumpsys activity activities | grep Hist", 18 | "dump": "npm run dump:ui && npm run dump:act" 19 | }, 20 | "author": "GuCong", 21 | "license": "MIT", 22 | "devDependencies": { 23 | "@auto.pro/webpack-plugin": "^8.13.3", 24 | "@babel/core": "^7.19.1", 25 | "@babel/plugin-syntax-jsx": "^7.18.6", 26 | "@babel/plugin-transform-runtime": "^7.19.1", 27 | "@babel/preset-env": "^7.19.1", 28 | "@devicefarmer/adbkit": "^3.2.3", 29 | "ansi-styles": "^5.2.0", 30 | "babel-loader": "^8.2.5", 31 | "eslint": "^8.24.0", 32 | "eslint-config-standard": "^17.0.0", 33 | "webpack": "^5.74.0", 34 | "webpack-cli": "^4.10.0", 35 | "wrapper-webpack-plugin": "^2.2.2" 36 | }, 37 | "dependencies": { 38 | "blob-polyfill": "^7.0.20220408", 39 | "core-js": "^3.26.0", 40 | "debounce": "^1.2.1", 41 | "headers-polyfill": "^3.1.2", 42 | "json5": "^2.2.1", 43 | "pretty-bytes": "^5.6.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /project.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs/promises"); 2 | const { spawnSync } = require("node:child_process"); 3 | 4 | (async () => { 5 | const [ 6 | packageConfig, 7 | appConfig, 8 | cmd, 9 | readme, 10 | ] = await Promise.all([ 11 | readFile("package.json"), 12 | readFile("src/miui_cleaner_app/project.json"), 13 | readFile("src/miui_cleaner_cmd/main.cmd"), 14 | readFile("README.md"), 15 | ]); 16 | const pkgInfo = packageConfig.json; 17 | const appInfo = appConfig.json; 18 | 19 | const date = new Date(); 20 | let versionCode; 21 | 22 | if (process.env.GITHUB_RUN_NUMBER) { 23 | versionCode = process.env.GITHUB_RUN_NUMBER - 0; 24 | } else { 25 | const controller = new AbortController(); 26 | versionCode = await Promise.any( 27 | [ 28 | "https://cdn.jsdelivr.net/gh/gucong3000/MiuiCleaner/src/miui_cleaner_app/project.json", 29 | "https://raw.fastgit.org/gucong3000/MiuiCleaner/main/src/miui_cleaner_app/project.json", 30 | "https://raw.githubusercontent.com/gucong3000/MiuiCleaner/main/src/miui_cleaner_app/project.json", 31 | ].map(async url => { 32 | const res = await fetch(url, { signal: controller.signal }); 33 | const data = await res.json(); 34 | return data.versionCode + 1; 35 | }), 36 | ); 37 | controller.abort(); 38 | } 39 | 40 | const versionName = [ 41 | date.getUTCFullYear(), 42 | date.getUTCMonth() + 1, 43 | date.getUTCDate(), 44 | versionCode, 45 | ].join("."); 46 | pkgInfo.version = versionName; 47 | appInfo.versionCode = versionCode; 48 | appInfo.versionName = versionName; 49 | appInfo.launchConfig.splashText = pkgInfo.description; 50 | cmd.constents = cmd.constents.replace( 51 | /^title\s+.*$/im, 52 | `title ${appInfo.name} - ${pkgInfo.description}`, 53 | ); 54 | 55 | const distCmd = fs.writeFile( 56 | "dist/miui_cleaner_cmd/MiuiCleaner.cmd", 57 | spawnSync( 58 | "iconv", 59 | [ 60 | "--from-code=utf-8", 61 | "--to-code=gb18030", 62 | ], 63 | { 64 | input: cmd.constents.replace( 65 | /^chcp\s+\d+/im, "chcp 936", 66 | ).replace( 67 | /^title\s+.*$/im, 68 | `title ${appInfo.name} - ${pkgInfo.description} -v${pkgInfo.version}`, 69 | ).replace(/\r?\n/g, "\r\n"), 70 | }, 71 | ).stdout, 72 | ); 73 | updateDoc(readme); 74 | 75 | await Promise.all([ 76 | appConfig.update(), 77 | packageConfig.update(), 78 | cmd.update(), 79 | readme.update(), 80 | distCmd, 81 | ]); 82 | })( 83 | ); 84 | 85 | async function readFile (path) { 86 | let constents = await fs.readFile(path); 87 | constents = constents.toString("utf-8"); 88 | const json = /\.json$/.test(path) && JSON.parse(constents); 89 | 90 | const file = { 91 | json, 92 | constents, 93 | update: (...args) => { 94 | let newContents; 95 | if (file.json) { 96 | newContents = JSON.stringify(file.json, 0, "\t"); 97 | } else { 98 | newContents = file.constents; 99 | } 100 | const finalNewline = /\.(cmd|bat)$/i.test(path) ? "\r\n" : "\n"; 101 | newContents = newContents.replace(/\r?\n/g, finalNewline); 102 | if (constents.trim() !== newContents.trim()) { 103 | return fs.writeFile(path, newContents.trim() + finalNewline, ...args); 104 | } 105 | }, 106 | }; 107 | return file; 108 | } 109 | 110 | // 保持文档的描述和关闭广告的单元测试数据一致 111 | function updateDoc (readme) { 112 | const docResult = []; 113 | const testCase = require("./src/miui_cleaner_app/test/services").testCase; 114 | delete testCase["关于手机"]; 115 | delete testCase["开发者选项"]; 116 | 117 | printTestCase(testCase); 118 | 119 | readme.constents = readme.constents.replace(/(#+\s*关闭各应用广告[\s\S]*?<\/summary>[\s\S]*?)-[\s\S]*(<\/details>)/, (s, prefix, suffix) => { 120 | return prefix + docResult.join("\n") + "\n\n" + suffix; 121 | }); 122 | 123 | function printTestCase (data, deep = 0) { 124 | for (const caseName in data) { 125 | if (data[caseName] === true) { 126 | docResult.push("\t".repeat(deep) + "- " + caseName + ":`打开`"); 127 | } else if (data[caseName] === false) { 128 | docResult.push("\t".repeat(deep) + "- " + caseName + ":`关闭`"); 129 | } else if ( 130 | typeof data[caseName] !== "object" || 131 | data[caseName] === null || 132 | (caseName === "广告服务" && !deep) 133 | ) { 134 | continue; 135 | } else { 136 | docResult.push("\t".repeat(deep) + "- " + caseName); 137 | printTestCase(data[caseName], deep + 1); 138 | } 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /res/drawable/ic_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_android.png -------------------------------------------------------------------------------- /res/drawable/ic_check_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_check_list.png -------------------------------------------------------------------------------- /res/drawable/ic_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_download.png -------------------------------------------------------------------------------- /res/drawable/ic_exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_exit.png -------------------------------------------------------------------------------- /res/drawable/ic_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_help.png -------------------------------------------------------------------------------- /res/drawable/ic_item_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_item_list.png -------------------------------------------------------------------------------- /res/drawable/ic_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_key.png -------------------------------------------------------------------------------- /res/drawable/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable/ic_log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_log.png -------------------------------------------------------------------------------- /res/drawable/ic_no_ad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_no_ad.png -------------------------------------------------------------------------------- /res/drawable/ic_phone_recovery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_phone_recovery.png -------------------------------------------------------------------------------- /res/drawable/ic_phone_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_phone_settings.png -------------------------------------------------------------------------------- /res/drawable/ic_recovery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_recovery.png -------------------------------------------------------------------------------- /res/drawable/ic_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gucong3000/MiuiCleaner/fed27d29171fe0e444ae98741548bcd9fff71009/res/drawable/ic_settings.png -------------------------------------------------------------------------------- /src/miui_cleaner_app/123pan.js: -------------------------------------------------------------------------------- 1 | const jsonParse = require("json5/lib/parse"); 2 | const fetch = global.fetch || require("./fetch"); 3 | const atob = global.atob || global.$base64.decode; 4 | 5 | // const webView = global.ui && require("./webView"); 6 | let userAgent; 7 | try { 8 | userAgent = android.webkit.WebSettings.getDefaultUserAgent(context); 9 | } catch (ex) { 10 | userAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1"; 11 | } 12 | 13 | async function getFileInfo (url) { 14 | url = parseUrl(url); 15 | const res = await fetch( 16 | url.href, 17 | { 18 | headers: { 19 | "accept": "text/html", 20 | "user-agent": userAgent, 21 | }, 22 | }, 23 | ); 24 | await checkResponse(res); 25 | const html = await res.text(); 26 | let initialProps = html.match(/\b(window\.)?g_initialProps\s*=\s*(.*);/m); 27 | initialProps = initialProps && jsonParse(initialProps[2]); 28 | return parseFileInfo(initialProps.reslist.data.InfoList, url, initialProps); 29 | } 30 | 31 | async function parseFileInfo (fileInfo, url, initialProps) { 32 | if (Array.isArray(fileInfo)) { 33 | // 解析多个结果 34 | fileInfo = await Promise.all(fileInfo.map(fileInfo => parseFileInfo(fileInfo, url, initialProps))); 35 | if (fileInfo.length === 1) { 36 | fileInfo = fileInfo[0]; 37 | } 38 | } else if (fileInfo.Etag || fileInfo.S3KeyFlag) { 39 | // 解析单个文件 40 | fileInfo.publicPath = initialProps.publicPath; 41 | fileInfo.shareKey = initialProps.res.data.ShareKey; 42 | fileInfo = new FileInfo(fileInfo); 43 | } else { 44 | // 解析文件夹 45 | const api = new URL(`share/get?limit=999&next=1&orderBy=share_id&orderDirection=desc&shareKey=${initialProps.res.data.ShareKey}&ParentFileId=${fileInfo.FileId}&Page=1`, initialProps.publicPath); 46 | let res = await fetch( 47 | api.href, 48 | { 49 | headers: { 50 | "accept": "application/json", 51 | "user-agent": userAgent, 52 | }, 53 | referrerPolicy: "no-referrer", 54 | }, 55 | ); 56 | await checkResponse(res); 57 | res = await res.json(); 58 | fileInfo = await parseFileInfo(res.data.InfoList, url, initialProps); 59 | } 60 | return fileInfo; 61 | } 62 | 63 | async function checkResponse (res) { 64 | if (!res.ok) { 65 | throw new Error(`status: ${res.status}\nmessage: ${res.message || JSON.stringify(await res.text())}\n at ${res.url}`); 66 | } 67 | } 68 | 69 | function parseUrl (url) { 70 | if (!url.href) { 71 | url = new URL(url); 72 | } 73 | return url; 74 | } 75 | 76 | class FileInfo { 77 | constructor (data) { 78 | Object.assign(this, data); 79 | } 80 | 81 | get fileName () { 82 | return this.FileName; 83 | } 84 | 85 | get size () { 86 | return this.Size; 87 | } 88 | 89 | get lastModified () { 90 | return Date.parse(this.UpdateAt); 91 | } 92 | 93 | get id () { 94 | return this.FileId; 95 | } 96 | 97 | async getLocation (redirect) { 98 | const data = await getRealFile(this, redirect); 99 | Object.assign(this, data); 100 | return this; 101 | } 102 | } 103 | 104 | async function parse (url, options) { 105 | const data = await getFileInfo(url, options || {}); 106 | if (Array.isArray(data)) { 107 | return data.map(data => new FileInfo(data)); 108 | } 109 | return new FileInfo(data); 110 | } 111 | 112 | async function getRealFile (fileInfo, redirect) { 113 | let res = await fetch("https://www.123pan.com/a/api/share/download/info", { 114 | headers: { 115 | "accept": "application/json", 116 | "content-type": "application/json;charset=UTF-8", 117 | "user-agent": userAgent, 118 | }, 119 | referrerPolicy: "no-referrer", 120 | body: JSON.stringify({ 121 | ShareKey: fileInfo.shareKey, 122 | FileID: fileInfo.FileId, 123 | S3keyFlag: fileInfo.S3KeyFlag, 124 | Size: fileInfo.Size, 125 | Etag: fileInfo.Etag, 126 | }), 127 | method: "POST", 128 | }); 129 | await checkResponse(res); 130 | res = await res.json(); 131 | fileInfo.location = decodeURI(atob(new URL(res.data.DownloadURL).searchParams.get("params"))); 132 | // if (redirect) { 133 | // // 134 | // } 135 | return fileInfo; 136 | } 137 | module.exports = parse; 138 | // (async () => { 139 | // let file = await parse("https://423down.lanzouv.com/tp/iKBGf0hcsq5e"); 140 | // console.log(file.url); 141 | // file = await parse("https://423down.lanzouv.com/tp/iKBGf0hcsq5e"); 142 | // console.log(file.url); 143 | // })(); 144 | // 145 | // getFileInfoFromUrl("https://www.123pan.com/s/A6cA-gT9Jh").then(async file => console.log(await file.getLocation(true))); 146 | // getFileInfoFromUrl("https://www.123pan.com/s/A6cA-dJAJh").then(file => console.log(file)); 147 | 148 | // getFileInfoFromUrl("https://www.123pan.com/s/ZYAZVv-TBYjd.html").then(file => console.log(file)); 149 | -------------------------------------------------------------------------------- /src/miui_cleaner_app/appDesc.js: -------------------------------------------------------------------------------- 1 | 2 | // https://gist.github.com/mcxiaoke/0a4c639d04e94c45eb6c787c0f98940a 3 | // https://fengooge.blogspot.com/2019/03/taking-ADB-to-uninstall-system-applications-in-MIUI-without-root.html 4 | 5 | module.exports = { 6 | // APP 外置开屏广告 7 | "com.miui.analytics": "广告分析", 8 | "com.miui.systemAdSolution": "小米广告联盟∑的开屏广告", 9 | // 桌面广告 APP 10 | "com.miui.personalassistant": "负一屏", 11 | "com.mi.android.globalminusscreen": "负一屏", 12 | "com.miui.smarttravel": "智能出行", 13 | "com.miui.newhome": "趣看看", 14 | "com.android.quicksearchbox": "桌面搜索框/搜索/全局搜索", 15 | "com.google.android.googlequicksearchbox": "桌面搜索框(Google)", 16 | "com.baidu.searchbox": "桌面搜索框(百度)", 17 | // 过时的 APP 18 | "com.miui.touchassistant": "悬浮球/Quickball", 19 | "com.miui.accessibility": "听障辅助工具", 20 | "com.miui.hybrid.accessory": "古早版智能家居", 21 | // 影音类 APP 22 | "com.miui.player": "QQ音乐简洁版,应替换成ES文件浏览器", 23 | "com.miui.videoplayer": "Mi Video,应替换成ES文件浏览器", 24 | "com.miui.video": "小米视频,应替换成ES文件浏览器", 25 | "com.tencent.qqlivexiaomi": "小米视频插件-腾讯视频小米版", 26 | "com.qiyi.video.sdkplayer": "小米视频插件-爱奇艺播放器", 27 | // 天气 28 | "com.miui.weather2": "小米天气,应替换成Holi天气", 29 | // 支付、电商、理财类 APP 30 | "com.xiaomi.shop": "小米商城", 31 | "com.xiaomi.ab": "小米商城系统组件/电商助手", 32 | "com.mipay.wallet": "小米钱包", 33 | "com.xiaomi.payment": "米币支付", 34 | "com.miui.nextpay": "小米支付", 35 | "com.xiaomi.pass": "小米卡包", 36 | "com.xiaomi.jr": "小米金融/天星金融", 37 | "com.xiaomi.jr.security": "小米金融/天星金融-安全组件", 38 | "com.xiaomi.mifisecurity": "小米金服安全组件", 39 | "com.unionpay.tsmservice.mi": "银联可信服务安全组件小米版", 40 | // 低使用频率 APP 41 | "com.miui.huanji": "小米换机", 42 | "com.xiaomi.vipaccount": "小米社区", 43 | "com.miui.bugreport": "bug反馈", 44 | "com.miui.klo.bugreport": "KLO bug反馈", 45 | "com.miui.miservice": "服务与反馈", 46 | "com.miui.vipservice": "我的服务", 47 | "com.mfashiongallery.emag": "小米画报", 48 | "com.android.wallpaper": "动态壁纸", 49 | "com.android.wallpaper.livepicker": "动态壁纸获取", 50 | "com.android.wallpaperbackup": "壁纸备份", 51 | "com.android.wallpapercropper": "壁纸编辑器", 52 | "com.miui.fm": "收音机/蜻蜓FM", 53 | "cn.wps.moffice_eng.xiaomi.lite": "WPS Office Lite,应替换成ES文件浏览器", 54 | "com.dragon.read": "阅读/番茄免费小说", 55 | "com.duokan.reader": "阅读/多看阅读器", 56 | "com.mi.health": "小米健康/小米运动健康", 57 | // 浏览器 58 | "com.android.browser": "小米浏览器", 59 | "com.mi.globalbrowser": "小米浏览器(国际版)", 60 | "com.android.chrome": "Chrome", 61 | // 内置输入法 62 | "com.baidu.input_mi": "百度输入法-小米版", 63 | "com.sohu.inputmethod.sogou.xiaomi": "搜狗输入法-小米版", 64 | "com.iflytek.inputmethod.miui": "讯飞输入法-小米版", 65 | "com.miui.securityinputmethod": "小米安全键盘", 66 | // 小米游戏中心 67 | "com.xiaomi.migameservice": "// 游戏中心(旧版)", 68 | "com.xiaomi.gamecenter": "游戏中心", 69 | "com.xiaomi.gamecenter.sdk.service": "游戏中心-SDK服务", 70 | "com.xiaomi.gamecenter.pad": "游戏中心-pad版", 71 | "com.xiaomi.joyose": "云控/温控/记步", 72 | // SIM 卡应用 73 | "com.miui.virtualsim": "全球上网", 74 | "com.xiaomi.mimobile": "小米移动", 75 | "com.xiaomi.mimobile.cloudsim": "小米移动-小米云流量", 76 | "com.xiaomi.mimobile.noti": "小米移动-全球上网-插件", 77 | "com.android.stk": "SIM卡应用", 78 | // 快应用 79 | "com.miui.quickappCenter.miAppStore": "快应用中心/快应用商店", 80 | "com.miui.hybrid": "快应用服务框架", 81 | // 语音助手 82 | "com.miui.voiceassist": "小爱语音/小爱同学", 83 | "com.miui.voicetrigger": "语音唤醒语音助手", 84 | "com.xiaomi.scanner": "小爱视觉/扫一扫", 85 | "com.xiaomi.aiasst.vision": "小爱翻译", 86 | "com.xiaomi.aiasst.service": "小爱通话(AI虚拟助手)", 87 | // 翻译 88 | "com.miui.translationservice": "MIUI翻译服务", 89 | "com.miui.translation.kingsoft": "MIUI翻译-金山", 90 | "com.miui.translation.xmcloud": "MIUI翻译-小米云", 91 | "com.miui.translation.youdao": "MIUI翻译-有道", 92 | }; 93 | -------------------------------------------------------------------------------- /src/miui_cleaner_app/appManager.js: -------------------------------------------------------------------------------- 1 | const startActivity = require("./startActivity"); 2 | const singleChoice = require("./singleChoice"); 3 | 4 | // https://blog.unidevel.cn/xiao-mi-dian-zi-shu-shang-yi-xie-yin-cang-de-she-zhi/ 5 | 6 | const actions = [ 7 | { 8 | name: "自启动管理", 9 | summary: "自启动及后台运行权限管理", 10 | icon: "./res/drawable/ic_check_list.png", 11 | packageName: "com.miui.securitycenter", 12 | className: "com.miui.permcenter.autostart.AutoStartManagementActivity", 13 | }, 14 | { 15 | name: "通知管理", 16 | summary: "通知栏、悬浮提示、图标角标的管理", 17 | icon: "./res/drawable/ic_item_list.png", 18 | packageName: "com.miui.notification", 19 | className: "miui.notification.management.activity.NotificationAppListActivity", 20 | }, 21 | { 22 | name: "APP卸载", 23 | summary: "APP的批量卸载", 24 | icon: "./res/drawable/ic_recovery.png", 25 | packageName: "com.miui.cleanmaster", 26 | className: "com.miui.optimizecenter.deepclean.installedapp.InstalledAppsActivity", 27 | }, 28 | { 29 | name: "APP管理", 30 | summary: "手机管家的应用管理功能", 31 | icon: "./res/drawable/ic_settings.png", 32 | packageName: "com.miui.securitycenter", 33 | className: "com.miui.appmanager.AppManagerMainActivity", 34 | }, 35 | // { 36 | // name: "应用升级", 37 | // packageName: "com.xiaomi.market", 38 | // className: ".ui.UpdateListActivity", 39 | // summary: "APP的更新管理", 40 | // }, 41 | { 42 | name: "APP信息", 43 | summary: "权限管理模块", 44 | icon: "./res/drawable/ic_key.png", 45 | packageName: "com.android.settings", 46 | className: ".applications.ManageApplications", 47 | }, 48 | // { 49 | // name: "甜品盒", 50 | // summary: "彩蛋", 51 | // icon: "./res/drawable/ic_android.png", 52 | // packageName: "com.android.systemui", 53 | // className: ".DessertCase", 54 | // }, 55 | // { 56 | // name: "Marshmallow Land", 57 | // summary: "彩蛋", 58 | // icon: "./res/drawable/ic_android.png", 59 | // packageName: "com.android.systemui", 60 | // className: ".egg.MLandActivity", 61 | // }, 62 | ].filter(action => ( 63 | app.getAppName(action.packageName) 64 | )); 65 | 66 | const name = "APP管家"; 67 | const icon = "./res/drawable/ic_phone_settings.png"; 68 | 69 | function appManager () { 70 | singleChoice({ 71 | title: name, 72 | icon, 73 | itemList: actions, 74 | fn: startActivity, 75 | }); 76 | require("./index")(); 77 | } 78 | 79 | module.exports = { 80 | name, 81 | icon, 82 | summary: "广告相关权限管理", 83 | fn: appManager, 84 | }; 85 | -------------------------------------------------------------------------------- /src/miui_cleaner_app/dialogs.js: -------------------------------------------------------------------------------- 1 | const resString = com.stardust.autojs.R.string; 2 | const AlertDialog = android.app.AlertDialog; 3 | 4 | const btnLabelMap = { 5 | positive: resString.ok, 6 | negative: resString.cancel, 7 | neutral: "在浏览器中打开", 8 | }; 9 | 10 | function alertDialog ( 11 | message, 12 | options, 13 | ) { 14 | options = { 15 | positive: true, 16 | negative: true, 17 | neutral: false, 18 | cancelable: false, 19 | message, 20 | ...options, 21 | }; 22 | const builder = new AlertDialog.Builder(activity); 23 | const emitter = options.emitter || events.emitter(); 24 | 25 | function createListener (eventKeyName, eventName) { 26 | const listener = {}; 27 | listener[`on${eventKeyName}`] = (...args) => { 28 | console.log("对话框事件:", eventName); 29 | emitter.emit(eventName, ...args); 30 | }; 31 | return listener; 32 | } 33 | 34 | function setAttr (dialog, filter) { 35 | let keys = Object.keys(dialog); 36 | if (filter) { 37 | keys = keys.filter(filter); 38 | } 39 | keys.forEach(key => { 40 | key = key.match(/^set?(On)?(\w+?)(Button|Listener)?$/); 41 | if (!key) { 42 | return; 43 | } 44 | const setName = key[0]; 45 | const attrKeyName = key[2]; 46 | const attrName = attrKeyName.replace(/^\w/, w => w.toLowerCase()); 47 | const type = key[3] || attrKeyName; 48 | let attrValue; 49 | if (type === "Listener") { 50 | attrValue = createListener(attrKeyName, attrName.replace(/[A-Z]/, w => "_" + w.toLowerCase())); 51 | } else if (attrName in options) { 52 | attrValue = options[attrName]; 53 | if (type === "Button") { 54 | if (attrValue) { 55 | attrValue = [ 56 | typeof attrValue === "string" ? attrValue : btnLabelMap[attrName], 57 | createListener("Click", attrName), 58 | ]; 59 | } else { 60 | return; 61 | } 62 | } else if (type === "MultiChoiceItems") { 63 | attrValue = [ 64 | attrValue.map(String), 65 | attrValue.map(Boolean), 66 | createListener("Click", "multi_choice"), 67 | ]; 68 | } else if (type === "Items") { 69 | attrValue = [ 70 | attrValue.map(String), 71 | createListener("Click", "single_choice"), 72 | ]; 73 | } 74 | } else { 75 | return; 76 | } 77 | dialog[setName].apply(dialog, Array.isArray(attrValue) ? attrValue : [attrValue]); 78 | }); 79 | } 80 | 81 | if (options.view) { 82 | const frame = ui.inflate(""); 83 | let viewList = options.view; 84 | viewList = Array.isArray(options.view) ? viewList : [viewList]; 85 | viewList.forEach(view => { 86 | if (typeof view === "string") { 87 | view = ui.inflate(view, frame); 88 | } 89 | frame.addView(view); 90 | }); 91 | options.view = frame; 92 | } 93 | 94 | setAttr(builder); 95 | ui.post(() => { 96 | const dialog = builder.create(); 97 | setAttr(dialog, attrName => !builder[attrName]); 98 | dialog.show(); 99 | console.log(`对话框:“${options.message || options.title}”`); 100 | return dialog; 101 | }, 1); 102 | return { 103 | emitter, 104 | then: (...args) => { 105 | return new Promise(resolve => { 106 | function call (...args) { 107 | ui.post(() => { 108 | resolve(...args); 109 | }); 110 | } 111 | emitter.once("positive", () => { call(true); }); 112 | emitter.once("negative", () => { call(false); }); 113 | emitter.once("neutral", () => { call(null); }); 114 | emitter.once("cancel", () => { call(); }); 115 | emitter.once("single_choice", (dialog, index) => { 116 | call(options.items[index]); 117 | }); 118 | }).then(...args); 119 | }, 120 | }; 121 | } 122 | 123 | function confirm ( 124 | message, 125 | options, 126 | ) { 127 | return alertDialog( 128 | message, 129 | { 130 | ...options, 131 | }, 132 | ); 133 | } 134 | 135 | function alert ( 136 | message, 137 | options, 138 | ) { 139 | return alertDialog( 140 | message, 141 | { 142 | negative: false, 143 | ...options, 144 | }, 145 | ).then(() => {}); 146 | } 147 | 148 | function prompt ( 149 | message, 150 | value, 151 | options, 152 | ) { 153 | const view = options.view || ui.inflate(``); 154 | return alertDialog( 155 | message, 156 | { 157 | view, 158 | ...options, 159 | }, 160 | ).then(result => { 161 | if (result) { 162 | return view.getText().toString(); 163 | } else { 164 | return null; 165 | } 166 | }); 167 | } 168 | 169 | function singleChoice ( 170 | items, 171 | options, 172 | ) { 173 | return alertDialog( 174 | null, 175 | { 176 | items, 177 | positive: false, 178 | // negative: false, 179 | ...options, 180 | }, 181 | ); 182 | } 183 | 184 | module.exports = Object.assign(alertDialog, { 185 | confirm, 186 | alert, 187 | prompt, 188 | singleChoice, 189 | }); 190 | -------------------------------------------------------------------------------- /src/miui_cleaner_app/downApp.js: -------------------------------------------------------------------------------- 1 | const getApplicationInfo = require("./getApplicationInfo"); 2 | const getRemoteFileInfo = require("./getRemoteFileInfo"); 3 | const singleChoice = require("./singleChoice"); 4 | const prettyBytes = require("pretty-bytes"); 5 | const downFile = require("./downFile"); 6 | const dialogs = require("./dialogs"); 7 | 8 | // https://github.abskoop.workers.dev/ 9 | // http://fastgit.org/ 10 | // https://download.fastgit.org/skylot/jadx/releases/download/v1.4.4/jadx-gui-1.4.4-no-jre-win.exe 11 | // https://download.fastgit.org/MrIkso/ArscEditor/releases/download/1.0.2/ArscEditor-1.0.2.zip 12 | 13 | const appList = [ 14 | { 15 | name: "李跳跳", 16 | summary: "干净小巧的广告自动跳过工具", 17 | icon: "https://litiaotiao.cn/apple-touch-icon.png", 18 | packageName: "hello.litiaotiao.app", 19 | url: "https://www.123pan.com/s/ZYAZVv-TBYjd", 20 | filter: function (files) { 21 | return files.filter(file => { 22 | return /李跳跳|MissLee/.test(file.fileName) && !file.fileName.includes("真实好友"); 23 | }); 24 | }, 25 | }, 26 | { 27 | name: "QQ音乐简洁版", 28 | summary: "MIUI音乐APP套壳的产品", 29 | icon: "https://m.32r.com/logo/210807/202108070906595774.png", 30 | packageName: "com.tencent.qqmusiclite", 31 | url: "https://www.coolapk.com/apk/com.tencent.qqmusiclite", 32 | }, 33 | { 34 | name: "Edge", 35 | summary: "浏览器,微软出品,带广告屏蔽功能", 36 | icon: "https://edgefrecdn.azureedge.net/welcome/static/favicon.png", 37 | packageName: "com.microsoft.emmx", 38 | url: "https://app.mi.com/details?id=com.microsoft.emmx", 39 | }, 40 | { 41 | name: "小米浏览器", 42 | summary: "国际版", 43 | icon: "https://m.32r.com/logo/210519/202105191427372351.png", 44 | packageName: "com.mi.globalbrowser", 45 | url: "https://wwm.lanzoul.com/tp/idzsf0bh062h", 46 | }, 47 | { 48 | name: "讯飞输入法", 49 | summary: "定制版、Google Play版", 50 | icon: "https://srf.xunfei.cn/favicon.ico", 51 | packageName: "com.iflytek.inputmethod", 52 | // url: "https://app.meizu.com/apps/public/detail?package_name=com.iflytek.inputmethod", 53 | // url: "https://m.32r.com/app/7401.html", 54 | url: "https://firepx.lanzoul.com/b00vf92jc#pwd=647w", 55 | }, 56 | // { 57 | // name: "软件包安装程序", 58 | // summary: "Google版", 59 | // packageName: "com.google.android.packageinstaller", 60 | // icon: "https://file.1xiazai.net/d/file/android/20220728/202266164286724.png", 61 | // url: { 62 | // // 3.01 MB 版本号 未知 适用于安卓 13 SDK 33 63 | // 33: "https://www.123pan.com/s/OZe0Vv-iOKl3", 64 | // // 3.14 MB 版本号 12-7567768 适用于安卓 12 SDK 31 65 | // 31: "https://www.123pan.com/s/OZe0Vv-LOKl3", 66 | // // 3.13 MB 版本号 11-7532981 适用于安卓 11 SDK 30 67 | // 30: "https://www.123pan.com/s/OZe0Vv-zOKl3", 68 | // // 1.83 MB 版本号 10-7029319 适用于安卓 10 SDK 29 69 | // 29: "https://www.123pan.com/s/OZe0Vv-tOKl3", 70 | // // 8.55 MB 版本号 9-7126274 适用于安卓 9 SDK 28 71 | // 28: "https://www.123pan.com/s/OZe0Vv-qOKl3", 72 | // }[device.sdkInt], 73 | // }, 74 | { 75 | name: "应用包管理组件", 76 | summary: "MIUI软件包安装程序v3.8.0,不含“纯净模式”", 77 | icon: "http://pic.danji100.com/upload/2022-4/20224261118377118.png", 78 | packageName: "com.miui.packageinstaller", 79 | url: "https://zisu.lanzoum.com/tp/iI7LGwn5xjc", 80 | filter: function (files) { 81 | files = files.map(file => { 82 | const miuiInst = file.fileName.match(/(应用包管理组件).*?([\d.]+)-(\d+).*?(\.\w+)$/); 83 | if (miuiInst) { 84 | const appName = miuiInst[1]; 85 | const versionCode = Number.parseInt(miuiInst[2].replace(/\./g, ""), 10); 86 | const versionName = `${Array.from(String(versionCode)).join(".")}-${miuiInst[3]}`; 87 | file.fileName = `${appName}_v${versionName}${miuiInst[4]}`; 88 | file.versionName = versionName; 89 | file.versionCode = versionCode; 90 | console.log(file); 91 | } 92 | return file; 93 | }); 94 | }, 95 | }, 96 | { 97 | name: "几何天气", 98 | summary: "干净、小巧、漂亮、功能多", 99 | icon: "https://raw.fastgit.org/WangDaYeeeeee/GeometricWeather/master/app/src/main/res/drawable/ic_launcher.png", 100 | packageName: "wangdaye.com.geometricweather", 101 | url: "https://github.com/WangDaYeeeeee/GeometricWeather/releases/latest", 102 | filter: function (files) { 103 | const appInfo = this; 104 | files = files.filter(file => { 105 | const verInfo = file.url.match(/\/(.+?)\/.*?\.\1_(\w+)\.\w+$/); 106 | if (verInfo) { 107 | const verName = verInfo[1]; 108 | const verType = verInfo[2]; 109 | file.versionName = `${verName}_${verType}`; 110 | file.versionCode = Number.parseInt(verName.replace(/\./, ""), 10); 111 | } 112 | return verInfo; 113 | }); 114 | 115 | if (appInfo.appName) { 116 | let subVer = appInfo.getVersionName().match(/_\w+$/); 117 | if (subVer) { 118 | subVer = subVer[0] + ".apk"; 119 | return files.filter(file => file.fileName.endsWith(subVer)); 120 | } 121 | } 122 | return [files[files.length - 1]]; 123 | }, 124 | }, 125 | { 126 | name: "ES文件浏览器", 127 | summary: "去广告版,替代MIUI视频、音乐、文档查看器", 128 | icon: "https://m.32r.com/logo/220311/202203111728435421.png", 129 | packageName: "com.estrongs.android.pop", 130 | url: "https://423down.lanzouv.com/b0f1d7s2h", 131 | }, 132 | { 133 | name: "WPS Office Lite", 134 | summary: "国际版,无广告,替代“文档查看器”", 135 | icon: "https://m.32r.com/logo/220908/202209081617517363.png", 136 | packageName: "cn.wps.moffice_i18n", 137 | url: "https://m.32r.com/app/109976.html", 138 | }, 139 | { 140 | name: "知乎", 141 | summary: "集成“知了”,“设置→知了”中有去广告开关", 142 | icon: "https://static.zhihu.com/heifetz/assets/apple-touch-icon-60.8f6c52aa.png", 143 | packageName: "com.zhihu.android", 144 | url: "https://www.123pan.com/s/A6cA-dJAJh", 145 | // url: "https://423down.lanzouo.com/b0f2lkafe", 146 | // url: "https://m.32r.com/app/80966.html", 147 | // https://www.423down.com/11775.html 148 | filter: function (files) { 149 | return files.filter(file => { 150 | return /知乎.*知了/.test(file.fileName); 151 | }); 152 | }, 153 | }, 154 | { 155 | name: "哔哩哔哩", 156 | summary: "“设置→哔哩漫游→关于版本”点五下有惊喜", 157 | icon: "https://m.32r.com/logo/221114/202211141125334046.png", 158 | packageName: "tv.danmaku.bili", 159 | // url: "https://www.123pan.com/s/A6cA-gT9Jh", 160 | url: "https://423down.lanzouv.com/b0f1gksne", 161 | // https://www.423down.com/12235.html 162 | filter: function (files) { 163 | return files.filter(file => { 164 | return /哔哩哔哩.*漫游/.test(file.fileName); 165 | }); 166 | }, 167 | }, 168 | { 169 | name: "优酷视频", 170 | summary: "去广告版", 171 | icon: "https://img.alicdn.com/tfs/TB1WeJ9Xrj1gK0jSZFuXXcrHpXa-195-195.png", 172 | packageName: "com.youku.phone", 173 | url: "https://423down.lanzouv.com/b0f1avpib", 174 | filter: function (files) { 175 | return files.filter(file => { 176 | file.fileName = file.fileName.replace(/忧(?=酷)/g, "优"); 177 | return file.fileName.includes("优酷视频"); 178 | }); 179 | }, 180 | }, 181 | { 182 | name: "高德地图", 183 | summary: "Google版、纯净版", 184 | icon: "https://m.amap.com/img/screenLogo.png", 185 | packageName: "com.autonavi.minimap", 186 | url: "https://423down.lanzouv.com/b0f29j15c", 187 | }, 188 | { 189 | name: "百度贴吧", 190 | summary: "去广告版", 191 | icon: "https://m.32r.com/logo/210810/202108101711331977.png", 192 | packageName: "com.baidu.tieba", 193 | url: "https://423down.lanzouv.com/b0f1b6q8d", 194 | }, 195 | { 196 | name: "酷安", 197 | summary: "应用商店,去广告版", 198 | icon: "https://static.coolapk.com/static/web/v8/images/header-logo.png", 199 | packageName: "com.coolapk.market", 200 | url: "https://423down.lanzouv.com/b0f2uzq2b", 201 | }, 202 | { 203 | name: "App分享", 204 | summary: "应用商店,刷机包,国际版提取的APP", 205 | icon: "http://pic.xfdown.com/uploads/2022-5/2022551511344265.png", 206 | packageName: "info.muge.appshare", 207 | url: "https://423down.lanzouv.com/tp/iHmmD06tw9xa", 208 | }, 209 | ]; 210 | 211 | function formatSize (number, options) { 212 | if (!number || !Number.isSafeInteger(number)) { 213 | return number; 214 | } 215 | return prettyBytes(number, { 216 | binary: true, 217 | ...options, 218 | }); 219 | } 220 | 221 | function formatDate (number) { 222 | if (!number || !Number.isSafeInteger(number)) { 223 | return number; 224 | } 225 | const dateFormat = android.text.format.DateFormat.getDateFormat(activity); 226 | return dateFormat.format(number) || number; 227 | } 228 | 229 | async function download (appInfo, item) { 230 | if (typeof appInfo === "string") { 231 | appInfo = appList.find(info => info.packageName === appInfo); 232 | } 233 | if (/^\w+:\/\/app.mi.com\//i.test(appInfo.url)) { 234 | app.startActivity({ 235 | action: "android.intent.action.VIEW", 236 | data: "market://details?id=" + appInfo.packageName, 237 | }); 238 | return; 239 | } 240 | const View = android.view.View; 241 | let progress = item.progress; 242 | if (progress) { 243 | progress.setVisibility(View.VISIBLE); 244 | progress.indeterminate = true; 245 | } else { 246 | progress = ui.inflate(` 247 | 248 | `, item, true); 249 | } 250 | function hideProgress () { 251 | // console.log(progress); 252 | progress.setVisibility(View.GONE); 253 | // item.removeView(progress); 254 | // item.invalidate(); 255 | // progress.invalidate(); 256 | } 257 | let file; 258 | let getLocationTask; 259 | function getLocation () { 260 | if (file && file.getLocation) { 261 | getLocationTask = file.getLocation(true); 262 | } 263 | }; 264 | try { 265 | file = await getRemoteFiles(appInfo); 266 | if (file.length > 1) { 267 | const choice = await dialogs.singleChoice(file.map(file => ({ 268 | toString: () => file.fileName + "\n" + [ 269 | file.versionName, 270 | formatSize(file.size), 271 | formatDate(file.lastModified), 272 | ].filter(Boolean).join(" | "), 273 | file, 274 | })), { 275 | title: `请选择要下载的“${appInfo.appName || appInfo.name}”版本`, 276 | neutral: true, 277 | }); 278 | file = choice && choice.file; 279 | getLocation(); 280 | } else { 281 | file = file[0]; 282 | getLocation(); 283 | const localVer = appInfo.appName && appInfo.getVersionName(); 284 | const confirm = await dialogs.confirm([ 285 | file.versionName && `版本:${(localVer ? `${localVer} → ` : "") + file.versionName}`, 286 | file.size && `大小:${formatSize(file.size)}`, 287 | file.lastModified && `日期:${formatDate(file.lastModified)}`, 288 | ].filter(Boolean).join("\n"), { 289 | title: `是否${appInfo.appName ? "更新" : "下载"}“${appInfo.appName || appInfo.name}”?`, 290 | neutral: true, 291 | }); 292 | file = confirm && file; 293 | } 294 | } catch (ex) { 295 | console.error(ex); 296 | file = null; 297 | } 298 | 299 | if (file) { 300 | await getLocationTask; 301 | const downTask = downFile(file); 302 | downTask.on("progress", (e) => { 303 | progress.indeterminate = false; 304 | progress.max = e.size; 305 | progress.progress = e.progress; 306 | }); 307 | const intent = await downTask; 308 | const confirm = intent.getPackage() || (await dialogs.confirm(`“${file.fileName}”下载完毕,立即安装?`, { 309 | title: "确认安装", 310 | })); 311 | if (confirm) { 312 | app.startActivity(intent); 313 | } 314 | } else { 315 | if (file === null && appInfo.url) { 316 | app.openUrl(appInfo.url); 317 | } 318 | } 319 | hideProgress(); 320 | } 321 | 322 | function verCompare (verA, verB) { 323 | function splitVer (versionName) { 324 | return versionName.replace(/^\D+|\D+$/g, "").split(/\./g); 325 | } 326 | function parseNum (str) { 327 | return Number.parseInt(str, 10) || 0; 328 | } 329 | verA = splitVer(verA); 330 | verB = splitVer(verB); 331 | const length = Math.max(verA.length, verB.length); 332 | let result; 333 | for (let i = 0; i < length && !result; i++) { 334 | result = parseNum(verA[i]) - parseNum(verB[i]); 335 | } 336 | return result; 337 | } 338 | 339 | function fileCompare (b, a) { 340 | let result; 341 | if (a.versionCode && b.versionCode) { 342 | result = a.versionCode - b.versionCode; 343 | } 344 | if (a.versionName && b.versionName) { 345 | result = result || verCompare(a.versionName, b.versionName); 346 | } 347 | if (a.lastModified && b.lastModified) { 348 | result = result || a.lastModified - b.lastModified; 349 | } 350 | return result; 351 | } 352 | 353 | function getRemoteFiles (appInfo) { 354 | return getRemoteFileInfo(appInfo.url).then(fileList => { 355 | if (!fileList) { 356 | return; 357 | } 358 | if (!Array.isArray(fileList)) { 359 | fileList = [fileList]; 360 | } 361 | if (appInfo.filter) { 362 | fileList = appInfo.filter(fileList) || fileList; 363 | } else if (fileList.length > 1) { 364 | fileList = fileList.filter(file => file.fileName.includes(appInfo.name)); 365 | } 366 | fileList = fileList.sort(fileCompare); 367 | if (fileList.length > 1 && fileList[0].versionName) { 368 | fileList = fileList.filter(file => file.versionName === fileList[0].versionName); 369 | } 370 | if (fileList.length > 1) { 371 | const mouse = fileList.find(file => /耗/.test(file.fileName)); 372 | if (mouse) { 373 | fileList = [mouse]; 374 | } 375 | } 376 | return fileList; 377 | }); 378 | } 379 | 380 | function downApp () { 381 | appList.forEach((appInfo) => { 382 | getApplicationInfo(appInfo); 383 | if (appInfo.appName) { 384 | appInfo.displayName = appInfo.appName + " v" + appInfo.getVersionName(); 385 | // if (!/^\w+:\/\/app.mi.com\//i.test(appInfo.url)) { 386 | // getRemoteFiles(appInfo); 387 | // } 388 | } else { 389 | delete appInfo.displayName; 390 | } 391 | }); 392 | singleChoice({ 393 | title: "请选择要下载的APP", 394 | itemList: appList, 395 | fn: download, 396 | }); 397 | require("./index")(); 398 | } 399 | // downApp.download = downApp; 400 | module.exports = { 401 | name: "去广告APP", 402 | summary: "各APP的去广告版和广告自动跳过工具", 403 | icon: "./res/drawable/ic_download.png", 404 | fn: downApp, 405 | }; 406 | -------------------------------------------------------------------------------- /src/miui_cleaner_app/downFile.js: -------------------------------------------------------------------------------- 1 | const DownloadManager = android.app.DownloadManager; 2 | const Cursor = android.database.Cursor; 3 | const Intent = android.content.Intent; 4 | 5 | const downloadManager = context.getSystemService(context.DOWNLOAD_SERVICE); 6 | const mimeTypeMap = android.webkit.MimeTypeMap.getSingleton(); 7 | const emitter = events.emitter(); 8 | 9 | function getValOfCursor (cursor, columnName, columnType) { 10 | let columnIndex = cursor.getColumnIndex(columnName); 11 | if (columnIndex < 0) { 12 | columnName = DownloadManager["COLUMN_" + columnName] || DownloadManager[columnName]; 13 | columnIndex = cursor.getColumnIndex(columnName); 14 | } 15 | if (columnIndex < 0) { 16 | return; 17 | } 18 | if (!columnType) { 19 | switch (cursor.getType(columnIndex)) { 20 | case Cursor.FIELD_TYPE_INTEGER: 21 | columnType = "Long"; 22 | break; 23 | case Cursor.FIELD_TYPE_FLOAT: 24 | columnType = "Float"; 25 | break; 26 | case Cursor.FIELD_TYPE_STRING: 27 | columnType = "String"; 28 | break; 29 | case Cursor.FIELD_TYPE_BLOB: 30 | columnType = "Blob"; 31 | break; 32 | } 33 | } 34 | return cursor[`get${columnType}`](columnIndex); 35 | } 36 | 37 | function queryDownList (callback, query) { 38 | const cursor = downloadManager.query(query || new DownloadManager.Query()); 39 | const valueOf = getValOfCursor.bind(cursor, cursor); 40 | let result; 41 | if (cursor) { 42 | if (cursor.moveToFirst()) { 43 | do { 44 | if ((result = callback(valueOf))) { 45 | break; 46 | } 47 | } while (cursor.moveToNext()); 48 | } 49 | cursor.close(); 50 | } 51 | return result; 52 | } 53 | 54 | function guessFileName (disposition) { 55 | if (disposition) { 56 | const fileName = disposition.match(/(^|;)\s*filename\*?\s*=\s*(UTF-8(''|\/))?(.*?)(;|\s|$)/i); 57 | return fileName && decodeURI(fileName[4]); 58 | } 59 | } 60 | 61 | function readConfig (options) { 62 | // let disposition; 63 | // if (options.headers) { 64 | // Object.keys(options.headers).forEach(key => { 65 | // switch (key.toLowerCase()) { 66 | // case "content-disposition": { 67 | // disposition = options.headers[key]; 68 | // break; 69 | // } 70 | // case "content-length": { 71 | // options.size = +options.headers[key]; 72 | // break; 73 | // } 74 | // case "content-type": { 75 | // if (options.headers[key] !== "application/octet-stream") { 76 | // options.mimeType = options.headers[key]; 77 | // } 78 | // break; 79 | // } 80 | // } 81 | // }); 82 | // } 83 | options.location = decodeURI(options.location || options.url); 84 | if (!options.fileName) { 85 | options.fileName = (guessFileName(options.disposition) || android.webkit.URLUtil.guessFileName(options.location, null, null)).replace(/_(Coolapk|\d+)(?=\.\w+$)/i, ""); 86 | } 87 | if (!options.mimeType || /^application\/octet-stream$/.test(options.mimeType)) { 88 | options.mimeType = mimeTypeMap.getMimeTypeFromExtension(files.getExtension(options.fileName)); 89 | } 90 | return options; 91 | } 92 | 93 | function downFile (options) { 94 | options = readConfig(options); 95 | let downId; 96 | let complete; 97 | const downEmitter = Object.create(events.emitter()); 98 | 99 | const promise = new Promise((resolve, reject) => { 100 | downEmitter.on("complete", resolve); 101 | // downEmitter.on("cancel", reject); 102 | // downEmitter.on("error", reject); 103 | }); 104 | 105 | function emitProgressEvent (progressEvent) { 106 | if (!progressEvent.size) { 107 | progressEvent.size = options.size; 108 | } 109 | downEmitter.emit("progress", progressEvent); 110 | } 111 | 112 | function emitCompleteEvent (intent) { 113 | if (!intent) { 114 | intent = new Intent(); 115 | intent.putExtra(DownloadManager.EXTRA_DOWNLOAD_ID, downId); 116 | } 117 | intent.setDataAndType( 118 | downloadManager.getUriForDownloadedFile(downId), 119 | options.mimeType, 120 | ); 121 | intent.setAction(Intent.ACTION_VIEW); 122 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 123 | intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 124 | const googleInstaller = "com.google.android.packageinstaller"; 125 | 126 | intent.setPackage(app.getAppName(googleInstaller) ? googleInstaller : null); 127 | 128 | downEmitter.emit("complete", intent); 129 | downEmitter.removeAllListeners(); 130 | throttle[downId] = null; 131 | complete = true; 132 | } 133 | 134 | function startTask () { 135 | queryDownList((valueOf) => { 136 | if (options.location === valueOf("URI")) { 137 | downId = valueOf("ID"); 138 | } else { 139 | return; 140 | } 141 | switch (valueOf("STATUS")) { 142 | case DownloadManager.STATUS_SUCCESSFUL: { 143 | emitCompleteEvent(); 144 | break; 145 | } 146 | case DownloadManager.STATUS_PAUSED: 147 | case DownloadManager.STATUS_FAILED: { 148 | try { 149 | app.launchPackage("com.android.providers.downloads.ui"); 150 | } catch (ex) { 151 | app.startActivity(new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)); 152 | } 153 | // falls through 154 | } 155 | default: { 156 | downEmitter.emit("start", downId); 157 | emitProgressEvent(createProgressEvent(valueOf)); 158 | } 159 | } 160 | return true; 161 | }); 162 | 163 | if (!downId) { 164 | const request = new DownloadManager.Request(android.net.Uri.parse(options.location)); 165 | request.addRequestHeader("User-Agent", options.userAgent || android.webkit.WebSettings.getDefaultUserAgent(context)); 166 | if (options.referer) { 167 | request.addRequestHeader("Referer", options.referer); 168 | } 169 | // request.setDestinationInExternalPublicDir(android.os.Environment.DIRECTORY_DOWNLOADS, options.fileName); 170 | request.setDestinationInExternalFilesDir(context, android.os.Environment.DIRECTORY_DOWNLOADS, options.fileName); 171 | request.setMimeType(options.mimeType); 172 | console.log("开始下载:", options); 173 | downId = downloadManager.enqueue(request); 174 | downEmitter.emit("start", downId); 175 | } 176 | 177 | emitter.on(`${downId}.click`, (...args) => downEmitter.emit("click", ...args)); 178 | 179 | if (!complete) { 180 | emitter.on(`${downId}.progress`, emitProgressEvent); 181 | emitter.once(`${downId}.complete`, (...args) => { 182 | console.log("下载完毕:", options); 183 | emitter.removeAllListeners(`${downId}.complete`); 184 | emitter.removeAllListeners(`${downId}.progress`); 185 | emitCompleteEvent(...args); 186 | // emitter.removeAllListeners(`${downId}.click`); 187 | }); 188 | startDownReceiver(); 189 | } 190 | } 191 | downEmitter.then = (...args) => promise.then(...args); 192 | setTimeout(startTask, 0); 193 | return downEmitter; 194 | } 195 | 196 | function registerReceiver (sysActionName, onReceive) { 197 | context.registerReceiver( 198 | new JavaAdapter(android.content.BroadcastReceiver, { 199 | onReceive, 200 | }), 201 | new android.content.IntentFilter(sysActionName), 202 | ); 203 | } 204 | 205 | registerReceiver(DownloadManager.ACTION_DOWNLOAD_COMPLETE, (context, intent) => { 206 | emitter.emit(`${intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)}.complete`, intent); 207 | }); 208 | 209 | registerReceiver(DownloadManager.ACTION_NOTIFICATION_CLICKED, (context, intent) => { 210 | intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS).forEach(downId => { 211 | emitter.emit(`${downId}.click`, intent); 212 | }); 213 | }); 214 | 215 | function createProgressEvent (valueOf) { 216 | // 已经下载文件大小 217 | const progress = valueOf("BYTES_DOWNLOADED_SO_FAR"); 218 | const speed = valueOf("downloading_current_speed"); 219 | // 下载文件的总大小 220 | const size = valueOf("TOTAL_SIZE_BYTES"); 221 | return { 222 | progress, 223 | speed: speed >= 0 ? speed : null, 224 | size, 225 | }; 226 | } 227 | 228 | let downStatus = false; 229 | let throttle = {}; 230 | 231 | function downReceiver () { 232 | let running; 233 | queryDownList(valueOf => { 234 | if (valueOf("STATUS") === DownloadManager.ACTION_DOWNLOAD_COMPLETE) { 235 | return; 236 | } 237 | const downId = valueOf("ID"); 238 | const progressEvent = createProgressEvent(valueOf); 239 | if (progressEvent.progress > 0 || progressEvent.size > 0) { 240 | const key = JSON.stringify(progressEvent); 241 | if (throttle[downId] !== key) { 242 | emitter.emit(`${downId}.progress`, progressEvent); 243 | throttle[downId] = key; 244 | } 245 | } 246 | running = true; 247 | }); 248 | if (running) { 249 | setTimeout(downReceiver, 0x200); 250 | } else { 251 | throttle = {}; 252 | } 253 | downStatus = running || false; 254 | } 255 | function startDownReceiver () { 256 | if (!downStatus) { 257 | downReceiver(); 258 | } 259 | } 260 | 261 | downFile.queryDownList = queryDownList; 262 | module.exports = downFile; 263 | -------------------------------------------------------------------------------- /src/miui_cleaner_app/emitItemShowEvent.js: -------------------------------------------------------------------------------- 1 | const debounce = require("debounce"); 2 | const Rect = android.graphics.Rect; 3 | const inNightMode = Boolean(activity.getApplicationContext().getResources().getConfiguration().uiMode & android.content.res.Configuration.UI_MODE_NIGHT_YES); 4 | function emitItemShowEvent (listView, defaultIcon) { 5 | const itemList = new Map(); 6 | listView.on("item_bind", function (itemView, itemHolder) { 7 | itemList.set(itemView, itemHolder); 8 | setTimeout(() => { 9 | listView.emit("item_show", itemHolder.item, itemView, listView); 10 | if (itemHolder.item.loadIcon || (itemHolder.item.icon && /^https?:/i.test(itemHolder.item.icon))) { 11 | itemView.icon.clearColorFilter(); 12 | } 13 | }, 0); 14 | }); 15 | listView.on("scroll_change", debounce(() => { 16 | const parentRect = new Rect(); 17 | listView.getGlobalVisibleRect(parentRect); 18 | function isVisible (target) { 19 | const rect = new Rect(); 20 | target.getGlobalVisibleRect(rect); 21 | return parentRect.contains(rect) || parentRect.intersect(rect); 22 | } 23 | itemList.forEach((itemHolder, itemView) => { 24 | if (isVisible(itemView)) { 25 | listView.emit("item_show", itemHolder.item, itemView, listView); 26 | } 27 | }); 28 | }, 80)); 29 | listView.on("item_show", function (item, itemView, listView) { 30 | const imageView = itemView.icon; 31 | if (item.loadIcon) { 32 | imageView.setImageDrawable(item.loadIcon()); 33 | } else if (!(item.icon && /^https?:/i.test(item.icon))) { 34 | imageView.setColorFilter(android.graphics.Color.parseColor(inNightMode ? "#FFCCCCCC" : "#FF333333")); 35 | return; 36 | } 37 | imageView.clearColorFilter(); 38 | }); 39 | } 40 | module.exports = emitItemShowEvent; 41 | -------------------------------------------------------------------------------- /src/miui_cleaner_app/fetch.js: -------------------------------------------------------------------------------- 1 | const okhttp3 = global.Packages?.okhttp3; 2 | const Headers = global.Headers || require("headers-polyfill").Headers; 3 | // const ReadableStream = global.ReadableStream || require("web-streams-ponyfill").ReadableStream; 4 | const Blob = global.Blob || require("blob-polyfill").Blob; 5 | // const FormData = global.FormData || require("formdata-polyfill").Headers; 6 | 7 | function fetchAny (url, options = {}) { 8 | if (Array.isArray(url)) { 9 | let controller; 10 | if (!options.signal) { 11 | if (global.AbortController) { 12 | controller = new AbortController(); 13 | options.signal = controller.signal; 14 | } else { 15 | options.signal = events.emitter(threads.currentThread()); 16 | } 17 | } 18 | return Promise.any(url.map(url => fetch(url, options))).then(res => { 19 | if (controller) { 20 | controller.abort(); 21 | } else if (options.signal?.emit) { 22 | options.signal.emit("abort"); 23 | } 24 | return res; 25 | }); 26 | } else { 27 | return fetch(url, options); 28 | } 29 | } 30 | 31 | function fetch (url, options = {}) { 32 | return new Promise((resolve, reject) => { 33 | options = { 34 | redirect: "follow", 35 | method: "GET", 36 | ...options, 37 | }; 38 | options.method = options.method.toUpperCase(); 39 | // console.time(url); 40 | const client = http.client().newBuilder() 41 | .followRedirects(/^follow$/i.test(options.redirect)) 42 | .build(); 43 | const call = client.newCall(http.buildRequest(url, options)); 44 | const work = events.emitter(threads.currentThread()); 45 | work.once("response", resolve); 46 | work.once("error", reject); 47 | call.enqueue(new okhttp3.Callback({ 48 | onResponse: function (call, res) { 49 | try { 50 | res = wrapResponse(res, options); 51 | } catch (ex) { 52 | work.emit("error", ex); 53 | return; 54 | } 55 | work.emit("response", res); 56 | // console.timeEnd(url); 57 | }, 58 | onFailure: function (call, err) { 59 | work.emit("error", err); 60 | }, 61 | })); 62 | 63 | if (options.signal) { 64 | const abort = () => { 65 | call.isCanceled() || call.cancel(); 66 | work.emit("error", new Error(options.signal.reason || "The user aborted a request.")); 67 | }; 68 | if (options.signal.aborted) { 69 | return abort(); 70 | } 71 | if (options.signal.addEventListener) { 72 | options.signal.addEventListener("abort", abort); 73 | } else if (options.signal.on) { 74 | options.signal.on("abort", abort); 75 | } 76 | } 77 | }); 78 | } 79 | 80 | const _response = new Map(); 81 | class Response { 82 | get status () { 83 | return _response.get(this).code(); 84 | } 85 | 86 | get ok () { 87 | return this.status >= 200 && this.status < 300; 88 | } 89 | 90 | get url () { 91 | return _response.get(this).request().url().toString(); 92 | } 93 | 94 | get redirected () { 95 | return _response.get(this).isRedirect(); 96 | } 97 | 98 | get statusText () { 99 | return _response.get(this).message(); 100 | } 101 | 102 | get headers () { 103 | return _response.get(_response.get(this)).getHeaders(); 104 | } 105 | 106 | get body () { 107 | return _response.get(_response.get(this)).getBody(); 108 | } 109 | 110 | blob () { 111 | return _response.get(_response.get(this)).getBlob(); 112 | } 113 | 114 | arrayBuffer () { 115 | this.blob().arrayBuffer(); 116 | } 117 | 118 | text () { 119 | return _response.get(_response.get(this)).getBodyText(); 120 | } 121 | 122 | json () { 123 | return this.text().then(text => JSON.parse(text)); 124 | } 125 | 126 | clone () { 127 | const response = Object.create(Response.prototype); 128 | _response.set(response, _response.get(this)); 129 | return response; 130 | } 131 | } 132 | function hexToArrayUint8Array (input) { 133 | const view = new Uint8Array(input.length / 2); 134 | for (let i = 0; i < input.length; i += 2) { 135 | view[i / 2] = parseInt(input.substring(i, i + 2), 16); 136 | } 137 | return view; 138 | } 139 | // https://square.github.io/okhttp/4.x/okhttp/okhttp3/-response/ 140 | function wrapResponse (res, options) { 141 | if (/^error$/i.test(options.redirect) && res.isRedirect()) { 142 | throw new Error("unexpected redirect"); 143 | } 144 | const response = Object.create(Response.prototype); 145 | _response.set(response, res); 146 | let headers; 147 | const body = res.body(); 148 | const bodyByteString = body.byteString(); 149 | body.close(); 150 | const contentType = body.contentType(); 151 | let text; 152 | const bodyToText = () => { 153 | if (text === undefined) { 154 | text = new java.lang.String(bodyByteString.toByteArray(), contentType.charset() || "UTF-8"); 155 | } 156 | return text; 157 | }; 158 | let blob; 159 | const bodyToBlob = () => { 160 | if (!blob) { 161 | blob = blob = new Blob([ 162 | hexToArrayUint8Array(bodyByteString.hex()), 163 | ], { 164 | type: `${contentType.type()}/${contentType.subtype()}`, 165 | }); 166 | } 167 | return blob; 168 | }; 169 | const resProps = { 170 | getHeaders: () => { 171 | if (!headers) { 172 | headers = new Headers(); 173 | res.headers().forEach(entry => { 174 | headers.append(entry.first, entry.second); 175 | }); 176 | } 177 | return headers; 178 | }, 179 | getBlob: () => { 180 | return Promise.resolve().then(bodyToBlob); 181 | }, 182 | getBody: () => { 183 | return bodyToBlob().stream(); 184 | }, 185 | getBodyText: (fnName) => { 186 | return Promise.resolve().then(bodyToText); 187 | }, 188 | }; 189 | _response.set(res, resProps); 190 | return response; 191 | } 192 | 193 | module.exports = fetchAny; 194 | 195 | // fetch( 196 | // "https://developer.lanzoug.com/file/?UjRbZQw9UGEHDgY+AzYGalJtAjpe5FPCA5BVtlaZU/sG41CUAchX5lOWB/gAsweqUKYC41ewUNtSs1fHAOUEUVIEW+sM2FCMB3wGYQN5BjFSJgIxXixTtAOLVfxW7VPOBo1Q4gHgV71T4AfoAMEH4FCeAqhXJlAzUiVXOgB6BGNSO1tgDDRQWwc4BjQDagY1UjkCPl42U2ADPVVjVjhTdAZgUHQBYVc0UzwHYABkBz9QPwIxVy5QIlIlV2wAbgQ1UmBbPAx+UDQHZQZ/A2UGP1InAjNeNFNoAz1ValY6U2EGM1A3AThXNVNgBzcAMAcyUDoCN1c+UGFSYldnAD8EMlJiWzwMM1A2B2gGYwNkBjJSOgIpXmBTIQNvVXVWf1MhBmNQdQE1V2dTOAdoAGMHMFA6AjBXLlAmUjxXPAA5BGNSb1s9DGdQMgdoBmgDZgYzUjoCMl4yU3cDYlU/Vn1TbwY3UDEBalc6Uz0HYABnBzBQOwIzVy5QJ1IlVyYAYQQ0UmdbNAxpUDQHaQZoA2MGMVIwAiFedFM4A3RVblY4U2IGMlApAW1XOlM8B38AZgc0UD8CKVc7UGNSc1c1ADAEOFJi", 197 | // { 198 | // redirect: "follow", 199 | // // method: "HEAD", 200 | // headers: { 201 | // "accept": "*/*", 202 | // "user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1", 203 | // "referer": "https://423down.lanzouv.com/tp/ic1wllc", 204 | // "x-forwarded-for": "202.247.192.146", 205 | // "client-ip": "59.142.129.197", 206 | // }, 207 | // }, 208 | // ); 209 | -------------------------------------------------------------------------------- /src/miui_cleaner_app/findClickableParent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 向上查找 UiObject 的父节点,找到可点击的祖先节点 3 | * @param {UiObject} node 节点 4 | * @returns 5 | */ 6 | function findClickableParent (node) { 7 | return !node || node.clickable() ? node : findClickableParent(node.parent()); 8 | } 9 | module.exports = findClickableParent; 10 | -------------------------------------------------------------------------------- /src/miui_cleaner_app/getApplicationInfo.js: -------------------------------------------------------------------------------- 1 | // const PackageManager = android.content.pm.PackageManager; 2 | const pm = context.getPackageManager(); 3 | // https://developer.android.google.cn/reference/kotlin/android/content/pm/ApplicationInfo 4 | // https://developer.android.google.cn/reference/kotlin/android/content/pm/PackageInfo 5 | function getApplicationInfo (options) { 6 | let appInfo; 7 | let packageInfo; 8 | // const getPackageInfo = () => packageInfo || (packageInfo = pm.getPackageInfo(options.packageName, PackageManager.GET_SIGNING_CERTIFICATES)); 9 | const getPackageInfo = () => packageInfo || (packageInfo = pm.getPackageInfo(options.packageName, 0)); 10 | 11 | try { 12 | appInfo = pm.getApplicationInfo(options.packageName, 0); 13 | } catch (ex) { 14 | return null; 15 | } 16 | if (!options.appName) { 17 | const appName = pm.getApplicationLabel(appInfo).toString(); 18 | if (appName === options.packageName) { 19 | if (!options.name && options.summary) { 20 | options.name = options.summary; 21 | options.summary = options.packageName; 22 | } 23 | } else { 24 | options.appName = appName; 25 | } 26 | } 27 | 28 | // if (!options.getSignature) { 29 | // options.getSignature = () => getPackageInfo().signingInfo.getApkContentsSigners(); 30 | // } 31 | if (!options.loadIcon && appInfo.icon) { 32 | options.loadIcon = () => appInfo.loadIcon(pm); 33 | } 34 | options.getVersionName = () => { 35 | let versionName = getPackageInfo().versionName; 36 | if (options.packageName === "com.miui.packageinstaller") { 37 | versionName = versionName.replace(/^\d+(?=-)/, () => Array.from(String(packageInfo.getLongVersionCode())).join(".")); 38 | } 39 | return versionName; 40 | }; 41 | options.getVersionCode = () => getPackageInfo().getLongVersionCode(); 42 | options.getUpdateTime = () => getPackageInfo().lastUpdateTime; 43 | return options; 44 | } 45 | module.exports = getApplicationInfo; 46 | 47 | // console.log( 48 | // getApplicationInfo({ 49 | // packageName: "org.autojs.autoxjs.v6", 50 | // }), 51 | // ); 52 | 53 | // const apkPath = "/data/app/org.autojs.autoxjs.v6-XPge4R-XoervO0iNge1BhQ==/base.apk"; 54 | 55 | // const apkPath = "/storage/emulated/0/Android/data/org.autojs.autoxjs.v6/files/Download/GeometricWeather.3.013_pub-2.apk"; 56 | // const info = pm.getPackageArchiveInfo( 57 | // apkPath, 58 | // PackageManager.GET_SIGNING_CERTIFICATES, 59 | // ); 60 | // const appInfo = info.applicationInfo; 61 | 62 | // appInfo.sourceDir = apkPath; 63 | // appInfo.publicSourceDir = apkPath; 64 | // console.log( 65 | // appInfo.loadLabel(pm).toString(), 66 | // ); 67 | // console.log( 68 | // appInfo.packageName, 69 | // ); 70 | // console.log( 71 | // appInfo.loadLabel(pm).toString(), 72 | // ); 73 | 74 | // const zipFile = $zip.open(apkPath); 75 | // log(zipFile.getPath()); 76 | // log(); 77 | // zipFile.getFileHeaders().forEach(file => { 78 | // const fileName = file.getFileName(); 79 | // if (/^META-INF\/.+?\..*SA$/i.test(fileName)) { 80 | // console.log(fileName); 81 | // console.log(file.getSignature()); 82 | // } 83 | // }); 84 | -------------------------------------------------------------------------------- /src/miui_cleaner_app/getRemoteFileInfo.js: -------------------------------------------------------------------------------- 1 | const fetch = require("./fetch"); 2 | const lanzou = require("./lanzou"); 3 | const _123pan = require("./123pan"); 4 | 5 | class Asset { 6 | constructor (data) { 7 | Object.assign(this, data); 8 | } 9 | 10 | async getLocation () { 11 | const location = await parseGithubRelease(new URL(this.url), false); 12 | if (location) { 13 | this.location = location; 14 | } 15 | return this; 16 | } 17 | } 18 | 19 | function parseGithubRelease (url, redirect) { 20 | const pathInfo = url.pathname.match(/\/releases\/(.+)$/); 21 | if (!pathInfo) { 22 | return; 23 | } 24 | if (pathInfo[1].includes("/")) { 25 | return fetch( 26 | [ 27 | url.href, 28 | url.protocol + "//gh.api.99988866.xyz/" + url.href, 29 | url.protocol + "//download.fastgit.org" + url.pathname + url.search, 30 | ], 31 | { 32 | redirect: redirect ? "follow" : "manual", 33 | method: "HEAD", 34 | }, 35 | ).then(res => { 36 | const location = res.headers.get("location"); 37 | if (location) { 38 | return location; 39 | } else if (res.ok) { 40 | return res.url; 41 | } 42 | }); 43 | } else { 44 | url.hostname = "api." + url.hostname; 45 | url.pathname = "/repos" + url.pathname; 46 | return fetch(url.href).then(res => res.json()).then(release => { 47 | const referer = release.html_url; 48 | const versionName = release.tag_name.replace(/^v/, ""); 49 | return release.assets.map( 50 | asset => new Asset({ 51 | fileName: asset.name, 52 | type: asset.content_type, 53 | size: asset.size, 54 | lastModified: Date.parse(asset.updated_at), 55 | id: asset.node_id, 56 | url: asset.browser_download_url, 57 | referer, 58 | versionName, 59 | }), 60 | ); 61 | }); 62 | } 63 | } 64 | 65 | function parse32r (url) { 66 | let id = url.pathname.match(/^\/\w+\/(\w+?)(\.\w+)?$/i); 67 | if (!id) { 68 | return; 69 | } 70 | id = id[1]; 71 | const htmlUrl = `https://m.32r.com/app/${id}.html`; 72 | const appUrl = `https://m.32r.com/downapp/${id}`; 73 | let html; 74 | let res = {}; 75 | return Promise.all([ 76 | fetch( 77 | appUrl, 78 | { 79 | method: "HEAD", 80 | headers: { 81 | Referer: htmlUrl, 82 | }, 83 | }, 84 | ).then(data => { 85 | res = data; 86 | }, console.error), 87 | fetch( 88 | htmlUrl, 89 | ).then(res => res.text()).then(data => { 90 | html = data; 91 | }, console.error), 92 | ]).then(() => { 93 | let json = html && html.match(/