├── .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(/