├── .DS_Store ├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── CHANGELOG.md ├── DISCLAIMER.md ├── LICENSE ├── README.md ├── apk └── SKIP-v3.1.0.apk ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── android │ │ └── skip │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── skip_config.yaml │ │ ├── skip_config_v2.yaml │ │ ├── skip_config_v3.json │ │ └── skip_config_v3.yaml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── android │ │ │ └── skip │ │ │ ├── MyApp.kt │ │ │ ├── data │ │ │ ├── SyncWorker.kt │ │ │ ├── config │ │ │ │ ├── ConfigLoadRepository.kt │ │ │ │ ├── ConfigReadRepository.kt │ │ │ │ └── ConfigViewModel.kt │ │ │ ├── download │ │ │ │ ├── ApkDownloadRepository.kt │ │ │ │ └── ApkDownloadViewModel.kt │ │ │ ├── network │ │ │ │ ├── MyApiNetwork.kt │ │ │ │ ├── ServiceCreator.kt │ │ │ │ └── api │ │ │ │ │ └── MyApiService.kt │ │ │ └── version │ │ │ │ ├── ApkVersionRepository.kt │ │ │ │ └── ApkVersionViewModel.kt │ │ │ ├── dataclass │ │ │ ├── AccessibilityNodeInfoCarrier.kt │ │ │ ├── AppListItem.kt │ │ │ ├── ConfigLoadSchema.kt │ │ │ ├── ConfigPostSchema.kt │ │ │ ├── ConfigReadSchema.kt │ │ │ ├── InspectRecordItem.kt │ │ │ ├── NodeChildSchema.kt │ │ │ ├── NodeRootSchema.kt │ │ │ ├── SemanticVersion.kt │ │ │ ├── SwitchThemeCarrier.kt │ │ │ └── VersionPostSchema.kt │ │ │ ├── service │ │ │ ├── AccessibilityInspectRepository.kt │ │ │ ├── AccessibilityInspectViewModel.kt │ │ │ ├── FloatingWindowService.kt │ │ │ ├── InspectService.kt │ │ │ ├── MyAccessibilityService.kt │ │ │ └── MyForegroundService.kt │ │ │ ├── ui │ │ │ ├── about │ │ │ │ ├── AboutActivity.kt │ │ │ │ ├── config │ │ │ │ │ └── ConfigVersionButton.kt │ │ │ │ ├── download │ │ │ │ │ └── ApkDownloadDialog.kt │ │ │ │ └── version │ │ │ │ │ └── ApkVersionButton.kt │ │ │ ├── alive │ │ │ │ ├── AliveActivity.kt │ │ │ │ ├── backstage │ │ │ │ │ ├── BackstageButton.kt │ │ │ │ │ └── BackstageViewModel.kt │ │ │ │ └── notificationbar │ │ │ │ │ ├── NotificationBarButton.kt │ │ │ │ │ ├── NotificationBarRepository.kt │ │ │ │ │ └── NotificationBarViewModel.kt │ │ │ ├── components │ │ │ │ ├── FlatButton.kt │ │ │ │ ├── FlatButtonMenu.kt │ │ │ │ ├── PictureDialog.kt │ │ │ │ ├── ResourceIcon.kt │ │ │ │ ├── RowContent.kt │ │ │ │ ├── ScaffoldPage.kt │ │ │ │ └── notification │ │ │ │ │ ├── NotificationDialog.kt │ │ │ │ │ └── NotificationDialogViewModel.kt │ │ │ ├── inspect │ │ │ │ ├── InspectActivity.kt │ │ │ │ ├── floating │ │ │ │ │ ├── FloatingWindowButton.kt │ │ │ │ │ ├── FloatingWindowRepository.kt │ │ │ │ │ └── FloatingWindowViewModel.kt │ │ │ │ ├── record │ │ │ │ │ ├── InspectRecordButton.kt │ │ │ │ │ ├── InspectRecordRepository.kt │ │ │ │ │ └── InspectRecordViewModel.kt │ │ │ │ └── start │ │ │ │ │ ├── StartInspectButton.kt │ │ │ │ │ ├── StartInspectRepository.kt │ │ │ │ │ └── StartInspectViewModel.kt │ │ │ ├── main │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── disclaimer │ │ │ │ │ ├── DisclaimerDialog.kt │ │ │ │ │ └── DisclaimerViewModel.kt │ │ │ │ ├── start │ │ │ │ │ ├── StartAccessibilityButton.kt │ │ │ │ │ ├── StartAccessibilityRepository.kt │ │ │ │ │ └── StartAccessibilityViewModel.kt │ │ │ │ └── tutorial │ │ │ │ │ ├── TutorialDialog.kt │ │ │ │ │ └── TutorialViewModel.kt │ │ │ ├── record │ │ │ │ ├── InspectRecordActivity.kt │ │ │ │ ├── dialog │ │ │ │ │ ├── JpegDialog.kt │ │ │ │ │ ├── JpegDialogRepository.kt │ │ │ │ │ └── JpegDialogViewModel.kt │ │ │ │ └── list │ │ │ │ │ ├── InspectListColumn.kt │ │ │ │ │ ├── InspectListPagingSource.kt │ │ │ │ │ ├── InspectListRepository.kt │ │ │ │ │ └── InspectListViewModel.kt │ │ │ ├── settings │ │ │ │ ├── SettingsActivity.kt │ │ │ │ ├── custom │ │ │ │ │ └── CustomButton.kt │ │ │ │ ├── recent │ │ │ │ │ ├── RecentButton.kt │ │ │ │ │ └── RecentViewModel.kt │ │ │ │ ├── strict │ │ │ │ │ ├── StrictButton.kt │ │ │ │ │ ├── StrictRepository.kt │ │ │ │ │ └── StrictViewModel.kt │ │ │ │ ├── theme │ │ │ │ │ ├── SwitchThemeButton.kt │ │ │ │ │ ├── SwitchThemeRepository.kt │ │ │ │ │ └── SwitchThemeViewModel.kt │ │ │ │ ├── tip │ │ │ │ │ ├── TipButton.kt │ │ │ │ │ ├── TipRepository.kt │ │ │ │ │ └── TipViewModel.kt │ │ │ │ └── update │ │ │ │ │ ├── AutoUpdateButton.kt │ │ │ │ │ └── AutoUpdateViewModel.kt │ │ │ ├── theme │ │ │ │ ├── AppTheme.kt │ │ │ │ ├── Color.kt │ │ │ │ └── Typography.kt │ │ │ ├── webview │ │ │ │ └── WebViewActivity.kt │ │ │ └── whitelist │ │ │ │ ├── WhiteListActivity.kt │ │ │ │ ├── WhiteListRepository.kt │ │ │ │ ├── WhiteListViewModel.kt │ │ │ │ ├── list │ │ │ │ ├── AppListColumn.kt │ │ │ │ ├── AppListPagingSource.kt │ │ │ │ ├── AppListRepository.kt │ │ │ │ └── AppListViewModel.kt │ │ │ │ ├── search │ │ │ │ ├── SearchTextField.kt │ │ │ │ └── SearchViewModel.kt │ │ │ │ └── system │ │ │ │ ├── ShowSystemButton.kt │ │ │ │ └── ShowSystemViewModel.kt │ │ │ └── util │ │ │ ├── AccessibilityStateUtils.kt │ │ │ ├── DataStoreUtils.kt │ │ │ ├── DebounceHelper.kt │ │ │ ├── MyToast.kt │ │ │ └── NetworkUtils.kt │ └── res │ │ ├── drawable │ │ ├── all_inclusive.xml │ │ ├── app_registration.xml │ │ ├── arrow_back.xml │ │ ├── backend_lock.jpg │ │ ├── block.xml │ │ ├── check_circle.xml │ │ ├── counter_1.xml │ │ ├── counter_2.xml │ │ ├── counter_3.xml │ │ ├── counter_4.xml │ │ ├── error.xml │ │ ├── favicon32.png │ │ ├── fit_screen.xml │ │ ├── float_landscape_2.xml │ │ ├── help.xml │ │ ├── hide_image.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── info.xml │ │ ├── lists.xml │ │ ├── notifications.xml │ │ ├── photo_camera.xml │ │ ├── point_scan.xml │ │ ├── settings.xml │ │ ├── sync.xml │ │ ├── target.xml │ │ ├── theme_auto.xml │ │ ├── theme_dark.xml │ │ ├── theme_light.xml │ │ ├── tune.xml │ │ └── warning.xml │ │ ├── layout │ │ └── floating_window.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── accessibility.xml │ │ ├── backup_rules.xml │ │ ├── data_extraction_rules.xml │ │ └── file_paths.xml │ └── test │ └── java │ └── com │ └── android │ └── skip │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── capture ├── 0f84d874-ada4-4b90-9829-a99f43950071.zip ├── 20f3db03-af60-4dc3-885a-b954b985931f.zip ├── 28b730a3-5ac7-44c7-8f8a-9daf1b8effbe.zip ├── 2adae253-623d-4fd3-ad75-b5b32b667dcd.zip ├── 368caf09-c152-4f6b-b8dd-3ee4718df65d.zip ├── 3737625b-383a-4891-aada-493e811f048a.zip ├── 43bcef4b-4b5b-4b63-a7f2-ad28e4f5f690.zip ├── 450b0add-b8e2-4cac-9b49-015744bb786f.zip ├── 5656e116-5a93-44da-8687-1a1dec170984.zip ├── 57861a13-956b-4ae1-be60-d77c33b82010.zip ├── 7aa2544b-8a80-41f3-bf0c-ee03c7cf83b2.zip ├── 7ba85f98-5b07-4947-95e0-dca6431b1fc6.zip ├── 7d45a0ad-f0ce-4856-b38b-dd545bfadeb5.zip ├── 812b304e-63fd-42da-b200-7497a2e635c4.zip ├── 84603cb6-3647-42aa-a0f9-e013a3c1e74c.zip ├── 88ad5f6b-e65b-4424-be8e-ab57e0eb613b.zip ├── 92e729ea-dd19-4384-93af-ec7581f306cf.zip ├── 94969846-73e3-41de-aca0-ad9994566fc6.zip ├── a310356a-4841-40fd-8a4f-314e77318230.zip ├── ab69a19e-56e8-4983-9656-cdbab1b09682.zip ├── b8592c72-1a8a-42ea-8022-2ba15bc83444.zip ├── bb3de897-19d4-4c6d-ac7a-e11130420b4a.zip ├── c1c0b4c7-9bed-4f11-93e8-6fcd08bb4717.zip ├── cfe9660a-1d45-4904-a8a7-8d846faa1a49.zip ├── dba416c1-f18d-45c4-8042-16e2c15bead5.zip ├── e2c456c9-2db5-4ff9-9f79-f42acf557358.zip ├── e835b293-7845-4988-9423-fe7f9d740d43.zip ├── ee283a25-cbbf-43b0-84dd-aaf9ccf24c83.zip └── f074f9ba-2fdd-4885-8b16-df8afdcbb46d.zip ├── dist └── .gitkeep ├── docs ├── .vitepress │ ├── config.mts │ ├── index.css │ └── theme │ │ ├── custom.css │ │ └── index.js ├── CNAME ├── advance │ ├── custom-config │ │ ├── data-structure.md │ │ ├── field-meaning.md │ │ ├── full-reference.md │ │ └── intro.md │ └── layout-inspect │ │ ├── intro.md │ │ └── usage.md ├── guide │ ├── about │ │ └── intro.md │ ├── contributing │ │ ├── config-file.md │ │ ├── core-logic.md │ │ └── layout-view.md │ ├── intro │ │ ├── DownloadApp.vue │ │ ├── download-app.md │ │ ├── getting-started.md │ │ └── what-is-skip.md │ ├── keep-alive │ │ └── intro.md │ ├── settings │ │ └── intro.md │ ├── usage │ │ ├── background-process-keep-alive.md │ │ └── enable-accessibility-service.md │ └── white-list │ │ └── intro.md ├── index.md ├── inspect │ ├── InspectContainer.vue │ ├── InspectHeader.vue │ ├── InspectHome.vue │ ├── InspectTable.vue │ ├── NodePic.vue │ ├── NodeTable.vue │ ├── NodeTree.vue │ ├── hook │ │ └── useZip.ts │ ├── index.md │ ├── inspect-node-container │ │ └── NodeCode.vue │ └── types.ts ├── my-index-db │ ├── file-item.ts │ ├── file-table.ts │ └── index.ts ├── public │ ├── 5f686585-c5d0-4057-93b2-c54832ffb393.zip │ ├── android-rect.png │ ├── click-button-on-the-screen-dark.png │ ├── click-button-on-the-screen-light.png │ ├── images │ │ ├── favicon.ico │ │ ├── getting-started-step-1.png │ │ ├── getting-started-step-2.png │ │ ├── getting-started-step-3.png │ │ ├── getting-started-step-4.png │ │ ├── getting-started-step-5.png │ │ ├── images.7z │ │ ├── inspect-intro.png │ │ ├── logo.png │ │ └── main-interface-light.png │ ├── js │ │ └── baidu-analytics.js │ ├── layout-reference.png │ ├── main-interface-dark.png │ ├── main-interface-light.png │ ├── skip-bottom-screenshot.jpg │ ├── skip-icon.png │ ├── use-accessibility-dark.png │ ├── use-accessibility-light.png │ ├── xiaomi-app-backend-lock-dark.png │ ├── xiaomi-app-backend-lock-light.png │ ├── xiaomi-enable-self-start-dark.png │ ├── xiaomi-enable-self-start-light.png │ ├── xiaomi-enable-self-start.jpg │ ├── xiaomi-ignoring-battery-optimization-dark.png │ └── xiaomi-ignoring-battery-optimization-light.png └── visual │ ├── Header.vue │ ├── Home.vue │ ├── LeftForm.vue │ ├── RightCode.vue │ └── index.md ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── gui ├── .DS_Store ├── backend_lock.jpg ├── background.png ├── favicon-16x16.png ├── favicon-32x32.png ├── main.png ├── start_icon │ ├── .DS_Store │ ├── android │ │ ├── ic_launcher.png │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-ldpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ └── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ └── ios │ │ ├── .DS_Store │ │ └── AppIcon.appiconset │ │ ├── icon-1024.png │ │ ├── icon-20-ipad.png │ │ ├── icon-20@2x-ipad.png │ │ ├── icon-20@2x.png │ │ ├── icon-20@3x.png │ │ ├── icon-29-ipad.png │ │ ├── icon-29.png │ │ ├── icon-29@2x-ipad.png │ │ ├── icon-29@2x.png │ │ ├── icon-29@3x.png │ │ ├── icon-40.png │ │ ├── icon-40@2x.png │ │ ├── icon-40@3x.png │ │ ├── icon-60@2x.png │ │ ├── icon-60@3x.png │ │ ├── icon-76.png │ │ ├── icon-76@2x.png │ │ └── icon-83.5@2x.png └── svg │ ├── all_inclusive_FILL0_wght400_GRAD0_opsz24.svg │ ├── app_registration_FILL0_wght400_GRAD0_opsz24.svg │ ├── arrow_back_FILL0_wght400_GRAD0_opsz24.svg │ ├── block_FILL0_wght400_GRAD0_opsz24.svg │ ├── brightness_4_FILL0_wght400_GRAD0_opsz24.svg │ ├── brightness_5_FILL0_wght400_GRAD0_opsz24.svg │ ├── brightness_6_FILL0_wght400_GRAD0_opsz24.svg │ ├── check_circle_FILL0_wght400_GRAD0_opsz24.svg │ ├── cloud_download_FILL0_wght400_GRAD0_opsz24.svg │ ├── counter_1_FILL0_wght400_GRAD0_opsz24.svg │ ├── counter_2_FILL0_wght400_GRAD0_opsz24.svg │ ├── counter_3_FILL0_wght400_GRAD0_opsz24.svg │ ├── counter_4_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg │ ├── error_24dp_000000_FILL0_wght0_GRAD0_opszNaN.svg │ ├── fit_screen_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg │ ├── float_landscape_2_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg │ ├── help_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg │ ├── hide_image_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg │ ├── info_FILL0_wght400_GRAD0_opsz24.svg │ ├── list_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg │ ├── lists_FILL0_wght400_GRAD0_opsz24.svg │ ├── notifications_FILL0_wght400_GRAD0_opsz24.svg │ ├── photo_camera_64dp_000000_FILL1_wght400_GRAD0_opsz48.svg │ ├── point_scan_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg │ ├── screenshot_keyboard_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg │ ├── settings_FILL0_wght400_GRAD0_opsz24.svg │ ├── sync_FILL0_wght400_GRAD0_opsz24.svg │ ├── target_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg │ ├── tune_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg │ └── warning_FILL0_wght400_GRAD0_opsz24.svg ├── jest.config.ts ├── package-lock.json ├── package.json ├── postcss.config.js ├── scripts ├── __tests__ │ ├── examples │ │ ├── apk-has-version │ │ │ └── SKIP-v2.1.1.apk │ │ ├── apk-multiple │ │ │ ├── SKIP-v2.0.0.apk │ │ │ └── SKIP-v2.0.1.apk │ │ ├── apk-no-version │ │ │ └── SKIP.apk │ │ ├── apk-not-apk │ │ │ └── SKIP-v2.0.0.txt │ │ ├── correct-yaml.yaml │ │ ├── missing-packageName.yaml │ │ ├── only-packageName.yaml │ │ ├── packageName-empty.yaml │ │ ├── packageName-leading-space.yaml │ │ ├── packageName-not-string.yaml │ │ ├── skipBounds-bound-empty.yaml │ │ ├── skipBounds-bound-not-4-parts.yaml │ │ ├── skipBounds-bound-not-number.yaml │ │ ├── skipBounds-bound-not-string.yaml │ │ ├── skipBounds-missing-bound.yaml │ │ ├── skipBounds-missing-resolution.yaml │ │ ├── skipBounds-not-array.yaml │ │ ├── skipBounds-resolution-empty.yaml │ │ ├── skipBounds-resolution-not-2-parts.yaml │ │ ├── skipBounds-resolution-not-number.yaml │ │ ├── skipBounds-resolution-not-string.yaml │ │ ├── skipIds-id-empty.yaml │ │ ├── skipIds-id-leading-space.yaml │ │ ├── skipIds-id-not-string.yaml │ │ ├── skipIds-missing-id.yaml │ │ ├── skipIds-not-array.yaml │ │ ├── skipTexts-length-negative.yaml │ │ ├── skipTexts-length-not-number.yaml │ │ ├── skipTexts-not-array.yaml │ │ ├── skipTexts-text-empty.yaml │ │ ├── skipTexts-text-leading-space.yaml │ │ ├── skipTexts-text-not-string.yaml │ │ ├── skipTexts-unknown-keys.yaml │ │ └── unknown-keys.yaml │ ├── release-file.test.ts │ └── yaml-check.test.ts ├── index.ts ├── release-file.ts └── yaml-check.ts ├── settings.gradle.kts ├── tailwind.config.js └── tsconfig.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/.DS_Store -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # 构建 VitePress 站点并将其部署到 GitHub Pages 的示例工作流程 2 | # 3 | name: Deploy VitePress site to Pages 4 | 5 | on: 6 | # 在针对 `main` 分支的推送上运行。如果你 7 | # 使用 `master` 分支作为默认分支,请将其更改为 `master` 8 | push: 9 | branches: [main] 10 | 11 | # 允许你从 Actions 选项卡手动运行此工作流程 12 | workflow_dispatch: 13 | 14 | # 设置 GITHUB_TOKEN 的权限,以允许部署到 GitHub Pages 15 | permissions: 16 | contents: read 17 | pages: write 18 | id-token: write 19 | 20 | # 只允许同时进行一次部署,跳过正在运行和最新队列之间的运行队列 21 | # 但是,不要取消正在进行的运行,因为我们希望允许这些生产部署完成 22 | concurrency: 23 | group: pages 24 | cancel-in-progress: false 25 | 26 | jobs: 27 | # 构建工作 28 | build: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 # 如果未启用 lastUpdated,则不需要 35 | # - uses: pnpm/action-setup@v3 # 如果使用 pnpm,请取消注释 36 | # - uses: oven-sh/setup-bun@v1 # 如果使用 Bun,请取消注释 37 | - name: Setup Node 38 | uses: actions/setup-node@v4 39 | with: 40 | node-version: 20 41 | cache: npm # 或 pnpm / yarn 42 | - name: Setup Pages 43 | uses: actions/configure-pages@v4 44 | - name: Install dependencies 45 | run: npm ci 46 | - name: Build with VitePress 47 | run: npm run docs:build 48 | - name: Release 49 | run: npm run release-file 50 | - name: Upload artifact 51 | uses: actions/upload-pages-artifact@v3 52 | with: 53 | path: docs/.vitepress/dist 54 | 55 | # 部署工作 56 | deploy: 57 | environment: 58 | name: github-pages 59 | url: ${{ steps.deployment.outputs.page_url }} 60 | needs: build 61 | runs-on: ubuntu-latest 62 | name: Deploy 63 | steps: 64 | - name: Deploy to GitHub Pages 65 | id: deployment 66 | uses: actions/deploy-pages@v4 -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v3.1.0 - 2025.04.27 2 | 3 | - 新增「悬浮窗按钮」用于执行布局检查功能 4 | - 新增应用白名单搜索功能 5 | - 新增「严格模式」下可在应用内执行「跳过」 6 | - 修复加载的配置文件包含 Unicode 编码时未转换为中文的问题 7 | - 修复点击右上角「功能介绍」后菜单未自动关闭的问题 8 | - 修复断网后无法使用的问题 9 | - 修复「后台任务隐藏」失效的问题 10 | 11 | ## v3.0.0 - 2024.09.27 12 | 13 | ### 新增 14 | 15 | - 依赖库版本更新,并基于 Android MVVM 架构重构项目 16 | - 新增「发生故障」标识,方便判断无障碍是否发生故障 17 | - 新增「布局检查」功能 18 | - 新增「自定义配置」功能 [#188](https://github.com/GuoXiCheng/SKIP/issues/188) [#237](https://github.com/GuoXiCheng/SKIP/issues/237) 19 | - 新的可配置项:`activityName`、`click` 20 | - 新增「后台隐藏」选项 [#196](https://github.com/GuoXiCheng/SKIP/issues/196) 21 | - 应用保活新增常驻通知栏功能 22 | - 新增「严格模式」启用开关 23 | - 页面右上角新增「功能介绍」入口 24 | - 新增后台定时自动检查更新功能 25 | -------------------------------------------------------------------------------- /DISCLAIMER.md: -------------------------------------------------------------------------------- 1 | # 免责声明 2 | 3 | 感谢您信任并使用「SKIP」,为了保障您的权益,请在使用「SKIP」前,仔细阅读本免责声明。 4 | 5 | 本项目遵循 GNU 通用公共许可证第 3 版(GPLv3)发布。在使用本项目的过程中,请注意以下事项: 6 | 7 | ## 一、无任何担保 8 | 9 | 本项目“按现状”(as-is)提供,开发者不对其做出任何明示或暗示的担保。无论是适销性、特定用途的适用性,还是非侵权性,本项目均不做任何承诺。使用本项目的风险完全由您自己承担。 10 | 11 | ## 二、不承担任何责任 12 | 13 | 在适用法律允许的最大范围内,项目的开发者、贡献者不对因使用或无法使用本项目或其衍生物而引起的任何形式的损失、损害或法律责任负责。这包括但不限于直接损失、间接损失、特殊损害、偶然损害或惩罚性赔偿。 14 | 15 | ## 三、知识产权声明 16 | 17 | 本项目中所涉及的任何代码、文档或其他文件均受 GPLv3 许可证的约束。您可以自由使用、修改和分发本项目,但必须保留原始许可条款,并且在适用的情况下,任何分发的衍生作品也必须遵循相同的许可证。 18 | 19 | ## 四、责任自负 20 | 21 | 您在使用本项目的过程中,可能需要遵守您所在国家或地区的相关法律和规定。请确保您在使用、修改或分发本项目之前,已经了解并遵循所有适用的法律要求。项目的开发者和贡献者不承担您在使用本项目时产生的任何法律后果。 22 | 23 | ## 五、第三方依赖 24 | 25 | 本项目包含来自第三方的开源组件或依赖项,这些第三方的许可条款可能与 GPLv3 不同。请在使用这些组件时仔细阅读并遵守相应的许可证要求。 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHub](https://img.shields.io/github/license/GuoXiCheng/SKIP) ![GitHub all releases](https://img.shields.io/github/downloads/GuoXiCheng/SKIP/total) ![GitHub Repo stars](https://img.shields.io/github/stars/GuoXiCheng/SKIP) 2 | 3 | ## SKIP 介绍 4 | 5 | SKIP 是一款免费开源的安卓应用,旨在利用安卓无障碍服务帮助用户快速点击 APP 开屏广告的跳过按钮,让你的使用体验更加流畅。 6 | 7 | ## 主界面预览 8 | 9 | https://skip.guoxicheng.top//images/main-interface-light.png 10 | 11 | ## 文档 12 | 13 | [SKIP 文档](https://skip.guoxicheng.top/) 14 | 15 | ## 许可证 16 | 17 | [GPL-3.0 license](https://github.com/GuoXiCheng/SKIP/blob/main/LICENSE) 18 | 19 | ## 补充说明 20 | 21 | [免责声明](https://github.com/GuoXiCheng/SKIP/blob/main/DISCLAIMER.md) 22 | 23 | ## Star History 24 | 25 | [![Star History Chart](https://api.star-history.com/svg?repos=GuoXiCheng/SKIP&type=Date)](https://star-history.com/#GuoXiCheng/SKIP&Date) 26 | 27 | ## 自定义规则订阅 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 48 | 49 | 50 | 51 |
规则描述订阅链接作者源码地址
默认订阅https://skip.guoxicheng.top/skip_config_v3.yamlGuoXiCheng 44 | skip_config_v3 47 |
52 | 53 | > 添加自定义订阅规则订阅请修改[README.md](https://github.com/GuoXiCheng/SKIP/edit/main/README.md)提交 PR。 54 | -------------------------------------------------------------------------------- /apk/SKIP-v3.1.0.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/apk/SKIP-v3.1.0.apk -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/android/skip/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.android.skip", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/assets/skip_config.yaml: -------------------------------------------------------------------------------- 1 | # 蚂蚁财富 2 | - package_name: com.antfortune.wealth 3 | max_click_count: 1 4 | skip_bounds: 5 | - 1440,3024#1139,224,1370,315 6 | 7 | # 酷安 8 | - package_name: com.coolapk.market 9 | skip_id: com.byted.pangle.m:id/tt_splash_skip_btn 10 | skip_bounds: 11 | - 1440,3024#1116,219,1332,316 12 | 13 | # 支付宝 14 | - package_name: com.eg.android.AlipayGphone 15 | max_click_count: 0 16 | 17 | # Github 18 | - package_name: com.github.android 19 | max_click_count: 0 20 | 21 | # 京东阅读 22 | - package_name: com.jd.app.reader 23 | skip_id: com.jd.app.reader:id/mJumpBtn 24 | skip_bounds: 25 | - 1440,3024#1209,113,1338,165 26 | 27 | # 铁路12306 28 | - package_name: com.MobileTicket 29 | skip_id: com.MobileTicket:id/tv_skip 30 | 31 | # 爱奇艺 32 | - package_name: com.qiyi.video 33 | skip_text: 关闭 34 | 35 | # 爱奇艺极速版 36 | - package_name: com.qiyi.video.lite 37 | skip_text: 关闭 38 | 39 | # 腾讯微云 40 | - package_name: com.qq.qcloud 41 | skip_id: com.qq.qcloud:id/gdt_ad_text 42 | skip_bounds: 43 | - 1440,3024#1223,196,1384,308 44 | 45 | # 夸克 46 | - package_name: com.quark.browser 47 | max_click_count: 0 48 | 49 | # 微信 50 | - package_name: com.tencent.mm 51 | max_click_count: 0 52 | 53 | # 华为自带时钟 54 | - package_name: com.android.deskclock 55 | max_click_count: 0 56 | 57 | # CSDN 58 | - package_name: net.csdn.csdnplus 59 | skip_id: com.byted.pangle.m:id/tt_splash_skip_btn 60 | 61 | 62 | # 小度 63 | - package_name: com.baidu.duer.superapp 64 | skip_id: com.byted.pangle:id/tt_splash_skip_btn 65 | 66 | 67 | # VIVO 应用商店, 跳过开屏应用推荐 68 | - package_name: com.bbk.appstore 69 | skip_text: 进入首页 70 | skip_ids: com.bbk.appstore:id/vbutton_title 71 | 72 | # 民生银行 73 | - package_name: cn.com.cmbc.newmbank 74 | skip_id: cn.com.cmbc.newmbank:id/view_count_down 75 | 76 | # 菜鸟 77 | - package_name: com.cainiao.wireless 78 | skip_id: com.cainiao.wireless:id/tt_splash_skip_btn -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/MyApp.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Application 5 | import android.content.Context 6 | import android.os.Build 7 | import androidx.hilt.work.HiltWorkerFactory 8 | import androidx.work.Configuration 9 | import com.android.skip.util.DataStoreUtils 10 | import com.blankj.utilcode.util.LogUtils 11 | import com.blankj.utilcode.util.Utils 12 | import dagger.hilt.android.HiltAndroidApp 13 | import javax.inject.Inject 14 | 15 | @HiltAndroidApp 16 | class MyApp : Application(), Configuration.Provider { 17 | @Inject 18 | lateinit var workerFactory: HiltWorkerFactory 19 | 20 | override fun onCreate() { 21 | super.onCreate() 22 | context = this 23 | deviceName = "${Build.MANUFACTURER} ${Build.MODEL}" 24 | 25 | DataStoreUtils.init(this) 26 | 27 | Utils.init(this) 28 | LogUtils.getConfig() 29 | .setLogSwitch(false) // 是否输出日志开关 30 | .setConsoleSwitch(true) // 是否在控制台输出日志开关 31 | .setGlobalTag("SKIP_APP") // 全局标签 32 | .setLog2FileSwitch(true) // 是否写入日志文件开关 33 | .setDir("${this.cacheDir}/logs") // 日志文件目录 34 | .setFilePrefix("log") // 日志文件前缀 35 | .setBorderSwitch(true) // 日志边框开关 36 | .setConsoleFilter(LogUtils.V) // 控制台过滤器 37 | .setFileFilter(LogUtils.V) // 文件过滤器 38 | .setStackDeep(1) // 栈深度 39 | .setSaveDays(7) // 日志保留天数 40 | } 41 | 42 | companion object { 43 | @SuppressLint("StaticFieldLeak") 44 | lateinit var context: Context 45 | 46 | lateinit var deviceName: String 47 | } 48 | 49 | override fun getWorkManagerConfiguration() = 50 | Configuration.Builder() 51 | .setWorkerFactory(workerFactory) 52 | .build() 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/data/SyncWorker.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.data 2 | 3 | import android.content.Context 4 | import androidx.hilt.work.HiltWorker 5 | import androidx.work.WorkerParameters 6 | import androidx.work.CoroutineWorker 7 | import com.android.skip.data.config.ConfigReadRepository 8 | import com.android.skip.data.version.ApkVersionRepository 9 | import com.blankj.utilcode.util.LogUtils 10 | import dagger.assisted.Assisted 11 | import dagger.assisted.AssistedInject 12 | 13 | @HiltWorker 14 | class SyncWorker@AssistedInject constructor( 15 | @Assisted context: Context, 16 | @Assisted workerParams: WorkerParameters, 17 | private val configReadRepository: ConfigReadRepository, 18 | private val versionRepository: ApkVersionRepository 19 | ) : CoroutineWorker(context, workerParams) { 20 | override suspend fun doWork(): Result { 21 | return try { 22 | LogUtils.d("SyncWorker doWork") 23 | val configPostSchema = configReadRepository.readConfig() 24 | configReadRepository.changeConfigPostState(configPostSchema) 25 | 26 | versionRepository.checkVersion() 27 | Result.success() 28 | } catch (e: Exception) { 29 | LogUtils.e(e) 30 | Result.failure() 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/data/config/ConfigViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.data.config 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.android.skip.R 7 | import com.android.skip.dataclass.ConfigPostSchema 8 | import com.android.skip.dataclass.ConfigState 9 | import com.blankj.utilcode.util.StringUtils.getString 10 | import dagger.hilt.android.lifecycle.HiltViewModel 11 | import kotlinx.coroutines.launch 12 | import javax.inject.Inject 13 | 14 | @HiltViewModel 15 | class ConfigViewModel @Inject constructor( 16 | private val configReadRepository: ConfigReadRepository, 17 | private val configLoadRepository: ConfigLoadRepository 18 | ) : ViewModel() { 19 | var configPostState: LiveData = configReadRepository.configPostState 20 | 21 | fun readConfig() { 22 | viewModelScope.launch { 23 | val configPostSchema = configReadRepository.readConfig() 24 | configReadRepository.changeConfigPostState(configPostSchema) 25 | } 26 | } 27 | 28 | fun loadConfig(configPostSchema: ConfigPostSchema) { 29 | if (configPostSchema.status == ConfigState.SUCCESS) { 30 | val configMap = configReadRepository.handleConfig(configPostSchema) 31 | if (configMap != null) { 32 | configLoadRepository.loadConfig(configMap) 33 | } else { 34 | configLoadRepository.loadConfig(mutableMapOf()) 35 | changeConfigPostState(ConfigPostSchema(ConfigState.FAIL, getString(R.string.invalid_config))) 36 | } 37 | } 38 | } 39 | 40 | fun changeConfigPostState(configPostSchema: ConfigPostSchema) = 41 | configReadRepository.changeConfigPostState(configPostSchema) 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/data/download/ApkDownloadRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.data.download 2 | 3 | import com.android.skip.data.network.MyApiNetwork 4 | import kotlinx.coroutines.Dispatchers 5 | import kotlinx.coroutines.withContext 6 | import javax.inject.Inject 7 | import javax.inject.Singleton 8 | 9 | @Singleton 10 | class ApkDownloadRepository @Inject constructor( 11 | private val myApiNetwork: MyApiNetwork 12 | ) { 13 | suspend fun downloadAPK(latestVersion: String, onDownloadProcess: (process: Int) -> Unit) = 14 | withContext(Dispatchers.IO) { 15 | myApiNetwork.fetchAPK(latestVersion, onDownloadProcess) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/data/download/ApkDownloadViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.data.download 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlinx.coroutines.launch 10 | import kotlinx.coroutines.withContext 11 | import javax.inject.Inject 12 | 13 | @HiltViewModel 14 | class ApkDownloadViewModel @Inject constructor( 15 | private val apkDownloadRepository: ApkDownloadRepository 16 | ) : ViewModel() { 17 | private val _isShowDialog = MutableLiveData(false) 18 | val isShowDialog: LiveData = _isShowDialog 19 | 20 | private val _apkDownloadProcess = MutableLiveData(0) 21 | val apkDownloadProcess: LiveData = _apkDownloadProcess 22 | 23 | fun changeDialogState(showDialog: Boolean) { 24 | _isShowDialog.postValue(showDialog) 25 | } 26 | 27 | private fun changeApkDownloadProcess(process: Int) { 28 | _apkDownloadProcess.postValue(process) 29 | } 30 | 31 | fun downloadAPK(latestVersion: String) { 32 | viewModelScope.launch { 33 | withContext(Dispatchers.IO) { 34 | apkDownloadRepository.downloadAPK(latestVersion) { 35 | changeApkDownloadProcess(it) 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/data/network/ServiceCreator.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.data.network 2 | 3 | import okhttp3.OkHttpClient 4 | import retrofit2.Retrofit 5 | import retrofit2.converter.gson.GsonConverterFactory 6 | import retrofit2.converter.scalars.ScalarsConverterFactory 7 | 8 | object ServiceCreator { 9 | private const val BASE_URL = "https://skip.guoxicheng.top" 10 | 11 | private val httpClient = OkHttpClient.Builder() 12 | 13 | private val builder = Retrofit.Builder() 14 | .baseUrl(BASE_URL) 15 | .client(httpClient.build()) 16 | .addConverterFactory(ScalarsConverterFactory.create()) 17 | .addConverterFactory(GsonConverterFactory.create()) 18 | 19 | private val retrofit = builder.build() 20 | 21 | fun create(serviceClass: Class): T = retrofit.create(serviceClass) 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/data/network/api/MyApiService.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.data.network.api 2 | 3 | import okhttp3.ResponseBody 4 | import retrofit2.Response 5 | import retrofit2.http.GET 6 | import retrofit2.http.Path 7 | import retrofit2.http.Streaming 8 | import retrofit2.http.Url 9 | 10 | interface MyApiService { 11 | @GET 12 | suspend fun getConfigFromUrl(@Url url: String): Response 13 | 14 | @GET("/latest_version.txt") 15 | suspend fun getLatestVersion(): Response 16 | 17 | @GET("/SKIP-v{version}.apk") 18 | @Streaming 19 | suspend fun downloadAPK(@Path("version") latestVersion: String): Response 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/data/version/ApkVersionViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.data.version 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.android.skip.R 6 | import com.android.skip.util.DataStoreUtils 7 | import com.blankj.utilcode.util.StringUtils.getString 8 | import dagger.hilt.android.lifecycle.HiltViewModel 9 | import kotlinx.coroutines.launch 10 | import javax.inject.Inject 11 | 12 | @HiltViewModel 13 | class ApkVersionViewModel @Inject constructor( 14 | private val apkVersionRepository: ApkVersionRepository 15 | ) : ViewModel() { 16 | val versionPostState = apkVersionRepository.versionPostState 17 | 18 | fun checkVersion() { 19 | viewModelScope.launch { 20 | apkVersionRepository.checkVersion() 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/dataclass/AccessibilityNodeInfoCarrier.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.dataclass 2 | 3 | import android.view.accessibility.AccessibilityNodeInfo 4 | 5 | data class AccessibilityNodeInfoCarrier( 6 | val node: AccessibilityNodeInfo, 7 | val depth: Int, 8 | val parentId: Int, 9 | val nodeId: Int 10 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/dataclass/AppListItem.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.dataclass 2 | 3 | import android.graphics.drawable.Drawable 4 | import androidx.compose.runtime.MutableState 5 | 6 | data class AppListItem( 7 | val appName: String, 8 | val packageName: String, 9 | val appIcon: Drawable, 10 | val checked: Boolean 11 | ) 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/dataclass/ConfigLoadSchema.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.dataclass 2 | 3 | import android.graphics.Rect 4 | 5 | data class LoadSkipText( 6 | val text: String, 7 | val activityName: String? = null, 8 | val length: Int? = null, 9 | val click: Rect? = null 10 | ) 11 | 12 | data class LoadSkipId( 13 | val id: String, 14 | val activityName: String? = null, 15 | val click: Rect? = null 16 | ) 17 | 18 | data class LoadSkipBound( 19 | val bound: Rect, 20 | val activityName: String? = null, 21 | val click: Rect? = null 22 | ) 23 | 24 | data class ConfigLoadSchema( 25 | val packageName: String, 26 | var skipTexts: List? = null, 27 | val skipIds: List? = null, 28 | val skipBounds: List? = null 29 | ) 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/dataclass/ConfigPostSchema.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.dataclass 2 | 3 | enum class ConfigState { 4 | SUCCESS, PENDING, FAIL 5 | } 6 | 7 | data class ConfigPostSchema( 8 | val status: ConfigState, 9 | val value: String, 10 | val configReadSchemaList: List? = null 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/dataclass/ConfigReadSchema.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.dataclass 2 | 3 | data class ReadSkipText( 4 | val text: String, 5 | val length: Int? = null, 6 | val activityName: String? = null, 7 | val click: String? = null 8 | ) 9 | 10 | data class ReadSkipId( 11 | val id: String, 12 | val activityName: String? = null, 13 | val click: String? = null 14 | ) 15 | 16 | data class ReadSkipBound( 17 | val bound: String, 18 | val activityName: String? = null, 19 | val click: String? = null 20 | ) 21 | 22 | data class ConfigReadSchema( 23 | val packageName: String, 24 | val skipTexts: List? = null, 25 | val skipIds: List? = null, 26 | val skipBounds: List? = null 27 | ) 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/dataclass/InspectRecordItem.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.dataclass 2 | 3 | import android.graphics.drawable.Drawable 4 | 5 | data class InspectRecordItem( 6 | val fileId: String, 7 | val appIcon: Drawable, 8 | val appName: String, 9 | val packageName: String, 10 | val activityName: String, 11 | val createTime: Long 12 | ) 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/dataclass/NodeChildSchema.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.dataclass 2 | 3 | data class NodeChildSchema( 4 | val depth: Int, 5 | val childCount: Int, 6 | val parentId: Int, 7 | val nodeId: Int, 8 | val left: Int, 9 | val top: Int, 10 | val right: Int, 11 | val bottom: Int, 12 | val isClickable: Boolean, 13 | var className: String? = null, 14 | var text: String? = null, 15 | var viewIdResourceName: String? = null 16 | ) 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/dataclass/NodeRootSchema.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.dataclass 2 | 3 | data class NodeRootSchema( 4 | val fileId: String, 5 | val appName: String, 6 | val packageName: String, 7 | val activityName: String, 8 | val screenHeight: Int, 9 | val screenWidth: Int, 10 | val createTime: Long, 11 | val deviceName: String, 12 | val nodes: MutableList 13 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/dataclass/SemanticVersion.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.dataclass 2 | 3 | data class SemanticVersion(val major: Int, val minor: Int, val patch: Int) -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/dataclass/SwitchThemeCarrier.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.dataclass 2 | 3 | enum class ThemeMode { 4 | UI_LIGHT,UI_DARK, UI_AUTO 5 | } 6 | 7 | data class SwitchThemeCarrier(val themeMode: ThemeMode, val themeName: Int, val themeIcon: Int) 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/dataclass/VersionPostSchema.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.dataclass 2 | 3 | enum class VersionState { 4 | PENDING, CURRENT_LATEST, DISCOVER_LATEST 5 | } 6 | 7 | data class VersionPostSchema( 8 | val status: VersionState, 9 | val value: String, 10 | val latestVersion: String 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/service/AccessibilityInspectViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.service 2 | 3 | import androidx.lifecycle.ViewModel 4 | import dagger.hilt.android.lifecycle.HiltViewModel 5 | import javax.inject.Inject 6 | 7 | @HiltViewModel 8 | class AccessibilityInspectViewModel @Inject constructor( 9 | accessibilityInspectRepository: AccessibilityInspectRepository, 10 | ) : ViewModel() { 11 | val accessibilityInspectSuccess = accessibilityInspectRepository.accessibilityInspectSuccess 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/service/MyForegroundService.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.service 2 | 3 | import android.app.Notification 4 | import android.app.NotificationChannel 5 | import android.app.NotificationManager 6 | import android.app.PendingIntent 7 | import android.app.Service 8 | import android.content.Context 9 | import android.content.Intent 10 | import android.os.IBinder 11 | import androidx.core.app.NotificationCompat 12 | import com.android.skip.R 13 | import com.android.skip.ui.main.MainActivity 14 | 15 | class MyForegroundService : Service() { 16 | private lateinit var notification: Notification 17 | 18 | override fun onBind(p0: Intent?): IBinder? { 19 | TODO("Not yet implemented") 20 | } 21 | 22 | override fun onCreate() { 23 | super.onCreate() 24 | 25 | val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 26 | val channel = NotificationChannel( 27 | "SKIP_FOREGROUND_SERVICE", 28 | "SKIP 无障碍服务", 29 | NotificationManager.IMPORTANCE_DEFAULT 30 | ) 31 | manager.createNotificationChannel(channel) 32 | 33 | val intent = Intent(this, MainActivity::class.java) 34 | val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) 35 | notification = NotificationCompat.Builder(this, "SKIP_FOREGROUND_SERVICE") 36 | .setContentTitle(getString(R.string.app_name)) 37 | .setContentText(getString(R.string.notification_accessibility_service_running)) 38 | .setSmallIcon(R.drawable.favicon32) 39 | // .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.favicon32)) 40 | .setContentIntent(pendingIntent) 41 | .setOngoing(true) 42 | .build() 43 | 44 | startForeground(2, notification) 45 | } 46 | 47 | override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 48 | super.onStartCommand(intent, flags, startId) 49 | startForeground(2, notification) 50 | return START_STICKY 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/about/config/ConfigVersionButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.about.config 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.livedata.observeAsState 5 | import com.android.skip.R 6 | import com.android.skip.data.config.ConfigViewModel 7 | import com.android.skip.dataclass.ConfigPostSchema 8 | import com.android.skip.dataclass.ConfigState 9 | import com.android.skip.ui.components.FlatButton 10 | import com.android.skip.ui.components.RowContent 11 | import com.blankj.utilcode.util.StringUtils.getString 12 | 13 | @Composable 14 | fun ConfigVersionButton(configViewModel: ConfigViewModel) { 15 | val configState = configViewModel.configPostState.observeAsState() 16 | 17 | FlatButton(content = { 18 | RowContent( 19 | title = R.string.about_config_version, 20 | subTitle = configState.value?.value 21 | ) 22 | }, onClick = { 23 | configViewModel.changeConfigPostState( 24 | ConfigPostSchema( 25 | ConfigState.PENDING, 26 | getString(R.string.checking) 27 | ) 28 | ) 29 | configViewModel.readConfig() 30 | }) 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/about/version/ApkVersionButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.about.version 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.livedata.observeAsState 5 | import com.android.skip.R 6 | import com.android.skip.data.version.ApkVersionViewModel 7 | import com.android.skip.ui.components.FlatButton 8 | import com.android.skip.ui.components.RowContent 9 | import com.android.skip.util.DataStoreUtils 10 | import com.blankj.utilcode.util.StringUtils.getString 11 | 12 | @Composable 13 | fun ApkVersionButton(apkVersionViewModel: ApkVersionViewModel) { 14 | val versionPostState = apkVersionViewModel.versionPostState.observeAsState() 15 | 16 | FlatButton(content = { 17 | RowContent( 18 | title = R.string.about_app_version, 19 | subTitle = versionPostState.value?.value 20 | ) 21 | }, { 22 | DataStoreUtils.removeSync(getString(R.string.store_not_update)) 23 | apkVersionViewModel.checkVersion() 24 | }) 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/alive/backstage/BackstageButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.alive.backstage 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.livedata.observeAsState 5 | import androidx.compose.ui.res.painterResource 6 | import com.android.skip.R 7 | import com.android.skip.ui.components.FlatButton 8 | import com.android.skip.ui.components.PictureDialog 9 | import com.android.skip.ui.components.ResourceIcon 10 | import com.android.skip.ui.components.RowContent 11 | 12 | @Composable 13 | fun BackstageButton( 14 | backstageViewModel:BackstageViewModel, 15 | onClick: ()->Unit={} 16 | ) { 17 | val state = backstageViewModel.showDialog.observeAsState() 18 | PictureDialog(showDialog = state.value == true, painter = painterResource(R.drawable.backend_lock)) { 19 | backstageViewModel.changeDialogState(false) 20 | } 21 | FlatButton( 22 | content = { 23 | RowContent( 24 | R.string.alive_backstage, 25 | R.string.alive_backstage_subtitle, 26 | { ResourceIcon(iconResource = R.drawable.counter_3) }) 27 | }, onClick = onClick 28 | ) 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/alive/backstage/BackstageViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.alive.backstage 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import javax.inject.Inject 8 | 9 | @HiltViewModel 10 | class BackstageViewModel @Inject constructor() : ViewModel() { 11 | private val _showDialog = MutableLiveData(false) 12 | val showDialog: LiveData = _showDialog 13 | 14 | fun changeDialogState(isShow: Boolean) { 15 | _showDialog.postValue(isShow) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/alive/notificationbar/NotificationBarButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.alive.notificationbar 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.livedata.observeAsState 5 | import com.android.skip.R 6 | import com.android.skip.ui.components.FlatButton 7 | import com.android.skip.ui.components.ResourceIcon 8 | import com.android.skip.ui.components.RowContent 9 | 10 | @Composable 11 | fun NotificationBarButton( 12 | notificationBarViewModel: NotificationBarViewModel, 13 | onClick: () -> Unit = {} 14 | ) { 15 | val enable = notificationBarViewModel.enable.observeAsState() 16 | FlatButton( 17 | content = { 18 | RowContent( 19 | R.string.alive_notification_bar, 20 | R.string.alive_notification_bar_subtitle, 21 | { ResourceIcon(iconResource = R.drawable.counter_4) }, 22 | enable.value, { 23 | notificationBarViewModel.changeEnable(it) 24 | }) 25 | }, onClick = onClick 26 | ) 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/alive/notificationbar/NotificationBarRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.alive.notificationbar 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.android.skip.R 6 | import com.android.skip.util.DataStoreUtils 7 | import com.blankj.utilcode.util.StringUtils.getString 8 | import javax.inject.Inject 9 | import javax.inject.Singleton 10 | 11 | @Singleton 12 | class NotificationBarRepository @Inject constructor() { 13 | private val _enable = MutableLiveData( 14 | DataStoreUtils.getSyncData(getString(R.string.store_resident_notification_bar), false) 15 | ) 16 | val enable: LiveData = _enable 17 | 18 | fun changeEnable(enable: Boolean) { 19 | _enable.postValue(enable) 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/alive/notificationbar/NotificationBarViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.alive.notificationbar 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.android.skip.R 6 | import com.android.skip.util.DataStoreUtils 7 | import com.blankj.utilcode.util.StringUtils.getString 8 | import dagger.hilt.android.lifecycle.HiltViewModel 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.launch 11 | import kotlinx.coroutines.withContext 12 | import javax.inject.Inject 13 | 14 | @HiltViewModel 15 | class NotificationBarViewModel @Inject constructor( 16 | private val notificationBarRepository:NotificationBarRepository 17 | ) : ViewModel() { 18 | val enable = notificationBarRepository.enable 19 | 20 | fun changeEnable(enable: Boolean) { 21 | notificationBarRepository.changeEnable(enable) 22 | 23 | viewModelScope.launch { 24 | withContext(Dispatchers.IO) { 25 | DataStoreUtils.putSyncData(getString(R.string.store_resident_notification_bar), enable) 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/components/FlatButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.components 2 | 3 | import androidx.compose.foundation.layout.RowScope 4 | import androidx.compose.foundation.layout.fillMaxWidth 5 | import androidx.compose.foundation.layout.heightIn 6 | import androidx.compose.material3.ExtendedFloatingActionButton 7 | import androidx.compose.material3.FloatingActionButtonDefaults 8 | import androidx.compose.material3.MaterialTheme 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.unit.dp 12 | 13 | 14 | typealias ButtonContent = @Composable RowScope.() -> Unit 15 | 16 | @Composable 17 | fun FlatButton(content: ButtonContent, onClick: () -> Unit = {}) { 18 | ExtendedFloatingActionButton( 19 | modifier = Modifier 20 | .fillMaxWidth() 21 | .heightIn(min=60.dp), 22 | onClick = onClick, 23 | content = content, 24 | elevation = FloatingActionButtonDefaults.elevation( 25 | defaultElevation = 0.dp, 26 | pressedElevation = 0.dp, 27 | hoveredElevation = 0.dp, 28 | focusedElevation = 0.dp 29 | ), 30 | containerColor = MaterialTheme.colorScheme.background 31 | ) 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/components/FlatButtonMenu.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.ColumnScope 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.RowScope 7 | import androidx.compose.material3.DropdownMenu 8 | import androidx.compose.material3.MaterialTheme 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.MutableState 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.unit.Dp 13 | import androidx.compose.ui.unit.DpOffset 14 | import androidx.compose.ui.unit.dp 15 | import com.blankj.utilcode.util.ScreenUtils 16 | 17 | @Composable 18 | fun FlatButtonMenu( 19 | content: @Composable RowScope.() -> Unit, 20 | menuItems: @Composable (ColumnScope.() -> Unit), 21 | expanded: MutableState 22 | ) { 23 | Row { 24 | FlatButton(content = content) { 25 | expanded.value = true 26 | } 27 | 28 | DropdownMenu( 29 | expanded = expanded.value, 30 | modifier = Modifier 31 | .background(MaterialTheme.colorScheme.background), 32 | offset = DpOffset(Dp(ScreenUtils.getScreenXDpi() / 3), (-30).dp), 33 | onDismissRequest = { expanded.value = false }, 34 | ) { 35 | menuItems() 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/components/PictureDialog.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.components 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.heightIn 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.Surface 8 | import androidx.compose.material3.Text 9 | import androidx.compose.material3.TextButton 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Alignment 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.graphics.painter.Painter 14 | import androidx.compose.ui.res.stringResource 15 | import androidx.compose.ui.unit.Dp 16 | import androidx.compose.ui.window.Dialog 17 | import com.android.skip.R 18 | import com.blankj.utilcode.util.ScreenUtils 19 | 20 | @Composable 21 | fun PictureDialog( 22 | showDialog: Boolean, 23 | painter: Painter?, 24 | onClick: () -> Unit 25 | ) { 26 | if (showDialog) { 27 | Dialog(onDismissRequest = { /*TODO*/ }) { 28 | Surface(shape = MaterialTheme.shapes.medium) { 29 | Column(horizontalAlignment = Alignment.CenterHorizontally) { 30 | if (painter != null) { 31 | Image( 32 | painter = painter, 33 | contentDescription = null, 34 | modifier = Modifier.heightIn(max = Dp(ScreenUtils.getScreenYDpi() * 2)) 35 | ) 36 | } 37 | TextButton(onClick = onClick) { 38 | Text(stringResource(id = R.string.dialog_close)) 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/components/ResourceIcon.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.components 2 | 3 | import androidx.compose.material3.Icon 4 | import androidx.compose.material3.MaterialTheme 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.res.painterResource 7 | 8 | @Composable 9 | fun ResourceIcon(iconResource: Int) { 10 | Icon( 11 | painter = painterResource(id = iconResource), 12 | contentDescription = null, 13 | tint = MaterialTheme.colorScheme.onBackground 14 | ) 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/components/notification/NotificationDialogViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.components.notification 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import javax.inject.Inject 8 | 9 | @HiltViewModel 10 | class NotificationDialogViewModel @Inject constructor(): ViewModel() { 11 | private val _showDialog = MutableLiveData(false) 12 | val showDialog: LiveData = _showDialog 13 | 14 | fun changeDialogState(isShow: Boolean) { 15 | _showDialog.postValue(isShow) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/inspect/floating/FloatingWindowButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.inspect.floating 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.livedata.observeAsState 6 | import com.android.skip.R 7 | import com.android.skip.ui.components.FlatButton 8 | import com.android.skip.ui.components.ResourceIcon 9 | import com.android.skip.ui.components.RowContent 10 | 11 | @Composable 12 | fun FloatingWindowButton(floatingWindowViewModel: FloatingWindowViewModel) { 13 | val floatingWindowState by floatingWindowViewModel.floatingWindowState.observeAsState() 14 | 15 | FlatButton(content = { 16 | RowContent( 17 | title = R.string.inspect_floating_window_title, 18 | subTitle = R.string.inspect_floating_window_subtitle, 19 | icon = { ResourceIcon(iconResource = R.drawable.float_landscape_2) }, 20 | checked = floatingWindowState, 21 | { 22 | floatingWindowViewModel.changeFloatingWindowState(it) 23 | } 24 | ) 25 | }) 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/inspect/floating/FloatingWindowRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.inspect.floating 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.android.skip.service.InspectService 6 | import com.blankj.utilcode.util.ServiceUtils 7 | import javax.inject.Inject 8 | import javax.inject.Singleton 9 | 10 | @Singleton 11 | class FloatingWindowRepository @Inject constructor() { 12 | private val _floatingWindowState = MutableLiveData(ServiceUtils.isServiceRunning(InspectService::class.java)) 13 | val floatingWindowState: LiveData = _floatingWindowState 14 | 15 | fun changeFloatingWindowState(state: Boolean) { 16 | _floatingWindowState.postValue(state) 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/inspect/floating/FloatingWindowViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.inspect.floating 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.ViewModel 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import javax.inject.Inject 7 | 8 | @HiltViewModel 9 | class FloatingWindowViewModel @Inject constructor(private val repository: FloatingWindowRepository): ViewModel() { 10 | val floatingWindowState: LiveData = repository.floatingWindowState 11 | 12 | fun changeFloatingWindowState(state: Boolean) = repository.changeFloatingWindowState(state) 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/inspect/record/InspectRecordButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.inspect.record 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.livedata.observeAsState 6 | import androidx.compose.ui.res.stringResource 7 | import com.android.skip.R 8 | import com.android.skip.ui.components.FlatButton 9 | import com.android.skip.ui.components.ResourceIcon 10 | import com.android.skip.ui.components.RowContent 11 | 12 | @Composable 13 | fun InspectRecordButton(inspectRecordViewModel: InspectRecordViewModel, onClick: () -> Unit = {}) { 14 | val zipFileCount by inspectRecordViewModel.zipFileCount.observeAsState() 15 | 16 | FlatButton(content = { 17 | RowContent( 18 | title = R.string.inspect_record_title, 19 | subTitle = zipFileCount?.let { 20 | stringResource( 21 | id = R.string.inspect_record_subtitle, 22 | it 23 | ) 24 | }, 25 | icon = { ResourceIcon(iconResource = R.drawable.lists) }, 26 | ) 27 | }, onClick = onClick) 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/inspect/record/InspectRecordRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.inspect.record 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.android.skip.MyApp 6 | import com.blankj.utilcode.util.FileUtils 7 | import javax.inject.Inject 8 | import javax.inject.Singleton 9 | 10 | @Singleton 11 | class InspectRecordRepository @Inject constructor() { 12 | private val _zipFileCount = MutableLiveData(0) 13 | var zipFileCount: LiveData = _zipFileCount 14 | 15 | fun changeZipFileCount() { 16 | val fileList = FileUtils.listFilesInDirWithFilter("${MyApp.context.filesDir}/capture") { 17 | it.name.endsWith("zip") 18 | } 19 | _zipFileCount.postValue(fileList.size) 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/inspect/record/InspectRecordViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.inspect.record 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.ViewModel 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import javax.inject.Inject 7 | 8 | @HiltViewModel 9 | class InspectRecordViewModel @Inject constructor(private val repository: InspectRecordRepository): ViewModel() { 10 | val zipFileCount: LiveData = repository.zipFileCount 11 | 12 | init { 13 | changeZipFileCount() 14 | } 15 | 16 | fun changeZipFileCount() = repository.changeZipFileCount() 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/inspect/start/StartInspectButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.inspect.start 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.livedata.observeAsState 6 | import com.android.skip.R 7 | import com.android.skip.ui.components.FlatButton 8 | import com.android.skip.ui.components.ResourceIcon 9 | import com.android.skip.ui.components.RowContent 10 | 11 | @Composable 12 | fun StartInspectButton(startInspectViewModel: StartInspectViewModel) { 13 | val inspectState by startInspectViewModel.inspectState.observeAsState() 14 | 15 | FlatButton(content = { 16 | RowContent( 17 | title = R.string.inspect_start_title, 18 | subTitle = R.string.inspect_start_subtitle, 19 | icon = { ResourceIcon(iconResource = R.drawable.fit_screen) }, 20 | checked = inspectState, 21 | { 22 | startInspectViewModel.changeInspectState(it) 23 | } 24 | ) 25 | }) 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/inspect/start/StartInspectRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.inspect.start 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.android.skip.R 6 | import com.android.skip.util.AccessibilityState 7 | import com.android.skip.util.AccessibilityStateUtils 8 | import com.android.skip.util.MyToast 9 | import javax.inject.Inject 10 | import javax.inject.Singleton 11 | 12 | @Singleton 13 | class StartInspectRepository @Inject constructor() { 14 | private val _inspectState = MutableLiveData(false) 15 | val inspectState: LiveData = _inspectState 16 | 17 | fun changeInspectState(state: Boolean) { 18 | if (state && AccessibilityStateUtils.getAccessibilityState() != AccessibilityState.STARTED) { 19 | MyToast.show(R.string.toast_start_accessibility_first) 20 | return 21 | } 22 | _inspectState.postValue(state) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/inspect/start/StartInspectViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.inspect.start 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.ViewModel 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import javax.inject.Inject 7 | 8 | @HiltViewModel 9 | class StartInspectViewModel @Inject constructor(private val repository: StartInspectRepository): ViewModel() { 10 | val inspectState: LiveData = repository.inspectState 11 | 12 | fun changeInspectState(state: Boolean) = repository.changeInspectState(state) 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/main/disclaimer/DisclaimerViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.main.disclaimer 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import com.android.skip.R 7 | import com.android.skip.util.DataStoreUtils 8 | import com.blankj.utilcode.util.StringUtils.getString 9 | import dagger.hilt.android.lifecycle.HiltViewModel 10 | import javax.inject.Inject 11 | 12 | @HiltViewModel 13 | class DisclaimerViewModel @Inject constructor() : ViewModel() { 14 | private val _isShowDialog = 15 | MutableLiveData(DataStoreUtils.getSyncData(getString(R.string.store_show_disclaimer), true)) 16 | val isShowDialog: LiveData = _isShowDialog 17 | 18 | fun changeDialogState(showDialog: Boolean) { 19 | _isShowDialog.postValue(showDialog) 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/main/start/StartAccessibilityButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.main.start 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.foundation.layout.Spacer 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.heightIn 8 | import androidx.compose.foundation.layout.width 9 | import androidx.compose.material3.ExtendedFloatingActionButton 10 | import androidx.compose.material3.Icon 11 | import androidx.compose.material3.Text 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.collectAsState 14 | import androidx.compose.runtime.getValue 15 | import androidx.compose.ui.Alignment 16 | import androidx.compose.ui.Modifier 17 | import androidx.compose.ui.graphics.Color 18 | import androidx.compose.ui.res.painterResource 19 | import androidx.compose.ui.unit.dp 20 | import androidx.compose.ui.unit.sp 21 | 22 | @Composable 23 | fun StartButton(startAccessibilityViewModel: StartAccessibilityViewModel, onClick: () -> Unit = {}) { 24 | val buttonState by startAccessibilityViewModel.buttonState.collectAsState() 25 | 26 | ExtendedFloatingActionButton( 27 | modifier = Modifier 28 | .fillMaxWidth() 29 | .heightIn(min=80.dp), 30 | onClick = onClick, 31 | content = { 32 | Row( 33 | modifier = Modifier.fillMaxWidth(), 34 | verticalAlignment = Alignment.CenterVertically, 35 | horizontalArrangement = Arrangement.Start 36 | ) { 37 | Icon( 38 | painter = painterResource(id = buttonState.iconResource), 39 | tint = Color.White, 40 | contentDescription = "StartButton" 41 | ) 42 | Spacer(modifier = Modifier.width(16.dp)) 43 | Text(buttonState.buttonText, color = Color.White, fontSize = 16.sp) 44 | } 45 | }, 46 | containerColor = buttonState.backgroundColor 47 | ) 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/main/start/StartAccessibilityRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.main.start 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.android.skip.service.InspectService 6 | import com.android.skip.util.AccessibilityState 7 | import com.android.skip.util.AccessibilityStateUtils 8 | import com.blankj.utilcode.util.ServiceUtils 9 | import javax.inject.Inject 10 | import javax.inject.Singleton 11 | 12 | @Singleton 13 | class StartAccessibilityRepository @Inject constructor() { 14 | private val _accessibilityState = 15 | MutableLiveData(AccessibilityStateUtils.getAccessibilityState()) 16 | val accessibilityState: LiveData = _accessibilityState 17 | 18 | fun changeAccessibilityState(state: AccessibilityState) { 19 | _accessibilityState.postValue(state) 20 | if (state != AccessibilityState.STARTED) { 21 | ServiceUtils.stopService(InspectService::class.java) 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/main/start/StartAccessibilityViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.main.start 2 | 3 | import androidx.compose.ui.graphics.Color 4 | import androidx.lifecycle.Observer 5 | import androidx.lifecycle.ViewModel 6 | import com.android.skip.R 7 | import com.android.skip.util.AccessibilityState 8 | import dagger.hilt.android.lifecycle.HiltViewModel 9 | import kotlinx.coroutines.flow.MutableStateFlow 10 | import kotlinx.coroutines.flow.asStateFlow 11 | import javax.inject.Inject 12 | 13 | @HiltViewModel 14 | class StartAccessibilityViewModel @Inject constructor(private val repository: StartAccessibilityRepository) : ViewModel() { 15 | private val stopped = ButtonState(Color(0xFF808080), "已停止\n点此启动", R.drawable.block) 16 | private val started = ButtonState(Color(0xFF1E4377), "运行中", R.drawable.check_circle) 17 | private val faulted = ButtonState(Color(0xFF771E1E), "发生故障\n点此重新启动", R.drawable.error) 18 | 19 | private val _buttonState = MutableStateFlow(stopped) 20 | val buttonState = _buttonState.asStateFlow() 21 | 22 | private val observer = Observer { 23 | changeButtonState(it) 24 | } 25 | 26 | init { 27 | repository.accessibilityState.observeForever(observer) 28 | } 29 | 30 | private fun changeButtonState(isRunning: AccessibilityState) { 31 | _buttonState.value = when (isRunning) { 32 | AccessibilityState.STARTED -> started 33 | AccessibilityState.STOPPED -> stopped 34 | AccessibilityState.FAULTED -> faulted 35 | } 36 | } 37 | 38 | override fun onCleared() { 39 | super.onCleared() 40 | repository.accessibilityState.removeObserver(observer) 41 | } 42 | 43 | data class ButtonState( 44 | val backgroundColor: Color, 45 | val buttonText: String, 46 | val iconResource: Int 47 | ) 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/main/tutorial/TutorialDialog.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.main.tutorial 2 | 3 | import androidx.compose.material3.AlertDialog 4 | import androidx.compose.material3.Button 5 | import androidx.compose.material3.MaterialTheme 6 | import androidx.compose.material3.Text 7 | import androidx.compose.material3.TextButton 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.runtime.livedata.observeAsState 10 | import androidx.compose.ui.res.stringResource 11 | import com.android.skip.R 12 | 13 | @Composable 14 | fun TutorialDialog( 15 | tutorialViewModel: TutorialViewModel, 16 | onDismiss: () -> Unit, 17 | onConfirm: () -> Unit 18 | ) { 19 | val isShowDialog = tutorialViewModel.isShowDialog.observeAsState() 20 | if (isShowDialog.value == true) { 21 | AlertDialog( 22 | containerColor = MaterialTheme.colorScheme.background, 23 | title = { 24 | Text(text = stringResource(id = R.string.dialog_get_started)) 25 | }, 26 | text = { 27 | Text(text = stringResource(id = R.string.dialog_get_started_content)) 28 | }, 29 | onDismissRequest = {}, 30 | confirmButton = { 31 | Button( 32 | onClick = onConfirm 33 | ) { 34 | Text(stringResource(id = R.string.dialog_go_at_once)) 35 | } 36 | }, 37 | dismissButton = { 38 | TextButton( 39 | onClick = onDismiss 40 | ) { 41 | Text(stringResource(id = R.string.dialog_no_more_reminders)) 42 | } 43 | } 44 | ) 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/main/tutorial/TutorialViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.main.tutorial 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import javax.inject.Inject 8 | 9 | @HiltViewModel 10 | class TutorialViewModel @Inject constructor() : ViewModel() { 11 | private val _isShowDialog = MutableLiveData() 12 | val isShowDialog: LiveData = _isShowDialog 13 | 14 | fun changeDialogState(showDialog: Boolean) { 15 | _isShowDialog.postValue(showDialog) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/record/dialog/JpegDialog.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.record.dialog 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.livedata.observeAsState 5 | import androidx.compose.ui.graphics.asImageBitmap 6 | import androidx.compose.ui.graphics.painter.BitmapPainter 7 | import com.android.skip.ui.components.PictureDialog 8 | 9 | @Composable 10 | fun JpegDialog(jpegDialogViewModel: JpegDialogViewModel) { 11 | val jpegFileIdState = jpegDialogViewModel.jpegFileId.observeAsState() 12 | PictureDialog( 13 | showDialog = jpegFileIdState.value != null, 14 | painter = jpegDialogViewModel.bitmapJpeg?.let { BitmapPainter(it.asImageBitmap()) } 15 | ) { 16 | jpegDialogViewModel.changeJpegFileId(null) 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/record/dialog/JpegDialogRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.record.dialog 2 | 3 | import android.graphics.Bitmap 4 | import android.graphics.BitmapFactory 5 | import com.android.skip.MyApp 6 | import javax.inject.Inject 7 | import javax.inject.Singleton 8 | 9 | @Singleton 10 | class JpegDialogRepository @Inject constructor(){ 11 | fun getJpegByFileId(fileId: String): Bitmap { 12 | val filePath = "${MyApp.context.filesDir}/capture/${fileId}.jpeg" 13 | val bitmap = BitmapFactory.decodeFile(filePath) 14 | return bitmap 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/record/dialog/JpegDialogViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.record.dialog 2 | 3 | import android.graphics.Bitmap 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.ViewModel 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class JpegDialogViewModel @Inject constructor( 12 | private val jpegDialogRepository: JpegDialogRepository 13 | ) : ViewModel() { 14 | private val _jpegFileId = MutableLiveData() 15 | val jpegFileId: LiveData = _jpegFileId 16 | 17 | private var _bitmapJpeg: Bitmap? = null 18 | val bitmapJpeg 19 | get() = _bitmapJpeg 20 | 21 | fun changeJpegFileId(fileId: String?) { 22 | if (fileId != null) { 23 | _bitmapJpeg = jpegDialogRepository.getJpegByFileId(fileId) 24 | } 25 | _jpegFileId.postValue(fileId) 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/record/list/InspectListPagingSource.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.record.list 2 | 3 | import androidx.paging.PagingSource 4 | import androidx.paging.PagingState 5 | import com.android.skip.dataclass.InspectRecordItem 6 | import com.blankj.utilcode.util.LogUtils 7 | import javax.inject.Inject 8 | 9 | 10 | class ZipFilePagingSource @Inject constructor(private val inspectListRepository: InspectListRepository) : 11 | PagingSource() { 12 | override fun getRefreshKey(state: PagingState): Int? { 13 | return state.anchorPosition?.let { anchorPosition -> 14 | state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1) 15 | ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus((1)) 16 | } 17 | } 18 | 19 | override suspend fun load(params: LoadParams): LoadResult { 20 | return try { 21 | val currentPage = params.key ?: 0 22 | val pageSize = params.loadSize 23 | 24 | val pageData = inspectListRepository.getData(currentPage, pageSize) 25 | 26 | val fromIndex = currentPage * pageSize 27 | val toIndex = minOf(fromIndex + pageSize, pageData.size) 28 | 29 | LoadResult.Page( 30 | data = pageData, 31 | prevKey = if (currentPage == 0) null else currentPage - 1, 32 | nextKey = if (toIndex == pageData.size) null else currentPage + 1 33 | ) 34 | } catch (e: Exception) { 35 | LogUtils.e(e) 36 | LoadResult.Error(e) 37 | } 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/record/list/InspectListViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.record.list 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import androidx.paging.Pager 8 | import androidx.paging.PagingConfig 9 | import androidx.paging.PagingData 10 | import androidx.paging.cachedIn 11 | import com.android.skip.dataclass.InspectRecordItem 12 | import dagger.hilt.android.lifecycle.HiltViewModel 13 | import kotlinx.coroutines.flow.Flow 14 | import javax.inject.Inject 15 | 16 | @HiltViewModel 17 | class InspectListViewModel @Inject constructor( 18 | private val inspectListRepository: InspectListRepository 19 | ) : ViewModel() { 20 | private val _delFileId = MutableLiveData() 21 | val delFileId: LiveData = _delFileId 22 | 23 | private var currentPagingSource: ZipFilePagingSource? = null 24 | 25 | fun deleteByFileId(fileId: String) = inspectListRepository.deleteByFileId(fileId) 26 | 27 | fun deleteAllFile() = inspectListRepository.deleteAllFile() 28 | 29 | fun changeDeleteFileId(fileId: String) { 30 | _delFileId.postValue(fileId) 31 | } 32 | 33 | fun reloadData() { 34 | currentPagingSource?.invalidate() 35 | filePagingData = createPager().flow.cachedIn(viewModelScope) 36 | } 37 | 38 | var filePagingData: Flow> = createPager().flow.cachedIn(viewModelScope) 39 | 40 | private fun createPager(): Pager { 41 | return Pager( 42 | config = PagingConfig( 43 | pageSize = 20, 44 | enablePlaceholders = false 45 | ), 46 | pagingSourceFactory = { 47 | val newPagingSource = ZipFilePagingSource(inspectListRepository) 48 | currentPagingSource = newPagingSource 49 | newPagingSource 50 | } 51 | ) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/recent/RecentButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.recent 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.livedata.observeAsState 5 | import com.android.skip.R 6 | import com.android.skip.ui.components.FlatButton 7 | import com.android.skip.ui.components.ResourceIcon 8 | import com.android.skip.ui.components.RowContent 9 | 10 | @Composable 11 | fun RecentButton(recentViewModel: RecentViewModel) { 12 | val excludeFromRecent = recentViewModel.excludeFromRecent.observeAsState() 13 | FlatButton(content = { 14 | RowContent( 15 | title = R.string.settings_background_task, 16 | subTitle = R.string.settings_background_task_subtitle, 17 | icon = { ResourceIcon(iconResource = R.drawable.hide_image)}, 18 | checked =excludeFromRecent.value, 19 | onCheckedChange = { 20 | recentViewModel.changeExcludeFromRecent(it) 21 | } 22 | ) 23 | }) 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/recent/RecentViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.recent 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import com.android.skip.R 8 | import com.android.skip.util.DataStoreUtils 9 | import com.blankj.utilcode.util.StringUtils.getString 10 | import dagger.hilt.android.lifecycle.HiltViewModel 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.launch 13 | import kotlinx.coroutines.withContext 14 | import javax.inject.Inject 15 | 16 | @HiltViewModel 17 | class RecentViewModel @Inject constructor() : ViewModel() { 18 | private val _excludeFromRecent = MutableLiveData( 19 | DataStoreUtils.getSyncData( 20 | getString(R.string.store_exclude_from_recent), 21 | false 22 | ) 23 | ) 24 | val excludeFromRecent: LiveData = _excludeFromRecent 25 | 26 | fun changeExcludeFromRecent(excludeFromRecent: Boolean) { 27 | _excludeFromRecent.postValue(excludeFromRecent) 28 | 29 | viewModelScope.launch { 30 | withContext(Dispatchers.IO) { 31 | DataStoreUtils.putData(getString(R.string.store_exclude_from_recent), excludeFromRecent) 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/strict/StrictButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.strict 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.livedata.observeAsState 5 | import androidx.compose.ui.res.stringResource 6 | import com.android.skip.R 7 | import com.android.skip.ui.components.FlatButton 8 | import com.android.skip.ui.components.ResourceIcon 9 | import com.android.skip.ui.components.RowContent 10 | 11 | @Composable 12 | fun StrictButton( 13 | strictViewModel: StrictViewModel 14 | ) { 15 | val enable = strictViewModel.enable.observeAsState() 16 | val strictText = if (enable.value == true) { 17 | R.string.settings_strict_yes 18 | } else { 19 | R.string.settings_strict_no 20 | } 21 | 22 | FlatButton( 23 | content = { 24 | RowContent( 25 | R.string.settings_strict, 26 | stringResource(id = strictText), 27 | { ResourceIcon(iconResource = R.drawable.target) }, 28 | enable.value, { 29 | strictViewModel.changeEnable(it) 30 | }) 31 | }, onClick = {} 32 | ) 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/strict/StrictRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.strict 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.android.skip.R 6 | import com.android.skip.util.DataStoreUtils 7 | import com.blankj.utilcode.util.StringUtils.getString 8 | import javax.inject.Inject 9 | import javax.inject.Singleton 10 | 11 | @Singleton 12 | class StrictRepository @Inject constructor() { 13 | private val _enable = MutableLiveData( 14 | DataStoreUtils.getSyncData(getString(R.string.store_strict_mode), false) 15 | ) 16 | 17 | val enable: LiveData = _enable 18 | 19 | fun changeEnable(enable: Boolean) { 20 | _enable.postValue(enable) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/strict/StrictViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.strict 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.android.skip.R 6 | import com.android.skip.util.DataStoreUtils 7 | import com.blankj.utilcode.util.StringUtils.getString 8 | import dagger.hilt.android.lifecycle.HiltViewModel 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.launch 11 | import kotlinx.coroutines.withContext 12 | import javax.inject.Inject 13 | 14 | @HiltViewModel 15 | class StrictViewModel @Inject constructor( 16 | private val strictRepository: StrictRepository, 17 | ): ViewModel(){ 18 | val enable = strictRepository.enable 19 | 20 | fun changeEnable(enable: Boolean) { 21 | strictRepository.changeEnable(enable) 22 | 23 | viewModelScope.launch { 24 | withContext(Dispatchers.IO) { 25 | DataStoreUtils.putData(getString(R.string.store_strict_mode), enable) 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/theme/SwitchThemeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.theme 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.android.skip.R 6 | import com.android.skip.dataclass.SwitchThemeCarrier 7 | import com.android.skip.dataclass.ThemeMode 8 | import com.android.skip.util.DataStoreUtils 9 | import com.blankj.utilcode.util.StringUtils.getString 10 | import javax.inject.Inject 11 | import javax.inject.Singleton 12 | 13 | @Singleton 14 | class SwitchThemeRepository @Inject constructor() { 15 | private val _currentTheme = MutableLiveData( 16 | getThemeCarrier( 17 | DataStoreUtils.getSyncData( 18 | getString(R.string.store_current_theme), 19 | ThemeMode.UI_AUTO.toString() 20 | ) 21 | ) 22 | ) 23 | val currentTheme: LiveData = _currentTheme 24 | 25 | fun changeCurrentTheme(currentTheme: String) { 26 | _currentTheme.postValue(getThemeCarrier(currentTheme)) 27 | } 28 | 29 | private fun getThemeCarrier(theme: String): SwitchThemeCarrier { 30 | return when (theme) { 31 | ThemeMode.UI_LIGHT.toString() -> SwitchThemeCarrier( 32 | themeMode = ThemeMode.UI_LIGHT, 33 | themeName = R.string.settings_theme_light, 34 | themeIcon = R.drawable.theme_light 35 | ) 36 | 37 | ThemeMode.UI_DARK.toString() -> SwitchThemeCarrier( 38 | themeMode = ThemeMode.UI_DARK, 39 | themeName = R.string.settings_theme_dark, 40 | themeIcon = R.drawable.theme_dark 41 | ) 42 | 43 | else -> SwitchThemeCarrier( 44 | themeMode = ThemeMode.UI_AUTO, 45 | themeName = R.string.settings_theme_auto, 46 | themeIcon = R.drawable.theme_auto 47 | ) 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/theme/SwitchThemeViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.theme 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.android.skip.R 7 | import com.android.skip.dataclass.SwitchThemeCarrier 8 | import com.android.skip.util.DataStoreUtils 9 | import com.blankj.utilcode.util.StringUtils.getString 10 | import dagger.hilt.android.lifecycle.HiltViewModel 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.launch 13 | import kotlinx.coroutines.withContext 14 | import javax.inject.Inject 15 | 16 | @HiltViewModel 17 | class SwitchThemeViewModel @Inject constructor( 18 | private val switchThemeRepository: SwitchThemeRepository 19 | ) : ViewModel() { 20 | val currentTheme: LiveData = switchThemeRepository.currentTheme 21 | 22 | fun changeCurrentTheme(currentTheme: String) { 23 | switchThemeRepository.changeCurrentTheme(currentTheme) 24 | 25 | viewModelScope.launch { 26 | withContext(Dispatchers.IO) { 27 | DataStoreUtils.putData(getString(R.string.store_current_theme), currentTheme) 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/tip/TipButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.tip 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.livedata.observeAsState 5 | import androidx.compose.ui.platform.LocalContext 6 | import androidx.compose.ui.res.stringResource 7 | import com.android.skip.R 8 | import com.android.skip.ui.components.FlatButton 9 | import com.android.skip.ui.components.ResourceIcon 10 | import com.android.skip.ui.components.RowContent 11 | 12 | @Composable 13 | fun TipButton(tipViewModel: TipViewModel) { 14 | val enable = tipViewModel.enable.observeAsState() 15 | val context = LocalContext.current 16 | val isShowText = 17 | if (enable.value == true) 18 | context.getString(R.string.settings_tip_yes) 19 | else 20 | context.getString(R.string.settings_tip_no) 21 | 22 | FlatButton( 23 | content = { 24 | RowContent( 25 | R.string.settings_tip, 26 | stringResource(id = R.string.settings_tip_subtitle, isShowText), 27 | { ResourceIcon(iconResource = R.drawable.notifications) }, 28 | enable.value, { 29 | tipViewModel.changeEnable(it) 30 | }) 31 | }, onClick = {} 32 | ) 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/tip/TipRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.tip 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.android.skip.R 6 | import com.android.skip.util.DataStoreUtils 7 | import com.blankj.utilcode.util.StringUtils.getString 8 | import javax.inject.Inject 9 | import javax.inject.Singleton 10 | 11 | @Singleton 12 | class TipRepository @Inject constructor() { 13 | private val _enable = MutableLiveData( 14 | DataStoreUtils.getSyncData(getString(R.string.store_skip_tip), false) 15 | ) 16 | 17 | val enable: LiveData = _enable 18 | 19 | fun changeEnable(enable: Boolean) { 20 | _enable.postValue(enable) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/tip/TipViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.tip 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.android.skip.R 6 | import com.android.skip.util.DataStoreUtils 7 | import com.blankj.utilcode.util.StringUtils.getString 8 | import dagger.hilt.android.lifecycle.HiltViewModel 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.launch 11 | import kotlinx.coroutines.withContext 12 | import javax.inject.Inject 13 | 14 | @HiltViewModel 15 | class TipViewModel @Inject constructor( 16 | private val tipRepository: TipRepository 17 | ) : ViewModel() { 18 | val enable = tipRepository.enable 19 | fun changeEnable(enable: Boolean) { 20 | tipRepository.changeEnable(enable) 21 | 22 | viewModelScope.launch { 23 | withContext(Dispatchers.IO) { 24 | DataStoreUtils.putData(getString(R.string.store_skip_tip), enable) 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/update/AutoUpdateButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.update 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.livedata.observeAsState 5 | import com.android.skip.R 6 | import com.android.skip.ui.components.FlatButton 7 | import com.android.skip.ui.components.ResourceIcon 8 | import com.android.skip.ui.components.RowContent 9 | 10 | @Composable 11 | fun AutoUpdateButton(autoUpdateViewModel: AutoUpdateViewModel) { 12 | val autoUpdate = autoUpdateViewModel.autoUpdate.observeAsState() 13 | 14 | FlatButton(content = { 15 | RowContent( 16 | title = R.string.settings_auto_update, 17 | subTitle = R.string.settings_auto_update_subtitle, 18 | icon = { ResourceIcon(iconResource = R.drawable.sync)}, 19 | checked = autoUpdate.value, 20 | { 21 | autoUpdateViewModel.changeAutoUpdate(it) 22 | } 23 | ) 24 | }) 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/settings/update/AutoUpdateViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.settings.update 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import com.android.skip.R 8 | import com.android.skip.util.DataStoreUtils 9 | import com.blankj.utilcode.util.StringUtils.getString 10 | import dagger.hilt.android.lifecycle.HiltViewModel 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.launch 13 | import kotlinx.coroutines.withContext 14 | import javax.inject.Inject 15 | 16 | @HiltViewModel 17 | class AutoUpdateViewModel @Inject constructor() : ViewModel() { 18 | private val _autoUpdate = 19 | MutableLiveData(DataStoreUtils.getSyncData(getString(R.string.store_auto_update), false)) 20 | val autoUpdate: LiveData = _autoUpdate 21 | 22 | fun changeAutoUpdate(autoUpdate: Boolean) { 23 | _autoUpdate.postValue(autoUpdate) 24 | 25 | viewModelScope.launch { 26 | withContext(Dispatchers.IO) { 27 | DataStoreUtils.putData(getString(R.string.store_auto_update), autoUpdate) 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | val Purple40 = Color(0xFF6650a4) 9 | val PurpleGrey40 = Color(0xFF625b71) 10 | val Pink40 = Color(0xFF7D5260) 11 | val White = Color.White 12 | val Black = Color.Black 13 | val Gray232 = Color(0xFFE8E8E8) 14 | val Gray172 = Color(0xFFACACAC) 15 | val Black54 = Color(0xFF363636) 16 | val Black91 = Color(0xFF5B5B5B) -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/theme/Typography.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | val Typography = Typography( 10 | bodyLarge = TextStyle( 11 | fontFamily = FontFamily.Default, 12 | fontWeight = FontWeight.Normal, 13 | fontSize = 16.sp, 14 | lineHeight = 24.sp, 15 | letterSpacing = 0.5.sp 16 | ) 17 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/webview/WebViewActivity.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.webview 2 | 3 | import android.os.Bundle 4 | import android.webkit.WebView 5 | import android.webkit.WebViewClient 6 | import androidx.activity.compose.setContent 7 | import androidx.activity.viewModels 8 | import androidx.appcompat.app.AppCompatActivity 9 | import androidx.compose.foundation.layout.fillMaxSize 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.viewinterop.AndroidView 13 | import com.android.skip.ui.settings.theme.SwitchThemeViewModel 14 | import com.android.skip.ui.theme.AppTheme 15 | import dagger.hilt.android.AndroidEntryPoint 16 | 17 | @AndroidEntryPoint 18 | class WebViewActivity : AppCompatActivity() { 19 | 20 | private val switchThemeViewModel by viewModels() 21 | 22 | override fun onCreate(savedInstanceState: Bundle?) { 23 | super.onCreate(savedInstanceState) 24 | setContent { 25 | AppTheme(switchThemeViewModel) { 26 | val url = intent.getIntExtra("url", -1) 27 | if (url != -1) { 28 | WebViewPage(url = getString(url)) 29 | } else { 30 | val address = intent.getStringExtra("address") 31 | if (address != null) { 32 | WebViewPage(url = address) 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | @Composable 40 | fun WebViewPage(url: String) { 41 | AndroidView( 42 | factory = { context -> 43 | WebView(context).apply { 44 | webViewClient = WebViewClient() 45 | settings.javaScriptEnabled = true 46 | loadUrl(url) 47 | } 48 | }, 49 | modifier = Modifier.fillMaxSize() 50 | ) 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/whitelist/WhiteListRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.whitelist 2 | 3 | import com.android.skip.MyApp 4 | import com.android.skip.R 5 | import com.android.skip.util.dataStore 6 | import com.blankj.utilcode.util.StringUtils.getString 7 | import kotlinx.coroutines.Dispatchers 8 | import kotlinx.coroutines.flow.first 9 | import kotlinx.coroutines.runBlocking 10 | import kotlinx.coroutines.withContext 11 | import javax.inject.Inject 12 | import javax.inject.Singleton 13 | 14 | @Singleton 15 | class WhiteListRepository @Inject constructor() { 16 | private val _whiteList: MutableList by lazy { 17 | runBlocking { 18 | initWhitelist().toMutableList() 19 | } 20 | } 21 | 22 | private suspend fun initWhitelist() = withContext(Dispatchers.IO) { 23 | val allPreferences = MyApp.context.dataStore.data.first() 24 | allPreferences.asMap() 25 | .filter { 26 | it.key.name.startsWith(getString(R.string.whitelist_dot)) && it.value == true 27 | }.keys.map { 28 | it.name.substringAfter( 29 | getString(R.string.whitelist_dot) 30 | ) 31 | } 32 | } 33 | 34 | fun updateWhiteList(packageName: String, checked: Boolean) { 35 | if (checked) { 36 | _whiteList.add(packageName) 37 | } else { 38 | _whiteList.remove(packageName) 39 | } 40 | } 41 | 42 | fun isAppInWhiteList(packageName: String): Boolean { 43 | return _whiteList.indexOf(packageName) != -1 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/whitelist/WhiteListViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.whitelist 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.android.skip.R 6 | import com.android.skip.util.DataStoreUtils 7 | import com.blankj.utilcode.util.StringUtils.getString 8 | import dagger.hilt.android.lifecycle.HiltViewModel 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.launch 11 | import kotlinx.coroutines.withContext 12 | import javax.inject.Inject 13 | 14 | @HiltViewModel 15 | class WhiteListViewModel @Inject constructor( 16 | private val whiteListRepository: WhiteListRepository 17 | ) : ViewModel() { 18 | fun updateWhiteList(packageName: String, checked: Boolean) { 19 | whiteListRepository.updateWhiteList(packageName, checked) 20 | viewModelScope.launch { 21 | withContext(Dispatchers.IO) { 22 | DataStoreUtils.putData(getString(R.string.whitelist_dot) + packageName, checked) 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/whitelist/list/AppListPagingSource.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.whitelist.list 2 | 3 | import androidx.paging.PagingSource 4 | import androidx.paging.PagingState 5 | import com.android.skip.dataclass.AppListItem 6 | import javax.inject.Inject 7 | 8 | class AppListPagingSource @Inject constructor( 9 | private val appListRepository: AppListRepository, 10 | private val isShowSystem: Boolean, 11 | private val query: String 12 | ) : PagingSource() { 13 | override fun getRefreshKey(state: PagingState): Int? { 14 | return state.anchorPosition?.let { anchorPosition -> 15 | state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1) 16 | ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus((1)) 17 | } 18 | } 19 | 20 | override suspend fun load(params: LoadParams): LoadResult { 21 | val currentPage = params.key ?: 0 22 | val pageSize = params.loadSize 23 | 24 | val pageData = appListRepository.getData(currentPage, pageSize, isShowSystem, query) 25 | 26 | return LoadResult.Page( 27 | data = pageData, 28 | prevKey = if (currentPage > 0) currentPage - 1 else null, 29 | nextKey = if (pageData.isEmpty()) null else currentPage + 1, 30 | ) 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/whitelist/list/AppListRepository.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.whitelist.list 2 | 3 | import android.content.pm.ApplicationInfo 4 | import android.content.pm.PackageManager 5 | import com.android.skip.MyApp 6 | import com.android.skip.dataclass.AppListItem 7 | import com.android.skip.ui.whitelist.WhiteListRepository 8 | import javax.inject.Inject 9 | 10 | class AppListRepository @Inject constructor() { 11 | 12 | @Inject 13 | lateinit var whiteListRepository: WhiteListRepository 14 | 15 | private val appInfos: MutableList by lazy { 16 | MyApp.context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA) 17 | } 18 | 19 | fun getData(currentPage: Int, pageSize: Int, isShowSystem: Boolean, query: String = ""): List { 20 | try { 21 | val pkgManager = MyApp.context.packageManager 22 | 23 | // 是否显示系统应用的过滤 24 | val apps = if (isShowSystem) { 25 | appInfos 26 | } else { 27 | appInfos.filter { (it.flags and ApplicationInfo.FLAG_SYSTEM) == 0 } 28 | } 29 | 30 | // 根据关键字进行过滤 31 | val filteredApps = if (query.isNotEmpty()) { 32 | apps.filter { it.loadLabel(pkgManager).toString().contains(query, ignoreCase = true) } 33 | } else { 34 | apps 35 | } 36 | 37 | val fromIndex = currentPage * pageSize 38 | val toIndex = minOf(fromIndex + pageSize, filteredApps.size) 39 | 40 | return filteredApps.subList(fromIndex, toIndex) 41 | .map { 42 | AppListItem( 43 | it.loadLabel(pkgManager).toString(), 44 | it.packageName, 45 | it.loadIcon(pkgManager), 46 | whiteListRepository.isAppInWhiteList(it.packageName) 47 | ) 48 | } 49 | } catch (e: Exception) { 50 | return listOf() 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/whitelist/list/AppListViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.whitelist.list 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import androidx.paging.Pager 6 | import androidx.paging.PagingConfig 7 | import androidx.paging.PagingData 8 | import androidx.paging.cachedIn 9 | import com.android.skip.dataclass.AppListItem 10 | import dagger.hilt.android.lifecycle.HiltViewModel 11 | import kotlinx.coroutines.flow.Flow 12 | import kotlinx.coroutines.flow.MutableStateFlow 13 | import kotlinx.coroutines.flow.StateFlow 14 | import javax.inject.Inject 15 | 16 | @HiltViewModel 17 | class AppListViewModel @Inject constructor( 18 | private val appListRepository: AppListRepository 19 | ): ViewModel() { 20 | private val _pagingData = MutableStateFlow(createPager(false).flow.cachedIn(viewModelScope)) 21 | val pagingData: StateFlow>> = _pagingData 22 | 23 | private var currentPagingSource: AppListPagingSource? = null 24 | 25 | fun reloadData(isShowSystem: Boolean, query: String = "") { 26 | currentPagingSource?.invalidate() 27 | _pagingData.value = createPager(isShowSystem, query).flow.cachedIn(viewModelScope) 28 | } 29 | 30 | private fun createPager(isShowSystem: Boolean, query: String = ""): Pager { 31 | return Pager( 32 | config = PagingConfig( 33 | pageSize = 20, 34 | enablePlaceholders = false 35 | ), 36 | pagingSourceFactory = { 37 | val newPagingSource = AppListPagingSource(appListRepository, isShowSystem, query) 38 | currentPagingSource = newPagingSource 39 | newPagingSource 40 | } 41 | ) 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/whitelist/search/SearchViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.whitelist.search 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import javax.inject.Inject 8 | 9 | @HiltViewModel 10 | class SearchViewModel @Inject constructor(): ViewModel() { 11 | private val _query = MutableLiveData("") 12 | val query: LiveData = _query 13 | 14 | fun changeQuery(query: String) { 15 | _query.postValue(query) 16 | } 17 | 18 | fun getQuery(): String { 19 | return _query.value ?: "" 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/whitelist/system/ShowSystemButton.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.whitelist.system 2 | 3 | import androidx.compose.foundation.layout.Row 4 | import androidx.compose.foundation.layout.Spacer 5 | import androidx.compose.foundation.layout.width 6 | import androidx.compose.material3.Checkbox 7 | import androidx.compose.material3.Text 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.runtime.livedata.observeAsState 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.res.stringResource 13 | import androidx.compose.ui.unit.dp 14 | import com.android.skip.R 15 | 16 | @Composable 17 | fun ShowSystemButton(showSystemViewModel: ShowSystemViewModel) { 18 | val isShowSystem = showSystemViewModel.isShowSystem.observeAsState() 19 | Row(verticalAlignment = Alignment.CenterVertically) { 20 | Checkbox( 21 | checked = isShowSystem.value == true, 22 | onCheckedChange = { 23 | showSystemViewModel.changeIsShowSystem(it) 24 | } 25 | ) 26 | Text(stringResource(id = R.string.whitelist_show_system)) 27 | Spacer(modifier = Modifier.width(10.dp)) 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/ui/whitelist/system/ShowSystemViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.ui.whitelist.system 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import javax.inject.Inject 8 | 9 | @HiltViewModel 10 | class ShowSystemViewModel @Inject constructor():ViewModel() { 11 | private val _isShowSystem = MutableLiveData(false) 12 | val isShowSystem: LiveData = _isShowSystem 13 | 14 | fun changeIsShowSystem(isShow: Boolean) { 15 | _isShowSystem.postValue(isShow) 16 | } 17 | 18 | fun getIsShowSystem(): Boolean { 19 | return _isShowSystem.value ?: false 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/util/DebounceHelper.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.util 2 | 3 | class DebounceHelper(private val delayMillis: Long) { 4 | private var lastTime: Long = 0 5 | 6 | fun run(action: () -> Unit) { 7 | val currentTime = System.currentTimeMillis() 8 | if (currentTime - lastTime >= delayMillis) { 9 | action() 10 | lastTime = currentTime 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/util/MyToast.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.util 2 | 3 | import android.os.Handler 4 | import android.os.Looper 5 | import android.widget.Toast 6 | import com.android.skip.MyApp 7 | 8 | object MyToast { 9 | fun show(content: Int) { 10 | Handler(Looper.getMainLooper()).post{ 11 | Toast.makeText(MyApp.context, MyApp.context.getString(content), Toast.LENGTH_SHORT).show() 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/skip/util/NetworkUtils.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip.util 2 | 3 | import android.content.Context 4 | import android.net.ConnectivityManager 5 | import android.net.NetworkCapabilities 6 | import android.os.Build 7 | 8 | 9 | object NetworkUtil { 10 | fun isNetworkConnected(context: Context): Boolean { 11 | val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 12 | val network = cm.activeNetwork ?: return false 13 | val capabilities = cm.getNetworkCapabilities(network) ?: return false 14 | return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/all_inclusive.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/app_registration.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/arrow_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/backend_lock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/drawable/backend_lock.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/block.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/check_circle.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/counter_1.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/counter_2.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/counter_3.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/counter_4.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/error.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/favicon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/drawable/favicon32.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/fit_screen.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/float_landscape_2.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/help.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/hide_image.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/info.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/lists.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/notifications.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/photo_camera.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/point_scan.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/settings.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sync.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/target.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/theme_auto.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/theme_dark.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/theme_light.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tune.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/warning.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/floating_window.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #FFBB86FC 5 | #FF3700B3 6 | #FFFFFF 7 | 8 | #FFBB86FC 9 | #FF3700B3 10 | #FFFFFF 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/xml/accessibility.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/test/java/com/android/skip/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.android.skip 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | alias(libs.plugins.android.application) apply false 4 | alias(libs.plugins.jetbrains.kotlin.android) apply false 5 | alias(libs.plugins.compose.compiler) apply false 6 | id("com.google.dagger.hilt.android") version "2.51.1" apply false 7 | } -------------------------------------------------------------------------------- /capture/0f84d874-ada4-4b90-9829-a99f43950071.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/0f84d874-ada4-4b90-9829-a99f43950071.zip -------------------------------------------------------------------------------- /capture/20f3db03-af60-4dc3-885a-b954b985931f.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/20f3db03-af60-4dc3-885a-b954b985931f.zip -------------------------------------------------------------------------------- /capture/28b730a3-5ac7-44c7-8f8a-9daf1b8effbe.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/28b730a3-5ac7-44c7-8f8a-9daf1b8effbe.zip -------------------------------------------------------------------------------- /capture/2adae253-623d-4fd3-ad75-b5b32b667dcd.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/2adae253-623d-4fd3-ad75-b5b32b667dcd.zip -------------------------------------------------------------------------------- /capture/368caf09-c152-4f6b-b8dd-3ee4718df65d.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/368caf09-c152-4f6b-b8dd-3ee4718df65d.zip -------------------------------------------------------------------------------- /capture/3737625b-383a-4891-aada-493e811f048a.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/3737625b-383a-4891-aada-493e811f048a.zip -------------------------------------------------------------------------------- /capture/43bcef4b-4b5b-4b63-a7f2-ad28e4f5f690.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/43bcef4b-4b5b-4b63-a7f2-ad28e4f5f690.zip -------------------------------------------------------------------------------- /capture/450b0add-b8e2-4cac-9b49-015744bb786f.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/450b0add-b8e2-4cac-9b49-015744bb786f.zip -------------------------------------------------------------------------------- /capture/5656e116-5a93-44da-8687-1a1dec170984.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/5656e116-5a93-44da-8687-1a1dec170984.zip -------------------------------------------------------------------------------- /capture/57861a13-956b-4ae1-be60-d77c33b82010.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/57861a13-956b-4ae1-be60-d77c33b82010.zip -------------------------------------------------------------------------------- /capture/7aa2544b-8a80-41f3-bf0c-ee03c7cf83b2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/7aa2544b-8a80-41f3-bf0c-ee03c7cf83b2.zip -------------------------------------------------------------------------------- /capture/7ba85f98-5b07-4947-95e0-dca6431b1fc6.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/7ba85f98-5b07-4947-95e0-dca6431b1fc6.zip -------------------------------------------------------------------------------- /capture/7d45a0ad-f0ce-4856-b38b-dd545bfadeb5.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/7d45a0ad-f0ce-4856-b38b-dd545bfadeb5.zip -------------------------------------------------------------------------------- /capture/812b304e-63fd-42da-b200-7497a2e635c4.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/812b304e-63fd-42da-b200-7497a2e635c4.zip -------------------------------------------------------------------------------- /capture/84603cb6-3647-42aa-a0f9-e013a3c1e74c.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/84603cb6-3647-42aa-a0f9-e013a3c1e74c.zip -------------------------------------------------------------------------------- /capture/88ad5f6b-e65b-4424-be8e-ab57e0eb613b.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/88ad5f6b-e65b-4424-be8e-ab57e0eb613b.zip -------------------------------------------------------------------------------- /capture/92e729ea-dd19-4384-93af-ec7581f306cf.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/92e729ea-dd19-4384-93af-ec7581f306cf.zip -------------------------------------------------------------------------------- /capture/94969846-73e3-41de-aca0-ad9994566fc6.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/94969846-73e3-41de-aca0-ad9994566fc6.zip -------------------------------------------------------------------------------- /capture/a310356a-4841-40fd-8a4f-314e77318230.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/a310356a-4841-40fd-8a4f-314e77318230.zip -------------------------------------------------------------------------------- /capture/ab69a19e-56e8-4983-9656-cdbab1b09682.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/ab69a19e-56e8-4983-9656-cdbab1b09682.zip -------------------------------------------------------------------------------- /capture/b8592c72-1a8a-42ea-8022-2ba15bc83444.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/b8592c72-1a8a-42ea-8022-2ba15bc83444.zip -------------------------------------------------------------------------------- /capture/bb3de897-19d4-4c6d-ac7a-e11130420b4a.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/bb3de897-19d4-4c6d-ac7a-e11130420b4a.zip -------------------------------------------------------------------------------- /capture/c1c0b4c7-9bed-4f11-93e8-6fcd08bb4717.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/c1c0b4c7-9bed-4f11-93e8-6fcd08bb4717.zip -------------------------------------------------------------------------------- /capture/cfe9660a-1d45-4904-a8a7-8d846faa1a49.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/cfe9660a-1d45-4904-a8a7-8d846faa1a49.zip -------------------------------------------------------------------------------- /capture/dba416c1-f18d-45c4-8042-16e2c15bead5.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/dba416c1-f18d-45c4-8042-16e2c15bead5.zip -------------------------------------------------------------------------------- /capture/e2c456c9-2db5-4ff9-9f79-f42acf557358.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/e2c456c9-2db5-4ff9-9f79-f42acf557358.zip -------------------------------------------------------------------------------- /capture/e835b293-7845-4988-9423-fe7f9d740d43.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/e835b293-7845-4988-9423-fe7f9d740d43.zip -------------------------------------------------------------------------------- /capture/ee283a25-cbbf-43b0-84dd-aaf9ccf24c83.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/ee283a25-cbbf-43b0-84dd-aaf9ccf24c83.zip -------------------------------------------------------------------------------- /capture/f074f9ba-2fdd-4885-8b16-df8afdcbb46d.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/capture/f074f9ba-2fdd-4885-8b16-df8afdcbb46d.zip -------------------------------------------------------------------------------- /dist/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/dist/.gitkeep -------------------------------------------------------------------------------- /docs/.vitepress/index.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss/base"; 2 | @import "tailwindcss/components"; 3 | @import "tailwindcss/utilities"; 4 | 5 | @layer utilities { 6 | .hide-scrollbar { 7 | /* 对于大多数现代浏览器 */ 8 | scrollbar-width: none; /* Firefox */ 9 | -ms-overflow-style: none; /* IE 10+ */ 10 | 11 | /* 对于 Webkit 浏览器 */ 12 | &::-webkit-scrollbar { 13 | display: none; /* Chrome, Safari, Opera */ 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/custom.css: -------------------------------------------------------------------------------- 1 | p img { 2 | width: 300px; 3 | } 4 | :root { 5 | --vp-home-hero-name-color: transparent; 6 | --vp-home-hero-name-background: -webkit-linear-gradient(120deg, #bd34fe 30%, #41d1ff); 7 | --vp-home-hero-image-filter: blur(56px); 8 | --vp-home-hero-image-background-image: linear-gradient(-45deg, #bd34fe 50%, #47caff 50%); 9 | } 10 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.js: -------------------------------------------------------------------------------- 1 | import DefaultTheme from 'vitepress/theme' 2 | import './custom.css' 3 | import 'element-plus/dist/index.css' 4 | import ElementPlus from "element-plus"; 5 | import 'element-plus/theme-chalk/dark/css-vars.css' 6 | import '../index.css' 7 | 8 | export default { 9 | ...DefaultTheme, 10 | enhanceApp({ app }) { 11 | app.use(ElementPlus) 12 | } 13 | } -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | skip.guoxicheng.top -------------------------------------------------------------------------------- /docs/advance/custom-config/data-structure.md: -------------------------------------------------------------------------------- 1 | # 可配置字段数据结构 2 | 3 | <<< @/../app/src/main/java/com/android/skip/dataclass/ConfigReadSchema.kt 4 | -------------------------------------------------------------------------------- /docs/advance/custom-config/field-meaning.md: -------------------------------------------------------------------------------- 1 | # 可配置字段 2 | 3 | ## packageName `必填` 4 | 5 | ### skipTexts `可选` 6 | 7 | #### text `必填` 8 | 9 | 根据节点的`text`属性跳过指定的文本。 10 | 11 | #### length `可选` 12 | 13 | 判断节点的`text`属性的长度是否小于等于`length`的值。 14 | 15 | #### activityName `可选` 16 | 17 | 判断当前页面的`activityName`是否等于`activityName`的值。相等时才会执行。 18 | 19 | #### click `可选` 20 | 21 | 点击的坐标,格式为`x,y`。 22 | 23 | 默认情况下,会点击找到节点的中心点。可以通过`click`属性可额外指定点击的坐标,而不点击所找到节点的中心点。 24 | 25 | #### 参考 26 | 27 | ```yaml 28 | - packageName: com.android.skip 29 | skipTexts: 30 | - text: 布局检查 31 | length: 4 32 | activityName: com.android.skip.ui.inspect.InspectActivity 33 | click: 123,456 34 | ``` 35 | 36 | ### skipIds `可选` 37 | 38 | #### id `必填` 39 | 40 | 根据节点的`viewIdResourceName`属性搜索指定的节点。 41 | 42 | #### activityName `可选` 43 | 44 | #### click `可选` 45 | 46 | #### 参考 47 | 48 | ```yaml 49 | - packageName: com.android.skip 50 | skipIds: 51 | - id: android:id/navigationBarBackground 52 | activityName: com.android.skip.ui.inspect.InspectActivity 53 | click: 123,456 54 | ``` 55 | 56 | ### skipBounds `可选` 57 | 58 | #### bound `必填` 59 | 60 | 根据节点的`boundsInScreen`属性搜索指定的节点,即:`bound(left,top,right,bottom)`。 61 | 62 | #### activityName `可选` 63 | 64 | #### click `可选` 65 | 66 | #### 参考 67 | 68 | ```yaml 69 | - packageName: com.android.skip 70 | skipBounds: 71 | - bound: 53,639,1387,849 72 | activityName: com.android.skip.ui.inspect.InspectActivity 73 | click: 123,456 74 | ``` 75 | -------------------------------------------------------------------------------- /docs/advance/custom-config/full-reference.md: -------------------------------------------------------------------------------- 1 | # 完整的配置参考 2 | 3 | ## 默认配置文件 4 | 5 | ::: code-group 6 | 7 | <<< @/../app/src/main/assets/skip_config_v3.yaml [YAML 格式] 8 | 9 | <<< @/../app/src/main/assets/skip_config_v3.json [JSON 格式] 10 | 11 | ::: 12 | -------------------------------------------------------------------------------- /docs/advance/custom-config/intro.md: -------------------------------------------------------------------------------- 1 | # 自定义配置功能介绍 2 | 3 | 由于应用市场的软件繁多,各种手机的型号也不一样,因此没有一种配置能够适应所有的情况。 4 | 5 | 你可以根据自己的需求,通过布局检查获取屏幕的节点信息,再通过自定义配置功能,维护好自己的配置文件。 6 | -------------------------------------------------------------------------------- /docs/advance/layout-inspect/intro.md: -------------------------------------------------------------------------------- 1 | # 布局检查功能介绍 2 | 3 | 布局检查功能可以用于探查屏幕的节点布局信息。 4 | 5 | 掌握屏幕的节点信息,有助于编写自定义配置文件。 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/advance/layout-inspect/usage.md: -------------------------------------------------------------------------------- 1 | # 使用方法 2 | 3 | ::: tip 4 | 布局检查功能需要通知权限。 5 | ::: 6 | 7 | ## 首先 8 | 9 | 开启「SKIP 无障碍服务」 10 | 11 | ## 然后 12 | 13 | 开启「是否启用布局检查」的开关 14 | 15 | ## 通过 16 | 17 | 按下一次「音量-」按键,可以执行一次布局检查。 18 | 19 | ## 最后 20 | 21 | 在布局检查记录页面中,可以查看或删除布局检查记录,或是分享到其他应用转存。 22 | 23 | 再在 Web 端通过 [布局检查工具](/inspect/index) 导入并查看详细的节点信息。 24 | -------------------------------------------------------------------------------- /docs/guide/about/intro.md: -------------------------------------------------------------------------------- 1 | # 关于功能介绍 2 | 3 | ## 源代码 4 | 5 | 点击「源代码」功能按钮时,会跳转到 GitHub 仓库页面,方便查看源代码。 6 | 7 | 但某些情况下,如果网络缓慢,可能会出现无法访问的情况。 8 | 9 | ## 文档 10 | 11 | 点击「文档」功能按钮时,会跳转到「SKIP」文档页面,可以查看完整文档。 12 | 13 | ## 应用版本号 14 | 15 | 点击「应用版本号」功能按钮时,会主动检查应用的版本号: 16 | 17 | - 如果有新版本,会提示更新 18 | - 如果没有新版本,会显示当前版本号 19 | 20 | ## 配置版本号 21 | 22 | 点击「配置版本号」功能按钮时,会主动检查配置的版本号。 23 | 24 | 如果在自定义配置输入框中填写了: 25 | 26 | - 有效的 URL 地址,会从该地址获取配置文件,并显示版本号。如果反复点击配置版本号,会重新获取配置文件并显示最新的版本号 27 | - 有效的纯文本的配置内容,会显示对应的版本号,并且即使反复点击配置版本号也不会变化 28 | - 无效的配置内容,会显示「未检测到有效的配置」 29 | 30 | ### 配置版本号的算法 31 | 32 | 配置版本号的算法是通过计算 JSON 字符串格式配置文件的 MD5 值来实现的,因此,只要配置内容不变,配置版本号就不会变化。 33 | 34 | <<< @/../app/src/main/java/com/android/skip/data/config/ConfigReadRepository.kt#MD5 35 | -------------------------------------------------------------------------------- /docs/guide/contributing/config-file.md: -------------------------------------------------------------------------------- 1 | # 配置文件 2 | 3 | 配置文件是一个 YAML 文件,用于根据不同的 App 应用不同的配置。 4 | 5 | 配置文件位置:[配置文件](https://github.com/GuoXiCheng/SKIP/blob/main/app/src/main/assets/skip_config_v2.yaml) 6 | 7 | ## 配置文件选项 8 | 9 | ### packageName(必填) 10 | 11 | 应用包名称。 12 | 13 | 在 SKIP 的应用白名单中,可以查看应用的包名。 14 | 15 | ### skipTexts (选填) 16 | 17 | 根据文本匹配,当节点的文本**包含**指定字符串时,执行点击动作。可以配置多组。 18 | 19 | 在手机屏幕上看到的文本是什么就可以填什么,但是实际节点 text = null 时,会失效。 20 | 21 | ```yaml 22 | - packageName: com.xxx.xxx 23 | skipTexts: 24 | - text: 跳过广告 25 | length: 4 26 | ``` 27 | 28 | ### skipIds (选填) 29 | 30 | 根据 id 匹配,当节点的 id **等于**指定字符串时,执行点击动作。可以配置多组。 31 | 32 | id 需要使用 Android 布局分析工具查询,但实际节点 id = null 时,不可用。 33 | 34 | ```yaml 35 | - packageName: com.xxx.xxx 36 | skipIds: 37 | - id: com.xxx.xxx:id/view_count_down 38 | ``` 39 | 40 | ### skipBounds(选填) 41 | 42 | 根据 bounds 匹配,当所设定的 bounds **包含**节点的 bounds 时,执行点击动作。可以配置多组。 43 | 44 | 一般可交互的节点都会具有 bounds,需要使用 Android 布局分析工具查询。 45 | -------------------------------------------------------------------------------- /docs/guide/contributing/core-logic.md: -------------------------------------------------------------------------------- 1 | # 核心逻辑 2 | 3 | ## 实现原理 4 | 5 | 利用 Android 无障碍服务,获取当前窗口对象,遍历窗口节点,当节点符合指定条件时,执行点击的动作。 6 | 7 | ## 核心逻辑 8 | 9 | 主要分为以下几个步骤: 10 | 11 | ```mermaid 12 | graph 13 | A[根据text查找节点] --> B{是否存在} 14 | B -- 是 --> C[点击节点] 15 | B -- 否 --> D[根据id查找节点] --> E{是否存在} 16 | E -- 是 --> C 17 | E -- 否 --> F[根据bounds查找节点] --> G{是否存在} 18 | G -- 是 --> C 19 | G -- 否 --> H[结束] 20 | 21 | classDef default color:#fff; 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/guide/contributing/layout-view.md: -------------------------------------------------------------------------------- 1 | # 布局查看 2 | 3 | 使用布局查看器可以探查屏幕节点的布局结构,例如使用[web-editor](https://github.com/alibaba/web-editor)或 Android Studio 的布局查看器。 4 | 5 | ## 应用布局参考图 6 | 7 | 当选中想要的目标节点时,会显示该节点的布局结构,参考如下: 8 | 9 | ![应用布局参考图](/layout-reference.png) 10 | 11 | ### 配置 skipTexts 和 skipIds 12 | 13 | text 呈现的值,可以对应到配置文件中的 text 14 | 15 | resourceId 呈现的值,可以对应到配置文件中的 id 16 | 17 | ### 配置 skipBounds 18 | 19 | skipBounds 的值是需要计算得到的,可能长得像这样 20 | 21 | ```yaml 22 | - packageName: com.xxx.xxx 23 | skipBounds: 24 | - bound: 1223,196,1384,308 25 | resolution: 1440,3024 26 | ``` 27 | 28 | 其中的 1440,3024 是你当前手机屏幕的最大宽高。 29 | 30 | 其中的 1223,196,1384,308 分别表示一个节点在屏幕中的: left,top,right,bottom 31 | 32 | 参考上图中 rect 的信息: left=x,top=y,right=x+width,bottom=y+height 33 | 34 | ![Android屏幕节点布局](/android-rect.png) 35 | -------------------------------------------------------------------------------- /docs/guide/intro/DownloadApp.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 21 | -------------------------------------------------------------------------------- /docs/guide/intro/download-app.md: -------------------------------------------------------------------------------- 1 | # 下载 APP 2 | 3 | | 下载方式 | 下载地址 | 4 | | -------- | -------------------------------------------------------------------- | 5 | | 直接下载 | | 6 | | Github | [点击跳转](https://github.com/GuoXiCheng/SKIP/releases) | 7 | | 夸克网盘 | [点击跳转](https://pan.quark.cn/s/8502a8ff74c3) | 8 | | 百度网盘 | [点击跳转](https://pan.baidu.com/s/1tDXPcEUSZj5qNkOEmToz0A?pwd=m9u9) | 9 | 10 | 13 | -------------------------------------------------------------------------------- /docs/guide/intro/getting-started.md: -------------------------------------------------------------------------------- 1 | # 开始使用 2 | 3 | ## 步骤一: 点击“启动”按钮 4 | 5 | ![](/images/getting-started-step-1.png) 6 | 7 | ## 步骤二: 点击“已下载的应用” 8 | 9 | ![](/images/getting-started-step-2.png) 10 | 11 | ## 步骤三: 点击“SKIP” 12 | 13 | ![](/images/getting-started-step-3.png) 14 | 15 | ## 步骤四: 开启“无障碍服务” 16 | 17 | ![](/images/getting-started-step-4.png) 18 | 19 | ## 步骤五: 勾选“同意”并点击“确定” 20 | 21 | ![](/images/getting-started-step-5.png) 22 | -------------------------------------------------------------------------------- /docs/guide/intro/what-is-skip.md: -------------------------------------------------------------------------------- 1 | # 基本介绍 2 | 3 | ## 什么是 SKIP 4 | 5 | SKIP 是一款免费开源的安卓应用,旨在利用安卓无障碍服务帮助用户快速点击 APP 开屏广告的跳过按钮,让你的使用体验更加流畅。 6 | 7 | ## 主界面预览 8 | 9 | ![主界面预览](/images/main-interface-light.png) 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/guide/keep-alive/intro.md: -------------------------------------------------------------------------------- 1 | # 应用保活功能介绍 2 | 3 | ## 为什么要应用保活 4 | 5 | 在移动端,应用保活是指应用在后台运行时,保持应用的一些功能或服务持续运行,以提升用户体验。例如,音乐播放器在后台播放音乐。 6 | 7 | 为了保证无障碍服务的正常运行,需要保证应用在后台运行时,服务不会被系统回收。因此,需要使用应用保活功能。 8 | 9 | ## 应用保活的方式 10 | 11 | - 允许应用自启动 12 | - 锁定应用 13 | - 使用前台服务常驻通知栏 14 | -------------------------------------------------------------------------------- /docs/guide/settings/intro.md: -------------------------------------------------------------------------------- 1 | # 设置功能介绍 2 | 3 | ## 自动更新 4 | 5 | 开启「自动更新」后,应用会在后台每隔 12 小时左右检查: 6 | 7 | - 「应用版本号」是否有新版本,如果有,在「关于」页面会显示提示。 8 | - 「配置版本号」是否有新版本,如果有,会主动更新当前的配置文件。 9 | 10 | ## 后台任务隐藏 11 | 12 | 开启「后台任务隐藏」后,在后台任务窗口中,「SKIP」应用窗口会被隐藏。 13 | 14 | ## 是否允许提示 15 | 16 | 该功能需要开启「允许通知」。 17 | 18 | 开启「是否允许提示」后,当成功执行「点击」操作时,会显示提示「已为您跳过广告」。 19 | 20 | ## 是否使用严格模式 21 | 22 | 如果开启「严格模式」,「SKIP」会完全遵循配置文件中定义的规则,因此,如果配置文件中没有定义规则的应用,就不会执行「点击」操作。 23 | 24 | 如果关闭「严格模式」,「SKIP」会默认搜索界面中的「跳过」关键字,如果找到,就会执行「点击」操作。因此,即使没有在配置文件中定义规则的应用,也可以实现「跳过广告」的功能。但同时,也会存在误触发的情况出现。 25 | 26 | ## 自定义配置 27 | 28 | 点击「自定义配置」,可以输入自定义配置规则。 29 | 30 | 支持输入纯文本的 YAML 格式或 JSON 格式,或可以返回 JSON 或 YAML 文本的 URL 链接。 31 | 32 | 配置规则参考:[自定义配置](/advance/custom-config/intro) 33 | 34 | ## 切换主题 35 | 36 | 点击「切换主题」,可以选择「浅色主题」、「深色主题」、「跟随系统」。 37 | -------------------------------------------------------------------------------- /docs/guide/usage/background-process-keep-alive.md: -------------------------------------------------------------------------------- 1 | # 后台进程保活 2 | 3 | ## 1. 应用后台锁定 4 | 5 | 进入「后台管理」,长按「SKIP」,锁定 6 | 7 | ![](/xiaomi-app-backend-lock-dark.png) 8 | 9 | ## 2. 忽略电池优化 10 | 11 | 进入「应用信息」,进入「省电策略」,选择「无限制」 12 | 13 | ![](/xiaomi-ignoring-battery-optimization-dark.png) 14 | 15 | ## 3. 允许自启动 16 | 17 | 进入「应用信息」,打开「自启动」 18 | 19 | ![](/xiaomi-enable-self-start-dark.png) 20 | -------------------------------------------------------------------------------- /docs/guide/usage/enable-accessibility-service.md: -------------------------------------------------------------------------------- 1 | # 启用无障碍服务 2 | 3 | ## 1. 点击屏幕中央按钮 4 | 5 | ![](/click-button-on-the-screen-dark.png) 6 | 7 | ## 2. 点击「使用“SKIP”」 8 | 9 | ![](/use-accessibility-dark.png) 10 | -------------------------------------------------------------------------------- /docs/guide/white-list/intro.md: -------------------------------------------------------------------------------- 1 | # 应用白名单功能介绍 2 | 3 | 应用白名单功能会列出所有已经安装的应用,可以在右上角的菜单中选择是否「列出系统应用」。因为系统应用较多,所以默认是不列出的。 4 | 5 | 当把一个应用加入白名单时(开关打开状态),「SKIP」就不会对这个应用执行任何「点击」的动作(无论是否在配置中定义),因此,该功能可以用来防止「SKIP」对某些应用的干扰。 6 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: SKIP 6 | text: 基于安卓无障碍服务的自动跳过开屏广告 App 7 | image: 8 | src: /images/logo.png 9 | actions: 10 | - theme: brand 11 | text: Get Started 12 | link: /guide/intro/what-is-skip 13 | - theme: alt 14 | text: View on GitHub 15 | link: https://github.com/GuoXiCheng/SKIP 16 | --- 17 | -------------------------------------------------------------------------------- /docs/inspect/NodeTree.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | -------------------------------------------------------------------------------- /docs/inspect/hook/useZip.ts: -------------------------------------------------------------------------------- 1 | import JSZip from "jszip"; 2 | import { AccessibilityWindow } from "../types"; 3 | import { ref } from "vue"; 4 | import { FileTable } from "../../my-index-db/file-table"; 5 | import { MyIndexDB } from "../../my-index-db"; 6 | 7 | export function useZip(arrayBuffer: ArrayBuffer) { 8 | const pic = ref(); 9 | const raw = ref(); 10 | const added = ref(false); 11 | 12 | const extractZip = async () => { 13 | const zip = await JSZip.loadAsync(arrayBuffer); 14 | 15 | const jpegFile = zip.filter((relativePath, file) => relativePath.endsWith(".jpeg")); 16 | const blobPromise = jpegFile[0].async("blob"); 17 | 18 | const jsonFile = zip.filter((relativePath, file) => relativePath.endsWith(".json")); 19 | const jsonPromise = jsonFile[0].async("text"); 20 | 21 | const [blobFile, jsonText] = await Promise.all([blobPromise, jsonPromise]); 22 | const jsonObj = JSON.parse(jsonText) as AccessibilityWindow; 23 | 24 | raw.value = jsonObj; 25 | pic.value = blobFile; 26 | 27 | const { fileId, appName, packageName, deviceName, createTime } = jsonObj; 28 | const targetFileTable = await FileTable.findFileTableByFileId(fileId); 29 | if (targetFileTable == null) { 30 | MyIndexDB.addFileData({ 31 | fileId, 32 | pic: blobFile, 33 | raw: jsonObj, 34 | createTime, 35 | appName, 36 | packageName, 37 | deviceName, 38 | }); 39 | added.value = true; 40 | } 41 | }; 42 | 43 | return { pic, raw, added, extractZip }; 44 | } 45 | -------------------------------------------------------------------------------- /docs/inspect/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: false 3 | --- 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/inspect/types.ts: -------------------------------------------------------------------------------- 1 | export interface Tree { 2 | label: string; 3 | children?: Tree[]; 4 | } 5 | 6 | export interface AccessibilityWindow { 7 | fileId: string; 8 | appName: string; 9 | activityName: string; 10 | packageName: string; 11 | screenHeight: number; 12 | screenWidth: number; 13 | createTime: number; 14 | deviceName: string; 15 | nodes: AccessibilityNode[]; 16 | } 17 | 18 | export interface AccessibilityNode { 19 | childCount: number; 20 | className: string; 21 | depth: number; 22 | nodeId: number; 23 | parentId: number; 24 | left: number; 25 | top: number; 26 | right: number; 27 | bottom: number; 28 | isClickable: boolean; 29 | text?: string; 30 | viewIdResourceName?: string; 31 | [key: string]: any; 32 | } 33 | 34 | export interface AccessibilityNodeTree { 35 | label: string; 36 | children: AccessibilityNodeTree[]; 37 | text?: string; 38 | className?: string; 39 | viewIdResourceName?: string; 40 | childCount: number; 41 | } 42 | 43 | export interface FileTableData { 44 | fileId: string; 45 | createTime: number; 46 | appName: string; 47 | packageName: string; 48 | deviceName: string; 49 | } 50 | 51 | export interface FileItemData { 52 | fileId: string; 53 | pic: Blob; 54 | raw: AccessibilityWindow; 55 | } 56 | 57 | export type FileData = FileTableData & FileItemData; 58 | -------------------------------------------------------------------------------- /docs/my-index-db/file-item.ts: -------------------------------------------------------------------------------- 1 | import { MyIndexDB } from "."; 2 | 3 | export const FILE_ITEM_NAME = "FileItem"; 4 | 5 | export class FileItem { 6 | static async findFileItemByFileId(fileId: string) { 7 | const db = await MyIndexDB.getMyIndexDB(); 8 | return db.get(FILE_ITEM_NAME, fileId); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /docs/my-index-db/file-table.ts: -------------------------------------------------------------------------------- 1 | import { MyIndexDB } from "."; 2 | 3 | export const FILE_TABLE_NAME = "FileTable"; 4 | 5 | export class FileTable { 6 | static async findFileTableByFileId(fileId: string) { 7 | const db = await MyIndexDB.getMyIndexDB(); 8 | return db.get(FILE_TABLE_NAME, fileId); 9 | } 10 | 11 | static async findAllFileTable() { 12 | const db = await MyIndexDB.getMyIndexDB(); 13 | return db.getAll(FILE_TABLE_NAME); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docs/public/5f686585-c5d0-4057-93b2-c54832ffb393.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/5f686585-c5d0-4057-93b2-c54832ffb393.zip -------------------------------------------------------------------------------- /docs/public/android-rect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/android-rect.png -------------------------------------------------------------------------------- /docs/public/click-button-on-the-screen-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/click-button-on-the-screen-dark.png -------------------------------------------------------------------------------- /docs/public/click-button-on-the-screen-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/click-button-on-the-screen-light.png -------------------------------------------------------------------------------- /docs/public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/images/favicon.ico -------------------------------------------------------------------------------- /docs/public/images/getting-started-step-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/images/getting-started-step-1.png -------------------------------------------------------------------------------- /docs/public/images/getting-started-step-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/images/getting-started-step-2.png -------------------------------------------------------------------------------- /docs/public/images/getting-started-step-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/images/getting-started-step-3.png -------------------------------------------------------------------------------- /docs/public/images/getting-started-step-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/images/getting-started-step-4.png -------------------------------------------------------------------------------- /docs/public/images/getting-started-step-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/images/getting-started-step-5.png -------------------------------------------------------------------------------- /docs/public/images/images.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/images/images.7z -------------------------------------------------------------------------------- /docs/public/images/inspect-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/images/inspect-intro.png -------------------------------------------------------------------------------- /docs/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/images/logo.png -------------------------------------------------------------------------------- /docs/public/images/main-interface-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/images/main-interface-light.png -------------------------------------------------------------------------------- /docs/public/js/baidu-analytics.js: -------------------------------------------------------------------------------- 1 | var _hmt = _hmt || []; 2 | (function () { 3 | var hm = document.createElement("script"); 4 | hm.src = "https://hm.baidu.com/hm.js?d3d747ec2508d20c876d4d06e9dbd248"; 5 | var s = document.getElementsByTagName("script")[0]; 6 | s.parentNode.insertBefore(hm, s); 7 | })(); 8 | 9 | -------------------------------------------------------------------------------- /docs/public/layout-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/layout-reference.png -------------------------------------------------------------------------------- /docs/public/main-interface-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/main-interface-dark.png -------------------------------------------------------------------------------- /docs/public/main-interface-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/main-interface-light.png -------------------------------------------------------------------------------- /docs/public/skip-bottom-screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/skip-bottom-screenshot.jpg -------------------------------------------------------------------------------- /docs/public/skip-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/skip-icon.png -------------------------------------------------------------------------------- /docs/public/use-accessibility-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/use-accessibility-dark.png -------------------------------------------------------------------------------- /docs/public/use-accessibility-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/use-accessibility-light.png -------------------------------------------------------------------------------- /docs/public/xiaomi-app-backend-lock-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/xiaomi-app-backend-lock-dark.png -------------------------------------------------------------------------------- /docs/public/xiaomi-app-backend-lock-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/xiaomi-app-backend-lock-light.png -------------------------------------------------------------------------------- /docs/public/xiaomi-enable-self-start-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/xiaomi-enable-self-start-dark.png -------------------------------------------------------------------------------- /docs/public/xiaomi-enable-self-start-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/xiaomi-enable-self-start-light.png -------------------------------------------------------------------------------- /docs/public/xiaomi-enable-self-start.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/xiaomi-enable-self-start.jpg -------------------------------------------------------------------------------- /docs/public/xiaomi-ignoring-battery-optimization-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/xiaomi-ignoring-battery-optimization-dark.png -------------------------------------------------------------------------------- /docs/public/xiaomi-ignoring-battery-optimization-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/docs/public/xiaomi-ignoring-battery-optimization-light.png -------------------------------------------------------------------------------- /docs/visual/Header.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 19 | -------------------------------------------------------------------------------- /docs/visual/Home.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 30 | -------------------------------------------------------------------------------- /docs/visual/LeftForm.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 25 | -------------------------------------------------------------------------------- /docs/visual/RightCode.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 23 | -------------------------------------------------------------------------------- /docs/visual/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: false 3 | --- 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. For more details, visit 12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Aug 29 10:36:05 CST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gui/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/.DS_Store -------------------------------------------------------------------------------- /gui/backend_lock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/backend_lock.jpg -------------------------------------------------------------------------------- /gui/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/background.png -------------------------------------------------------------------------------- /gui/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/favicon-16x16.png -------------------------------------------------------------------------------- /gui/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/favicon-32x32.png -------------------------------------------------------------------------------- /gui/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/main.png -------------------------------------------------------------------------------- /gui/start_icon/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/.DS_Store -------------------------------------------------------------------------------- /gui/start_icon/android/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/android/ic_launcher.png -------------------------------------------------------------------------------- /gui/start_icon/android/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/android/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /gui/start_icon/android/mipmap-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/android/mipmap-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /gui/start_icon/android/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/android/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /gui/start_icon/android/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/android/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /gui/start_icon/android/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/android/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /gui/start_icon/android/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/android/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /gui/start_icon/ios/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/.DS_Store -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-1024.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-20-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-20-ipad.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-20@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-20@2x-ipad.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-20@2x.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-20@3x.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-29-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-29-ipad.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-29@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-29@2x-ipad.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-76@2x.png -------------------------------------------------------------------------------- /gui/start_icon/ios/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/gui/start_icon/ios/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /gui/svg/all_inclusive_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/app_registration_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/arrow_back_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/block_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/brightness_4_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/brightness_5_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/brightness_6_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/check_circle_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/cloud_download_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/counter_1_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/counter_2_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/counter_3_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/counter_4_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/error_24dp_000000_FILL0_wght0_GRAD0_opszNaN.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/fit_screen_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/float_landscape_2_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/help_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/hide_image_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/info_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/list_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/lists_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/notifications_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/photo_camera_64dp_000000_FILL1_wght400_GRAD0_opsz48.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/point_scan_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/screenshot_keyboard_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/settings_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/sync_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/target_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/tune_24dp_000000_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/svg/warning_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | testTimeout: 30000, // 设置测试超时时间 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | collectCoverage: true, // 开启测试覆盖率 6 | coverageDirectory: "coverage", // 指定覆盖率报告输出目录 7 | collectCoverageFrom: [ 8 | // 指定需要收集覆盖率的文件 9 | "scripts/**/*.ts", // 包括 src 目录下所有的 TypeScript 文件 10 | "!scripts/**/*.d.ts", // 排除 TypeScript 声明文件 11 | "!scripts/__tests__/**/*.ts", 12 | ], 13 | testMatch: ["/scripts/__tests__/*.test.ts"], // 指定测试文件的匹配规则 14 | testPathIgnorePatterns: [], // 指定需要忽略的测试文件 15 | }; 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "skip", 3 | "version": "1.0.0", 4 | "description": "![GitHub](https://img.shields.io/github/license/GuoXiCheng/SKIP) ![GitHub all releases](https://img.shields.io/github/downloads/GuoXiCheng/SKIP/total) ![GitHub Repo stars](https://img.shields.io/github/stars/GuoXiCheng/SKIP)", 5 | "main": "index.js", 6 | "scripts": { 7 | "docs:dev": "vitepress dev docs", 8 | "docs:build": "vitepress build docs", 9 | "docs:preview": "vitepress preview docs", 10 | "release-file": "ts-node scripts/index.ts --func=release-file --apkPath=apk --releasePath=docs/.vitepress/dist", 11 | "yaml-check": "ts-node ./scripts/yaml-check.ts", 12 | "test": "jest --coverage" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "devDependencies": { 17 | "@codemirror/lang-yaml": "^6.1.2", 18 | "@codemirror/theme-one-dark": "^6.1.2", 19 | "@types/crypto-js": "^4.2.2", 20 | "@types/jest": "^29.5.14", 21 | "@types/js-yaml": "^4.0.9", 22 | "autoprefixer": "^10.4.20", 23 | "codemirror": "^6.0.1", 24 | "crypto-js": "^4.2.0", 25 | "element-plus": "^2.9.2", 26 | "idb": "^8.0.1", 27 | "jest": "^29.7.0", 28 | "js-yaml": "^4.1.0", 29 | "jszip": "^3.10.1", 30 | "postcss": "^8.4.49", 31 | "tailwindcss": "^3.4.17", 32 | "ts-jest": "^29.2.5", 33 | "ts-node": "^10.9.2", 34 | "vitepress": "^1.5.0", 35 | "vitepress-plugin-mermaid": "^2.0.17", 36 | "vue-codemirror6": "^1.3.8", 37 | "zod": "^3.24.1" 38 | }, 39 | "dependencies": { 40 | "skip": "file:" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /scripts/__tests__/examples/apk-has-version/SKIP-v2.1.1.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/scripts/__tests__/examples/apk-has-version/SKIP-v2.1.1.apk -------------------------------------------------------------------------------- /scripts/__tests__/examples/apk-multiple/SKIP-v2.0.0.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/scripts/__tests__/examples/apk-multiple/SKIP-v2.0.0.apk -------------------------------------------------------------------------------- /scripts/__tests__/examples/apk-multiple/SKIP-v2.0.1.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/scripts/__tests__/examples/apk-multiple/SKIP-v2.0.1.apk -------------------------------------------------------------------------------- /scripts/__tests__/examples/apk-no-version/SKIP.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/scripts/__tests__/examples/apk-no-version/SKIP.apk -------------------------------------------------------------------------------- /scripts/__tests__/examples/apk-not-apk/SKIP-v2.0.0.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoXiCheng/SKIP/3f4dc18e8d53939c3ed7ade1541361916228c84e/scripts/__tests__/examples/apk-not-apk/SKIP-v2.0.0.txt -------------------------------------------------------------------------------- /scripts/__tests__/examples/correct-yaml.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipTexts: 3 | - text: 跳过 4 | length: 2 5 | skipIds: 6 | - id: com.test.skip:id/skip 7 | skipBounds: 8 | - bound: 123,456,789,1011 9 | resolution: 1080,1920 10 | -------------------------------------------------------------------------------- /scripts/__tests__/examples/missing-packageName.yaml: -------------------------------------------------------------------------------- 1 | - package: com.test.skip 2 | skipTexts: 3 | - text: 跳过 4 | length: 2 5 | skipIds: 6 | - id: com.test.skip:id/skip 7 | skipBounds: 8 | - bound: 123,456,789,1011 9 | resolution: 1080,1920 -------------------------------------------------------------------------------- /scripts/__tests__/examples/only-packageName.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip -------------------------------------------------------------------------------- /scripts/__tests__/examples/packageName-empty.yaml: -------------------------------------------------------------------------------- 1 | - packageName: " " 2 | skipTexts: 3 | - text: 跳过 4 | length: 2 5 | skipIds: 6 | - id: com.test.skip:id/skip 7 | skipBounds: 8 | - bound: 123,456,789,1011 9 | resolution: 1080,1920 -------------------------------------------------------------------------------- /scripts/__tests__/examples/packageName-leading-space.yaml: -------------------------------------------------------------------------------- 1 | - packageName: " com.test.skip" 2 | skipTexts: 3 | - text: 跳过 4 | length: 2 5 | skipIds: 6 | - id: com.test.skip:id/skip 7 | skipBounds: 8 | - bound: 123,456,789,1011 9 | resolution: 1080,1920 -------------------------------------------------------------------------------- /scripts/__tests__/examples/packageName-not-string.yaml: -------------------------------------------------------------------------------- 1 | - packageName: 123456 2 | skipTexts: 3 | - text: 跳过 4 | length: 2 5 | skipIds: 6 | - id: com.test.skip:id/skip 7 | skipBounds: 8 | - bound: 123,456,789,1011 9 | resolution: 1080,1920 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipBounds-bound-empty.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipBounds: 3 | - resolution: 1920,1080 4 | bound: " " -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipBounds-bound-not-4-parts.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipBounds: 3 | - resolution: 1920,1080 4 | bound: 123,456,789,1011 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipBounds-bound-not-number.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipBounds: 3 | - resolution: 1920,1080 4 | bound: "a,b,c,d" -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipBounds-bound-not-string.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipBounds: 3 | - resolution: 1920,1080 4 | bound: 1234 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipBounds-missing-bound.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipBounds: 3 | - resolution: 1920,1080 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipBounds-missing-resolution.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipBounds: 3 | - bound: 123,456,789,1011 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipBounds-not-array.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipBounds: "123" -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipBounds-resolution-empty.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipBounds: 3 | - resolution: " " 4 | bound: 123,456,789,321 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipBounds-resolution-not-2-parts.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipBounds: 3 | - resolution: 1920,1080 4 | bound: 123,456,789,321 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipBounds-resolution-not-number.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipBounds: 3 | - resolution: a,b 4 | bound: 123,456,789,321 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipBounds-resolution-not-string.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipBounds: 3 | - resolution: 1920 4 | bound: 123,456,789,321 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipIds-id-empty.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipIds: 3 | - id: " " -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipIds-id-leading-space.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipIds: 3 | - id: " com.test.skip:id/skip" -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipIds-id-not-string.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipIds: 3 | - id: 123 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipIds-missing-id.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipIds: 3 | - ids: com.test.skip:id/skip -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipIds-not-array.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipIds: com.test.skip:id/skip -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipTexts-length-negative.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipTexts: 3 | - length: -1 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipTexts-length-not-number.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipTexts: 3 | - length: "123" -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipTexts-not-array.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipTexts: 跳过 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipTexts-text-empty.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipTexts: 3 | - text: " " -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipTexts-text-leading-space.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipTexts: 3 | - text: " 跳过" -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipTexts-text-not-string.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipTexts: 3 | - text: 123 -------------------------------------------------------------------------------- /scripts/__tests__/examples/skipTexts-unknown-keys.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipTexts: 3 | - unknownKey: "123" -------------------------------------------------------------------------------- /scripts/__tests__/examples/unknown-keys.yaml: -------------------------------------------------------------------------------- 1 | - packageName: com.test.skip 2 | skipTexts: 3 | - text: 跳过 4 | length: 2 5 | skipIds: 6 | - id: com.test.skip:id/skip 7 | skipBounds: 8 | - bound: 123,456,789,1011 9 | resolution: 1080,1920 10 | unknownKey: unknownValue -------------------------------------------------------------------------------- /scripts/index.ts: -------------------------------------------------------------------------------- 1 | (() => { 2 | const args = process.argv.slice(2); 3 | const params: Record = {}; 4 | args.forEach((arg) => { 5 | const [key, value] = arg.split("="); 6 | params[key.substring(2)] = value; 7 | }); 8 | 9 | if (params.func === "release-file") { 10 | const { releaseFile } = require("./release-file"); 11 | releaseFile(params); 12 | } 13 | })(); 14 | -------------------------------------------------------------------------------- /scripts/release-file.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | 3 | interface ReleaseFileOptions { 4 | apkPath: string; 5 | releasePath: string; 6 | } 7 | 8 | export function releaseFile(options: ReleaseFileOptions) { 9 | const { apkPath, releasePath } = options; 10 | if (!fs.existsSync(apkPath) || !fs.existsSync(releasePath)) { 11 | throw new Error("apk path and release path should exist"); 12 | } 13 | 14 | const apkFileList = fs.readdirSync(apkPath); 15 | 16 | if (apkFileList.length !== 1) { 17 | throw new Error("apk path should contain only one file"); 18 | } 19 | 20 | const apkFile = apkFileList[0]; 21 | if (!apkFile.endsWith(".apk")) { 22 | throw new Error("apk file should end with .apk"); 23 | } 24 | 25 | const regex = /SKIP-v(\d+\.\d+\.\d+)\.apk/; 26 | const match = apkFile.match(regex); 27 | if (!match) { 28 | throw new Error("apk file should contain version number"); 29 | } 30 | 31 | const apkVersion = match[1]; 32 | 33 | // 写入 apkVersion 到 releasePath 34 | fs.writeFileSync(`${releasePath}/latest_version.txt`, apkVersion); 35 | // 复制 apkFile 到 releasePath 36 | fs.copyFileSync(`${apkPath}/${apkFile}`, `${releasePath}/${apkFile}`); 37 | // 复制 config 38 | fs.copyFileSync("app/src/main/assets/skip_config.yaml", `${releasePath}/skip_config.yaml`); 39 | fs.copyFileSync("app/src/main/assets/skip_config_v2.yaml", `${releasePath}/skip_config_v2.yaml`); 40 | fs.copyFileSync("app/src/main/assets/skip_config_v3.yaml", `${releasePath}/skip_config_v3.yaml`); 41 | } 42 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { setUrl("https://maven.aliyun.com/repository/central") } 4 | maven { setUrl("https://maven.aliyun.com/repository/jcenter") } 5 | maven { setUrl("https://maven.aliyun.com/repository/google") } 6 | maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") } 7 | maven { setUrl("https://maven.aliyun.com/repository/public") } 8 | maven { setUrl("https://jitpack.io") } 9 | 10 | google() 11 | mavenCentral() 12 | gradlePluginPortal() 13 | } 14 | } 15 | 16 | dependencyResolutionManagement { 17 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 18 | repositories { 19 | maven { setUrl("https://maven.aliyun.com/repository/central") } 20 | maven { setUrl("https://maven.aliyun.com/repository/jcenter") } 21 | maven { setUrl("https://maven.aliyun.com/repository/google") } 22 | maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") } 23 | maven { setUrl("https://maven.aliyun.com/repository/public") } 24 | maven { setUrl("https://jitpack.io") } 25 | 26 | google() 27 | mavenCentral() 28 | } 29 | } 30 | 31 | rootProject.name = "SKIP" 32 | include(":app") 33 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './docs/.vitepress/**/*.{js,ts,vue}', 5 | './docs/**/*.{js,ts,vue,md}', 6 | ], 7 | theme: { 8 | extend: {}, 9 | }, 10 | plugins: [], 11 | } 12 | 13 | --------------------------------------------------------------------------------