mirror of
https://github.com/YTVanced/VancedManager
synced 2024-11-18 01:05:10 +00:00
Compare commits
610 commits
Author | SHA1 | Date | |
---|---|---|---|
|
8b3f2c5e2e | ||
|
7421ed9462 | ||
|
2ded1e87dd | ||
|
dcc3ad9a55 | ||
|
42bb94bd84 | ||
|
0db7ddd83a | ||
|
fc9da62528 | ||
|
dd9ffeca18 | ||
|
a2720c00fd | ||
|
a2d6c1b9de | ||
|
a170835b1d | ||
|
9ca0fb789b | ||
|
eb28c6163c | ||
|
368808de0c | ||
|
4af47e8de0 | ||
|
9a14d0f10c | ||
|
25f8f800ea | ||
|
851727a1c1 | ||
|
7c1dbb654a | ||
|
7c1392de94 | ||
|
9a31f2b7e3 | ||
|
f887ba5d6c | ||
|
ec17869600 | ||
|
17f3af190f | ||
|
4eae61e7a6 | ||
|
36fc660811 | ||
|
f882e05965 | ||
|
e7c6d2dc3d | ||
|
bbe993edf7 | ||
|
9358447838 | ||
|
f110dc6a14 | ||
|
ad97dab04a | ||
|
6ed72d3b65 | ||
|
ccd82f3bce | ||
|
e787217bf2 | ||
|
0a87f0d81c | ||
|
0db9faad94 | ||
|
a70d731ec4 | ||
|
ce18bb0963 | ||
|
a101e3a79f | ||
|
b1a51f837f | ||
|
02f7fe62ba | ||
|
4a50b27812 | ||
|
465d9f4fdb | ||
|
5ef967f097 | ||
|
7fca85be14 | ||
|
1cd0294801 | ||
|
715eb9cf7a | ||
|
46cb058cab | ||
|
697fbf5ea7 | ||
|
bd04fddeb0 | ||
|
1e0c187b43 | ||
|
d23f9a7bcf | ||
|
be35498e65 | ||
|
6445564c85 | ||
|
bb100aeefd | ||
|
1759c01e98 | ||
|
956d8800e9 | ||
|
a7ee51a90b | ||
|
f98c21ddbe | ||
|
d7dfb27ca5 | ||
|
b9820468c6 | ||
|
1ea32c75ff | ||
|
e6f04a95c8 | ||
|
9346324ec5 | ||
|
4167aa422e | ||
|
58a0b06b92 | ||
|
b53d9f35cc | ||
|
1150f5800b | ||
|
cd7c05d25a | ||
|
04d3500bb0 | ||
|
0e93d2091a | ||
|
df56ee8f04 | ||
|
bfd8a0e1ef | ||
|
ecca7dfcba | ||
|
c4ae3887fb | ||
|
f15dcdfaef | ||
|
81f8c7a036 | ||
|
c69bccfc99 | ||
|
c5a92d51b2 | ||
|
69e0221e82 | ||
|
96a69144fe | ||
|
d8fc45dd39 | ||
|
ffdc76a2ca | ||
|
88c3a3b12b | ||
|
28346f7ae3 | ||
|
f92061f54d | ||
|
822d7bdb78 | ||
|
a420ab2d79 | ||
|
822b900396 | ||
|
5a2be52767 | ||
|
f6231f809c | ||
|
24cac1920f | ||
|
835f84078b | ||
|
00896b77c3 | ||
|
c40c83d91c | ||
|
48632c3d2a | ||
|
ab6beff186 | ||
|
ac8f5f6540 | ||
|
de8c0a7ac7 | ||
|
0a7f579063 | ||
|
487dc15c2a | ||
|
afe6ec2f07 | ||
|
521495eaa8 | ||
|
10c683a66c | ||
|
287b169bc9 | ||
|
b3db04e96e | ||
|
48dbb7527b | ||
|
b6e53bafa4 | ||
|
a7107e4427 | ||
|
91f40ad846 | ||
|
2c3cdd0697 | ||
|
606664cf39 | ||
|
4e776e78ca | ||
|
bfb094f6b3 | ||
|
f9c40b12b2 | ||
|
4adfb9db3c | ||
|
f248bdba62 | ||
|
cd3a419326 | ||
|
ac09ae3c58 | ||
|
e43bef60e5 | ||
|
0a354f4e3d | ||
|
2a1fc505ea | ||
|
0e0ed12401 | ||
|
431139eb6b | ||
|
56a3850b5e | ||
|
c1cb75afe6 | ||
|
3620438efb | ||
|
33bfedfdf2 | ||
|
01fadc6854 | ||
|
b0d9611056 | ||
|
b264c13623 | ||
|
5b78a5b4b7 | ||
|
1aff587b09 | ||
|
b89e6d01c8 | ||
|
de94258b11 | ||
|
5f87878d94 | ||
|
0969b8d744 | ||
|
fac5ec0f1b | ||
|
5914932728 | ||
|
b119be8bbf | ||
|
24424c1d52 | ||
|
9db254d2f2 | ||
|
9368af2187 | ||
|
f1b6519825 | ||
|
96d01de045 | ||
|
5b2c8ce671 | ||
|
f0f98b40b6 | ||
|
135454645a | ||
|
8bdeff3c83 | ||
|
33d059d002 | ||
|
82ba0b2615 | ||
|
108ca5ae5d | ||
|
4086b1150b | ||
|
b84a2bd930 | ||
|
a14eeda825 | ||
|
5d63aaf510 | ||
|
a570aae1a1 | ||
|
f82dc4eecf | ||
|
29158e7c9a | ||
|
ebf9e8031b | ||
|
aeaa0d445e | ||
|
4b645ef1c0 | ||
|
98c4b88961 | ||
|
2d30eb60af | ||
|
e08f6513cf | ||
|
65f6a1c8d3 | ||
|
7550c37fb6 | ||
|
39b9b981ca | ||
|
4fee34b661 | ||
|
c1770e2217 | ||
|
af3937e23f | ||
|
7e3edb9cb6 | ||
|
b5b577cf8b | ||
|
3d8886359c | ||
|
c0d2dd6aa9 | ||
|
1a4504de1c | ||
|
e2a6f056c9 | ||
|
982fff9989 | ||
|
0673920103 | ||
|
e73b56ed4d | ||
|
1c3ab835a6 | ||
|
16e7d1586d | ||
|
140a3ebf4b | ||
|
f7d90b7c59 | ||
|
85e2d0c5c2 | ||
|
6c506c989c | ||
|
38860ace07 | ||
|
e11d918a82 | ||
|
22d67b51c3 | ||
|
5583674974 | ||
|
ad17c6dfec | ||
|
ad299ed96d | ||
|
3525ef88c1 | ||
|
1f600430ea | ||
|
18cad94748 | ||
|
ae37a1e406 | ||
|
2e3416ef43 | ||
|
4fcfe72a0f | ||
|
b7a5741f70 | ||
|
ff8177e147 | ||
|
c84e795d75 | ||
|
365fb4aede | ||
|
5c7b953fb4 | ||
|
fd9defcb5e | ||
|
7c6e3e6d6f | ||
|
72b46024c0 | ||
|
ab0e73745c | ||
|
7c72632f73 | ||
|
ab3014d5af | ||
|
b32ebe51a0 | ||
|
e9ff1bfd9d | ||
|
42d1c1ec3a | ||
|
4107464c48 | ||
|
5ee365cffb | ||
|
208def0baa | ||
|
46378af4c9 | ||
|
1bb2c0de93 | ||
|
99674b38c5 | ||
|
2c8e69bbf0 | ||
|
6c1dd9ecab | ||
|
e99bd102e4 | ||
|
60d01d4c49 | ||
|
a712853d8c | ||
|
430cafbaf6 | ||
|
af8922bb82 | ||
|
22d6d1e84a | ||
|
922fbbc96e | ||
|
3e588fa75e | ||
|
a11faf346b | ||
|
08be8526eb | ||
|
943e5256f4 | ||
|
e2ef42a8fb | ||
|
1fb3a79d06 | ||
|
5f4afe9dd1 | ||
|
630e27d170 | ||
|
f9b6fb9831 | ||
|
880d6f4f3f | ||
|
2d58e20e85 | ||
|
6604be1698 | ||
|
89093222dc | ||
|
81d1b30b9e | ||
|
e664ae6a57 | ||
|
c4d886bde3 | ||
|
b5ce1f14ff | ||
|
b6df68a5ff | ||
|
4c181d743f | ||
|
167f3ce1aa | ||
|
6d530b48a7 | ||
|
f97325fd6a | ||
|
257a14104e | ||
|
d00699bde9 | ||
|
1b25daed4b | ||
|
7616582094 | ||
|
dde4d24e9b | ||
|
30fbb53395 | ||
|
6543ac47e3 | ||
|
0ec97c10a8 | ||
|
fdabeb75c4 | ||
|
416738a8a8 | ||
|
5f08cf0759 | ||
|
15e11254fd | ||
|
388880395e | ||
|
4ba33c8482 | ||
|
3ea4059d00 | ||
|
5dce2f5073 | ||
|
64fd410d43 | ||
|
18b79aace8 | ||
|
98f07cdf50 | ||
|
dced053569 | ||
|
ac076f5035 | ||
|
c783de1d74 | ||
|
979e1da8f7 | ||
|
dcaaa04796 | ||
|
5ddfcec174 | ||
|
598d75dc88 | ||
|
01cd25d8b2 | ||
|
a53e4a1c83 | ||
|
494ea8b6a2 | ||
|
a283097491 | ||
|
95bb60981b | ||
|
935c656520 | ||
|
7acb40234b | ||
|
eb17a0d6f6 | ||
|
b7d72bd2e3 | ||
|
28539c7466 | ||
|
9297023201 | ||
|
557cf8a934 | ||
|
4ab533bf7e | ||
|
bbee245d96 | ||
|
91167f1a28 | ||
|
93b5e1b3a4 | ||
|
ae1d5691ac | ||
|
87eeeaf65f | ||
|
ccd3d6b71c | ||
|
8c4b8332e8 | ||
|
a4e5c008ec | ||
|
ad907a6565 | ||
|
c5991a8456 | ||
|
31b36dec76 | ||
|
42f2908dc1 | ||
|
479d3025e5 | ||
|
0d7ca349c6 | ||
|
44ee4a6112 | ||
|
e6c7a23cfd | ||
|
71daba634e | ||
|
581cb0c551 | ||
|
629e2e5200 | ||
|
043c890bf3 | ||
|
38ad763613 | ||
|
2b46f8c3b8 | ||
|
231916f0a7 | ||
|
37cedd74a9 | ||
|
438ddffc85 | ||
|
bd97270626 | ||
|
00d4c42a57 | ||
|
fb57354a3a | ||
|
9c9c93c0ee | ||
|
835e6c4611 | ||
|
073caa4469 | ||
|
b4b950e5e4 | ||
|
bda401bd93 | ||
|
2136d21af9 | ||
|
5f87eb936a | ||
|
8f97203cb4 | ||
|
fcae09c41a | ||
|
dba3824cbc | ||
|
b0d194724c | ||
|
fd9a4990e1 | ||
|
7656af8d45 | ||
|
3a5db1f7ef | ||
|
ee8d17a40b | ||
|
6e7c64d7d4 | ||
|
73d7d9e679 | ||
|
4cc4744a3c | ||
|
54b90237fd | ||
|
8d2b55aad9 | ||
|
54103c28d1 | ||
|
84005a7f85 | ||
|
52a8581f50 | ||
|
c19ffdac1b | ||
|
9a0313d450 | ||
|
b4690d97b2 | ||
|
9dfa8b774a | ||
|
abfabe10f2 | ||
|
d0c9329a6a | ||
|
f99cb1136a | ||
|
537e76499a | ||
|
597d7a7a4e | ||
|
cb97aa2fe1 | ||
|
66184450ea | ||
|
f1a43970b6 | ||
|
af9524fb2e | ||
|
26bf0a90d7 | ||
|
d0e765fa39 | ||
|
fbc5c6eec0 | ||
|
6f4516f439 | ||
|
ba61a9409a | ||
|
4dd5e9f66d | ||
|
15eabf37fb | ||
|
b5803994c6 | ||
|
ca19201899 | ||
|
d61937a41b | ||
|
150eb5bbf3 | ||
|
2bfef3bdf7 | ||
|
3b11865f3b | ||
|
c1fd1d945e | ||
|
c5de17d34c | ||
|
6ddd3cee9e | ||
|
03a69cfd9c | ||
|
564e9b13b9 | ||
|
eac1cfeecd | ||
|
e95abb8383 | ||
|
814dd399e9 | ||
|
1b86674735 | ||
|
805ff46048 | ||
|
c0af27ec93 | ||
|
ce2687b772 | ||
|
69accb784d | ||
|
05c0b01eb7 | ||
|
c1c5458aaa | ||
|
79f0a44bee | ||
|
e23e72f845 | ||
|
5f325789cd | ||
|
5bf881cd2a | ||
|
95db340034 | ||
|
8ffe5a7389 | ||
|
09825992b5 | ||
|
9fe527de6b | ||
|
bb6812c4df | ||
|
4b9287a11c | ||
|
2cb4578316 | ||
|
70c2ecfb31 | ||
|
3c7ecd155d | ||
|
bdce53e44b | ||
|
6d4adc8ef4 | ||
|
067aa25606 | ||
|
eab6a83edc | ||
|
45291c0f37 | ||
|
35c52e9cdb | ||
|
b69b4df687 | ||
|
8e553ab669 | ||
|
a3c7a1dce7 | ||
|
260c032fe0 | ||
|
7e58bcd30d | ||
|
d33d9c8077 | ||
|
b309f36c29 | ||
|
65c807a878 | ||
|
b8560b210e | ||
|
37417fe6b4 | ||
|
0d3c06b558 | ||
|
199e6e9517 | ||
|
f63dacbd1e | ||
|
bb6ec588ef | ||
|
a650d75a73 | ||
|
a3eb10f5a4 | ||
|
ab5f5182b1 | ||
|
ae72720d42 | ||
|
1769d86a88 | ||
|
15b259d853 | ||
|
cb38dedb11 | ||
|
62b540b95c | ||
|
b6c01333b0 | ||
|
0c2b9805e4 | ||
|
0cff9f51b5 | ||
|
7d75990f30 | ||
|
ee608c72ae | ||
|
6ddd67a8d7 | ||
|
66a1b10e00 | ||
|
c9245122f5 | ||
|
dfbb623102 | ||
|
dc0a66c700 | ||
|
55e1d41f25 | ||
|
da9ecb08a6 | ||
|
6a4b68e491 | ||
|
07b2acf237 | ||
|
089e54426c | ||
|
ac611373aa | ||
|
e37561ad39 | ||
|
34df7b156e | ||
|
ed61fd1c1a | ||
|
3aad0c2f90 | ||
|
0d0cca6eaf | ||
|
419cbd7319 | ||
|
3e5bb35342 | ||
|
28aa185a73 | ||
|
0f5b5c6d53 | ||
|
b1a0b1db87 | ||
|
4d9bc88ada | ||
|
672113248a | ||
|
2441fd4834 | ||
|
8569bb3af4 | ||
|
ee1bd06249 | ||
|
7fe8445162 | ||
|
18cb0cd504 | ||
|
414a7fa819 | ||
|
8baccc0909 | ||
|
34c8ed2f6b | ||
|
0ea2c6881d | ||
|
ca2ef94978 | ||
|
30c39210cf | ||
|
811fd789c7 | ||
|
80c33a2d26 | ||
|
eeed6624de | ||
|
0575f2ecbd | ||
|
01bc70c1c7 | ||
|
bffcd4000d | ||
|
79864e3d53 | ||
|
bd231022c6 | ||
|
6587359cfb | ||
|
5cb1564362 | ||
|
703a669261 | ||
|
16246aa51f | ||
|
9df77a8ef7 | ||
|
813d9682e0 | ||
|
69f50e5d58 | ||
|
e218441710 | ||
|
446fa9092c | ||
|
425622bf5e | ||
|
ef234f2529 | ||
|
df594df1fe | ||
|
c0ab5f0279 | ||
|
fa664dd056 | ||
|
4e08e39c4c | ||
|
0a00a81d70 | ||
|
7829916289 | ||
|
f6790ff22c | ||
|
4aae6e0c81 | ||
|
238943af62 | ||
|
4efaaed304 | ||
|
3896e7daad | ||
|
c08636c505 | ||
|
ca2a89aa1d | ||
|
e406d75200 | ||
|
68ef2d4e61 | ||
|
3bfa8a6c01 | ||
|
33e7e8cf4f | ||
|
587c1a1a4a | ||
|
0f938c33c6 | ||
|
56ea23df97 | ||
|
c07ac96fca | ||
|
6fed5fadd0 | ||
|
0e85fa8e85 | ||
|
195fe4d17c | ||
|
c894a92aab | ||
|
b3328bd34a | ||
|
bcfd1de35b | ||
|
581df0873d | ||
|
d028ca9953 | ||
|
e0f672acec | ||
|
284a6c07a8 | ||
|
baeb657ebf | ||
|
74dcd98d7b | ||
|
2830aebc83 | ||
|
2fec062f42 | ||
|
1ef4afc2ba | ||
|
b8d1553bb6 | ||
|
b5f0c46711 | ||
|
a8878aed9f | ||
|
65ad9d12db | ||
|
c9c08baffa | ||
|
9f6bf97fe6 | ||
|
e776ad35e7 | ||
|
d4fe9c4c5a | ||
|
eb690d4229 | ||
|
d5741b609d | ||
|
4044771843 | ||
|
6dcfc4c706 | ||
|
5b88baedab | ||
|
89e2b498f7 | ||
|
6c9e4a175d | ||
|
9a8a329df2 | ||
|
782e200020 | ||
|
554c53fcba | ||
|
75c4b48daf | ||
|
60cc27570d | ||
|
738da25881 | ||
|
8e99791721 | ||
|
5272c98289 | ||
|
4df1f39dad | ||
|
b022621108 | ||
|
7d8915cb28 | ||
|
2aefce3aed | ||
|
3110e917a5 | ||
|
a8768c5fc3 | ||
|
2f5e5e2b21 | ||
|
fbf251f72e | ||
|
fe2862d79a | ||
|
2849b2c4a6 | ||
|
4b5ff4a093 | ||
|
5464b8f1cf | ||
|
c37f493cc8 | ||
|
7517cbe14f | ||
|
a34af2edbd | ||
|
d10a9b2fca | ||
|
26cacd813d | ||
|
cbfd0e54c4 | ||
|
8318a4a819 | ||
|
1cd0e8e48d | ||
|
eb21e1dde5 | ||
|
d170f44a88 | ||
|
3d9cec54c2 | ||
|
ad46571905 | ||
|
cddd9951f2 | ||
|
9e41333d22 | ||
|
aa2fcd1ff3 | ||
|
6e16620a8d | ||
|
d0c3dbca70 | ||
|
508e7e3d70 | ||
|
edfdaeea3a | ||
|
6780e34da0 | ||
|
7f6d0b30fb | ||
|
e5a0050cb5 | ||
|
2c7f040b21 | ||
|
53c164e532 | ||
|
ece088b9b3 | ||
|
bab9ab0a93 | ||
|
0abf89acbb | ||
|
f99c20c1de | ||
|
63aeab827a | ||
|
930f37fa17 | ||
|
61eed341ff | ||
|
f087893557 | ||
|
808327b697 | ||
|
0519cd4abd | ||
|
b30e79069c | ||
|
a71f834e36 | ||
|
2973d1db92 | ||
|
928aec22e9 | ||
|
15a28473e0 | ||
|
d4c868c1e4 | ||
|
6a76edd101 | ||
|
a494951831 | ||
|
a18f9c05a9 | ||
|
351f849da8 | ||
|
efa9ec7bc6 | ||
|
44351912b4 | ||
|
a3fd2111d4 | ||
|
ed39a2ebf6 | ||
|
8643ced9f4 | ||
|
9c2b530d4d | ||
|
d58a2c3f71 | ||
|
3a4fa0abe0 | ||
|
89611742ed | ||
|
9e41d32592 | ||
|
83d184ca44 | ||
|
7e63ef1a6a | ||
|
bff17853d7 | ||
|
6a0bd14680 | ||
|
a7fa67e726 |
241 changed files with 6362 additions and 4326 deletions
29
.github/ISSUE_TEMPLATE/bug-issue-template.md
vendored
29
.github/ISSUE_TEMPLATE/bug-issue-template.md
vendored
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
name: Bug issue template
|
||||
about: Vanced Manager Bug template
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Please only report your issue here, if all points below are true**
|
||||
- I installed the App from [vancedapp.com](https://vancedapp.com), this github repository or the Vanced Discord server
|
||||
- I am using the latest version
|
||||
- This is an issue in the Vanced Manager app (NOT Youtube Vanced)
|
||||
- This issue keeps re-occurring every time I try
|
||||
- For MIUI users: I disabled MIUI optimisation
|
||||
|
||||
**Phone Specifications:**
|
||||
- Brand:
|
||||
- Operating System:
|
||||
- Android Version:
|
||||
- Vanced Manager Version:
|
||||
|
||||
**Please describe the problem you are having in as much detail as possible:**
|
||||
|
||||
|
||||
**Steps to reproduce:**
|
||||
|
||||
|
||||
**Further details:**
|
41
.github/ISSUE_TEMPLATE/bug-issue-template.yml
vendored
Normal file
41
.github/ISSUE_TEMPLATE/bug-issue-template.yml
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
name: Bug Report
|
||||
description: File a bug report
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
assignees:
|
||||
- X1nto
|
||||
body:
|
||||
- type: textarea
|
||||
id: device-info
|
||||
attributes:
|
||||
label: Device
|
||||
description: What device are you using?
|
||||
value: |
|
||||
Device:
|
||||
Operating System:
|
||||
Android Version:
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: manager-version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of Vanced Manager are you using?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: bug-description
|
||||
attributes:
|
||||
label: Bug Description
|
||||
description: Describe the bug and how to reproduce it in as much detail as possible.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: is-manager-bug
|
||||
attributes:
|
||||
label: Additional checks
|
||||
options:
|
||||
- label: I have checked other bug reports and this is not a duplicate.
|
||||
required: true
|
||||
- label: This is a bug in Vanced Manager and NOT YouTube Vanced/YouTube Vanced Music/Vanced microG.
|
||||
required: true
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
blank_issues_enabled: false
|
29
.github/ISSUE_TEMPLATE/feature-issue-template.yml
vendored
Normal file
29
.github/ISSUE_TEMPLATE/feature-issue-template.yml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
name: Feature Request
|
||||
description: Request a feature
|
||||
title: "[Feature]: "
|
||||
labels: ["enhancement"]
|
||||
assignees:
|
||||
- X1nto
|
||||
body:
|
||||
- type: textarea
|
||||
id: suggestion
|
||||
attributes:
|
||||
label: Suggestion
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: suggestion-relevancy
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: Why is this suggestion relevant?
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: is-manager-suggestion
|
||||
attributes:
|
||||
label: Additional checks
|
||||
options:
|
||||
- label: I have checked other feature requests and this is not a duplicate.
|
||||
required: true
|
||||
- label: This is a suggestion for Vanced Manager and NOT YouTube Vanced/YouTube Vanced Music/Vanced microG.
|
||||
required: true
|
13
.github/workflows/debug.yml
vendored
13
.github/workflows/debug.yml
vendored
|
@ -4,27 +4,28 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: set up JDK 1.8
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
java-version: 11
|
||||
|
||||
- name: Grant rights
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
- name: Build project with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Build Debug APK with Gradle
|
||||
- name: Build debug APK with Gradle
|
||||
run: ./gradlew assembleDebug
|
||||
|
||||
- name: Upload Debug
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -6,5 +6,5 @@ app/src/main/java/com/vanced/manager/core/base/DummyJava.java
|
|||
app/build/
|
||||
app/release
|
||||
local.properties
|
||||
/.github/
|
||||
*.iml
|
||||
.vscode/
|
||||
|
|
85
README.md
85
README.md
|
@ -1,35 +1,64 @@
|
|||
Pull requests should be made to the Dev branch as that is the working branch, master is for release code.
|
||||
======
|
||||
For anyone who wants to provide translations please submit them to https://crowdin.com/project/vanced-manager as we also use it for YouTube Vanced. Any issues with translations should be posted there too.
|
||||
======
|
||||
Vanced FAQ (from the faq branch) now available on the playstore! https://play.google.com/store/apps/details?id=com.vanced.faq
|
||||
## The FAQ app has just been suspended due to "Impersonating Vanced", an appeal has been filed, reporting other apps which impersonate Vanced on the play store is appreciated.
|
||||
|
||||
[![Github All Releases](https://img.shields.io/github/downloads/YTVanced/VancedManager/total.svg)](https://github.com/YTVanced/VancedManager/releases/latest) [![Github All Releases](https://img.shields.io/github/release/YTVanced/VancedManager.svg)](https://github.com/YTVanced/VancedManager/releases/latest)
|
||||
# Vanced Manager
|
||||
Hi, when we released Vanced 15.05.54, people were upset because it used the .apks format, which was way harder to install than a traditional .apk file. Even though we wrote clear instructions on how to install the new Vanced build, people still couldn't figure it out.
|
||||
Then we thought, "why don't we make a manager for vanced, which will download, update and uninstall Vanced and MicroG, have an easy and understandable UI and be less than 10mb?" and that's how Vanced Manager was born.
|
||||
<div>
|
||||
|
||||
[![Github All Releases](https://img.shields.io/github/downloads/YTVanced/VancedManager/total.svg?style=for-the-badge)](https://github.com/YTVanced/VancedManager/releases/latest) [![Github All Releases](https://img.shields.io/github/release/YTVanced/VancedManager.svg?style=for-the-badge)](https://github.com/YTVanced/VancedManager/releases/latest)
|
||||
|
||||
</div>
|
||||
|
||||
## Introduction
|
||||
|
||||
Hi, when we released Vanced 15.05.54, people were upset because it used the .apks format, which was burdensome to install than a traditional .apk file. Even though we wrote clear instructions on how to install the new Vanced build, people still couldn't figure it out.
|
||||
|
||||
Then we thought, "why don't we make a manager for vanced, which will download, update and uninstall Vanced and MicroG, have an easy and user-friendly UI and be less than 10mb?" and that's how Vanced Manager was born.
|
||||
|
||||
After 3 months of development, we are finally ready to introduce Vanced Manager to the masses. Vanced manager can easily install and uninstall vanced and microg, has various settings for customisation and better experience. The Manager comes with an easy-to-use interface
|
||||
After 3 months of development, we are finally ready to Introduce [Vanced Manager](https://github.com/YTVanced/VancedManager) to the masses!!
|
||||
|
||||
##### Background download/installation feature is no longer supported due to problems with some ROMs, please do NOT report issues regarding background activity.
|
||||
## Features
|
||||
|
||||
## Vanced Developers
|
||||
- xfileFIN
|
||||
- KevinX8
|
||||
- Zanezam
|
||||
- Laura Almeida
|
||||
- Vanced manager can easily install and uninstall Vanced and MicroG.
|
||||
- It has various settings for customization and better experience.
|
||||
- The Manager comes with an easy-to-use Interface.
|
||||
|
||||
## Vanced Manager Developers
|
||||
- Xinto (X1nto)
|
||||
- Koopah (ostajic)
|
||||
</br>
|
||||
|
||||
## Contributors
|
||||
- AioiLight
|
||||
- HaliksaR
|
||||
<div class="note">
|
||||
<p><strong>NOTE: </strong>Background download/installation feature is no longer supported due to problems with some ROMs, please <b>DO NOT</b> report issues regarding background activity.</p>
|
||||
</div>
|
||||
|
||||
<!-- ##### Background download/installation feature is no longer supported due to problems with some ROMs, please do NOT report issues regarding background activity. -->
|
||||
|
||||
## Contributions
|
||||
Pull Requests should be made to the [Dev](https://github.com/YTVanced/VancedManager) Branch as that is the working branch, master is for Release code only.
|
||||
|
||||
For anyone who wants to provide translations please submit them to this [link](https://crowdin.com/project/vanced-manager) as we also use it for YouTube Vanced. Any issues with translations should be posted there too.
|
||||
|
||||
## TODO
|
||||
- [ ] Clean up the ViewModel and DataModel code
|
||||
- [ ] Migrate to Jetpack Compose when it's officially released
|
||||
|
||||
## Building
|
||||
|
||||
<div>
|
||||
|
||||
[![Build](https://github.com/YTVanced/VancedManager/actions/workflows/debug.yml/badge.svg?branch=dev)](https://github.com/YTVanced/VancedManager/actions/workflows/debug.yml)
|
||||
|
||||
</div>
|
||||
|
||||
## Using Android Studio
|
||||
Clone the Repository, open it in Android Studio and build the application.
|
||||
|
||||
## Google Advanced Protection Program
|
||||
If you are using this feature on your Google account, you must either disable it or log out from your Google account before installing Youtube Vanced via Vanced Manager.
|
||||
The Google Advanced Protection Program does not allow the installation of apps from unknown sources. These security measures are tied to the protected account and not the device. After the installation, you will be able to log back in or enroll again into the program.
|
||||
|
||||
## Using Command Line
|
||||
#### On Windows:
|
||||
```powershell
|
||||
.\gradlew.bat assembleDebug
|
||||
```
|
||||
#### On Linux/macOS:
|
||||
```bash
|
||||
chmod +x gradlew
|
||||
./gradlew assembleDebug
|
||||
```
|
||||
|
||||
## Credits
|
||||
- topjohnwu for his wonderful [LibSU](https://github.com/topjohnwu/libsu)
|
||||
- aefyr for [SAI](https://github.com/aefyr/SAI), which was an inspiration for our Manager
|
||||
- kittinunf for [Fuel](https://github.com/kittinunf/Fuel) HTTP client
|
||||
- cbeust for [klaxon](https://github.com/cbeust/klaxon) JSON parser
|
||||
|
|
|
@ -48,6 +48,3 @@ Vanced Manager sucks 100% of your CPU to mine Bitcoins, this is a new technique
|
|||
![Zanezam](https://i.imgur.com/QVcXA6q.png)
|
||||
- Laura Almeida
|
||||
![Laura Almeida](https://i.imgur.com/ovVD939.png)
|
||||
|
||||
###### If someone is reading this pls help me, KevinX8 is bullying me and forces me to develop manager. please send bobs and veganas and call 911
|
||||
|
||||
|
|
|
@ -6,28 +6,27 @@ plugins {
|
|||
id("com.google.firebase.crashlytics")
|
||||
id("com.google.firebase.firebase-perf")
|
||||
id("androidx.navigation.safeargs.kotlin")
|
||||
id("kotlin-android")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion(30)
|
||||
compileSdk = 31
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.vanced.manager"
|
||||
minSdkVersion(21)
|
||||
targetSdkVersion(30)
|
||||
versionCode = 230
|
||||
versionName = "2.3.0 (MicroShitMoment)"
|
||||
minSdk = 21
|
||||
targetSdk = 31
|
||||
versionCode = 262
|
||||
versionName = "2.6.2 (Crimson)"
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
|
||||
buildConfigField("String[]", "MANAGER_LANGUAGES", "{" + getLanguages() + "}")
|
||||
buildConfigField("Boolean", "ENABLE_CROWDIN_AUTH", "false")
|
||||
buildConfigField("String", "CROWDIN_HASH", "\"${System.getenv("CROWDIN_HASH")}\"")
|
||||
buildConfigField("String", "CROWDIN_CLIENT_ID", "\"${System.getenv("CROWDIN_CLIENT_ID")}\"")
|
||||
buildConfigField("String", "CROWDIN_CLIENT_SECRET", "\"${System.getenv("CROWDIN_CLIENT_SECRET")}\"")
|
||||
buildConfigField("String[]", "MANAGER_LANGUAGES", "{$languages}")
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
lint {
|
||||
disable("MissingTranslation", "ExtraTranslation")
|
||||
}
|
||||
|
||||
|
@ -43,13 +42,14 @@ android {
|
|||
}
|
||||
|
||||
buildFeatures {
|
||||
dataBinding = true // ObservableField migrate to flow or liveData
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude("META-INF/DEPENDENCIES")
|
||||
exclude("META-INF/*.kotlin_module")
|
||||
resources {
|
||||
excludes += "META-INF/DEPENDENCIES"
|
||||
excludes += "META-INF/*.kotlin_module"
|
||||
}
|
||||
}
|
||||
|
||||
// To inline the bytecode built with JVM target 1.8 into
|
||||
|
@ -68,75 +68,72 @@ android {
|
|||
|
||||
}
|
||||
|
||||
fun getLanguages(): String {
|
||||
val languages: String get() {
|
||||
val langs = arrayListOf("en", "bn_BD", "bn_IN", "pa_IN", "pa_PK", "pt_BR", "pt_PT", "zh_CN", "zh_TW")
|
||||
val exceptions = arrayOf("bn", "pa", "pt", "zh")
|
||||
|
||||
File("$projectDir/src/main/res").listFiles()?.forEach { dir ->
|
||||
if (dir.name.startsWith("values-") && !dir.name.contains("v23")) {
|
||||
val dirname = dir.name.substringAfter("-").substringBefore("-")
|
||||
if (!exceptions.any { dirname == it }) {
|
||||
langs.add(dirname)
|
||||
}
|
||||
File("$projectDir/src/main/res").listFiles()?.filter {
|
||||
val name = it.name
|
||||
name.startsWith("values-") && !name.contains("v23")
|
||||
}?.forEach { dir ->
|
||||
val dirname = dir.name.substringAfter("-").substringBefore("-")
|
||||
if (!exceptions.contains(dirname)) {
|
||||
langs.add(dirname)
|
||||
}
|
||||
}
|
||||
return langs.joinToString(", ") { "\"$it\"" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(project(":core-presentation"))
|
||||
implementation(project(":core-ui"))
|
||||
|
||||
implementation(project(":library-network"))
|
||||
|
||||
// Kotlin
|
||||
// Kotlin
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
implementation(kotlin("reflect"))
|
||||
|
||||
// AndroidX
|
||||
implementation("androidx.appcompat:appcompat:1.2.0")
|
||||
// AndroidX
|
||||
implementation("androidx.appcompat:appcompat:1.3.1")
|
||||
implementation("androidx.browser:browser:1.3.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
|
||||
implementation("androidx.core:core-ktx:1.3.2")
|
||||
implementation("androidx.fragment:fragment-ktx:1.2.5")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-core-ktx:2.2.0")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.1")
|
||||
implementation("androidx.core:core-ktx:1.6.0")
|
||||
implementation("androidx.fragment:fragment-ktx:1.3.6")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-core-ktx:2.3.1")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
|
||||
implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.3.2")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.3.2")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.3.5")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.3.5")
|
||||
implementation("androidx.preference:preference-ktx:1.1.1")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("androidx.work:work-runtime-ktx:2.7.0-rc01")
|
||||
|
||||
//Appearance
|
||||
implementation("com.github.madrapps:pikolo:2.0.1")
|
||||
implementation("com.google.android.material:material:1.3.0-rc01")
|
||||
implementation("com.github.madrapps:pikolo:2.0.2")
|
||||
implementation("com.google.android.material:material:1.5.0-alpha04")
|
||||
|
||||
// JSON parser
|
||||
implementation("com.beust:klaxon:5.4")
|
||||
|
||||
// Crowdin
|
||||
implementation("com.crowdin.platform:mobile-sdk:1.2.0")
|
||||
|
||||
// Tips
|
||||
implementation("com.github.florent37:viewtooltip:1.2.2")
|
||||
implementation("com.beust:klaxon:5.5")
|
||||
|
||||
// HTTP networking
|
||||
implementation("com.github.kittinunf.fuel:fuel:2.3.0")
|
||||
implementation("com.github.kittinunf.fuel:fuel-coroutines:2.2.3")
|
||||
implementation("com.github.kittinunf.fuel:fuel-json:2.2.3")
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
|
||||
implementation("com.github.kittinunf.fuel:fuel:2.3.1")
|
||||
implementation("com.github.kittinunf.fuel:fuel-coroutines:2.3.1")
|
||||
implementation("com.github.kittinunf.fuel:fuel-json:2.3.1")
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:4.9.2")
|
||||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
||||
|
||||
// Root permissions
|
||||
implementation("com.github.topjohnwu.libsu:core:3.0.2")
|
||||
implementation("com.github.topjohnwu.libsu:io:3.0.2")
|
||||
val libsuVersion = "3.1.2"
|
||||
implementation("com.github.topjohnwu.libsu:core:$libsuVersion")
|
||||
implementation("com.github.topjohnwu.libsu:io:$libsuVersion")
|
||||
//implementation("com.github.topjohnwu.libsu:busybox:$libsuVersion")
|
||||
|
||||
// Layout
|
||||
implementation("com.google.android:flexbox:2.0.1")
|
||||
implementation("com.google.android.flexbox:flexbox:3.0.0")
|
||||
|
||||
// Firebase
|
||||
implementation("com.google.firebase:firebase-analytics-ktx:18.0.1")
|
||||
implementation("com.google.firebase:firebase-crashlytics:17.3.0")
|
||||
implementation("com.google.firebase:firebase-messaging:21.0.1")
|
||||
implementation("com.google.firebase:firebase-perf:19.1.0")
|
||||
implementation("com.google.firebase:firebase-analytics-ktx:19.0.2")
|
||||
implementation("com.google.firebase:firebase-crashlytics:18.2.3")
|
||||
implementation("com.google.firebase:firebase-messaging:22.0.0")
|
||||
implementation("com.google.firebase:firebase-perf:20.0.3")
|
||||
}
|
||||
|
|
2
app/proguard-rules.pro
vendored
2
app/proguard-rules.pro
vendored
|
@ -17,7 +17,7 @@
|
|||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
-keepattributes SourceFile, LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<!-- is required for some Android 5.x devices -->
|
||||
<uses-permission
|
||||
|
@ -21,6 +21,7 @@
|
|||
<package android:name="com.vanced.android.apps.youtube.music" />
|
||||
<package android:name="com.google.android.apps.youtube.music" />
|
||||
<package android:name="com.mgoogle.android.gms" />
|
||||
<package android:name="com.vanced.faq" />
|
||||
<package android:name="com.android.vending" />
|
||||
</queries>
|
||||
|
||||
|
@ -31,13 +32,13 @@
|
|||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
tools:ignore="UnusedAttribute">
|
||||
android:supportsRtl="true">
|
||||
|
||||
<activity
|
||||
android:name=".ui.core.SplashScreenActivity"
|
||||
android:name=".ui.SplashScreenActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/SplashTheme">
|
||||
android:theme="@style/SplashTheme"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
@ -55,10 +56,10 @@
|
|||
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:configChanges="layoutDirection|locale"
|
||||
android:exported="true"
|
||||
android:configChanges="layoutDirection|locale|keyboardHidden|orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/DarkTheme">
|
||||
android:theme="@style/DarkTheme"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
@ -66,8 +67,7 @@
|
|||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data
|
||||
android:scheme="https"
|
||||
android:host="vancedapp.com"
|
||||
android:pathPrefix="/downloads"/>
|
||||
android:host="api.vancedapp.com"/>
|
||||
|
||||
</intent-filter>
|
||||
|
||||
|
@ -76,8 +76,8 @@
|
|||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 25 KiB |
|
@ -1,123 +0,0 @@
|
|||
package com.vanced.manager.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.florent37.viewtooltip.ViewTooltip
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.databinding.ViewAppBinding
|
||||
import com.vanced.manager.model.DataModel
|
||||
import com.vanced.manager.model.RootDataModel
|
||||
import com.vanced.manager.ui.dialogs.AppInfoDialog
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
||||
import com.vanced.manager.utils.enableMusic
|
||||
import com.vanced.manager.utils.enableVanced
|
||||
import com.vanced.manager.utils.managerVariant
|
||||
|
||||
class AppListAdapter(
|
||||
private val context: FragmentActivity,
|
||||
private val viewModel: HomeViewModel,
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val tooltip: ViewTooltip
|
||||
) : RecyclerView.Adapter<AppListAdapter.ListViewHolder>() {
|
||||
|
||||
val apps = mutableListOf<String>()
|
||||
private val dataModels = mutableListOf<DataModel?>()
|
||||
private val rootDataModels = mutableListOf<RootDataModel?>()
|
||||
private val prefs = getDefaultSharedPreferences(context)
|
||||
private var itemCount = 0
|
||||
|
||||
private val isRoot = prefs.managerVariant == "root"
|
||||
|
||||
inner class ListViewHolder(private val binding: ViewAppBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
val appCard = binding.appCard
|
||||
fun bind(position: Int) {
|
||||
val dataModel = if (isRoot) rootDataModels[position] else dataModels[position]
|
||||
with(binding) {
|
||||
appName.text = dataModel?.appName
|
||||
dataModel?.buttonTxt?.observe(lifecycleOwner) {
|
||||
appInstallButton.text = it
|
||||
}
|
||||
appInstallButton.setOnClickListener {
|
||||
viewModel.openInstallDialog(it, apps[position])
|
||||
}
|
||||
appUninstall.setOnClickListener {
|
||||
dataModel?.appPkg?.let { it1 -> viewModel.uninstallPackage(it1) }
|
||||
}
|
||||
appLaunch.setOnClickListener {
|
||||
viewModel.launchApp(apps[position], isRoot)
|
||||
}
|
||||
with(dataModel?.isAppInstalled?.value) {
|
||||
appUninstall.isVisible = this == true
|
||||
appLaunch.isVisible = this == true
|
||||
}
|
||||
dataModel?.isAppInstalled?.observe(lifecycleOwner) {
|
||||
appUninstall.isVisible = it
|
||||
appLaunch.isVisible = it
|
||||
}
|
||||
dataModel?.versionName?.observe(lifecycleOwner) {
|
||||
appRemoteVersion.text = it
|
||||
}
|
||||
dataModel?.installedVersionName?.observe(lifecycleOwner) {
|
||||
appInstalledVersion.text = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
|
||||
val view = ViewAppBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
return ListViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
|
||||
holder.bind(position)
|
||||
val dataModel = if (isRoot) rootDataModels[position] else dataModels[position]
|
||||
holder.appCard.setOnClickListener {
|
||||
tooltip.close()
|
||||
AppInfoDialog.newInstance(
|
||||
appName = apps[position],
|
||||
appIcon = dataModel?.appIcon,
|
||||
changelog = dataModel?.changelog?.value
|
||||
).show(context.supportFragmentManager, "info")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = itemCount
|
||||
|
||||
init {
|
||||
|
||||
if (prefs.enableVanced) {
|
||||
if (isRoot) {
|
||||
rootDataModels.add(viewModel.vancedRootModel.value)
|
||||
} else {
|
||||
dataModels.add(viewModel.vancedModel.value)
|
||||
}
|
||||
apps.add(context.getString(R.string.vanced))
|
||||
itemCount++
|
||||
}
|
||||
|
||||
if (prefs.enableMusic) {
|
||||
if (isRoot) {
|
||||
rootDataModels.add(viewModel.musicRootModel.value)
|
||||
} else {
|
||||
dataModels.add(viewModel.musicModel.value)
|
||||
}
|
||||
apps.add(context.getString(R.string.music))
|
||||
itemCount++
|
||||
}
|
||||
|
||||
if (!isRoot) {
|
||||
dataModels.add(viewModel.microgModel.value)
|
||||
apps.add(context.getString(R.string.microg))
|
||||
itemCount++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
package com.vanced.manager.adapter
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.core.animation.addListener
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.databinding.ViewAppExpandableBinding
|
||||
import com.vanced.manager.model.ButtonTag
|
||||
import com.vanced.manager.model.DataModel
|
||||
import com.vanced.manager.ui.dialogs.AppInfoDialog
|
||||
import com.vanced.manager.ui.dialogs.AppUninstallDialog
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
||||
import com.vanced.manager.utils.*
|
||||
|
||||
class ExpandableAppListAdapter(
|
||||
private val activity: FragmentActivity,
|
||||
private val viewModel: HomeViewModel
|
||||
) : RecyclerView.Adapter<ExpandableAppListAdapter.ListViewHolder>() {
|
||||
|
||||
private val apps = mutableListOf<String>()
|
||||
private val dataModels = mutableListOf<DataModel?>()
|
||||
private val prefs = getDefaultSharedPreferences(activity)
|
||||
|
||||
private val isRoot = prefs.managerVariant == "root"
|
||||
|
||||
private var isAnimationRunning = false
|
||||
|
||||
inner class ListViewHolder(private val binding: ViewAppExpandableBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
private var isExpanded = false
|
||||
|
||||
fun bind(position: Int) {
|
||||
val dataModel = dataModels[position]
|
||||
with(binding) {
|
||||
appTitle.text = dataModel?.appName
|
||||
appDescription.text = dataModel?.appDescription
|
||||
dataModel?.appIcon?.let { appIcon.setImageResource(it) }
|
||||
appClickableLayout.setOnClickListener {
|
||||
if (isAnimationRunning) return@setOnClickListener
|
||||
val rootHeight = root.measuredHeight
|
||||
val expandedViewHeight = appExpandedView.height
|
||||
val expandedTranslation = appClickableLayout.height.toFloat()
|
||||
|
||||
when (isExpanded.also { isExpanded = !isExpanded }) {
|
||||
true -> {
|
||||
appExpandedView.toggle(0f, 0.8f, -expandedTranslation)
|
||||
root.toggleCard(rootHeight - expandedViewHeight)
|
||||
appExpandArrow.rotateArrow(90f)
|
||||
}
|
||||
false -> {
|
||||
root.toggleCard(rootHeight + expandedViewHeight)
|
||||
appExpandedView.toggle(1f, 1f, expandedTranslation)
|
||||
appExpandArrow.rotateArrow(-90f)
|
||||
}
|
||||
}
|
||||
}
|
||||
appUninstall.setOnClickListener {
|
||||
AppUninstallDialog.newInstance(
|
||||
dataModel?.appName,
|
||||
dataModel?.appPkg
|
||||
).show(activity.supportFragmentManager, null)
|
||||
}
|
||||
appLaunch.setOnClickListener {
|
||||
viewModel.launchApp(apps[position], isRoot)
|
||||
}
|
||||
appInfo.setOnClickListener {
|
||||
AppInfoDialog.newInstance(
|
||||
appName = apps[position],
|
||||
appIcon = dataModel?.appIcon,
|
||||
changelog = dataModel?.changelog?.value
|
||||
).show(activity.supportFragmentManager, "info")
|
||||
}
|
||||
dataModel?.buttonTag?.observe(activity) { buttonTag ->
|
||||
appDownload.apply {
|
||||
setOnClickListener {
|
||||
viewModel.openInstallDialog(
|
||||
activity.supportFragmentManager,
|
||||
buttonTag,
|
||||
apps[position]
|
||||
)
|
||||
}
|
||||
appDownload.setIconResource(buttonTag.image)
|
||||
contentDescription = activity.getString(
|
||||
when (buttonTag) {
|
||||
ButtonTag.UPDATE -> R.string.accessibility_update
|
||||
ButtonTag.REINSTALL -> R.string.accessibility_reinstall
|
||||
else -> R.string.accessibility_download
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
dataModel?.isAppInstalled?.observe(activity) {
|
||||
appUninstall.isVisible = it
|
||||
appLaunch.isVisible = it
|
||||
}
|
||||
dataModel?.versionName?.observe(activity) {
|
||||
appVersionLatest.text = it
|
||||
appDownload.isGone = it == activity.getString(R.string.unavailable)
|
||||
}
|
||||
dataModel?.installedVersionName?.observe(activity) {
|
||||
appVersionInstalled.text = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
|
||||
val view = ViewAppExpandableBinding.inflate(LayoutInflater.from(activity), parent, false)
|
||||
return ListViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
|
||||
holder.bind(position)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = apps.size
|
||||
|
||||
private fun ImageView.rotateArrow(degrees: Float) {
|
||||
animate().apply {
|
||||
duration = animationDuration
|
||||
rotation(degrees)
|
||||
}
|
||||
}
|
||||
|
||||
private fun View.toggle(
|
||||
alpha: Float,
|
||||
scale: Float,
|
||||
translation: Float
|
||||
) {
|
||||
animate().apply {
|
||||
duration = animationDuration
|
||||
scaleX(scale)
|
||||
scaleY(scale)
|
||||
alpha(alpha)
|
||||
translationYBy(translation)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MaterialCardView.toggleCard(resultHeight: Int) {
|
||||
ValueAnimator.ofInt(measuredHeight, resultHeight).apply {
|
||||
duration = animationDuration
|
||||
addUpdateListener { value ->
|
||||
updateLayoutParams {
|
||||
height = value.animatedValue as Int
|
||||
}
|
||||
}
|
||||
addListener(
|
||||
onStart = {
|
||||
isAnimationRunning = true
|
||||
},
|
||||
onEnd = {
|
||||
isAnimationRunning = false
|
||||
}
|
||||
)
|
||||
}.start()
|
||||
}
|
||||
|
||||
init {
|
||||
|
||||
if (prefs.enableVanced) {
|
||||
if (isRoot) {
|
||||
dataModels.add(viewModel.vancedRootModel.value)
|
||||
} else {
|
||||
dataModels.add(viewModel.vancedModel.value)
|
||||
}
|
||||
apps.add(activity.getString(R.string.vanced))
|
||||
}
|
||||
|
||||
if (prefs.enableMusic) {
|
||||
if (isRoot) {
|
||||
dataModels.add(viewModel.musicRootModel.value)
|
||||
} else {
|
||||
dataModels.add(viewModel.musicModel.value)
|
||||
}
|
||||
apps.add(activity.getString(R.string.music))
|
||||
}
|
||||
|
||||
if (!isRoot) {
|
||||
dataModels.add(viewModel.microgModel.value)
|
||||
apps.add(activity.getString(R.string.microg))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val animationDuration = 250L
|
||||
}
|
||||
|
||||
}
|
|
@ -8,10 +8,13 @@ import com.google.firebase.messaging.FirebaseMessaging
|
|||
import com.vanced.manager.R
|
||||
import com.vanced.manager.databinding.ViewNotificationSettingBinding
|
||||
import com.vanced.manager.model.NotifModel
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
|
||||
class GetNotifAdapter(private val context: Context) :
|
||||
RecyclerView.Adapter<GetNotifAdapter.GetNotifViewHolder>() {
|
||||
|
||||
private val prefs = context.defPrefs
|
||||
|
||||
private val vanced = NotifModel(
|
||||
"Vanced-Update",
|
||||
context.getString(R.string.push_notifications, context.getString(R.string.vanced)),
|
||||
|
@ -33,15 +36,24 @@ class GetNotifAdapter(private val context: Context) :
|
|||
|
||||
private val apps = arrayOf(vanced, music, microg)
|
||||
|
||||
inner class GetNotifViewHolder(val binding: ViewNotificationSettingBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
inner class GetNotifViewHolder(val binding: ViewNotificationSettingBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
val switch = binding.notifSwitch
|
||||
|
||||
fun bind(position: Int) {
|
||||
val app = apps[position]
|
||||
with(binding.notifSwitch) {
|
||||
setKey(apps[position].key)
|
||||
setSummary(apps[position].switchSummary)
|
||||
setTitle(apps[position].switchTitle)
|
||||
setKey(app.key)
|
||||
setSummary(app.switchSummary)
|
||||
setTitle(app.switchTitle)
|
||||
setDefaultValue(true)
|
||||
with(prefs) {
|
||||
setChecked(
|
||||
getBoolean(
|
||||
"enable_" + app.key.substringBefore("_"),
|
||||
true
|
||||
) && getBoolean(app.key, true)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,13 +57,14 @@ class LinkAdapter(
|
|||
|
||||
val links = arrayOf(instagram, youtube, github, website, telegram, twitter, discord, reddit)
|
||||
|
||||
inner class LinkViewHolder(private val binding: ViewSocialLinkBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
inner class LinkViewHolder(private val binding: ViewSocialLinkBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
val logo = binding.linkImage
|
||||
|
||||
fun bind(position: Int) {
|
||||
binding.linkBg.setOnClickListener {
|
||||
viewModel.openUrl(links[position].linkUrl)
|
||||
viewModel.openUrl(context, links[position].linkUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,21 +18,22 @@ class SelectAppsAdapter(private val context: Context) :
|
|||
|
||||
private val vanced = SelectAppModel(
|
||||
context.getString(R.string.vanced),
|
||||
context.getString(R.string.select_apps_vanced),
|
||||
context.getString(R.string.description_vanced),
|
||||
"vanced",
|
||||
prefs.enableVanced
|
||||
)
|
||||
|
||||
private val music = SelectAppModel(
|
||||
context.getString(R.string.music),
|
||||
context.getString(R.string.select_apps_music),
|
||||
context.getString(R.string.description_vanced_music),
|
||||
"music",
|
||||
prefs.enableMusic
|
||||
)
|
||||
|
||||
val apps = arrayOf(vanced, music)
|
||||
|
||||
inner class SelectAppsViewHolder(binding: ViewAppCheckboxBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
inner class SelectAppsViewHolder(binding: ViewAppCheckboxBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
val appName = binding.appCheckboxText
|
||||
val appDescription = binding.appCheckboxDescription
|
||||
val appCard = binding.appCheckboxBg
|
||||
|
|
|
@ -9,15 +9,19 @@ import com.vanced.manager.R
|
|||
import com.vanced.manager.databinding.ViewSponsorBinding
|
||||
import com.vanced.manager.model.SponsorModel
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
||||
import com.vanced.manager.utils.LIGHT
|
||||
import com.vanced.manager.utils.currentTheme
|
||||
|
||||
class SponsorAdapter(
|
||||
private val context: Context,
|
||||
private val viewModel: HomeViewModel,
|
||||
//private val json: ObservableField<JsonObject?>
|
||||
private val viewModel: HomeViewModel
|
||||
) : RecyclerView.Adapter<SponsorAdapter.LinkViewHolder>() {
|
||||
|
||||
private val brave = SponsorModel(
|
||||
AppCompatResources.getDrawable(context, R.drawable.ic_brave),
|
||||
if (currentTheme == LIGHT) AppCompatResources.getDrawable(
|
||||
context,
|
||||
R.drawable.ic_brave_light
|
||||
) else AppCompatResources.getDrawable(context, R.drawable.ic_brave),
|
||||
"Brave",
|
||||
BRAVE
|
||||
)
|
||||
|
@ -38,7 +42,7 @@ class SponsorAdapter(
|
|||
with(binding) {
|
||||
sponsorName.text = sponsors[position].name
|
||||
cardSponsor.setOnClickListener {
|
||||
viewModel.openUrl(sponsors[position].url)
|
||||
viewModel.openUrl(context, sponsors[position].url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package com.vanced.manager.adapter
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.vanced.manager.ui.fragments.GrantRootFragment
|
||||
import com.vanced.manager.ui.fragments.SelectAppsFragment
|
||||
import com.vanced.manager.ui.fragments.WelcomeFragment
|
||||
|
||||
class WelcomePageAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
|
||||
|
||||
override fun getItemCount(): Int = 3
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when (position) {
|
||||
0 -> WelcomeFragment()
|
||||
1 -> SelectAppsFragment()
|
||||
2 -> GrantRootFragment()
|
||||
else -> throw IllegalArgumentException("Unknown fragment")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,21 +1,18 @@
|
|||
package com.vanced.manager.core
|
||||
|
||||
import android.app.Application
|
||||
import android.content.res.Configuration
|
||||
import android.util.Log
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.crowdin.platform.CrowdinConfig
|
||||
import com.crowdin.platform.data.model.AuthConfig
|
||||
import com.crowdin.platform.data.remote.NetworkType
|
||||
import com.vanced.manager.BuildConfig.*
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.vanced.manager.BuildConfig
|
||||
import com.vanced.manager.utils.loadJson
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
import com.vanced.manager.utils.mutableAccentColor
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
open class App: Application() {
|
||||
class App : Application() {
|
||||
|
||||
private val prefs by lazy { getDefaultSharedPreferences(this) }
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
@ -23,30 +20,15 @@ open class App: Application() {
|
|||
override fun onCreate() {
|
||||
scope.launch { loadJson(this@App) }
|
||||
super.onCreate()
|
||||
|
||||
Crowdin.init(this,
|
||||
CrowdinConfig.Builder().apply {
|
||||
withDistributionHash(CROWDIN_HASH)
|
||||
withNetworkType(NetworkType.WIFI)
|
||||
if (ENABLE_CROWDIN_AUTH) {
|
||||
if (prefs.getBoolean("crowdin_real_time", false))
|
||||
withRealTimeUpdates()
|
||||
|
||||
withSourceLanguage("en")
|
||||
withAuthConfig(AuthConfig(CROWDIN_CLIENT_ID, CROWDIN_CLIENT_SECRET, null))
|
||||
withScreenshotEnabled()
|
||||
Log.d("test", "crowdin credentials")
|
||||
}
|
||||
}.build()
|
||||
mutableAccentColor.value = prefs.managerAccent
|
||||
Shell.enableVerboseLogging = BuildConfig.DEBUG
|
||||
Shell.setDefaultBuilder(
|
||||
Shell.Builder
|
||||
.create()
|
||||
.setFlags(Shell.FLAG_REDIRECT_STDERR)
|
||||
//.setInitializers(BusyBoxInstaller::class.java) //TODO fix busybox
|
||||
.setTimeout(10)
|
||||
)
|
||||
|
||||
if (prefs.getBoolean("crowdin_upload_screenshot", false))
|
||||
Crowdin.registerScreenShotContentObserver(this)
|
||||
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
Crowdin.onConfigurationChanged()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package com.vanced.manager.core
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
|
||||
/**
|
||||
* CombinedLiveData is a helper class to combine results from two LiveData sources.
|
||||
* @param combine Function reference that will be used to combine all LiveData data.
|
||||
* @param R The type of data returned after combining all LiveData data.
|
||||
* Usage:
|
||||
* CombinedLiveData(
|
||||
* getLiveData1(),
|
||||
* getLiveData2()
|
||||
* ) { data1, data2 ->
|
||||
* // Use datas[0], datas[1], ..., datas[N] to return a value
|
||||
* }
|
||||
*/
|
||||
class CombinedLiveData<R, A, B>(
|
||||
liveDataA: LiveData<A>,
|
||||
liveDataB: LiveData<B>,
|
||||
private val combine: (a: A?, b: B?) -> R
|
||||
) : MediatorLiveData<R>() {
|
||||
|
||||
private var a: A? = null
|
||||
private var b: B? = null
|
||||
|
||||
init {
|
||||
addSource(liveDataA) {
|
||||
a = it
|
||||
value = combine(a, b)
|
||||
}
|
||||
addSource(liveDataB) {
|
||||
b = it
|
||||
value = combine(a, b)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,11 +2,9 @@ package com.vanced.manager.core.downloader
|
|||
|
||||
import android.content.Context
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.*
|
||||
import com.vanced.manager.utils.DownloadHelper.download
|
||||
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||
import com.vanced.manager.utils.PackageHelper.install
|
||||
import com.vanced.manager.utils.baseInstallUrl
|
||||
import com.vanced.manager.utils.microg
|
||||
|
||||
object MicrogDownloader {
|
||||
|
||||
|
@ -18,14 +16,14 @@ object MicrogDownloader {
|
|||
download(url, "$baseInstallUrl/", folderName, fileName, context, onDownloadComplete = {
|
||||
startMicrogInstall(context)
|
||||
}, onError = {
|
||||
downloadProgress.value?.downloadingFile?.postValue(context.getString(R.string.error_downloading, fileName))
|
||||
downloadingFile.postValue(context.getString(R.string.error_downloading, fileName))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
fun startMicrogInstall(context: Context) {
|
||||
downloadProgress.value?.installing?.postValue(true)
|
||||
downloadProgress.value?.postReset()
|
||||
installing.postValue(true)
|
||||
postReset()
|
||||
install("${context.getExternalFilesDir(folderName)}/$fileName", context)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import com.vanced.manager.utils.*
|
|||
import com.vanced.manager.utils.AppUtils.musicRootPkg
|
||||
import com.vanced.manager.utils.AppUtils.validateTheme
|
||||
import com.vanced.manager.utils.DownloadHelper.download
|
||||
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||
import com.vanced.manager.utils.PackageHelper.downloadStockCheck
|
||||
import com.vanced.manager.utils.PackageHelper.install
|
||||
import com.vanced.manager.utils.PackageHelper.installMusicRoot
|
||||
|
@ -14,19 +13,21 @@ import com.vanced.manager.utils.PackageHelper.installMusicRoot
|
|||
object MusicDownloader {
|
||||
|
||||
private var variant: String? = null
|
||||
private var version: String? = null
|
||||
private var musicVersion: String? = null
|
||||
private var versionCode: Int? = null
|
||||
private var baseurl = ""
|
||||
private var folderName: String? = null
|
||||
private var downloadPath: String? = null
|
||||
private var hashUrl: String? = null
|
||||
|
||||
fun downloadMusic(context: Context) {
|
||||
fun downloadMusic(context: Context, version: String? = null) {
|
||||
val prefs = context.defPrefs
|
||||
version = prefs.musicVersion?.getLatestAppVersion(musicVersions.value?.value ?: listOf(""))
|
||||
musicVersion = version ?: prefs.musicVersion?.getLatestAppVersion(
|
||||
musicVersions.value?.value ?: listOf("")
|
||||
)
|
||||
versionCode = music.value?.int("versionCode")
|
||||
variant = prefs.managerVariant
|
||||
baseurl = "$baseInstallUrl/music/v$version"
|
||||
baseurl = "$baseInstallUrl/music/v$musicVersion"
|
||||
folderName = "music/$variant"
|
||||
downloadPath = context.getExternalFilesDir(folderName)?.path
|
||||
hashUrl = "$baseurl/hash.json"
|
||||
|
@ -36,36 +37,48 @@ object MusicDownloader {
|
|||
|
||||
private fun downloadApk(context: Context, apk: String = "music") {
|
||||
val url = if (apk == "stock") "$baseurl/stock/${getArch()}.apk" else "$baseurl/$variant.apk"
|
||||
download(url, "$baseurl/", folderName!!, getFileNameFromUrl(url), context, onDownloadComplete = {
|
||||
if (variant == "root" && apk != "stock") {
|
||||
downloadApk(context, "stock")
|
||||
return@download
|
||||
}
|
||||
|
||||
when (apk) {
|
||||
"music" -> {
|
||||
if (variant == "root") {
|
||||
if (validateTheme(downloadPath!!, "root", hashUrl!!, context)) {
|
||||
if (downloadStockCheck(musicRootPkg, versionCode!!, context))
|
||||
downloadApk(context, "stock")
|
||||
else
|
||||
startMusicInstall(context)
|
||||
} else {
|
||||
downloadApk(context, apk)
|
||||
}
|
||||
} else
|
||||
startMusicInstall(context)
|
||||
download(
|
||||
url,
|
||||
"$baseurl/",
|
||||
folderName!!,
|
||||
getFileNameFromUrl(url),
|
||||
context,
|
||||
onDownloadComplete = {
|
||||
if (variant == "root" && apk != "stock") {
|
||||
downloadApk(context, "stock")
|
||||
return@download
|
||||
}
|
||||
"stock" -> startMusicInstall(context)
|
||||
}
|
||||
}, onError = {
|
||||
downloadProgress.value?.downloadingFile?.postValue(context.getString(R.string.error_downloading, getFileNameFromUrl(url)))
|
||||
})
|
||||
|
||||
when (apk) {
|
||||
"music" -> {
|
||||
if (variant == "root") {
|
||||
if (validateTheme(downloadPath!!, "root", hashUrl!!, context)) {
|
||||
if (downloadStockCheck(musicRootPkg, versionCode!!, context))
|
||||
downloadApk(context, "stock")
|
||||
else
|
||||
startMusicInstall(context)
|
||||
} else {
|
||||
downloadApk(context, apk)
|
||||
}
|
||||
} else
|
||||
startMusicInstall(context)
|
||||
}
|
||||
"stock" -> startMusicInstall(context)
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
downloadingFile.postValue(
|
||||
context.getString(
|
||||
R.string.error_downloading,
|
||||
getFileNameFromUrl(url)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fun startMusicInstall(context: Context) {
|
||||
downloadProgress.value?.installing?.postValue(true)
|
||||
downloadProgress.value?.postReset()
|
||||
installing.postValue(true)
|
||||
postReset()
|
||||
if (variant == "root")
|
||||
installMusicRoot(context)
|
||||
else
|
||||
|
|
|
@ -2,22 +2,21 @@ package com.vanced.manager.core.downloader
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
import com.google.firebase.analytics.ktx.logEvent
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.*
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
import com.vanced.manager.utils.AppUtils.validateTheme
|
||||
import com.vanced.manager.utils.AppUtils.vancedRootPkg
|
||||
import com.vanced.manager.utils.DownloadHelper.download
|
||||
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||
import com.vanced.manager.utils.PackageHelper.downloadStockCheck
|
||||
import com.vanced.manager.utils.PackageHelper.installVanced
|
||||
import com.vanced.manager.utils.PackageHelper.installSplitApkFiles
|
||||
import com.vanced.manager.utils.PackageHelper.installVancedRoot
|
||||
import java.io.File
|
||||
|
||||
object VancedDownloader {
|
||||
|
||||
|
||||
private lateinit var prefs: SharedPreferences
|
||||
private lateinit var defPrefs: SharedPreferences
|
||||
private lateinit var arch: String
|
||||
|
@ -48,7 +47,9 @@ object VancedDownloader {
|
|||
lang = it.split(", ").toMutableList()
|
||||
}
|
||||
theme = prefs.theme
|
||||
vancedVersion = version ?: defPrefs.vancedVersion?.getLatestAppVersion(vancedVersions.value?.value ?: listOf(""))
|
||||
vancedVersion = version ?: defPrefs.vancedVersion?.getLatestAppVersion(
|
||||
vancedVersions.value?.value ?: listOf("")
|
||||
)
|
||||
themePath = "$baseInstallUrl/apks/v$vancedVersion/$variant/Theme"
|
||||
hashUrl = "apks/v$vancedVersion/$variant/Theme/hash.json"
|
||||
arch = getArch()
|
||||
|
@ -58,8 +59,8 @@ object VancedDownloader {
|
|||
try {
|
||||
downloadSplits(context)
|
||||
} catch (e: Exception) {
|
||||
Log.d("VMDownloader", e.stackTraceToString())
|
||||
downloadProgress.value?.downloadingFile?.postValue(context.getString(R.string.error_downloading, "Vanced"))
|
||||
log("VMDownloader", e.stackTraceToString())
|
||||
downloadingFile.postValue(context.getString(R.string.error_downloading, "Vanced"))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -69,58 +70,73 @@ object VancedDownloader {
|
|||
"theme" -> "$themePath/$theme.apk"
|
||||
"arch" -> "$baseInstallUrl/apks/v$vancedVersion/$variant/Arch/split_config.$arch.apk"
|
||||
"stock" -> "$themePath/stock.apk"
|
||||
"dpi" -> "$themePath/dpi.apk"
|
||||
"dpi" -> "$themePath/dpi.apk"
|
||||
"lang" -> "$baseInstallUrl/apks/v$vancedVersion/$variant/Language/split_config.${lang[count]}.apk"
|
||||
else -> throw NotImplementedError("This type of APK is NOT valid. What the hell did you even do?")
|
||||
}
|
||||
|
||||
download(url, "$baseInstallUrl/", folderName!!, getFileNameFromUrl(url), context, onDownloadComplete = {
|
||||
when (type) {
|
||||
"theme" ->
|
||||
if (variant == "root") {
|
||||
if (validateTheme(downloadPath!!, theme!!, hashUrl, context)) {
|
||||
if (downloadStockCheck(vancedRootPkg, vancedVersionCode, context))
|
||||
downloadSplits(context, "arch")
|
||||
else
|
||||
startVancedInstall(context)
|
||||
download(
|
||||
url,
|
||||
"$baseInstallUrl/",
|
||||
folderName!!,
|
||||
getFileNameFromUrl(url),
|
||||
context,
|
||||
onDownloadComplete = {
|
||||
when (type) {
|
||||
"theme" ->
|
||||
if (variant == "root") {
|
||||
if (validateTheme(downloadPath!!, theme!!, hashUrl, context)) {
|
||||
if (downloadStockCheck(vancedRootPkg, vancedVersionCode, context))
|
||||
downloadSplits(context, "arch")
|
||||
else
|
||||
startVancedInstall(context)
|
||||
} else
|
||||
downloadSplits(context, "theme")
|
||||
} else
|
||||
downloadSplits(context, "theme")
|
||||
} else
|
||||
downloadSplits(context, "arch")
|
||||
"arch" -> if (variant == "root") downloadSplits(context, "stock") else downloadSplits(context, "lang")
|
||||
"stock" -> downloadSplits(context, "dpi")
|
||||
"dpi" -> downloadSplits(context, "lang")
|
||||
"lang" -> {
|
||||
count++
|
||||
succesfulLangCount++
|
||||
if (count < lang.size)
|
||||
downloadSplits(context, "lang")
|
||||
else
|
||||
startVancedInstall(context)
|
||||
}
|
||||
|
||||
}
|
||||
}, onError = {
|
||||
if (type == "lang") {
|
||||
count++
|
||||
when {
|
||||
count < lang.size -> downloadSplits(context, "lang")
|
||||
succesfulLangCount == 0 -> {
|
||||
lang.add("en")
|
||||
downloadSplits(context, "lang")
|
||||
downloadSplits(context, "arch")
|
||||
"arch" -> if (variant == "root") downloadSplits(
|
||||
context,
|
||||
"stock"
|
||||
) else downloadSplits(context, "lang")
|
||||
"stock" -> downloadSplits(context, "dpi")
|
||||
"dpi" -> downloadSplits(context, "lang")
|
||||
"lang" -> {
|
||||
count++
|
||||
succesfulLangCount++
|
||||
if (count < lang.size)
|
||||
downloadSplits(context, "lang")
|
||||
else
|
||||
startVancedInstall(context)
|
||||
}
|
||||
else -> startVancedInstall(context)
|
||||
}
|
||||
|
||||
} else {
|
||||
downloadProgress.value?.downloadingFile?.postValue(context.getString(R.string.error_downloading, getFileNameFromUrl(url)))
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
if (type == "lang") {
|
||||
count++
|
||||
when {
|
||||
count < lang.size -> downloadSplits(context, "lang")
|
||||
succesfulLangCount == 0 -> {
|
||||
lang.add("en")
|
||||
downloadSplits(context, "lang")
|
||||
}
|
||||
else -> startVancedInstall(context)
|
||||
}
|
||||
|
||||
} else {
|
||||
downloadingFile.postValue(
|
||||
context.getString(
|
||||
R.string.error_downloading,
|
||||
getFileNameFromUrl(url)
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun startVancedInstall(context: Context, variant: String? = this.variant) {
|
||||
downloadProgress.value?.installing?.postValue(true)
|
||||
downloadProgress.value?.postReset()
|
||||
installing.postValue(true)
|
||||
postReset()
|
||||
FirebaseAnalytics.getInstance(context).logEvent(FirebaseAnalytics.Event.SELECT_ITEM) {
|
||||
variant?.let { param("vanced_variant", it) }
|
||||
theme?.let { param("vanced_theme", it) }
|
||||
|
@ -128,6 +144,6 @@ object VancedDownloader {
|
|||
if (variant == "root")
|
||||
installVancedRoot(context)
|
||||
else
|
||||
installVanced(context)
|
||||
installSplitApkFiles(context, "vanced")
|
||||
}
|
||||
}
|
|
@ -1,13 +1,12 @@
|
|||
package com.vanced.manager.core.firebase
|
||||
|
||||
import android.util.Log
|
||||
import com.google.firebase.messaging.FirebaseMessagingService
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
|
||||
class CloudMessaging : FirebaseMessagingService() {
|
||||
|
||||
override fun onNewToken(p0: String) {
|
||||
super.onNewToken(p0)
|
||||
Log.d("VMC", "Generated new token: $p0")
|
||||
log("VMC", "Generated new token: $p0")
|
||||
}
|
||||
|
||||
}
|
|
@ -4,33 +4,35 @@ import android.app.Service
|
|||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
import com.vanced.manager.utils.AppUtils.sendCloseDialog
|
||||
import com.vanced.manager.utils.AppUtils.sendFailure
|
||||
import com.vanced.manager.utils.AppUtils.sendRefresh
|
||||
|
||||
class AppInstallerService: Service() {
|
||||
class AppInstallerService : Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||
Log.d(TAG, "Requesting user confirmation for installation")
|
||||
log(TAG, "Requesting user confirmation for installation")
|
||||
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
||||
confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
startActivity(confirmationIntent)
|
||||
} catch (e: Exception) {
|
||||
Log.d("VMInstall", "Unable to start installation")
|
||||
log("VMInstall", "Unable to start installation")
|
||||
}
|
||||
}
|
||||
PackageInstaller.STATUS_SUCCESS -> {
|
||||
Log.d(TAG, "Installation succeed")
|
||||
log(TAG, "Installation succeed")
|
||||
sendCloseDialog(this)
|
||||
sendRefresh(this)
|
||||
}
|
||||
else -> {
|
||||
sendCloseDialog(this)
|
||||
sendFailure(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999), intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE),this)
|
||||
intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)?.let {
|
||||
sendFailure(it, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
stopSelf()
|
||||
|
|
|
@ -4,16 +4,16 @@ import android.app.Service
|
|||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
import com.vanced.manager.utils.AppUtils.sendRefresh
|
||||
|
||||
class AppUninstallerService: Service() {
|
||||
class AppUninstallerService : Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val pkgName = intent?.getStringExtra("pkg")
|
||||
when (intent?.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||
Log.d(AppInstallerService.TAG, "Requesting user confirmation for uninstallation")
|
||||
log(AppInstallerService.TAG, "Requesting user confirmation for uninstallation")
|
||||
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
||||
confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
|
@ -24,11 +24,11 @@ class AppUninstallerService: Service() {
|
|||
//Delay broadcast until activity (and fragment) show up on the screen
|
||||
PackageInstaller.STATUS_SUCCESS -> {
|
||||
sendRefresh(this)
|
||||
Log.d("VMpm", "Successfully uninstalled $pkgName")
|
||||
log("VMpm", "Successfully uninstalled $pkgName")
|
||||
}
|
||||
PackageInstaller.STATUS_FAILURE -> {
|
||||
sendRefresh(this)
|
||||
Log.d("VMpm", "Failed to uninstall $pkgName")
|
||||
log("VMpm", "Failed to uninstall $pkgName")
|
||||
}
|
||||
}
|
||||
stopSelf()
|
||||
|
|
10
app/src/main/java/com/vanced/manager/model/ButtonTag.kt
Normal file
10
app/src/main/java/com/vanced/manager/model/ButtonTag.kt
Normal file
|
@ -0,0 +1,10 @@
|
|||
package com.vanced.manager.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.vanced.manager.R
|
||||
|
||||
enum class ButtonTag(@DrawableRes val image: Int) {
|
||||
INSTALL(R.drawable.ic_app_download),
|
||||
UPDATE(R.drawable.ic_app_update),
|
||||
REINSTALL(R.drawable.ic_app_reinstall)
|
||||
}
|
|
@ -3,88 +3,79 @@ package com.vanced.manager.model
|
|||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.*
|
||||
import com.beust.klaxon.JsonObject
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.CombinedLiveData
|
||||
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
|
||||
|
||||
open class DataModel(
|
||||
private val jsonObject: LiveData<JsonObject?>,
|
||||
private val context: Context,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
jsonObject: LiveData<JsonObject?>,
|
||||
context: Context,
|
||||
val appPkg: String,
|
||||
val appName: String,
|
||||
val appIcon: Drawable?,
|
||||
val appDescription: String,
|
||||
@DrawableRes val appIcon: Int
|
||||
) {
|
||||
|
||||
private val versionCode = MutableLiveData<Int>()
|
||||
private val installedVersionCode = MutableLiveData<Int>()
|
||||
val isAppInstalled = Transformations.map(jsonObject) { isAppInstalled(appPkg) }
|
||||
|
||||
val isAppInstalled = MutableLiveData<Boolean>()
|
||||
val versionName = MutableLiveData<String>()
|
||||
val installedVersionName = MutableLiveData<String>()
|
||||
val buttonTxt = MutableLiveData<String>()
|
||||
val changelog = MutableLiveData<String>()
|
||||
private val versionCode = Transformations.map(jsonObject) { jobj ->
|
||||
jobj?.int("versionCode") ?: 0
|
||||
}
|
||||
private val installedVersionCode = Transformations.map(isAppInstalled) {
|
||||
getPkgVersionCode(appPkg, it)
|
||||
}
|
||||
private val unavailable = context.getString(R.string.unavailable)
|
||||
private val pm = context.packageManager
|
||||
|
||||
private fun fetch() {
|
||||
val jobj = jsonObject.value
|
||||
isAppInstalled.value = isAppInstalled(appPkg)
|
||||
versionCode.value = jobj?.int("versionCode") ?: 0
|
||||
versionName.value = jobj?.string("version")?.removeSuffix("-vanced") ?: context.getString(R.string.unavailable)
|
||||
changelog.value = jobj?.string("changelog") ?: context.getString(R.string.unavailable)
|
||||
val versionName = Transformations.map(jsonObject) { jobj ->
|
||||
jobj?.string("version") ?: unavailable
|
||||
}
|
||||
val changelog = Transformations.map(jsonObject) { jobj ->
|
||||
jobj?.string("changelog") ?: unavailable
|
||||
}
|
||||
val installedVersionName = Transformations.map(isAppInstalled) {
|
||||
getPkgVersionName(appPkg, it)
|
||||
}
|
||||
val buttonTag = CombinedLiveData(versionCode, installedVersionCode) { versionCode, installedVersionCode ->
|
||||
compareInt(installedVersionCode, versionCode)
|
||||
}
|
||||
|
||||
init {
|
||||
fetch()
|
||||
with(lifecycleOwner) {
|
||||
jsonObject.observe(this) {
|
||||
fetch()
|
||||
}
|
||||
isAppInstalled.observe(this) {
|
||||
installedVersionCode.value = getPkgVersionCode(appPkg)
|
||||
installedVersionName.value = getPkgVersionName(appPkg)
|
||||
}
|
||||
versionCode.observe(this) { versionCode ->
|
||||
installedVersionCode.observe(this) { installedVersionCode ->
|
||||
buttonTxt.value = compareInt(installedVersionCode, versionCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open fun isAppInstalled(pkg: String): Boolean = isPackageInstalled(pkg, pm)
|
||||
|
||||
open fun isAppInstalled(pkg: String): Boolean = isPackageInstalled(pkg, context.packageManager)
|
||||
|
||||
private fun getPkgVersionName(pkg: String): String {
|
||||
val pm = context.packageManager
|
||||
return if (isAppInstalled.value == true) {
|
||||
pm?.getPackageInfo(pkg, 0)?.versionName?.removeSuffix("-vanced") ?: context.getString(R.string.unavailable)
|
||||
private fun getPkgVersionName(pkg: String, isAppInstalled: Boolean): String {
|
||||
return if (isAppInstalled) {
|
||||
pm?.getPackageInfo(pkg, 0)?.versionName?.removeSuffix("-vanced") ?: unavailable
|
||||
} else {
|
||||
context.getString(R.string.unavailable)
|
||||
unavailable
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun getPkgVersionCode(pkg: String): Int {
|
||||
val pm = context.packageManager
|
||||
return if (isAppInstalled.value == true) {
|
||||
private fun getPkgVersionCode(pkg: String, isAppInstalled: Boolean): Int {
|
||||
return if (isAppInstalled) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
pm?.getPackageInfo(pkg, 0)?.longVersionCode?.and(0xFFFFFFFF)?.toInt() ?: 0
|
||||
else
|
||||
pm?.getPackageInfo(pkg, 0)?.versionCode ?: 0
|
||||
} else 0
|
||||
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
private fun compareInt(int1: Int?, int2: Int?): String {
|
||||
private fun compareInt(int1: Int?, int2: Int?): ButtonTag {
|
||||
if (int2 != null && int1 != null) {
|
||||
return when {
|
||||
int1 == 0 -> context.getString(R.string.install)
|
||||
int2 > int1 -> context.getString(R.string.update)
|
||||
int2 == int1 || int1 > int2 -> context.getString(R.string.button_reinstall)
|
||||
else -> context.getString(R.string.install)
|
||||
int1 == 0 -> ButtonTag.INSTALL
|
||||
int2 > int1 -> ButtonTag.UPDATE
|
||||
int1 >= int2 -> ButtonTag.REINSTALL
|
||||
else -> ButtonTag.INSTALL
|
||||
}
|
||||
}
|
||||
return context.getString(R.string.install)
|
||||
return ButtonTag.INSTALL
|
||||
}
|
||||
}
|
|
@ -4,5 +4,5 @@ import android.graphics.drawable.Drawable
|
|||
|
||||
data class LinkModel(
|
||||
val linkIcon: Drawable?,
|
||||
val linkUrl: String,
|
||||
val linkUrl: String
|
||||
)
|
|
@ -1,8 +1,8 @@
|
|||
package com.vanced.manager.model
|
||||
|
||||
data class NotifModel(
|
||||
val topic: String,
|
||||
val switchTitle: String,
|
||||
val switchSummary: String,
|
||||
val key: String
|
||||
val topic: String,
|
||||
val switchTitle: String,
|
||||
val switchSummary: String,
|
||||
val key: String
|
||||
)
|
|
@ -1,30 +0,0 @@
|
|||
package com.vanced.manager.model
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Call
|
||||
|
||||
open class ProgressModel {
|
||||
|
||||
val downloadProgress = MutableLiveData<Int>()
|
||||
val downloadingFile = MutableLiveData<String>()
|
||||
val installing = MutableLiveData<Boolean>()
|
||||
|
||||
var currentDownload: Call<ResponseBody>? = null
|
||||
|
||||
fun reset() {
|
||||
downloadProgress.value = 0
|
||||
downloadingFile.value = ""
|
||||
}
|
||||
|
||||
fun postReset() {
|
||||
downloadProgress.postValue(0)
|
||||
downloadingFile.postValue("")
|
||||
}
|
||||
|
||||
init {
|
||||
installing.postValue(false)
|
||||
reset()
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package com.vanced.manager.model
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.beust.klaxon.JsonObject
|
||||
|
@ -10,18 +10,18 @@ import com.vanced.manager.utils.PackageHelper
|
|||
class RootDataModel(
|
||||
jsonObject: LiveData<JsonObject?>,
|
||||
context: Context,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
appPkg: String,
|
||||
appName: String,
|
||||
appIcon: Drawable?,
|
||||
appDescription: String,
|
||||
@DrawableRes appIcon: Int,
|
||||
//BUG THIS!
|
||||
//kotlin thinks that this value is null if we use
|
||||
//private val scriptName: String
|
||||
//Although it's impossible for it to be null.
|
||||
//Ironic, isn't it?
|
||||
private val scriptName: String?
|
||||
): DataModel(
|
||||
jsonObject, context, lifecycleOwner, appPkg, appName, appIcon
|
||||
) : DataModel(
|
||||
jsonObject, context, appPkg, appName, appDescription, appIcon
|
||||
) {
|
||||
|
||||
override fun isAppInstalled(pkg: String): Boolean {
|
||||
|
|
|
@ -3,7 +3,7 @@ package com.vanced.manager.model
|
|||
import android.graphics.drawable.Drawable
|
||||
|
||||
data class SponsorModel(
|
||||
val logo: Drawable?,
|
||||
val name: String,
|
||||
val url: String
|
||||
val logo: Drawable?,
|
||||
val name: String,
|
||||
val url: String
|
||||
)
|
|
@ -1,10 +1,12 @@
|
|||
package com.vanced.manager.ui
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
|
@ -13,10 +15,7 @@ import androidx.navigation.findNavController
|
|||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.crowdin.platform.LoadingStateListener
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import com.vanced.manager.BuildConfig.ENABLE_CROWDIN_AUTH
|
||||
import com.vanced.manager.BuildConfig.VERSION_CODE
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.databinding.ActivityMainBinding
|
||||
|
@ -26,30 +25,21 @@ import com.vanced.manager.ui.dialogs.URLChangeDialog
|
|||
import com.vanced.manager.ui.fragments.HomeFragmentDirections
|
||||
import com.vanced.manager.ui.fragments.SettingsFragmentDirections
|
||||
import com.vanced.manager.utils.*
|
||||
|
||||
import com.vanced.manager.utils.AppUtils.currentLocale
|
||||
import com.vanced.manager.utils.AppUtils.faqpkg
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
import com.vanced.manager.utils.AppUtils.playStorePkg
|
||||
import com.vanced.manager.utils.AppUtils.vancedRootPkg
|
||||
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
lateinit var binding: ActivityMainBinding
|
||||
private val navHost by lazy { findNavController(R.id.nav_host) }
|
||||
|
||||
private val loadingObserver = object : LoadingStateListener {
|
||||
val tag = "VMLocalisation"
|
||||
override fun onDataChanged() {
|
||||
Log.d(tag, "Loaded data")
|
||||
}
|
||||
|
||||
override fun onFailure(throwable: Throwable) {
|
||||
Log.d(tag, "Failed to load data: $throwable")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setFinalTheme()
|
||||
super.onCreate(savedInstanceState)
|
||||
if (ENABLE_CROWDIN_AUTH)
|
||||
authCrowdin()
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
@ -68,7 +58,7 @@ class MainActivity : AppCompatActivity() {
|
|||
initDialogs(intent.getBooleanExtra("firstLaunch", false))
|
||||
manager.observe(this) {
|
||||
if (manager.value?.int("versionCode") ?: 0 > VERSION_CODE) {
|
||||
ManagerUpdateDialog.newInstance(false).show(this)
|
||||
ManagerUpdateDialog.newInstance(true).show(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,36 +69,35 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun setDisplayHomeAsUpEnabled(isNeeded: Boolean) {
|
||||
binding.toolbar.navigationIcon = if (isNeeded) ContextCompat.getDrawable(this, R.drawable.ic_keyboard_backspace_black_24dp) else null
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
Crowdin.unregisterDataLoadingObserver(loadingObserver)
|
||||
binding.toolbar.navigationIcon = if (isNeeded) ContextCompat.getDrawable(
|
||||
this,
|
||||
R.drawable.ic_keyboard_backspace_black_24dp
|
||||
) else null
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
setFinalTheme()
|
||||
super.onResume()
|
||||
Crowdin.registerDataLoadingObserver(loadingObserver)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
return true
|
||||
}
|
||||
return when (item.itemId) {
|
||||
R.id.toolbar_about -> {
|
||||
navHost.navigate(HomeFragmentDirections.toAboutFragment())
|
||||
return true
|
||||
true
|
||||
}
|
||||
R.id.toolbar_settings -> {
|
||||
navHost.navigate(HomeFragmentDirections.toSettingsFragment())
|
||||
return true
|
||||
true
|
||||
}
|
||||
R.id.toolbar_log -> {
|
||||
navHost.navigate(HomeFragmentDirections.toLogFragment())
|
||||
true
|
||||
}
|
||||
R.id.toolbar_update_manager -> {
|
||||
ManagerUpdateDialog.newInstance(false).show(supportFragmentManager, "manager_update")
|
||||
ManagerUpdateDialog.newInstance(false)
|
||||
.show(supportFragmentManager, "manager_update")
|
||||
true
|
||||
}
|
||||
R.id.dev_settings -> {
|
||||
navHost.navigate(SettingsFragmentDirections.toDevSettingsFragment())
|
||||
|
@ -116,22 +105,34 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun attachBaseContext(newBase: Context) {
|
||||
super.attachBaseContext(Crowdin.wrapContext(LanguageContextWrapper.wrap(newBase)))
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
onActivityResult(requestCode)
|
||||
super.attachBaseContext(LanguageContextWrapper.wrap(newBase))
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
recreate() //restarting activity to recreate viewmodels, otherwise some text won't update
|
||||
|
||||
//update manager language when system language is changed
|
||||
@Suppress("DEPRECATION")
|
||||
if (newConfig.locale != currentLocale) {
|
||||
recreate() //restarting activity in order to recreate viewmodels, otherwise some text won't update
|
||||
return
|
||||
}
|
||||
|
||||
when (newConfig.orientation) {
|
||||
Configuration.ORIENTATION_PORTRAIT -> log(
|
||||
"VMUI",
|
||||
"screen orientation changed to portrait"
|
||||
)
|
||||
Configuration.ORIENTATION_LANDSCAPE -> log(
|
||||
"VMUI",
|
||||
"screen orientation changed to landscape"
|
||||
)
|
||||
else -> log("VMUI", "screen orientation changed to [REDACTED]")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun recreate() {
|
||||
|
@ -153,26 +154,30 @@ class MainActivity : AppCompatActivity() {
|
|||
urldialog.show(this)
|
||||
}
|
||||
|
||||
when {
|
||||
firstLaunch -> {
|
||||
DialogContainer.showSecurityDialog(this)
|
||||
with(FirebaseMessaging.getInstance()) {
|
||||
subscribeToTopic("Vanced-Update")
|
||||
subscribeToTopic("Music-Update")
|
||||
subscribeToTopic("MicroG-Update")
|
||||
}
|
||||
if (firstLaunch) {
|
||||
DialogContainer.showSecurityDialog(this)
|
||||
with(FirebaseMessaging.getInstance()) {
|
||||
subscribeToTopic("Vanced-Update")
|
||||
subscribeToTopic("Music-Update")
|
||||
subscribeToTopic("MicroG-Update")
|
||||
}
|
||||
!prefs.getBoolean("statement", true) -> DialogContainer.statementFalse(this)
|
||||
variant == "root" -> {
|
||||
if (PackageHelper.getPackageVersionName(
|
||||
"com.google.android.youtube",
|
||||
packageManager
|
||||
) == "14.21.54")
|
||||
DialogContainer.basicDialog(
|
||||
getString(R.string.hold_on),
|
||||
getString(R.string.magisk_vanced),
|
||||
this
|
||||
)
|
||||
} else {
|
||||
if (isMiuiOptimizationsEnabled) {
|
||||
DialogContainer.miuiDialog(this)
|
||||
}
|
||||
}
|
||||
|
||||
if (!prefs.getBoolean("statement", true)) {
|
||||
DialogContainer.statementFalse(this)
|
||||
}
|
||||
|
||||
if (variant == "root") {
|
||||
if (PackageHelper.getPackageVersionName(vancedRootPkg, packageManager) == "14.21.54") {
|
||||
DialogContainer.basicDialog(
|
||||
getString(R.string.hold_on),
|
||||
getString(R.string.magisk_vanced),
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package com.vanced.manager.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.topjohnwu.superuser.Shell
|
||||
|
||||
class SplashScreenActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
//Preheat the shell
|
||||
Shell.getShell {
|
||||
if (getDefaultSharedPreferences(this).getBoolean("firstLaunch", true)) {
|
||||
startActivity(Intent(this, WelcomeActivity::class.java))
|
||||
finish()
|
||||
} else {
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,21 +1,86 @@
|
|||
package com.vanced.manager.ui
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.os.Bundle
|
||||
import android.util.LayoutDirection
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.navigation.findNavController
|
||||
import com.vanced.manager.R
|
||||
import androidx.core.animation.addListener
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.vanced.manager.adapter.WelcomePageAdapter
|
||||
import com.vanced.manager.databinding.ActivityWelcomeBinding
|
||||
import kotlin.math.abs
|
||||
|
||||
class WelcomeActivity : AppCompatActivity() {
|
||||
|
||||
private val navHost by lazy { findNavController(R.id.welcome_navhost) }
|
||||
private lateinit var binding: ActivityWelcomeBinding
|
||||
private var isRtl = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_welcome)
|
||||
binding = ActivityWelcomeBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
isRtl = resources.configuration.layoutDirection == LayoutDirection.RTL
|
||||
|
||||
binding.welcomeViewpager.apply {
|
||||
adapter = WelcomePageAdapter(this@WelcomeActivity)
|
||||
isUserInputEnabled = false
|
||||
setPageTransformer { page, position ->
|
||||
page.apply {
|
||||
val pageWidth = width.toFloat()
|
||||
//Thank you, fragula dev!
|
||||
when {
|
||||
position > 0 && position < 1 -> {
|
||||
alpha = 1f
|
||||
translationX = 0f
|
||||
}
|
||||
position > -1 && position <= 0 -> {
|
||||
alpha = 1.0f - abs(position * 0.7f)
|
||||
translationX = pageWidth.rtlCompat * position / 1.3F
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (!navHost.popBackStack())
|
||||
finish()
|
||||
with(binding) {
|
||||
if (welcomeViewpager.currentItem == 0) {
|
||||
super.onBackPressed()
|
||||
} else {
|
||||
navigateTo(welcomeViewpager.currentItem - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun navigateTo(position: Int) {
|
||||
binding.welcomeViewpager.currentPosition = position
|
||||
}
|
||||
|
||||
private val Float.rtlCompat get() = if (isRtl) this else -this
|
||||
|
||||
//Shit way to implement animation duration, but at least it works
|
||||
private var ViewPager2.currentPosition: Int
|
||||
get() = currentItem
|
||||
set(value) {
|
||||
val pixelsToDrag: Int = width * (value - currentItem)
|
||||
val animator = ValueAnimator.ofInt(0, pixelsToDrag)
|
||||
var previousValue = 0
|
||||
animator.apply {
|
||||
addUpdateListener { valueAnimator ->
|
||||
val currentValue = valueAnimator.animatedValue as Int
|
||||
val currentPxToDrag = (currentValue - previousValue).toFloat()
|
||||
fakeDragBy(currentPxToDrag.rtlCompat)
|
||||
previousValue = currentValue
|
||||
}
|
||||
addListener(
|
||||
onStart = { beginFakeDrag() },
|
||||
onEnd = { endFakeDrag() }
|
||||
)
|
||||
duration = 500
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
package com.vanced.manager.ui.compose
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package com.vanced.manager.ui.compose
|
||||
|
||||
//import androidx.compose.foundation.clickable
|
||||
//import androidx.compose.foundation.layout.Column
|
||||
//import androidx.compose.foundation.layout.ColumnScope
|
||||
//import androidx.compose.foundation.layout.padding
|
||||
//import androidx.compose.material.Switch
|
||||
//import androidx.compose.material.Text
|
||||
//import androidx.compose.runtime.*
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.platform.LocalContext
|
||||
//import androidx.compose.ui.tooling.preview.Preview
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.em
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import androidx.constraintlayout.compose.ConstraintLayout
|
||||
//import androidx.core.content.edit
|
||||
//import androidx.preference.PreferenceManager
|
||||
//
|
||||
//@Composable
|
||||
//@Preview
|
||||
//inline fun PreferenceCategory(
|
||||
// categoryTitle: String,
|
||||
// content: @Composable ColumnScope.() -> Unit
|
||||
//) {
|
||||
// Column {
|
||||
// Text(
|
||||
// categoryTitle,
|
||||
// letterSpacing = 0.15.em,
|
||||
// color = Color(LocalContext.current.accentColor)
|
||||
// )
|
||||
// content()
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//
|
||||
//@Composable
|
||||
//@Preview
|
||||
//inline fun SwitchPreference(
|
||||
// preferenceTitle: String,
|
||||
// preferenceDescription: String? = null,
|
||||
// preferenceKey: String,
|
||||
// defValue: Boolean = true,
|
||||
// crossinline onCheckedChange: (Boolean) -> Unit = {}
|
||||
//) {
|
||||
// val prefs = PreferenceManager.getDefaultSharedPreferences(LocalContext.current)
|
||||
// val isChecked = remember { mutableStateOf(prefs.getBoolean(preferenceKey, defValue)) }
|
||||
// ConstraintLayout(modifier = Modifier.padding(12.dp, 4.dp).clickable {
|
||||
// isChecked.value = !isChecked.value
|
||||
// }) {
|
||||
// val (title, description, switch) = createRefs()
|
||||
// Text(preferenceTitle, fontSize = 16.sp, modifier = Modifier.constrainAs(title) {
|
||||
// top.linkTo(parent.top)
|
||||
// start.linkTo(parent.start)
|
||||
// end.linkTo(switch.start, 4.dp)
|
||||
// if (preferenceDescription != null) {
|
||||
// bottom.linkTo(description.top)
|
||||
// } else {
|
||||
// bottom.linkTo(parent.bottom)
|
||||
// }
|
||||
// })
|
||||
// if (preferenceDescription != null) {
|
||||
// Text(preferenceDescription, fontSize = 13.sp, modifier = Modifier.constrainAs(description) {
|
||||
// top.linkTo(title.bottom)
|
||||
// start.linkTo(parent.start)
|
||||
// end.linkTo(switch.start, 8.dp)
|
||||
// })
|
||||
// }
|
||||
// Switch(
|
||||
// isChecked.value,
|
||||
// onCheckedChange = {
|
||||
// prefs.edit { putBoolean(preferenceKey, it) }
|
||||
// onCheckedChange(it)
|
||||
// },
|
||||
// modifier = Modifier.clickable(false) {}
|
||||
// )
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//@Composable
|
||||
//@Preview
|
||||
//fun Preference(
|
||||
// preferenceTitle: String,
|
||||
// preferenceDescription: String? = null,
|
||||
// onClick: () -> Unit
|
||||
//) {
|
||||
// ConstraintLayout(modifier = Modifier.padding(12.dp, 4.dp).clickable(onClick = onClick)) {
|
||||
// val (title, description, switch) = createRefs()
|
||||
// Text(preferenceTitle, fontSize = 16.sp, modifier = Modifier.constrainAs(title) {
|
||||
// top.linkTo(parent.top)
|
||||
// start.linkTo(parent.start)
|
||||
// end.linkTo(switch.start, 4.dp)
|
||||
// if (preferenceDescription != null) {
|
||||
// bottom.linkTo(description.top)
|
||||
// } else {
|
||||
// bottom.linkTo(parent.bottom)
|
||||
// }
|
||||
// })
|
||||
// if (preferenceDescription != null) {
|
||||
// Text(preferenceDescription, fontSize = 13.sp, modifier = Modifier.constrainAs(description) {
|
||||
// top.linkTo(title.bottom)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
52
app/src/main/java/com/vanced/manager/ui/compose/Theme.kt
Normal file
52
app/src/main/java/com/vanced/manager/ui/compose/Theme.kt
Normal file
|
@ -0,0 +1,52 @@
|
|||
package com.vanced.manager.ui.compose
|
||||
|
||||
//import android.content.Context
|
||||
//import androidx.compose.foundation.isSystemInDarkTheme
|
||||
//import androidx.compose.material.MaterialTheme
|
||||
//import androidx.compose.material.darkColors
|
||||
//import androidx.compose.material.lightColors
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.runtime.MutableState
|
||||
//import androidx.compose.runtime.mutableStateOf
|
||||
//import androidx.compose.runtime.remember
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.platform.LocalContext
|
||||
//import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
//import com.vanced.manager.utils.mutableAccentColor
|
||||
//
|
||||
//const val Dark = "Dark"
|
||||
//const val SystemDefault = "System Default"
|
||||
//
|
||||
//const val defAccentColor: Int = -13732865
|
||||
//
|
||||
//val Context.accentColor get() = mutableAccentColor.value ?: getDefaultSharedPreferences(this).getInt("manager_accent_color", defAccentColor)
|
||||
//
|
||||
//enum class Theme {
|
||||
// DARK, LIGHT
|
||||
//}
|
||||
//
|
||||
//val lightColors = lightColors(
|
||||
// primary = Color(defAccentColor)
|
||||
//)
|
||||
//
|
||||
//val darkColors = darkColors(
|
||||
// primary = Color(defAccentColor)
|
||||
//)
|
||||
//
|
||||
//fun Context.retrieveTheme(): Theme = when (getDefaultSharedPreferences(this).getString("manager_theme", SystemDefault)) {
|
||||
// SystemDefault -> if (isSystemInDarkTheme()) Theme.DARK else Theme.LIGHT
|
||||
// Dark -> Theme.DARK
|
||||
// else -> Theme.LIGHT
|
||||
//}
|
||||
//
|
||||
//val Context.isDarkTheme: Boolean
|
||||
// get() = retrieveTheme() == Theme.DARK
|
||||
//
|
||||
//fun Context.ManagerTheme(
|
||||
// content: @Composable () -> Unit
|
||||
//) {
|
||||
// MaterialTheme(
|
||||
// colors = if (isDarkTheme) darkColors else lightColors,
|
||||
// content = content
|
||||
// )
|
||||
//}
|
|
@ -31,7 +31,7 @@ class EmptyPreference @JvmOverloads constructor(
|
|||
}
|
||||
|
||||
fun setSummary(newSummary: String) {
|
||||
with (binding) {
|
||||
with(binding) {
|
||||
preferenceSummary.text = newSummary
|
||||
preferenceSummary.isVisible = true
|
||||
preferenceTitle.setPadding(0, 0, 0, 0)
|
||||
|
@ -40,10 +40,11 @@ class EmptyPreference @JvmOverloads constructor(
|
|||
|
||||
private fun initAttrs(context: Context, attrs: AttributeSet?) {
|
||||
attrs?.let { mAttrs ->
|
||||
val typedArray = context.obtainStyledAttributes(mAttrs, R.styleable.EmptyPreference, 0, 0)
|
||||
val typedArray =
|
||||
context.obtainStyledAttributes(mAttrs, R.styleable.EmptyPreference, 0, 0)
|
||||
val title = typedArray.getText(R.styleable.EmptyPreference_preference_title)
|
||||
val summary = typedArray.getText(R.styleable.EmptyPreference_preference_summary)
|
||||
with (binding) {
|
||||
with(binding) {
|
||||
if (summary != null) {
|
||||
preferenceSummary.text = summary
|
||||
} else {
|
||||
|
|
|
@ -10,7 +10,7 @@ import com.vanced.manager.databinding.ViewPreferenceCategoryBinding
|
|||
class PreferenceCategory @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0,
|
||||
defStyle: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyle) {
|
||||
|
||||
private var _binding: ViewPreferenceCategoryBinding? = null
|
||||
|
@ -27,7 +27,8 @@ class PreferenceCategory @JvmOverloads constructor(
|
|||
|
||||
private fun initAttrs(context: Context, attrs: AttributeSet?) {
|
||||
attrs.let { mAttrs ->
|
||||
val typedArray = context.obtainStyledAttributes(mAttrs, R.styleable.PreferenceCategory, 0, 0)
|
||||
val typedArray =
|
||||
context.obtainStyledAttributes(mAttrs, R.styleable.PreferenceCategory, 0, 0)
|
||||
val title = typedArray.getText(R.styleable.PreferenceCategory_category_title)
|
||||
|
||||
binding.categoryTitle.text = title
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.CompoundButton
|
||||
|
@ -31,6 +32,13 @@ class PreferenceSwitch @JvmOverloads constructor(
|
|||
|
||||
private var mListener: OnCheckedListener? = null
|
||||
|
||||
private val prefListener =
|
||||
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||
if (key == prefKey) {
|
||||
binding.preferenceSwitch.isChecked = sharedPreferences.getBoolean(key, defValue)
|
||||
}
|
||||
}
|
||||
|
||||
private var _binding: ViewPreferenceSwitchBinding? = null
|
||||
|
||||
val binding: ViewPreferenceSwitchBinding
|
||||
|
@ -38,6 +46,7 @@ class PreferenceSwitch @JvmOverloads constructor(
|
|||
|
||||
init {
|
||||
_binding = ViewPreferenceSwitchBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
prefs.registerOnSharedPreferenceChangeListener(prefListener)
|
||||
attrs?.let { mAttrs ->
|
||||
with(context.obtainStyledAttributes(mAttrs, R.styleable.PreferenceSwitch, 0, 0)) {
|
||||
val title = getText(R.styleable.PreferenceSwitch_switch_title)
|
||||
|
@ -85,4 +94,8 @@ class PreferenceSwitch @JvmOverloads constructor(
|
|||
defValue = newVal
|
||||
binding.preferenceSwitch.isChecked = prefs.getBoolean(prefKey, newVal)
|
||||
}
|
||||
|
||||
fun setChecked(checked: Boolean) {
|
||||
binding.preferenceSwitch.isChecked = checked
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
open class SlidingConstraintLayout : ConstraintLayout {
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(
|
||||
context,
|
||||
attrs
|
||||
)
|
||||
|
||||
var xFraction: Float
|
||||
get() {
|
||||
val width = width
|
||||
return if (width != 0)
|
||||
x / getWidth()
|
||||
else
|
||||
x
|
||||
}
|
||||
set(xFraction) {
|
||||
val width = width
|
||||
val newWidth =
|
||||
if (width > 0)
|
||||
xFraction * width
|
||||
else
|
||||
(1).toFloat()
|
||||
x = newWidth
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.LinearLayout
|
||||
|
||||
open class SlidingLinearLayout: LinearLayout {
|
||||
|
||||
constructor(context: Context?) : super(context)
|
||||
constructor(context: Context?, attrs: AttributeSet?) : super(
|
||||
context,
|
||||
attrs
|
||||
)
|
||||
|
||||
var yFraction: Float
|
||||
get() {
|
||||
val height = height
|
||||
return if (height != 0)
|
||||
y / height
|
||||
else
|
||||
y
|
||||
}
|
||||
set(yFraction) {
|
||||
val height = height
|
||||
val newHeight =
|
||||
if (height > 0)
|
||||
yFraction * height
|
||||
else
|
||||
(1).toFloat()
|
||||
y = newHeight
|
||||
}
|
||||
|
||||
var xFraction: Float
|
||||
get() {
|
||||
val width = width
|
||||
return if (width != 0)
|
||||
x / getWidth()
|
||||
else
|
||||
x
|
||||
}
|
||||
set(xFraction) {
|
||||
val width = width
|
||||
val newWidth =
|
||||
if (width > 0)
|
||||
xFraction * width
|
||||
else
|
||||
(1).toFloat()
|
||||
x = newWidth
|
||||
}
|
||||
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
|
||||
open class SlidingSwipeRefreshLayout : SwipeRefreshLayout {
|
||||
|
||||
constructor(context: Context?) : super(context!!)
|
||||
constructor(context: Context?, attrs: AttributeSet?) : super(
|
||||
context!!,
|
||||
attrs
|
||||
)
|
||||
|
||||
var xFraction: Float
|
||||
get() {
|
||||
val width = width
|
||||
return if (width != 0)
|
||||
x / getWidth()
|
||||
else
|
||||
x
|
||||
}
|
||||
set(xFraction) {
|
||||
val width = width
|
||||
val newWidth =
|
||||
if (width > 0)
|
||||
xFraction * width
|
||||
else
|
||||
(1).toFloat()
|
||||
x = newWidth
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
import com.vanced.manager.ui.WelcomeActivity
|
||||
|
||||
class SplashScreenActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (getDefaultSharedPreferences(this).getBoolean("firstLaunch", true)) {
|
||||
startActivity(Intent(this, WelcomeActivity::class.java))
|
||||
finish()
|
||||
} else {
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedAppCard @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : MaterialCardView(context, attributeSet, defStyleAttr) {
|
||||
|
||||
init {
|
||||
setCardBackgroundColor(ColorStateList.valueOf(accentColor.value!!).withAlpha(35))
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.widget.Toast
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedIconButton @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : MaterialButton(context, attributeSet, defStyleAttr) {
|
||||
|
||||
init {
|
||||
iconTint = ColorStateList.valueOf(accentColor.value!!)
|
||||
setOnLongClickListener {
|
||||
Toast.makeText(context, contentDescription, Toast.LENGTH_SHORT).show()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -7,9 +7,7 @@ import androidx.core.graphics.ColorUtils
|
|||
import com.google.android.material.button.MaterialButton
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.accentColor
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.lifecycleOwner
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
|
||||
class ThemedMaterialButton @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
@ -18,8 +16,7 @@ class ThemedMaterialButton @JvmOverloads constructor(
|
|||
) : MaterialButton(context, attributeSet, defStyleAttr) {
|
||||
|
||||
init {
|
||||
setBgColor(context.defPrefs.managerAccent)
|
||||
context.lifecycleOwner()?.let { owner ->
|
||||
context.lifecycleOwner?.let { owner ->
|
||||
accentColor.observe(owner) { color ->
|
||||
setBgColor(color.toInt())
|
||||
}
|
||||
|
|
|
@ -5,14 +5,13 @@ import android.content.res.ColorStateList
|
|||
import android.util.AttributeSet
|
||||
import com.google.android.material.checkbox.MaterialCheckBox
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedMaterialCheckbox @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null,
|
||||
) : MaterialCheckBox(context, attributeSet, R.attr.checkboxStyle) {
|
||||
init {
|
||||
buttonTintList = ColorStateList.valueOf(context.defPrefs.managerAccent)
|
||||
buttonTintList = ColorStateList.valueOf(accentColor.value!!)
|
||||
}
|
||||
}
|
|
@ -5,14 +5,13 @@ import android.content.res.ColorStateList
|
|||
import android.util.AttributeSet
|
||||
import com.google.android.material.radiobutton.MaterialRadioButton
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedMaterialRadioButton @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null,
|
||||
) : MaterialRadioButton(context, attributeSet, R.attr.radioButtonStyle) {
|
||||
init {
|
||||
buttonTintList = ColorStateList.valueOf(context.defPrefs.managerAccent)
|
||||
buttonTintList = ColorStateList.valueOf(accentColor.value!!)
|
||||
}
|
||||
}
|
|
@ -4,17 +4,20 @@ import android.content.Context
|
|||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import com.google.android.material.slider.Slider
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedMaterialSlider@JvmOverloads constructor(
|
||||
class ThemedMaterialSlider @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : Slider(context, attributeSet, defStyleAttr) {
|
||||
|
||||
init {
|
||||
thumbStrokeColor = ColorStateList.valueOf(context.defPrefs.managerAccent)
|
||||
val accentValue = ColorStateList.valueOf(accentColor.value!!)
|
||||
thumbTintList = accentValue
|
||||
trackActiveTintList = accentValue
|
||||
trackInactiveTintList = accentValue.withAlpha(70)
|
||||
haloTintList = accentValue.withAlpha(60)
|
||||
}
|
||||
|
||||
}
|
|
@ -6,10 +6,7 @@ import android.util.AttributeSet
|
|||
import androidx.core.graphics.ColorUtils
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.vanced.manager.utils.accentColor
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.lifecycleOwner
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
|
||||
|
||||
class ThemedOutlinedMaterialButton @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
@ -17,8 +14,7 @@ class ThemedOutlinedMaterialButton @JvmOverloads constructor(
|
|||
defStyleAttr: Int = 0
|
||||
) : MaterialButton(context, attributeSet, defStyleAttr) {
|
||||
init {
|
||||
applyAccent(context.defPrefs.managerAccent)
|
||||
context.lifecycleOwner()?.let { owner ->
|
||||
context.lifecycleOwner?.let { owner ->
|
||||
accentColor.observe(owner) { color ->
|
||||
applyAccent(color.toInt())
|
||||
}
|
||||
|
@ -27,6 +23,9 @@ class ThemedOutlinedMaterialButton @JvmOverloads constructor(
|
|||
|
||||
private fun applyAccent(color: Int) {
|
||||
setTextColor(color)
|
||||
rippleColor = ColorStateList(arrayOf(intArrayOf()), intArrayOf(ColorUtils.setAlphaComponent(color, 50)))
|
||||
rippleColor = ColorStateList(
|
||||
arrayOf(intArrayOf()),
|
||||
intArrayOf(ColorUtils.setAlphaComponent(color, 50))
|
||||
)
|
||||
}
|
||||
}
|
|
@ -4,21 +4,27 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedSwipeRefreshlayout @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null
|
||||
) : SwipeRefreshLayout(context, attributeSet) {
|
||||
init {
|
||||
setColorSchemeColors(context.defPrefs.managerAccent)
|
||||
setColorSchemeColors(accentColor.value!!)
|
||||
initAttrs(context, attributeSet)
|
||||
}
|
||||
|
||||
private fun initAttrs(context: Context, attributeSet: AttributeSet?) {
|
||||
attributeSet.let {
|
||||
val typedAttrs = context.obtainStyledAttributes(it, R.styleable.ThemedSwipeRefreshlayout, 0, 0)
|
||||
setProgressBackgroundColorSchemeColor(typedAttrs.getColor(R.styleable.ThemedSwipeRefreshlayout_progressBackgroundColor, 0))
|
||||
val typedAttrs =
|
||||
context.obtainStyledAttributes(it, R.styleable.ThemedSwipeRefreshlayout, 0, 0)
|
||||
setProgressBackgroundColorSchemeColor(
|
||||
typedAttrs.getColor(
|
||||
R.styleable.ThemedSwipeRefreshlayout_progressBackgroundColor,
|
||||
0
|
||||
)
|
||||
)
|
||||
typedAttrs.recycle()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,20 +9,18 @@ import androidx.core.graphics.ColorUtils
|
|||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.accentColor
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.lifecycleOwner
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
|
||||
class ThemedSwitchCompat @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null,
|
||||
) : SwitchCompat(context, attributeSet, R.attr.switchStyle) {
|
||||
|
||||
private val states = arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked))
|
||||
private val states =
|
||||
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked))
|
||||
|
||||
init {
|
||||
setSwitchColors(context.defPrefs.managerAccent)
|
||||
context.lifecycleOwner()?.let { owner ->
|
||||
context.lifecycleOwner?.let { owner ->
|
||||
accentColor.observe(owner) { color ->
|
||||
setSwitchColors(color.toInt())
|
||||
}
|
||||
|
@ -32,7 +30,13 @@ class ThemedSwitchCompat @JvmOverloads constructor(
|
|||
private fun setSwitchColors(color: Int) {
|
||||
val thumbColors = intArrayOf(Color.LTGRAY, color)
|
||||
val trackColors = intArrayOf(Color.GRAY, ColorUtils.setAlphaComponent(color, 70))
|
||||
DrawableCompat.setTintList(DrawableCompat.wrap(thumbDrawable), ColorStateList(states, thumbColors))
|
||||
DrawableCompat.setTintList(DrawableCompat.wrap(trackDrawable), ColorStateList(states, trackColors))
|
||||
DrawableCompat.setTintList(
|
||||
DrawableCompat.wrap(thumbDrawable),
|
||||
ColorStateList(states, thumbColors)
|
||||
)
|
||||
DrawableCompat.setTintList(
|
||||
DrawableCompat.wrap(trackDrawable),
|
||||
ColorStateList(states, trackColors)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -4,9 +4,7 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import com.vanced.manager.utils.accentColor
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.lifecycleOwner
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
|
||||
class ThemedTextView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
@ -14,8 +12,7 @@ class ThemedTextView @JvmOverloads constructor(
|
|||
defStyleAttr: Int = 0
|
||||
) : AppCompatTextView(context, attributeSet, defStyleAttr) {
|
||||
init {
|
||||
setTextColor(context.defPrefs.managerAccent)
|
||||
context.lifecycleOwner()?.let { owner ->
|
||||
context.lifecycleOwner?.let { owner ->
|
||||
accentColor.observe(owner) { color ->
|
||||
setTextColor(color.toInt())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.vanced.manager.ui.dialogs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
@ -17,8 +18,7 @@ import com.vanced.manager.core.downloader.MusicDownloader.downloadMusic
|
|||
import com.vanced.manager.core.downloader.VancedDownloader.downloadVanced
|
||||
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
||||
import com.vanced.manager.databinding.DialogAppDownloadBinding
|
||||
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||
import com.vanced.manager.utils.applyAccent
|
||||
import com.vanced.manager.utils.*
|
||||
|
||||
class AppDownloadDialog : BindingDialogFragment<DialogAppDownloadBinding>() {
|
||||
|
||||
|
@ -73,39 +73,43 @@ class AppDownloadDialog : BindingDialogFragment<DialogAppDownloadBinding>() {
|
|||
appDownloadHeader.text = app
|
||||
if (arguments?.getBoolean(TAG_INSTALLING) == false) {
|
||||
when (app) {
|
||||
getString(R.string.vanced) -> downloadVanced(requireContext(), arguments?.getString(TAG_VERSION))
|
||||
getString(R.string.music) -> downloadMusic(requireContext())
|
||||
getString(R.string.vanced) -> downloadVanced(
|
||||
requireContext(),
|
||||
arguments?.getString(TAG_VERSION)
|
||||
)
|
||||
getString(R.string.music) -> downloadMusic(
|
||||
requireContext(),
|
||||
arguments?.getString(TAG_VERSION)
|
||||
)
|
||||
getString(R.string.microg) -> downloadMicrog(requireContext())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun DialogAppDownloadBinding.bindDownloadProgress() {
|
||||
with(downloadProgress) {
|
||||
observe(viewLifecycleOwner) { progressModel ->
|
||||
progressModel.downloadProgress.observe(viewLifecycleOwner) {
|
||||
appDownloadProgressbar.progress = it
|
||||
}
|
||||
progressModel.installing.observe(viewLifecycleOwner) { installing ->
|
||||
appDownloadProgressbar.isVisible = !installing
|
||||
appInstallProgressbar.isVisible = installing
|
||||
appDownloadFile.isVisible = !installing
|
||||
appDownloadCancel.isEnabled = !installing
|
||||
appDownloadCancel.setOnClickListener {
|
||||
if (installing) {
|
||||
return@setOnClickListener
|
||||
}
|
||||
progressModel.currentDownload?.cancel()
|
||||
progressModel.downloadProgress.value = 0
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
progressModel.downloadingFile.observe(viewLifecycleOwner) {
|
||||
appDownloadFile.text = it
|
||||
downloadProgress.observe(viewLifecycleOwner) {
|
||||
appDownloadProgressbar.progress = it
|
||||
appDownloadProgress.text = "$it%"
|
||||
}
|
||||
installing.observe(viewLifecycleOwner) { installing ->
|
||||
appDownloadProgressbarContainer.isVisible = !installing
|
||||
appInstallProgressbar.isVisible = installing
|
||||
appDownloadFile.isVisible = !installing
|
||||
appDownloadCancel.isEnabled = !installing
|
||||
appDownloadCancel.setOnClickListener {
|
||||
if (installing) {
|
||||
return@setOnClickListener
|
||||
}
|
||||
currentDownload?.cancel()
|
||||
downloadProgress.value = 0
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
downloadingFile.observe(viewLifecycleOwner) {
|
||||
appDownloadFile.text = it
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
@ -2,11 +2,10 @@ package com.vanced.manager.ui.dialogs
|
|||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
||||
import com.vanced.manager.databinding.DialogAppInfoBinding
|
||||
|
@ -21,13 +20,15 @@ class AppInfoDialog : BindingDialogFragment<DialogAppInfoBinding>() {
|
|||
|
||||
fun newInstance(
|
||||
appName: String?,
|
||||
appIcon: Drawable?,
|
||||
@DrawableRes appIcon: Int?,
|
||||
changelog: String?
|
||||
): AppInfoDialog = AppInfoDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(TAG_APP_NAME, appName)
|
||||
putString(TAG_CHANGELOG, changelog)
|
||||
putParcelable(TAG_APP_ICON, appIcon?.toBitmap())
|
||||
if (appIcon != null) {
|
||||
putInt(TAG_APP_ICON, appIcon)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +48,7 @@ class AppInfoDialog : BindingDialogFragment<DialogAppInfoBinding>() {
|
|||
with(binding) {
|
||||
aboutAppName.text = getString(R.string.about_app, arguments?.getString(TAG_APP_NAME))
|
||||
aboutAppChangelog.text = arguments?.getString(TAG_CHANGELOG)
|
||||
aboutAppImage.setImageBitmap(arguments?.getParcelable(TAG_APP_ICON))
|
||||
arguments?.getInt(TAG_APP_ICON)?.let { aboutAppImage.setImageResource(it) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.vanced.manager.ui.dialogs
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
||||
import com.vanced.manager.databinding.DialogAppUninstallBinding
|
||||
import com.vanced.manager.utils.PackageHelper
|
||||
|
||||
class AppUninstallDialog : BindingDialogFragment<DialogAppUninstallBinding>() {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG_APP_NAME = "APP_NAME"
|
||||
private const val TAG_APP_PACKAGE = "APP_PACKAGE"
|
||||
|
||||
|
||||
fun newInstance(
|
||||
appName: String?,
|
||||
appPackage: String?,
|
||||
) = AppUninstallDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(TAG_APP_NAME, appName)
|
||||
putString(TAG_APP_PACKAGE, appPackage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun binding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogAppUninstallBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun otherSetups() {
|
||||
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
bindData()
|
||||
}
|
||||
|
||||
private fun bindData() {
|
||||
val appName = arguments?.getString(TAG_APP_NAME)
|
||||
val appPackage = arguments?.getString(TAG_APP_PACKAGE)
|
||||
with(binding) {
|
||||
appUninstallConfirm.setOnClickListener {
|
||||
if (appPackage != null) {
|
||||
PackageHelper.uninstallApk(
|
||||
pkg = appPackage,
|
||||
context = requireActivity()
|
||||
)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
appUninstallCancel.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
appUninstallMessage.text = getString(R.string.uninstall_app_text, appName)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,10 +11,12 @@ import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
|||
import com.vanced.manager.core.ui.ext.showDialog
|
||||
import com.vanced.manager.databinding.DialogBottomRadioButtonBinding
|
||||
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
||||
import com.vanced.manager.utils.checkedButtonTag
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.getCheckedButtonTag
|
||||
import com.vanced.manager.utils.formatVersion
|
||||
|
||||
class AppVersionSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
||||
class AppVersionSelectorDialog :
|
||||
BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
||||
|
||||
private val prefs by lazy { requireActivity().defPrefs }
|
||||
|
||||
|
@ -63,7 +65,7 @@ class AppVersionSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomRa
|
|||
}
|
||||
dialogTitle.text = getString(R.string.version)
|
||||
dialogSave.setOnClickListener {
|
||||
val checkedTag = dialogRadiogroup.getCheckedButtonTag()
|
||||
val checkedTag = dialogRadiogroup.checkedButtonTag
|
||||
if (checkedTag != null) {
|
||||
prefs.edit { putString("${arguments?.getString(TAG_APP)}_version", checkedTag) }
|
||||
}
|
||||
|
@ -75,7 +77,7 @@ class AppVersionSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomRa
|
|||
private fun loadBoxes() =
|
||||
arguments?.getStringArrayList(TAG_VERSIONS)?.map { version ->
|
||||
ThemedMaterialRadioButton(requireActivity()).apply {
|
||||
text = version
|
||||
text = version.formatVersion(requireActivity())
|
||||
tag = version
|
||||
textSize = 18f
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ import androidx.core.content.edit
|
|||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.applyAccent
|
||||
import com.vanced.manager.utils.isMiui
|
||||
import com.vanced.manager.utils.isMiuiOptimizationsEnabled
|
||||
import com.vanced.manager.utils.openUrl
|
||||
import com.vanced.manager.utils.showWithAccent
|
||||
|
||||
object DialogContainer {
|
||||
|
||||
|
@ -16,26 +16,21 @@ object DialogContainer {
|
|||
setTitle(context.resources.getString(R.string.welcome))
|
||||
setMessage(context.resources.getString(R.string.security_context))
|
||||
setPositiveButton(context.resources.getString(R.string.close)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
setOnDismissListener {
|
||||
if (isMiui()) {
|
||||
applyAccentMiuiDialog(context)
|
||||
}
|
||||
dialog.cancel()
|
||||
}
|
||||
setOnCancelListener {
|
||||
if (isMiui()) {
|
||||
applyAccentMiuiDialog(context)
|
||||
if (context.isMiuiOptimizationsEnabled) {
|
||||
miuiDialog(context)
|
||||
}
|
||||
}
|
||||
create()
|
||||
applyAccent()
|
||||
showWithAccent()
|
||||
}
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
prefs.edit { putBoolean("firstLaunch", false) }
|
||||
}
|
||||
|
||||
private fun applyAccentMiuiDialog(context: Context) {
|
||||
fun miuiDialog(context: Context) {
|
||||
MaterialAlertDialogBuilder(context).apply {
|
||||
setTitle(context.getString(R.string.miui_one_title))
|
||||
setMessage(context.getString(R.string.miui_one))
|
||||
|
@ -49,7 +44,7 @@ object DialogContainer {
|
|||
}
|
||||
setCancelable(false)
|
||||
create()
|
||||
applyAccent()
|
||||
showWithAccent()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +54,7 @@ object DialogContainer {
|
|||
setMessage("So this statement is false huh? I'll go with True!")
|
||||
setPositiveButton("wut?") { dialog, _ -> dialog.dismiss() }
|
||||
create()
|
||||
applyAccent()
|
||||
showWithAccent()
|
||||
}
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
@ -73,28 +68,54 @@ object DialogContainer {
|
|||
when (msg) {
|
||||
context.getString(R.string.installation_signature) -> {
|
||||
setPositiveButton(context.getString(R.string.guide)) { _, _ ->
|
||||
openUrl("https://lmgtfy.com/?q=andnixsh+apk+verification+disable", R.color.Twitter, context)
|
||||
openUrl(
|
||||
"https://lmgtfy.com/?q=andnixsh+apk+verification+disable",
|
||||
R.color.Twitter,
|
||||
context
|
||||
)
|
||||
}
|
||||
setNeutralButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
if (fullMsg != null)
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ -> basicDialog(context.getString(R.string.advanced), fullMsg, context) }
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ ->
|
||||
basicDialog(
|
||||
context.getString(R.string.advanced),
|
||||
fullMsg,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
context.getString(R.string.installation_miui) -> {
|
||||
setPositiveButton(context.getString(R.string.guide)) { _, _ ->
|
||||
openUrl("https://telegra.ph/How-to-install-v15-on-MIUI-02-11", R.color.Telegram, context)
|
||||
openUrl(
|
||||
"https://telegra.ph/How-to-install-v15-on-MIUI-02-11",
|
||||
R.color.Telegram,
|
||||
context
|
||||
)
|
||||
}
|
||||
setNeutralButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
if (fullMsg != null)
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ -> basicDialog(context.getString(R.string.advanced), fullMsg, context) }
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ ->
|
||||
basicDialog(
|
||||
context.getString(R.string.advanced),
|
||||
fullMsg,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
setPositiveButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
if (fullMsg != null)
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ -> basicDialog(context.getString(R.string.advanced), fullMsg, context) }
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ ->
|
||||
basicDialog(
|
||||
context.getString(R.string.advanced),
|
||||
fullMsg,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
create()
|
||||
applyAccent()
|
||||
showWithAccent()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +125,7 @@ object DialogContainer {
|
|||
setMessage(msg)
|
||||
setPositiveButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
create()
|
||||
applyAccent()
|
||||
showWithAccent()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ import com.vanced.manager.databinding.DialogInstallationFilesDetectedBinding
|
|||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.managerVariant
|
||||
|
||||
class InstallationFilesDetectedDialog : BindingBottomSheetDialogFragment<DialogInstallationFilesDetectedBinding>() {
|
||||
class InstallationFilesDetectedDialog :
|
||||
BindingBottomSheetDialogFragment<DialogInstallationFilesDetectedBinding>() {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -40,15 +41,17 @@ class InstallationFilesDetectedDialog : BindingBottomSheetDialogFragment<DialogI
|
|||
|
||||
private fun bindData() {
|
||||
with(binding) {
|
||||
val app = arguments?.getString(TAG_APP) ?: throw IllegalArgumentException("app name is null")
|
||||
val app =
|
||||
arguments?.getString(TAG_APP) ?: throw IllegalArgumentException("app name is null")
|
||||
installationDetectedTitle.text = getString(R.string.app_install_files_detected, app)
|
||||
installationDetectedSummary.text = getString(R.string.app_install_files_detected_summary, app)
|
||||
installationDetectedSummary.text =
|
||||
getString(R.string.app_install_files_detected_summary, app)
|
||||
installationDetectedRedownload.setOnClickListener {
|
||||
dismiss()
|
||||
if (app == getString(R.string.vanced))
|
||||
showDialog(VancedPreferencesDialog())
|
||||
else {
|
||||
showDialog(AppDownloadDialog.newInstance(app))
|
||||
when (app) {
|
||||
getString(R.string.vanced) -> showDialog(VancedPreferencesDialog())
|
||||
getString(R.string.music) -> showDialog(MusicPreferencesDialog())
|
||||
else -> showDialog(AppDownloadDialog.newInstance(app))
|
||||
}
|
||||
}
|
||||
installationDetectedInstall.setOnClickListener {
|
||||
|
|
|
@ -2,10 +2,10 @@ package com.vanced.manager.ui.dialogs
|
|||
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
|
@ -16,6 +16,7 @@ import com.vanced.manager.R
|
|||
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
||||
import com.vanced.manager.databinding.DialogManagerAccentColorBinding
|
||||
import com.vanced.manager.utils.*
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
|
||||
class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorBinding>() {
|
||||
|
||||
|
@ -34,6 +35,7 @@ class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorB
|
|||
) = DialogManagerAccentColorBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun otherSetups() {
|
||||
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
bindData()
|
||||
}
|
||||
|
||||
|
@ -48,9 +50,20 @@ class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorB
|
|||
hexEdittext.apply {
|
||||
setText(accent.toHex(), TextView.BufferType.EDITABLE)
|
||||
addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun beforeTextChanged(
|
||||
s: CharSequence?,
|
||||
start: Int,
|
||||
count: Int,
|
||||
after: Int
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
override fun onTextChanged(
|
||||
s: CharSequence?,
|
||||
start: Int,
|
||||
before: Int,
|
||||
count: Int
|
||||
) {
|
||||
if (length() == 0) {
|
||||
setText("#")
|
||||
setSelection(1)
|
||||
|
@ -61,7 +74,8 @@ class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorB
|
|||
val colorFromEditText = Color.parseColor(text.toString())
|
||||
accentPicker.setColor(colorFromEditText)
|
||||
mutableAccentColor.value = colorFromEditText
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,8 +107,12 @@ class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorB
|
|||
mutableAccentColor.value = colorFromEditText
|
||||
prefs.managerAccent = colorFromEditText
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.d("VMTheme", getString(R.string.failed_accent))
|
||||
Toast.makeText(requireActivity(), getString(R.string.failed_accent), Toast.LENGTH_SHORT).show()
|
||||
log("VMTheme", getString(R.string.failed_accent))
|
||||
Toast.makeText(
|
||||
requireActivity(),
|
||||
getString(R.string.failed_accent),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import com.vanced.manager.BuildConfig.MANAGER_LANGUAGES
|
|||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||
import com.vanced.manager.databinding.DialogManagerLanguageBinding
|
||||
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
||||
import com.vanced.manager.utils.getCheckedButtonTag
|
||||
import com.vanced.manager.utils.checkedButtonTag
|
||||
import com.vanced.manager.utils.getLanguageFormat
|
||||
import com.vanced.manager.utils.managerLang
|
||||
|
||||
|
@ -43,7 +43,7 @@ class ManagerLanguageDialog : BindingBottomSheetDialogFragment<DialogManagerLang
|
|||
val language = prefs.managerLang
|
||||
root.findViewWithTag<ThemedMaterialRadioButton>(language)?.isChecked = true
|
||||
languageSave.setOnClickListener {
|
||||
val newPref = binding.languageRadiogroup.getCheckedButtonTag()
|
||||
val newPref = binding.languageRadiogroup.checkedButtonTag
|
||||
if (language != newPref) {
|
||||
prefs.managerLang = newPref
|
||||
dismiss()
|
||||
|
|
|
@ -7,7 +7,7 @@ import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
|||
import com.google.android.material.radiobutton.MaterialRadioButton
|
||||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||
import com.vanced.manager.databinding.DialogManagerThemeBinding
|
||||
import com.vanced.manager.utils.getCheckedButtonTag
|
||||
import com.vanced.manager.utils.checkedButtonTag
|
||||
import com.vanced.manager.utils.managerTheme
|
||||
|
||||
class ManagerThemeDialog : BindingBottomSheetDialogFragment<DialogManagerThemeBinding>() {
|
||||
|
@ -36,7 +36,7 @@ class ManagerThemeDialog : BindingBottomSheetDialogFragment<DialogManagerThemeBi
|
|||
val theme = prefs.managerTheme
|
||||
root.findViewWithTag<MaterialRadioButton>(theme).isChecked = true
|
||||
themeSave.setOnClickListener {
|
||||
val newPref = themeRadiogroup.getCheckedButtonTag()
|
||||
val newPref = themeRadiogroup.checkedButtonTag
|
||||
if (theme != newPref) {
|
||||
prefs.managerTheme = newPref
|
||||
dismiss()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.vanced.manager.ui.dialogs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
@ -16,8 +17,9 @@ import com.vanced.manager.R
|
|||
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
||||
import com.vanced.manager.databinding.DialogManagerUpdateBinding
|
||||
import com.vanced.manager.utils.DownloadHelper.downloadManager
|
||||
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||
import com.vanced.manager.utils.applyAccent
|
||||
import com.vanced.manager.utils.currentDownload
|
||||
import com.vanced.manager.utils.downloadProgress
|
||||
import com.vanced.manager.utils.manager
|
||||
|
||||
class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>() {
|
||||
|
@ -56,7 +58,8 @@ class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>()
|
|||
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
bindData()
|
||||
if (arguments?.getBoolean(TAG_FORCE_UPDATE) == true) {
|
||||
binding.managerUpdatePatient.text = requireActivity().getString(R.string.please_be_patient)
|
||||
binding.managerUpdatePatient.text =
|
||||
requireActivity().getString(R.string.please_be_patient)
|
||||
downloadManager(requireActivity())
|
||||
} else {
|
||||
checkUpdates()
|
||||
|
@ -68,24 +71,20 @@ class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>()
|
|||
isCancelable = false
|
||||
managerUpdateProgressbar.applyAccent()
|
||||
managerUpdateCancel.setOnClickListener {
|
||||
with(downloadProgress.value) {
|
||||
this?.downloadProgress?.value = 0
|
||||
this?.currentDownload?.cancel()
|
||||
}
|
||||
downloadProgress.value = 0
|
||||
currentDownload?.cancel()
|
||||
dismiss()
|
||||
}
|
||||
bindDownloadProgress()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun DialogManagerUpdateBinding.bindDownloadProgress() {
|
||||
with(downloadProgress) {
|
||||
observe(viewLifecycleOwner) { progressModel ->
|
||||
progressModel.downloadProgress.observe(viewLifecycleOwner) {
|
||||
managerUpdateProgressbar.progress = it
|
||||
managerUpdateProgressbar.isVisible = it != 0
|
||||
}
|
||||
}
|
||||
downloadProgress.observe(viewLifecycleOwner) {
|
||||
managerUpdateProgressbar.progress = it
|
||||
managerUpdateProgressbarContainer.isVisible = it != 0
|
||||
managerUpdateProgress.text = "$it%"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,12 +93,19 @@ class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>()
|
|||
registerReceiver()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
localBroadcastManager.unregisterReceiver(broadcastReceiver)
|
||||
}
|
||||
|
||||
private fun checkUpdates() {
|
||||
if (manager.value?.int("versionCode") ?: 0 > VERSION_CODE) {
|
||||
binding.managerUpdatePatient.text = requireActivity().getString(R.string.please_be_patient)
|
||||
binding.managerUpdatePatient.text =
|
||||
requireActivity().getString(R.string.please_be_patient)
|
||||
downloadManager(requireActivity())
|
||||
} else {
|
||||
binding.managerUpdatePatient.text = requireActivity().getString(R.string.update_not_found)
|
||||
binding.managerUpdatePatient.text =
|
||||
requireActivity().getString(R.string.update_not_found)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import com.google.android.material.radiobutton.MaterialRadioButton
|
|||
import com.topjohnwu.superuser.Shell
|
||||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||
import com.vanced.manager.databinding.DialogManagerVariantBinding
|
||||
import com.vanced.manager.utils.getCheckedButtonTag
|
||||
import com.vanced.manager.utils.checkedButtonTag
|
||||
import com.vanced.manager.utils.managerVariant
|
||||
|
||||
class ManagerVariantDialog : BindingBottomSheetDialogFragment<DialogManagerVariantBinding>() {
|
||||
|
@ -37,11 +37,11 @@ class ManagerVariantDialog : BindingBottomSheetDialogFragment<DialogManagerVaria
|
|||
val variant = prefs.managerVariant
|
||||
root.findViewWithTag<MaterialRadioButton>(variant).isChecked = true
|
||||
variantSave.setOnClickListener {
|
||||
val newPref = variantRadiogroup.getCheckedButtonTag()
|
||||
val newPref = variantRadiogroup.checkedButtonTag
|
||||
if (variant != newPref) {
|
||||
prefs.managerVariant =
|
||||
if (newPref == "root" && Shell.rootAccess()) {
|
||||
"root"
|
||||
"root"
|
||||
} else {
|
||||
"nonroot"
|
||||
}
|
||||
|
|
|
@ -7,9 +7,7 @@ import com.vanced.manager.R
|
|||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||
import com.vanced.manager.core.ui.ext.showDialog
|
||||
import com.vanced.manager.databinding.DialogMusicPreferencesBinding
|
||||
import com.vanced.manager.utils.convertToAppVersions
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.musicVersions
|
||||
import com.vanced.manager.utils.*
|
||||
|
||||
class MusicPreferencesDialog : BindingBottomSheetDialogFragment<DialogMusicPreferencesBinding>() {
|
||||
|
||||
|
@ -35,9 +33,13 @@ class MusicPreferencesDialog : BindingBottomSheetDialogFragment<DialogMusicPrefe
|
|||
private fun bindData() {
|
||||
with(binding) {
|
||||
val musicVersionsConv = musicVersions.value?.value?.convertToAppVersions()
|
||||
musicInstallTitle.text = getString(R.string.app_installation_preferences, getString(R.string.music))
|
||||
musicVersion.text = getString(R.string.chosen_version, prefs.getString("music_version", "latest"))
|
||||
openVersionSelector.setOnClickListener {
|
||||
musicInstallTitle.text =
|
||||
getString(R.string.app_installation_preferences, getString(R.string.music))
|
||||
musicVersion.text = getString(
|
||||
R.string.chosen_version,
|
||||
prefs.musicVersion?.formatVersion(requireActivity())
|
||||
)
|
||||
openVersionSelectorLayout.setOnClickListener {
|
||||
dismiss()
|
||||
showDialog(
|
||||
AppVersionSelectorDialog.newInstance(
|
||||
|
|
|
@ -43,12 +43,17 @@ class SelectAppsDialog : BindingBottomSheetDialogFragment<DialogSelectAppsBindin
|
|||
}
|
||||
selectAppsSave.setOnClickListener {
|
||||
if (ad.apps.all { app -> !app.isChecked }) {
|
||||
Toast.makeText(requireActivity(), R.string.select_at_least_one_app, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireActivity(),
|
||||
R.string.select_at_least_one_app,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
prefs.edit {
|
||||
ad.apps.forEach { app ->
|
||||
putBoolean("enable_${app.tag}", app.isChecked)
|
||||
putBoolean("${app.tag}_notifs", app.isChecked)
|
||||
}
|
||||
}
|
||||
dismiss()
|
||||
|
|
|
@ -32,7 +32,7 @@ class ServiceDTimerDialog : BindingDialogFragment<DialogServicedTimerBinding>()
|
|||
}
|
||||
|
||||
private fun bindData() {
|
||||
with (binding) {
|
||||
with(binding) {
|
||||
servicedSlider.value = prefs.getInt("serviced_sleep_timer", 1).toFloat()
|
||||
servicedCancel.setOnClickListener {
|
||||
dismiss()
|
||||
|
@ -41,12 +41,26 @@ class ServiceDTimerDialog : BindingDialogFragment<DialogServicedTimerBinding>()
|
|||
try {
|
||||
arrayOf("vanced", "music").forEach { app ->
|
||||
if (scriptExists(app)) {
|
||||
val apkFPath = "${PackageHelper.apkInstallPath}/${app.capitalize(Locale.ROOT)}/base.apk"
|
||||
getPackageDir(requireActivity(), getPkgNameRoot(app))?.let { it1 -> requireActivity().writeServiceDScript(apkFPath, it1, app) }
|
||||
val apkFPath =
|
||||
"${PackageHelper.apkInstallPath}/${app.capitalize(Locale.ROOT)}/base.apk"
|
||||
getPackageDir(
|
||||
requireActivity(),
|
||||
getPkgNameRoot(app)
|
||||
)?.let { it1 ->
|
||||
requireActivity().writeServiceDScript(
|
||||
apkFPath,
|
||||
it1,
|
||||
app
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Toast.makeText(requireActivity(), R.string.script_save_failed, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireActivity(),
|
||||
R.string.script_save_failed,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
|
|
|
@ -45,11 +45,12 @@ class URLChangeDialog : BindingDialogFragment<DialogCustomUrlBinding>() {
|
|||
TextView.BufferType.EDITABLE
|
||||
)
|
||||
urlSave.setOnClickListener {
|
||||
val finalUrl = if (urlInput.text?.startsWith("https://") == true || urlInput.text?.startsWith("http://") == true) {
|
||||
urlInput.text?.removeSuffix("/").toString()
|
||||
} else {
|
||||
"https://${urlInput.text}".removeSuffix("/")
|
||||
}
|
||||
val finalUrl =
|
||||
if (urlInput.text?.startsWith("https://") == true || urlInput.text?.startsWith("http://") == true) {
|
||||
urlInput.text?.removeSuffix("/").toString()
|
||||
} else {
|
||||
"https://${urlInput.text}".removeSuffix("/")
|
||||
}
|
||||
saveUrl(finalUrl)
|
||||
}
|
||||
urlReset.setOnClickListener { saveUrl(baseUrl) }
|
||||
|
|
|
@ -19,7 +19,8 @@ import com.vanced.manager.utils.lang
|
|||
import com.vanced.manager.utils.vanced
|
||||
import java.util.*
|
||||
|
||||
class VancedLanguageSelectionDialog : BindingBottomSheetDialogFragment<DialogVancedLanguageSelectionBinding>() {
|
||||
class VancedLanguageSelectionDialog :
|
||||
BindingBottomSheetDialogFragment<DialogVancedLanguageSelectionBinding>() {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -52,7 +53,11 @@ class VancedLanguageSelectionDialog : BindingBottomSheetDialogFragment<DialogVan
|
|||
}
|
||||
}
|
||||
if (chosenLangs.isEmpty()) {
|
||||
Toast.makeText(requireActivity(), R.string.select_at_least_one_lang, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireActivity(),
|
||||
R.string.select_at_least_one_lang,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
prefs.lang = chosenLangs.joinToString()
|
||||
|
|
|
@ -3,14 +3,11 @@ package com.vanced.manager.ui.dialogs
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||
import com.vanced.manager.core.ui.ext.showDialog
|
||||
import com.vanced.manager.databinding.DialogVancedPreferencesBinding
|
||||
import com.vanced.manager.utils.*
|
||||
import com.vanced.manager.utils.AppUtils.vancedPkg
|
||||
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
|
||||
import java.util.*
|
||||
|
||||
class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPreferencesBinding>() {
|
||||
|
@ -38,20 +35,27 @@ class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPre
|
|||
private fun bindData() {
|
||||
with(binding) {
|
||||
val showLang = mutableListOf<String>()
|
||||
installPrefs.lang?.split(", ")?.toTypedArray()?.forEach { lang ->
|
||||
installPrefs.lang?.split(", ")?.forEach { lang ->
|
||||
val loc = Locale(lang)
|
||||
showLang.add(loc.getDisplayLanguage(loc).capitalize(Locale.ROOT))
|
||||
}
|
||||
val vancedVersionsConv = vancedVersions.value?.value?.convertToAppVersions()
|
||||
vancedInstallTitle.text = getString(R.string.app_installation_preferences, getString(R.string.vanced))
|
||||
vancedTheme.text = getString(R.string.chosen_theme, installPrefs.getString("theme", "dark")?.convertToAppTheme(requireActivity()))
|
||||
vancedVersion.text = getString(R.string.chosen_version, defPrefs.getString("vanced_version", "latest"))
|
||||
vancedInstallTitle.text =
|
||||
getString(R.string.app_installation_preferences, getString(R.string.vanced))
|
||||
vancedTheme.text = getString(
|
||||
R.string.chosen_theme,
|
||||
installPrefs.theme?.convertToAppTheme(requireActivity())
|
||||
)
|
||||
vancedVersion.text = getString(
|
||||
R.string.chosen_version,
|
||||
defPrefs.vancedVersion?.formatVersion(requireActivity())
|
||||
)
|
||||
vancedLang.text = getString(R.string.chosen_lang, showLang)
|
||||
openThemeSelector.setOnClickListener {
|
||||
openThemeSelectorLayout.setOnClickListener {
|
||||
dismiss()
|
||||
showDialog(VancedThemeSelectorDialog())
|
||||
}
|
||||
openVersionSelector.setOnClickListener {
|
||||
openVersionSelectorLayout.setOnClickListener {
|
||||
dismiss()
|
||||
showDialog(
|
||||
AppVersionSelectorDialog.newInstance(
|
||||
|
@ -60,7 +64,7 @@ class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPre
|
|||
)
|
||||
)
|
||||
}
|
||||
openLanguageSelector.setOnClickListener {
|
||||
openLanguageSelectorLayout.setOnClickListener {
|
||||
dismiss()
|
||||
showDialog(VancedLanguageSelectionDialog())
|
||||
}
|
||||
|
@ -68,33 +72,12 @@ class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPre
|
|||
if (showLang.isEmpty()) {
|
||||
installPrefs.lang = "en"
|
||||
}
|
||||
|
||||
fun downloadVanced(version: String? = null) {
|
||||
dismiss()
|
||||
showDialog(
|
||||
AppDownloadDialog.newInstance(
|
||||
app = getString(R.string.vanced),
|
||||
version = version
|
||||
)
|
||||
dismiss()
|
||||
showDialog(
|
||||
AppDownloadDialog.newInstance(
|
||||
app = getString(R.string.vanced)
|
||||
)
|
||||
}
|
||||
|
||||
if (defPrefs.managerVariant == "nonroot" && isMicrogBroken && installPrefs.vancedVersion?.getLatestAppVersion(vancedVersions.value?.value ?: listOf(""))?.take(2)?.toIntOrNull() == 16 && !isPackageInstalled(vancedPkg, requireActivity().packageManager)) {
|
||||
MaterialAlertDialogBuilder(requireActivity()).apply {
|
||||
setTitle(R.string.microg_bug)
|
||||
setMessage(R.string.microg_bug_summary)
|
||||
setPositiveButton(R.string.auth_dialog_ok) { _, _ ->
|
||||
downloadVanced("15.43.32")
|
||||
}
|
||||
setNeutralButton(R.string.cancel) { _, _ ->
|
||||
dismiss()
|
||||
}
|
||||
create()
|
||||
}.applyAccent()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
downloadVanced()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,13 @@ import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
|||
import com.vanced.manager.core.ui.ext.showDialog
|
||||
import com.vanced.manager.databinding.DialogBottomRadioButtonBinding
|
||||
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
||||
import com.vanced.manager.utils.checkedButtonTag
|
||||
import com.vanced.manager.utils.convertToAppTheme
|
||||
import com.vanced.manager.utils.getCheckedButtonTag
|
||||
import com.vanced.manager.utils.theme
|
||||
import com.vanced.manager.utils.vanced
|
||||
|
||||
class VancedThemeSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
||||
class VancedThemeSelectorDialog :
|
||||
BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -24,7 +25,12 @@ class VancedThemeSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomR
|
|||
}
|
||||
}
|
||||
|
||||
private val prefs by lazy { requireActivity().getSharedPreferences("installPrefs", Context.MODE_PRIVATE) }
|
||||
private val prefs by lazy {
|
||||
requireActivity().getSharedPreferences(
|
||||
"installPrefs",
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
}
|
||||
|
||||
override fun binding(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -51,7 +57,7 @@ class VancedThemeSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomR
|
|||
tag.isChecked = true
|
||||
}
|
||||
dialogSave.setOnClickListener {
|
||||
val checkedTag = binding.dialogRadiogroup.getCheckedButtonTag()
|
||||
val checkedTag = binding.dialogRadiogroup.checkedButtonTag
|
||||
if (checkedTag != null) {
|
||||
prefs.theme = checkedTag
|
||||
}
|
||||
|
@ -60,7 +66,7 @@ class VancedThemeSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomR
|
|||
}
|
||||
}
|
||||
|
||||
private fun loadButtons() = vanced.value?.array<String>("themes")?.value?.map {theme ->
|
||||
private fun loadButtons() = vanced.value?.array<String>("themes")?.value?.map { theme ->
|
||||
ThemedMaterialRadioButton(requireActivity()).apply {
|
||||
text = theme.convertToAppTheme(requireActivity())
|
||||
tag = theme
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
package com.vanced.manager.ui.events
|
||||
|
||||
open class Event<out T>(private val content: T) {
|
||||
|
||||
private var hasBeenHandled = false
|
||||
|
||||
/**
|
||||
* Returns the content and prevents its use again.
|
||||
*/
|
||||
fun getContentIfNotHandled(): T? {
|
||||
return if (hasBeenHandled) {
|
||||
null
|
||||
} else {
|
||||
hasBeenHandled = true
|
||||
content
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content, even if it's already been handled.
|
||||
*/
|
||||
fun peekContent(): T = content
|
||||
}
|
|
@ -6,7 +6,6 @@ import android.view.LayoutInflater
|
|||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.content.edit
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.preference.PreferenceManager
|
||||
|
@ -37,11 +36,11 @@ class AboutFragment : BindingFragment<FragmentAboutBinding>() {
|
|||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun dataBind() {
|
||||
requireActivity().title = getString(R.string.title_about)
|
||||
binding.aboutHeader.root.setOnClickListener {
|
||||
binding.aboutVersionCard.setOnClickListener {
|
||||
showDialog(
|
||||
AppInfoDialog.newInstance(
|
||||
appName = getString(R.string.app_name),
|
||||
appIcon = AppCompatResources.getDrawable(requireActivity(), R.mipmap.ic_launcher),
|
||||
appIcon = R.mipmap.ic_launcher,
|
||||
changelog = manager.value?.string("changelog")
|
||||
)
|
||||
)
|
||||
|
@ -61,17 +60,25 @@ class AboutFragment : BindingFragment<FragmentAboutBinding>() {
|
|||
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
val devSettings = prefs.getBoolean("devSettings", false)
|
||||
if (!devSettings) {
|
||||
Toast.makeText(requireContext(), "Dev options unlocked!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
"Dev options unlocked!",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
prefs.edit { putBoolean("devSettings", true) }
|
||||
} else
|
||||
Toast.makeText(requireContext(), "Dev options already unlocked", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
"Dev options already unlocked",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
|
||||
}
|
||||
return@setOnTouchListener true
|
||||
}
|
||||
false
|
||||
}
|
||||
binding.aboutSources.aboutGithubButton.setOnClickListener { viewModel.openUrl("https://github.com/YTVanced/VancedInstaller") }
|
||||
binding.aboutSources.aboutLicenseButton.setOnClickListener { viewModel.openUrl("https://raw.githubusercontent.com/YTVanced/VancedInstaller/dev/LICENSE") }
|
||||
binding.aboutGithubButton.setOnClickListener { viewModel.openUrl("https://github.com/YTVanced/VancedInstaller") }
|
||||
binding.aboutLicenseButton.setOnClickListener { viewModel.openUrl("https://raw.githubusercontent.com/YTVanced/VancedInstaller/dev/LICENSE") }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,22 +3,15 @@ package com.vanced.manager.ui.fragments
|
|||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.vanced.manager.BuildConfig
|
||||
import com.vanced.manager.core.ui.base.BindingFragment
|
||||
import com.vanced.manager.databinding.FragmentDevSettingsBinding
|
||||
import com.vanced.manager.ui.WelcomeActivity
|
||||
import com.vanced.manager.ui.dialogs.ManagerUpdateDialog
|
||||
import com.vanced.manager.ui.dialogs.URLChangeDialog
|
||||
import com.vanced.manager.utils.authCrowdin
|
||||
|
||||
class DevSettingsFragment : BindingFragment<FragmentDevSettingsBinding>() {
|
||||
|
||||
|
@ -40,7 +33,6 @@ class DevSettingsFragment : BindingFragment<FragmentDevSettingsBinding>() {
|
|||
bindWelcomeLauncher()
|
||||
bindForceUpdate()
|
||||
bindChannelURL()
|
||||
bindCrowdin()
|
||||
bindKernelArch()
|
||||
bindAndroidVersion()
|
||||
}
|
||||
|
@ -72,31 +64,6 @@ class DevSettingsFragment : BindingFragment<FragmentDevSettingsBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun FragmentDevSettingsBinding.bindCrowdin() {
|
||||
if (BuildConfig.ENABLE_CROWDIN_AUTH) {
|
||||
val isAuthorized = Crowdin.isAuthorized()
|
||||
crowdinCategory.isVisible = true
|
||||
|
||||
crowdinAuth.isVisible = !isAuthorized
|
||||
screenshotUploading.isVisible = isAuthorized
|
||||
realTimeUpdates.isVisible = isAuthorized
|
||||
|
||||
crowdinAuth.setOnClickListener {
|
||||
requireActivity().authCrowdin()
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
if (!Settings.canDrawOverlays(requireActivity())) {
|
||||
val intent = Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
("package:" + requireActivity().packageName).toUri()
|
||||
)
|
||||
startActivityForResult(intent, 69)
|
||||
}
|
||||
|
||||
Crowdin.authorize(requireActivity())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun FragmentDevSettingsBinding.bindKernelArch() {
|
||||
val supportedAbis: Array<String> = Build.SUPPORTED_ABIS
|
||||
|
||||
|
|
|
@ -41,7 +41,12 @@ class GrantRootFragment : BindingFragment<FragmentGrantRootBinding>() {
|
|||
|
||||
private fun grantRoot() {
|
||||
if (Shell.rootAccess()) {
|
||||
getDefaultSharedPreferences(requireActivity()).edit { putString("vanced_variant", "root") }
|
||||
getDefaultSharedPreferences(requireActivity()).edit {
|
||||
putString(
|
||||
"vanced_variant",
|
||||
"root"
|
||||
)
|
||||
}
|
||||
navigateToFirstLaunch()
|
||||
} else {
|
||||
Toast.makeText(requireActivity(), R.string.root_not_granted, Toast.LENGTH_SHORT).show()
|
||||
|
|
|
@ -9,41 +9,36 @@ import android.view.LayoutInflater
|
|||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.crowdin.platform.util.inflateWithCrowdin
|
||||
import com.github.florent37.viewtooltip.ViewTooltip
|
||||
import com.google.android.flexbox.FlexboxLayoutManager
|
||||
import com.google.android.flexbox.JustifyContent
|
||||
import com.vanced.manager.BuildConfig.VERSION_CODE
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.adapter.AppListAdapter
|
||||
import com.vanced.manager.adapter.ExpandableAppListAdapter
|
||||
import com.vanced.manager.adapter.LinkAdapter
|
||||
import com.vanced.manager.adapter.SponsorAdapter
|
||||
import com.vanced.manager.core.ui.base.BindingFragment
|
||||
import com.vanced.manager.core.ui.ext.showDialog
|
||||
import com.vanced.manager.databinding.FragmentHomeBinding
|
||||
import com.vanced.manager.ui.dialogs.AppInfoDialog
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.installAlertBuilder
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModelFactory
|
||||
import com.vanced.manager.utils.isFetching
|
||||
import com.vanced.manager.utils.manager
|
||||
|
||||
open class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
||||
class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
||||
|
||||
companion object {
|
||||
const val INSTALL_FAILED = "INSTALL_FAILED"
|
||||
const val REFRESH_HOME = "REFRESH_HOME"
|
||||
}
|
||||
|
||||
private val viewModel: HomeViewModel by viewModels {
|
||||
HomeViewModelFactory(requireActivity())
|
||||
}
|
||||
private val viewModel: HomeViewModel by viewModels()
|
||||
|
||||
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(requireActivity()) }
|
||||
private val prefs by lazy { PreferenceManager.getDefaultSharedPreferences(requireActivity()) }
|
||||
private lateinit var tooltip: ViewTooltip
|
||||
|
||||
override fun binding(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -58,28 +53,27 @@ open class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
|||
private fun bindData() {
|
||||
requireActivity().title = getString(R.string.title_home)
|
||||
setHasOptionsMenu(true)
|
||||
with (binding) {
|
||||
with(binding) {
|
||||
homeRefresh.setOnRefreshListener { viewModel.fetchData() }
|
||||
isFetching.observe(viewLifecycleOwner) { homeRefresh.isRefreshing = it }
|
||||
tooltip = ViewTooltip
|
||||
.on(recyclerAppList)
|
||||
.position(ViewTooltip.Position.TOP)
|
||||
.autoHide(false, 0)
|
||||
.color(ResourcesCompat.getColor(requireActivity().resources, R.color.Twitter, null))
|
||||
.withShadow(false)
|
||||
.corner(25)
|
||||
.onHide {
|
||||
prefs.edit { putBoolean("show_changelog_tooltip", false) }
|
||||
}
|
||||
.text(requireActivity().getString(R.string.app_changelog_tooltip))
|
||||
|
||||
if (prefs.getBoolean("show_changelog_tooltip", true)) {
|
||||
tooltip.show()
|
||||
}
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
if (prefs.contains("LastVersionCode")) {
|
||||
if (prefs.getInt("LastVersionCode", -1) < VERSION_CODE) {
|
||||
showDialog(
|
||||
AppInfoDialog.newInstance(
|
||||
appName = getString(R.string.app_name),
|
||||
appIcon = R.mipmap.ic_launcher,
|
||||
changelog = manager.value?.string("changelog")
|
||||
)
|
||||
)
|
||||
prefs.edit().putInt("LastVersionCode", VERSION_CODE).apply()
|
||||
}
|
||||
} else prefs.edit().putInt("LastVersionCode", VERSION_CODE).apply()
|
||||
|
||||
recyclerAppList.apply {
|
||||
layoutManager = LinearLayoutManager(requireActivity())
|
||||
adapter = AppListAdapter(requireActivity(), viewModel, viewLifecycleOwner, tooltip)
|
||||
adapter = ExpandableAppListAdapter(requireActivity(), viewModel /*, tooltip*/)
|
||||
setHasFixedSize(true)
|
||||
}
|
||||
|
||||
|
@ -102,14 +96,12 @@ open class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
|||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflateWithCrowdin(R.menu.toolbar_menu, menu, resources)
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(R.menu.toolbar_menu, menu)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
localBroadcastManager.unregisterReceiver(broadcastReceiver)
|
||||
tooltip.close()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -120,7 +112,11 @@ open class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
|||
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (intent.action) {
|
||||
INSTALL_FAILED -> installAlertBuilder(intent.getStringExtra("errorMsg").toString(), intent.getStringExtra("fullErrorMsg"), requireActivity())
|
||||
INSTALL_FAILED -> installAlertBuilder(
|
||||
intent.getStringExtra("errorMsg").toString(),
|
||||
intent.getStringExtra("fullErrorMsg"),
|
||||
requireActivity()
|
||||
)
|
||||
REFRESH_HOME -> viewModel.fetchData()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.ui.base.BindingFragment
|
||||
import com.vanced.manager.databinding.FragmentLogBinding
|
||||
import com.vanced.manager.utils.AppUtils.logs
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class LogFragment : BindingFragment<FragmentLogBinding>() {
|
||||
|
||||
override fun binding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = FragmentLogBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun otherSetups() {
|
||||
binding.bindData()
|
||||
}
|
||||
|
||||
private fun FragmentLogBinding.bindData() {
|
||||
val logs = TextUtils.concat(*logs.toTypedArray())
|
||||
logText.text = logs
|
||||
logSave.setOnClickListener {
|
||||
try {
|
||||
val calendar = Calendar.getInstance()
|
||||
val year = calendar.get(Calendar.YEAR)
|
||||
val month = calendar.get(Calendar.MONTH)
|
||||
val day = calendar.get(Calendar.DAY_OF_MONTH)
|
||||
val hour = calendar.get(Calendar.HOUR_OF_DAY)
|
||||
val minute = calendar.get(Calendar.MINUTE)
|
||||
val second = calendar.get(Calendar.SECOND)
|
||||
val savePath = requireActivity().getExternalFilesDir("logs")?.path + "/$year$month${day}_$hour$minute$second.log"
|
||||
val log =
|
||||
File(savePath)
|
||||
FileWriter(log).apply {
|
||||
append(logs)
|
||||
flush()
|
||||
close()
|
||||
}
|
||||
Toast.makeText(requireActivity(), getString(R.string.logs_saved, savePath), Toast.LENGTH_SHORT).show()
|
||||
} catch (e: IOException) {
|
||||
Toast.makeText(requireActivity(), R.string.logs_not_saved, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -5,13 +5,13 @@ import android.view.LayoutInflater
|
|||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.edit
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.adapter.SelectAppsAdapter
|
||||
import com.vanced.manager.core.ui.base.BindingFragment
|
||||
import com.vanced.manager.databinding.FragmentSelectAppsBinding
|
||||
import com.vanced.manager.ui.WelcomeActivity
|
||||
|
||||
class SelectAppsFragment : BindingFragment<FragmentSelectAppsBinding>() {
|
||||
|
||||
|
@ -45,13 +45,14 @@ class SelectAppsFragment : BindingFragment<FragmentSelectAppsBinding>() {
|
|||
|
||||
private fun actionOnClickAppsFab() {
|
||||
if (selectAdapter.apps.all { app -> !app.isChecked }) {
|
||||
Toast.makeText(requireActivity(), R.string.select_at_least_one_app, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireActivity(), R.string.select_at_least_one_app, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
return
|
||||
}
|
||||
val prefs = getDefaultSharedPreferences(requireActivity())
|
||||
selectAdapter.apps.forEach { app ->
|
||||
prefs.edit { putBoolean("enable_${app.tag}", app.isChecked) }
|
||||
}
|
||||
findNavController().navigate(SelectAppsFragmentDirections.selectAppsToGrantRoot())
|
||||
(requireActivity() as WelcomeActivity).navigateTo(2)
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import android.view.MenuInflater
|
|||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
|
@ -34,6 +35,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
private val prefs by lazy { getDefaultSharedPreferences(requireActivity()) }
|
||||
|
||||
private lateinit var variant: String
|
||||
private lateinit var parentActivity: FragmentActivity
|
||||
|
||||
override fun binding(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -43,6 +45,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
|
||||
override fun otherSetups() {
|
||||
setHasOptionsMenu(true)
|
||||
parentActivity = requireActivity()
|
||||
bindData()
|
||||
}
|
||||
|
||||
|
@ -63,8 +66,8 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
|
||||
private fun FragmentSettingsBinding.bindRecycler() {
|
||||
notificationsRecycler.apply {
|
||||
layoutManager = LinearLayoutManager(requireActivity())
|
||||
adapter = GetNotifAdapter(requireActivity())
|
||||
layoutManager = LinearLayoutManager(parentActivity)
|
||||
adapter = GetNotifAdapter(parentActivity)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +75,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
firebase.setOnCheckedListener { _, isChecked ->
|
||||
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(isChecked)
|
||||
FirebasePerformance.getInstance().isPerformanceCollectionEnabled = isChecked
|
||||
FirebaseAnalytics.getInstance(requireActivity()).setAnalyticsCollectionEnabled(isChecked)
|
||||
FirebaseAnalytics.getInstance(parentActivity).setAnalyticsCollectionEnabled(isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +96,13 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
private fun FragmentSettingsBinding.bindClearFiles() {
|
||||
clearFiles.setOnClickListener {
|
||||
with(requireActivity()) {
|
||||
listOf("vanced/nonroot", "vanced/root", "music/nonroot", "music/root", "microg").forEach { dir ->
|
||||
listOf(
|
||||
"vanced/nonroot",
|
||||
"vanced/root",
|
||||
"music/nonroot",
|
||||
"music/root",
|
||||
"microg"
|
||||
).forEach { dir ->
|
||||
File(getExternalFilesDir(dir)?.path.toString()).deleteRecursively()
|
||||
}
|
||||
Toast.makeText(this, getString(R.string.cleared_files), Toast.LENGTH_SHORT).show()
|
||||
|
@ -116,7 +125,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
}
|
||||
|
||||
private fun FragmentSettingsBinding.bindManagerAccentColor() {
|
||||
managerAccentColor.apply{
|
||||
managerAccentColor.apply {
|
||||
setSummary(prefs.getInt("manager_accent_color", defAccentColor).toHex())
|
||||
setOnClickListener { showDialog(ManagerAccentColorDialog()) }
|
||||
accentColor.observe(viewLifecycleOwner) {
|
||||
|
@ -128,14 +137,15 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
private fun FragmentSettingsBinding.bindManagerLanguage() {
|
||||
val langPref = prefs.getString("manager_lang", "System Default")
|
||||
managerLanguage.apply {
|
||||
setSummary(getLanguageFormat(requireActivity(), requireNotNull(langPref)))
|
||||
setSummary(getLanguageFormat(parentActivity, requireNotNull(langPref)))
|
||||
setOnClickListener { showDialog(ManagerLanguageDialog()) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
val devSettings = getDefaultSharedPreferences(requireActivity()).getBoolean("devSettings", false)
|
||||
val devSettings =
|
||||
getDefaultSharedPreferences(requireActivity()).getBoolean("devSettings", false)
|
||||
if (devSettings) {
|
||||
inflater.inflate(R.menu.dev_settings_menu, menu)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
//import android.os.Bundle
|
||||
//import android.view.LayoutInflater
|
||||
//import android.view.View
|
||||
//import android.view.ViewGroup
|
||||
//import androidx.compose.foundation.lazy.LazyColumn
|
||||
//import androidx.compose.ui.platform.ComposeView
|
||||
//import androidx.fragment.app.Fragment
|
||||
//import com.vanced.manager.R
|
||||
//import com.vanced.manager.ui.compose.Preference
|
||||
//import com.vanced.manager.ui.compose.PreferenceCategory
|
||||
//import com.vanced.manager.ui.compose.SwitchPreference
|
||||
//
|
||||
//class SettingsFragmentCompose : Fragment() {
|
||||
//
|
||||
// override fun onCreateView(
|
||||
// inflater: LayoutInflater,
|
||||
// container: ViewGroup?,
|
||||
// savedInstanceState: Bundle?
|
||||
// ): View {
|
||||
// return ComposeView(requireActivity()).apply {
|
||||
// setContent {
|
||||
// LazyColumn {
|
||||
// // use `item` for separate elements like headers
|
||||
// // and `items` for lists of identical elements
|
||||
// item {
|
||||
// PreferenceCategory(
|
||||
// categoryTitle = getString(R.string.category_behaviour)
|
||||
// ) {
|
||||
// SwitchPreference(
|
||||
// preferenceTitle = getString(R.string.use_custom_tabs),
|
||||
// preferenceDescription = getString(R.string.link_custom_tabs),
|
||||
// preferenceKey = "use_custom_tabs"
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// item {
|
||||
// PreferenceCategory(
|
||||
// categoryTitle = getString(R.string.category_appearance)
|
||||
// ) {
|
||||
// Preference(
|
||||
// preferenceTitle = "test",
|
||||
// preferenceDescription = "test",
|
||||
// ) {}
|
||||
// Preference(
|
||||
// preferenceTitle = "test"
|
||||
// ) {}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
|
@ -3,9 +3,9 @@ package com.vanced.manager.ui.fragments
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.vanced.manager.core.ui.base.BindingFragment
|
||||
import com.vanced.manager.databinding.FragmentWelcomeBinding
|
||||
import com.vanced.manager.ui.WelcomeActivity
|
||||
|
||||
class WelcomeFragment : BindingFragment<FragmentWelcomeBinding>() {
|
||||
|
||||
|
@ -20,10 +20,8 @@ class WelcomeFragment : BindingFragment<FragmentWelcomeBinding>() {
|
|||
}
|
||||
|
||||
private fun bindData() {
|
||||
binding.welcomeGetStarted.setOnClickListener { navigateToWelcome() }
|
||||
}
|
||||
|
||||
private fun navigateToWelcome() {
|
||||
findNavController().navigate(WelcomeFragmentDirections.welcomeToSelectApps())
|
||||
binding.welcomeGetStarted.setOnClickListener {
|
||||
(requireActivity() as WelcomeActivity).navigateTo(1)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import androidx.lifecycle.AndroidViewModel
|
|||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.openUrl
|
||||
|
||||
class AboutViewModel(application: Application): AndroidViewModel(application) {
|
||||
class AboutViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
fun openUrl(url: String) {
|
||||
openUrl(url, R.color.GitHub, getApplication())
|
||||
|
|
|
@ -1,25 +1,23 @@
|
|||
package com.vanced.manager.ui.viewmodels
|
||||
|
||||
import android.app.Application
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.adapter.LinkAdapter.Companion.DISCORD
|
||||
import com.vanced.manager.adapter.LinkAdapter.Companion.REDDIT
|
||||
import com.vanced.manager.adapter.LinkAdapter.Companion.TELEGRAM
|
||||
import com.vanced.manager.adapter.LinkAdapter.Companion.TWITTER
|
||||
import com.vanced.manager.adapter.SponsorAdapter.Companion.BRAVE
|
||||
import com.vanced.manager.model.ButtonTag
|
||||
import com.vanced.manager.model.DataModel
|
||||
import com.vanced.manager.model.RootDataModel
|
||||
import com.vanced.manager.ui.dialogs.AppDownloadDialog
|
||||
|
@ -27,6 +25,7 @@ import com.vanced.manager.ui.dialogs.InstallationFilesDetectedDialog
|
|||
import com.vanced.manager.ui.dialogs.MusicPreferencesDialog
|
||||
import com.vanced.manager.ui.dialogs.VancedPreferencesDialog
|
||||
import com.vanced.manager.utils.*
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
import com.vanced.manager.utils.AppUtils.managerPkg
|
||||
import com.vanced.manager.utils.AppUtils.microgPkg
|
||||
import com.vanced.manager.utils.AppUtils.musicPkg
|
||||
|
@ -40,10 +39,11 @@ import com.vanced.manager.utils.PackageHelper.uninstallRootApk
|
|||
import com.vanced.manager.utils.PackageHelper.vancedInstallFilesExist
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
open class HomeViewModel(private val activity: FragmentActivity): ViewModel() {
|
||||
class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
private val prefs = getDefaultSharedPreferences(activity)
|
||||
private val prefs = getDefaultSharedPreferences(context)
|
||||
private val variant get() = prefs.getString("vanced_variant", "nonroot")
|
||||
private val context: Context get() = getApplication()
|
||||
|
||||
val vancedModel = MutableLiveData<DataModel>()
|
||||
val vancedRootModel = MutableLiveData<RootDataModel>()
|
||||
|
@ -54,14 +54,13 @@ open class HomeViewModel(private val activity: FragmentActivity): ViewModel() {
|
|||
|
||||
fun fetchData() {
|
||||
viewModelScope.launch {
|
||||
loadJson(activity)
|
||||
Crowdin.forceUpdate(activity)
|
||||
loadJson(context)
|
||||
}
|
||||
}
|
||||
|
||||
private val microgToast = Toast.makeText(activity, R.string.no_microg, Toast.LENGTH_LONG)
|
||||
|
||||
fun openUrl(url: String) {
|
||||
private val microgToast = Toast.makeText(context, R.string.no_microg, Toast.LENGTH_LONG)
|
||||
|
||||
fun openUrl(context: Context, url: String) {
|
||||
val color: Int =
|
||||
when (url) {
|
||||
DISCORD -> R.color.Discord
|
||||
|
@ -71,75 +70,83 @@ open class HomeViewModel(private val activity: FragmentActivity): ViewModel() {
|
|||
BRAVE -> R.color.Brave
|
||||
else -> R.color.Vanced
|
||||
}
|
||||
|
||||
openUrl(url, color, activity)
|
||||
|
||||
openUrl(url, color, context)
|
||||
}
|
||||
|
||||
fun launchApp(app: String, isRoot: Boolean) {
|
||||
val componentName = when (app) {
|
||||
activity.getString(R.string.vanced) -> if (isRoot) ComponentName(vancedRootPkg, "$vancedRootPkg.HomeActivity") else ComponentName(vancedPkg, "$vancedRootPkg.HomeActivity")
|
||||
activity.getString(R.string.music) -> if (isRoot) ComponentName(musicRootPkg, "$musicRootPkg.activities.MusicActivity") else ComponentName(musicPkg, "$musicRootPkg.activities.MusicActivity")
|
||||
activity.getString(R.string.microg) -> ComponentName(microgPkg, "org.microg.gms.ui.SettingsActivity")
|
||||
context.getString(R.string.vanced) -> if (isRoot) ComponentName(
|
||||
vancedRootPkg,
|
||||
"$vancedRootPkg.HomeActivity"
|
||||
) else ComponentName(vancedPkg, "$vancedRootPkg.HomeActivity")
|
||||
context.getString(R.string.music) -> if (isRoot) ComponentName(
|
||||
musicRootPkg,
|
||||
"$musicRootPkg.activities.MusicActivity"
|
||||
) else ComponentName(musicPkg, "$musicRootPkg.activities.MusicActivity")
|
||||
context.getString(R.string.microg) -> ComponentName(
|
||||
microgPkg,
|
||||
"org.microg.gms.ui.SettingsActivity"
|
||||
)
|
||||
else -> throw IllegalArgumentException("Can't open this app")
|
||||
}
|
||||
try {
|
||||
activity.startActivity(Intent().setComponent(componentName))
|
||||
context.startActivity(Intent().setComponent(componentName))
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.d("VMHMV", e.toString())
|
||||
log("VMHMV", e.toString())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun openInstallDialog(view: View, app: String) {
|
||||
if (variant == "nonroot" && app != activity.getString(R.string.microg) && !microgModel.value?.isAppInstalled?.value!!) {
|
||||
fun openInstallDialog(fragmentManager: FragmentManager, buttonTag: ButtonTag?, app: String) {
|
||||
if (variant == "nonroot" && app != context.getString(R.string.microg) && !microgModel.value?.isAppInstalled?.value!!) {
|
||||
microgToast.show()
|
||||
return
|
||||
}
|
||||
|
||||
if ((view as MaterialButton).text == activity.getString(R.string.update)) {
|
||||
if (buttonTag == ButtonTag.UPDATE) {
|
||||
when (app) {
|
||||
activity.getString(R.string.vanced) -> VancedPreferencesDialog().show(activity)
|
||||
activity.getString(R.string.music) -> MusicPreferencesDialog().show(activity)
|
||||
else -> AppDownloadDialog.newInstance(app).show(activity)
|
||||
context.getString(R.string.vanced) -> VancedPreferencesDialog().show(fragmentManager)
|
||||
context.getString(R.string.music) -> MusicPreferencesDialog().show(fragmentManager)
|
||||
else -> AppDownloadDialog.newInstance(app).show(fragmentManager)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
when (app) {
|
||||
activity.getString(R.string.vanced) -> {
|
||||
context.getString(R.string.vanced) -> {
|
||||
when (variant) {
|
||||
"nonroot" -> {
|
||||
if (vancedInstallFilesExist(activity)) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(activity)
|
||||
if (vancedInstallFilesExist(context)) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
|
||||
} else {
|
||||
VancedPreferencesDialog().show(activity)
|
||||
VancedPreferencesDialog().show(fragmentManager)
|
||||
}
|
||||
}
|
||||
"root" -> {
|
||||
VancedPreferencesDialog().show(activity)
|
||||
VancedPreferencesDialog().show(fragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
activity.getString(R.string.music) -> {
|
||||
context.getString(R.string.music) -> {
|
||||
when (variant) {
|
||||
"nonroot" -> {
|
||||
if (musicApkExists(activity)) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(activity)
|
||||
if (musicApkExists(context)) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
|
||||
} else {
|
||||
MusicPreferencesDialog().show(activity)
|
||||
MusicPreferencesDialog().show(fragmentManager)
|
||||
}
|
||||
}
|
||||
"root" -> {
|
||||
MusicPreferencesDialog().show(activity)
|
||||
MusicPreferencesDialog().show(fragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
activity.getString(R.string.microg) -> {
|
||||
if (apkExist(activity, "microg.apk")) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(activity)
|
||||
context.getString(R.string.microg) -> {
|
||||
if (apkExist(context, "microg.apk")) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
|
||||
} else {
|
||||
AppDownloadDialog.newInstance(app).show(activity)
|
||||
AppDownloadDialog.newInstance(app).show(fragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,23 +155,67 @@ open class HomeViewModel(private val activity: FragmentActivity): ViewModel() {
|
|||
|
||||
fun uninstallPackage(pkg: String) {
|
||||
if (variant == "root" && uninstallRootApk(pkg)) {
|
||||
viewModelScope.launch { loadJson(activity) }
|
||||
viewModelScope.launch { loadJson(context) }
|
||||
} else {
|
||||
uninstallApk(pkg, activity)
|
||||
uninstallApk(pkg, context)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
with (activity) {
|
||||
with(context) {
|
||||
if (variant == "root") {
|
||||
vancedRootModel.value = RootDataModel(vanced, this, this, vancedRootPkg, this.getString(R.string.vanced), AppCompatResources.getDrawable(this, R.drawable.ic_vanced), "vanced")
|
||||
musicRootModel.value = RootDataModel(music, this, this, musicRootPkg, this.getString(R.string.music), AppCompatResources.getDrawable(this, R.drawable.ic_music), "music")
|
||||
vancedRootModel.value = RootDataModel(
|
||||
vanced,
|
||||
this,
|
||||
vancedRootPkg,
|
||||
this.getString(R.string.vanced),
|
||||
this.getString(R.string.description_vanced),
|
||||
R.drawable.ic_vanced,
|
||||
"vanced"
|
||||
)
|
||||
musicRootModel.value = RootDataModel(
|
||||
music,
|
||||
this,
|
||||
musicRootPkg,
|
||||
this.getString(R.string.music),
|
||||
this.getString(R.string.description_vanced_music),
|
||||
R.drawable.ic_music,
|
||||
"music"
|
||||
)
|
||||
} else {
|
||||
vancedModel.value = DataModel(vanced, this, this, vancedPkg, this.getString(R.string.vanced), AppCompatResources.getDrawable(this, R.drawable.ic_vanced))
|
||||
musicModel.value = DataModel(music, this, this, musicPkg, this.getString(R.string.music), AppCompatResources.getDrawable(this, R.drawable.ic_music))
|
||||
microgModel.value = DataModel(microg, this, this, microgPkg, this.getString(R.string.microg), AppCompatResources.getDrawable(this, R.drawable.ic_microg))
|
||||
vancedModel.value = DataModel(
|
||||
vanced,
|
||||
this,
|
||||
vancedPkg,
|
||||
this.getString(R.string.vanced),
|
||||
this.getString(R.string.description_vanced),
|
||||
R.drawable.ic_vanced
|
||||
)
|
||||
musicModel.value = DataModel(
|
||||
music,
|
||||
this,
|
||||
musicPkg,
|
||||
this.getString(R.string.music),
|
||||
this.getString(R.string.description_vanced_music),
|
||||
R.drawable.ic_music
|
||||
)
|
||||
microgModel.value = DataModel(
|
||||
microg,
|
||||
this,
|
||||
microgPkg,
|
||||
this.getString(R.string.microg),
|
||||
this.getString(R.string.description_microg),
|
||||
R.drawable.ic_microg
|
||||
)
|
||||
}
|
||||
managerModel.value = DataModel(manager, this, this, managerPkg, this.getString(R.string.app_name), AppCompatResources.getDrawable(this, R.mipmap.ic_launcher))
|
||||
managerModel.value = DataModel(
|
||||
manager,
|
||||
this,
|
||||
managerPkg,
|
||||
this.getString(R.string.app_name),
|
||||
"Just manager meh",
|
||||
R.mipmap.ic_launcher
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
package com.vanced.manager.ui.viewmodels
|
||||
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
|
||||
class HomeViewModelFactory(private val activity: FragmentActivity) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return HomeViewModel(activity) as T
|
||||
}
|
||||
|
||||
}
|
|
@ -2,65 +2,81 @@ package com.vanced.manager.utils
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.util.Log
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.vanced.manager.BuildConfig
|
||||
import com.vanced.manager.BuildConfig.APPLICATION_ID
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.dialogs.AppDownloadDialog
|
||||
import com.vanced.manager.ui.fragments.HomeFragment
|
||||
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
|
||||
object AppUtils: CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||
object AppUtils : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||
|
||||
const val vancedPkg = "com.vanced.android.youtube"
|
||||
const val vancedRootPkg = "com.google.android.youtube"
|
||||
const val musicPkg = "com.vanced.android.apps.youtube.music"
|
||||
const val musicRootPkg = "com.google.android.apps.youtube.music"
|
||||
const val microgPkg = "com.mgoogle.android.gms"
|
||||
const val faqpkg = "com.vanced.faq"
|
||||
const val managerPkg = APPLICATION_ID
|
||||
const val playStorePkg = "com.android.vending"
|
||||
|
||||
val logs = mutableListOf<Spannable>()
|
||||
|
||||
var currentLocale: Locale? = null
|
||||
|
||||
fun log(tag: String, message: String) {
|
||||
logs.add(
|
||||
SpannableString("$tag: $message\n").apply {
|
||||
setSpan(ForegroundColorSpan(Color.parseColor("#2e73ff")), 0, tag.length + 1, 0)
|
||||
setSpan(StyleSpan(Typeface.BOLD), 0, tag.length + 1, 0)
|
||||
setSpan(
|
||||
ForegroundColorSpan(Color.MAGENTA),
|
||||
tag.length + 2,
|
||||
tag.length + message.length + 2,
|
||||
0
|
||||
)
|
||||
}
|
||||
)
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(tag, message)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendRefresh(context: Context): Job {
|
||||
return launch {
|
||||
delay(700)
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
||||
LocalBroadcastManager.getInstance(context)
|
||||
.sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
||||
}
|
||||
}
|
||||
|
||||
fun sendCloseDialog(context: Context): Job {
|
||||
return launch {
|
||||
delay(700)
|
||||
downloadProgress.value?.installing?.postValue(false)
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(AppDownloadDialog.CLOSE_DIALOG))
|
||||
installing.postValue(false)
|
||||
LocalBroadcastManager.getInstance(context)
|
||||
.sendBroadcast(Intent(AppDownloadDialog.CLOSE_DIALOG))
|
||||
}
|
||||
}
|
||||
|
||||
fun sendFailure(status: Int, fullError: String?, context: Context): Job {
|
||||
//Delay error broadcast until activity (and fragment) get back to the screen
|
||||
return launch {
|
||||
delay(700)
|
||||
val intent = Intent(HomeFragment.INSTALL_FAILED)
|
||||
intent.putExtra("errorMsg", getErrorMessage(status, context))
|
||||
intent.putExtra("fullErrorMsg", fullError)
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendFailure(error: MutableList<String>, context: Context): Job {
|
||||
return launch {
|
||||
delay(700)
|
||||
val intent = Intent(HomeFragment.INSTALL_FAILED)
|
||||
intent.putExtra("errorMsg", getErrorMessage(error.joinToString(), context))
|
||||
intent.putExtra("fullErrorMsg", error.joinToString(" "))
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
|
||||
}
|
||||
fun sendFailure(error: List<String>, context: Context) {
|
||||
sendFailure(error.joinToString(" "), context)
|
||||
}
|
||||
|
||||
fun sendFailure(error: String, context: Context): Job {
|
||||
//Delay error broadcast until activity (and fragment) get back to the screen
|
||||
return launch {
|
||||
delay(700)
|
||||
val intent = Intent(HomeFragment.INSTALL_FAILED)
|
||||
|
@ -86,7 +102,7 @@ object AppUtils: CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
private fun printableHexString(data: ByteArray): String {
|
||||
// Create Hex String
|
||||
val hexString: StringBuilder = StringBuilder()
|
||||
for (aMessageDigest:Byte in data) {
|
||||
for (aMessageDigest: Byte in data) {
|
||||
var h: String = Integer.toHexString(0xFF and aMessageDigest.toInt())
|
||||
while (h.length < 2)
|
||||
h = "0$h"
|
||||
|
@ -106,6 +122,7 @@ object AppUtils: CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
}
|
||||
|
||||
private fun getErrorMessage(status: String, context: Context): String {
|
||||
log("VMInstall", status)
|
||||
return when {
|
||||
status.contains("INSTALL_FAILED_ABORTED") -> context.getString(R.string.installation_aborted)
|
||||
status.contains("INSTALL_FAILED_ALREADY_EXISTS") -> context.getString(R.string.installation_conflict)
|
||||
|
@ -120,27 +137,10 @@ object AppUtils: CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
status.contains("ModApk_Missing") -> context.getString(R.string.modapk_missing)
|
||||
status.contains("Files_Missing_VA") -> context.getString(R.string.files_missing_va)
|
||||
status.contains("Path_Missing") -> context.getString(R.string.path_missing)
|
||||
else ->
|
||||
if (isMiui())
|
||||
context.getString(R.string.installation_miui)
|
||||
else
|
||||
context.getString(R.string.installation_failed)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getErrorMessage(status: Int, context: Context): String {
|
||||
return when (status) {
|
||||
PackageInstaller.STATUS_FAILURE_ABORTED -> context.getString(R.string.installation_aborted)
|
||||
PackageInstaller.STATUS_FAILURE_BLOCKED -> context.getString(R.string.installation_blocked)
|
||||
PackageInstaller.STATUS_FAILURE_CONFLICT -> context.getString(R.string.installation_conflict)
|
||||
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> context.getString(R.string.installation_incompatible)
|
||||
PackageInstaller.STATUS_FAILURE_INVALID -> context.getString(R.string.installation_invalid)
|
||||
PackageInstaller.STATUS_FAILURE_STORAGE -> context.getString(R.string.installation_storage)
|
||||
else ->
|
||||
if (isMiui())
|
||||
context.getString(R.string.installation_miui)
|
||||
else
|
||||
context.getString(R.string.installation_failed)
|
||||
status.contains("INSTALL_FAILED_INTERNAL_ERROR: Permission Denied") -> context.getString(
|
||||
R.string.installation_miui
|
||||
)
|
||||
else -> context.getString(R.string.installation_failed)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.os.Build
|
||||
|
||||
fun getArch(): String = when {
|
||||
Build.SUPPORTED_ABIS.contains("x86") -> "x86"
|
||||
Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a"
|
||||
else -> "armeabi_v7a"
|
||||
}
|
||||
}
|
||||
|
||||
val intentFlags =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
PendingIntent.FLAG_MUTABLE
|
||||
else
|
||||
0
|
|
@ -1,15 +1,14 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.library.network.providers.createService
|
||||
import com.vanced.manager.model.ProgressModel
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
import com.vanced.manager.utils.AppUtils.sendCloseDialog
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -39,40 +38,49 @@ object DownloadHelper : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
fileFolder: String,
|
||||
fileName: String,
|
||||
context: Context,
|
||||
onDownloadComplete: () -> Unit,
|
||||
onError: (error: String) -> Unit
|
||||
onDownloadComplete: () -> Unit = {},
|
||||
onError: (error: String) -> Unit = {}
|
||||
) {
|
||||
downloadProgress.value?.downloadingFile?.postValue(context.getString(R.string.downloading_file, fileName))
|
||||
downloadingFile.postValue(context.getString(R.string.downloading_file, fileName))
|
||||
val downloadInterface = createService(DownloadHelper::class, baseUrl)
|
||||
val download = downloadInterface.download(url)
|
||||
downloadProgress.value?.currentDownload = download
|
||||
currentDownload = download
|
||||
download.enqueue(object : Callback<ResponseBody> {
|
||||
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
|
||||
if (response.isSuccessful) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
if (response.body()?.let { writeFile(it, context.getExternalFilesDir(fileFolder)?.path + "/" + fileName) } == true) {
|
||||
launch {
|
||||
if (response.body()?.let {
|
||||
writeFile(
|
||||
it,
|
||||
context.getExternalFilesDir(fileFolder)?.path + "/" + fileName
|
||||
)
|
||||
} == true) {
|
||||
onDownloadComplete()
|
||||
} else {
|
||||
onError("Could not save file")
|
||||
downloadProgress.value?.downloadProgress?.postValue(0)
|
||||
Log.d("VMDownloader", "Failed to save file: $url")
|
||||
downloadProgress.postValue(0)
|
||||
log(
|
||||
"VMDownloader",
|
||||
"Failed to save file: $url\n${response.errorBody()}"
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
onError(response.errorBody().toString())
|
||||
downloadProgress.value?.downloadProgress?.postValue(0)
|
||||
Log.d("VMDownloader", "Failed to download file: $url")
|
||||
val errorBody = response.errorBody().toString()
|
||||
onError(errorBody)
|
||||
downloadProgress.postValue(0)
|
||||
log("VMDownloader", "Failed to download file: $url\n$errorBody")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
|
||||
if (call.isCanceled) {
|
||||
Log.d("VMDownloader", "Download canceled")
|
||||
downloadProgress.value?.downloadProgress?.postValue(0)
|
||||
log("VMDownloader", "Download canceled")
|
||||
downloadProgress.postValue(0)
|
||||
} else {
|
||||
onError(t.stackTraceToString())
|
||||
downloadProgress.value?.downloadProgress?.postValue(0)
|
||||
Log.d("VMDownloader", "Failed to download file: $url")
|
||||
downloadProgress.postValue(0)
|
||||
log("VMDownloader", "Failed to download file: $url")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,7 +102,7 @@ object DownloadHelper : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
while (inputStream.read(fileReader).also { read = it } != -1) {
|
||||
outputStream.write(fileReader, 0, read)
|
||||
downloadedBytes += read.toLong()
|
||||
downloadProgress.value?.downloadProgress?.postValue((downloadedBytes * 100 / totalBytes).toInt())
|
||||
downloadProgress.postValue((downloadedBytes * 100 / totalBytes).toInt())
|
||||
}
|
||||
outputStream.flush()
|
||||
true
|
||||
|
@ -109,36 +117,42 @@ object DownloadHelper : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
}
|
||||
}
|
||||
|
||||
val downloadProgress = MutableLiveData<ProgressModel>()
|
||||
|
||||
init {
|
||||
downloadProgress.value = ProgressModel()
|
||||
}
|
||||
|
||||
fun downloadManager(context: Context) {
|
||||
val url = "https://github.com/YTVanced/VancedManager/releases/latest/download/manager.apk"
|
||||
download(url,"https://github.com/YTVanced/VancedManager/", "manager", "manager.apk", context, onDownloadComplete = {
|
||||
val apk = File("${context.getExternalFilesDir("manager")?.path}/manager.apk")
|
||||
val uri =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
FileProvider.getUriForFile(context, "${context.packageName}.provider", apk)
|
||||
else
|
||||
Uri.fromFile(apk)
|
||||
download(
|
||||
url,
|
||||
"https://github.com/YTVanced/VancedManager/",
|
||||
"manager",
|
||||
"manager.apk",
|
||||
context,
|
||||
onDownloadComplete = {
|
||||
val apk = File("${context.getExternalFilesDir("manager")?.path}/manager.apk")
|
||||
val uri =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
FileProvider.getUriForFile(context, "${context.packageName}.provider", apk)
|
||||
else
|
||||
Uri.fromFile(apk)
|
||||
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(uri, "application/vnd.android.package-archive")
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
context.startActivity(intent)
|
||||
sendCloseDialog(context)
|
||||
}, onError = {
|
||||
downloadProgress.value?.downloadingFile?.postValue(
|
||||
context.getString(
|
||||
R.string.error_downloading,
|
||||
"manager.apk"
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(uri, "application/vnd.android.package-archive")
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
try {
|
||||
context.startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
log("VMDownloader", e.stackTraceToString())
|
||||
} finally {
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
downloadingFile.postValue(
|
||||
context.getString(
|
||||
R.string.error_downloading,
|
||||
"manager.apk"
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ package com.vanced.manager.utils
|
|||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.DialogInterface
|
||||
import android.util.Log
|
||||
import android.widget.RadioGroup
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
|
@ -15,55 +15,61 @@ import com.google.android.material.radiobutton.MaterialRadioButton
|
|||
import com.topjohnwu.superuser.io.SuFile
|
||||
import com.topjohnwu.superuser.io.SuFileOutputStream
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
import java.util.*
|
||||
|
||||
fun RadioGroup.getCheckedButtonTag(): String? {
|
||||
return findViewById<MaterialRadioButton>(checkedRadioButtonId)?.tag?.toString()
|
||||
val RadioGroup.checkedButtonTag: String?
|
||||
get() = findViewById<MaterialRadioButton>(
|
||||
checkedRadioButtonId
|
||||
)?.tag?.toString()
|
||||
|
||||
fun DialogFragment.show(fragmentManager: FragmentManager) {
|
||||
try {
|
||||
show(fragmentManager, "")
|
||||
} catch (e: Exception) {
|
||||
log("VMUI", e.stackTraceToString())
|
||||
}
|
||||
}
|
||||
|
||||
fun DialogFragment.show(activity: FragmentActivity) {
|
||||
try {
|
||||
show(activity.supportFragmentManager, "")
|
||||
} catch (e: Exception) {
|
||||
Log.d("VMUI", e.stackTraceToString())
|
||||
}
|
||||
|
||||
show(activity.supportFragmentManager)
|
||||
}
|
||||
|
||||
fun List<String>.convertToAppVersions(): List<String> = listOf("latest") + reversed()
|
||||
|
||||
fun String.convertToAppTheme(context: Context): String {
|
||||
return context.getString(R.string.light_plus_other, this.capitalize(Locale.ROOT))
|
||||
fun String.formatVersion(context: Context): String =
|
||||
if (this == "latest") context.getString(R.string.install_latest) else this
|
||||
|
||||
fun String.convertToAppTheme(context: Context): String = with(context) {
|
||||
getString(
|
||||
R.string.light_plus_other,
|
||||
if (this@convertToAppTheme == "dark") getString(R.string.vanced_dark) else getString(R.string.vanced_black)
|
||||
)
|
||||
}
|
||||
|
||||
fun String.getLatestAppVersion(versions: List<String>): String = if (this == "latest") versions.reversed()[0] else this
|
||||
fun String.getLatestAppVersion(versions: List<String>): String =
|
||||
if (this == "latest") versions.reversed()[0] else this
|
||||
|
||||
fun Context.lifecycleOwner(): LifecycleOwner? {
|
||||
var curContext = this
|
||||
var maxDepth = 20
|
||||
while (maxDepth-- > 0 && curContext !is LifecycleOwner) {
|
||||
curContext = (curContext as ContextWrapper).baseContext
|
||||
val Context.lifecycleOwner: LifecycleOwner?
|
||||
get() = when (this) {
|
||||
is LifecycleOwner -> this
|
||||
!is LifecycleOwner -> (this as ContextWrapper).baseContext as LifecycleOwner
|
||||
else -> null
|
||||
}
|
||||
return if (curContext is LifecycleOwner) {
|
||||
curContext
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun Int.toHex(): String = java.lang.String.format("#%06X", 0xFFFFFF and this)
|
||||
|
||||
//Material team decided to keep their LinearProgressIndicator final
|
||||
//At least extension methods exist
|
||||
fun LinearProgressIndicator.applyAccent() {
|
||||
with(accentColor.value ?: context.defPrefs.managerAccent) {
|
||||
with(accentColor.value!!) {
|
||||
setIndicatorColor(this)
|
||||
trackColor = ColorUtils.setAlphaComponent(this, 70)
|
||||
}
|
||||
}
|
||||
|
||||
fun MaterialAlertDialogBuilder.applyAccent() {
|
||||
with(accentColor.value ?: context.defPrefs.managerAccent) {
|
||||
fun MaterialAlertDialogBuilder.showWithAccent() {
|
||||
with(accentColor.value!!) {
|
||||
show().apply {
|
||||
getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(this@with)
|
||||
getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(this@with)
|
||||
|
@ -75,6 +81,12 @@ fun MaterialAlertDialogBuilder.applyAccent() {
|
|||
fun Context.writeServiceDScript(apkFPath: String, path: String, app: String) {
|
||||
val shellFileZ = SuFile.open("/data/adb/service.d/$app.sh")
|
||||
shellFileZ.createNewFile()
|
||||
val code = """#!/system/bin/sh${"\n"}while [ "`getprop sys.boot_completed | tr -d '\r' `" != "1" ]; do sleep ${defPrefs.serviceDSleepTimer}; done${"\n"}chcon u:object_r:apk_data_file:s0 $apkFPath${"\n"}mount -o bind $apkFPath $path"""
|
||||
SuFileOutputStream(shellFileZ).use { out -> out.write(code.toByteArray())}
|
||||
val script = """
|
||||
#!/system/bin/sh
|
||||
while [ "$(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 1; done
|
||||
sleep ${defPrefs.serviceDSleepTimer}
|
||||
chcon u:object_r:apk_data_file:s0 $apkFPath
|
||||
mount -o bind $apkFPath $path
|
||||
""".trimIndent()
|
||||
SuFileOutputStream.open(shellFileZ).use { out -> out.write(script.toByteArray()) }
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
|
||||
open class FileInfo(val name: String, val fileSize: Long, val file: File? = null) {
|
||||
|
||||
open fun getInputStream(): InputStream =
|
||||
if (file!= null)
|
||||
FileInputStream(file)
|
||||
else
|
||||
throw NotImplementedError("need some way to create InputStream")
|
||||
}
|
|
@ -4,7 +4,11 @@ import android.content.Context
|
|||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
|
||||
val Context.installPrefs: SharedPreferences get() = getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||
val Context.installPrefs: SharedPreferences
|
||||
get() = getSharedPreferences(
|
||||
"installPrefs",
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
|
||||
var SharedPreferences.lang
|
||||
get() = getString("lang", getDefaultVancedLanguages())
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.beust.klaxon.JsonArray
|
|||
import com.beust.klaxon.JsonObject
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.AppUtils.generateChecksum
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
@ -31,8 +32,8 @@ val music = MutableLiveData<JsonObject?>()
|
|||
val microg = MutableLiveData<JsonObject?>()
|
||||
val manager = MutableLiveData<JsonObject?>()
|
||||
|
||||
val vancedVersions = MutableLiveData<JsonArray<String>>()
|
||||
val musicVersions = MutableLiveData<JsonArray<String>>()
|
||||
val vancedVersions = MutableLiveData<JsonArray<String>?>()
|
||||
val musicVersions = MutableLiveData<JsonArray<String>?>()
|
||||
|
||||
val isFetching = MutableLiveData<Boolean>()
|
||||
|
||||
|
@ -42,19 +43,28 @@ var baseInstallUrl = ""
|
|||
|
||||
fun openUrl(url: String, color: Int, context: Context) {
|
||||
try {
|
||||
val customTabPrefs = getDefaultSharedPreferences(context).getBoolean("use_custom_tabs", true)
|
||||
val customTabPrefs =
|
||||
getDefaultSharedPreferences(context).getBoolean("use_custom_tabs", true)
|
||||
if (customTabPrefs) {
|
||||
val builder = CustomTabsIntent.Builder()
|
||||
val params = CustomTabColorSchemeParams.Builder().setToolbarColor(ContextCompat.getColor(context, color))
|
||||
val params = CustomTabColorSchemeParams.Builder()
|
||||
.setToolbarColor(ContextCompat.getColor(context, color))
|
||||
builder.setDefaultColorSchemeParams(params.build())
|
||||
val customTabsIntent = builder.build()
|
||||
customTabsIntent.intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
customTabsIntent.launchUrl(context, url.toUri())
|
||||
} else
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, url.toUri()).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||
context.startActivity(
|
||||
Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
url.toUri()
|
||||
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
)
|
||||
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Toast.makeText(context, R.string.error, Toast.LENGTH_SHORT).show()
|
||||
} catch (e: SecurityException) {
|
||||
Toast.makeText(context, R.string.error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,17 +88,17 @@ suspend fun loadJson(context: Context) = withContext(Dispatchers.IO) {
|
|||
connect()
|
||||
}
|
||||
if (connection.responseCode != 200) {
|
||||
Log.d(TAG, latestbaseUrl + ": " + connection.responseCode.toString())
|
||||
log(TAG, latestbaseUrl + ": " + connection.responseCode.toString())
|
||||
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
|
||||
} catch (e: SocketTimeoutException) {
|
||||
Log.d(TAG, "connection timed out")
|
||||
log(TAG, "connection timed out")
|
||||
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
|
||||
}
|
||||
|
||||
Log.d(TAG, "Fetching using URL: $baseInstallUrl")
|
||||
log(TAG, "Fetching using URL: $baseInstallUrl")
|
||||
|
||||
val calendar = Calendar.getInstance()
|
||||
val hour = calendar.get(Calendar.HOUR_OF_DAY)
|
||||
|
@ -100,7 +110,7 @@ suspend fun loadJson(context: Context) = withContext(Dispatchers.IO) {
|
|||
val versions = getJson("$baseInstallUrl/versions.json?$fetchTime")
|
||||
isMicrogBroken = latest?.boolean("is_microg_broken") ?: false
|
||||
vanced.postValue(latest?.obj("vanced"))
|
||||
vancedVersions.postValue(versions?.array("vanced") )
|
||||
vancedVersions.postValue(versions?.array("vanced"))
|
||||
music.postValue(latest?.obj("music"))
|
||||
musicVersions.postValue(versions?.array("music"))
|
||||
microg.postValue(latest?.obj("microg"))
|
||||
|
@ -134,4 +144,4 @@ fun checkSHA256(sha256: String, updateFile: File): Boolean {
|
|||
}
|
||||
}
|
||||
|
||||
const val baseUrl = "https://vancedapp.com/api/v1"
|
||||
const val baseUrl = "https://api.vancedapp.com/api/v1"
|
|
@ -4,8 +4,7 @@ import android.content.Context
|
|||
import android.content.ContextWrapper
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.vanced.manager.utils.AppUtils.currentLocale
|
||||
import java.util.*
|
||||
|
||||
class LanguageContextWrapper(base: Context?) : ContextWrapper(base) {
|
||||
|
@ -15,20 +14,22 @@ class LanguageContextWrapper(base: Context?) : ContextWrapper(base) {
|
|||
fun wrap(context: Context): ContextWrapper {
|
||||
val config: Configuration = context.resources.configuration
|
||||
context.createConfigurationContext(setLocale(config, context))
|
||||
Crowdin.wrapContext(context)
|
||||
return LanguageContextWrapper(context)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun setLocale(config: Configuration, context: Context): Configuration {
|
||||
val pref = PreferenceManager.getDefaultSharedPreferences(context).getString("manager_lang", "System Default")
|
||||
val pref = context.defPrefs.managerLang
|
||||
val sysLocale = Resources.getSystem().configuration.locale
|
||||
val locale =
|
||||
when {
|
||||
pref == "System Default" -> Locale(sysLocale.language, sysLocale.country)
|
||||
pref?.length!! > 2 -> Locale(pref.substring(0, pref.length - 3), pref.substring(pref.length - 2))
|
||||
else -> Locale(pref)
|
||||
}
|
||||
val locale = when {
|
||||
pref == "System Default" -> Locale(sysLocale.language, sysLocale.country)
|
||||
pref?.length!! > 2 -> Locale(
|
||||
pref.substring(0, pref.length - 3),
|
||||
pref.substring(pref.length - 2)
|
||||
)
|
||||
else -> Locale(pref)
|
||||
}
|
||||
currentLocale = locale
|
||||
Locale.setDefault(locale)
|
||||
config.setLocale(locale)
|
||||
return config
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.LocaleList
|
||||
import android.provider.Settings
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.vanced.manager.R
|
||||
import java.util.*
|
||||
|
||||
|
@ -34,7 +29,10 @@ fun getLanguageFormat(context: Context, language: String): String {
|
|||
@Suppress("DEPRECATION")
|
||||
fun getDefaultVancedLanguages(): String {
|
||||
val serverLangs = vanced.value?.array("langs") ?: mutableListOf("")
|
||||
val sysLocales = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) Resources.getSystem().configuration.locales.toLangTags() else arrayOf(Resources.getSystem().configuration.locale.language)
|
||||
val sysLocales =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) Resources.getSystem().configuration.locales.toLangTags() else arrayOf(
|
||||
Resources.getSystem().configuration.locale.language
|
||||
)
|
||||
val finalLangs = mutableListOf<String>()
|
||||
sysLocales.forEach { sysLocale ->
|
||||
when {
|
||||
|
@ -53,26 +51,4 @@ fun LocaleList.toLangTags(): Array<String> {
|
|||
langTags[i] = langTags[i].substring(0, 2)
|
||||
}
|
||||
return langTags
|
||||
}
|
||||
|
||||
fun Activity.authCrowdin() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (!Settings.canDrawOverlays(this)) {
|
||||
val intent = Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:$packageName")
|
||||
)
|
||||
startActivityForResult(intent, 69)
|
||||
return
|
||||
}
|
||||
Crowdin.authorize(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.onActivityResult(requestCode: Int) {
|
||||
if (requestCode == 69 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (Settings.canDrawOverlays(this)) {
|
||||
Crowdin.authorize(this)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +1,12 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import android.content.Context
|
||||
import android.provider.Settings
|
||||
|
||||
private const val MIUI_PROP_NAME = "ro.miui.ui.version.name"
|
||||
private const val MIUI_OPTIMIZATION = "miui_optimization"
|
||||
|
||||
fun isMiui(): Boolean = !getSystemProps(MIUI_PROP_NAME).isNullOrEmpty()
|
||||
|
||||
private fun getSystemProps(propname: String): String? {
|
||||
var input: BufferedReader? = null
|
||||
return try {
|
||||
val process = Runtime.getRuntime().exec("getprop $propname")
|
||||
input = BufferedReader(InputStreamReader(process.inputStream), 1024)
|
||||
input.readLine()
|
||||
} catch (e: IOException) {
|
||||
null
|
||||
} finally {
|
||||
input?.close()
|
||||
}
|
||||
}
|
||||
val Context.isMiuiOptimizationsEnabled: Boolean
|
||||
get() = Settings.Secure.getString(
|
||||
contentResolver,
|
||||
MIUI_OPTIMIZATION
|
||||
) == "1"
|
|
@ -7,12 +7,11 @@ import android.content.pm.PackageInfo
|
|||
import android.content.pm.PackageInstaller
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.io.SuFile
|
||||
import com.vanced.manager.BuildConfig
|
||||
import com.vanced.manager.core.installer.AppInstallerService
|
||||
import com.vanced.manager.core.installer.AppUninstallerService
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
import com.vanced.manager.utils.AppUtils.musicRootPkg
|
||||
import com.vanced.manager.utils.AppUtils.playStorePkg
|
||||
import com.vanced.manager.utils.AppUtils.sendCloseDialog
|
||||
|
@ -22,27 +21,18 @@ import com.vanced.manager.utils.AppUtils.vancedRootPkg
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.*
|
||||
import java.text.SimpleDateFormat
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
object PackageHelper {
|
||||
|
||||
const val apkInstallPath = "/data/adb"
|
||||
private const val INSTALLER_TAG = "VMInstall"
|
||||
private val vancedThemes = vanced.value?.array<String>("themes")?.value ?: listOf("black", "dark", "pink", "blue")
|
||||
|
||||
init {
|
||||
Shell.enableVerboseLogging = BuildConfig.DEBUG
|
||||
Shell.setDefaultBuilder(
|
||||
Shell.Builder.create()
|
||||
.setFlags(Shell.FLAG_REDIRECT_STDERR)
|
||||
.setTimeout(10)
|
||||
)
|
||||
}
|
||||
private val vancedThemes =
|
||||
vanced.value?.array<String>("themes")?.value ?: listOf("black", "dark", "pink", "blue")
|
||||
|
||||
private fun getAppNameRoot(pkg: String): String {
|
||||
return when (pkg) {
|
||||
|
@ -61,7 +51,6 @@ object PackageHelper {
|
|||
return false
|
||||
}
|
||||
|
||||
|
||||
fun getPkgNameRoot(app: String): String {
|
||||
return when (app) {
|
||||
"vanced" -> vancedRootPkg
|
||||
|
@ -69,6 +58,7 @@ object PackageHelper {
|
|||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
fun isPackageInstalled(packageName: String, packageManager: PackageManager): Boolean {
|
||||
return try {
|
||||
packageManager.getPackageInfo(packageName, 0)
|
||||
|
@ -86,7 +76,7 @@ object PackageHelper {
|
|||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
fun getPkgVerCode(pkg: String, pm:PackageManager): Int? {
|
||||
fun getPkgVerCode(pkg: String, pm: PackageManager): Int? {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
pm.getPackageInfo(pkg, 0)?.longVersionCode?.and(0xFFFFFFFF)?.toInt()
|
||||
else
|
||||
|
@ -127,9 +117,15 @@ object PackageHelper {
|
|||
if (files?.isNotEmpty() == true) {
|
||||
for (file in files) {
|
||||
when {
|
||||
vancedThemes.any { file.name == "$it.apk" } && !splitFiles.contains("base") -> splitFiles.add("base")
|
||||
file.name.matches(Regex("split_config\\.(..)\\.apk")) && !splitFiles.contains("lang") -> splitFiles.add("lang")
|
||||
(file.name.startsWith("split_config.arm") || file.name.startsWith("split_config.x86")) && !splitFiles.contains("arch") -> splitFiles.add("arch")
|
||||
vancedThemes.any { file.name == "$it.apk" } && !splitFiles.contains("base") -> splitFiles.add(
|
||||
"base"
|
||||
)
|
||||
file.name.matches(Regex("split_config\\.(..)\\.apk")) && !splitFiles.contains(
|
||||
"lang"
|
||||
) -> splitFiles.add("lang")
|
||||
(file.name.startsWith("split_config.arm") || file.name.startsWith("split_config.x86")) && !splitFiles.contains(
|
||||
"arch"
|
||||
) -> splitFiles.add("arch")
|
||||
}
|
||||
|
||||
if (splitFiles.size == 3) {
|
||||
|
@ -153,7 +149,7 @@ object PackageHelper {
|
|||
fun uninstallApk(pkg: String, context: Context) {
|
||||
val callbackIntent = Intent(context, AppUninstallerService::class.java)
|
||||
callbackIntent.putExtra("pkg", pkg)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, intentFlags)
|
||||
try {
|
||||
context.packageManager.packageInstaller.uninstall(pkg, pendingIntent.intentSender)
|
||||
} catch (e: Exception) {
|
||||
|
@ -162,73 +158,94 @@ object PackageHelper {
|
|||
}
|
||||
|
||||
fun install(path: String, context: Context) {
|
||||
val callbackIntent = Intent(context, AppInstallerService::class.java)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, intentFlags)
|
||||
val packageInstaller = context.packageManager.packageInstaller
|
||||
val params =
|
||||
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
params.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED)
|
||||
}
|
||||
val sessionId: Int
|
||||
var session: PackageInstaller.Session? = null
|
||||
try {
|
||||
val callbackIntent = Intent(context, AppInstallerService::class.java)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0)
|
||||
val packageInstaller = context.packageManager.packageInstaller
|
||||
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
val sessionId = packageInstaller.createSession(params)
|
||||
val session = packageInstaller.openSession(sessionId)
|
||||
sessionId = packageInstaller.createSession(params)
|
||||
session = packageInstaller.openSession(sessionId)
|
||||
val inputStream: InputStream = FileInputStream(path)
|
||||
val outputStream = session.openWrite("install", 0, -1)
|
||||
val buffer = ByteArray(65536)
|
||||
var c: Int
|
||||
while (inputStream.read(buffer).also { c = it } != -1) {
|
||||
outputStream.write(buffer, 0, c)
|
||||
var length: Int
|
||||
while (inputStream.read(buffer).also { length = it } > 0) {
|
||||
outputStream.write(buffer, 0, length)
|
||||
}
|
||||
session.fsync(outputStream)
|
||||
inputStream.close()
|
||||
outputStream.close()
|
||||
session.commit(pendingIntent.intentSender)
|
||||
} catch (e: IOException) {
|
||||
Log.d(INSTALLER_TAG, e.stackTraceToString())
|
||||
} catch (e: Exception) {
|
||||
log(INSTALLER_TAG, e.stackTraceToString())
|
||||
sendFailure(e.stackTraceToString(), context)
|
||||
sendCloseDialog(context)
|
||||
} finally {
|
||||
session?.close()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun installRootMusic(files: ArrayList<FileInfo>, context: Context): Boolean {
|
||||
private fun installRootMusic(files: List<File>, context: Context): Boolean {
|
||||
files.forEach { apk ->
|
||||
if (apk.name != "root.apk") {
|
||||
val newPath = "/data/local/tmp/${apk.file?.name}"
|
||||
val newPath = "/data/local/tmp/${apk.name}"
|
||||
|
||||
//moving apk to tmp folder in order to avoid permission denials
|
||||
Shell.su("mv ${apk.file?.path} $newPath").exec()
|
||||
val command = Shell.su("pm install $newPath").exec()
|
||||
//Copy apk to tmp folder in order to avoid permission denials
|
||||
Shell.su("cp ${apk.path} $newPath").exec()
|
||||
val command = Shell.su("pm install -r $newPath").exec()
|
||||
Shell.su("rm $newPath").exec()
|
||||
if (command.isSuccess) {
|
||||
return true
|
||||
} else {
|
||||
if (!command.isSuccess) {
|
||||
sendFailure(command.out, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
private fun installRootApp(context: Context, app: String, appVerCode: Int, pkg: String, modApkBool: (fileName: String) -> Boolean) = CoroutineScope(Dispatchers.IO).launch {
|
||||
Shell.getShell {
|
||||
val apkFilesPath = context.getExternalFilesDir("$app/root")?.path
|
||||
val fileInfoList = apkFilesPath?.let { it1 -> getFileInfoList(it1) }
|
||||
if (fileInfoList != null) {
|
||||
val modApk: FileInfo? = fileInfoList.lastOrNull { modApkBool(it.name) }
|
||||
if (modApk != null) {
|
||||
if (overwriteBase(modApk, fileInfoList, appVerCode, pkg, app, context)) {
|
||||
private fun installRootApp(
|
||||
context: Context,
|
||||
app: String,
|
||||
appVerCode: Int?,
|
||||
pkg: String,
|
||||
modApkBool: (fileName: String) -> Boolean
|
||||
) = CoroutineScope(Dispatchers.IO).launch {
|
||||
if (!isMagiskInstalled()) {
|
||||
sendFailure("NO_MAGISK", context)
|
||||
sendCloseDialog(context)
|
||||
return@launch
|
||||
}
|
||||
|
||||
val apkFilesPath = context.getExternalFilesDir("$app/root")?.path
|
||||
val files = File(apkFilesPath.toString()).listFiles()?.toList()
|
||||
if (files != null) {
|
||||
val modApk: File? = files.lastOrNull { modApkBool(it.name) }
|
||||
if (modApk != null) {
|
||||
if (appVerCode != null) {
|
||||
if (overwriteBase(modApk, files, appVerCode, pkg, app, context)) {
|
||||
setInstallerPackage(context, pkg, playStorePkg)
|
||||
Log.d(INSTALLER_TAG, "Finished installation")
|
||||
log(INSTALLER_TAG, "Finished installation")
|
||||
sendRefresh(context)
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
} else {
|
||||
sendFailure(listOf("ModApk_Missing").toMutableList(), context)
|
||||
sendFailure("appVerCode is null", context)
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
} else {
|
||||
sendFailure(listOf("Files_Missing_VA").toMutableList(), context)
|
||||
sendFailure("ModApk_Missing", context)
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
|
||||
} else {
|
||||
sendFailure("Files_Missing_VA", context)
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -237,7 +254,7 @@ object PackageHelper {
|
|||
installRootApp(
|
||||
context,
|
||||
"music",
|
||||
music.value?.int("versionCode")!!,
|
||||
music.value?.int("versionCode"),
|
||||
musicRootPkg
|
||||
) {
|
||||
it == "root.apk"
|
||||
|
@ -248,202 +265,95 @@ object PackageHelper {
|
|||
installRootApp(
|
||||
context,
|
||||
"vanced",
|
||||
vanced.value?.int("versionCode")!!,
|
||||
vanced.value?.int("versionCode"),
|
||||
vancedRootPkg
|
||||
) { fileName ->
|
||||
vancedThemes.any { fileName == "$it.apk" }
|
||||
}
|
||||
}
|
||||
|
||||
fun installVanced(context: Context): Int {
|
||||
val apkFolderPath = context.getExternalFilesDir("vanced/nonroot")?.path.toString() + "/"
|
||||
val nameSizeMap = HashMap<String, Long>()
|
||||
var totalSize: Long = 0
|
||||
var sessionId = 0
|
||||
val folder = File(apkFolderPath)
|
||||
val listOfFiles = folder.listFiles()
|
||||
fun installSplitApkFiles(
|
||||
context: Context,
|
||||
appName: String
|
||||
) {
|
||||
val packageInstaller = context.packageManager.packageInstaller
|
||||
val folder = File(context.getExternalFilesDir("$appName/nonroot")?.path.toString())
|
||||
var session: PackageInstaller.Session? = null
|
||||
val sessionId: Int
|
||||
val sessionParams =
|
||||
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
sessionParams.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED)
|
||||
}
|
||||
val callbackIntent = Intent(context, AppInstallerService::class.java)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, intentFlags)
|
||||
try {
|
||||
for (listOfFile in listOfFiles!!) {
|
||||
if (listOfFile.isFile) {
|
||||
Log.d(INSTALLER_TAG, "installApk: " + listOfFile.name)
|
||||
nameSizeMap[listOfFile.name] = listOfFile.length()
|
||||
totalSize += listOfFile.length()
|
||||
sessionId = packageInstaller.createSession(sessionParams)
|
||||
session = packageInstaller.openSession(sessionId)
|
||||
folder.listFiles()?.forEach { apk ->
|
||||
val inputStream = FileInputStream(apk)
|
||||
val outputStream = session.openWrite(apk.name, 0, apk.length())
|
||||
val buffer = ByteArray(65536)
|
||||
var length: Int
|
||||
while (inputStream.read(buffer).also { length = it } > 0) {
|
||||
outputStream.write(buffer, 0, length)
|
||||
}
|
||||
session.fsync(outputStream)
|
||||
inputStream.close()
|
||||
outputStream.close()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return -1
|
||||
}
|
||||
val installParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
installParams.setSize(totalSize)
|
||||
try {
|
||||
sessionId = context.packageManager.packageInstaller.createSession(installParams)
|
||||
Log.d(INSTALLER_TAG,"Success: created install session [$sessionId]")
|
||||
for ((key, value) in nameSizeMap) {
|
||||
doWriteSession(sessionId, apkFolderPath + key, value, key, context)
|
||||
}
|
||||
doCommitSession(sessionId, context)
|
||||
Log.d(INSTALLER_TAG,"Success")
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return sessionId
|
||||
}
|
||||
|
||||
private fun doWriteSession(sessionId: Int, inPath: String?, sizeBytes: Long, splitName: String, context: Context): Int {
|
||||
var inPathToUse = inPath
|
||||
var sizeBytesToUse = sizeBytes
|
||||
if ("-" == inPathToUse) {
|
||||
inPathToUse = null
|
||||
} else if (inPathToUse != null) {
|
||||
val file = File(inPathToUse)
|
||||
if (file.isFile)
|
||||
sizeBytesToUse = file.length()
|
||||
}
|
||||
var session: PackageInstaller.Session? = null
|
||||
var inputStream: InputStream? = null
|
||||
var out: OutputStream? = null
|
||||
try {
|
||||
session = context.packageManager.packageInstaller.openSession(sessionId)
|
||||
if (inPathToUse != null) {
|
||||
inputStream = FileInputStream(inPathToUse)
|
||||
}
|
||||
out = session.openWrite(splitName, 0, sizeBytesToUse)
|
||||
var total = 0
|
||||
val buffer = ByteArray(65536)
|
||||
var c: Int
|
||||
while (true) {
|
||||
c = inputStream!!.read(buffer)
|
||||
if (c == -1)
|
||||
break
|
||||
total += c
|
||||
out.write(buffer, 0, c)
|
||||
}
|
||||
session.fsync(out)
|
||||
Log.d(INSTALLER_TAG, "Success: streamed $total bytes")
|
||||
return PackageInstaller.STATUS_SUCCESS
|
||||
} catch (e: IOException) {
|
||||
Log.e(INSTALLER_TAG, "Error: failed to write; " + e.message)
|
||||
return PackageInstaller.STATUS_FAILURE
|
||||
} finally {
|
||||
try {
|
||||
out?.close()
|
||||
inputStream?.close()
|
||||
session?.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun doCommitSession(sessionId: Int, context: Context) {
|
||||
var session: PackageInstaller.Session? = null
|
||||
try {
|
||||
session = context.packageManager.packageInstaller.openSession(sessionId)
|
||||
val callbackIntent = Intent(context, AppInstallerService::class.java)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0)
|
||||
session.commit(pendingIntent.intentSender)
|
||||
session.close()
|
||||
Log.d(INSTALLER_TAG, "install request sent")
|
||||
Log.d(INSTALLER_TAG, "doCommitSession: " + context.packageManager.packageInstaller.mySessions)
|
||||
Log.d(INSTALLER_TAG, "doCommitSession: after session commit ")
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: Exception) {
|
||||
log(INSTALLER_TAG, e.stackTraceToString())
|
||||
sendFailure(e.stackTraceToString(), context)
|
||||
sendCloseDialog(context)
|
||||
} finally {
|
||||
session?.close()
|
||||
}
|
||||
}
|
||||
|
||||
private fun installSplitApkFiles(apkFiles: ArrayList<FileInfo>, context: Context) : Boolean {
|
||||
var sessionId: Int?
|
||||
private fun installSplitApkFilesRoot(apkFiles: List<File>?, context: Context): Boolean {
|
||||
val filenames = arrayOf("black.apk", "dark.apk", "blue.apk", "pink.apk", "hash.json")
|
||||
Log.d(INSTALLER_TAG, "installing split apk files: $apkFiles")
|
||||
run {
|
||||
val sessionIdResult = Shell.su("pm install-create -r -t").exec().out
|
||||
val sessionIdPattern = Pattern.compile("(\\d+)")
|
||||
val sessionIdMatcher = sessionIdPattern.matcher(sessionIdResult[0])
|
||||
sessionIdMatcher.find()
|
||||
sessionId = Integer.parseInt(sessionIdMatcher.group(1)!!)
|
||||
}
|
||||
apkFiles.forEach { apkFile ->
|
||||
if (!filenames.any { apkFile.name == it }) {
|
||||
Log.d(INSTALLER_TAG, "installing APK: ${apkFile.name} ${apkFile.fileSize}")
|
||||
val command = arrayOf("su", "-c", "pm", "install-write", "-S", "${apkFile.fileSize}", "$sessionId", apkFile.name)
|
||||
val process: Process = Runtime.getRuntime().exec(command)
|
||||
val inputPipe = apkFile.getInputStream()
|
||||
try {
|
||||
process.outputStream.use { outputStream -> inputPipe.copyTo(outputStream) }
|
||||
} catch (e: Exception) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
process.destroyForcibly()
|
||||
else
|
||||
process.destroy()
|
||||
log(INSTALLER_TAG, "installing split apk files: ${apkFiles?.map { it.name }}")
|
||||
val sessionId =
|
||||
Shell.su("pm install-create -r").exec().out.joinToString(" ").filter { it.isDigit() }
|
||||
.toIntOrNull()
|
||||
|
||||
sendFailure(e.stackTrace.map { it.toString() }.toMutableList(), context)
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
process.waitFor()
|
||||
if (sessionId == null) {
|
||||
sendFailure("Session ID is null", context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
|
||||
apkFiles?.filter { !filenames.contains(it.name) }?.forEach { apkFile ->
|
||||
val apkName = apkFile.name
|
||||
log(INSTALLER_TAG, "installing APK: $apkName")
|
||||
val newPath = "/data/local/tmp/$apkName"
|
||||
//Copy apk to tmp folder in order to avoid permission denials
|
||||
Shell.su("cp ${apkFile.path} $newPath").exec()
|
||||
val command = Shell.su("pm install-write $sessionId $apkName $newPath").exec()
|
||||
Shell.su("rm $newPath").exec()
|
||||
if (!command.isSuccess) {
|
||||
sendFailure(command.out, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
}
|
||||
Log.d(INSTALLER_TAG, "committing...")
|
||||
log(INSTALLER_TAG, "committing...")
|
||||
val installResult = Shell.su("pm install-commit $sessionId").exec()
|
||||
if (installResult.isSuccess) {
|
||||
return true
|
||||
if (!installResult.isSuccess) {
|
||||
sendFailure(installResult.out, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
sendFailure(installResult.out, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
private fun SimpleDateFormat.tryParse(str: String) = try {
|
||||
parse(str) != null
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
private fun getFileInfoList(splitApkPath: String): ArrayList<FileInfo> {
|
||||
val parentFile = File(splitApkPath)
|
||||
val result = ArrayList<FileInfo>()
|
||||
|
||||
if (parentFile.exists() && parentFile.canRead()) {
|
||||
val listFiles = parentFile.listFiles() ?: return ArrayList()
|
||||
listFiles.mapTo(result) {
|
||||
FileInfo(it.name, it.length(), it)
|
||||
}
|
||||
return result
|
||||
}
|
||||
val longLines = Shell.su("ls -l $splitApkPath").exec().out
|
||||
val pattern = Pattern.compile(" +")
|
||||
val formatter = SimpleDateFormat("HH:mm", Locale.getDefault())
|
||||
longLinesLoop@ for (line in longLines) {
|
||||
val matcher = pattern.matcher(line)
|
||||
for (i in 0 until 4)
|
||||
if (!matcher.find())
|
||||
continue@longLinesLoop
|
||||
val startSizeStr = matcher.end()
|
||||
matcher.find()
|
||||
val endSizeStr = matcher.start()
|
||||
val fileSizeStr = line.substring(startSizeStr, endSizeStr)
|
||||
while (true) {
|
||||
val testTimeStr: String =
|
||||
line.substring(matcher.end(), line.indexOf(' ', matcher.end()))
|
||||
if (formatter.tryParse(testTimeStr)) {
|
||||
//found time, so apk is next
|
||||
val fileName = line.substring(line.indexOf(' ', matcher.end()) + 1)
|
||||
if (fileName.endsWith("apk"))
|
||||
result.add(FileInfo(fileName, fileSizeStr.toLong(), File(splitApkPath, fileName)))
|
||||
break
|
||||
}
|
||||
matcher.find()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
//overwrite stock Vanced/Music
|
||||
private fun overwriteBase(
|
||||
apkFile: FileInfo,
|
||||
baseApkFiles: ArrayList<FileInfo>,
|
||||
apkFile: File,
|
||||
baseApkFiles: List<File>,
|
||||
versionCode: Int,
|
||||
pkg: String,
|
||||
app: String,
|
||||
|
@ -451,17 +361,15 @@ object PackageHelper {
|
|||
): Boolean {
|
||||
if (checkVersion(versionCode, baseApkFiles, pkg, context)) {
|
||||
val path = getPackageDir(context, pkg)
|
||||
apkFile.file?.let {
|
||||
val apath = it.absolutePath
|
||||
val apath = apkFile.absolutePath
|
||||
|
||||
setupFolder("$apkInstallPath/${app.capitalize(Locale.ROOT)}")
|
||||
if (path != null) {
|
||||
val apkFPath = "$apkInstallPath/${app.capitalize(Locale.ROOT)}/base.apk"
|
||||
if (moveAPK(apath, apkFPath, pkg, context)) {
|
||||
if (chConV(apkFPath, context)) {
|
||||
if (setupScript(apkFPath, path, app, pkg, context)) {
|
||||
return linkApp(apkFPath, pkg, path)
|
||||
}
|
||||
setupFolder("$apkInstallPath/${app.capitalize(Locale.ROOT)}")
|
||||
if (path != null) {
|
||||
val apkFPath = "$apkInstallPath/${app.capitalize(Locale.ROOT)}/base.apk"
|
||||
if (moveAPK(apath, apkFPath, pkg, context)) {
|
||||
if (chConV(apkFPath, context)) {
|
||||
if (setupScript(apkFPath, path, app, pkg, context)) {
|
||||
return linkApp(apkFPath, pkg, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -470,23 +378,32 @@ object PackageHelper {
|
|||
return false
|
||||
}
|
||||
|
||||
private fun setupScript(apkFPath: String, path: String, app: String, pkg: String, context: Context): Boolean
|
||||
{
|
||||
private fun setupScript(
|
||||
apkFPath: String,
|
||||
path: String,
|
||||
app: String,
|
||||
pkg: String,
|
||||
context: Context
|
||||
): Boolean {
|
||||
try {
|
||||
Log.d(INSTALLER_TAG, "Setting up script")
|
||||
log(INSTALLER_TAG, "Setting up script")
|
||||
context.writeServiceDScript(apkFPath, path, app)
|
||||
Shell.su("""echo "#!/system/bin/sh\nwhile read line; do echo \${"$"}{line} | grep $pkg | awk '{print \${'$'}2}' | xargs umount -l; done< /proc/mounts" > /data/adb/post-fs-data.d/$app.sh""").exec()
|
||||
Shell.su("""echo "#!/system/bin/sh\nwhile read line; do echo \${"$"}{line} | grep $pkg | awk '{print \${'$'}2}' | xargs umount -l; done< /proc/mounts" > /data/adb/post-fs-data.d/$app.sh""")
|
||||
.exec()
|
||||
return Shell.su("chmod 744 /data/adb/service.d/$app.sh").exec().isSuccess
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
sendFailure(e.stackTraceToString(), context)
|
||||
sendCloseDialog(context)
|
||||
log(INSTALLER_TAG, e.stackTraceToString())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun linkApp(apkFPath: String, pkg: String, path: String): Boolean {
|
||||
Log.d(INSTALLER_TAG, "Linking app")
|
||||
log(INSTALLER_TAG, "Linking app")
|
||||
Shell.su("am force-stop $pkg").exec()
|
||||
Shell.su("""for i in ${'$'}(ls /data/app/ | grep $pkg | tr " "); do umount -l "/data/app/${"$"}i/base.apk"; done """).exec()
|
||||
Shell.su("""for i in ${'$'}(ls /data/app/ | grep $pkg | tr " "); do umount -l "/data/app/${"$"}i/base.apk"; done """)
|
||||
.exec()
|
||||
val response = Shell.su("""su -mm -c "mount -o bind $apkFPath $path"""").exec()
|
||||
Thread.sleep(500)
|
||||
Shell.su("am force-stop $pkg").exec()
|
||||
|
@ -498,12 +415,17 @@ object PackageHelper {
|
|||
}
|
||||
|
||||
//check version and perform action based on result
|
||||
private fun checkVersion(versionCode: Int, baseApkFiles: ArrayList<FileInfo>, pkg: String, context: Context): Boolean {
|
||||
Log.d(INSTALLER_TAG, "Checking stock version")
|
||||
private fun checkVersion(
|
||||
versionCode: Int,
|
||||
baseApkFiles: List<File>,
|
||||
pkg: String,
|
||||
context: Context
|
||||
): Boolean {
|
||||
log(INSTALLER_TAG, "Checking stock version")
|
||||
val path = getPackageDir(context, pkg)
|
||||
if (path != null) {
|
||||
if (path.contains("/data/app/")) {
|
||||
when (getVersionNumber(pkg, context)?.let { compareVersion(it,versionCode) } ) {
|
||||
when (getVersionNumber(pkg, context)?.let { compareVersion(it, versionCode) }) {
|
||||
1 -> return fixHigherVer(baseApkFiles, pkg, context)
|
||||
-1 -> return installStock(baseApkFiles, pkg, context)
|
||||
}
|
||||
|
@ -517,8 +439,8 @@ object PackageHelper {
|
|||
private fun getPkgInfo(pkg: String, context: Context): PackageInfo? {
|
||||
return try {
|
||||
context.packageManager.getPackageInfo(pkg, 0)
|
||||
} catch (e:Exception) {
|
||||
Log.d(INSTALLER_TAG, "Unable to get package info")
|
||||
} catch (e: Exception) {
|
||||
log(INSTALLER_TAG, "Unable to get package info")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
@ -532,10 +454,13 @@ object PackageHelper {
|
|||
}
|
||||
|
||||
//uninstall current update and install base that works with patch
|
||||
private fun fixHigherVer(apkFiles: ArrayList<FileInfo>, pkg: String, context: Context) : Boolean {
|
||||
Log.d(INSTALLER_TAG, "Downgrading stock")
|
||||
private fun fixHigherVer(apkFiles: List<File>, pkg: String, context: Context): Boolean {
|
||||
log(INSTALLER_TAG, "Downgrading stock")
|
||||
if (uninstallRootApk(pkg)) {
|
||||
return if (pkg == vancedRootPkg) installSplitApkFiles(apkFiles, context) else installRootMusic(apkFiles, context)
|
||||
return if (pkg == vancedRootPkg) installSplitApkFilesRoot(
|
||||
apkFiles,
|
||||
context
|
||||
) else installRootMusic(apkFiles, context)
|
||||
}
|
||||
sendFailure(listOf("Failed_Uninstall").toMutableList(), context)
|
||||
sendCloseDialog(context)
|
||||
|
@ -543,78 +468,70 @@ object PackageHelper {
|
|||
}
|
||||
|
||||
//install stock youtube matching vanced version
|
||||
private fun installStock(baseApkFiles: ArrayList<FileInfo>, pkg: String, context: Context): Boolean {
|
||||
Log.d(INSTALLER_TAG, "Installing stock")
|
||||
return if (pkg == vancedRootPkg) installSplitApkFiles(baseApkFiles, context) else installRootMusic(baseApkFiles, context)
|
||||
private fun installStock(baseApkFiles: List<File>, pkg: String, context: Context): Boolean {
|
||||
log(INSTALLER_TAG, "Installing stock")
|
||||
return if (pkg == vancedRootPkg) installSplitApkFilesRoot(
|
||||
baseApkFiles,
|
||||
context
|
||||
) else installRootMusic(baseApkFiles, context)
|
||||
}
|
||||
|
||||
private fun isMagiskInstalled() = Shell.su("magisk -c").exec().isSuccess
|
||||
|
||||
//set chcon to apk_data_file
|
||||
private fun chConV(apkFPath: String, context: Context): Boolean {
|
||||
Log.d(INSTALLER_TAG, "Running chcon")
|
||||
log(INSTALLER_TAG, "Running chcon")
|
||||
val response = Shell.su("chcon u:object_r:apk_data_file:s0 $apkFPath").exec()
|
||||
//val response = Shell.su("chcon -R u:object_r:system_file:s0 $path").exec()
|
||||
return if (response.isSuccess) {
|
||||
true
|
||||
} else {
|
||||
if (!response.isSuccess) {
|
||||
sendFailure(response.out, context)
|
||||
sendCloseDialog(context)
|
||||
false
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//move patch to data/app
|
||||
private fun moveAPK(apkFile: String, path: String, pkg: String, context: Context) : Boolean {
|
||||
Log.d(INSTALLER_TAG, "Moving app")
|
||||
val apkinF = SuFile.open(apkFile)
|
||||
val apkoutF = SuFile.open(path)
|
||||
private fun moveAPK(apkFile: String, path: String, pkg: String, context: Context): Boolean {
|
||||
log(INSTALLER_TAG, "Moving app")
|
||||
Shell.su("am force-stop $pkg").exec()
|
||||
|
||||
if(apkinF.exists()) {
|
||||
try {
|
||||
Shell.su("am force-stop $pkg").exec()
|
||||
|
||||
//Shell.su("rm -r SuFile.open(path).parent")
|
||||
|
||||
copy(apkinF,apkoutF)
|
||||
Shell.su("chmod 644 $path").exec().isSuccess
|
||||
return if(Shell.su("chown system:system $path").exec().isSuccess) {
|
||||
true
|
||||
} else {
|
||||
sendFailure(listOf("Chown_Fail").toMutableList(), context)
|
||||
sendCloseDialog(context)
|
||||
false
|
||||
}
|
||||
|
||||
}
|
||||
catch (e: IOException)
|
||||
{
|
||||
sendFailure(listOf("${e.message}").toMutableList(), context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
val mv = Shell.su("cp $apkFile $path").exec()
|
||||
if (!mv.isSuccess) {
|
||||
sendFailure(mv.out.apply { add(0, "MV_Fail") }, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
sendFailure(listOf("IFile_Missing").toMutableList(), context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
|
||||
val chmod = Shell.su("chmod 644 $path").exec()
|
||||
if (!chmod.isSuccess) {
|
||||
sendFailure(chmod.out.apply { add(0, "Chmod_Fail") }, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun copy(src: File, dst: File) {
|
||||
val cmd = Shell.su("mv ${src.absolutePath} ${dst.absolutePath}").exec().isSuccess
|
||||
Log.d("ZLog", cmd.toString())
|
||||
val chown = Shell.su("chown system:system $path").exec()
|
||||
if (!chown.isSuccess) {
|
||||
sendFailure(chown.out.apply { add(0, "Chown_Fail") }, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun getVersionNumber(pkg: String, context: Context): Int? {
|
||||
try {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
context.packageManager.getPackageInfo(vancedRootPkg, 0).longVersionCode.and(0xFFFFFFFF).toInt()
|
||||
context.packageManager.getPackageInfo(vancedRootPkg, 0).longVersionCode.and(
|
||||
0xFFFFFFFF
|
||||
).toInt()
|
||||
else
|
||||
context.packageManager.getPackageInfo(vancedRootPkg, 0).versionCode
|
||||
}
|
||||
catch (e : Exception) {
|
||||
} catch (e: Exception) {
|
||||
val execRes = Shell.su("dumpsys package $pkg | grep versionCode").exec()
|
||||
if(execRes.isSuccess) {
|
||||
if (execRes.isSuccess) {
|
||||
val result = execRes.out
|
||||
var version = 0
|
||||
result
|
||||
|
@ -630,22 +547,16 @@ object PackageHelper {
|
|||
}
|
||||
|
||||
//get path of the installed youtube
|
||||
fun getPackageDir(context: Context, pkg: String): String?
|
||||
{
|
||||
fun getPackageDir(context: Context, pkg: String): String? {
|
||||
val p = getPkgInfo(pkg, context)
|
||||
return if(p != null)
|
||||
{
|
||||
return if (p != null) {
|
||||
p.applicationInfo.sourceDir
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
val execRes = Shell.su("dumpsys package $pkg | grep codePath").exec()
|
||||
if(execRes.isSuccess)
|
||||
{
|
||||
if (execRes.isSuccess) {
|
||||
val result = execRes.out
|
||||
for (line in result)
|
||||
{
|
||||
if(line.contains("data/app")) "${line.substringAfter("=")}/base.apk"
|
||||
for (line in result) {
|
||||
if (line.contains("data/app")) "${line.substringAfter("=")}/base.apk"
|
||||
}
|
||||
}
|
||||
null
|
||||
|
@ -655,16 +566,17 @@ object PackageHelper {
|
|||
private fun setInstallerPackage(context: Context, target: String, installer: String) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return
|
||||
try {
|
||||
Log.d(INSTALLER_TAG, "Setting installer package to $installer for $target")
|
||||
log(INSTALLER_TAG, "Setting installer package to $installer for $target")
|
||||
val installerUid = context.packageManager.getPackageUid(installer, 0)
|
||||
val res = Shell.su("""su $installerUid -c 'pm set-installer $target $installer'""").exec()
|
||||
val res =
|
||||
Shell.su("""su $installerUid -c 'pm set-installer $target $installer'""").exec()
|
||||
if (res.out.any { line -> line.contains("Success") }) {
|
||||
Log.d(INSTALLER_TAG, "Installer package successfully set")
|
||||
log(INSTALLER_TAG, "Installer package successfully set")
|
||||
return
|
||||
}
|
||||
Log.d(INSTALLER_TAG, "Failed setting installer package")
|
||||
log(INSTALLER_TAG, "Failed setting installer package")
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
Log.d(INSTALLER_TAG, "Installer package $installer not found. Skipping setting installer")
|
||||
log(INSTALLER_TAG, "Installer package $installer not found. Skipping setting installer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,15 @@ import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
|||
|
||||
val Context.defPrefs: SharedPreferences get() = getDefaultSharedPreferences(this)
|
||||
|
||||
var SharedPreferences.managerTheme
|
||||
var SharedPreferences.managerTheme
|
||||
get() = getString("manager_theme", "System Default")
|
||||
set(value) = edit { putString("manager_theme", value) }
|
||||
|
||||
var SharedPreferences.managerAccent
|
||||
var SharedPreferences.managerAccent
|
||||
get() = getInt("manager_accent_color", defAccentColor)
|
||||
set(value) = edit { putInt("manager_accent_color", value) }
|
||||
|
||||
var SharedPreferences.managerVariant
|
||||
var SharedPreferences.managerVariant
|
||||
get() = getString("vanced_variant", "nonroot")
|
||||
set(value) = edit { putString("vanced_variant", value) }
|
||||
|
||||
|
@ -23,11 +23,11 @@ var SharedPreferences.managerLang
|
|||
get() = getString("manager_lang", "System Default")
|
||||
set(value) = edit { putString("manager_lang", value) }
|
||||
|
||||
var SharedPreferences.installUrl
|
||||
var SharedPreferences.installUrl
|
||||
get() = getString("install_url", baseUrl)
|
||||
set(value) = edit { putString("install_url", value) }
|
||||
|
||||
var SharedPreferences.vancedVersion
|
||||
var SharedPreferences.vancedVersion
|
||||
get() = getString("vanced_version", "latest")
|
||||
set(value) = edit { putString("vanced_version", value) }
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue