mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-01-15 23:33:58 +00:00
Compare commits
383 Commits
compose-de
...
feat/toolt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
239b03584c | ||
|
|
1dcd3d8aa2 | ||
|
|
9b53ffd7cf | ||
|
|
fc96137567 | ||
|
|
244674a603 | ||
|
|
47e4ed8336 | ||
|
|
486ed5967f | ||
|
|
789f9ec867 | ||
|
|
b51d1ee47a | ||
|
|
05afc6bd0c | ||
|
|
a2caffb089 | ||
|
|
71b0fd48ab | ||
|
|
438fa485bd | ||
|
|
7148ee66f8 | ||
|
|
1510aa58f2 | ||
|
|
f7a4ae5791 | ||
|
|
cb2dbbee24 | ||
|
|
578dcce9b6 | ||
|
|
8c6c0f3c76 | ||
|
|
979a2dc410 | ||
|
|
baa9122a88 | ||
|
|
111c74329f | ||
|
|
b70fc03bc7 | ||
|
|
81a4ebd327 | ||
|
|
5fc54eb53b | ||
|
|
88b0b8c078 | ||
|
|
7959c36e71 | ||
|
|
e9542c6cf0 | ||
|
|
0992e63c28 | ||
|
|
aebad0b0e2 | ||
|
|
c0d7cf7c2c | ||
|
|
2c1ff4d2cd | ||
|
|
7c5552f93f | ||
|
|
1c5373ff61 | ||
|
|
e629d2df0c | ||
|
|
7ca003a30d | ||
|
|
70a695017e | ||
|
|
9b2c99da05 | ||
|
|
07158ae1d1 | ||
|
|
2b380b0d7c | ||
|
|
5153e5e0cb | ||
|
|
a1f5dd3c26 | ||
|
|
2b0784865a | ||
|
|
d7c0913277 | ||
|
|
18199bb968 | ||
|
|
9d329e0f54 | ||
|
|
e9fcb4a383 | ||
|
|
68f74f1651 | ||
|
|
6264800a05 | ||
|
|
658699dd81 | ||
|
|
222089a7ec | ||
|
|
eff5c4860b | ||
|
|
d4e60acbaa | ||
|
|
1ab74acf1d | ||
|
|
28aad879ba | ||
|
|
9f44541bbd | ||
|
|
02d2153195 | ||
|
|
39e821738f | ||
|
|
7863fbb604 | ||
|
|
94ab6996ae | ||
|
|
1319a03651 | ||
|
|
f93085f782 | ||
|
|
40a4317993 | ||
|
|
8095a1f963 | ||
|
|
90c7600586 | ||
|
|
105492bfa5 | ||
|
|
ce63b799e6 | ||
|
|
2fe2d46c72 | ||
|
|
bc3888da79 | ||
|
|
52b982d81f | ||
|
|
ab48672621 | ||
|
|
0027c90ed3 | ||
|
|
b6ad686a26 | ||
|
|
f59d57499d | ||
|
|
5f65c12ec1 | ||
|
|
65e44dc5a8 | ||
|
|
e6ed4a88c9 | ||
|
|
390e3533c9 | ||
|
|
c1ff2f9924 | ||
|
|
d084925c0f | ||
|
|
3cf540f190 | ||
|
|
5514c75061 | ||
|
|
ca147cc6dc | ||
|
|
3c3e995f31 | ||
|
|
67809700c7 | ||
|
|
32c7eddb48 | ||
|
|
756e3a815f | ||
|
|
f8f915563e | ||
|
|
2733ce4915 | ||
|
|
04a78fabff | ||
|
|
94e26ba053 | ||
|
|
1704947c52 | ||
|
|
e027f8cc9c | ||
|
|
37e612febc | ||
|
|
374531237f | ||
|
|
abe5a20c4a | ||
|
|
0a29ff48ca | ||
|
|
95cffcc0a0 | ||
|
|
45ff64f26e | ||
|
|
7973b367ec | ||
|
|
641f6af6da | ||
|
|
6d142e72a6 | ||
|
|
e812f69740 | ||
|
|
df79e3d13a | ||
|
|
207b005d56 | ||
|
|
4a1695a766 | ||
|
|
737e709287 | ||
|
|
4727e8243c | ||
|
|
5e0ba77f4a | ||
|
|
a76a58d6ee | ||
|
|
4ebc33cd2a | ||
|
|
89a1a3026e | ||
|
|
16f16e859b | ||
|
|
83eb1a9fd7 | ||
|
|
9a336aa3ef | ||
|
|
d0b8cba2bf | ||
|
|
7436d99532 | ||
|
|
c982babaeb | ||
|
|
211f7d2fa2 | ||
|
|
d432ffbbe0 | ||
|
|
500cd63507 | ||
|
|
9404c3c297 | ||
|
|
f2f89aa185 | ||
|
|
d6e931a876 | ||
|
|
260964c633 | ||
|
|
87addbff55 | ||
|
|
5ff5298e0e | ||
|
|
0bb08c7afc | ||
|
|
a0e67a42e0 | ||
|
|
585d54a8a8 | ||
|
|
abdae89434 | ||
|
|
c7c4da54fb | ||
|
|
2dacfce61d | ||
|
|
f8da11e684 | ||
|
|
f384c66dd6 | ||
|
|
1a9031193c | ||
|
|
dc743021c3 | ||
|
|
a81913c2f7 | ||
|
|
494197b5dd | ||
|
|
285c55228d | ||
|
|
aedc6e9970 | ||
|
|
3ed2c87f45 | ||
|
|
d5b22258a6 | ||
|
|
e6361118a7 | ||
|
|
edf2f28eca | ||
|
|
b5abe1bbc3 | ||
|
|
8654da0dfe | ||
|
|
c48698334c | ||
|
|
f53299b2a6 | ||
|
|
8c1b8e1ee1 | ||
|
|
500e0ad9b7 | ||
|
|
0d6ee98609 | ||
|
|
231cf52f30 | ||
|
|
994cb6c4b0 | ||
|
|
7365fc241a | ||
|
|
e2f02ebf22 | ||
|
|
5150adeaff | ||
|
|
92612f9aec | ||
|
|
44b5f7b3bc | ||
|
|
46bd2f48a8 | ||
|
|
3ac2062992 | ||
|
|
95be465b39 | ||
|
|
226d9c9c23 | ||
|
|
dabf16a436 | ||
|
|
ff0bf43c7d | ||
|
|
016de8bb0d | ||
|
|
b6c02b7be1 | ||
|
|
9e7b26b1c8 | ||
|
|
05eb0d7457 | ||
|
|
4b0706f8b0 | ||
|
|
9c6f0c324b | ||
|
|
c98ca70e08 | ||
|
|
424fe25dfb | ||
|
|
666deda0b5 | ||
|
|
5e4510eed5 | ||
|
|
a9147ed0c0 | ||
|
|
bd7c4aa554 | ||
|
|
9d0f3a3605 | ||
|
|
093a4ebf49 | ||
|
|
17cc9f9e9e | ||
|
|
bd85b254e4 | ||
|
|
1460fd7be2 | ||
|
|
bc3fe3f0f2 | ||
|
|
bdc0fc89c3 | ||
|
|
aeefe644c2 | ||
|
|
48604804f9 | ||
|
|
03ccea46e2 | ||
|
|
b4bc14e4ed | ||
|
|
6dcbe271a7 | ||
|
|
3317fd5649 | ||
|
|
c9eb3ffa14 | ||
|
|
fe1e65ce9c | ||
|
|
f7426309b4 | ||
|
|
7f67a86413 | ||
|
|
a22ef4d9b8 | ||
|
|
ada99e80f9 | ||
|
|
111d8b6543 | ||
|
|
15599dbb21 | ||
|
|
24cd2dca75 | ||
|
|
c48f5b2488 | ||
|
|
06d4485032 | ||
|
|
73fdf92780 | ||
|
|
0fda344952 | ||
|
|
9c0665acb2 | ||
|
|
6bafa23bb4 | ||
|
|
8387ada245 | ||
|
|
048ba12703 | ||
|
|
1b6a77a463 | ||
|
|
ff9d021a2b | ||
|
|
6ac4819478 | ||
|
|
4b5e2e97f7 | ||
|
|
7a4b0bd7c8 | ||
|
|
205650865a | ||
|
|
39ff42db01 | ||
|
|
62f5acee1a | ||
|
|
67ecc13a28 | ||
|
|
ad10a19acd | ||
|
|
a35c62a99d | ||
|
|
8450243ddc | ||
|
|
d239efcf14 | ||
|
|
691b615b02 | ||
|
|
3f34407741 | ||
|
|
da4153039c | ||
|
|
1de59f420b | ||
|
|
464aa753f4 | ||
|
|
0cf49998e0 | ||
|
|
991a8cb5d1 | ||
|
|
ac75d1da27 | ||
|
|
5907659cc8 | ||
|
|
d8d2478d0f | ||
|
|
4ad3c3fb72 | ||
|
|
0e00b9f526 | ||
|
|
88f3701a6c | ||
|
|
a15924617e | ||
|
|
db04672d72 | ||
|
|
977345e5aa | ||
|
|
76e5731eb8 | ||
|
|
99bfd84e03 | ||
|
|
edb387e1a8 | ||
|
|
80ff6711f4 | ||
|
|
cadbb3f46d | ||
|
|
e1742fd4c0 | ||
|
|
c2c4895a29 | ||
|
|
ffe5c058e0 | ||
|
|
59ddd9f393 | ||
|
|
57ba3ad374 | ||
|
|
6fed17705b | ||
|
|
f915b544c4 | ||
|
|
11a383a13a | ||
|
|
4b178d947c | ||
|
|
f2e7661b5c | ||
|
|
e33862f436 | ||
|
|
51dc429330 | ||
|
|
5a41cc1162 | ||
|
|
28ab79d962 | ||
|
|
8f2c18585f | ||
|
|
2f533d12b9 | ||
|
|
44cec48a7f | ||
|
|
395da595a2 | ||
|
|
18ea6adb20 | ||
|
|
daeb534692 | ||
|
|
0b2ddbe0bf | ||
|
|
3c3ff64b18 | ||
|
|
43befa8713 | ||
|
|
3c820405a8 | ||
|
|
d65e830467 | ||
|
|
154b23202c | ||
|
|
91e0d48721 | ||
|
|
ae5eef0f2c | ||
|
|
382c068a03 | ||
|
|
cfaf874326 | ||
|
|
97d25b5602 | ||
|
|
cde470f867 | ||
|
|
1ad3e3423c | ||
|
|
d62f0a96fb | ||
|
|
d4ee3334e0 | ||
|
|
384fb19ddf | ||
|
|
ab04ef99c3 | ||
|
|
c812ce2011 | ||
|
|
fa8f154d65 | ||
|
|
af03eec4b5 | ||
|
|
da66b43497 | ||
|
|
e302ea9f9e | ||
|
|
6aa3b6c4b0 | ||
|
|
5cb887ebe6 | ||
|
|
4cd00c122d | ||
|
|
5744bdda80 | ||
|
|
89e373f98c | ||
|
|
ecd4b01108 | ||
|
|
08686252bb | ||
|
|
ab1dd8862d | ||
|
|
c4abf8a324 | ||
|
|
05adb78932 | ||
|
|
3ae1d3374a | ||
|
|
067f8adf4b | ||
|
|
83f6d287b3 | ||
|
|
3dde82fc18 | ||
|
|
de0af4c489 | ||
|
|
f98386dcc8 | ||
|
|
49209ca562 | ||
|
|
d68e7f71e9 | ||
|
|
fffdb314a1 | ||
|
|
ee41e315fb | ||
|
|
cd3d654318 | ||
|
|
3bd1ef3de7 | ||
|
|
ba1a152231 | ||
|
|
55573eb94f | ||
|
|
80e78f544b | ||
|
|
7572944c9e | ||
|
|
f5e9826dfb | ||
|
|
c8ac94d82d | ||
|
|
d9ff833100 | ||
|
|
7150fb4435 | ||
|
|
34c331f39b | ||
|
|
1ff76cf584 | ||
|
|
1de0e87983 | ||
|
|
93b2dd6176 | ||
|
|
f3e2435fef | ||
|
|
b42d8842d5 | ||
|
|
c052a0c0f5 | ||
|
|
34cf91d4b6 | ||
|
|
f99504d3e4 | ||
|
|
3ec1df9650 | ||
|
|
871a34df23 | ||
|
|
b65ec4560f | ||
|
|
0eaeb5d5ea | ||
|
|
060f39fb9b | ||
|
|
722dfadb3c | ||
|
|
6567be40cb | ||
|
|
9539d23c12 | ||
|
|
d0d0a17a55 | ||
|
|
d2e965f056 | ||
|
|
cda0e127d9 | ||
|
|
fea11dfef6 | ||
|
|
dcc4477e3e | ||
|
|
6eb21e1fab | ||
|
|
b8902d04d7 | ||
|
|
99efdb130f | ||
|
|
5177cd3083 | ||
|
|
ff4b9ab960 | ||
|
|
ad998ac22d | ||
|
|
881d2430c3 | ||
|
|
b07ae90c86 | ||
|
|
8e6519cfb0 | ||
|
|
bb90cc6e81 | ||
|
|
fd02e0799c | ||
|
|
f07204460c | ||
|
|
66be0f96e0 | ||
|
|
a1ca19b289 | ||
|
|
af779153d5 | ||
|
|
78966e13c4 | ||
|
|
8bdcf76832 | ||
|
|
05ecbde6c2 | ||
|
|
e558a47204 | ||
|
|
61de7568cb | ||
|
|
2e7f8457d3 | ||
|
|
332bad699d | ||
|
|
0b5ab33b3e | ||
|
|
5b4242d28b | ||
|
|
0c76ed3af0 | ||
|
|
39d698e545 | ||
|
|
18e91e7cbc | ||
|
|
14dfe07795 | ||
|
|
8e011a5d6b | ||
|
|
fc5f97e54b | ||
|
|
78728c1f2a | ||
|
|
90c95c0669 | ||
|
|
fbd1e221da | ||
|
|
c35c776ce2 | ||
|
|
f275f57c11 | ||
|
|
520b86df0a | ||
|
|
8991827ac7 | ||
|
|
0871180dcc | ||
|
|
7103bd2ec1 | ||
|
|
e5029c7d2c | ||
|
|
a512af50b5 | ||
|
|
cc59d60dfd | ||
|
|
4d894e908e | ||
|
|
77b499ef29 | ||
|
|
0142b85ede | ||
|
|
d9633906f5 | ||
|
|
3dd14fd34b | ||
|
|
0b19a9865d |
2
.github/workflows/build_pull_request.yml
vendored
2
.github/workflows/build_pull_request.yml
vendored
@@ -13,6 +13,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Cache Gradle
|
- name: Cache Gradle
|
||||||
uses: burrunan/gradle-cache-action@v1
|
uses: burrunan/gradle-cache-action@v1
|
||||||
|
|||||||
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
@@ -12,13 +12,14 @@ jobs:
|
|||||||
name: Release
|
name: Release
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
|
||||||
id-token: write
|
id-token: write
|
||||||
attestations: write
|
attestations: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
@@ -27,11 +28,10 @@ jobs:
|
|||||||
java-version: '17'
|
java-version: '17'
|
||||||
|
|
||||||
- name: Cache Gradle
|
- name: Cache Gradle
|
||||||
uses: burrunan/gradle-cache-action@v3
|
uses: burrunan/gradle-cache-action@v1
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
GITHUB_ACTOR: ${{ github.actor }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: ./gradlew assembleRelease
|
run: ./gradlew assembleRelease
|
||||||
|
|
||||||
@@ -55,26 +55,18 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "${{ secrets.KEYSTORE }}" | base64 --decode > "app/keystore.jks"
|
echo "${{ secrets.KEYSTORE }}" | base64 --decode > "app/keystore.jks"
|
||||||
|
|
||||||
- name: Release API
|
- name: Semantic Release
|
||||||
run: npx multi-semantic-release --tag-format 'api@${version}' --ignore-packages app
|
uses: cycjimmy/semantic-release-action@v4
|
||||||
|
id: semantic
|
||||||
env:
|
env:
|
||||||
GITHUB_ACTOR: ${{ github.actor }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Release
|
|
||||||
id: release
|
|
||||||
run: |
|
|
||||||
echo "NEW_TAG=$(npx multi-semantic-release --tag-format 'v${version}' --ignore-packages api | tee | grep 'Created tag ' | sed -E 's/.*Created tag ([^ ]+).*/\1/')" >> $GITHUB_OUTPUT
|
|
||||||
env:
|
|
||||||
GITHUB_ACTOR: ${{ github.actor }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||||
KEYSTORE_ENTRY_ALIAS: ${{ secrets.KEYSTORE_ENTRY_ALIAS }}
|
KEYSTORE_ENTRY_ALIAS: ${{ secrets.KEYSTORE_ENTRY_ALIAS }}
|
||||||
KEYSTORE_ENTRY_PASSWORD: ${{ secrets.KEYSTORE_ENTRY_PASSWORD }}
|
KEYSTORE_ENTRY_PASSWORD: ${{ secrets.KEYSTORE_ENTRY_PASSWORD }}
|
||||||
|
|
||||||
- name: Attest
|
- name: Attest
|
||||||
if: steps.release.outputs.NEW_TAG != ''
|
if: steps.semantic.outputs.new_release_published == 'true'
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v2
|
||||||
with:
|
with:
|
||||||
subject-name: 'ReVanced Manager ${{ steps.release.outputs.NEW_TAG }}'
|
subject-name: 'ReVanced Manager ${{ steps.release.outputs.new_release_git_tag }}'
|
||||||
subject-path: app/build/outputs/apk/release/revanced-manager*.apk
|
subject-path: app/build/outputs/apk/release/revanced-manager*.apk
|
||||||
|
|||||||
@@ -8,8 +8,7 @@
|
|||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
[
|
[
|
||||||
"@semantic-release/commit-analyzer",
|
"@semantic-release/commit-analyzer", {
|
||||||
{
|
|
||||||
"releaseRules": [
|
"releaseRules": [
|
||||||
{ "type": "build", "scope": "Needs bump", "release": "patch" }
|
{ "type": "build", "scope": "Needs bump", "release": "patch" }
|
||||||
]
|
]
|
||||||
@@ -23,7 +22,7 @@
|
|||||||
{
|
{
|
||||||
"assets": [
|
"assets": [
|
||||||
"CHANGELOG.md",
|
"CHANGELOG.md",
|
||||||
"gradle.properties"
|
"gradle.properties",
|
||||||
],
|
],
|
||||||
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||||
}
|
}
|
||||||
@@ -33,17 +32,17 @@
|
|||||||
{
|
{
|
||||||
"assets": [
|
"assets": [
|
||||||
{
|
{
|
||||||
"path": "build/outputs/apk/release/revanced-manager*.apk?(.asc)"
|
"path": "app/build/outputs/apk/release/revanced-manager*.apk?(.asc)"
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"successComment": false
|
successComment: false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"@saithodev/semantic-release-backmerge",
|
"@saithodev/semantic-release-backmerge",
|
||||||
{
|
{
|
||||||
"backmergeBranches": [{"from": "main", "to": "dev"}],
|
backmergeBranches: [{"from": "main", "to": "dev"}],
|
||||||
"clearWorkspace": true
|
clearWorkspace: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"info": "This is verification file for ads.fund project",
|
|
||||||
"project": {
|
|
||||||
"name": "ReVanced Manager",
|
|
||||||
"walletAddress": "0x7ab4091e00363654bf84B34151225742cd92FCE5",
|
|
||||||
"tokenAddress": "0xadf954bc6f509b3a32fb5e97ed4ba6c000e37155"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"branches": [
|
|
||||||
"main",
|
|
||||||
{
|
|
||||||
"name": "dev",
|
|
||||||
"prerelease": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
[
|
|
||||||
"@semantic-release/commit-analyzer",
|
|
||||||
{
|
|
||||||
"releaseRules": [
|
|
||||||
{ "type": "build", "scope": "Needs bump", "release": "patch" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@semantic-release/release-notes-generator",
|
|
||||||
"@semantic-release/changelog",
|
|
||||||
"gradle-semantic-release-plugin",
|
|
||||||
[
|
|
||||||
"@semantic-release/git",
|
|
||||||
{
|
|
||||||
"assets": [
|
|
||||||
"CHANGELOG.md",
|
|
||||||
"gradle.properties"
|
|
||||||
],
|
|
||||||
"message": "chore: Release API v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"@saithodev/semantic-release-backmerge",
|
|
||||||
{
|
|
||||||
"backmergeBranches": [{"from": "main", "to": "dev"}],
|
|
||||||
"clearWorkspace": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import java.io.IOException
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.library)
|
alias(libs.plugins.android.library)
|
||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
@@ -17,6 +19,43 @@ dependencies {
|
|||||||
implementation(libs.appcompat)
|
implementation(libs.appcompat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun String.runCommand(): String {
|
||||||
|
val process = ProcessBuilder(split("\\s".toRegex()))
|
||||||
|
.redirectErrorStream(true)
|
||||||
|
.directory(rootDir)
|
||||||
|
.start()
|
||||||
|
|
||||||
|
val output = StringBuilder()
|
||||||
|
val reader = process.inputStream.bufferedReader()
|
||||||
|
|
||||||
|
val thread = Thread {
|
||||||
|
reader.forEachLine {
|
||||||
|
output.appendLine(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
if (!process.waitFor(10, TimeUnit.SECONDS)) {
|
||||||
|
process.destroy()
|
||||||
|
throw IOException("Command timed out: $this")
|
||||||
|
}
|
||||||
|
|
||||||
|
thread.join()
|
||||||
|
return output.toString().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
val projectPath: String = projectDir.relativeTo(rootDir).path
|
||||||
|
val lastTag = "git describe --tags --abbrev=0".runCommand()
|
||||||
|
val hasChangesInThisModule = "git diff --name-only $lastTag..HEAD".runCommand().lineSequence().any {
|
||||||
|
it.startsWith(projectPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.matching { it.name.startsWith("publish") }.configureEach {
|
||||||
|
onlyIf {
|
||||||
|
hasChangesInThisModule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "app.revanced.manager.plugin.downloader"
|
namespace = "app.revanced.manager.plugin.downloader"
|
||||||
compileSdk = 35
|
compileSdk = 35
|
||||||
@@ -108,4 +147,4 @@ publishing {
|
|||||||
signing {
|
signing {
|
||||||
useGpgCmd()
|
useGpgCmd()
|
||||||
sign(publishing.publications["Api"])
|
sign(publishing.publications["Api"])
|
||||||
}
|
}
|
||||||
1
api/gradlew
vendored
1
api/gradlew
vendored
@@ -1 +0,0 @@
|
|||||||
../gradlew
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "api",
|
|
||||||
"private": false,
|
|
||||||
"devDependencies": {
|
|
||||||
"@anolilab/multi-semantic-release": "^1.1.10",
|
|
||||||
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
|
||||||
"@semantic-release/changelog": "^6.0.3",
|
|
||||||
"@semantic-release/git": "^10.0.1",
|
|
||||||
"gradle-semantic-release-plugin": "^1.10.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import io.github.z4kn4fein.semver.toVersion
|
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
@@ -110,16 +109,6 @@ dependencies {
|
|||||||
implementation(libs.compose.icons.fontawesome)
|
implementation(libs.compose.icons.fontawesome)
|
||||||
}
|
}
|
||||||
|
|
||||||
buildscript {
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
// Semantic versioning string parser
|
|
||||||
classpath(libs.semver.parser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "app.revanced.manager"
|
namespace = "app.revanced.manager"
|
||||||
compileSdk = 35
|
compileSdk = 35
|
||||||
@@ -129,15 +118,8 @@ android {
|
|||||||
applicationId = "app.revanced.manager"
|
applicationId = "app.revanced.manager"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
|
versionCode = 1
|
||||||
val versionStr = if (version == "unspecified") "1.0.0" else version.toString()
|
versionName = "0.0.1"
|
||||||
versionName = versionStr
|
|
||||||
versionCode = with(versionStr.toVersion()) {
|
|
||||||
major * 10_000_000 +
|
|
||||||
minor * 10_000 +
|
|
||||||
patch * 100 +
|
|
||||||
(preRelease?.substringAfterLast('.')?.toInt() ?: 99)
|
|
||||||
}
|
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
app/gradlew
vendored
1
app/gradlew
vendored
@@ -1 +0,0 @@
|
|||||||
../gradlew
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "app",
|
|
||||||
"private": false,
|
|
||||||
"devDependencies": {
|
|
||||||
"@anolilab/multi-semantic-release": "^1.1.10",
|
|
||||||
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
|
||||||
"@semantic-release/changelog": "^6.0.3",
|
|
||||||
"@semantic-release/git": "^10.0.1",
|
|
||||||
"gradle-semantic-release-plugin": "^1.10.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
67
app/proguard-rules.pro
vendored
67
app/proguard-rules.pro
vendored
@@ -1,14 +1,63 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.kts.kts.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
-dontobfuscate
|
-dontobfuscate
|
||||||
|
|
||||||
-keep class app.revanced.manager.patcher.runtime.process.* { *; }
|
# Required for serialization to work properly
|
||||||
-keep class app.revanced.manager.plugin.** { *; }
|
-if @kotlinx.serialization.Serializable class **
|
||||||
-keep class app.revanced.patcher.** { *; }
|
-keepclassmembers class <1> {
|
||||||
-keep class com.android.tools.smali.** { *; }
|
static <1>$Companion Companion;
|
||||||
-keep class kotlin.** { *; }
|
}
|
||||||
-keepnames class com.android.apksig.internal.** { *; }
|
-if @kotlinx.serialization.Serializable class ** {
|
||||||
-keepnames class org.xmlpull.** { *; }
|
static **$* *;
|
||||||
|
}
|
||||||
|
-keepclassmembers class <2>$<3> {
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
-if @kotlinx.serialization.Serializable class ** {
|
||||||
|
public static ** INSTANCE;
|
||||||
|
}
|
||||||
|
-keepclassmembers class <1> {
|
||||||
|
public static <1> INSTANCE;
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
|
||||||
-dontwarn com.google.j2objc.annotations.*
|
# This required for the process runtime.
|
||||||
|
-keep class app.revanced.manager.patcher.runtime.process.* {
|
||||||
|
*;
|
||||||
|
}
|
||||||
|
# Required for the patcher to function correctly
|
||||||
|
-keep class app.revanced.patcher.** {
|
||||||
|
*;
|
||||||
|
}
|
||||||
|
-keep class brut.** {
|
||||||
|
*;
|
||||||
|
}
|
||||||
|
-keep class org.xmlpull.** {
|
||||||
|
*;
|
||||||
|
}
|
||||||
|
-keep class kotlin.** {
|
||||||
|
*;
|
||||||
|
}
|
||||||
|
-keep class org.jf.** {
|
||||||
|
*;
|
||||||
|
}
|
||||||
|
-keep class com.android.** {
|
||||||
|
*;
|
||||||
|
}
|
||||||
|
-keep class app.revanced.manager.plugin.** {
|
||||||
|
*;
|
||||||
|
}
|
||||||
|
|
||||||
|
-dontwarn com.google.auto.value.**
|
||||||
-dontwarn java.awt.**
|
-dontwarn java.awt.**
|
||||||
-dontwarn javax.**
|
-dontwarn javax.**
|
||||||
-dontwarn org.slf4j.**
|
-dontwarn org.slf4j.**
|
||||||
|
-dontwarn it.skrape.fetcher.*
|
||||||
|
-dontwarn com.google.j2objc.annotations.*
|
||||||
|
|
||||||
|
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
|
||||||
@@ -6,7 +6,6 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -22,6 +21,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -69,7 +69,10 @@ fun AppTopBar(
|
|||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
if (onBackClick != null) {
|
if (onBackClick != null) {
|
||||||
IconButton(onClick = onBackClick) {
|
TooltipIconButton(
|
||||||
|
onClick = onBackClick,
|
||||||
|
tooltip = stringResource(R.string.back),
|
||||||
|
) {
|
||||||
backIcon()
|
backIcon()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +111,10 @@ fun AppTopBar(
|
|||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
if (onBackClick != null) {
|
if (onBackClick != null) {
|
||||||
IconButton(onClick = onBackClick) {
|
TooltipIconButton(
|
||||||
|
onClick = onBackClick,
|
||||||
|
tooltip = stringResource(R.string.back),
|
||||||
|
) {
|
||||||
backIcon()
|
backIcon()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import androidx.compose.animation.core.animateFloatAsState
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ArrowButton(
|
fun ArrowButton(
|
||||||
@@ -27,7 +27,11 @@ fun ArrowButton(
|
|||||||
)
|
)
|
||||||
|
|
||||||
onClick?.let {
|
onClick?.let {
|
||||||
IconButton(onClick = it) {
|
TooltipIconButton(
|
||||||
|
modifier = Modifier,
|
||||||
|
onClick = it,
|
||||||
|
tooltip = stringResource(description),
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.KeyboardArrowUp,
|
imageVector = Icons.Filled.KeyboardArrowUp,
|
||||||
contentDescription = stringResource(description),
|
contentDescription = stringResource(description),
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|||||||
import androidx.compose.material.icons.outlined.Share
|
import androidx.compose.material.icons.outlined.Share
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -18,6 +17,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.ui.component.bundle.BundleTopBar
|
import app.revanced.manager.ui.component.bundle.BundleTopBar
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -39,7 +39,8 @@ fun ExceptionViewerDialog(text: String, onDismiss: () -> Unit) {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
|
modifier = Modifier,
|
||||||
onClick = {
|
onClick = {
|
||||||
val sendIntent: Intent = Intent().apply {
|
val sendIntent: Intent = Intent().apply {
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
@@ -52,7 +53,8 @@ fun ExceptionViewerDialog(text: String, onDismiss: () -> Unit) {
|
|||||||
|
|
||||||
val shareIntent = Intent.createChooser(sendIntent, null)
|
val shareIntent = Intent.createChooser(sendIntent, null)
|
||||||
context.startActivity(shareIntent)
|
context.startActivity(shareIntent)
|
||||||
}
|
},
|
||||||
|
tooltip = stringResource(R.string.share),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Outlined.Share,
|
Icons.Outlined.Share,
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import androidx.compose.material.icons.outlined.Close
|
|||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -25,6 +24,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NotificationCard(
|
fun NotificationCard(
|
||||||
@@ -138,7 +138,10 @@ fun NotificationCard(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (onDismiss != null) {
|
if (onDismiss != null) {
|
||||||
IconButton(onClick = onDismiss) {
|
TooltipIconButton(
|
||||||
|
onClick = onDismiss,
|
||||||
|
tooltip = stringResource(R.string.close),
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.Close,
|
imageVector = Icons.Outlined.Close,
|
||||||
contentDescription = stringResource(R.string.close),
|
contentDescription = stringResource(R.string.close),
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.outlined.Visibility
|
import androidx.compose.material.icons.outlined.Visibility
|
||||||
import androidx.compose.material.icons.outlined.VisibilityOff
|
import androidx.compose.material.icons.outlined.VisibilityOff
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -19,6 +18,7 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
import androidx.compose.ui.text.input.VisualTransformation
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PasswordField(modifier: Modifier = Modifier, value: String, onValueChange: (String) -> Unit, label: @Composable (() -> Unit)? = null, placeholder: @Composable (() -> Unit)? = null) {
|
fun PasswordField(modifier: Modifier = Modifier, value: String, onValueChange: (String) -> Unit, label: @Composable (() -> Unit)? = null, placeholder: @Composable (() -> Unit)? = null) {
|
||||||
@@ -33,9 +33,15 @@ fun PasswordField(modifier: Modifier = Modifier, value: String, onValueChange: (
|
|||||||
label = label,
|
label = label,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
IconButton(onClick = {
|
TooltipIconButton(
|
||||||
visible = !visible
|
modifier = Modifier,
|
||||||
}) {
|
onClick = {
|
||||||
|
visible = !visible
|
||||||
|
},
|
||||||
|
tooltip = if (visible) stringResource(R.string.show_password_field) else stringResource(
|
||||||
|
R.string.hide_password_field
|
||||||
|
),
|
||||||
|
) {
|
||||||
val (icon, description) = remember(visible) {
|
val (icon, description) = remember(visible) {
|
||||||
if (visible) Icons.Outlined.VisibilityOff to R.string.hide_password_field else Icons.Outlined.Visibility to R.string.show_password_field
|
if (visible) Icons.Outlined.VisibilityOff to R.string.hide_password_field else Icons.Outlined.Visibility to R.string.show_password_field
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SearchBar
|
import androidx.compose.material3.SearchBar
|
||||||
import androidx.compose.material3.SearchBarColors
|
import androidx.compose.material3.SearchBarColors
|
||||||
@@ -19,6 +18,7 @@ import androidx.compose.ui.focus.focusRequester
|
|||||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -48,7 +48,10 @@ fun SearchView(
|
|||||||
onExpandedChange = onActiveChange,
|
onExpandedChange = onActiveChange,
|
||||||
placeholder = placeholder,
|
placeholder = placeholder,
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
IconButton(onClick = { onActiveChange(false) }) {
|
TooltipIconButton(
|
||||||
|
tooltip = stringResource(R.string.back),
|
||||||
|
onClick = { onActiveChange(false) }
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.AutoMirrored.Filled.ArrowBack,
|
Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
stringResource(R.string.back)
|
stringResource(R.string.back)
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ import app.revanced.manager.domain.repository.PatchBundleRepository
|
|||||||
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
||||||
import app.revanced.manager.ui.component.ExceptionViewerDialog
|
import app.revanced.manager.ui.component.ExceptionViewerDialog
|
||||||
import app.revanced.manager.ui.component.FullscreenDialog
|
import app.revanced.manager.ui.component.FullscreenDialog
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
import app.revanced.manager.ui.component.TextInputDialog
|
import app.revanced.manager.ui.component.TextInputDialog
|
||||||
import app.revanced.manager.ui.component.haptics.HapticSwitch
|
import app.revanced.manager.ui.component.haptics.HapticSwitch
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -103,7 +104,11 @@ fun BundleInformationDialog(
|
|||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
if (!src.isDefault) {
|
if (!src.isDefault) {
|
||||||
IconButton(onClick = onDeleteRequest) {
|
TooltipIconButton(
|
||||||
|
modifier = Modifier,
|
||||||
|
onClick = onDeleteRequest,
|
||||||
|
tooltip = stringResource(R.string.delete),
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Outlined.DeleteOutline,
|
Icons.Outlined.DeleteOutline,
|
||||||
stringResource(R.string.delete)
|
stringResource(R.string.delete)
|
||||||
@@ -111,7 +116,11 @@ fun BundleInformationDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isLocal && hasNetwork) {
|
if (!isLocal && hasNetwork) {
|
||||||
IconButton(onClick = onUpdate) {
|
TooltipIconButton(
|
||||||
|
modifier = Modifier,
|
||||||
|
onClick = onUpdate,
|
||||||
|
tooltip = stringResource(R.string.refresh),
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Outlined.Update,
|
Icons.Outlined.Update,
|
||||||
stringResource(R.string.refresh)
|
stringResource(R.string.refresh)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package app.revanced.manager.ui.component.bundle
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
@@ -10,7 +9,11 @@ import androidx.compose.material3.TopAppBarDefaults
|
|||||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
import androidx.compose.material3.surfaceColorAtElevation
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -33,7 +36,10 @@ fun BundleTopBar(
|
|||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
if (onBackClick != null) {
|
if (onBackClick != null) {
|
||||||
IconButton(onClick = onBackClick) {
|
TooltipIconButton(
|
||||||
|
tooltip = stringResource(R.string.back),
|
||||||
|
onClick = onBackClick
|
||||||
|
) {
|
||||||
backIcon()
|
backIcon()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ private fun ImportBundleStep(
|
|||||||
},
|
},
|
||||||
supportingContent = { Text(stringResource(if (patchBundle != null) R.string.file_field_set else R.string.file_field_not_set)) },
|
supportingContent = { Text(stringResource(if (patchBundle != null) R.string.file_field_set else R.string.file_field_not_set)) },
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
|
// TODO: Determine if this button should be [TooltipWrap]'ped
|
||||||
IconButton(onClick = launchPatchActivity) {
|
IconButton(onClick = launchPatchActivity) {
|
||||||
Icon(imageVector = Icons.Default.Topic, contentDescription = null)
|
Icon(imageVector = Icons.Default.Topic, contentDescription = null)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package app.revanced.manager.ui.component.haptics
|
||||||
|
|
||||||
|
import android.view.HapticFeedbackConstants
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.FloatingActionButtonDefaults
|
||||||
|
import androidx.compose.material3.FloatingActionButtonElevation
|
||||||
|
import androidx.compose.material3.SmallFloatingActionButton
|
||||||
|
import androidx.compose.material3.contentColorFor
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
import app.revanced.manager.util.withHapticFeedback
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HapticSmallFloatingActionButton (
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
shape: Shape = FloatingActionButtonDefaults.smallShape,
|
||||||
|
containerColor: Color = FloatingActionButtonDefaults.containerColor,
|
||||||
|
contentColor: Color = contentColorFor(containerColor),
|
||||||
|
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
|
||||||
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
|
content: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
SmallFloatingActionButton(
|
||||||
|
onClick = onClick.withHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY),
|
||||||
|
modifier = modifier,
|
||||||
|
shape = shape,
|
||||||
|
containerColor = containerColor,
|
||||||
|
contentColor = contentColor,
|
||||||
|
elevation = elevation,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -67,6 +67,7 @@ import app.revanced.manager.ui.component.LongInputDialog
|
|||||||
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
|
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
|
||||||
import app.revanced.manager.ui.component.haptics.HapticRadioButton
|
import app.revanced.manager.ui.component.haptics.HapticRadioButton
|
||||||
import app.revanced.manager.ui.component.haptics.HapticSwitch
|
import app.revanced.manager.ui.component.haptics.HapticSwitch
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
import app.revanced.manager.util.isScrollingUp
|
import app.revanced.manager.util.isScrollingUp
|
||||||
import app.revanced.manager.util.mutableStateSetOf
|
import app.revanced.manager.util.mutableStateSetOf
|
||||||
import app.revanced.manager.util.saver.snapshotStateListSaver
|
import app.revanced.manager.util.saver.snapshotStateListSaver
|
||||||
@@ -123,7 +124,11 @@ private interface OptionEditor<T : Any> {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ListItemTrailingContent(scope: OptionEditorScope<T>) {
|
fun ListItemTrailingContent(scope: OptionEditorScope<T>) {
|
||||||
IconButton(onClick = { scope.checkSafeguard { clickAction(scope) } }) {
|
TooltipIconButton(
|
||||||
|
modifier = Modifier,
|
||||||
|
tooltip = stringResource(R.string.edit),
|
||||||
|
onClick = { scope.checkSafeguard { clickAction(scope) } }
|
||||||
|
) {
|
||||||
Icon(Icons.Outlined.Edit, stringResource(R.string.edit))
|
Icon(Icons.Outlined.Edit, stringResource(R.string.edit))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,7 +275,9 @@ private object StringOptionEditor : OptionEditor<String> {
|
|||||||
},
|
},
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
var showDropdownMenu by rememberSaveable { mutableStateOf(false) }
|
var showDropdownMenu by rememberSaveable { mutableStateOf(false) }
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
|
modifier = Modifier,
|
||||||
|
tooltip = stringResource(R.string.string_option_menu_description),
|
||||||
onClick = { showDropdownMenu = true }
|
onClick = { showDropdownMenu = true }
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -580,7 +587,9 @@ private class ListOptionEditor<T : Serializable>(private val elementEditor: Opti
|
|||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
if (deleteMode) {
|
if (deleteMode) {
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
|
modifier = Modifier,
|
||||||
|
tooltip = stringResource(R.string.select_deselect_all),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (items.size == deletionTargets.size) deletionTargets.clear()
|
if (items.size == deletionTargets.size) deletionTargets.clear()
|
||||||
else deletionTargets.addAll(items.map { it.key })
|
else deletionTargets.addAll(items.map { it.key })
|
||||||
@@ -591,7 +600,9 @@ private class ListOptionEditor<T : Serializable>(private val elementEditor: Opti
|
|||||||
stringResource(R.string.select_deselect_all)
|
stringResource(R.string.select_deselect_all)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
|
modifier = Modifier,
|
||||||
|
tooltip = stringResource(R.string.delete),
|
||||||
onClick = {
|
onClick = {
|
||||||
items.removeIf { it.key in deletionTargets }
|
items.removeIf { it.key in deletionTargets }
|
||||||
deletionTargets.clear()
|
deletionTargets.clear()
|
||||||
@@ -604,8 +615,15 @@ private class ListOptionEditor<T : Serializable>(private val elementEditor: Opti
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
IconButton(onClick = items::clear) {
|
TooltipIconButton(
|
||||||
Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
|
modifier = Modifier,
|
||||||
|
tooltip = stringResource(R.string.reset),
|
||||||
|
onClick = items::clear
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Outlined.Restore,
|
||||||
|
stringResource(R.string.reset)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -673,9 +691,10 @@ private class ListOptionEditor<T : Serializable>(private val elementEditor: Opti
|
|||||||
),
|
),
|
||||||
tonalElevation = if (deleteMode && item.key in deletionTargets) 8.dp else 0.dp,
|
tonalElevation = if (deleteMode && item.key in deletionTargets) 8.dp else 0.dp,
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
modifier = Modifier.draggableHandle(interactionSource = interactionSource),
|
modifier = Modifier.draggableHandle(interactionSource = interactionSource),
|
||||||
onClick = {},
|
tooltip = stringResource(R.string.delete),
|
||||||
|
onClick = { }
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Filled.DragHandle,
|
Icons.Filled.DragHandle,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.compose.foundation.clickable
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Edit
|
import androidx.compose.material.icons.outlined.Edit
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -17,6 +16,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.domain.manager.base.Preference
|
import app.revanced.manager.domain.manager.base.Preference
|
||||||
import app.revanced.manager.ui.component.IntInputDialog
|
import app.revanced.manager.ui.component.IntInputDialog
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -65,10 +65,14 @@ fun IntegerItem(
|
|||||||
headlineContent = stringResource(headline),
|
headlineContent = stringResource(headline),
|
||||||
supportingContent = stringResource(description),
|
supportingContent = stringResource(description),
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
IconButton(onClick = { dialogOpen = true }) {
|
TooltipIconButton(
|
||||||
|
modifier = modifier,
|
||||||
|
onClick = { dialogOpen = true },
|
||||||
|
tooltip = stringResource(R.string.edit),
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Outlined.Edit,
|
imageVector = Icons.Outlined.Edit,
|
||||||
contentDescription = stringResource(R.string.edit)
|
contentDescription = stringResource(R.string.edit),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package app.revanced.manager.ui.component.tooltip
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FloatingActionButtonDefaults
|
||||||
|
import androidx.compose.material3.FloatingActionButtonElevation
|
||||||
|
import androidx.compose.material3.TooltipDefaults
|
||||||
|
import androidx.compose.material3.contentColorFor
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
|
import androidx.compose.ui.window.PopupPositionProvider
|
||||||
|
import app.revanced.manager.ui.component.haptics.HapticFloatingActionButton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [HapticFloatingActionButton] with tooltip-specific params.
|
||||||
|
*
|
||||||
|
* @param tooltip [String] text to show in a tooltip.
|
||||||
|
* @param positionProvider [PopupPositionProvider] Anchor point for the tooltip.
|
||||||
|
* @param haptic Whether to perform haptic feedback when the tooltip shown.
|
||||||
|
* @param hapticFeedbackType The type of haptic feedback to perform.
|
||||||
|
*
|
||||||
|
* @see [HapticFloatingActionButton]
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun TooltipFloatingActionButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
shape: Shape = FloatingActionButtonDefaults.shape,
|
||||||
|
containerColor: Color = FloatingActionButtonDefaults.containerColor,
|
||||||
|
contentColor: Color = contentColorFor(containerColor),
|
||||||
|
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
|
||||||
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
|
tooltip: String,
|
||||||
|
positionProvider: PopupPositionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
haptic: Boolean = true,
|
||||||
|
hapticFeedbackType: HapticFeedbackType = HapticFeedbackType.LongPress,
|
||||||
|
content: @Composable (() -> Unit)
|
||||||
|
) {
|
||||||
|
TooltipWrap(
|
||||||
|
tooltip = tooltip,
|
||||||
|
positionProvider = positionProvider,
|
||||||
|
haptic = haptic,
|
||||||
|
hapticFeedbackType = hapticFeedbackType,
|
||||||
|
) {
|
||||||
|
HapticFloatingActionButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
shape = shape,
|
||||||
|
containerColor = containerColor,
|
||||||
|
contentColor = contentColor,
|
||||||
|
elevation = elevation,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [HapticFloatingActionButton] with tooltip-specific params.
|
||||||
|
*
|
||||||
|
* @param tooltip [Int] or `id` string resource to show in a tooltip.
|
||||||
|
* @param positionProvider [PopupPositionProvider] Anchor point for the tooltip.
|
||||||
|
* @param haptic Whether to perform haptic feedback when the tooltip shown.
|
||||||
|
* @param hapticFeedbackType The type of haptic feedback to perform.
|
||||||
|
*
|
||||||
|
* @see [HapticFloatingActionButton]
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun TooltipFloatingActionButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
shape: Shape = FloatingActionButtonDefaults.shape,
|
||||||
|
containerColor: Color = FloatingActionButtonDefaults.containerColor,
|
||||||
|
contentColor: Color = contentColorFor(containerColor),
|
||||||
|
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
|
||||||
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
|
@StringRes tooltip: Int,
|
||||||
|
positionProvider: PopupPositionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
haptic: Boolean = true,
|
||||||
|
hapticFeedbackType: HapticFeedbackType = HapticFeedbackType.LongPress,
|
||||||
|
content: @Composable (() -> Unit)
|
||||||
|
) {
|
||||||
|
TooltipWrap(
|
||||||
|
tooltip = tooltip,
|
||||||
|
positionProvider = positionProvider,
|
||||||
|
haptic = haptic,
|
||||||
|
hapticFeedbackType = hapticFeedbackType,
|
||||||
|
) {
|
||||||
|
HapticFloatingActionButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
shape = shape,
|
||||||
|
containerColor = containerColor,
|
||||||
|
contentColor = contentColor,
|
||||||
|
elevation = elevation,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package app.revanced.manager.ui.component.tooltip
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.IconButtonColors
|
||||||
|
import androidx.compose.material3.IconButtonDefaults
|
||||||
|
import androidx.compose.material3.TooltipDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
|
import androidx.compose.ui.window.PopupPositionProvider
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [IconButton] with tooltip-specific params.
|
||||||
|
*
|
||||||
|
* @param tooltip [String] text to show in a tooltip.
|
||||||
|
* @param positionProvider [PopupPositionProvider] Anchor point for the tooltip.
|
||||||
|
* @param haptic Whether to perform haptic feedback when the tooltip shown.
|
||||||
|
* @param hapticFeedbackType The type of haptic feedback to perform.
|
||||||
|
*
|
||||||
|
* @see [IconButton]
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun TooltipIconButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
colors: IconButtonColors = IconButtonDefaults.iconButtonColors(),
|
||||||
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
|
tooltip: String,
|
||||||
|
positionProvider: PopupPositionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
haptic: Boolean = true,
|
||||||
|
hapticFeedbackType: HapticFeedbackType = HapticFeedbackType.LongPress,
|
||||||
|
content: @Composable (() -> Unit),
|
||||||
|
) {
|
||||||
|
TooltipWrap(
|
||||||
|
tooltip = tooltip,
|
||||||
|
positionProvider = positionProvider,
|
||||||
|
haptic = haptic,
|
||||||
|
hapticFeedbackType = hapticFeedbackType,
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
colors = colors,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [IconButton] with tooltip-specific params.
|
||||||
|
*
|
||||||
|
* @param tooltip [Int] or `id` string resource to show in a tooltip.
|
||||||
|
* @param positionProvider [PopupPositionProvider] Anchor point for the tooltip.
|
||||||
|
* @param haptic Whether to perform haptic feedback when the tooltip shown.
|
||||||
|
* @param hapticFeedbackType The type of haptic feedback to perform.
|
||||||
|
*
|
||||||
|
* @see [IconButton]
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun TooltipIconButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
colors: IconButtonColors = IconButtonDefaults.iconButtonColors(),
|
||||||
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
|
@StringRes tooltip: Int,
|
||||||
|
positionProvider: PopupPositionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
haptic: Boolean = true,
|
||||||
|
hapticFeedbackType: HapticFeedbackType = HapticFeedbackType.LongPress,
|
||||||
|
content: @Composable (() -> Unit),
|
||||||
|
) {
|
||||||
|
TooltipWrap(
|
||||||
|
tooltip = tooltip,
|
||||||
|
positionProvider = positionProvider,
|
||||||
|
haptic = haptic,
|
||||||
|
hapticFeedbackType = hapticFeedbackType,
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
colors = colors,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package app.revanced.manager.ui.component.tooltip
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FloatingActionButtonDefaults
|
||||||
|
import androidx.compose.material3.FloatingActionButtonElevation
|
||||||
|
import androidx.compose.material3.TooltipDefaults
|
||||||
|
import androidx.compose.material3.contentColorFor
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
|
import androidx.compose.ui.window.PopupPositionProvider
|
||||||
|
import app.revanced.manager.ui.component.haptics.HapticSmallFloatingActionButton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [HapticSmallFloatingActionButton] with tooltip-specific params.
|
||||||
|
*
|
||||||
|
* @param tooltip [String] text to show in a tooltip.
|
||||||
|
* @param positionProvider [PopupPositionProvider] Anchor point for the tooltip.
|
||||||
|
* @param haptic Whether to perform haptic feedback when the tooltip shown.
|
||||||
|
* @param hapticFeedbackType The type of haptic feedback to perform.
|
||||||
|
*
|
||||||
|
* @see [HapticSmallFloatingActionButton]
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun TooltipSmallFloatingActionButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
shape: Shape = FloatingActionButtonDefaults.smallShape,
|
||||||
|
containerColor: Color = FloatingActionButtonDefaults.containerColor,
|
||||||
|
contentColor: Color = contentColorFor(containerColor),
|
||||||
|
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
|
||||||
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
|
tooltip: String,
|
||||||
|
positionProvider: PopupPositionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
haptic: Boolean = true,
|
||||||
|
hapticFeedbackType: HapticFeedbackType = HapticFeedbackType.LongPress,
|
||||||
|
content: @Composable (() -> Unit)
|
||||||
|
) {
|
||||||
|
TooltipWrap(
|
||||||
|
tooltip = tooltip,
|
||||||
|
positionProvider = positionProvider,
|
||||||
|
haptic = haptic,
|
||||||
|
hapticFeedbackType = hapticFeedbackType,
|
||||||
|
) {
|
||||||
|
HapticSmallFloatingActionButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
shape = shape,
|
||||||
|
containerColor = containerColor,
|
||||||
|
contentColor = contentColor,
|
||||||
|
elevation = elevation,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [HapticSmallFloatingActionButton] with tooltip-specific params.
|
||||||
|
*
|
||||||
|
* @param tooltip [Int] or `id` string resource to show in a tooltip.
|
||||||
|
* @param positionProvider [PopupPositionProvider] Anchor point for the tooltip.
|
||||||
|
* @param haptic Whether to perform haptic feedback when the tooltip shown.
|
||||||
|
* @param hapticFeedbackType The type of haptic feedback to perform.
|
||||||
|
*
|
||||||
|
* @see [HapticSmallFloatingActionButton]
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun TooltipSmallFloatingActionButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
shape: Shape = FloatingActionButtonDefaults.smallShape,
|
||||||
|
containerColor: Color = FloatingActionButtonDefaults.containerColor,
|
||||||
|
contentColor: Color = contentColorFor(containerColor),
|
||||||
|
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
|
||||||
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
|
@StringRes tooltip: Int,
|
||||||
|
positionProvider: PopupPositionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
haptic: Boolean = true,
|
||||||
|
hapticFeedbackType: HapticFeedbackType = HapticFeedbackType.LongPress,
|
||||||
|
content: @Composable (() -> Unit)
|
||||||
|
) {
|
||||||
|
TooltipWrap(
|
||||||
|
tooltip = tooltip,
|
||||||
|
positionProvider = positionProvider,
|
||||||
|
haptic = haptic,
|
||||||
|
hapticFeedbackType = hapticFeedbackType,
|
||||||
|
) {
|
||||||
|
HapticSmallFloatingActionButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
shape = shape,
|
||||||
|
containerColor = containerColor,
|
||||||
|
contentColor = contentColor,
|
||||||
|
elevation = elevation,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package app.revanced.manager.ui.component.tooltip
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.PlainTooltip
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TooltipBox
|
||||||
|
import androidx.compose.material3.TooltipDefaults
|
||||||
|
import androidx.compose.material3.rememberTooltipState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.window.PopupPositionProvider
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a composable with a tooltip.
|
||||||
|
*
|
||||||
|
* @param modifier the [Modifier] to applied to Tooltip.
|
||||||
|
* @param tooltip [String] text to show in a tooltip.
|
||||||
|
* @param positionProvider [PopupPositionProvider] Anchor point for the tooltip.
|
||||||
|
* @param content The composable UI to wrapped with.
|
||||||
|
* @param haptic Whether to perform haptic feedback when the tooltip shown.
|
||||||
|
* @param hapticFeedbackType The type of haptic feedback to perform.
|
||||||
|
*
|
||||||
|
* @see [TooltipBox]
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
fun TooltipWrap(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
tooltip: String,
|
||||||
|
positionProvider: PopupPositionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
haptic: Boolean = true,
|
||||||
|
hapticFeedbackType: HapticFeedbackType = HapticFeedbackType.LongPress,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val tooltipState = rememberTooltipState()
|
||||||
|
val localHaptic = LocalHapticFeedback.current
|
||||||
|
|
||||||
|
LaunchedEffect(tooltipState.isVisible) {
|
||||||
|
if (tooltipState.isVisible && haptic) {
|
||||||
|
localHaptic.performHapticFeedback(hapticFeedbackType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TooltipBox(
|
||||||
|
modifier = modifier,
|
||||||
|
positionProvider = positionProvider,
|
||||||
|
tooltip = { PlainTooltip { Text(tooltip) } },
|
||||||
|
state = tooltipState,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a composable with a tooltip.
|
||||||
|
*
|
||||||
|
* @param modifier the [Modifier] to applied to tooltip.
|
||||||
|
* @param tooltip [Int] or `id` string resource to show in a tooltip.
|
||||||
|
* @param positionProvider [PopupPositionProvider] Anchor point for the tooltip.
|
||||||
|
* @param content The composable UI to wrapped with.
|
||||||
|
* @param haptic Whether to perform haptic feedback when the tooltip shown.
|
||||||
|
* @param hapticFeedbackType The type of haptic feedback to perform.
|
||||||
|
*
|
||||||
|
* @see [TooltipBox]
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
fun TooltipWrap(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
@StringRes tooltip: Int,
|
||||||
|
positionProvider: PopupPositionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
haptic: Boolean = true,
|
||||||
|
hapticFeedbackType: HapticFeedbackType = HapticFeedbackType.LongPress,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val tooltipState = rememberTooltipState()
|
||||||
|
val localHaptic = LocalHapticFeedback.current
|
||||||
|
|
||||||
|
LaunchedEffect(tooltipState.isVisible) {
|
||||||
|
if (tooltipState.isVisible && haptic) {
|
||||||
|
localHaptic.performHapticFeedback(hapticFeedbackType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TooltipBox(
|
||||||
|
modifier = modifier,
|
||||||
|
positionProvider = positionProvider,
|
||||||
|
tooltip = { PlainTooltip { Text(stringResource(tooltip)) } },
|
||||||
|
state = tooltipState,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -16,7 +16,6 @@ import androidx.compose.material.icons.outlined.Search
|
|||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@@ -44,6 +43,7 @@ import app.revanced.manager.ui.component.LazyColumnWithScrollbar
|
|||||||
import app.revanced.manager.ui.component.LoadingIndicator
|
import app.revanced.manager.ui.component.LoadingIndicator
|
||||||
import app.revanced.manager.ui.component.NonSuggestedVersionDialog
|
import app.revanced.manager.ui.component.NonSuggestedVersionDialog
|
||||||
import app.revanced.manager.ui.component.SearchView
|
import app.revanced.manager.ui.component.SearchView
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
import app.revanced.manager.ui.model.SelectedApp
|
import app.revanced.manager.ui.model.SelectedApp
|
||||||
import app.revanced.manager.ui.viewmodel.AppSelectorViewModel
|
import app.revanced.manager.ui.viewmodel.AppSelectorViewModel
|
||||||
import app.revanced.manager.util.APK_MIMETYPE
|
import app.revanced.manager.util.APK_MIMETYPE
|
||||||
@@ -162,8 +162,14 @@ fun AppSelectorScreen(
|
|||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
onBackClick = onBackClick,
|
onBackClick = onBackClick,
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(onClick = { search = true }) {
|
TooltipIconButton(
|
||||||
Icon(Icons.Outlined.Search, stringResource(R.string.search))
|
tooltip = stringResource(R.string.search_patches),
|
||||||
|
onClick = { search = true }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Outlined.Search,
|
||||||
|
stringResource(R.string.search)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -66,8 +66,9 @@ import app.revanced.manager.ui.component.NotificationCard
|
|||||||
import app.revanced.manager.ui.component.ConfirmDialog
|
import app.revanced.manager.ui.component.ConfirmDialog
|
||||||
import app.revanced.manager.ui.component.bundle.BundleTopBar
|
import app.revanced.manager.ui.component.bundle.BundleTopBar
|
||||||
import app.revanced.manager.ui.component.bundle.ImportPatchBundleDialog
|
import app.revanced.manager.ui.component.bundle.ImportPatchBundleDialog
|
||||||
import app.revanced.manager.ui.component.haptics.HapticFloatingActionButton
|
|
||||||
import app.revanced.manager.ui.component.haptics.HapticTab
|
import app.revanced.manager.ui.component.haptics.HapticTab
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipFloatingActionButton
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
import app.revanced.manager.ui.viewmodel.DashboardViewModel
|
import app.revanced.manager.ui.viewmodel.DashboardViewModel
|
||||||
import app.revanced.manager.util.RequestInstallAppsContract
|
import app.revanced.manager.util.RequestInstallAppsContract
|
||||||
import app.revanced.manager.util.toast
|
import app.revanced.manager.util.toast
|
||||||
@@ -181,18 +182,20 @@ fun DashboardScreen(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
showDeleteConfirmationDialog = true
|
showDeleteConfirmationDialog = true
|
||||||
}
|
},
|
||||||
|
tooltip = stringResource(R.string.delete),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Outlined.DeleteOutline,
|
Icons.Outlined.DeleteOutline,
|
||||||
stringResource(R.string.delete)
|
stringResource(R.string.delete)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
onClick = vm::updateSources
|
onClick = vm::updateSources,
|
||||||
|
tooltip = stringResource(R.string.refresh)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Outlined.Refresh,
|
Icons.Outlined.Refresh,
|
||||||
@@ -206,8 +209,9 @@ fun DashboardScreen(
|
|||||||
title = stringResource(R.string.app_name),
|
title = stringResource(R.string.app_name),
|
||||||
actions = {
|
actions = {
|
||||||
if (!vm.updatedManagerVersion.isNullOrEmpty()) {
|
if (!vm.updatedManagerVersion.isNullOrEmpty()) {
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
onClick = onUpdateClick,
|
onClick = onUpdateClick,
|
||||||
|
tooltip = stringResource(R.string.update),
|
||||||
) {
|
) {
|
||||||
BadgedBox(
|
BadgedBox(
|
||||||
badge = {
|
badge = {
|
||||||
@@ -218,8 +222,17 @@ fun DashboardScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IconButton(onClick = onSettingsClick) {
|
TooltipIconButton(
|
||||||
Icon(Icons.Outlined.Settings, stringResource(R.string.settings))
|
onClick = onSettingsClick,
|
||||||
|
tooltip = stringResource(R.string.settings),
|
||||||
|
) {
|
||||||
|
BadgedBox(
|
||||||
|
badge = {
|
||||||
|
Badge(modifier = Modifier.size(6.dp))
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(Icons.Outlined.Settings, stringResource(R.string.settings))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
applyContainerColor = true
|
applyContainerColor = true
|
||||||
@@ -227,7 +240,8 @@ fun DashboardScreen(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
HapticFloatingActionButton(
|
TooltipFloatingActionButton(
|
||||||
|
tooltip = stringResource(R.string.add),
|
||||||
onClick = {
|
onClick = {
|
||||||
vm.cancelSourceSelection()
|
vm.cancelSourceSelection()
|
||||||
|
|
||||||
@@ -240,11 +254,11 @@ fun DashboardScreen(
|
|||||||
DashboardPage.BUNDLES.ordinal
|
DashboardPage.BUNDLES.ordinal
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return@HapticFloatingActionButton
|
return@TooltipFloatingActionButton
|
||||||
}
|
}
|
||||||
if (vm.android11BugActive) {
|
if (vm.android11BugActive) {
|
||||||
showAndroid11Dialog = true
|
showAndroid11Dialog = true
|
||||||
return@HapticFloatingActionButton
|
return@TooltipFloatingActionButton
|
||||||
}
|
}
|
||||||
|
|
||||||
onAppSelectorClick()
|
onAppSelectorClick()
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import androidx.compose.material3.AlertDialog
|
|||||||
import androidx.compose.material3.BottomAppBar
|
import androidx.compose.material3.BottomAppBar
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
@@ -51,6 +50,7 @@ import app.revanced.manager.ui.component.InstallerStatusDialog
|
|||||||
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
|
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
|
||||||
import app.revanced.manager.ui.component.patcher.InstallPickerDialog
|
import app.revanced.manager.ui.component.patcher.InstallPickerDialog
|
||||||
import app.revanced.manager.ui.component.patcher.Steps
|
import app.revanced.manager.ui.component.patcher.Steps
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
import app.revanced.manager.ui.model.StepCategory
|
import app.revanced.manager.ui.model.StepCategory
|
||||||
import app.revanced.manager.ui.viewmodel.PatcherViewModel
|
import app.revanced.manager.ui.viewmodel.PatcherViewModel
|
||||||
import app.revanced.manager.util.APK_MIMETYPE
|
import app.revanced.manager.util.APK_MIMETYPE
|
||||||
@@ -164,15 +164,17 @@ fun PatcherScreen(
|
|||||||
bottomBar = {
|
bottomBar = {
|
||||||
BottomAppBar(
|
BottomAppBar(
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
onClick = { exportApkLauncher.launch("${viewModel.packageName}_${viewModel.version}_revanced_patched.apk") },
|
onClick = { exportApkLauncher.launch("${viewModel.packageName}_${viewModel.version}_revanced_patched.apk") },
|
||||||
enabled = patcherSucceeded == true
|
enabled = patcherSucceeded == true,
|
||||||
|
tooltip = stringResource(R.string.save_apk),
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Outlined.Save, stringResource(id = R.string.save_apk))
|
Icon(Icons.Outlined.Save, stringResource(id = R.string.save_apk))
|
||||||
}
|
}
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
onClick = { viewModel.exportLogs(context) },
|
onClick = { viewModel.exportLogs(context) },
|
||||||
enabled = patcherSucceeded != null
|
enabled = patcherSucceeded != null,
|
||||||
|
tooltip = stringResource(R.string.save_logs),
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Outlined.PostAdd, stringResource(id = R.string.save_logs))
|
Icon(Icons.Outlined.PostAdd, stringResource(id = R.string.save_logs))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.ScrollableTabRow
|
import androidx.compose.material3.ScrollableTabRow
|
||||||
import androidx.compose.material3.SmallFloatingActionButton
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.surfaceColorAtElevation
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
@@ -73,6 +72,9 @@ import app.revanced.manager.ui.component.haptics.HapticCheckbox
|
|||||||
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
|
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
|
||||||
import app.revanced.manager.ui.component.haptics.HapticTab
|
import app.revanced.manager.ui.component.haptics.HapticTab
|
||||||
import app.revanced.manager.ui.component.patches.OptionItem
|
import app.revanced.manager.ui.component.patches.OptionItem
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipFloatingActionButton
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipSmallFloatingActionButton
|
||||||
import app.revanced.manager.ui.component.patches.SelectionWarningDialog
|
import app.revanced.manager.ui.component.patches.SelectionWarningDialog
|
||||||
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel
|
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel
|
||||||
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.Companion.SHOW_INCOMPATIBLE
|
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.Companion.SHOW_INCOMPATIBLE
|
||||||
@@ -259,14 +261,15 @@ fun PatchesSelectorScreen(
|
|||||||
animationSpec = tween(durationMillis = 400, easing = EaseInOut),
|
animationSpec = tween(durationMillis = 400, easing = EaseInOut),
|
||||||
label = "SearchBar back button"
|
label = "SearchBar back button"
|
||||||
)
|
)
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (searchExpanded) {
|
if (searchExpanded) {
|
||||||
setSearchExpanded(false)
|
setSearchExpanded(false)
|
||||||
} else {
|
} else {
|
||||||
onBackClick()
|
onBackClick()
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
tooltip = stringResource(R.string.back),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
modifier = Modifier.rotate(rotation),
|
modifier = Modifier.rotate(rotation),
|
||||||
@@ -282,9 +285,10 @@ fun PatchesSelectorScreen(
|
|||||||
transitionSpec = { fadeIn() togetherWith fadeOut() }
|
transitionSpec = { fadeIn() togetherWith fadeOut() }
|
||||||
) { searchExpanded ->
|
) { searchExpanded ->
|
||||||
if (searchExpanded) {
|
if (searchExpanded) {
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
onClick = { setQuery("") },
|
onClick = { setQuery("") },
|
||||||
enabled = query.isNotEmpty()
|
enabled = query.isNotEmpty(),
|
||||||
|
tooltip = stringResource(R.string.clear),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.Close,
|
imageVector = Icons.Filled.Close,
|
||||||
@@ -292,7 +296,10 @@ fun PatchesSelectorScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
IconButton(onClick = { showBottomSheet = true }) {
|
TooltipIconButton(
|
||||||
|
onClick = { showBottomSheet = true },
|
||||||
|
tooltip = stringResource(R.string.more),
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.FilterList,
|
imageVector = Icons.Outlined.FilterList,
|
||||||
contentDescription = stringResource(R.string.more)
|
contentDescription = stringResource(R.string.more)
|
||||||
@@ -354,7 +361,8 @@ fun PatchesSelectorScreen(
|
|||||||
horizontalAlignment = Alignment.End,
|
horizontalAlignment = Alignment.End,
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
) {
|
) {
|
||||||
SmallFloatingActionButton(
|
TooltipSmallFloatingActionButton(
|
||||||
|
tooltip = stringResource(R.string.reset),
|
||||||
onClick = viewModel::reset,
|
onClick = viewModel::reset,
|
||||||
containerColor = MaterialTheme.colorScheme.tertiaryContainer
|
containerColor = MaterialTheme.colorScheme.tertiaryContainer
|
||||||
) {
|
) {
|
||||||
@@ -506,6 +514,7 @@ private fun PatchItem(
|
|||||||
supportingContent = patch.description?.let { { Text(it) } },
|
supportingContent = patch.description?.let { { Text(it) } },
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
if (patch.options?.isNotEmpty() == true) {
|
if (patch.options?.isNotEmpty() == true) {
|
||||||
|
// TODO: Determine if this button should be [TooltipWrap]
|
||||||
IconButton(onClick = onOptionsDialog, enabled = compatible) {
|
IconButton(onClick = onOptionsDialog, enabled = compatible) {
|
||||||
Icon(Icons.Outlined.Settings, null)
|
Icon(Icons.Outlined.Settings, null)
|
||||||
}
|
}
|
||||||
@@ -529,7 +538,10 @@ fun ListHeader(
|
|||||||
},
|
},
|
||||||
trailingContent = onHelpClick?.let {
|
trailingContent = onHelpClick?.let {
|
||||||
{
|
{
|
||||||
IconButton(onClick = it) {
|
TooltipIconButton(
|
||||||
|
tooltip = stringResource(R.string.help),
|
||||||
|
onClick = it
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.AutoMirrored.Outlined.HelpOutline,
|
Icons.AutoMirrored.Outlined.HelpOutline,
|
||||||
stringResource(R.string.help)
|
stringResource(R.string.help)
|
||||||
@@ -609,7 +621,10 @@ private fun OptionsDialog(
|
|||||||
title = patch.name,
|
title = patch.name,
|
||||||
onBackClick = onDismissRequest,
|
onBackClick = onDismissRequest,
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(onClick = reset) {
|
TooltipIconButton(
|
||||||
|
tooltip = stringResource(R.string.reset),
|
||||||
|
onClick = reset
|
||||||
|
) {
|
||||||
Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
|
Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import androidx.compose.material.icons.outlined.MailOutline
|
|||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FilledTonalButton
|
import androidx.compose.material3.FilledTonalButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedCard
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@@ -50,6 +49,7 @@ import app.revanced.manager.network.dto.ReVancedSocial
|
|||||||
import app.revanced.manager.ui.component.AppTopBar
|
import app.revanced.manager.ui.component.AppTopBar
|
||||||
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
||||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
import app.revanced.manager.ui.component.settings.SettingsListItem
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
import app.revanced.manager.ui.model.navigation.Settings
|
import app.revanced.manager.ui.model.navigation.Settings
|
||||||
import app.revanced.manager.ui.viewmodel.AboutViewModel
|
import app.revanced.manager.ui.viewmodel.AboutViewModel
|
||||||
import app.revanced.manager.ui.viewmodel.AboutViewModel.Companion.DEVELOPER_OPTIONS_TAPS
|
import app.revanced.manager.ui.viewmodel.AboutViewModel.Companion.DEVELOPER_OPTIONS_TAPS
|
||||||
@@ -252,9 +252,10 @@ fun AboutSettingsScreen(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally)
|
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally)
|
||||||
) {
|
) {
|
||||||
socialButtons.forEach { (icon, text, onClick) ->
|
socialButtons.forEach { (icon, text, onClick) ->
|
||||||
IconButton(
|
TooltipIconButton(
|
||||||
onClick = onClick,
|
|
||||||
modifier = Modifier.padding(end = 8.dp),
|
modifier = Modifier.padding(end = 8.dp),
|
||||||
|
tooltip = text,
|
||||||
|
onClick = onClick
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
icon,
|
icon,
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import androidx.compose.material.icons.outlined.Restore
|
|||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@@ -52,6 +51,7 @@ import app.revanced.manager.ui.component.settings.BooleanItem
|
|||||||
import app.revanced.manager.ui.component.settings.IntegerItem
|
import app.revanced.manager.ui.component.settings.IntegerItem
|
||||||
import app.revanced.manager.ui.component.settings.SafeguardBooleanItem
|
import app.revanced.manager.ui.component.settings.SafeguardBooleanItem
|
||||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
import app.revanced.manager.ui.component.settings.SettingsListItem
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
import app.revanced.manager.ui.viewmodel.AdvancedSettingsViewModel
|
import app.revanced.manager.ui.viewmodel.AdvancedSettingsViewModel
|
||||||
import app.revanced.manager.util.toast
|
import app.revanced.manager.util.toast
|
||||||
import app.revanced.manager.util.withHapticFeedback
|
import app.revanced.manager.util.withHapticFeedback
|
||||||
@@ -243,7 +243,11 @@ private fun APIUrlDialog(currentUrl: String, defaultUrl: String, onSubmit: (Stri
|
|||||||
onValueChange = { url = it },
|
onValueChange = { url = it },
|
||||||
label = { Text(stringResource(R.string.api_url)) },
|
label = { Text(stringResource(R.string.api_url)) },
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
IconButton(onClick = { url = defaultUrl }) {
|
TooltipIconButton(
|
||||||
|
modifier = Modifier,
|
||||||
|
tooltip = stringResource(R.string.api_url_dialog_reset),
|
||||||
|
onClick = { url = defaultUrl }
|
||||||
|
) {
|
||||||
Icon(Icons.Outlined.Restore, stringResource(R.string.api_url_dialog_reset))
|
Icon(Icons.Outlined.Restore, stringResource(R.string.api_url_dialog_reset))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import androidx.compose.foundation.lazy.items
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
|
import androidx.compose.material.icons.outlined.Search
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -40,8 +41,10 @@ import app.revanced.manager.ui.component.ExceptionViewerDialog
|
|||||||
import app.revanced.manager.ui.component.GroupHeader
|
import app.revanced.manager.ui.component.GroupHeader
|
||||||
import app.revanced.manager.ui.component.LazyColumnWithScrollbar
|
import app.revanced.manager.ui.component.LazyColumnWithScrollbar
|
||||||
import app.revanced.manager.ui.component.ConfirmDialog
|
import app.revanced.manager.ui.component.ConfirmDialog
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipWrap
|
||||||
import app.revanced.manager.ui.component.haptics.HapticCheckbox
|
import app.revanced.manager.ui.component.haptics.HapticCheckbox
|
||||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
import app.revanced.manager.ui.component.settings.SettingsListItem
|
||||||
|
import app.revanced.manager.ui.component.tooltip.TooltipIconButton
|
||||||
import app.revanced.manager.ui.viewmodel.DownloadsViewModel
|
import app.revanced.manager.ui.viewmodel.DownloadsViewModel
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
@@ -75,7 +78,10 @@ fun DownloadsSettingsScreen(
|
|||||||
onBackClick = onBackClick,
|
onBackClick = onBackClick,
|
||||||
actions = {
|
actions = {
|
||||||
if (viewModel.appSelection.isNotEmpty()) {
|
if (viewModel.appSelection.isNotEmpty()) {
|
||||||
IconButton(onClick = { showDeleteConfirmationDialog = true }) {
|
TooltipIconButton(
|
||||||
|
tooltip = stringResource(R.string.delete),
|
||||||
|
onClick = { showDeleteConfirmationDialog = true }
|
||||||
|
) {
|
||||||
Icon(Icons.Default.Delete, stringResource(R.string.delete))
|
Icon(Icons.Default.Delete, stringResource(R.string.delete))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,4 +3,4 @@
|
|||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Learn how to add new, manage existing or remove patches.
|
|||||||
|
|
||||||
## ⏭️ What's next
|
## ⏭️ What's next
|
||||||
|
|
||||||
The next page will explain how to manage downloaders.
|
The next page will explain how to update ReVanced Manager.
|
||||||
|
|
||||||
Continue: [🔄 Updating ReVanced Manager](2_4_managing_downloaders.md)
|
Continue: [🔄 Updating ReVanced Manager](2_4_updating.md)
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
# 🧩 Managing downloaders
|
|
||||||
|
|
||||||
Learn how to manage downloaders.
|
|
||||||
Refer to the [template](https://github.com/ReVanced/revanced-manager-downloader-template) if you are developer who wants to create a plugin.
|
|
||||||
|
|
||||||
Downloaders are Apk files and are installed, updated and uninstalled just like regular Android apps.
|
|
||||||
Downloaders can execute arbitrary code inside ReVanced Manager and must be marked as trusted before use. Manager will show a notification in the dashboard when a new downloader is discovered.
|
|
||||||
Trust can also be granted and revoked under `Settings` > `Downloads`.
|
|
||||||
|
|
||||||
## ⏭️ What's next
|
|
||||||
|
|
||||||
The next page will explain how to update ReVanced Manager.
|
|
||||||
|
|
||||||
Continue: [🔄 Updating ReVanced Manager](2_5_updating.md)
|
|
||||||
|
|
||||||
@@ -15,4 +15,4 @@ and let you when an update is available.
|
|||||||
|
|
||||||
The next page will explain how to configure ReVanced Manager.
|
The next page will explain how to configure ReVanced Manager.
|
||||||
|
|
||||||
Continue: [⚙️ Configuring ReVanced Manager](2_6_settings.md)
|
Continue: [⚙️ Configuring ReVanced Manager](2_5_settings.md)
|
||||||
@@ -1,6 +1,28 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
|
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||||
|
# Android operating system, and which are packaged with your app's APK
|
||||||
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
# Enables namespacing of each library's R class so that its R class includes only the
|
||||||
|
# resources declared in the library itself and none from the library's dependencies,
|
||||||
|
# thereby reducing the size of the R class for that library
|
||||||
android.nonTransitiveRClass=true
|
android.nonTransitiveRClass=true
|
||||||
android.nonFinalResIds=false
|
android.nonFinalResIds=false
|
||||||
|
# Task :app:assembleReleaseSignApk fails if this is set to true.
|
||||||
|
org.gradle.configuration-cache=false
|
||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
|
version=1.25.0-dev.1
|
||||||
@@ -36,7 +36,6 @@ compose-icons = "1.2.4"
|
|||||||
kotlin-process = "1.5.1"
|
kotlin-process = "1.5.1"
|
||||||
hidden-api-stub = "4.3.3"
|
hidden-api-stub = "4.3.3"
|
||||||
binary-compatibility-validator = "0.17.0"
|
binary-compatibility-validator = "0.17.0"
|
||||||
semver-parser = "3.0.0"
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# AndroidX Core
|
# AndroidX Core
|
||||||
@@ -130,9 +129,6 @@ reorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "reo
|
|||||||
# switch to br.com.devsrsouza.compose.icons after DevSrSouza/compose-icons#30 is merged
|
# switch to br.com.devsrsouza.compose.icons after DevSrSouza/compose-icons#30 is merged
|
||||||
compose-icons-fontawesome = { group = "com.github.BenjaminHalko.compose-icons", name = "font-awesome", version.ref = "compose-icons" }
|
compose-icons-fontawesome = { group = "com.github.BenjaminHalko.compose-icons", name = "font-awesome", version.ref = "compose-icons" }
|
||||||
|
|
||||||
# Semantic versioning parser
|
|
||||||
semver-parser = { module = "io.github.z4kn4fein:semver", version.ref = "semver-parser" }
|
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
|
android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
|
||||||
android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" }
|
android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" }
|
||||||
|
|||||||
1593
package-lock.json
generated
1593
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@@ -1,11 +1,10 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
|
||||||
"workspaces": [
|
|
||||||
"api",
|
|
||||||
"app"
|
|
||||||
],
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@anolilab/multi-semantic-release": "^2.0.3",
|
"@anolilab/multi-semantic-release": "^1.1.10",
|
||||||
"semantic-release": "^24.2.7"
|
"gradle-semantic-release-plugin": "^1.10.1",
|
||||||
|
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
||||||
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
|
"@semantic-release/exec": "^6.0.3",
|
||||||
|
"@semantic-release/git": "^10.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user