├── .github
└── workflows
│ └── build-release.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── FAQ.md
├── LICENSE
├── README.md
├── README_EN.md
├── build.gradle.kts
├── composeApp
├── build.gradle.kts
├── compose-desktop.pro
├── launcher
│ ├── icon.icns
│ ├── icon.ico
│ └── icon.png
├── libs
│ ├── bcpkix-jdk18on-1.80.jar
│ ├── bcprov-jdk18on-1.80.jar
│ └── bcutil-jdk18on-1.80.jar
├── resources
│ ├── common
│ │ ├── honor_unsigned.apk
│ │ ├── huawei_unsigned.apk
│ │ ├── oppo_unsigned.apk
│ │ ├── qq_unsigned.apk
│ │ ├── vivo_unsigned.apk
│ │ └── xiaomi_unsigned.apk
│ ├── linux
│ │ └── aapt2
│ ├── macos-arm64
│ │ └── aapt2
│ ├── macos-x64
│ │ └── aapt2
│ └── windows
│ │ └── aapt2.exe
└── src
│ ├── commonMain
│ ├── composeResources
│ │ ├── drawable
│ │ │ └── icon.png
│ │ ├── files
│ │ │ ├── lottie_loading_dark.json
│ │ │ ├── lottie_loading_light.json
│ │ │ ├── lottie_main_1_dark.json
│ │ │ ├── lottie_main_1_light.json
│ │ │ ├── lottie_main_2_dark.json
│ │ │ ├── lottie_main_2_light.json
│ │ │ ├── lottie_main_3_dark.json
│ │ │ ├── lottie_main_3_light.json
│ │ │ ├── lottie_main_4_dark.json
│ │ │ ├── lottie_main_4_light.json
│ │ │ └── upload.json
│ │ ├── font
│ │ │ └── ZCOOLKuaiLe-Regular.ttf
│ │ ├── values-zh-rCN
│ │ │ └── strings.xml
│ │ └── values
│ │ │ └── strings.xml
│ └── kotlin
│ │ ├── App.kt
│ │ ├── constant
│ │ └── ConfigConstant.kt
│ │ ├── database
│ │ └── PreferencesDataSource.kt
│ │ ├── model
│ │ ├── FileSelectorType.kt
│ │ ├── IconFactoryData.kt
│ │ ├── Model.kt
│ │ ├── Sequence.kt
│ │ └── UserData.kt
│ │ ├── platform
│ │ └── Expect.kt
│ │ ├── theme
│ │ ├── Color.kt
│ │ ├── Theme.kt
│ │ └── Type.kt
│ │ ├── ui
│ │ ├── ApkInformation.kt
│ │ ├── ApkSignature.kt
│ │ ├── ClearBuild.kt
│ │ ├── IconFactory.kt
│ │ ├── JunkCode.kt
│ │ ├── SetUp.kt
│ │ ├── SignatureGeneration.kt
│ │ ├── SignatureInformation.kt
│ │ └── UI.kt
│ │ ├── utils
│ │ ├── AndroidJunkGenerator.kt
│ │ ├── Copy.kt
│ │ ├── CoroutinesUtils.kt
│ │ ├── ExternalCommand.kt
│ │ ├── ImageRequest.kt
│ │ ├── LottieAnimation.kt
│ │ ├── Utils.kt
│ │ └── update.kt
│ │ └── vm
│ │ └── MainViewModel.kt
│ └── desktopMain
│ └── kotlin
│ ├── main.kt
│ └── platform
│ ├── DatabaseDriverFactory.kt
│ └── RustFileDealWithFactory.kt
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── rs
├── Cargo.toml
├── build.rs
├── src
│ ├── lib.rs
│ ├── srgb.rs
│ └── toolkit.udl
└── uniffi-bindgen.rs
├── screenshots
├── fail_to_quantize.png
├── screenshot_apk_information_1.png
├── screenshot_apk_information_1_en.png
├── screenshot_apk_signature_1.png
├── screenshot_apk_signature_1_en.png
├── screenshot_cache_clear_0.png
├── screenshot_cache_clear_0_en.png
├── screenshot_cache_clear_1.png
├── screenshot_cache_clear_1_en.png
├── screenshot_dark.png
├── screenshot_dark_en.png
├── screenshot_icon_factory_1.png
├── screenshot_icon_factory_1_en.png
├── screenshot_icon_factory_2.png
├── screenshot_icon_factory_2_en.png
├── screenshot_light.png
├── screenshot_light_en.png
├── screenshot_signature_generation_1.png
├── screenshot_signature_generation_1_en.png
├── screenshot_signature_information_1.png
├── screenshot_signature_information_1_en.png
├── screenshot_signature_information_2.png
├── screenshot_signature_information_2_en.png
├── screenshot_signature_information_3.png
├── screenshot_signature_information_3_en.png
├── unopen_1.png
├── unopen_2.png
└── unopen_3.png
└── settings.gradle.kts
/.github/workflows/build-release.yml:
--------------------------------------------------------------------------------
1 | name: Build Release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | tag_version:
7 | description: 'Specify the version tag (required)'
8 | required: true
9 | jobs:
10 | create-release-distribution:
11 | strategy:
12 | matrix:
13 | os: [ windows-latest , ubuntu-latest , macos-13 , macos-14 ]
14 | runs-on: ${{ matrix.os }}
15 | name: Create Release Distribution
16 |
17 | steps:
18 | - if: matrix.os != 'macos-14'
19 | name: Setup Jdk
20 | uses: actions/setup-java@v4
21 | with:
22 | distribution: "zulu"
23 | java-version: "17"
24 | architecture: x64
25 |
26 | - if: matrix.os == 'macos-14'
27 | name: Setup Jdk
28 | uses: actions/setup-java@v4
29 | with:
30 | distribution: "zulu"
31 | java-version: "17"
32 | architecture: aarch64
33 |
34 | - name: Checkout
35 | uses: actions/checkout@v4
36 |
37 | - name: PackageReleaseDistributionForCurrentOS
38 | run: ./gradlew packageReleaseDistributionForCurrentOS
39 |
40 | - if: matrix.os == 'windows-latest'
41 | name: Rename File
42 | run: |
43 | $ErrorActionPreference = "Stop"
44 | mv D:\a\AndroidToolKit\AndroidToolKit\composeApp\output\main-release\msi\AndroidToolKit-${{ inputs.tag_version }}.msi D:\a\AndroidToolKit\AndroidToolKit\composeApp\output\main-release\msi\AndroidToolKit-windows-x64.msi
45 | mv D:\a\AndroidToolKit\AndroidToolKit\composeApp\output\main-release\exe\AndroidToolKit-${{ inputs.tag_version }}.exe D:\a\AndroidToolKit\AndroidToolKit\composeApp\output\main-release\exe\AndroidToolKit-windows-x64.exe
46 |
47 | - if: matrix.os == 'ubuntu-latest'
48 | name: Rename File
49 | run: |
50 | mv /home/runner/work/AndroidToolKit/AndroidToolKit/composeApp/output/main-release/deb/androidtoolkit_${{ inputs.tag_version }}-1_amd64.deb /home/runner/work/AndroidToolKit/AndroidToolKit/composeApp/output/main-release/deb/AndroidToolKit-linux-amd64.deb
51 | mv /home/runner/work/AndroidToolKit/AndroidToolKit/composeApp/output/main-release/rpm/androidtoolkit-${{ inputs.tag_version }}-1.x86_64.rpm /home/runner/work/AndroidToolKit/AndroidToolKit/composeApp/output/main-release/rpm/AndroidToolKit-linux-x86_64.rpm
52 |
53 | - if: matrix.os == 'macos-13'
54 | name: Rename File
55 | run: |
56 | mv /Users/runner/work/AndroidToolKit/AndroidToolKit/composeApp/output/main-release/dmg/AndroidToolKit-${{ inputs.tag_version }}.dmg /Users/runner/work/AndroidToolKit/AndroidToolKit/composeApp/output/main-release/dmg/AndroidToolKit-macos-x64.dmg
57 |
58 | - if: matrix.os == 'macos-14'
59 | name: Rename File
60 | run: |
61 | mv /Users/runner/work/AndroidToolKit/AndroidToolKit/composeApp/output/main-release/dmg/AndroidToolKit-${{ inputs.tag_version }}.dmg /Users/runner/work/AndroidToolKit/AndroidToolKit/composeApp/output/main-release/dmg/AndroidToolKit-macos-arm64.dmg
62 |
63 | - if: matrix.os == 'windows-latest'
64 | name: Draft Release
65 | uses: ncipollo/release-action@v1
66 | with:
67 | draft: true
68 | allowUpdates: true
69 | tag: "v${{ inputs.tag_version }}"
70 | artifacts: "composeApp/output/main-release/msi/*,composeApp/output/main-release/exe/*"
71 | token: ${{ secrets.GH_TOKEN }}
72 |
73 | - if: matrix.os == 'ubuntu-latest'
74 | name: Draft Release
75 | uses: ncipollo/release-action@v1
76 | with:
77 | draft: true
78 | allowUpdates: true
79 | tag: "v${{ inputs.tag_version }}"
80 | artifacts: "composeApp/output/main-release/deb/*,composeApp/output/main-release/rpm/*"
81 | token: ${{ secrets.GH_TOKEN }}
82 |
83 | - if: matrix.os == 'macos-13' || matrix.os == 'macos-14'
84 | name: Draft Release
85 | uses: ncipollo/release-action@v1
86 | with:
87 | draft: true
88 | allowUpdates: true
89 | tag: "v${{ inputs.tag_version }}"
90 | artifacts: "composeApp/output/main-release/dmg/*"
91 | token: ${{ secrets.GH_TOKEN }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | **/build/
4 | xcuserdata
5 | !src/**/build/
6 | local.properties
7 | .idea
8 | .DS_Store
9 | captures
10 | .externalNativeBuild
11 | .cxx
12 | *.xcodeproj/*
13 | !*.xcodeproj/project.pbxproj
14 | !*.xcodeproj/xcshareddata/
15 | !*.xcodeproj/project.xcworkspace/
16 | !*.xcworkspace/contents.xcworkspacedata
17 | **/xcshareddata/WorkspaceSettings.xcsettings
18 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | lazyiones@gmail.com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/FAQ.md:
--------------------------------------------------------------------------------
1 | ## 常见问题
2 |
3 | ## 1.无法打开 "AndroidToolKit。app",因为Apple无法检查其是否包含恶意软件。
4 | 出现此问题,按照图片依次点击解决
5 |
6 | | 截图 | 截图 | 截图 |
7 | |:-----------------------------------------------------------------:|:-----------------------------------------------------------------:|:-----------------------------------------------------------------:|
8 | |
|
|
|
9 |
10 | ## 2.使用图标工厂出现报错 Fail to quantize: QUALITY_TOO_LOW
11 |
12 | 出现此问题,打开更多设置,调整为**无损压缩**,或将**PNG质量**项的**最低限度**下调即可
13 | 
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 LazyIonEs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## AndroidToolKit
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
21 |
22 | 简体中文 | [English](./README_EN.md)
23 |
24 | 适用于安卓开发的桌面工具,支持 Windows、Mac 和 Linux :tada:
25 |
26 | > linux 平台未经测试,如有问题,请及时反馈
27 |
28 | ## 主要功能
29 |
30 | - [x] 签名信息 - 分析(APK/签名)的签名信息(modulus、md5、sha-1、sha-256等)
31 | - [x] APK信息 - 解析`AndroidManifest.xml`,提取部分信息
32 | - [x] APK签名 - 对APK进行签名
33 | - [x] 签名生成 - 生成签名证书
34 | - [x] 图标生成 - 一键生成多尺寸图标
35 | - [x] 缓存清理 - 清理Android项目缓存目录
36 |
37 | > 支持APK签名校验;单签名校验(需输入签名密码);文件拖拽;apk签名文件对齐;生成签名指定密钥类型,密钥大小;外观浅色深色模式。
38 |
39 | ## 下载 - [Releases](https://github.com/LazyIonEs/AndroidToolKit/releases/latest)
40 |
41 | | 设备 | 芯片 | 下载 |
42 | |:-------:|:-----------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
43 | | macos | apple | [`macos-arm64.dmg`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-macos-arm64.dmg) |
44 | | macos | intel | [`macos-x64.dmg`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-macos-x64.dmg) |
45 | | windows | intel / amd | [`windows-x64.msi`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-windows-x64.msi) **/** [`windows-x64.exe`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-windows-x64.exe) |
46 | | linux | - | [`linux-amd64.deb`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-linux-amd64.deb) **/** [`linux-x86_64.rpm`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-linux-x86_64.rpm) |
47 |
48 | > [!CAUTION]
49 | > windows版不建议安装到C盘(默认安装路径是C盘 :clown_face:),可能会有权限等问题。遇到问题可以看看 [FAQ](FAQ.md)
50 |
51 | ## 截屏
52 |
53 | | 签名信息 | 签名信息 | 签名信息 |
54 | |:-------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------:|
55 | |
|
|
|
56 | | APK信息 | APK签名 | 签名生成 |
57 | |
|
|
|
58 | | 图标生成 | 缓存清理 | 黑白主题 |
59 | |
|
|
|
60 | | 图标生成 | 缓存清理 | 黑白主题 |
61 | |
|
|
|
62 |
63 | ## 技术栈
64 |
65 | - [Kotlin Multiplatform](https://kotlinlang.org/lp/multiplatform/)
66 | - [Kotlin Coroutines](https://github.com/Kotlin/kotlinx.coroutines)
67 | - [Compose Multiplatform](https://www.jetbrains.com/lp/compose-multiplatform/)
68 | - [Kotlinx Serialization](https://github.com/Kotlin/kotlinx.serialization)
69 | - [Rust](https://github.com/rust-lang/rust)
70 | - [mozjpeg](https://github.com/mozilla/mozjpeg)
71 | - [libimagequant](https://github.com/ImageOptim/libimagequant)
72 | - [uniffi-rs](https://github.com/mozilla/uniffi-rs)
73 |
74 | 有关所使用依赖项的完整列表,请查看 [catalog](/gradle/libs.versions.toml) 文件
75 |
76 | ## License
77 |
78 | ```
79 | MIT License
80 |
81 | Copyright (c) 2024 LazyIonEs
82 |
83 | Permission is hereby granted, free of charge, to any person obtaining a copy
84 | of this software and associated documentation files (the "Software"), to deal
85 | in the Software without restriction, including without limitation the rights
86 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
87 | copies of the Software, and to permit persons to whom the Software is
88 | furnished to do so, subject to the following conditions:
89 |
90 | The above copyright notice and this permission notice shall be included in all
91 | copies or substantial portions of the Software.
92 |
93 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
94 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
95 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
96 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
97 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
98 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
99 | SOFTWARE.
100 | ```
101 |
--------------------------------------------------------------------------------
/README_EN.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## AndroidToolKit
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
21 |
22 |
23 | [简体中文](./README.md) | English
24 |
25 | Desktop tools applicable to Android development, supporting Windows, Mac and Linux :tada:
26 | > The Linux platform has not been tested. If you have any questions, please give us feedback in time.
27 |
28 | ## Key Features
29 |
30 | - [x] Signature Information - Analyze the signature information of (APK/Signature) (modulus, md5, sha-1, sha-256, etc.)
31 | - [x] APK Information - Parse `AndroidManifest.xml` and extract some information
32 | - [x] APK Signature - Sign your APK
33 | - [x] Signature Generation - Generate a signed certificate
34 | - [x] Icon Generation - Generate icons of multiple sizes with one click
35 | - [x] Cache Cleaner - Cleans the Android project cache directory
36 |
37 | > Supports APK signature verification; single signature verification (signature password required); file dragging; apk
38 | > signature file alignment; generate signatures with specified key type and key size; appearance light and dark modes.
39 |
40 | ## Download - [Releases](https://github.com/LazyIonEs/AndroidToolKit/releases/latest)
41 |
42 | | device | chip | download |
43 | |:-------:|:-----------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
44 | | macos | apple | [`macos-arm64.dmg`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-macos-arm64.dmg) |
45 | | macos | intel | [`macos-x64.dmg`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-macos-x64.dmg) |
46 | | windows | intel / amd | [`windows-x64.msi`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-windows-x64.msi) **/** [`windows-x64.exe`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-windows-x64.exe) |
47 | | linux | - | [`linux-amd64.deb`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-linux-amd64.deb) **/** [`linux-x86_64.rpm`](https://github.com/LazyIonEs/AndroidToolKit/releases/latest/download/AndroidToolKit-linux-x86_64.rpm) |
48 |
49 | > [!CAUTION]
50 | > It is not recommended to install the Windows version to the C drive (the default installation path is the C drive :
51 | > clown_face:), which may cause problems such as permissions. If you encounter any problems, please refer
52 | > to [FAQ](FAQ.md)
53 |
54 | ## screenshot
55 |
56 | | Signature Information | Signature Information | Signature Information |
57 | |:----------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------:|
58 | |
|
|
|
59 | | APK Information | APK Signature | Signature generation |
60 | |
|
|
|
61 | | Icon Generation | Cache Cleaner | Black and white theme |
62 | |
|
|
|
63 | | Icon Generation | Cache Cleaner | Black and white theme |
64 | |
|
|
|
65 |
66 | ## Technology Stack
67 |
68 | - [Kotlin Multiplatform](https://kotlinlang.org/lp/multiplatform/)
69 | - [Kotlin Coroutines](https://github.com/Kotlin/kotlinx.coroutines)
70 | - [Compose Multiplatform](https://www.jetbrains.com/lp/compose-multiplatform/)
71 | - [Kotlinx Serialization](https://github.com/Kotlin/kotlinx.serialization)
72 | - [Rust](https://github.com/rust-lang/rust)
73 | - [mozjpeg](https://github.com/mozilla/mozjpeg)
74 | - [libimagequant](https://github.com/ImageOptim/libimagequant)
75 | - [uniffi-rs](https://github.com/mozilla/uniffi-rs)
76 |
77 | For a complete list of dependencies used, check the [catalog](/gradle/libs.versions.toml) file
78 |
79 | ## License
80 |
81 | ```
82 | MIT License
83 |
84 | Copyright (c) 2024 LazyIonEs
85 |
86 | Permission is hereby granted, free of charge, to any person obtaining a copy
87 | of this software and associated documentation files (the "Software"), to deal
88 | in the Software without restriction, including without limitation the rights
89 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
90 | copies of the Software, and to permit persons to whom the Software is
91 | furnished to do so, subject to the following conditions:
92 |
93 | The above copyright notice and this permission notice shall be included in all
94 | copies or substantial portions of the Software.
95 |
96 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
97 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
98 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
99 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
100 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
101 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
102 | SOFTWARE.
103 | ```
104 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | // this is necessary to avoid the plugins to be loaded multiple times
3 | // in each subproject's classloader
4 | alias(libs.plugins.jetbrainsCompose) apply false
5 | alias(libs.plugins.kotlinMultiplatform) apply false
6 | alias(libs.plugins.githubBuildconfig) apply false
7 | alias(libs.plugins.compose.compiler) apply false
8 | alias(libs.plugins.about.libraries) apply false
9 | alias(libs.plugins.hot.reload) apply false
10 | }
--------------------------------------------------------------------------------
/composeApp/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.compose.desktop.application.dsl.TargetFormat
2 | import org.jetbrains.compose.reload.ComposeHotRun
3 | import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
4 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget
5 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
6 | import java.io.FileOutputStream
7 | import java.nio.file.Files
8 |
9 | plugins {
10 | alias(libs.plugins.kotlinMultiplatform)
11 | alias(libs.plugins.jetbrainsCompose)
12 | alias(libs.plugins.githubBuildconfig)
13 | alias(libs.plugins.kotlinSerialization)
14 | alias(libs.plugins.compose.compiler)
15 | alias(libs.plugins.about.libraries)
16 | alias(libs.plugins.hot.reload)
17 | }
18 |
19 | val javaLanguageVersion = JavaLanguageVersion.of(17)
20 | val linuxArmTarget = "aarch64-unknown-linux-gnu"
21 | val linuxX64Target = "x86_64-unknown-linux-gnu"
22 |
23 | val kitVersion by extra("1.5.5")
24 | val kitPackageName = "AndroidToolKit"
25 | val kitDescription = "Desktop tools applicable to Android development, supporting Windows, Mac and Linux"
26 | val kitCopyright = "Copyright (c) 2024 LazyIonEs"
27 | val kitVendor = "LazyIonEs"
28 | val kitLicenseFile = project.rootProject.file("LICENSE")
29 |
30 | val useCross = (properties.getOrDefault("useCross", "false") as String).toBoolean()
31 | val isLinuxAarch64 = (properties.getOrDefault("isLinuxAarch64", "false") as String).toBoolean()
32 |
33 | val rustGeneratedSource = "${layout.buildDirectory.get()}/generated/source/uniffi/main/org/tool/kit/kotlin"
34 |
35 | val aboutLibrariesSource = "src/commonMain/composeResources/files/aboutlibraries.json"
36 |
37 | group = "org.tool.kit"
38 | version = kitVersion
39 |
40 | configurations.commonMainApi {
41 | // com.android.tools:sdk-common
42 | exclude(group = "org.bouncycastle", module = "bcpkix-jdk18on")
43 | exclude(group = "org.bouncycastle", module = "bcprov-jdk18on")
44 | exclude(group = "org.bouncycastle", module = "bcutil-jdk18on")
45 | }
46 |
47 | kotlin {
48 | jvmToolchain {
49 | languageVersion.set(javaLanguageVersion)
50 | }
51 |
52 | jvm("desktop")
53 | jvmToolchain(17)
54 |
55 | sourceSets {
56 | val desktopMain by getting
57 |
58 | desktopMain.kotlin.srcDir(rustGeneratedSource)
59 |
60 | commonMain.dependencies {
61 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
62 | implementation(compose.runtime)
63 | implementation(compose.foundation)
64 | implementation(compose.material)
65 | implementation(compose.ui)
66 | implementation(compose.components.resources)
67 | implementation(compose.material3)
68 | implementation(compose.materialIconsExtended)
69 | implementation(compose.foundation)
70 | implementation(libs.slf4j.api)
71 | implementation(libs.slf4j.simple)
72 | implementation(libs.android.apksig)
73 | implementation(libs.android.sdk.common)
74 | implementation(libs.android.binary.resources)
75 | implementation(libs.commons.codec)
76 | implementation(libs.asm)
77 | implementation(libs.lifecycle.viewmodel.compose)
78 | implementation(libs.jna)
79 | implementation(libs.filekit.core)
80 | implementation(libs.filekit.dialogs)
81 | implementation(libs.filekit.dialogs.compose)
82 | implementation(libs.multiplatform.settings)
83 | implementation(libs.multiplatform.settings.coroutines)
84 | implementation(libs.multiplatform.settings.serialization)
85 | implementation(libs.kotlinx.serialization.json)
86 | implementation(libs.kotlinx.datetime)
87 | implementation("com.jetbrains.intellij.platform:util:243.26053.20") {
88 | exclude(group = "com.fasterxml", module = "aalto-xml")
89 | exclude(group = "com.github.ben-manes.caffeine", module = "caffeine")
90 | exclude(group = "com.intellij.platform", module = "kotlinx-coroutines-core-jvm")
91 | exclude(group = "com.intellij.platform", module = "kotlinx-coroutines-debug")
92 | exclude(group = "com.jetbrains.intellij.platform", module = "util-jdom")
93 | exclude(group = "com.jetbrains.intellij.platform", module = "util-class-loader")
94 | exclude(group = "com.jetbrains.intellij.platform", module = "util-xml-dom")
95 | exclude(group = "commons-codec", module = "commons-codec")
96 | exclude(group = "commons-io", module = "commons-io")
97 | exclude(group = "net.java.dev.jna", module = "jna-platform")
98 | exclude(group = "org.apache.commons", module = "commons-compress")
99 | exclude(group = "org.jetbrains.intellij.deps.fastutil", module = "intellij-deps-fastutil")
100 | exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
101 | exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-core-jvm")
102 | exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-json-jvm")
103 | exclude(group = "org.lz4", module = "lz4-java")
104 | exclude(group = "org.slf4j", module = "log4j-over-slf4j")
105 | exclude(group = "oro", module = "oro")
106 | }
107 | runtimeOnly(libs.kotlinx.coroutines.swing)
108 | implementation(libs.about.libraries.core)
109 | implementation(libs.about.libraries.compose.m3)
110 | implementation(libs.coil.compose)
111 | implementation(libs.zoomimage.compose.coil3)
112 | }
113 | desktopMain.dependencies {
114 | implementation(compose.desktop.currentOs)
115 | }
116 | }
117 | }
118 |
119 | tasks.withType {
120 | compilerOptions.jvmTarget = JvmTarget.JVM_17
121 | compilerOptions.freeCompilerArgs.add("-opt-in=kotlin.RequiresOptIn")
122 | }
123 |
124 | tasks.withType {
125 | javaLauncher.set(javaToolchains.launcherFor {
126 | languageVersion.set(javaLanguageVersion)
127 | })
128 | }
129 |
130 | compose.desktop {
131 | application {
132 | mainClass = "MainKt"
133 |
134 | jvmArgs += listOf("-Dapple.awt.application.appearance=system")
135 |
136 | this@application.dependsOn("rustTasks")
137 |
138 | sourceSets.forEach {
139 | it.java.srcDir(rustGeneratedSource)
140 | }
141 |
142 | nativeDistributions {
143 | targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Exe, TargetFormat.Deb, TargetFormat.Rpm)
144 | packageName = kitPackageName
145 | packageVersion = kitVersion
146 | description = kitDescription
147 | copyright = kitCopyright
148 | vendor = kitVendor
149 | licenseFile.set(kitLicenseFile)
150 |
151 | modules(
152 | "java.compiler",
153 | "java.instrument",
154 | "java.naming",
155 | "java.prefs",
156 | "java.rmi",
157 | "java.sql",
158 | "jdk.management",
159 | "jdk.security.auth",
160 | "jdk.unsupported"
161 | )
162 |
163 | outputBaseDir.set(project.layout.projectDirectory.dir("output"))
164 | appResourcesRootDir.set(project.layout.projectDirectory.dir("resources"))
165 |
166 | linux {
167 | debPackageVersion = packageVersion
168 | rpmPackageVersion = packageVersion
169 | debMaintainer = "lazyiones@gmail.com"
170 | iconFile.set(project.file("launcher/icon.png"))
171 | }
172 | macOS {
173 | dmgPackageVersion = packageVersion
174 | pkgPackageVersion = packageVersion
175 |
176 | packageBuildVersion = packageVersion
177 | dmgPackageBuildVersion = packageVersion
178 | pkgPackageBuildVersion = packageVersion
179 | bundleID = "org.tool.kit"
180 |
181 | dockName = kitPackageName
182 | iconFile.set(project.file("launcher/icon.icns"))
183 | }
184 | windows {
185 | msiPackageVersion = packageVersion
186 | exePackageVersion = packageVersion
187 | menuGroup = packageName
188 | dirChooser = true
189 | perUserInstall = true
190 | shortcut = true
191 | menu = true
192 | upgradeUuid = "2B0C6D0B-BEB7-4E64-807E-BEE0F91C7B04"
193 | iconFile.set(project.file("launcher/icon.ico"))
194 | }
195 | }
196 | buildTypes.release.proguard {
197 | obfuscate.set(true)
198 | optimize.set(true)
199 | joinOutputJars.set(true)
200 | configurationFiles.from(project.file("compose-desktop.pro"))
201 | }
202 | }
203 | }
204 |
205 | task("rustTasks") {
206 | runBuildRust()
207 | }
208 |
209 | tasks.getByName("compileKotlinDesktop").doLast { runBuildRust() }
210 |
211 | // 执行导出收集依赖项详细信息json文件
212 | tasks.getByName("copyNonXmlValueResourcesForCommonMain").dependsOn("exportLibraryDefinitions")
213 |
214 | buildConfig {
215 | className("BuildConfig")
216 | packageName("org.tool.kit")
217 | buildConfigField("APP_NAME", kitPackageName)
218 | buildConfigField("APP_VERSION", kitVersion)
219 | buildConfigField("APP_DESCRIPTION", kitDescription)
220 | buildConfigField("APP_COPYRIGHT", kitCopyright)
221 | buildConfigField("APP_VENDOR", kitVendor)
222 | buildConfigField("APP_LICENSE", "MIT License")
223 | buildConfigField("APP_LICENSE_URI", uri("https://opensource.org/license/mit"))
224 | buildConfigField("APP_LICENSE_FILE", kitLicenseFile)
225 | buildConfigField("AUTHOR_GITHUB_URI", uri("https://github.com/LazyIonEs"))
226 | buildConfigField("APP_GITHUB_URI", uri("https://github.com/LazyIonEs/AndroidToolKit"))
227 | }
228 |
229 | enum class OS {
230 | LINUX,
231 | WINDOWS,
232 | MAC
233 | }
234 |
235 | fun currentOs(): OS {
236 | val os = System.getProperty("os.name")
237 | return when {
238 | os.equals("Mac OS X", ignoreCase = true) -> OS.MAC
239 | os.startsWith("Win", ignoreCase = true) -> OS.WINDOWS
240 | os.startsWith("Linux", ignoreCase = true) -> OS.LINUX
241 | else -> error("Unknown OS name: $os")
242 | }
243 | }
244 |
245 | fun runBuildRust() {
246 | val destinyLibFile = getRustDestinyLibFile()
247 | val destinyKtFile = getRustDestinyKtFile()
248 | if (destinyLibFile.exists() && destinyKtFile.exists()) {
249 | // 已存在,不重新编译
250 | return
251 | }
252 | buildRust()
253 | copyRustBuild()
254 | generateKotlinFromUdl()
255 | }
256 |
257 | fun buildRust() {
258 | providers.exec {
259 | println("Build rs called")
260 | val binary = if (currentOs() == OS.LINUX && useCross) {
261 | "cross"
262 | } else {
263 | "cargo"
264 | }
265 |
266 | val params = mutableListOf(
267 | binary, "build", "--release", "--features=uniffi/cli",
268 | )
269 |
270 | if (currentOs() == OS.LINUX && useCross) {
271 | if (isLinuxAarch64) {
272 | params.add("--target=$linuxArmTarget")
273 | } else {
274 | params.add("--target=$linuxX64Target")
275 | }
276 | }
277 |
278 | workingDir = File(rootDir, "rs")
279 | commandLine = params
280 | }.result.get()
281 | }
282 |
283 | fun copyRustBuild() {
284 | val workingDirPath = if (currentOs() == OS.LINUX && useCross) {
285 | if (isLinuxAarch64) {
286 | "rs/target/$linuxArmTarget/release"
287 | } else {
288 | "rs/target/$linuxX64Target/release"
289 | }
290 | } else {
291 | "rs/target/release"
292 | }
293 |
294 | val workingDir = File(rootDir, workingDirPath)
295 |
296 | val originLib = when (currentOs()) {
297 | OS.LINUX -> "libtoolkit_rs.so"
298 | OS.WINDOWS -> "toolkit_rs.dll"
299 | OS.MAC -> "libtoolkit_rs.dylib"
300 | }
301 |
302 | val originFile = File(workingDir, originLib)
303 | val destinyFile = getRustDestinyLibFile()
304 |
305 | Files.copy(originFile.toPath(), FileOutputStream(destinyFile))
306 | println("Copy rs build completed")
307 | }
308 |
309 | fun getRustDestinyLibFile(): File {
310 | val outputDir = "${layout.buildDirectory.asFile.get().absolutePath}/classes/kotlin/desktop/main"
311 | val directory = File(outputDir)
312 | directory.mkdirs()
313 | val destinyLib = when (currentOs()) {
314 | OS.LINUX -> "libuniffi_toolkit.so"
315 | OS.WINDOWS -> "uniffi_toolkit.dll"
316 | OS.MAC -> "libuniffi_toolkit.dylib"
317 | }
318 | val destinyFile = File(directory, destinyLib)
319 | return destinyFile
320 | }
321 |
322 | fun getRustDestinyKtFile() =
323 | File(rustGeneratedSource + File.separator + "uniffi" + File.separator + "toolkit", "toolkit.kt")
324 |
325 | fun generateKotlinFromUdl() {
326 | providers.exec {
327 | workingDir = File(rootDir, "rs")
328 | commandLine = listOf(
329 | "cargo", "run", "--features=uniffi/cli",
330 | "--bin", "uniffi-bindgen", "generate", "src/toolkit.udl",
331 | "--language", "kotlin",
332 | "--out-dir", rustGeneratedSource
333 | )
334 | }.result.get()
335 | }
336 |
337 | aboutLibraries {
338 | export {
339 | outputFile = file(aboutLibrariesSource)
340 | }
341 | }
342 |
343 | tasks.withType().configureEach {
344 | mainClass.set("MainKt")
345 | }
346 |
347 | composeCompiler {
348 | featureFlags.add(ComposeFeatureFlag.OptimizeNonSkippingGroups)
349 | }
--------------------------------------------------------------------------------
/composeApp/compose-desktop.pro:
--------------------------------------------------------------------------------
1 | -ignorewarnings
2 | # -verbose
3 |
4 | -dontnote java.awt.*
5 | -dontnote java.nio.*
6 | -dontnote java.util.*
7 | -dontnote com.intellij.**
8 | -dontnote org.jdom.**
9 | -dontnote com.google.**
10 | -dontnote org.apache.**
11 | -dontnote com.sun.**
12 | -dontnote javax.activation.**
13 | -dontnote org.intellij.**
14 | -dontnote org.jetbrains.**
15 |
16 | -dontwarn java.awt.*
17 | -dontwarn java.nio.*
18 | -dontwarn java.util.*
19 | -dontwarn com.intellij.**
20 | -dontwarn org.jdom.**
21 | -dontwarn com.google.**
22 | -dontwarn org.apache.**
23 |
24 | -keep enum org.jetbrains.nav_cupcake.** { *; }
25 | -keep class com.android.ddmlib.** { *; }
26 | -keep class org.lwjgl.** { *; }
27 | -keep class org.bouncycastle.** { *; }
28 | -keep class com.android.apksig.** { *; }
29 | -keep class org.sqlite.** { *; }
30 | -keep class org.slf4j.** { *; }
31 | #-keep class java.nio.** { *; }
32 | #-keep class java.util.concurrent.** { *; }
33 | -keep class uniffi.toolkit.** { *; }
34 | -keep class androidx.datastore.** { *; }
35 | -keep class com.sun.jna.** { *; }
36 | -keep class * implements com.sun.jna.** { *; }
37 | -keepclassmembers class * extends com.sun.jna.* { public *; }
38 | -keepclassmembers class androidx.datastore.** { *; }
39 | -keep class kotlinx.coroutines.swing.SwingDispatcherFactory
40 | -keep class androidx.datastore.preferences.protobuf.** { *; }
41 | -keepclassmembers class androidx.datastore.preferences.protobuf.** { *; }
42 | -keepattributes Signature,LineNumberTable,RuntimeVisibleAnnotations,AnnotationDefault,*Annotation*,InnerClasses
43 |
44 | # coil3
45 | -keep class okio.** { *; }
46 | -keep class * extends coil3.util.DecoderServiceLoaderTarget { *; }
47 | -keep class * extends coil3.util.FetcherServiceLoaderTarget { *; }
--------------------------------------------------------------------------------
/composeApp/launcher/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/launcher/icon.icns
--------------------------------------------------------------------------------
/composeApp/launcher/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/launcher/icon.ico
--------------------------------------------------------------------------------
/composeApp/launcher/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/launcher/icon.png
--------------------------------------------------------------------------------
/composeApp/libs/bcpkix-jdk18on-1.80.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/libs/bcpkix-jdk18on-1.80.jar
--------------------------------------------------------------------------------
/composeApp/libs/bcprov-jdk18on-1.80.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/libs/bcprov-jdk18on-1.80.jar
--------------------------------------------------------------------------------
/composeApp/libs/bcutil-jdk18on-1.80.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/libs/bcutil-jdk18on-1.80.jar
--------------------------------------------------------------------------------
/composeApp/resources/common/honor_unsigned.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/resources/common/honor_unsigned.apk
--------------------------------------------------------------------------------
/composeApp/resources/common/huawei_unsigned.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/resources/common/huawei_unsigned.apk
--------------------------------------------------------------------------------
/composeApp/resources/common/oppo_unsigned.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/resources/common/oppo_unsigned.apk
--------------------------------------------------------------------------------
/composeApp/resources/common/qq_unsigned.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/resources/common/qq_unsigned.apk
--------------------------------------------------------------------------------
/composeApp/resources/common/vivo_unsigned.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/resources/common/vivo_unsigned.apk
--------------------------------------------------------------------------------
/composeApp/resources/common/xiaomi_unsigned.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/resources/common/xiaomi_unsigned.apk
--------------------------------------------------------------------------------
/composeApp/resources/linux/aapt2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/resources/linux/aapt2
--------------------------------------------------------------------------------
/composeApp/resources/macos-arm64/aapt2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/resources/macos-arm64/aapt2
--------------------------------------------------------------------------------
/composeApp/resources/macos-x64/aapt2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/resources/macos-x64/aapt2
--------------------------------------------------------------------------------
/composeApp/resources/windows/aapt2.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/resources/windows/aapt2.exe
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/src/commonMain/composeResources/drawable/icon.png
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/files/lottie_main_3_dark.json:
--------------------------------------------------------------------------------
1 | {"v":"4.5.9","fr":29.9700012207031,"ip":0,"op":87.0000035435826,"w":350,"h":350,"ddd":0,"assets":[],"layers":[{"ddd":0,"ind":0,"ty":4,"nm":"Earth Outline","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[175,175,0]},"a":{"a":0,"k":[30,30,0]},"s":{"a":0,"k":[235,235,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[11.238,-3.576],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0.491,2.648],[-0.322,0.244],[-0.809,1.455],[0.254,0.981],[-0.086,1.297],[0.058,0.483],[-0.104,1.657],[0.728,0.741],[-5.105,2.693],[-0.016,-0.329],[0,0],[0,0],[-0.578,0.534],[-0.53,0.196],[0.038,-0.214],[1.318,-0.692],[-0.184,-2.341],[0,0],[0,0],[-0.923,0],[-1.77,1.353],[-1.962,0],[-1.56,0.67],[-0.514,0.064],[0.172,-0.579],[0.69,0],[0.224,0.228],[1.738,-1.812],[-0.082,-1.981],[0,0],[0.12,0.054],[0.902,0],[0.163,-0.244],[-0.296,-0.703],[0.728,-1.222],[-0.433,-0.761],[-0.73,0],[-1.555,0.37],[-0.268,0],[-0.336,-0.928],[-0.862,-0.664],[-0.46,0],[-0.188,0.611],[-0.018,0.06],[-0.021,-0.232],[-0.47,0],[-1.023,0.474],[-0.257,0.005],[-0.042,-0.254],[-0.51,-0.775],[-3.249,0],[-1.154,1.791],[-0.17,1.174],[-0.397,0.274],[-1.196,-1.563],[-0.853,0],[0,0],[-0.269,0.306]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.183,-2.332],[0.317,-0.291],[0.807,-0.609],[0.734,-1.321],[-0.156,-0.594],[0.113,-1.697],[-0.049,-0.419],[0.119,-1.901],[2.634,-5.139],[-0.179,0.521],[0,0],[0,0],[0.409,0],[2.67,-2.462],[-0.004,0.12],[-0.193,1.072],[-1.65,0.869],[0,0],[0,0],[0.985,0.385],[2.54,0],[1.597,-1.22],[1.045,0],[0.909,-0.391],[1.124,0.896],[-0.513,1.709],[-0.1,0],[-1.647,-1.646],[-1.305,1.36],[0,0],[-0.123,-0.055],[-1.414,-0.644],[-0.784,0],[-0.475,0.717],[0.695,1.661],[-0.524,0.878],[0.269,0.473],[0.632,0],[0.396,-0.096],[0.546,0],[0.254,0.701],[0.535,0.41],[1.19,0],[0.015,-0.053],[0.083,0.09],[0.106,1.292],[0.458,0],[0.901,-0.411],[0.065,0.163],[0.115,0.676],[1.587,2.4],[1.945,0],[0.277,-0.431],[0.118,-0.819],[0.005,0],[0.48,0.629],[0,0],[0.472,0],[-0.103,12.386]],"v":[[8.474,26.686],[8.91,24.086],[12.306,21.33],[14.25,15.266],[6.25,15.233],[4.691,12.411],[-1.113,8.062],[-4.88,8.209],[-8.29,7.136],[-10.107,5.883],[-9.594,5.712],[-10.536,5.587],[-11.44,4.964],[-12.821,5.284],[-15.189,4.969],[-18.473,6.561],[-18.79,6.666],[-18.772,6.731],[-21.627,8.089],[-24.985,12.614],[-27.523,5.116],[-26.574,4.333],[-24.124,1.639],[-23.757,-1.405],[-24.001,-3.966],[-24.004,-6.647],[-24,-9.157],[-24.931,-12.72],[-13.021,-24.776],[-13.279,-23.558],[-13.234,-22.604],[-12.28,-22.604],[-11.136,-23.282],[-6.733,-26.666],[-6.792,-26.172],[-9.549,-23.751],[-13.067,-19.463],[-13.018,-18.839],[-12.435,-18.61],[-9.559,-18.028],[-3.418,-20.782],[1.648,-23.154],[5.726,-24.458],[8.374,-25.439],[8.867,-22.059],[6.511,-19.073],[6.027,-19.327],[0.197,-19.007],[-1.757,-13.668],[-1.724,-13.2],[-2.087,-13.365],[-5.626,-14.564],[-6.96,-13.911],[-6.856,-11.59],[-7.794,-7.187],[-8.338,-4.787],[-6.833,-4.075],[-3.672,-4.61],[-2.674,-4.753],[-1.711,-3.74],[-0.28,-1.504],[1.18,-0.9],[2.907,-2.652],[2.96,-2.827],[3.074,-2.386],[4.455,-0.899],[6.517,-1.559],[8.12,-2.06],[8.289,-1.279],[9.054,1.013],[17.078,5.995],[21.816,3.257],[22.365,1.019],[23.192,-2.251],[24.901,-0.644],[26.897,0.708],[26.898,0.708],[27.995,0.233]],"c":true}},"nm":"Path 1","mn":"ADBE Vector Shape - Group"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,13.734],[0.671,2.488],[0,0],[0,0],[0.012,0.044],[0.271,0.731],[0.006,0.021],[0.301,0.657],[0.054,0.114],[0.337,0.6],[0.088,0.152],[0.382,0.563],[0.069,0.1],[0.398,0.499],[0.075,0.09],[0.489,0.518],[0.076,0.079],[6.459,1.095],[0.042,0.006],[0.76,0.067],[0.124,0.008],[0.768,0],[0.513,-0.026],[0.007,-0.001],[3.244,-1.383],[0,0],[0,0],[0,-12.178],[-3.848,-5.044],[0,0],[0,0],[-0.147,-0.181],[-0.08,-0.095],[-0.105,-0.121],[0,0],[0,0],[-4.412,-1.778],[0,0],[0,0],[-0.059,-0.022],[-0.183,-0.065],[-0.259,-0.083],[-0.208,-0.06],[-0.246,-0.065],[-0.213,-0.052],[-0.253,-0.056],[-0.211,-0.041],[-0.268,-0.045],[-0.203,-0.028],[-0.287,-0.033],[-0.192,-0.019],[-0.323,-0.02],[-0.16,-0.007],[-0.487,0],[-0.577,0.032],[-0.182,0.013],[-0.395,0.045],[-0.21,0.03],[-0.364,0.063],[-0.215,0.042],[-0.39,0.093],[-0.148,0.038],[0,0],[0,0]],"o":[[0,-2.698],[0,0],[0,0],[-0.013,-0.046],[-0.214,-0.756],[-0.008,-0.021],[-0.255,-0.682],[-0.053,-0.115],[-0.295,-0.625],[-0.086,-0.152],[-0.346,-0.59],[-0.07,-0.101],[-0.366,-0.524],[-0.073,-0.09],[-0.45,-0.552],[-0.075,-0.081],[-4.359,-4.53],[-0.042,-0.008],[-0.744,-0.123],[-0.123,-0.011],[-0.753,-0.057],[-0.516,0],[-0.006,0],[-3.521,0.181],[0,0],[0,0],[-10.449,4.697],[0,6.821],[0,0],[0,0],[0.141,0.183],[0.077,0.097],[0.103,0.124],[0,0],[0,0],[3.086,3.523],[0,0],[0,0],[0.059,0.022],[0.183,0.068],[0.257,0.09],[0.205,0.065],[0.246,0.073],[0.212,0.058],[0.252,0.063],[0.209,0.045],[0.265,0.052],[0.202,0.034],[0.284,0.042],[0.19,0.023],[0.32,0.03],[0.16,0.011],[0.486,0.024],[0.585,0],[0.18,-0.012],[0.399,-0.031],[0.211,-0.025],[0.366,-0.05],[0.215,-0.038],[0.395,-0.079],[0.149,-0.037],[0,0],[0,0],[12.613,-3.546]],"v":[[30,0],[28.965,-7.8],[28.966,-7.808],[28.906,-8.027],[28.864,-8.159],[28.142,-10.392],[28.119,-10.454],[27.279,-12.461],[27.123,-12.805],[26.17,-14.642],[25.908,-15.098],[24.821,-16.831],[24.609,-17.128],[23.463,-18.663],[23.246,-18.938],[21.835,-20.54],[21.608,-20.779],[5.031,-29.569],[4.906,-29.591],[2.651,-29.876],[2.281,-29.903],[0,-30],[-1.541,-29.96],[-1.563,-29.959],[-11.746,-27.61],[-12.25,-27.396],[-12.253,-27.373],[-30,0],[-23.86,18.157],[-23.854,18.184],[-23.717,18.36],[-23.276,18.899],[-23.041,19.187],[-22.734,19.561],[-22.596,19.723],[-22.564,19.738],[-11.172,27.834],[-11.17,27.851],[-10.62,28.06],[-10.442,28.123],[-9.893,28.317],[-9.12,28.579],[-8.5,28.766],[-7.761,28.976],[-7.124,29.138],[-6.366,29.315],[-5.737,29.444],[-4.937,29.587],[-4.331,29.683],[-3.474,29.792],[-2.903,29.858],[-1.938,29.931],[-1.46,29.963],[0,30],[1.741,29.947],[2.282,29.905],[3.474,29.793],[4.107,29.712],[5.202,29.54],[5.848,29.424],[7.022,29.163],[7.472,29.062],[8.103,28.901],[8.108,28.878]],"c":true}},"nm":"Path 2","mn":"ADBE Vector Shape - Group"},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge"},{"ty":"mm","mm":4,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge"},{"ty":"fl","c":{"a":0,"k":[0.6941,0.8196,0.5412,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[30,30],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":5,"mn":"ADBE Vector Group"}],"ip":0,"op":900.000036657751,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":1,"ty":4,"nm":"Plane Outline","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[0],"e":[359]},{"t":87.0000035435826}]},"p":{"a":0,"k":[175,175,0]},"a":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[174.42,246.5,0],"e":[174.42,232.5,0],"to":[0,-2.33333325386047,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":43,"s":[174.42,232.5,0],"e":[174.42,246.5,0],"to":[0,0,0],"ti":[0,-2.33333325386047,0]},{"t":69.0000028104276}]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":16,"s":[150,150,100],"e":[186,186,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":43,"s":[186,186,100],"e":[150,150,100]},{"t":69.0000028104276}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-10.521,3.811],[-0.007,5.58],[5.236,1.909],[11.201,0],[0,0],[0,0],[0,0],[0,0],[0,0],[1.132,0],[0,0],[0.676,-0.952],[0,-0.745],[-0.134,-0.383],[0,0],[0,0],[0,0],[1.458,0],[0,0],[0.673,-1.006],[-0.466,-1.118],[0,0],[0,0],[0,0],[0,0],[0,-0.469],[-0.401,-0.604],[-1.208,0],[0,0],[-0.561,1.346],[0,0],[0,0],[0,0],[0,-0.395],[-0.445,-0.618],[-1.168,0],[0,0],[-0.687,0.905]],"o":[[0,0],[0,0],[0,0],[11.125,0],[5.248,-1.903],[0,-5.588],[-10.514,-3.838],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.687,-0.901],[0,0],[-1.168,0],[-0.445,0.622],[0,0.394],[0,0],[0,0],[0,0],[-0.561,-1.346],[0,0],[-1.208,0],[-0.676,1.009],[0,0],[0,0],[0,0],[0,0],[-0.188,0.448],[0,0.703],[0.673,1.005],[0,0],[1.458,0],[0,0],[0,0],[0,0],[-0.134,0.383],[0,0.741],[0.676,0.956],[0,0],[1.132,0],[0,0]],"v":[[-6.09,90.416],[21.346,54.25],[48.785,18.076],[67.356,18.083],[99.946,12.348],[108.593,0.033],[99.975,-12.3],[67.252,-18.083],[48.778,-18.09],[21.346,-54.25],[10.944,-68.716],[-6.09,-90.417],[-18.724,-107.071],[-21.606,-108.5],[-43.306,-108.5],[-46.25,-106.984],[-46.923,-104.883],[-46.724,-103.704],[-17.212,-18.112],[-68.641,-15.053],[-90.751,-44.792],[-94.09,-47.017],[-104.788,-47.017],[-107.799,-45.407],[-108.128,-42.007],[-94.462,-5.212],[-97.408,0.5],[-94.408,6.5],[-108.128,42.008],[-108.406,43.399],[-107.799,45.407],[-104.788,47.017],[-94.09,47.017],[-90.751,44.792],[-68.641,15.396],[-17.19,18.055],[-46.724,103.704],[-46.923,104.883],[-46.25,106.98],[-43.306,108.5],[-21.606,108.5],[-18.724,107.067]],"c":true}},"nm":"Path 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.2915,0.3948,0.1652,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[174.407,174.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[13.48,13.48],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":900.000036657751,"st":0,"bm":0,"sr":1}]}
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/files/lottie_main_3_light.json:
--------------------------------------------------------------------------------
1 | {"v":"4.5.9","fr":29.9700012207031,"ip":0,"op":87.0000035435826,"w":350,"h":350,"ddd":0,"assets":[],"layers":[{"ddd":0,"ind":0,"ty":4,"nm":"Earth Outline","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[175,175,0]},"a":{"a":0,"k":[30,30,0]},"s":{"a":0,"k":[235,235,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[11.238,-3.576],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0.491,2.648],[-0.322,0.244],[-0.809,1.455],[0.254,0.981],[-0.086,1.297],[0.058,0.483],[-0.104,1.657],[0.728,0.741],[-5.105,2.693],[-0.016,-0.329],[0,0],[0,0],[-0.578,0.534],[-0.53,0.196],[0.038,-0.214],[1.318,-0.692],[-0.184,-2.341],[0,0],[0,0],[-0.923,0],[-1.77,1.353],[-1.962,0],[-1.56,0.67],[-0.514,0.064],[0.172,-0.579],[0.69,0],[0.224,0.228],[1.738,-1.812],[-0.082,-1.981],[0,0],[0.12,0.054],[0.902,0],[0.163,-0.244],[-0.296,-0.703],[0.728,-1.222],[-0.433,-0.761],[-0.73,0],[-1.555,0.37],[-0.268,0],[-0.336,-0.928],[-0.862,-0.664],[-0.46,0],[-0.188,0.611],[-0.018,0.06],[-0.021,-0.232],[-0.47,0],[-1.023,0.474],[-0.257,0.005],[-0.042,-0.254],[-0.51,-0.775],[-3.249,0],[-1.154,1.791],[-0.17,1.174],[-0.397,0.274],[-1.196,-1.563],[-0.853,0],[0,0],[-0.269,0.306]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.183,-2.332],[0.317,-0.291],[0.807,-0.609],[0.734,-1.321],[-0.156,-0.594],[0.113,-1.697],[-0.049,-0.419],[0.119,-1.901],[2.634,-5.139],[-0.179,0.521],[0,0],[0,0],[0.409,0],[2.67,-2.462],[-0.004,0.12],[-0.193,1.072],[-1.65,0.869],[0,0],[0,0],[0.985,0.385],[2.54,0],[1.597,-1.22],[1.045,0],[0.909,-0.391],[1.124,0.896],[-0.513,1.709],[-0.1,0],[-1.647,-1.646],[-1.305,1.36],[0,0],[-0.123,-0.055],[-1.414,-0.644],[-0.784,0],[-0.475,0.717],[0.695,1.661],[-0.524,0.878],[0.269,0.473],[0.632,0],[0.396,-0.096],[0.546,0],[0.254,0.701],[0.535,0.41],[1.19,0],[0.015,-0.053],[0.083,0.09],[0.106,1.292],[0.458,0],[0.901,-0.411],[0.065,0.163],[0.115,0.676],[1.587,2.4],[1.945,0],[0.277,-0.431],[0.118,-0.819],[0.005,0],[0.48,0.629],[0,0],[0.472,0],[-0.103,12.386]],"v":[[8.474,26.686],[8.91,24.086],[12.306,21.33],[14.25,15.266],[6.25,15.233],[4.691,12.411],[-1.113,8.062],[-4.88,8.209],[-8.29,7.136],[-10.107,5.883],[-9.594,5.712],[-10.536,5.587],[-11.44,4.964],[-12.821,5.284],[-15.189,4.969],[-18.473,6.561],[-18.79,6.666],[-18.772,6.731],[-21.627,8.089],[-24.985,12.614],[-27.523,5.116],[-26.574,4.333],[-24.124,1.639],[-23.757,-1.405],[-24.001,-3.966],[-24.004,-6.647],[-24,-9.157],[-24.931,-12.72],[-13.021,-24.776],[-13.279,-23.558],[-13.234,-22.604],[-12.28,-22.604],[-11.136,-23.282],[-6.733,-26.666],[-6.792,-26.172],[-9.549,-23.751],[-13.067,-19.463],[-13.018,-18.839],[-12.435,-18.61],[-9.559,-18.028],[-3.418,-20.782],[1.648,-23.154],[5.726,-24.458],[8.374,-25.439],[8.867,-22.059],[6.511,-19.073],[6.027,-19.327],[0.197,-19.007],[-1.757,-13.668],[-1.724,-13.2],[-2.087,-13.365],[-5.626,-14.564],[-6.96,-13.911],[-6.856,-11.59],[-7.794,-7.187],[-8.338,-4.787],[-6.833,-4.075],[-3.672,-4.61],[-2.674,-4.753],[-1.711,-3.74],[-0.28,-1.504],[1.18,-0.9],[2.907,-2.652],[2.96,-2.827],[3.074,-2.386],[4.455,-0.899],[6.517,-1.559],[8.12,-2.06],[8.289,-1.279],[9.054,1.013],[17.078,5.995],[21.816,3.257],[22.365,1.019],[23.192,-2.251],[24.901,-0.644],[26.897,0.708],[26.898,0.708],[27.995,0.233]],"c":true}},"nm":"Path 1","mn":"ADBE Vector Shape - Group"},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,13.734],[0.671,2.488],[0,0],[0,0],[0.012,0.044],[0.271,0.731],[0.006,0.021],[0.301,0.657],[0.054,0.114],[0.337,0.6],[0.088,0.152],[0.382,0.563],[0.069,0.1],[0.398,0.499],[0.075,0.09],[0.489,0.518],[0.076,0.079],[6.459,1.095],[0.042,0.006],[0.76,0.067],[0.124,0.008],[0.768,0],[0.513,-0.026],[0.007,-0.001],[3.244,-1.383],[0,0],[0,0],[0,-12.178],[-3.848,-5.044],[0,0],[0,0],[-0.147,-0.181],[-0.08,-0.095],[-0.105,-0.121],[0,0],[0,0],[-4.412,-1.778],[0,0],[0,0],[-0.059,-0.022],[-0.183,-0.065],[-0.259,-0.083],[-0.208,-0.06],[-0.246,-0.065],[-0.213,-0.052],[-0.253,-0.056],[-0.211,-0.041],[-0.268,-0.045],[-0.203,-0.028],[-0.287,-0.033],[-0.192,-0.019],[-0.323,-0.02],[-0.16,-0.007],[-0.487,0],[-0.577,0.032],[-0.182,0.013],[-0.395,0.045],[-0.21,0.03],[-0.364,0.063],[-0.215,0.042],[-0.39,0.093],[-0.148,0.038],[0,0],[0,0]],"o":[[0,-2.698],[0,0],[0,0],[-0.013,-0.046],[-0.214,-0.756],[-0.008,-0.021],[-0.255,-0.682],[-0.053,-0.115],[-0.295,-0.625],[-0.086,-0.152],[-0.346,-0.59],[-0.07,-0.101],[-0.366,-0.524],[-0.073,-0.09],[-0.45,-0.552],[-0.075,-0.081],[-4.359,-4.53],[-0.042,-0.008],[-0.744,-0.123],[-0.123,-0.011],[-0.753,-0.057],[-0.516,0],[-0.006,0],[-3.521,0.181],[0,0],[0,0],[-10.449,4.697],[0,6.821],[0,0],[0,0],[0.141,0.183],[0.077,0.097],[0.103,0.124],[0,0],[0,0],[3.086,3.523],[0,0],[0,0],[0.059,0.022],[0.183,0.068],[0.257,0.09],[0.205,0.065],[0.246,0.073],[0.212,0.058],[0.252,0.063],[0.209,0.045],[0.265,0.052],[0.202,0.034],[0.284,0.042],[0.19,0.023],[0.32,0.03],[0.16,0.011],[0.486,0.024],[0.585,0],[0.18,-0.012],[0.399,-0.031],[0.211,-0.025],[0.366,-0.05],[0.215,-0.038],[0.395,-0.079],[0.149,-0.037],[0,0],[0,0],[12.613,-3.546]],"v":[[30,0],[28.965,-7.8],[28.966,-7.808],[28.906,-8.027],[28.864,-8.159],[28.142,-10.392],[28.119,-10.454],[27.279,-12.461],[27.123,-12.805],[26.17,-14.642],[25.908,-15.098],[24.821,-16.831],[24.609,-17.128],[23.463,-18.663],[23.246,-18.938],[21.835,-20.54],[21.608,-20.779],[5.031,-29.569],[4.906,-29.591],[2.651,-29.876],[2.281,-29.903],[0,-30],[-1.541,-29.96],[-1.563,-29.959],[-11.746,-27.61],[-12.25,-27.396],[-12.253,-27.373],[-30,0],[-23.86,18.157],[-23.854,18.184],[-23.717,18.36],[-23.276,18.899],[-23.041,19.187],[-22.734,19.561],[-22.596,19.723],[-22.564,19.738],[-11.172,27.834],[-11.17,27.851],[-10.62,28.06],[-10.442,28.123],[-9.893,28.317],[-9.12,28.579],[-8.5,28.766],[-7.761,28.976],[-7.124,29.138],[-6.366,29.315],[-5.737,29.444],[-4.937,29.587],[-4.331,29.683],[-3.474,29.792],[-2.903,29.858],[-1.938,29.931],[-1.46,29.963],[0,30],[1.741,29.947],[2.282,29.905],[3.474,29.793],[4.107,29.712],[5.202,29.54],[5.848,29.424],[7.022,29.163],[7.472,29.062],[8.103,28.901],[8.108,28.878]],"c":true}},"nm":"Path 2","mn":"ADBE Vector Shape - Group"},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge"},{"ty":"mm","mm":4,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge"},{"ty":"fl","c":{"a":0,"k":[0.3451,0.3843,0.2863,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[30,30],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":5,"mn":"ADBE Vector Group"}],"ip":0,"op":900.000036657751,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":1,"ty":4,"nm":"Plane Outline","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[0],"e":[359]},{"t":87.0000035435826}]},"p":{"a":0,"k":[175,175,0]},"a":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":16,"s":[174.42,246.5,0],"e":[174.42,232.5,0],"to":[0,-2.33333325386047,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":43,"s":[174.42,232.5,0],"e":[174.42,246.5,0],"to":[0,0,0],"ti":[0,-2.33333325386047,0]},{"t":69.0000028104276}]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":16,"s":[150,150,100],"e":[186,186,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":43,"s":[186,186,100],"e":[150,150,100]},{"t":69.0000028104276}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-10.521,3.811],[-0.007,5.58],[5.236,1.909],[11.201,0],[0,0],[0,0],[0,0],[0,0],[0,0],[1.132,0],[0,0],[0.676,-0.952],[0,-0.745],[-0.134,-0.383],[0,0],[0,0],[0,0],[1.458,0],[0,0],[0.673,-1.006],[-0.466,-1.118],[0,0],[0,0],[0,0],[0,0],[0,-0.469],[-0.401,-0.604],[-1.208,0],[0,0],[-0.561,1.346],[0,0],[0,0],[0,0],[0,-0.395],[-0.445,-0.618],[-1.168,0],[0,0],[-0.687,0.905]],"o":[[0,0],[0,0],[0,0],[11.125,0],[5.248,-1.903],[0,-5.588],[-10.514,-3.838],[0,0],[0,0],[0,0],[0,0],[0,0],[-0.687,-0.901],[0,0],[-1.168,0],[-0.445,0.622],[0,0.394],[0,0],[0,0],[0,0],[-0.561,-1.346],[0,0],[-1.208,0],[-0.676,1.009],[0,0],[0,0],[0,0],[0,0],[-0.188,0.448],[0,0.703],[0.673,1.005],[0,0],[1.458,0],[0,0],[0,0],[0,0],[-0.134,0.383],[0,0.741],[0.676,0.956],[0,0],[1.132,0],[0,0]],"v":[[-6.09,90.416],[21.346,54.25],[48.785,18.076],[67.356,18.083],[99.946,12.348],[108.593,0.033],[99.975,-12.3],[67.252,-18.083],[48.778,-18.09],[21.346,-54.25],[10.944,-68.716],[-6.09,-90.417],[-18.724,-107.071],[-21.606,-108.5],[-43.306,-108.5],[-46.25,-106.984],[-46.923,-104.883],[-46.724,-103.704],[-17.212,-18.112],[-68.641,-15.053],[-90.751,-44.792],[-94.09,-47.017],[-104.788,-47.017],[-107.799,-45.407],[-108.128,-42.007],[-94.462,-5.212],[-97.408,0.5],[-94.408,6.5],[-108.128,42.008],[-108.406,43.399],[-107.799,45.407],[-104.788,47.017],[-94.09,47.017],[-90.751,44.792],[-68.641,15.396],[-17.19,18.055],[-46.724,103.704],[-46.923,104.883],[-46.25,106.98],[-43.306,108.5],[-21.606,108.5],[-18.724,107.067]],"c":true}},"nm":"Path 1","mn":"ADBE Vector Shape - Group"},{"ty":"fl","c":{"a":0,"k":[0.298,0.4,0.1686,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[174.407,174.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[13.48,13.48],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"mn":"ADBE Vector Group"}],"ip":0,"op":900.000036657751,"st":0,"bm":0,"sr":1}]}
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/font/ZCOOLKuaiLe-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/composeApp/src/commonMain/composeResources/font/ZCOOLKuaiLe-Regular.ttf
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 签名信息
3 | APK信息
4 | APK签名
5 | 签名生成
6 | 图标生成
7 | 垃圾代码
8 | 缓存清理
9 | 设置
10 |
11 | 签名信息
12 | APK信息
13 | APK签名
14 | 签名生成
15 | 图标生成
16 | 垃圾代码
17 | 缓存清理
18 | 设置
19 |
20 | %1$s,%2$s
21 |
22 | 愣着干嘛,还不松手
23 | 上传APK
24 |
25 | 应用名称:
26 | 版本:
27 | 版本号:
28 | 包名:
29 | 编译SDK版本:
30 | 最小SDK版本:
31 | 目标SDK版本:
32 | ABIs:
33 | 文件MD5:
34 | 大小:
35 | 应用权限列表:
36 |
37 | 输出路径
38 | 密钥文件路径
39 | 密钥库密码
40 | 密钥别名
41 | 密钥密码
42 | APK文件
43 | 签名策略:
44 | 使用 V2 Only 签名的APK包仅支持Android 7及更高版本的系统安装和使用,请注意
45 | 开始签名
46 | APK Signature Scheme V1
47 | APK Signature Scheme V2,包含V1
48 | APK Signature Scheme V2,不包含V1
49 | APK Signature Scheme V3,包含V1和V2
50 |
51 | 请检查Error项
52 | 请检查空项
53 |
54 | 选择文件夹
55 | 总存储空间
56 | 已使用空间
57 | 可用空间
58 | 已扫描文件夹
59 | 已选择文件夹
60 | 个文件夹
61 | 占总存储空间
62 | 快速清理缓存目录
63 | 快速、深度扫描 Android 项目构建缓存目录\n批量删除构建缓存,释放磁盘空间\n选择 Android 项目或更上层目录
64 | yyyy年MM月dd日
65 | 请先选择需要删除的目录
66 | 日期(从新到旧)
67 | 日期(从旧到新)
68 | 大小(从大到小)
69 | 大小(从小到大)
70 | 名称(从 A 到 Z)
71 | 名称(从 Z 到 A)
72 | 确认删除缓存目录?
73 | 此操作将永久清除该目录下的所有文件,删除后数据将无法恢复,且可能导致下次构建时间延长。请确保您已备份所有重要数据。
74 | 确认删除
75 | 取消
76 |
77 | 一键生成应用图标
78 | 支持png、jpg、jpeg文件\n上传 1024 x 1024 像素的图片以获得最佳效果
79 | 更多设置
80 | 图标输出路径
81 | 图标名称
82 | 收起
83 | 展开
84 | 压缩自定义
85 | 无损压缩
86 | 有损压缩
87 | 压缩速度
88 | 快速
89 | 疯狂
90 | 外部目录名称
91 | Android目录
92 | PNG质量
93 | JPEG质量
94 | 最低限度:%1$d
95 | 目标:%1$d
96 | PNG 缩放算法
97 | JPEG 缩放算法
98 | 上传图片
99 | 开始制作
100 |
101 | AAR输出路径
102 | AAR 名称
103 | 包的数量
104 | 每个包里 activity 的数量
105 | 资源前缀
106 | 包名
107 | 后缀
108 | 开始生成
109 |
110 | APK签名
111 | 签名后缀
112 | 签名后缀: Apk签名后输出名称(比如:输入Apk名称为apk_unsign.apk,则输入Apk名称为apk_unsign%1$s.apk
113 | 是否删除重名输出文件
114 | 注意:输出文件重名后无法成功签名,会提示输出文件已存在
115 | 启用文件对齐
116 | 注意:目标 R+(版本 30 及更高版本)要求已安装 APK 内的文件未压缩存储并在 4 字节边界上对齐
117 |
118 | 签名生成
119 | 目标密钥类型
120 | 注意:JKS 密钥库使用专用格式。建议使用行业标准格式 PKCS12。
121 | 目标密钥大小
122 | 注意:生成的证书 使用的 1024 位 RSA 密钥 被视为存在安全风险。此密钥大小将在未来的更新中被禁用。
123 |
124 | 常规
125 | 默认输出路径
126 | 外观
127 | 跟随系统
128 | 亮色模式
129 | 暗色模式
130 |
131 | ToolKit拓展
132 | 启用拓展选项
133 | 启用垃圾代码生成选项
134 |
135 | 关于
136 | 应用名称
137 | 应用版本
138 | 应用描述
139 | 应用版权
140 | 应用作者
141 | 开源协议
142 | 源代码
143 | 作者
144 | 许可证
145 | 开源许可证
146 |
147 | 密钥输出路径
148 | 密钥文件名称
149 | 密钥
150 | 有效期(单位:年)
151 | 证书
152 | 作者名称
153 | 组织单位
154 | 组织
155 | 城市或地区
156 | 省份
157 | 国家编码
158 | 别名密码
159 | 创建密钥库
160 | 确认密码
161 |
162 | 上传(APK/签名)文件
163 | 复制MD5、SHA1和SHA-256
164 | 快捷复制
165 | 密钥库密码验证
166 | 密钥库密码错误
167 | 确认
168 |
169 | 已开启ToolKit扩展模式
170 | 输出文件已存在:%1$s
171 | APK签名成功,点击跳转至已签名文件
172 | 跳转
173 | 签名失败,请联系开发者排查问题
174 | 执行命令出现错误
175 | APK解析失败
176 | 创建签名成功,点击跳转至签名文件
177 | 签名制作失败,请检查输入项是否合法。
178 | 签名验证失败
179 | APK签名验证失败
180 | 构建结束:成功,文件大小:%1$s,点击跳转至构建文件
181 | 构建失败
182 | 图标制作失败
183 | 图标生成完成。点击跳转至输出目录
184 | 未扫描到缓存目录
185 | 清理完成,已为您清理%1$s
186 | %1$d个文件删除异常
187 | 已复制到剪切板
188 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Signature
3 | APK
4 | Sign
5 | KeyStore
6 | Icon
7 | Garbage
8 | Cache
9 | Setting
10 |
11 | Signature Information
12 | APK Information
13 | APK Signature
14 | Signature Generation
15 | Icon Generation
16 | Garbage Code
17 | Cache Cleanup
18 | Setting
19 |
20 | %1$s, %2$s
21 |
22 | LetGo
23 | Upload APK
24 |
25 | App Name:
26 | Version:
27 | Version Code:
28 | Package Name:
29 | Compile SDK:
30 | Minimum SDK:
31 | Target SDK:
32 | ABIs:
33 | File MD5:
34 | Size:
35 | Permissions:
36 |
37 | Output Path
38 | Key Store File Path
39 | Key Store Password
40 | Key Alias
41 | Key Password
42 | APK File
43 | Signature Strategy:
44 | The APK package signed with V2 Only supports Android 7 and higher system installation and use. Please note
45 | Start Signing
46 | APK Signature Scheme V1
47 | APK Signature Scheme V2, including V1
48 | APK Signature Scheme V2, not including V1
49 | APK Signature Scheme V3, including V1 and V2
50 |
51 | Please check the Error item
52 | Please check for empty items
53 |
54 | SELECT A FOLDER
55 | Total Storage Space
56 | Used Space
57 | Available Space
58 | Scanned Folders
59 | Selected Folders
60 | Folders
61 | Percentage of total storage space
62 | Quickly clear cache directories
63 | Quickly and thoroughly scan the build‑cache directories of an Android project.\nBatch‑delete build caches to free up disk space.\nSelect an Android project or a higher‑level directory.
64 | MMM dd, yyyy
65 | Please first select the directory to delete
66 | Newest date first
67 | Oldest date first
68 | Largest first
69 | Smallest first
70 | Name A -> Z
71 | Name Z -> A
72 | Delete selected folders?
73 | This operation will permanently delete all files in the selected directory. Once removed, the data cannot be recovered and subsequent build times may be longer. Please ensure you have backed up all important data.
74 | Confirm deletion
75 | Cancel
76 |
77 | One‑Click App Icon Generation
78 | Supports PNG, JPG, and JPEG files. \nUpload a 1024 × 1024 pixel image for best results.
79 | More Settings
80 | Icon Output Path
81 | Icon Name
82 | Close
83 | Expand
84 | Compress Custom
85 | Lossless Compression
86 | Lossy Compression
87 | Compression Speed
88 | Fast
89 | Crazy
90 | External Directory Name
91 | Android Directory
92 | PNG Quality
93 | JPEG Quality
94 | Minimum: %1$d
95 | Target: %1$d
96 | PNG scaling Algorithm
97 | JPEG scaling Algorithm
98 | Upload Image
99 | Start Making
100 |
101 | AAR Output Path
102 | AAR Name
103 | Number of packages
104 | The Number Of Activities In Each Package
105 | Resource Prefix
106 | Package Name
107 | Suffix
108 | Start Generating
109 |
110 | APK Signature
111 | Signature Suffix
112 | Signature Suffix: The output name of the Apk after signing (for example, if the input Apk name is apk_unsign.apk, then the input Apk name is apk_unsign%1$s.apk
113 | Whether to delete output files with duplicate names
114 | Note: If the output file has the same name, it cannot be signed successfully, and the system will prompt that the output file already exists.
115 | Enable File Alignment
116 | Note: Target R+ (version 30 and higher) requires that files within the installed APK be stored uncompressed and aligned on 4-byte boundaries.
117 |
118 | Signature Generation
119 | Target Key Type
120 | Note: JKS keystores use a proprietary format. The industry standard format PKCS12 is recommended.
121 | Target Key Size
122 | Note: The generated certificates use 1024-bit RSA keys which are considered a security risk. This key size will be disabled in a future update.
123 |
124 | Conventional
125 | Default Output Path
126 | Appearance
127 | Auto
128 | Light
129 | Dark
130 |
131 | ToolKit Expand
132 | Enable Extended Options
133 | Enable garbage code generation option
134 |
135 | About
136 | Application Name
137 | Application Version
138 | Application Description
139 | Application Copyright
140 | App Author
141 | Open Source Agreement
142 | Source Code
143 | Author
144 | License
145 | Open Source Licenses
146 |
147 | Key Output Path
148 | Key File Name
149 | Key
150 | Validity (years)
151 | 证书
152 | First and Last Name
153 | Organizational Unit
154 | Organization
155 | City or Locality
156 | State or Province
157 | Country Code (XX)
158 | Alias Password
159 | Create Key Store
160 | Confirm Password
161 |
162 | Upload APK/Signature File
163 | Copy MD5, SHA1, and SHA-256
164 | Quick Copy
165 | Password Verification
166 | Wrong key store password
167 | Confirm
168 |
169 | ToolKit extension mode is enabled
170 | Output file already exists: %1$s
171 | APK is signed successfully, click to jump to the signed file
172 | Jump
173 | Signing failed, please contact the developer to troubleshoot the problem
174 | An error occurred while executing the command
175 | APK parsing failed
176 | The signature is created successfully, click to jump to the signature file
177 | Signature creation failed, please check whether the input items are legal.
178 | Signature verification failed
179 | APK signature verification failed
180 | Build completed: Success, file size: %1$s, click to jump to the build file
181 | Build failure
182 | Icon creation failed
183 | The icon generation is complete. Click to jump to the output directory
184 | The cache directory was not scanned.
185 | Cleaning completed, %1$s has been cleaned for you
186 | %1$d files deleted abnormally
187 | Copied to clipboard
188 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/App.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.animation.AnimatedVisibility
2 | import androidx.compose.animation.Crossfade
3 | import androidx.compose.animation.expandVertically
4 | import androidx.compose.animation.fadeIn
5 | import androidx.compose.animation.fadeOut
6 | import androidx.compose.animation.shrinkVertically
7 | import androidx.compose.foundation.isSystemInDarkTheme
8 | import androidx.compose.foundation.layout.Arrangement
9 | import androidx.compose.foundation.layout.Column
10 | import androidx.compose.foundation.layout.Row
11 | import androidx.compose.foundation.layout.fillMaxHeight
12 | import androidx.compose.foundation.layout.fillMaxSize
13 | import androidx.compose.foundation.layout.padding
14 | import androidx.compose.material.icons.Icons
15 | import androidx.compose.material.icons.automirrored.outlined.Article
16 | import androidx.compose.material.icons.automirrored.rounded.Article
17 | import androidx.compose.material.icons.outlined.Assessment
18 | import androidx.compose.material.icons.outlined.Cookie
19 | import androidx.compose.material.icons.outlined.DesignServices
20 | import androidx.compose.material.icons.outlined.FolderDelete
21 | import androidx.compose.material.icons.outlined.Settings
22 | import androidx.compose.material.icons.outlined.Verified
23 | import androidx.compose.material.icons.outlined.VpnKey
24 | import androidx.compose.material.icons.rounded.Assessment
25 | import androidx.compose.material.icons.rounded.Cookie
26 | import androidx.compose.material.icons.rounded.DesignServices
27 | import androidx.compose.material.icons.rounded.FolderDelete
28 | import androidx.compose.material.icons.rounded.Settings
29 | import androidx.compose.material.icons.rounded.Verified
30 | import androidx.compose.material.icons.rounded.VpnKey
31 | import androidx.compose.material3.ExperimentalMaterial3Api
32 | import androidx.compose.material3.Icon
33 | import androidx.compose.material3.MaterialTheme
34 | import androidx.compose.material3.NavigationRail
35 | import androidx.compose.material3.NavigationRailItem
36 | import androidx.compose.material3.PlainTooltip
37 | import androidx.compose.material3.Scaffold
38 | import androidx.compose.material3.SnackbarHost
39 | import androidx.compose.material3.SnackbarHostState
40 | import androidx.compose.material3.SnackbarResult
41 | import androidx.compose.material3.Text
42 | import androidx.compose.material3.TooltipBox
43 | import androidx.compose.material3.rememberTooltipState
44 | import androidx.compose.runtime.Composable
45 | import androidx.compose.runtime.LaunchedEffect
46 | import androidx.compose.runtime.collectAsState
47 | import androidx.compose.runtime.getValue
48 | import androidx.compose.runtime.remember
49 | import androidx.compose.runtime.rememberCoroutineScope
50 | import androidx.compose.ui.Alignment
51 | import androidx.compose.ui.Modifier
52 | import androidx.compose.ui.graphics.vector.ImageVector
53 | import androidx.compose.ui.platform.LocalDensity
54 | import androidx.compose.ui.unit.IntOffset
55 | import androidx.compose.ui.unit.IntRect
56 | import androidx.compose.ui.unit.IntSize
57 | import androidx.compose.ui.unit.LayoutDirection
58 | import androidx.compose.ui.unit.dp
59 | import androidx.compose.ui.window.PopupPositionProvider
60 | import androidx.lifecycle.viewmodel.compose.viewModel
61 | import com.russhwolf.settings.ExperimentalSettingsApi
62 | import kotlinx.coroutines.Dispatchers
63 | import kotlinx.coroutines.flow.drop
64 | import kotlinx.coroutines.flow.first
65 | import kotlinx.coroutines.launch
66 | import model.DarkThemeConfig
67 | import org.jetbrains.compose.resources.StringResource
68 | import org.jetbrains.compose.resources.stringResource
69 | import org.tool.kit.composeapp.generated.resources.Res
70 | import org.tool.kit.composeapp.generated.resources.apk_information_rail
71 | import org.tool.kit.composeapp.generated.resources.apk_information_tooltip
72 | import org.tool.kit.composeapp.generated.resources.apk_signature_rail
73 | import org.tool.kit.composeapp.generated.resources.apk_signature_tooltip
74 | import org.tool.kit.composeapp.generated.resources.cache_cleanup_rail
75 | import org.tool.kit.composeapp.generated.resources.cache_cleanup_tooltip
76 | import org.tool.kit.composeapp.generated.resources.garbage_code_rail
77 | import org.tool.kit.composeapp.generated.resources.garbage_code_tooltip
78 | import org.tool.kit.composeapp.generated.resources.icon_generation_rail
79 | import org.tool.kit.composeapp.generated.resources.icon_generation_tooltip
80 | import org.tool.kit.composeapp.generated.resources.setting_rail
81 | import org.tool.kit.composeapp.generated.resources.setting_tooltip
82 | import org.tool.kit.composeapp.generated.resources.signature_generation_rail
83 | import org.tool.kit.composeapp.generated.resources.signature_generation_tooltip
84 | import org.tool.kit.composeapp.generated.resources.signature_information_rail
85 | import org.tool.kit.composeapp.generated.resources.signature_information_tooltip
86 | import platform.createFlowSettings
87 | import theme.AppTheme
88 | import ui.ApkInformation
89 | import ui.ApkSignature
90 | import ui.ClearBuild
91 | import ui.ClearBuildBottom
92 | import ui.IconFactory
93 | import ui.JunkCode
94 | import ui.LoadingAnimate
95 | import ui.SetUp
96 | import ui.SignatureGeneration
97 | import ui.SignatureInformation
98 | import vm.MainViewModel
99 | import vm.UIState
100 |
101 | @OptIn(ExperimentalSettingsApi::class)
102 | @Composable
103 | fun App() {
104 | val viewModel = viewModel { MainViewModel(settings = createFlowSettings()) }
105 | val themeConfig by viewModel.themeConfig.collectAsState()
106 | val useDarkTheme = when (themeConfig) {
107 | DarkThemeConfig.LIGHT -> false
108 | DarkThemeConfig.DARK -> true
109 | DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme()
110 | }
111 | AppTheme(useDarkTheme) {
112 | MainContentScreen(viewModel)
113 | }
114 | }
115 |
116 | /**
117 | * 主要模块
118 | */
119 | @OptIn(ExperimentalMaterial3Api::class)
120 | @Composable
121 | fun MainContentScreen(viewModel: MainViewModel) {
122 | val scope = rememberCoroutineScope()
123 | val snackbarHostState = remember { SnackbarHostState() }
124 | scope.launch(Dispatchers.IO) {
125 | collectOutputPath(viewModel)
126 | }
127 | Scaffold(bottomBar = {
128 | AnimatedVisibility(
129 | visible = viewModel.uiPageIndex == Page.CLEAR_BUILD && viewModel.fileClearUIState == UIState.WAIT && viewModel.pendingDeletionFileList.isNotEmpty(),
130 | enter = fadeIn() + expandVertically(),
131 | exit = shrinkVertically() + fadeOut()
132 | ) {
133 | ClearBuildBottom(viewModel)
134 | }
135 | }, snackbarHost = {
136 | SnackbarHost(hostState = snackbarHostState)
137 | }) { innerPadding ->
138 | Row(
139 | modifier = Modifier.fillMaxSize().padding(innerPadding), verticalAlignment = Alignment.CenterVertically
140 | ) {
141 | val junkCode by viewModel.junkCode.collectAsState()
142 | val pages = if (junkCode) {
143 | Page.entries.toMutableList()
144 | } else {
145 | Page.entries.toMutableList().also { list -> list.remove(Page.JUNK_CODE) }
146 | }
147 | // 导航栏
148 | AnimatedVisibility(viewModel.pendingDeletionFileList.isEmpty()) {
149 | NavigationRail(Modifier.fillMaxHeight()) {
150 | Column(
151 | modifier = Modifier.fillMaxHeight(),
152 | verticalArrangement = Arrangement.Center,
153 | horizontalAlignment = Alignment.CenterHorizontally
154 | ) {
155 | pages.forEachIndexed { _, page ->
156 | TooltipBox(
157 | positionProvider = rememberRichTooltipPositionProvider(), tooltip = {
158 | PlainTooltip {
159 | Text(
160 | stringResource(page.tooltip), style = MaterialTheme.typography.bodySmall
161 | )
162 | }
163 | }, state = rememberTooltipState(), enableUserInput = viewModel.uiPageIndex != page
164 | ) {
165 | val icon = if (viewModel.uiPageIndex == page) page.selectedIcon else page.unSelectedIcon
166 | NavigationRailItem(
167 | label = { Text(stringResource(page.title)) },
168 | icon = { Icon(icon, contentDescription = stringResource(page.title)) },
169 | selected = viewModel.uiPageIndex == page,
170 | onClick = { viewModel.updateUiState(page) },
171 | alwaysShowLabel = false,
172 | )
173 | }
174 | }
175 | }
176 | }
177 | }
178 | // 主界面
179 | val content: @Composable (Page) -> Unit = { page ->
180 | when (page) {
181 | Page.SIGNATURE_INFORMATION -> SignatureInformation(viewModel)
182 | Page.APK_INFORMATION -> ApkInformation(viewModel)
183 | Page.APK_SIGNATURE -> ApkSignature(viewModel)
184 | Page.SIGNATURE_GENERATION -> SignatureGeneration(viewModel)
185 | Page.JUNK_CODE -> JunkCode(viewModel)
186 | Page.ICON_FACTORY -> IconFactory(viewModel)
187 | Page.CLEAR_BUILD -> ClearBuild(viewModel)
188 | Page.SET_UP -> SetUp(viewModel)
189 | }
190 | }
191 | // 淡入淡出切换页面
192 | Crossfade(targetState = viewModel.uiPageIndex, modifier = Modifier.fillMaxSize(), content = content)
193 | }
194 | }
195 | val snackbarVisuals by viewModel.snackbarVisuals.collectAsState()
196 | LaunchedEffect(snackbarVisuals) {
197 | if (snackbarVisuals.message.isBlank()) return@LaunchedEffect
198 | val snackbarResult = snackbarHostState.showSnackbar(snackbarVisuals)
199 | when (snackbarResult) {
200 | SnackbarResult.ActionPerformed -> snackbarVisuals.action?.invoke()
201 | SnackbarResult.Dismissed -> Unit
202 | }
203 | }
204 | LoadingAnimate(isShowLoading(viewModel), viewModel, scope)
205 | }
206 |
207 | private fun isShowLoading(viewModel: MainViewModel) =
208 | viewModel.junkCodeUIState == UIState.Loading || viewModel.iconFactoryUIState == UIState.Loading
209 | || viewModel.apkSignatureUIState == UIState.Loading || viewModel.apkInformationState == UIState.Loading
210 | || viewModel.keyStoreInfoUIState == UIState.Loading || viewModel.verifierState == UIState.Loading
211 | || (viewModel.fileClearUIState == UIState.Loading && viewModel.isClearing)
212 |
213 | suspend fun collectOutputPath(viewModel: MainViewModel) {
214 | val userData = viewModel.userData.drop(0).first()
215 | val outputPath = userData.defaultOutputPath
216 | viewModel.apply {
217 | updateApkSignature(viewModel.apkSignatureState.copy(outputPath = outputPath))
218 | updateSignatureGenerate(viewModel.keyStoreInfoState.copy(keyStorePath = outputPath))
219 | updateJunkCodeInfo(viewModel.junkCodeInfoState.copy(outputPath = outputPath))
220 | updateIconFactoryInfo(viewModel.iconFactoryInfoState.copy(outputPath = outputPath))
221 | }
222 | }
223 |
224 | enum class Page(
225 | val title: StringResource,
226 | val tooltip: StringResource,
227 | val selectedIcon: ImageVector,
228 | val unSelectedIcon: ImageVector
229 | ) {
230 | SIGNATURE_INFORMATION(
231 | Res.string.signature_information_rail,
232 | Res.string.signature_information_tooltip,
233 | Icons.AutoMirrored.Rounded.Article,
234 | Icons.AutoMirrored.Outlined.Article
235 | ),
236 | APK_INFORMATION(
237 | Res.string.apk_information_rail,
238 | Res.string.apk_information_tooltip,
239 | Icons.Rounded.Assessment,
240 | Icons.Outlined.Assessment
241 | ),
242 | APK_SIGNATURE(
243 | Res.string.apk_signature_rail,
244 | Res.string.apk_signature_tooltip,
245 | Icons.Rounded.VpnKey,
246 | Icons.Outlined.VpnKey
247 | ),
248 | SIGNATURE_GENERATION(
249 | Res.string.signature_generation_rail,
250 | Res.string.signature_generation_tooltip,
251 | Icons.Rounded.Verified,
252 | Icons.Outlined.Verified
253 | ),
254 | JUNK_CODE(
255 | Res.string.garbage_code_rail,
256 | Res.string.garbage_code_tooltip,
257 | Icons.Rounded.Cookie,
258 | Icons.Outlined.Cookie
259 | ),
260 | ICON_FACTORY(
261 | Res.string.icon_generation_rail,
262 | Res.string.icon_generation_tooltip,
263 | Icons.Rounded.DesignServices,
264 | Icons.Outlined.DesignServices
265 | ),
266 | CLEAR_BUILD(
267 | Res.string.cache_cleanup_rail,
268 | Res.string.cache_cleanup_tooltip,
269 | Icons.Rounded.FolderDelete,
270 | Icons.Outlined.FolderDelete
271 | ),
272 | SET_UP(
273 | Res.string.setting_rail,
274 | Res.string.setting_tooltip,
275 | Icons.Rounded.Settings,
276 | Icons.Outlined.Settings
277 | )
278 | }
279 |
280 | @Composable
281 | private fun rememberRichTooltipPositionProvider(): PopupPositionProvider {
282 | val tooltipAnchorSpacing = with(LocalDensity.current) { 4.dp.roundToPx() }
283 | return remember(tooltipAnchorSpacing) {
284 | object : PopupPositionProvider {
285 | override fun calculatePosition(
286 | anchorBounds: IntRect, windowSize: IntSize, layoutDirection: LayoutDirection, popupContentSize: IntSize
287 | ): IntOffset {
288 | var x = anchorBounds.right
289 | if (x + popupContentSize.width > windowSize.width) {
290 | x = anchorBounds.left - popupContentSize.width
291 | if (x < 0) x = anchorBounds.left + (anchorBounds.width - popupContentSize.width) / 2
292 | }
293 | x -= tooltipAnchorSpacing
294 | val y = anchorBounds.top + (anchorBounds.height - popupContentSize.height) / 2
295 | return IntOffset(x, y)
296 | }
297 | }
298 | }
299 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/constant/ConfigConstant.kt:
--------------------------------------------------------------------------------
1 | package constant
2 |
3 | import model.JpegAlgorithm
4 | import model.PngAlgorithm
5 | import model.UnsignedApk
6 | import utils.resourcesDirWithCommon
7 | import java.io.File
8 |
9 |
10 | object ConfigConstant {
11 |
12 | /**
13 | * 未签名APK列表
14 | */
15 | val unsignedApkList = listOf(
16 | UnsignedApk("oppo", File(resourcesDirWithCommon, "oppo_unsigned.apk").absolutePath),
17 | UnsignedApk("vivo", File(resourcesDirWithCommon, "vivo_unsigned.apk").absolutePath),
18 | UnsignedApk("huawei", File(resourcesDirWithCommon, "huawei_unsigned.apk").absolutePath),
19 | UnsignedApk("xiaomi", File(resourcesDirWithCommon, "xiaomi_unsigned.apk").absolutePath),
20 | UnsignedApk("qq", File(resourcesDirWithCommon, "qq_unsigned.apk").absolutePath),
21 | UnsignedApk("honor", File(resourcesDirWithCommon, "honor_unsigned.apk").absolutePath)
22 | )
23 |
24 | /**
25 | * Android 图标目录
26 | */
27 | val ANDROID_ICON_DIR_LIST = listOf("mipmap", "drawable")
28 |
29 | /**
30 | * ICON 对应 文件/尺寸
31 | */
32 | val ICON_FILE_LIST = listOf("mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi")
33 | val ICON_SIZE_LIST = listOf(48.toUInt(), 72.toUInt(), 96.toUInt(), 144.toUInt(), 192.toUInt())
34 |
35 | /**
36 | * ICON PNG 缩放算法
37 | */
38 | val ICON_PNG_ALGORITHM = listOf(
39 | PngAlgorithm.Triangle,
40 | PngAlgorithm.Catrom,
41 | PngAlgorithm.Mitchell,
42 | PngAlgorithm.Lanczos3,
43 | )
44 |
45 | /**
46 | * ICON JPEG 缩放算法
47 | */
48 | val ICON_JPEG_ALGORITHM = listOf(
49 | JpegAlgorithm.Bilinear,
50 | JpegAlgorithm.Hamming,
51 | JpegAlgorithm.CatmullRom,
52 | JpegAlgorithm.Mitchell,
53 | JpegAlgorithm.Gaussian,
54 | JpegAlgorithm.Lanczos3,
55 | )
56 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/database/PreferencesDataSource.kt:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import com.russhwolf.settings.ExperimentalSettingsApi
4 | import com.russhwolf.settings.coroutines.FlowSettings
5 | import com.russhwolf.settings.coroutines.toBlockingSettings
6 | import com.russhwolf.settings.serialization.decodeValue
7 | import com.russhwolf.settings.serialization.encodeValue
8 | import kotlinx.coroutines.flow.MutableStateFlow
9 | import kotlinx.coroutines.flow.asStateFlow
10 | import kotlinx.coroutines.flow.map
11 | import kotlinx.serialization.ExperimentalSerializationApi
12 | import model.DarkThemeConfig
13 | import model.DestStoreSize
14 | import model.DestStoreType
15 | import model.IconFactoryData
16 | import model.JpegAlgorithm
17 | import model.PngAlgorithm
18 | import model.UserData
19 | import utils.getDownloadDirectory
20 |
21 | /**
22 | * @Author : LazyIonEs
23 | * @CreateDate : 2024/2/20 19:36
24 | * @Description : 用户偏好设置
25 | * @Version : 1.0
26 | */
27 | @OptIn(ExperimentalSerializationApi::class, ExperimentalSettingsApi::class)
28 | class PreferencesDataSource @OptIn(ExperimentalSettingsApi::class) constructor(private val settings: FlowSettings) {
29 |
30 | private val blockingSettings = settings.toBlockingSettings()
31 |
32 | companion object {
33 | private const val THEME_CONFIG = "theme_config"
34 | val DEFAULT_THEME_CONFIG = DarkThemeConfig.FOLLOW_SYSTEM
35 | private const val USER_DATA = "user_data"
36 | val DEFAULT_USER_DATA = UserData(
37 | defaultOutputPath = getDownloadDirectory(),
38 | duplicateFileRemoval = true,
39 | defaultSignerSuffix = "_sign",
40 | alignFileSize = true,
41 | destStoreType = DestStoreType.JKS,
42 | destStoreSize = DestStoreSize.TWO_THOUSAND_FORTY_EIGHT
43 | )
44 | private const val ICON_FACTORY_DATA = "icon_factory_data"
45 | val DEFAULT_ICON_FACTORY_DATA = IconFactoryData(
46 | pngTypIdx = PngAlgorithm.Lanczos3,
47 | jpegTypIdx = JpegAlgorithm.Lanczos3,
48 | lossless = true,
49 | minimum = 70,
50 | target = 100,
51 | speed = 1,
52 | preset = 6,
53 | percentage = 1f,
54 | quality = 85f
55 | )
56 | private const val JUNK_CODE = "junk_code"
57 | private const val DEVELOPER_MODE = "developer_mode"
58 | }
59 |
60 | private val _userData = MutableStateFlow(DEFAULT_USER_DATA)
61 | val userData = _userData.asStateFlow()
62 |
63 | private val _iconFactoryData = MutableStateFlow(DEFAULT_ICON_FACTORY_DATA)
64 | val iconFactoryData = _iconFactoryData.asStateFlow()
65 |
66 | val themeConfig = settings.getStringOrNullFlow(THEME_CONFIG).map { string ->
67 | string?.let {
68 | when (it) {
69 | DarkThemeConfig.FOLLOW_SYSTEM.name -> DarkThemeConfig.FOLLOW_SYSTEM
70 | DarkThemeConfig.DARK.name -> DarkThemeConfig.DARK
71 | else -> DarkThemeConfig.LIGHT
72 | }
73 | } ?: let {
74 | DEFAULT_THEME_CONFIG
75 | }
76 | }
77 |
78 | init {
79 | _userData.value = blockingSettings.decodeValue(UserData.serializer(), USER_DATA, DEFAULT_USER_DATA)
80 | _iconFactoryData.value =
81 | blockingSettings.decodeValue(IconFactoryData.serializer(), ICON_FACTORY_DATA, DEFAULT_ICON_FACTORY_DATA)
82 | }
83 |
84 | suspend fun saveThemeConfig(themeConfig: DarkThemeConfig) {
85 | settings.putString(THEME_CONFIG, themeConfig.name)
86 | }
87 |
88 | fun saveUserData(userData: UserData) {
89 | blockingSettings.encodeValue(UserData.serializer(), USER_DATA, userData)
90 | _userData.value = userData
91 | }
92 |
93 | fun saveIconFactoryData(iconFactoryData: IconFactoryData) {
94 | blockingSettings.encodeValue(IconFactoryData.serializer(), ICON_FACTORY_DATA, iconFactoryData)
95 | _iconFactoryData.value = iconFactoryData
96 | }
97 |
98 | val junkCode = settings.getBooleanOrNullFlow(JUNK_CODE).map { mode -> mode == true }
99 |
100 | suspend fun saveJunkCode(show: Boolean) {
101 | settings.putBoolean(JUNK_CODE, show)
102 | }
103 |
104 | val developerMode = settings.getBooleanOrNullFlow(DEVELOPER_MODE).map { mode -> mode == true }
105 |
106 | suspend fun saveDeveloperMode(show: Boolean) {
107 | settings.putBoolean(DEVELOPER_MODE, show)
108 | }
109 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/model/FileSelectorType.kt:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | /**
4 | * 文件选择类型
5 | */
6 | enum class FileSelectorType {
7 | APK,
8 | KEY,
9 | IMAGE,
10 | EXECUTE
11 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/model/IconFactoryData.kt:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class IconFactoryData(
7 | val pngTypIdx: PngAlgorithm,
8 | val jpegTypIdx: JpegAlgorithm,
9 | val lossless: Boolean,
10 | val minimum: Int,
11 | val target: Int,
12 | val speed: Int,
13 | val preset: Int,
14 | val percentage: Float,
15 | val quality: Float
16 | )
17 |
18 | enum class PngAlgorithm(val typIdx: Int) {
19 | Triangle(0), Catrom(1), Mitchell(2), Lanczos3(3)
20 | }
21 |
22 | enum class JpegAlgorithm(val typIdx: Int) {
23 | Bilinear(0), Hamming(1), CatmullRom(2), Mitchell(3), Gaussian(4), Lanczos3(5)
24 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/model/Model.kt:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import androidx.compose.material3.SnackbarDuration
4 | import androidx.compose.material3.SnackbarVisuals
5 | import androidx.compose.runtime.getValue
6 | import androidx.compose.runtime.mutableStateOf
7 | import androidx.compose.runtime.setValue
8 | import androidx.compose.ui.graphics.ImageBitmap
9 | import org.jetbrains.compose.resources.StringResource
10 | import org.tool.kit.composeapp.generated.resources.Res
11 | import org.tool.kit.composeapp.generated.resources.apk_signature_v1
12 | import org.tool.kit.composeapp.generated.resources.apk_signature_v2
13 | import org.tool.kit.composeapp.generated.resources.apk_signature_v2_only
14 | import org.tool.kit.composeapp.generated.resources.apk_signature_v3
15 | import java.io.File
16 |
17 | /**
18 | * @Author : LazyIonEs
19 | * @CreateDate : 2024/2/1 20:23
20 | * @Description : 描述
21 | * @Version : 1.0
22 | */
23 | /**
24 | * Apk签名信息结果
25 | */
26 | data class VerifierResult(
27 | val isSuccess: Boolean, // 验证是否成功
28 | val isApk: Boolean, // 是否是apk验证
29 | val path: String, // 文件路径
30 | val name: String, // 文件名称
31 | val data: ArrayList // 验证信息
32 | )
33 |
34 | /**
35 | * 签名信息结果
36 | */
37 | data class Verifier(
38 | val version: Int,
39 | val subject: String,
40 | val validFrom: String,
41 | val validUntil: String,
42 | val publicKeyType: String,
43 | val modulus: String,
44 | val signatureType: String,
45 | var md5: String,
46 | var sha1: String,
47 | var sha256: String
48 | )
49 |
50 | /**
51 | * Apk签名,存储页面信息,viewModel中
52 | */
53 | data class ApkSignature(
54 | var apkPath: String = "", // apk路径
55 | var outputPath: String = "", // apk输出路径
56 | var keyStorePolicy: SignaturePolicy = SignaturePolicy.V2, // 密钥策略
57 | private var _keyStorePath: String = "", // 密钥
58 | var keyStorePassword: String = "", // 密钥密码
59 | var keyStoreAlisaList: ArrayList? = null, // 别名列表
60 | var keyStoreAlisaIndex: Int = 0, // 别名选中下标
61 | var keyStoreAlisaPassword: String = "" // 别名密码
62 | ) {
63 | var keyStorePath: String
64 | get() = _keyStorePath
65 | set(value) {
66 | if (_keyStorePath != value) {
67 | this.keyStorePassword = ""
68 | this.keyStoreAlisaList = null
69 | this.keyStoreAlisaIndex = 0
70 | this.keyStoreAlisaPassword = ""
71 | }
72 | _keyStorePath = value
73 | }
74 | }
75 |
76 | /**
77 | * APK签名策略
78 | */
79 | enum class SignaturePolicy(val title: String, val value: StringResource) {
80 | V1("V1", Res.string.apk_signature_v1),
81 | V2("V2", Res.string.apk_signature_v2),
82 | V2Only("V2 Only", Res.string.apk_signature_v2_only),
83 | V3("V3", Res.string.apk_signature_v3)
84 | }
85 |
86 | /**
87 | * Apk信息
88 | */
89 | data class ApkInformation(
90 | var label: String = "", // 名称
91 | var icon: ImageBitmap? = null, // 图标
92 | var size: Long = 0L, // 大小
93 | var md5: String = "", // 文件md5
94 | var packageName: String = "", // 包名
95 | var versionCode: String = "", // 版本号
96 | var versionName: String = "", // 版本
97 | var compileSdkVersion: String = "", // 编译版本
98 | var minSdkVersion: String = "", // 最小版本
99 | var targetSdkVersion: String = "", // 目标版本
100 | var usesPermissionList: ArrayList? = null, // 权限列表
101 | var nativeCode: String = "" // 架构
102 | ) {
103 | fun isBlank(): Boolean {
104 | return label.isBlank() && packageName.isBlank() && versionCode.isBlank() && versionName.isBlank()
105 | }
106 | }
107 |
108 | /**
109 | * 签名信息,存储页面信息,viewModel中
110 | */
111 | data class KeyStoreInfo(
112 | var keyStorePath: String = "", // 密钥路径
113 | var keyStoreName: String = "sign.jks", // 密钥名称
114 | var keyStorePassword: String = "", // 密钥密码
115 | var keyStoreConfirmPassword: String = "", // 密钥确认密码
116 | var keyStoreAlisa: String = "", // 别名
117 | var keyStoreAlisaPassword: String = "", // 别名密码
118 | var keyStoreAlisaConfirmPassword: String = "", // 别名确认密码
119 | var validityPeriod: String = "25", // 密码有效期
120 | var authorName: String = "", // 作者名称
121 | var organizationalUnit: String = "", // 组织单位
122 | var organizational: String = "", // 组织
123 | var city: String = "", // 城市
124 | var province: String = "", // 省份
125 | var countryCode: String = "" // 国家编码
126 | )
127 |
128 | /**
129 | * 垃圾代码生成信息,存储页面信息,viewModel中
130 | */
131 | data class JunkCodeInfo(
132 | var outputPath: String = "", // 输出路径
133 | var aarName: String = "junk_com_dev_junk_plugin_TT2.0.0.aar", // aar名称
134 | private var _packageName: String = "com.dev.junk", // 包名
135 | private var _suffix: String = "plugin", // 后缀
136 | var packageCount: String = "50", // 包数量
137 | var activityCountPerPackage: String = "30", // 每个包里 activity 的数量
138 | var resPrefix: String = "junk_", // 资源前缀
139 | ) {
140 | var packageName: String
141 | get() = _packageName
142 | set(value) {
143 | _packageName = value
144 | aarName = "junk_" + packageName.replace(".", "_") + "_" + this.suffix + "_TT2.0.0.aar"
145 | }
146 |
147 | var suffix: String
148 | get() = _suffix
149 | set(value) {
150 | _suffix = value
151 | aarName = "junk_" + packageName.replace(".", "_") + "_" + this.suffix + "_TT2.0.0.aar"
152 | }
153 | }
154 |
155 |
156 | /**
157 | * 未签名APK
158 | */
159 | data class UnsignedApk(val title: String, val path: String)
160 |
161 | /**
162 | * 图标工厂信息,存储页面信息,viewModel中
163 | */
164 | data class IconFactoryInfo(
165 | var icon: File? = null, // 图标文件
166 | var outputPath: String = "", // 输出路径
167 | var fileDir: String = "res", // 顶级目录
168 | var iconDir: String = "mipmap", // Android目录
169 | var iconName: String = "ic_launcher", // icon名称
170 | var result: MutableList? = null // 生成的结果
171 | )
172 |
173 | /**
174 | * Snackbar信息
175 | * @param action 需要执行的操作
176 | */
177 | data class SnackbarVisualsData(
178 | override var message: String = "",
179 | override var actionLabel: String? = null,
180 | override var withDismissAction: Boolean = false,
181 | override var duration: SnackbarDuration = SnackbarDuration.Short,
182 | var timestamp: Long = System.currentTimeMillis(),
183 | var action: (() -> Unit)? = null
184 | ) : SnackbarVisuals {
185 |
186 | fun reset(): SnackbarVisualsData {
187 | actionLabel = null
188 | withDismissAction = false
189 | duration = SnackbarDuration.Short
190 | action = null
191 | timestamp = System.currentTimeMillis()
192 | return this
193 | }
194 | }
195 |
196 | class PendingDeletionFile(
197 | val directoryPath: String,
198 | val file: File,
199 | val filePath: String,
200 | val fileLength: Long,
201 | val fileLastModified: Long,
202 | deleteExceptions: Boolean = false,
203 | initialChecked: Boolean = true,
204 | ) {
205 | var checked by mutableStateOf(initialChecked)
206 | var exception by mutableStateOf(deleteExceptions)
207 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/model/Sequence.kt:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | /**
4 | * 列表排序
5 | */
6 | enum class Sequence {
7 | DATE_NEW_TO_OLD, // 日期从新到旧
8 | DATE_OLD_TO_NEW, // 日期从旧到新
9 | SIZE_LARGE_TO_SMALL, // 大小从大到小
10 | SIZE_SMALL_TO_LARGE, // 大小从小到大
11 | NAME_A_TO_Z, // 名称从A到Z
12 | NAME_Z_TO_A, // 名称从Z到A
13 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/model/UserData.kt:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import kotlinx.serialization.Serializable
4 | import org.jetbrains.compose.resources.StringResource
5 | import org.tool.kit.composeapp.generated.resources.Res
6 | import org.tool.kit.composeapp.generated.resources.dark_mode
7 | import org.tool.kit.composeapp.generated.resources.follow_the_system
8 | import org.tool.kit.composeapp.generated.resources.light_mode
9 |
10 | @Serializable
11 | data class UserData(
12 | val defaultOutputPath: String, // 默认输出路径
13 | val duplicateFileRemoval: Boolean, // 重复文件删除
14 | val defaultSignerSuffix: String, // 默认签名后缀
15 | val alignFileSize: Boolean, // 文件对齐
16 | val destStoreType: DestStoreType, // 目标密钥类型
17 | val destStoreSize: DestStoreSize, // 目标密钥大小
18 | )
19 |
20 | enum class DarkThemeConfig(val resource: StringResource) {
21 | FOLLOW_SYSTEM(Res.string.follow_the_system),
22 | LIGHT(Res.string.light_mode),
23 | DARK(Res.string.dark_mode)
24 | }
25 |
26 | enum class DestStoreSize(val size: Int) {
27 | ONE_THOUSAND_TWENTY_FOUR(1024), TWO_THOUSAND_FORTY_EIGHT(2048)
28 | }
29 |
30 | enum class DestStoreType {
31 | JKS, PKCS12
32 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/platform/Expect.kt:
--------------------------------------------------------------------------------
1 | package platform
2 |
3 | import androidx.annotation.FloatRange
4 | import androidx.annotation.IntRange
5 | import com.russhwolf.settings.ExperimentalSettingsApi
6 | import com.russhwolf.settings.coroutines.FlowSettings
7 |
8 | @OptIn(ExperimentalSettingsApi::class)
9 | expect fun createFlowSettings(): FlowSettings
10 |
11 | /**
12 | * 缩放图片
13 | * @param inputPath 输入路径
14 | * @param outputPath 输出路径
15 | * @param width 宽度
16 | * @param height 高度
17 | * @param typIdx 缩放算法 0 Triangle 1 Catrom 2 Mitchell 3 Lanczos3
18 | */
19 | @Throws(RustException::class)
20 | expect fun resizePng(inputPath: String, outputPath: String, width: UInt, height: UInt, typIdx: UByte = 3u)
21 |
22 | /**
23 | * 缩放图片
24 | * @param inputPath 输入路径
25 | * @param outputPath 输出路径
26 | * @param width 宽度
27 | * @param height 高度
28 | * @param typIdx 缩放算法 0 Bilinear 1 Hamming 2 CatmullRom 3 Mitchell 4 Gaussian 5 Lanczos3
29 | */
30 | @Throws(RustException::class)
31 | expect fun resizeFir(inputPath: String, outputPath: String, width: UInt, height: UInt, typIdx: UByte = 5u)
32 |
33 | /**
34 | * 量化(有损)并压缩图片
35 | * @param inputPath 输入路径
36 | * @param outputPath 输出路径
37 | * @param minimum 最低限度
38 | * @param target 目标质量 如果不能满足最低质量,量化将因错误而中止。默认值为最小值 0,最大值 100,表示尽力而为,并且永不中止该过程。
39 | * 如果最大值小于 100,则库将尝试使用较少的颜色。颜色较少的图像并不总是较小,因为它会导致抖动增加。
40 | * @param speed 速度 1 - 10 更快的速度会生成质量较低的图像,但可能对于实时生成图像有用
41 | * @param preset 预设
42 | */
43 | @Throws(RustException::class)
44 | expect fun quantize(
45 | inputPath: String,
46 | outputPath: String,
47 | @IntRange(from = 0, to = 100) minimum: Int = 70,
48 | @IntRange(from = 30, to = 100) target: Int = 100,
49 | @IntRange(from = 1, to = 10) speed: Int = 1,
50 | @IntRange(from = 0, to = 6) preset: Int = 6
51 | )
52 |
53 | /**
54 | * 无损压缩PNG
55 | * @param inputPath 输入路径
56 | * @param outputPath 输出路径
57 | * @param preset 预设 1 - 6 预设越高、速度越慢、压缩效果越好
58 | */
59 | @Throws(RustException::class)
60 | expect fun oxipng(
61 | inputPath: String,
62 | outputPath: String,
63 | @IntRange(from = 0, to = 6) preset: Int = 6
64 | )
65 |
66 | /**
67 | * 压缩图片
68 | * @param inputPath 输入路径
69 | * @param outputPath 输出路径
70 | * @param quality 图像质量。建议值为 60-80
71 | */
72 | @Throws(RustException::class)
73 | expect fun mozJpeg(inputPath: String, outputPath: String, @FloatRange(from = 0.0, to = 100.0) quality: Float = 85f)
74 |
75 | class RustException(
76 | message: String?
77 | ) : Exception(message)
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package theme
2 | import androidx.compose.ui.graphics.Color
3 |
4 | val primaryLight = Color(0xFF4C662B)
5 | val onPrimaryLight = Color(0xFFFFFFFF)
6 | val primaryContainerLight = Color(0xFFCDEDA3)
7 | val onPrimaryContainerLight = Color(0xFF102000)
8 | val secondaryLight = Color(0xFF586249)
9 | val onSecondaryLight = Color(0xFFFFFFFF)
10 | val secondaryContainerLight = Color(0xFFDCE7C8)
11 | val onSecondaryContainerLight = Color(0xFF151E0B)
12 | val tertiaryLight = Color(0xFF386663)
13 | val onTertiaryLight = Color(0xFFFFFFFF)
14 | val tertiaryContainerLight = Color(0xFFBCECE7)
15 | val onTertiaryContainerLight = Color(0xFF00201E)
16 | val errorLight = Color(0xFFBA1A1A)
17 | val onErrorLight = Color(0xFFFFFFFF)
18 | val errorContainerLight = Color(0xFFFFDAD6)
19 | val onErrorContainerLight = Color(0xFF410002)
20 | val backgroundLight = Color(0xFFF9FAEF)
21 | val onBackgroundLight = Color(0xFF1A1C16)
22 | val surfaceLight = Color(0xFFF9FAEF)
23 | val onSurfaceLight = Color(0xFF1A1C16)
24 | val surfaceVariantLight = Color(0xFFE1E4D5)
25 | val onSurfaceVariantLight = Color(0xFF44483D)
26 | val outlineLight = Color(0xFF75796C)
27 | val outlineVariantLight = Color(0xFFC5C8BA)
28 | val scrimLight = Color(0xFF000000)
29 | val inverseSurfaceLight = Color(0xFF2F312A)
30 | val inverseOnSurfaceLight = Color(0xFFF1F2E6)
31 | val inversePrimaryLight = Color(0xFFB1D18A)
32 | val surfaceDimLight = Color(0xFFDADBD0)
33 | val surfaceBrightLight = Color(0xFFF9FAEF)
34 | val surfaceContainerLowestLight = Color(0xFFFFFFFF)
35 | val surfaceContainerLowLight = Color(0xFFF3F4E9)
36 | val surfaceContainerLight = Color(0xFFEEEFE3)
37 | val surfaceContainerHighLight = Color(0xFFE8E9DE)
38 | val surfaceContainerHighestLight = Color(0xFFE2E3D8)
39 |
40 | val primaryLightMediumContrast = Color(0xFF314A12)
41 | val onPrimaryLightMediumContrast = Color(0xFFFFFFFF)
42 | val primaryContainerLightMediumContrast = Color(0xFF617D3F)
43 | val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF)
44 | val secondaryLightMediumContrast = Color(0xFF3C462F)
45 | val onSecondaryLightMediumContrast = Color(0xFFFFFFFF)
46 | val secondaryContainerLightMediumContrast = Color(0xFF6E785E)
47 | val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF)
48 | val tertiaryLightMediumContrast = Color(0xFF1A4A47)
49 | val onTertiaryLightMediumContrast = Color(0xFFFFFFFF)
50 | val tertiaryContainerLightMediumContrast = Color(0xFF4F7D79)
51 | val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF)
52 | val errorLightMediumContrast = Color(0xFF8C0009)
53 | val onErrorLightMediumContrast = Color(0xFFFFFFFF)
54 | val errorContainerLightMediumContrast = Color(0xFFDA342E)
55 | val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF)
56 | val backgroundLightMediumContrast = Color(0xFFF9FAEF)
57 | val onBackgroundLightMediumContrast = Color(0xFF1A1C16)
58 | val surfaceLightMediumContrast = Color(0xFFF9FAEF)
59 | val onSurfaceLightMediumContrast = Color(0xFF1A1C16)
60 | val surfaceVariantLightMediumContrast = Color(0xFFE1E4D5)
61 | val onSurfaceVariantLightMediumContrast = Color(0xFF404439)
62 | val outlineLightMediumContrast = Color(0xFF5D6155)
63 | val outlineVariantLightMediumContrast = Color(0xFF787C70)
64 | val scrimLightMediumContrast = Color(0xFF000000)
65 | val inverseSurfaceLightMediumContrast = Color(0xFF2F312A)
66 | val inverseOnSurfaceLightMediumContrast = Color(0xFFF1F2E6)
67 | val inversePrimaryLightMediumContrast = Color(0xFFB1D18A)
68 | val surfaceDimLightMediumContrast = Color(0xFFDADBD0)
69 | val surfaceBrightLightMediumContrast = Color(0xFFF9FAEF)
70 | val surfaceContainerLowestLightMediumContrast = Color(0xFFFFFFFF)
71 | val surfaceContainerLowLightMediumContrast = Color(0xFFF3F4E9)
72 | val surfaceContainerLightMediumContrast = Color(0xFFEEEFE3)
73 | val surfaceContainerHighLightMediumContrast = Color(0xFFE8E9DE)
74 | val surfaceContainerHighestLightMediumContrast = Color(0xFFE2E3D8)
75 |
76 | val primaryLightHighContrast = Color(0xFF142700)
77 | val onPrimaryLightHighContrast = Color(0xFFFFFFFF)
78 | val primaryContainerLightHighContrast = Color(0xFF314A12)
79 | val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF)
80 | val secondaryLightHighContrast = Color(0xFF1C2511)
81 | val onSecondaryLightHighContrast = Color(0xFFFFFFFF)
82 | val secondaryContainerLightHighContrast = Color(0xFF3C462F)
83 | val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF)
84 | val tertiaryLightHighContrast = Color(0xFF002725)
85 | val onTertiaryLightHighContrast = Color(0xFFFFFFFF)
86 | val tertiaryContainerLightHighContrast = Color(0xFF1A4A47)
87 | val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF)
88 | val errorLightHighContrast = Color(0xFF4E0002)
89 | val onErrorLightHighContrast = Color(0xFFFFFFFF)
90 | val errorContainerLightHighContrast = Color(0xFF8C0009)
91 | val onErrorContainerLightHighContrast = Color(0xFFFFFFFF)
92 | val backgroundLightHighContrast = Color(0xFFF9FAEF)
93 | val onBackgroundLightHighContrast = Color(0xFF1A1C16)
94 | val surfaceLightHighContrast = Color(0xFFF9FAEF)
95 | val onSurfaceLightHighContrast = Color(0xFF000000)
96 | val surfaceVariantLightHighContrast = Color(0xFFE1E4D5)
97 | val onSurfaceVariantLightHighContrast = Color(0xFF21251C)
98 | val outlineLightHighContrast = Color(0xFF404439)
99 | val outlineVariantLightHighContrast = Color(0xFF404439)
100 | val scrimLightHighContrast = Color(0xFF000000)
101 | val inverseSurfaceLightHighContrast = Color(0xFF2F312A)
102 | val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF)
103 | val inversePrimaryLightHighContrast = Color(0xFFD6F7AC)
104 | val surfaceDimLightHighContrast = Color(0xFFDADBD0)
105 | val surfaceBrightLightHighContrast = Color(0xFFF9FAEF)
106 | val surfaceContainerLowestLightHighContrast = Color(0xFFFFFFFF)
107 | val surfaceContainerLowLightHighContrast = Color(0xFFF3F4E9)
108 | val surfaceContainerLightHighContrast = Color(0xFFEEEFE3)
109 | val surfaceContainerHighLightHighContrast = Color(0xFFE8E9DE)
110 | val surfaceContainerHighestLightHighContrast = Color(0xFFE2E3D8)
111 |
112 | val primaryDark = Color(0xFFB1D18A)
113 | val onPrimaryDark = Color(0xFF1F3701)
114 | val primaryContainerDark = Color(0xFF354E16)
115 | val onPrimaryContainerDark = Color(0xFFCDEDA3)
116 | val secondaryDark = Color(0xFFBFCBAD)
117 | val onSecondaryDark = Color(0xFF2A331E)
118 | val secondaryContainerDark = Color(0xFF404A33)
119 | val onSecondaryContainerDark = Color(0xFFDCE7C8)
120 | val tertiaryDark = Color(0xFFA0D0CB)
121 | val onTertiaryDark = Color(0xFF003735)
122 | val tertiaryContainerDark = Color(0xFF1F4E4B)
123 | val onTertiaryContainerDark = Color(0xFFBCECE7)
124 | val errorDark = Color(0xFFFFB4AB)
125 | val onErrorDark = Color(0xFF690005)
126 | val errorContainerDark = Color(0xFF93000A)
127 | val onErrorContainerDark = Color(0xFFFFDAD6)
128 | val backgroundDark = Color(0xFF12140E)
129 | val onBackgroundDark = Color(0xFFE2E3D8)
130 | val surfaceDark = Color(0xFF12140E)
131 | val onSurfaceDark = Color(0xFFE2E3D8)
132 | val surfaceVariantDark = Color(0xFF44483D)
133 | val onSurfaceVariantDark = Color(0xFFC5C8BA)
134 | val outlineDark = Color(0xFF8F9285)
135 | val outlineVariantDark = Color(0xFF44483D)
136 | val scrimDark = Color(0xFF000000)
137 | val inverseSurfaceDark = Color(0xFFE2E3D8)
138 | val inverseOnSurfaceDark = Color(0xFF2F312A)
139 | val inversePrimaryDark = Color(0xFF4C662B)
140 | val surfaceDimDark = Color(0xFF12140E)
141 | val surfaceBrightDark = Color(0xFF383A32)
142 | val surfaceContainerLowestDark = Color(0xFF0C0F09)
143 | val surfaceContainerLowDark = Color(0xFF1A1C16)
144 | val surfaceContainerDark = Color(0xFF1E201A)
145 | val surfaceContainerHighDark = Color(0xFF282B24)
146 | val surfaceContainerHighestDark = Color(0xFF33362E)
147 |
148 | val primaryDarkMediumContrast = Color(0xFFB5D58E)
149 | val onPrimaryDarkMediumContrast = Color(0xFF0C1A00)
150 | val primaryContainerDarkMediumContrast = Color(0xFF7D9A59)
151 | val onPrimaryContainerDarkMediumContrast = Color(0xFF000000)
152 | val secondaryDarkMediumContrast = Color(0xFFC4CFB1)
153 | val onSecondaryDarkMediumContrast = Color(0xFF101907)
154 | val secondaryContainerDarkMediumContrast = Color(0xFF8A9579)
155 | val onSecondaryContainerDarkMediumContrast = Color(0xFF000000)
156 | val tertiaryDarkMediumContrast = Color(0xFFA4D4D0)
157 | val onTertiaryDarkMediumContrast = Color(0xFF001A19)
158 | val tertiaryContainerDarkMediumContrast = Color(0xFF6B9995)
159 | val onTertiaryContainerDarkMediumContrast = Color(0xFF000000)
160 | val errorDarkMediumContrast = Color(0xFFFFBAB1)
161 | val onErrorDarkMediumContrast = Color(0xFF370001)
162 | val errorContainerDarkMediumContrast = Color(0xFFFF5449)
163 | val onErrorContainerDarkMediumContrast = Color(0xFF000000)
164 | val backgroundDarkMediumContrast = Color(0xFF12140E)
165 | val onBackgroundDarkMediumContrast = Color(0xFFE2E3D8)
166 | val surfaceDarkMediumContrast = Color(0xFF12140E)
167 | val onSurfaceDarkMediumContrast = Color(0xFFFBFCF0)
168 | val surfaceVariantDarkMediumContrast = Color(0xFF44483D)
169 | val onSurfaceVariantDarkMediumContrast = Color(0xFFC9CCBE)
170 | val outlineDarkMediumContrast = Color(0xFFA1A497)
171 | val outlineVariantDarkMediumContrast = Color(0xFF818578)
172 | val scrimDarkMediumContrast = Color(0xFF000000)
173 | val inverseSurfaceDarkMediumContrast = Color(0xFFE2E3D8)
174 | val inverseOnSurfaceDarkMediumContrast = Color(0xFF282B24)
175 | val inversePrimaryDarkMediumContrast = Color(0xFF364F17)
176 | val surfaceDimDarkMediumContrast = Color(0xFF12140E)
177 | val surfaceBrightDarkMediumContrast = Color(0xFF383A32)
178 | val surfaceContainerLowestDarkMediumContrast = Color(0xFF0C0F09)
179 | val surfaceContainerLowDarkMediumContrast = Color(0xFF1A1C16)
180 | val surfaceContainerDarkMediumContrast = Color(0xFF1E201A)
181 | val surfaceContainerHighDarkMediumContrast = Color(0xFF282B24)
182 | val surfaceContainerHighestDarkMediumContrast = Color(0xFF33362E)
183 |
184 | val primaryDarkHighContrast = Color(0xFFF4FFDF)
185 | val onPrimaryDarkHighContrast = Color(0xFF000000)
186 | val primaryContainerDarkHighContrast = Color(0xFFB5D58E)
187 | val onPrimaryContainerDarkHighContrast = Color(0xFF000000)
188 | val secondaryDarkHighContrast = Color(0xFFF4FFDF)
189 | val onSecondaryDarkHighContrast = Color(0xFF000000)
190 | val secondaryContainerDarkHighContrast = Color(0xFFC4CFB1)
191 | val onSecondaryContainerDarkHighContrast = Color(0xFF000000)
192 | val tertiaryDarkHighContrast = Color(0xFFEAFFFC)
193 | val onTertiaryDarkHighContrast = Color(0xFF000000)
194 | val tertiaryContainerDarkHighContrast = Color(0xFFA4D4D0)
195 | val onTertiaryContainerDarkHighContrast = Color(0xFF000000)
196 | val errorDarkHighContrast = Color(0xFFFFF9F9)
197 | val onErrorDarkHighContrast = Color(0xFF000000)
198 | val errorContainerDarkHighContrast = Color(0xFFFFBAB1)
199 | val onErrorContainerDarkHighContrast = Color(0xFF000000)
200 | val backgroundDarkHighContrast = Color(0xFF12140E)
201 | val onBackgroundDarkHighContrast = Color(0xFFE2E3D8)
202 | val surfaceDarkHighContrast = Color(0xFF12140E)
203 | val onSurfaceDarkHighContrast = Color(0xFFFFFFFF)
204 | val surfaceVariantDarkHighContrast = Color(0xFF44483D)
205 | val onSurfaceVariantDarkHighContrast = Color(0xFFF9FCED)
206 | val outlineDarkHighContrast = Color(0xFFC9CCBE)
207 | val outlineVariantDarkHighContrast = Color(0xFFC9CCBE)
208 | val scrimDarkHighContrast = Color(0xFF000000)
209 | val inverseSurfaceDarkHighContrast = Color(0xFFE2E3D8)
210 | val inverseOnSurfaceDarkHighContrast = Color(0xFF000000)
211 | val inversePrimaryDarkHighContrast = Color(0xFF1A3000)
212 | val surfaceDimDarkHighContrast = Color(0xFF12140E)
213 | val surfaceBrightDarkHighContrast = Color(0xFF383A32)
214 | val surfaceContainerLowestDarkHighContrast = Color(0xFF0C0F09)
215 | val surfaceContainerLowDarkHighContrast = Color(0xFF1A1C16)
216 | val surfaceContainerDarkHighContrast = Color(0xFF1E201A)
217 | val surfaceContainerHighDarkHighContrast = Color(0xFF282B24)
218 | val surfaceContainerHighestDarkHighContrast = Color(0xFF33362E)
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package theme
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.material3.darkColorScheme
6 | import androidx.compose.material3.lightColorScheme
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.runtime.Immutable
9 | import androidx.compose.ui.graphics.Color
10 |
11 | private val lightScheme = lightColorScheme(
12 | primary = primaryLight,
13 | onPrimary = onPrimaryLight,
14 | primaryContainer = primaryContainerLight,
15 | onPrimaryContainer = onPrimaryContainerLight,
16 | secondary = secondaryLight,
17 | onSecondary = onSecondaryLight,
18 | secondaryContainer = secondaryContainerLight,
19 | onSecondaryContainer = onSecondaryContainerLight,
20 | tertiary = tertiaryLight,
21 | onTertiary = onTertiaryLight,
22 | tertiaryContainer = tertiaryContainerLight,
23 | onTertiaryContainer = onTertiaryContainerLight,
24 | error = errorLight,
25 | onError = onErrorLight,
26 | errorContainer = errorContainerLight,
27 | onErrorContainer = onErrorContainerLight,
28 | background = backgroundLight,
29 | onBackground = onBackgroundLight,
30 | surface = surfaceLight,
31 | onSurface = onSurfaceLight,
32 | surfaceVariant = surfaceVariantLight,
33 | onSurfaceVariant = onSurfaceVariantLight,
34 | outline = outlineLight,
35 | outlineVariant = outlineVariantLight,
36 | scrim = scrimLight,
37 | inverseSurface = inverseSurfaceLight,
38 | inverseOnSurface = inverseOnSurfaceLight,
39 | inversePrimary = inversePrimaryLight,
40 | surfaceDim = surfaceDimLight,
41 | surfaceBright = surfaceBrightLight,
42 | surfaceContainerLowest = surfaceContainerLowestLight,
43 | surfaceContainerLow = surfaceContainerLowLight,
44 | surfaceContainer = surfaceContainerLight,
45 | surfaceContainerHigh = surfaceContainerHighLight,
46 | surfaceContainerHighest = surfaceContainerHighestLight,
47 | )
48 |
49 | private val darkScheme = darkColorScheme(
50 | primary = primaryDark,
51 | onPrimary = onPrimaryDark,
52 | primaryContainer = primaryContainerDark,
53 | onPrimaryContainer = onPrimaryContainerDark,
54 | secondary = secondaryDark,
55 | onSecondary = onSecondaryDark,
56 | secondaryContainer = secondaryContainerDark,
57 | onSecondaryContainer = onSecondaryContainerDark,
58 | tertiary = tertiaryDark,
59 | onTertiary = onTertiaryDark,
60 | tertiaryContainer = tertiaryContainerDark,
61 | onTertiaryContainer = onTertiaryContainerDark,
62 | error = errorDark,
63 | onError = onErrorDark,
64 | errorContainer = errorContainerDark,
65 | onErrorContainer = onErrorContainerDark,
66 | background = backgroundDark,
67 | onBackground = onBackgroundDark,
68 | surface = surfaceDark,
69 | onSurface = onSurfaceDark,
70 | surfaceVariant = surfaceVariantDark,
71 | onSurfaceVariant = onSurfaceVariantDark,
72 | outline = outlineDark,
73 | outlineVariant = outlineVariantDark,
74 | scrim = scrimDark,
75 | inverseSurface = inverseSurfaceDark,
76 | inverseOnSurface = inverseOnSurfaceDark,
77 | inversePrimary = inversePrimaryDark,
78 | surfaceDim = surfaceDimDark,
79 | surfaceBright = surfaceBrightDark,
80 | surfaceContainerLowest = surfaceContainerLowestDark,
81 | surfaceContainerLow = surfaceContainerLowDark,
82 | surfaceContainer = surfaceContainerDark,
83 | surfaceContainerHigh = surfaceContainerHighDark,
84 | surfaceContainerHighest = surfaceContainerHighestDark,
85 | )
86 |
87 | private val mediumContrastLightColorScheme = lightColorScheme(
88 | primary = primaryLightMediumContrast,
89 | onPrimary = onPrimaryLightMediumContrast,
90 | primaryContainer = primaryContainerLightMediumContrast,
91 | onPrimaryContainer = onPrimaryContainerLightMediumContrast,
92 | secondary = secondaryLightMediumContrast,
93 | onSecondary = onSecondaryLightMediumContrast,
94 | secondaryContainer = secondaryContainerLightMediumContrast,
95 | onSecondaryContainer = onSecondaryContainerLightMediumContrast,
96 | tertiary = tertiaryLightMediumContrast,
97 | onTertiary = onTertiaryLightMediumContrast,
98 | tertiaryContainer = tertiaryContainerLightMediumContrast,
99 | onTertiaryContainer = onTertiaryContainerLightMediumContrast,
100 | error = errorLightMediumContrast,
101 | onError = onErrorLightMediumContrast,
102 | errorContainer = errorContainerLightMediumContrast,
103 | onErrorContainer = onErrorContainerLightMediumContrast,
104 | background = backgroundLightMediumContrast,
105 | onBackground = onBackgroundLightMediumContrast,
106 | surface = surfaceLightMediumContrast,
107 | onSurface = onSurfaceLightMediumContrast,
108 | surfaceVariant = surfaceVariantLightMediumContrast,
109 | onSurfaceVariant = onSurfaceVariantLightMediumContrast,
110 | outline = outlineLightMediumContrast,
111 | outlineVariant = outlineVariantLightMediumContrast,
112 | scrim = scrimLightMediumContrast,
113 | inverseSurface = inverseSurfaceLightMediumContrast,
114 | inverseOnSurface = inverseOnSurfaceLightMediumContrast,
115 | inversePrimary = inversePrimaryLightMediumContrast,
116 | surfaceDim = surfaceDimLightMediumContrast,
117 | surfaceBright = surfaceBrightLightMediumContrast,
118 | surfaceContainerLowest = surfaceContainerLowestLightMediumContrast,
119 | surfaceContainerLow = surfaceContainerLowLightMediumContrast,
120 | surfaceContainer = surfaceContainerLightMediumContrast,
121 | surfaceContainerHigh = surfaceContainerHighLightMediumContrast,
122 | surfaceContainerHighest = surfaceContainerHighestLightMediumContrast,
123 | )
124 |
125 | private val highContrastLightColorScheme = lightColorScheme(
126 | primary = primaryLightHighContrast,
127 | onPrimary = onPrimaryLightHighContrast,
128 | primaryContainer = primaryContainerLightHighContrast,
129 | onPrimaryContainer = onPrimaryContainerLightHighContrast,
130 | secondary = secondaryLightHighContrast,
131 | onSecondary = onSecondaryLightHighContrast,
132 | secondaryContainer = secondaryContainerLightHighContrast,
133 | onSecondaryContainer = onSecondaryContainerLightHighContrast,
134 | tertiary = tertiaryLightHighContrast,
135 | onTertiary = onTertiaryLightHighContrast,
136 | tertiaryContainer = tertiaryContainerLightHighContrast,
137 | onTertiaryContainer = onTertiaryContainerLightHighContrast,
138 | error = errorLightHighContrast,
139 | onError = onErrorLightHighContrast,
140 | errorContainer = errorContainerLightHighContrast,
141 | onErrorContainer = onErrorContainerLightHighContrast,
142 | background = backgroundLightHighContrast,
143 | onBackground = onBackgroundLightHighContrast,
144 | surface = surfaceLightHighContrast,
145 | onSurface = onSurfaceLightHighContrast,
146 | surfaceVariant = surfaceVariantLightHighContrast,
147 | onSurfaceVariant = onSurfaceVariantLightHighContrast,
148 | outline = outlineLightHighContrast,
149 | outlineVariant = outlineVariantLightHighContrast,
150 | scrim = scrimLightHighContrast,
151 | inverseSurface = inverseSurfaceLightHighContrast,
152 | inverseOnSurface = inverseOnSurfaceLightHighContrast,
153 | inversePrimary = inversePrimaryLightHighContrast,
154 | surfaceDim = surfaceDimLightHighContrast,
155 | surfaceBright = surfaceBrightLightHighContrast,
156 | surfaceContainerLowest = surfaceContainerLowestLightHighContrast,
157 | surfaceContainerLow = surfaceContainerLowLightHighContrast,
158 | surfaceContainer = surfaceContainerLightHighContrast,
159 | surfaceContainerHigh = surfaceContainerHighLightHighContrast,
160 | surfaceContainerHighest = surfaceContainerHighestLightHighContrast,
161 | )
162 |
163 | private val mediumContrastDarkColorScheme = darkColorScheme(
164 | primary = primaryDarkMediumContrast,
165 | onPrimary = onPrimaryDarkMediumContrast,
166 | primaryContainer = primaryContainerDarkMediumContrast,
167 | onPrimaryContainer = onPrimaryContainerDarkMediumContrast,
168 | secondary = secondaryDarkMediumContrast,
169 | onSecondary = onSecondaryDarkMediumContrast,
170 | secondaryContainer = secondaryContainerDarkMediumContrast,
171 | onSecondaryContainer = onSecondaryContainerDarkMediumContrast,
172 | tertiary = tertiaryDarkMediumContrast,
173 | onTertiary = onTertiaryDarkMediumContrast,
174 | tertiaryContainer = tertiaryContainerDarkMediumContrast,
175 | onTertiaryContainer = onTertiaryContainerDarkMediumContrast,
176 | error = errorDarkMediumContrast,
177 | onError = onErrorDarkMediumContrast,
178 | errorContainer = errorContainerDarkMediumContrast,
179 | onErrorContainer = onErrorContainerDarkMediumContrast,
180 | background = backgroundDarkMediumContrast,
181 | onBackground = onBackgroundDarkMediumContrast,
182 | surface = surfaceDarkMediumContrast,
183 | onSurface = onSurfaceDarkMediumContrast,
184 | surfaceVariant = surfaceVariantDarkMediumContrast,
185 | onSurfaceVariant = onSurfaceVariantDarkMediumContrast,
186 | outline = outlineDarkMediumContrast,
187 | outlineVariant = outlineVariantDarkMediumContrast,
188 | scrim = scrimDarkMediumContrast,
189 | inverseSurface = inverseSurfaceDarkMediumContrast,
190 | inverseOnSurface = inverseOnSurfaceDarkMediumContrast,
191 | inversePrimary = inversePrimaryDarkMediumContrast,
192 | surfaceDim = surfaceDimDarkMediumContrast,
193 | surfaceBright = surfaceBrightDarkMediumContrast,
194 | surfaceContainerLowest = surfaceContainerLowestDarkMediumContrast,
195 | surfaceContainerLow = surfaceContainerLowDarkMediumContrast,
196 | surfaceContainer = surfaceContainerDarkMediumContrast,
197 | surfaceContainerHigh = surfaceContainerHighDarkMediumContrast,
198 | surfaceContainerHighest = surfaceContainerHighestDarkMediumContrast,
199 | )
200 |
201 | private val highContrastDarkColorScheme = darkColorScheme(
202 | primary = primaryDarkHighContrast,
203 | onPrimary = onPrimaryDarkHighContrast,
204 | primaryContainer = primaryContainerDarkHighContrast,
205 | onPrimaryContainer = onPrimaryContainerDarkHighContrast,
206 | secondary = secondaryDarkHighContrast,
207 | onSecondary = onSecondaryDarkHighContrast,
208 | secondaryContainer = secondaryContainerDarkHighContrast,
209 | onSecondaryContainer = onSecondaryContainerDarkHighContrast,
210 | tertiary = tertiaryDarkHighContrast,
211 | onTertiary = onTertiaryDarkHighContrast,
212 | tertiaryContainer = tertiaryContainerDarkHighContrast,
213 | onTertiaryContainer = onTertiaryContainerDarkHighContrast,
214 | error = errorDarkHighContrast,
215 | onError = onErrorDarkHighContrast,
216 | errorContainer = errorContainerDarkHighContrast,
217 | onErrorContainer = onErrorContainerDarkHighContrast,
218 | background = backgroundDarkHighContrast,
219 | onBackground = onBackgroundDarkHighContrast,
220 | surface = surfaceDarkHighContrast,
221 | onSurface = onSurfaceDarkHighContrast,
222 | surfaceVariant = surfaceVariantDarkHighContrast,
223 | onSurfaceVariant = onSurfaceVariantDarkHighContrast,
224 | outline = outlineDarkHighContrast,
225 | outlineVariant = outlineVariantDarkHighContrast,
226 | scrim = scrimDarkHighContrast,
227 | inverseSurface = inverseSurfaceDarkHighContrast,
228 | inverseOnSurface = inverseOnSurfaceDarkHighContrast,
229 | inversePrimary = inversePrimaryDarkHighContrast,
230 | surfaceDim = surfaceDimDarkHighContrast,
231 | surfaceBright = surfaceBrightDarkHighContrast,
232 | surfaceContainerLowest = surfaceContainerLowestDarkHighContrast,
233 | surfaceContainerLow = surfaceContainerLowDarkHighContrast,
234 | surfaceContainer = surfaceContainerDarkHighContrast,
235 | surfaceContainerHigh = surfaceContainerHighDarkHighContrast,
236 | surfaceContainerHighest = surfaceContainerHighestDarkHighContrast,
237 | )
238 |
239 | @Immutable
240 | data class ColorFamily(
241 | val color: Color,
242 | val onColor: Color,
243 | val colorContainer: Color,
244 | val onColorContainer: Color
245 | )
246 |
247 | val unspecified_scheme = ColorFamily(
248 | Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified
249 | )
250 |
251 | @Composable
252 | fun AppTheme(useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
253 | val colors = if (!useDarkTheme) {
254 | lightScheme
255 | } else {
256 | darkScheme
257 | }
258 | MaterialTheme(
259 | colorScheme = colors,
260 | typography = AppTypography,
261 | content = content
262 | )
263 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package 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 AppTypography = Typography()
10 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/ui/ApkInformation.kt:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import androidx.compose.animation.AnimatedVisibility
4 | import androidx.compose.animation.fadeIn
5 | import androidx.compose.animation.fadeOut
6 | import androidx.compose.foundation.BorderStroke
7 | import androidx.compose.foundation.ExperimentalFoundationApi
8 | import androidx.compose.foundation.draganddrop.dragAndDropTarget
9 | import androidx.compose.foundation.isSystemInDarkTheme
10 | import androidx.compose.foundation.layout.Box
11 | import androidx.compose.foundation.layout.Column
12 | import androidx.compose.foundation.layout.Row
13 | import androidx.compose.foundation.layout.fillMaxSize
14 | import androidx.compose.foundation.layout.height
15 | import androidx.compose.foundation.layout.padding
16 | import androidx.compose.foundation.layout.size
17 | import androidx.compose.foundation.lazy.LazyColumn
18 | import androidx.compose.foundation.onClick
19 | import androidx.compose.material3.Card
20 | import androidx.compose.material3.MaterialTheme
21 | import androidx.compose.material3.Surface
22 | import androidx.compose.material3.Text
23 | import androidx.compose.runtime.Composable
24 | import androidx.compose.runtime.collectAsState
25 | import androidx.compose.runtime.getValue
26 | import androidx.compose.runtime.mutableStateOf
27 | import androidx.compose.runtime.remember
28 | import androidx.compose.runtime.rememberCoroutineScope
29 | import androidx.compose.runtime.setValue
30 | import androidx.compose.ui.Alignment
31 | import androidx.compose.ui.Modifier
32 | import androidx.compose.ui.graphics.asSkiaBitmap
33 | import androidx.compose.ui.unit.DpSize
34 | import androidx.compose.ui.unit.dp
35 | import androidx.compose.ui.window.Window
36 | import androidx.compose.ui.window.rememberWindowState
37 | import coil3.compose.AsyncImage
38 | import com.github.panpf.zoomimage.CoilZoomAsyncImage
39 | import kotlinx.coroutines.CoroutineScope
40 | import model.ApkInformation
41 | import model.DarkThemeConfig
42 | import model.FileSelectorType
43 | import org.jetbrains.compose.resources.painterResource
44 | import org.jetbrains.compose.resources.stringResource
45 | import org.tool.kit.composeapp.generated.resources.ABIs
46 | import org.tool.kit.composeapp.generated.resources.Res
47 | import org.tool.kit.composeapp.generated.resources.app_name
48 | import org.tool.kit.composeapp.generated.resources.compile_sdk_version
49 | import org.tool.kit.composeapp.generated.resources.file_md5
50 | import org.tool.kit.composeapp.generated.resources.icon
51 | import org.tool.kit.composeapp.generated.resources.let_go
52 | import org.tool.kit.composeapp.generated.resources.minimum_sdk_version
53 | import org.tool.kit.composeapp.generated.resources.package_name
54 | import org.tool.kit.composeapp.generated.resources.permissions
55 | import org.tool.kit.composeapp.generated.resources.size
56 | import org.tool.kit.composeapp.generated.resources.target_sdk_version
57 | import org.tool.kit.composeapp.generated.resources.upload_apk
58 | import org.tool.kit.composeapp.generated.resources.version
59 | import org.tool.kit.composeapp.generated.resources.version_code
60 | import theme.AppTheme
61 | import utils.LottieAnimation
62 | import utils.copy
63 | import utils.formatFileSize
64 | import utils.getImageRequest
65 | import utils.isApk
66 | import vm.MainViewModel
67 | import vm.UIState
68 | import kotlin.io.path.pathString
69 |
70 | /**
71 | * @Author : LazyIonEs
72 | * @CreateDate : 2024/2/8 16:13
73 | * @Description : APK信息
74 | * @Version : 1.0
75 | */
76 | @Composable
77 | fun ApkInformation(viewModel: MainViewModel) {
78 | val scope = rememberCoroutineScope()
79 | if (viewModel.apkInformationState == UIState.WAIT) {
80 | ApkInformationLottie(viewModel, scope)
81 | }
82 | ApkInformationBox(viewModel)
83 | ApkDraggingBox(viewModel, scope)
84 | }
85 |
86 | /**
87 | * 主页动画
88 | */
89 | @Composable
90 | private fun ApkInformationLottie(viewModel: MainViewModel, scope: CoroutineScope) {
91 | val themeConfig by viewModel.themeConfig.collectAsState()
92 | val useDarkTheme = when (themeConfig) {
93 | DarkThemeConfig.LIGHT -> false
94 | DarkThemeConfig.DARK -> true
95 | DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme()
96 | }
97 | Box(
98 | modifier = Modifier.padding(6.dp), contentAlignment = Alignment.Center
99 | ) {
100 | if (useDarkTheme) {
101 | LottieAnimation(scope, "files/lottie_main_2_dark.json")
102 | } else {
103 | LottieAnimation(scope, "files/lottie_main_2_light.json")
104 | }
105 | }
106 | }
107 |
108 | @OptIn(ExperimentalFoundationApi::class)
109 | @Composable
110 | private fun ApkDraggingBox(viewModel: MainViewModel, scope: CoroutineScope) {
111 | var dragging by remember { mutableStateOf(false) }
112 | UploadAnimate(dragging, scope)
113 | Box(
114 | modifier = Modifier.fillMaxSize()
115 | .dragAndDropTarget(shouldStartDragAndDrop = accept@{ true }, target = dragAndDropTarget(dragging = {
116 | dragging = it
117 | }, onFinish = { result ->
118 | result.onSuccess { fileList ->
119 | fileList.firstOrNull()?.let {
120 | val path = it.toAbsolutePath().pathString
121 | if (path.isApk) {
122 | viewModel.apkInformation(path)
123 | }
124 | }
125 | }
126 | }))
127 | ) {
128 | Box(
129 | modifier = Modifier.align(Alignment.BottomEnd)
130 | ) {
131 | FileButton(
132 | value = if (dragging) {
133 | stringResource(Res.string.let_go)
134 | } else {
135 | stringResource(Res.string.upload_apk)
136 | }, expanded = viewModel.apkInformationState == UIState.WAIT, FileSelectorType.APK
137 | ) { path ->
138 | viewModel.apkInformation(path)
139 | }
140 | }
141 | }
142 | }
143 |
144 | @OptIn(ExperimentalFoundationApi::class)
145 | @Composable
146 | private fun ApkInformationBox(
147 | viewModel: MainViewModel
148 | ) {
149 | val uiState = viewModel.apkInformationState
150 | AnimatedVisibility(
151 | visible = uiState is UIState.Success, enter = fadeIn(), exit = fadeOut()
152 | ) {
153 | Card(
154 | modifier = Modifier.fillMaxSize().padding(top = 14.dp, bottom = 14.dp, end = 14.dp),
155 | border = BorderStroke(2.dp, MaterialTheme.colorScheme.outline)
156 | ) {
157 | Box(
158 | modifier = Modifier.fillMaxSize().padding(vertical = 12.dp)
159 | ) {
160 | if (uiState is UIState.Success) {
161 | val apkInformation = uiState.result as ApkInformation
162 | LazyColumn {
163 | item {
164 | AppInfoItem(stringResource(Res.string.app_name), apkInformation.label, viewModel)
165 | }
166 | item {
167 | AppInfoItem(stringResource(Res.string.version), apkInformation.versionName, viewModel)
168 | }
169 | item {
170 | AppInfoItem(stringResource(Res.string.version_code), apkInformation.versionCode, viewModel)
171 | }
172 | item {
173 | AppInfoItem(stringResource(Res.string.package_name), apkInformation.packageName, viewModel)
174 | }
175 | item {
176 | AppInfoItem(
177 | stringResource(Res.string.compile_sdk_version),
178 | apkInformation.compileSdkVersion,
179 | viewModel
180 | )
181 | }
182 | item {
183 | AppInfoItem(
184 | stringResource(Res.string.minimum_sdk_version),
185 | apkInformation.minSdkVersion,
186 | viewModel
187 | )
188 | }
189 | item {
190 | AppInfoItem(
191 | stringResource(Res.string.target_sdk_version),
192 | apkInformation.targetSdkVersion,
193 | viewModel
194 | )
195 | }
196 | item {
197 | AppInfoItem(stringResource(Res.string.ABIs), apkInformation.nativeCode, viewModel)
198 | }
199 | item {
200 | AppInfoItem(stringResource(Res.string.file_md5), apkInformation.md5, viewModel)
201 | }
202 | item {
203 | AppInfoItem(
204 | stringResource(Res.string.size),
205 | apkInformation.size.formatFileSize(scale = 1, withInterval = true),
206 | viewModel
207 | )
208 | }
209 | item {
210 | PermissionsList(apkInformation.usesPermissionList)
211 | }
212 | }
213 | apkInformation.icon?.let { image ->
214 | var isOpenImage by remember { mutableStateOf(false) }
215 | if (isOpenImage) {
216 | val windowState = rememberWindowState(size = DpSize(450.dp, 450.dp))
217 | Window(
218 | onCloseRequest = { isOpenImage = false },
219 | state = windowState,
220 | title = "Zoom Image",
221 | icon = painterResource(Res.drawable.icon),
222 | alwaysOnTop = true
223 | ) {
224 | val themeConfig by viewModel.themeConfig.collectAsState()
225 | val useDarkTheme = when (themeConfig) {
226 | DarkThemeConfig.LIGHT -> false
227 | DarkThemeConfig.DARK -> true
228 | DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme()
229 | }
230 | AppTheme(useDarkTheme) {
231 | Surface(color = MaterialTheme.colorScheme.background) {
232 | CoilZoomAsyncImage(
233 | model = getImageRequest(image.asSkiaBitmap()),
234 | contentDescription = "zoom image",
235 | modifier = Modifier.fillMaxSize(),
236 | )
237 | }
238 | }
239 | }
240 | }
241 | AsyncImage(
242 | model = getImageRequest(image.asSkiaBitmap()),
243 | contentDescription = "app icon",
244 | modifier = Modifier.align(Alignment.TopEnd)
245 | .padding(top = 6.dp, end = 18.dp)
246 | .size(128.dp)
247 | .onClick {
248 | isOpenImage = !isOpenImage
249 | }
250 | )
251 | }
252 | }
253 | }
254 | }
255 | }
256 | }
257 |
258 | @Composable
259 | private fun AppInfoItem(title: String, value: String, viewModel: MainViewModel) {
260 | Card(modifier = Modifier.padding(horizontal = 12.dp).height(36.dp), onClick = {
261 | copy(value, viewModel)
262 | }) {
263 | Row(
264 | modifier = Modifier.fillMaxSize().padding(horizontal = 24.dp)
265 | ) {
266 | Text(
267 | title,
268 | style = MaterialTheme.typography.titleMedium,
269 | modifier = Modifier.weight(1.2f).align(Alignment.CenterVertically)
270 | )
271 | Text(
272 | value,
273 | style = MaterialTheme.typography.bodyMedium,
274 | modifier = Modifier.weight(4f).align(Alignment.CenterVertically)
275 | )
276 | }
277 | }
278 | }
279 |
280 | @Composable
281 | private fun PermissionsList(permissions: ArrayList?) {
282 | permissions?.let {
283 | Column(
284 | modifier = Modifier.padding(horizontal = 12.dp),
285 | ) {
286 | Row(
287 | modifier = Modifier.fillMaxSize().padding(horizontal = 24.dp)
288 | ) {
289 | Text(
290 | stringResource(Res.string.permissions),
291 | modifier = Modifier.weight(1.2f),
292 | style = MaterialTheme.typography.titleMedium
293 | )
294 | Column(
295 | modifier = Modifier.weight(4f)
296 | ) {
297 | it.forEach { permission ->
298 | Text(text = permission, style = MaterialTheme.typography.bodyMedium)
299 | }
300 | }
301 | }
302 | }
303 | }
304 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/ui/JunkCode.kt:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import androidx.compose.foundation.layout.Row
4 | import androidx.compose.foundation.layout.Spacer
5 | import androidx.compose.foundation.layout.fillMaxSize
6 | import androidx.compose.foundation.layout.fillMaxWidth
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.foundation.layout.size
9 | import androidx.compose.foundation.lazy.LazyColumn
10 | import androidx.compose.material3.Button
11 | import androidx.compose.material3.Card
12 | import androidx.compose.material3.MaterialTheme
13 | import androidx.compose.material3.OutlinedTextField
14 | import androidx.compose.material3.Text
15 | import androidx.compose.runtime.Composable
16 | import androidx.compose.ui.Alignment
17 | import androidx.compose.ui.Modifier
18 | import androidx.compose.ui.unit.dp
19 | import org.jetbrains.compose.resources.stringResource
20 | import org.tool.kit.composeapp.generated.resources.Res
21 | import org.tool.kit.composeapp.generated.resources.aar_name
22 | import org.tool.kit.composeapp.generated.resources.aar_output_path
23 | import org.tool.kit.composeapp.generated.resources.check_empty
24 | import org.tool.kit.composeapp.generated.resources.check_error
25 | import org.tool.kit.composeapp.generated.resources.junk_package_name
26 | import org.tool.kit.composeapp.generated.resources.number_of_activities
27 | import org.tool.kit.composeapp.generated.resources.number_of_packages
28 | import org.tool.kit.composeapp.generated.resources.resource_prefix
29 | import org.tool.kit.composeapp.generated.resources.start_generating
30 | import org.tool.kit.composeapp.generated.resources.suffix
31 | import vm.MainViewModel
32 | import java.io.File
33 |
34 | /**
35 | * @Author : LazyIonEs
36 | * @CreateDate : 2024/4/1 20:07
37 | * @Description : 垃圾代码生成页面
38 | * @Version : 1.0
39 | */
40 | @Composable
41 | fun JunkCode(viewModel: MainViewModel) {
42 | JunkCodeBox(viewModel)
43 | }
44 |
45 | @Composable
46 | private fun JunkCodeBox(viewModel: MainViewModel) {
47 | Card(
48 | modifier = Modifier.fillMaxSize().padding(top = 20.dp, bottom = 20.dp, end = 14.dp)
49 | ) {
50 | val outputPathError =
51 | viewModel.junkCodeInfoState.outputPath.isNotBlank() && !File(viewModel.junkCodeInfoState.outputPath).isDirectory
52 | LazyColumn(
53 | modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally
54 | ) {
55 | item {
56 | Spacer(Modifier.size(16.dp))
57 | FolderInput(
58 | value = viewModel.junkCodeInfoState.outputPath,
59 | label = stringResource(Res.string.aar_output_path),
60 | isError = outputPathError,
61 | onValueChange = { path ->
62 | viewModel.updateJunkCodeInfo(viewModel.junkCodeInfoState.copy(outputPath = path))
63 | })
64 | }
65 | item {
66 | Spacer(Modifier.size(8.dp))
67 | StringInput(
68 | value = viewModel.junkCodeInfoState.aarName,
69 | label = stringResource(Res.string.aar_name),
70 | isError = false,
71 | realOnly = true,
72 | onValueChange = { })
73 | }
74 | item {
75 | Spacer(Modifier.size(8.dp))
76 | PackageName(viewModel)
77 | }
78 | item {
79 | Spacer(Modifier.size(8.dp))
80 | IntInput(
81 | value = viewModel.junkCodeInfoState.packageCount,
82 | label = stringResource(Res.string.number_of_packages),
83 | isError = viewModel.junkCodeInfoState.packageCount.isBlank(),
84 | onValueChange = { packageCount ->
85 | viewModel.updateJunkCodeInfo(viewModel.junkCodeInfoState.copy(packageCount = packageCount))
86 | })
87 | }
88 | item {
89 | Spacer(Modifier.size(8.dp))
90 | IntInput(
91 | value = viewModel.junkCodeInfoState.activityCountPerPackage,
92 | label = stringResource(Res.string.number_of_activities),
93 | isError = viewModel.junkCodeInfoState.activityCountPerPackage.isBlank(),
94 | onValueChange = { activityCountPerPackage ->
95 | viewModel.updateJunkCodeInfo(viewModel.junkCodeInfoState.copy(activityCountPerPackage = activityCountPerPackage))
96 | })
97 | }
98 | item {
99 | Spacer(Modifier.size(8.dp))
100 | StringInput(
101 | value = viewModel.junkCodeInfoState.resPrefix,
102 | label = stringResource(Res.string.resource_prefix),
103 | isError = viewModel.junkCodeInfoState.resPrefix.isBlank(),
104 | onValueChange = { resPrefix ->
105 | viewModel.updateJunkCodeInfo(viewModel.junkCodeInfoState.copy(resPrefix = resPrefix))
106 | })
107 | }
108 | item {
109 | Spacer(Modifier.size(12.dp))
110 | Generate(
111 | viewModel, outputPathError
112 | )
113 | Spacer(Modifier.size(24.dp))
114 | }
115 | }
116 | }
117 | }
118 |
119 | @Composable
120 | private fun PackageName(viewModel: MainViewModel) {
121 | Row(
122 | modifier = Modifier.fillMaxWidth().padding(start = 16.dp, end = 64.dp),
123 | verticalAlignment = Alignment.CenterVertically
124 | ) {
125 | OutlinedTextField(
126 | modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 3.dp).weight(3f),
127 | value = viewModel.junkCodeInfoState.packageName,
128 | onValueChange = { packageName ->
129 | val junkCodeInfo = viewModel.junkCodeInfoState.copy()
130 | junkCodeInfo.packageName = packageName
131 | viewModel.updateJunkCodeInfo(junkCodeInfo)
132 | },
133 | label = { Text(text = stringResource(Res.string.junk_package_name), style = MaterialTheme.typography.labelLarge) },
134 | singleLine = true,
135 | isError = viewModel.junkCodeInfoState.packageName.isBlank()
136 | )
137 | Text(
138 | ".",
139 | style = MaterialTheme.typography.labelLarge,
140 | modifier = Modifier.align(Alignment.Bottom).padding(bottom = 3.dp)
141 | )
142 | OutlinedTextField(
143 | modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 3.dp).weight(2f),
144 | value = viewModel.junkCodeInfoState.suffix,
145 | onValueChange = { suffix ->
146 | val junkCodeInfo = viewModel.junkCodeInfoState.copy()
147 | junkCodeInfo.suffix = suffix
148 | viewModel.updateJunkCodeInfo(junkCodeInfo)
149 | },
150 | label = { Text(text = stringResource(Res.string.suffix), style = MaterialTheme.typography.labelLarge) },
151 | singleLine = true,
152 | isError = viewModel.junkCodeInfoState.suffix.isBlank()
153 | )
154 | }
155 | }
156 |
157 | @Composable
158 | private fun Generate(
159 | viewModel: MainViewModel, outputPathError: Boolean
160 | ) {
161 | Button(onClick = {
162 | if (outputPathError) {
163 | viewModel.updateSnackbarVisuals(Res.string.check_error)
164 | return@Button
165 | }
166 | if (viewModel.junkCodeInfoState.outputPath.isBlank() || viewModel.junkCodeInfoState.packageName.isBlank() || viewModel.junkCodeInfoState.suffix.isBlank() || viewModel.junkCodeInfoState.packageCount.isBlank() || viewModel.junkCodeInfoState.activityCountPerPackage.isEmpty() || viewModel.junkCodeInfoState.resPrefix.isBlank()) {
167 | viewModel.updateSnackbarVisuals(Res.string.check_empty)
168 | return@Button
169 | }
170 | viewModel.generateJunkCode()
171 | }) {
172 | Text(
173 | text = stringResource(Res.string.start_generating),
174 | style = MaterialTheme.typography.titleMedium,
175 | modifier = Modifier.padding(horizontal = 48.dp)
176 | )
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/utils/Copy.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import org.tool.kit.composeapp.generated.resources.Res
4 | import org.tool.kit.composeapp.generated.resources.copied_to_clipboard
5 | import vm.MainViewModel
6 | import java.awt.Toolkit
7 | import java.awt.datatransfer.Clipboard
8 | import java.awt.datatransfer.StringSelection
9 |
10 | /**
11 | * @Author : LazyIonEs
12 | * @CreateDate : 2024/2/3 15:56
13 | * @Description : 描述
14 | * @Version : 1.0
15 | */
16 |
17 | fun copy(value: String, viewModel: MainViewModel) {
18 | copy(value)
19 | viewModel.updateSnackbarVisuals(Res.string.copied_to_clipboard)
20 | }
21 |
22 | /**
23 | * 复制到剪切板
24 | */
25 | private fun copy(value: String) {
26 | val stringSelection = StringSelection(value)
27 | val clipboard: Clipboard = Toolkit.getDefaultToolkit().systemClipboard
28 | clipboard.setContents(stringSelection, null)
29 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/utils/CoroutinesUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package utils
18 |
19 | import kotlinx.coroutines.flow.SharingStarted
20 | import kotlinx.coroutines.flow.StateFlow
21 |
22 | private const val StopTimeoutMillis: Long = 5000
23 |
24 | /**
25 | * A [SharingStarted] meant to be used with a [StateFlow] to expose data to the UI.
26 | *
27 | * When the UI stops observing, upstream flows stay active for some time to allow the system to
28 | * come back from a short-lived configuration change (such as rotations). If the UI stops
29 | * observing for longer, the cache is kept but the upstream flows are stopped. When the UI comes
30 | * back, the latest value is replayed and the upstream flows are executed again. This is done to
31 | * save resources when the app is in the background but let users switch between apps quickly.
32 | */
33 | val WhileUiSubscribed: SharingStarted = SharingStarted.WhileSubscribed(StopTimeoutMillis)
34 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/utils/ExternalCommand.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import java.io.File
4 | import java.io.IOException
5 | import java.io.InputStream
6 | import java.io.OutputStream
7 | import java.util.concurrent.TimeUnit
8 |
9 | /**
10 | * Executes a command with ability to pass `stdin` and capture `stdout` and `stderr`.
11 | */
12 | class ExternalCommand(private val executable: String) {
13 |
14 | @Throws(IOException::class)
15 | fun execute(
16 | args: List,
17 | stdin: InputStream,
18 | stdout: OutputStream,
19 | stderr: OutputStream,
20 | timeout: Long = 60000L,
21 | unit: TimeUnit = TimeUnit.MILLISECONDS
22 | ): Int {
23 | val command: MutableList = ArrayList()
24 | val exe = File(executable)
25 | command.add(exe.absolutePath)
26 | command.addAll(args)
27 | val pb = ProcessBuilder(command)
28 | val process = pb.start()
29 | val inToProcess = PipeConnector(stdin, process.outputStream)
30 | val processToOut = PipeConnector(process.inputStream, stdout)
31 | val processToErr = PipeConnector(process.errorStream, stderr)
32 | inToProcess.start()
33 | processToOut.start()
34 | processToErr.start()
35 | var code = 255
36 | try {
37 | val finished = process.waitFor(timeout, unit)
38 | if (!finished) {
39 | process.destroyForcibly()
40 | } else {
41 | code = process.exitValue()
42 | }
43 | processToOut.join()
44 | stdin.close()
45 | inToProcess.join()
46 | } catch (e: InterruptedException) {
47 | println(e)
48 | }
49 | return code
50 | }
51 |
52 | private class PipeConnector(private val input: InputStream, private val output: OutputStream) : Thread() {
53 | override fun run() {
54 | try {
55 | val buffer = ByteArray(8192)
56 | var read: Int
57 | while (input.read(buffer).also { read = it } > 0) {
58 | output.write(buffer, 0, read)
59 | output.flush()
60 | }
61 | } catch (e: IOException) {
62 | // Ignore and exit the thread
63 | }
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/utils/ImageRequest.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import coil3.ImageLoader
4 | import coil3.asImage
5 | import coil3.decode.DataSource
6 | import coil3.fetch.FetchResult
7 | import coil3.fetch.Fetcher
8 | import coil3.fetch.ImageFetchResult
9 | import coil3.request.ImageRequest
10 | import coil3.request.Options
11 | import coil3.size.Size
12 | import org.jetbrains.skia.Bitmap
13 |
14 | fun getImageRequest(data: Any?) =
15 | ImageRequest.Builder(coil3.PlatformContext.INSTANCE)
16 | .data(data = data)
17 | .size(Size.ORIGINAL)
18 | .fetcherFactory(SkiaBitmapFetcher.Factory())
19 | .build()
20 |
21 | class SkiaBitmapFetcher(
22 | private val data: Bitmap
23 | ) : Fetcher {
24 | override suspend fun fetch(): FetchResult {
25 | return ImageFetchResult(
26 | image = data.asImage(),
27 | isSampled = false,
28 | dataSource = DataSource.MEMORY,
29 | )
30 | }
31 |
32 | class Factory : Fetcher.Factory {
33 | override fun create(data: Bitmap, options: Options, imageLoader: ImageLoader): Fetcher {
34 | return SkiaBitmapFetcher(data)
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/utils/LottieAnimation.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import androidx.compose.animation.core.*
4 | import androidx.compose.foundation.Canvas
5 | import androidx.compose.foundation.layout.fillMaxSize
6 | import androidx.compose.runtime.*
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
9 | import androidx.compose.ui.graphics.nativeCanvas
10 | import kotlinx.coroutines.CoroutineScope
11 | import kotlinx.coroutines.launch
12 | import org.jetbrains.compose.resources.ExperimentalResourceApi
13 | import org.jetbrains.skia.Rect
14 | import org.jetbrains.skia.skottie.Animation
15 | import org.jetbrains.skia.sksg.InvalidationController
16 | import org.tool.kit.composeapp.generated.resources.Res
17 | import kotlin.math.roundToInt
18 |
19 | /**
20 | * @Author : LazyIonEs
21 | * @CreateDate : 2024/3/7 16:54
22 | * @Description : 加载json动画
23 | * @Version : 1.0
24 | */
25 | @OptIn(ExperimentalResourceApi::class)
26 | @Composable
27 | fun LottieAnimation(scope: CoroutineScope, path: String, modifier: Modifier = Modifier) {
28 | var animation by remember { mutableStateOf(null) }
29 | scope.launch {
30 | val json = Res.readBytes(path).decodeToString()
31 | animation = Animation.makeFromString(json)
32 | }
33 | animation?.let { InfiniteAnimation(it, modifier.fillMaxSize()) }
34 | }
35 |
36 | @Composable
37 | private fun InfiniteAnimation(animation: Animation, modifier: Modifier) {
38 | val infiniteTransition = rememberInfiniteTransition()
39 | val time by infiniteTransition.animateFloat(
40 | initialValue = 0f, targetValue = animation.duration, animationSpec = infiniteRepeatable(
41 | animation = tween((animation.duration * 1000).roundToInt(), easing = LinearEasing),
42 | repeatMode = RepeatMode.Restart
43 | )
44 | )
45 | val invalidationController = remember { InvalidationController() }
46 | animation.seekFrameTime(time, invalidationController)
47 | Canvas(modifier) {
48 | drawIntoCanvas {
49 | animation.render(
50 | canvas = it.nativeCanvas, dst = Rect.makeWH(size.width, size.height)
51 | )
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/utils/update.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import androidx.compose.runtime.MutableState
4 |
5 | inline fun MutableState.update(
6 | transform: (T) -> T
7 | ): T = run {
8 | transform(this.value).also { this.value = it }
9 | }
--------------------------------------------------------------------------------
/composeApp/src/desktopMain/kotlin/main.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.ui.window.Window
2 | import androidx.compose.ui.window.application
3 | import org.jetbrains.compose.resources.painterResource
4 | import org.tool.kit.composeapp.generated.resources.Res
5 | import org.tool.kit.composeapp.generated.resources.icon
6 |
7 | fun main() = application {
8 | Window(
9 | onCloseRequest = ::exitApplication, title = "AndroidToolKit", icon = painterResource(Res.drawable.icon)
10 | ) {
11 | App()
12 | }
13 | }
--------------------------------------------------------------------------------
/composeApp/src/desktopMain/kotlin/platform/DatabaseDriverFactory.kt:
--------------------------------------------------------------------------------
1 | package platform
2 |
3 | import com.russhwolf.settings.ExperimentalSettingsApi
4 | import com.russhwolf.settings.PreferencesSettings
5 | import com.russhwolf.settings.coroutines.FlowSettings
6 | import com.russhwolf.settings.coroutines.toFlowSettings
7 | import kotlinx.coroutines.Dispatchers
8 |
9 | /**
10 | * @Author : LazyIonEs
11 | * @CreateDate : 2024/2/20 17:52
12 | * @Description : 数据库驱动工厂
13 | * @Version : 1.0
14 | */
15 | @OptIn(ExperimentalSettingsApi::class)
16 | actual fun createFlowSettings(): FlowSettings = PreferencesSettings.Factory().create("toolkit").toFlowSettings(Dispatchers.IO)
--------------------------------------------------------------------------------
/composeApp/src/desktopMain/kotlin/platform/RustFileDealWithFactory.kt:
--------------------------------------------------------------------------------
1 | package platform
2 |
3 | import androidx.annotation.FloatRange
4 | import androidx.annotation.IntRange
5 | import uniffi.toolkit.ToolKitRustException
6 |
7 | /**
8 | * 缩放图片
9 | * @param inputPath 输入路径
10 | * @param outputPath 输出路径
11 | * @param width 宽度
12 | * @param height 高度
13 | * @param typIdx 缩放算法 0 Triangle 1 Catrom 2 Mitchell 3 Lanczos3
14 | */
15 | @Throws(RustException::class)
16 | actual fun resizePng(inputPath: String, outputPath: String, width: UInt, height: UInt, typIdx: UByte) {
17 | try {
18 | uniffi.toolkit.resizePng(
19 | inputPath = inputPath, outputPath = outputPath, dstWidth = width, dstHeight = height, typIdx = typIdx
20 | )
21 | } catch (e: ToolKitRustException) {
22 | e.printStackTrace()
23 | throw RustException(e.message)
24 | }
25 | }
26 |
27 | /**
28 | * 缩放图片
29 | * @param inputPath 输入路径
30 | * @param outputPath 输出路径
31 | * @param width 宽度
32 | * @param height 高度
33 | */
34 | @Throws(RustException::class)
35 | actual fun resizeFir(inputPath: String, outputPath: String, width: UInt, height: UInt, typIdx: UByte) {
36 | try {
37 | uniffi.toolkit.resizeFir(
38 | inputPath = inputPath, outputPath = outputPath, dstWidth = width, dstHeight = height, typIdx = typIdx
39 | )
40 | } catch (e: ToolKitRustException) {
41 | e.printStackTrace()
42 | throw RustException(e.message)
43 | }
44 | }
45 |
46 | /**
47 | * 量化(有损)并压缩图片
48 | * @param inputPath 输入路径
49 | * @param outputPath 输出路径
50 | * @param minimum 最低限度
51 | * @param target 目标质量 如果不能满足最低质量,量化将因错误而中止。默认值为最小值 0,最大值 100,表示尽力而为,并且永不中止该过程。
52 | * 如果最大值小于 100,则库将尝试使用较少的颜色。颜色较少的图像并不总是较小,因为它会导致抖动增加。
53 | * @param speed 速度 1 - 10 更快的速度会生成质量较低的图像,但可能对于实时生成图像有用
54 | * @param preset 预设 1 - 6 预设越高、速度越慢、压缩效果越好
55 | */
56 | @Throws(RustException::class)
57 | actual fun quantize(
58 | inputPath: String,
59 | outputPath: String,
60 | @IntRange(from = 0, to = 100) minimum: Int,
61 | @IntRange(from = 30, to = 100) target: Int,
62 | @IntRange(from = 1, to = 10) speed: Int,
63 | @IntRange(from = 0, to = 6) preset: Int
64 | ) {
65 | try {
66 | uniffi.toolkit.quantize(
67 | inputPath = inputPath,
68 | outputPath = outputPath,
69 | minimum = minimum.toUByte(),
70 | target = target.toUByte(),
71 | speed = speed,
72 | preset = preset.toUByte()
73 | )
74 | } catch (e: ToolKitRustException) {
75 | e.printStackTrace()
76 | throw RustException(e.message)
77 | }
78 | }
79 |
80 | /**
81 | * 无损压缩PNG
82 | * @param inputPath 输入路径
83 | * @param outputPath 输出路径
84 | * @param preset 预设 1 - 6 预设越高、速度越慢、压缩效果越好
85 | */
86 | @Throws(RustException::class)
87 | actual fun oxipng(
88 | inputPath: String,
89 | outputPath: String,
90 | @IntRange(from = 0, to = 6) preset: Int
91 | ) {
92 | try {
93 | uniffi.toolkit.oxipng(
94 | inputPath = inputPath,
95 | outputPath = outputPath,
96 | preset = preset.toUByte()
97 | )
98 | } catch (e: ToolKitRustException) {
99 | e.printStackTrace()
100 | throw RustException(e.message)
101 | }
102 | }
103 |
104 |
105 | /**
106 | * 压缩图片
107 | * @param inputPath 输入路径
108 | * @param outputPath 输出路径
109 | * @param quality 图像质量。建议值为 60-80
110 | */
111 | @Throws(RustException::class)
112 | actual fun mozJpeg(inputPath: String, outputPath: String, @FloatRange(from = 0.0, to = 100.0) quality: Float) {
113 | try {
114 | uniffi.toolkit.mozJpeg(
115 | inputPath = inputPath, outputPath = outputPath, quality = quality
116 | )
117 | } catch (e: ToolKitRustException) {
118 | e.printStackTrace()
119 | throw RustException(e.message)
120 | }
121 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.code.style=official
2 | #Gradle
3 | org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
4 |
5 | #Development
6 | development=true
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | compose = "1.6.11"
3 | compose-compiler = "1.5.8"
4 | compose-plugin = "1.8.1"
5 | junit = "4.13.2"
6 | kotlin = "2.1.21"
7 | apksig = "8.9.1"
8 | tools = "31.9.1"
9 | slf4j = "2.0.17"
10 | buildconfig = "5.6.2"
11 | commons-codec = "1.18.0"
12 | asm = "9.8"
13 | jna = "5.17.0"
14 | logging = "7.0.0"
15 | lifecycle = "2.8.4"
16 | coroutines = "1.10.2"
17 | filekit = "0.10.0-beta04"
18 | multiplatform-settings = "1.3.0"
19 | kotlinx-serialization-json = "1.8.1"
20 | kotlinx-datetime = "0.6.2"
21 | about-libraries = "12.1.2"
22 | hot-reload = "1.0.0-alpha10"
23 | foojay = "0.10.0"
24 | coil = "3.2.0"
25 | zoomimage = "1.3.0"
26 |
27 | [libraries]
28 | kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
29 | kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
30 | junit = { group = "junit", name = "junit", version.ref = "junit" }
31 | compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
32 | compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
33 | compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" }
34 | compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" }
35 | compose-material = { module = "androidx.compose.material:material", version.ref = "compose" }
36 | compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "compose" }
37 | compose-material-icons-extended-desktop = { module = "androidx.compose.material:material-icons-extended-desktop", version.ref = "compose" }
38 | # https://mvnrepository.com/artifact/org.jetbrains.androidx.lifecycle/lifecycle-viewmodel-compose
39 | lifecycle-viewmodel-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" }
40 | # https://mvnrepository.com/artifact/com.android.tools.build/apksig
41 | android-apksig = { module = "com.android.tools.build:apksig", version.ref = "apksig" }
42 | # https://mvnrepository.com/artifact/com.android.tools/sdk-common
43 | android-sdk-common = { module = "com.android.tools:sdk-common", version.ref = "tools" }
44 | # https://mvnrepository.com/artifact/com.android.tools.apkparser/binary-resources
45 | android-binary-resources = { module = "com.android.tools.apkparser:binary-resources", version.ref = "tools" }
46 | # https://mvnrepository.com/artifact/org.slf4j/slf4j-api
47 | slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
48 | slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
49 | # https://mvnrepository.com/artifact/commons-codec/commons-codec
50 | commons-codec = { module = "commons-codec:commons-codec", version.ref = "commons-codec" }
51 | # https://mvnrepository.com/artifact/org.ow2.asm/asm
52 | asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
53 | # https://mvnrepository.com/artifact/net.java.dev.jna/jna
54 | jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
55 | logging = { module = "io.github.oshai:kotlin-logging-jvm", version.ref = "logging" }
56 | # https://github.com/Kotlin/kotlinx.coroutines
57 | kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" }
58 | # https://github.com/vinceglb/FileKit
59 | filekit-core = { module = "io.github.vinceglb:filekit-core", version.ref = "filekit" }
60 | filekit-dialogs = { module = "io.github.vinceglb:filekit-dialogs", version.ref = "filekit" }
61 | filekit-dialogs-compose = { module = "io.github.vinceglb:filekit-dialogs-compose", version.ref = "filekit" }
62 | # https://github.com/russhwolf/multiplatform-settings
63 | multiplatform-settings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatform-settings" }
64 | multiplatform-settings-coroutines = { module = "com.russhwolf:multiplatform-settings-coroutines", version.ref = "multiplatform-settings" }
65 | multiplatform-settings-serialization = { module = "com.russhwolf:multiplatform-settings-serialization", version.ref = "multiplatform-settings" }
66 | # https://github.com/Kotlin/kotlinx.serialization
67 | kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
68 | # https://github.com/Kotlin/kotlinx-datetime
69 | kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }
70 | # https://github.com/mikepenz/AboutLibraries
71 | about-libraries-core = { module = "com.mikepenz:aboutlibraries-core", version.ref = "about-libraries" }
72 | about-libraries-compose-m3 = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "about-libraries" }
73 | # https://github.com/coil-kt/coil
74 | coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
75 | # https://github.com/panpf/zoomimage
76 | zoomimage-compose-coil3 = { module = "io.github.panpf.zoomimage:zoomimage-compose-coil3", version.ref = "zoomimage" }
77 |
78 | [plugins]
79 | jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" }
80 | kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
81 | # https://github.com/gmazzo/gradle-buildconfig-plugin
82 | githubBuildconfig = { id = "com.github.gmazzo.buildconfig", version.ref = "buildconfig" }
83 | kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
84 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
85 | about-libraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "about-libraries" }
86 | # https://github.com/JetBrains/compose-hot-reload
87 | hot-reload = { id = "org.jetbrains.compose.hot-reload", version.ref = "hot-reload" }
88 | foojay-resolver-convention = { id = "org.gradle.toolchains.foojay-resolver-convention", version.ref = "foojay" }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
87 |
88 | # Use the maximum available, or set MAX_FD != -1 to use that value.
89 | MAX_FD=maximum
90 |
91 | warn () {
92 | echo "$*"
93 | } >&2
94 |
95 | die () {
96 | echo
97 | echo "$*"
98 | echo
99 | exit 1
100 | } >&2
101 |
102 | # OS specific support (must be 'true' or 'false').
103 | cygwin=false
104 | msys=false
105 | darwin=false
106 | nonstop=false
107 | case "$( uname )" in #(
108 | CYGWIN* ) cygwin=true ;; #(
109 | Darwin* ) darwin=true ;; #(
110 | MSYS* | MINGW* ) msys=true ;; #(
111 | NONSTOP* ) nonstop=true ;;
112 | esac
113 |
114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
115 |
116 |
117 | # Determine the Java command to use to start the JVM.
118 | if [ -n "$JAVA_HOME" ] ; then
119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
120 | # IBM's JDK on AIX uses strange locations for the executables
121 | JAVACMD=$JAVA_HOME/jre/sh/java
122 | else
123 | JAVACMD=$JAVA_HOME/bin/java
124 | fi
125 | if [ ! -x "$JAVACMD" ] ; then
126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
127 |
128 | Please set the JAVA_HOME variable in your environment to match the
129 | location of your Java installation."
130 | fi
131 | else
132 | JAVACMD=java
133 | if ! command -v java >/dev/null 2>&1
134 | then
135 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
136 |
137 | Please set the JAVA_HOME variable in your environment to match the
138 | location of your Java installation."
139 | fi
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
147 | # shellcheck disable=SC3045
148 | MAX_FD=$( ulimit -H -n ) ||
149 | warn "Could not query maximum file descriptor limit"
150 | esac
151 | case $MAX_FD in #(
152 | '' | soft) :;; #(
153 | *)
154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
155 | # shellcheck disable=SC3045
156 | ulimit -n "$MAX_FD" ||
157 | warn "Could not set maximum file descriptor limit to $MAX_FD"
158 | esac
159 | fi
160 |
161 | # Collect all arguments for the java command, stacking in reverse order:
162 | # * args from the command line
163 | # * the main class name
164 | # * -classpath
165 | # * -D...appname settings
166 | # * --module-path (only if needed)
167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
168 |
169 | # For Cygwin or MSYS, switch paths to Windows format before running java
170 | if "$cygwin" || "$msys" ; then
171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
173 |
174 | JAVACMD=$( cygpath --unix "$JAVACMD" )
175 |
176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
177 | for arg do
178 | if
179 | case $arg in #(
180 | -*) false ;; # don't mess with options #(
181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
182 | [ -e "$t" ] ;; #(
183 | *) false ;;
184 | esac
185 | then
186 | arg=$( cygpath --path --ignore --mixed "$arg" )
187 | fi
188 | # Roll the args list around exactly as many times as the number of
189 | # args, so each arg winds up back in the position where it started, but
190 | # possibly modified.
191 | #
192 | # NB: a `for` loop captures its iteration list before it begins, so
193 | # changing the positional parameters here affects neither the number of
194 | # iterations, nor the values presented in `arg`.
195 | shift # remove old arg
196 | set -- "$@" "$arg" # push replacement arg
197 | done
198 | fi
199 |
200 |
201 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
202 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
203 |
204 | # Collect all arguments for the java command;
205 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
206 | # shell script including quotes and variable substitutions, so put them in
207 | # double quotes to make sure that they get re-expanded; and
208 | # * put everything else in single quotes, so that it's not re-expanded.
209 |
210 | set -- \
211 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
212 | -classpath "$CLASSPATH" \
213 | org.gradle.wrapper.GradleWrapperMain \
214 | "$@"
215 |
216 | # Stop when "xargs" is not available.
217 | if ! command -v xargs >/dev/null 2>&1
218 | then
219 | die "xargs is not available"
220 | fi
221 |
222 | # Use "xargs" to parse quoted args.
223 | #
224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
225 | #
226 | # In Bash we could simply go:
227 | #
228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
229 | # set -- "${ARGS[@]}" "$@"
230 | #
231 | # but POSIX shell has neither arrays nor command substitution, so instead we
232 | # post-process each arg (as a line of input to sed) to backslash-escape any
233 | # character that might be a shell metacharacter, then use eval to reverse
234 | # that process (while maintaining the separation between arguments), and wrap
235 | # the whole thing up as a single "set" statement.
236 | #
237 | # This will of course break if any of these variables contains a newline or
238 | # an unmatched quote.
239 | #
240 |
241 | eval "set -- $(
242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
243 | xargs -n1 |
244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
245 | tr '\n' ' '
246 | )" '"$@"'
247 |
248 | exec "$JAVACMD" "$@"
249 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/rs/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "toolkit-rs"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [lib]
7 | crate-type = ["cdylib"]
8 | name = "toolkit_rs"
9 |
10 | [dependencies]
11 | uniffi = { version = "0.29.2", features = [ "cli" ] }
12 | image = "0.25.6"
13 | imagequant = "4.3.4"
14 | thiserror = "2.0.12"
15 | lodepng = "3.11.0"
16 | fast_image_resize = { version = "5.1.4", features = ["image"] }
17 | mozjpeg = "0.10.13"
18 | oxipng = { version = "9.1.5", features = ["parallel", "zopfli", "filetime"], default-features = false }
19 | rgb = { version = "0.8.50", default-features = false }
20 | resize = "0.8.8"
21 |
22 | [build-dependencies]
23 | uniffi = { version = "0.29.2", features = ["build"] }
24 |
25 | [[bin]]
26 | name = "uniffi-bindgen"
27 | path = "uniffi-bindgen.rs"
28 |
--------------------------------------------------------------------------------
/rs/build.rs:
--------------------------------------------------------------------------------
1 | include!("./src/srgb.rs");
2 |
3 | use std::io::Write;
4 |
5 | fn main() -> std::io::Result<()> {
6 | let mut srgb_to_linear_lut = String::from("static SRGB_TO_LINEAR_LUT: [f32; 256] = [");
7 | for i in 0..256 {
8 | srgb_to_linear_lut.push_str(&format!("{0:.7}", srgb_to_linear((i as f32) / 255.0)));
9 | srgb_to_linear_lut.push_str(",");
10 | }
11 | srgb_to_linear_lut.pop().unwrap();
12 | srgb_to_linear_lut.push_str("];");
13 |
14 | let mut file = std::fs::File::create("src/lut.inc")?;
15 | file.write_all(srgb_to_linear_lut.as_bytes())?;
16 |
17 | uniffi::generate_scaffolding("src/toolkit.udl").unwrap();
18 |
19 | Ok(())
20 | }
21 |
--------------------------------------------------------------------------------
/rs/src/srgb.rs:
--------------------------------------------------------------------------------
1 | #[allow(dead_code)]
2 | pub trait Clamp: PartialOrd + Sized {
3 | fn clamp(self, min: Self, max: Self) -> Self {
4 | if self.lt(&min) {
5 | min
6 | } else if self.gt(&max) {
7 | max
8 | } else {
9 | self
10 | }
11 | }
12 | }
13 |
14 | impl Clamp for f32 {}
15 |
16 | #[allow(dead_code)]
17 | pub fn srgb_to_linear(v: f32) -> f32 {
18 | if v < 0.04045 {
19 | v / 12.92
20 | } else {
21 | ((v + 0.055) / 1.055).powf(2.4).clamp(0.0, 1.0)
22 | }
23 | }
24 |
25 | pub fn linear_to_srgb(v: f32) -> f32 {
26 | if v < 0.0031308 {
27 | v * 12.92
28 | } else {
29 | (1.055 * v.powf(1.0 / 2.4) - 0.055).clamp(0.0, 1.0)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/rs/src/toolkit.udl:
--------------------------------------------------------------------------------
1 | namespace toolkit {
2 | [Throws=ToolKitRustError]
3 | void resize_fir(string input_path, string output_path, u32 dst_width, u32 dst_height, u8 typ_idx);
4 | [Throws=ToolKitRustError]
5 | void resize_png(string input_path, string output_path, u32 dst_width, u32 dst_height, u8 typ_idx);
6 | [Throws=ToolKitRustError]
7 | void quantize(string input_path, string output_path, u8 minimum, u8 target, i32 speed, u8 preset);
8 | [Throws=ToolKitRustError]
9 | void oxipng(string input_path, string output_path, u8 preset);
10 | [Throws=ToolKitRustError]
11 | void moz_jpeg(string input_path, string output_path, float quality);
12 | };
13 |
14 | [Error]
15 | enum ToolKitRustError {
16 | "Error"
17 | };
--------------------------------------------------------------------------------
/rs/uniffi-bindgen.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | uniffi::uniffi_bindgen_main()
3 | }
4 |
--------------------------------------------------------------------------------
/screenshots/fail_to_quantize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/fail_to_quantize.png
--------------------------------------------------------------------------------
/screenshots/screenshot_apk_information_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_apk_information_1.png
--------------------------------------------------------------------------------
/screenshots/screenshot_apk_information_1_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_apk_information_1_en.png
--------------------------------------------------------------------------------
/screenshots/screenshot_apk_signature_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_apk_signature_1.png
--------------------------------------------------------------------------------
/screenshots/screenshot_apk_signature_1_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_apk_signature_1_en.png
--------------------------------------------------------------------------------
/screenshots/screenshot_cache_clear_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_cache_clear_0.png
--------------------------------------------------------------------------------
/screenshots/screenshot_cache_clear_0_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_cache_clear_0_en.png
--------------------------------------------------------------------------------
/screenshots/screenshot_cache_clear_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_cache_clear_1.png
--------------------------------------------------------------------------------
/screenshots/screenshot_cache_clear_1_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_cache_clear_1_en.png
--------------------------------------------------------------------------------
/screenshots/screenshot_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_dark.png
--------------------------------------------------------------------------------
/screenshots/screenshot_dark_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_dark_en.png
--------------------------------------------------------------------------------
/screenshots/screenshot_icon_factory_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_icon_factory_1.png
--------------------------------------------------------------------------------
/screenshots/screenshot_icon_factory_1_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_icon_factory_1_en.png
--------------------------------------------------------------------------------
/screenshots/screenshot_icon_factory_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_icon_factory_2.png
--------------------------------------------------------------------------------
/screenshots/screenshot_icon_factory_2_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_icon_factory_2_en.png
--------------------------------------------------------------------------------
/screenshots/screenshot_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_light.png
--------------------------------------------------------------------------------
/screenshots/screenshot_light_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_light_en.png
--------------------------------------------------------------------------------
/screenshots/screenshot_signature_generation_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_signature_generation_1.png
--------------------------------------------------------------------------------
/screenshots/screenshot_signature_generation_1_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_signature_generation_1_en.png
--------------------------------------------------------------------------------
/screenshots/screenshot_signature_information_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_signature_information_1.png
--------------------------------------------------------------------------------
/screenshots/screenshot_signature_information_1_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_signature_information_1_en.png
--------------------------------------------------------------------------------
/screenshots/screenshot_signature_information_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_signature_information_2.png
--------------------------------------------------------------------------------
/screenshots/screenshot_signature_information_2_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_signature_information_2_en.png
--------------------------------------------------------------------------------
/screenshots/screenshot_signature_information_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_signature_information_3.png
--------------------------------------------------------------------------------
/screenshots/screenshot_signature_information_3_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/screenshot_signature_information_3_en.png
--------------------------------------------------------------------------------
/screenshots/unopen_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/unopen_1.png
--------------------------------------------------------------------------------
/screenshots/unopen_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/unopen_2.png
--------------------------------------------------------------------------------
/screenshots/unopen_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LazyIonEs/AndroidToolKit/e5257457e5eac74789798ad3e0d0c42aaacd83dc/screenshots/unopen_3.png
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "AndroidToolKit"
2 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
3 |
4 | pluginManagement {
5 | plugins {
6 | // https://github.com/JetBrains/compose-hot-reload
7 | id("org.jetbrains.compose.hot-reload") version "1.0.0-alpha09"
8 | }
9 | repositories {
10 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
11 | google()
12 | gradlePluginPortal()
13 | mavenCentral()
14 | }
15 | }
16 |
17 | plugins {
18 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0"
19 | }
20 |
21 | dependencyResolutionManagement {
22 | repositories {
23 | google()
24 | mavenCentral()
25 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
26 | maven("https://www.jetbrains.com/intellij-repository/releases")
27 | }
28 | }
29 |
30 | include(":composeApp")
--------------------------------------------------------------------------------