mirror of
https://github.com/topjohnwu/Magisk
synced 2025-11-06 21:02:30 +01:00
Compare commits
477 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b34c477d5e | ||
|
|
28611304f7 | ||
|
|
76af9e6e1f | ||
|
|
7b3b965ed7 | ||
|
|
567b905ef1 | ||
|
|
a94268329c | ||
|
|
a11a18686a | ||
|
|
c58e3a99ee | ||
|
|
b166663e89 | ||
|
|
ac13ac14f6 | ||
|
|
06531f6d06 | ||
|
|
f6274d94f6 | ||
|
|
2b303a7e23 | ||
|
|
2bb074a5ad | ||
|
|
3b2db56243 | ||
|
|
45483fde74 | ||
|
|
d742cfa48f | ||
|
|
95353ce9eb | ||
|
|
ab2cc72814 | ||
|
|
5c54a2c008 | ||
|
|
2fe3082518 | ||
|
|
5a889d28c8 | ||
|
|
45e7c1c030 | ||
|
|
c6dcff0ae7 | ||
|
|
b791dc5e1a | ||
|
|
46db281006 | ||
|
|
636479b15b | ||
|
|
dcbb4eabb5 | ||
|
|
068cedaa84 | ||
|
|
02dd962601 | ||
|
|
256d715648 | ||
|
|
cbe97cdfde | ||
|
|
407dfc7547 | ||
|
|
a8e4e077ec | ||
|
|
3d06ba1878 | ||
|
|
8a23d1da58 | ||
|
|
d3eb61e0e4 | ||
|
|
7cdf2d244d | ||
|
|
c59a41a607 | ||
|
|
e0410b6f10 | ||
|
|
8eac6c0b48 | ||
|
|
bf8b74e996 | ||
|
|
691e41e22e | ||
|
|
15e91d42ee | ||
|
|
5e8e94fd0f | ||
|
|
5313a46aa2 | ||
|
|
761a8dde65 | ||
|
|
a73acfb9c2 | ||
|
|
fbe17dde03 | ||
|
|
a01a3404fe | ||
|
|
454e5dfc5d | ||
|
|
47545b45b8 | ||
|
|
7c9908d953 | ||
|
|
5f4cd50cc4 | ||
|
|
b0fba6ce5b | ||
|
|
1f5992f2c2 | ||
|
|
abfd3c3e5d | ||
|
|
97da7f9691 | ||
|
|
2752083d29 | ||
|
|
c826318da4 | ||
|
|
6582a4abd9 | ||
|
|
a699dab5b3 | ||
|
|
21c8ad5b9e | ||
|
|
195d885887 | ||
|
|
519bd2f30f | ||
|
|
20ef724fad | ||
|
|
f443cbaa2b | ||
|
|
dbf45da8ab | ||
|
|
6b67902d53 | ||
|
|
0ad0ef485c | ||
|
|
7dfe3e53d5 | ||
|
|
5be3bd1e64 | ||
|
|
bc0c1980db | ||
|
|
2997258fd0 | ||
|
|
11600fc116 | ||
|
|
a8640f52ef | ||
|
|
0f4e44c38f | ||
|
|
053f4d481d | ||
|
|
f466c27da9 | ||
|
|
bfe6bc3095 | ||
|
|
ff8f3e766e | ||
|
|
6635ea3e29 | ||
|
|
591788c0df | ||
|
|
571b8986a4 | ||
|
|
bb7a74e4b4 | ||
|
|
76ddfeb93a | ||
|
|
c38b826abf | ||
|
|
21d7db0959 | ||
|
|
d7b51d2807 | ||
|
|
a7af8b5722 | ||
|
|
9c93fe6003 | ||
|
|
21505a7470 | ||
|
|
ba6e6cc15a | ||
|
|
fd7bf2bc3a | ||
|
|
b2cd24ed1b | ||
|
|
66cf2c984a | ||
|
|
de1b2b19b0 | ||
|
|
e31583485d | ||
|
|
490e51c1d7 | ||
|
|
1df2a04713 | ||
|
|
42804d5314 | ||
|
|
558710bbdd | ||
|
|
f4926cb822 | ||
|
|
1e77e0862a | ||
|
|
8c696cb8ca | ||
|
|
62ef8ade8f | ||
|
|
3d88dd3123 | ||
|
|
880b348ce6 | ||
|
|
31fe3a1cd8 | ||
|
|
19182ffddf | ||
|
|
afcc60066e | ||
|
|
d3ade06421 | ||
|
|
f1a3ef9590 | ||
|
|
d1d73f11a5 | ||
|
|
05697372f8 | ||
|
|
0c1f68816e | ||
|
|
92546e8a74 | ||
|
|
a4faa3f392 | ||
|
|
df191cd2b5 | ||
|
|
baa19f0ccf | ||
|
|
5a49bd3ac9 | ||
|
|
b37d7e0500 | ||
|
|
f4ed6274a4 | ||
|
|
56eb1a1cf9 | ||
|
|
a7c156a9e3 | ||
|
|
d81ca77231 | ||
|
|
bf013f6ebb | ||
|
|
dd8116e285 | ||
|
|
b5d80a88d1 | ||
|
|
7f4f95cf83 | ||
|
|
87c2f6ad14 | ||
|
|
ad47dba064 | ||
|
|
41b701846f | ||
|
|
5c42830328 | ||
|
|
69617309f8 | ||
|
|
48e2d6a8da | ||
|
|
b4120cddfb | ||
|
|
54e3f1998a | ||
|
|
edcf9f1b0c | ||
|
|
de3747d65e | ||
|
|
b76a3614da | ||
|
|
94cc64c51b | ||
|
|
0f71edee96 | ||
|
|
e097c097fe | ||
|
|
1443a5b175 | ||
|
|
2d82ad93dd | ||
|
|
384c257a74 | ||
|
|
49dfa2c3a0 | ||
|
|
7bd3e768db | ||
|
|
65224ed22b | ||
|
|
0a28dfe1e2 | ||
|
|
1c8ebfacb0 | ||
|
|
5d6d241791 | ||
|
|
4f116d15b9 | ||
|
|
228570640e | ||
|
|
65a79610aa | ||
|
|
24984ea4f2 | ||
|
|
048b2af0fc | ||
|
|
449989ddd9 | ||
|
|
01ebe5724a | ||
|
|
95fb230b8c | ||
|
|
632971af15 | ||
|
|
5787aa1078 | ||
|
|
d8b9265484 | ||
|
|
9ea3169ca9 | ||
|
|
aebf2672cd | ||
|
|
68ac409bfd | ||
|
|
fef44bd24f | ||
|
|
e4a7617dde | ||
|
|
4dfb193d10 | ||
|
|
c248d94995 | ||
|
|
d4ac458d17 | ||
|
|
93e443c4ad | ||
|
|
4b3988cef9 | ||
|
|
4eb5ee17b4 | ||
|
|
e1b63d7dec | ||
|
|
4b5651bd6f | ||
|
|
50515d9128 | ||
|
|
28b5faab0c | ||
|
|
82a01c22d3 | ||
|
|
be9b0c2e8f | ||
|
|
b6affe06a5 | ||
|
|
1e05f8c646 | ||
|
|
7e9d4512b6 | ||
|
|
5fa127c415 | ||
|
|
ac26681fe7 | ||
|
|
3c62636133 | ||
|
|
ca874fa12c | ||
|
|
c3508bbb99 | ||
|
|
6935033db5 | ||
|
|
421277d730 | ||
|
|
56988944b5 | ||
|
|
528601d25a | ||
|
|
ddd153c00d | ||
|
|
b8c1588284 | ||
|
|
4dac9e40bd | ||
|
|
def1811d48 | ||
|
|
c53e507713 | ||
|
|
e0ea777249 | ||
|
|
4c1962f3c7 | ||
|
|
258e89c964 | ||
|
|
3d3bfb42e5 | ||
|
|
6dbd8baa7e | ||
|
|
e660fabc57 | ||
|
|
2115bcd8b0 | ||
|
|
1bdd6e1a9d | ||
|
|
98deec232b | ||
|
|
022c217cfe | ||
|
|
81f57949ed | ||
|
|
fca5eb083f | ||
|
|
a3695cc66b | ||
|
|
6723d20616 | ||
|
|
627ec91687 | ||
|
|
9126cf0c73 | ||
|
|
16322ab30c | ||
|
|
5682917356 | ||
|
|
c91ccc8b4e | ||
|
|
63f670fc36 | ||
|
|
e20b07fa24 | ||
|
|
472656517f | ||
|
|
d232cba02d | ||
|
|
e49d29a914 | ||
|
|
3aa1a68cdc | ||
|
|
f94452083f | ||
|
|
ce1ee5cb9d | ||
|
|
48df6b8485 | ||
|
|
ae23ae2d37 | ||
|
|
e34e04af04 | ||
|
|
ff3f377911 | ||
|
|
18065826b9 | ||
|
|
84e19ceef0 | ||
|
|
59161efd08 | ||
|
|
6663fd3526 | ||
|
|
2c44e1bb93 | ||
|
|
e3f6399473 | ||
|
|
89c2c21774 | ||
|
|
2954eb4bdc | ||
|
|
e08de91666 | ||
|
|
a170acb9d7 | ||
|
|
6a086bb222 | ||
|
|
b2f152e641 | ||
|
|
6c5b261804 | ||
|
|
8bd0c44e83 | ||
|
|
34c36984e9 | ||
|
|
8bd6aca0dd | ||
|
|
983b74be77 | ||
|
|
a3eafdd2c6 | ||
|
|
ea75a09f95 | ||
|
|
4c747c4148 | ||
|
|
49abfcafed | ||
|
|
50710c72ad | ||
|
|
2e299b3814 | ||
|
|
43d11d877d | ||
|
|
d7e7df3bd9 | ||
|
|
8d8ba11221 | ||
|
|
2536a18c00 | ||
|
|
11728b2b15 | ||
|
|
627501b9ba | ||
|
|
3599384b38 | ||
|
|
4b307cad2c | ||
|
|
7496d51580 | ||
|
|
4194ac894c | ||
|
|
ffb5d9ea9c | ||
|
|
770b28ca30 | ||
|
|
62e464f706 | ||
|
|
8d0dc37ec0 | ||
|
|
fe41df87bb | ||
|
|
8276a0775d | ||
|
|
abfb3bb3bb | ||
|
|
e184eb4a23 | ||
|
|
d0fc372ecd | ||
|
|
6f54c57647 | ||
|
|
e8ae103d5f | ||
|
|
b0198dab6c | ||
|
|
b75ec09998 | ||
|
|
c8ac6c07b0 | ||
|
|
27814e3015 | ||
|
|
f59309a445 | ||
|
|
b0292d7319 | ||
|
|
7f18616cc0 | ||
|
|
2fef98a5af | ||
|
|
36765caedc | ||
|
|
f7aed10ea2 | ||
|
|
410bbb8285 | ||
|
|
f56ea52932 | ||
|
|
cb4361b7b7 | ||
|
|
ecd332c573 | ||
|
|
a0fe78a728 | ||
|
|
49cc9c529e | ||
|
|
7635b2c33f | ||
|
|
50c26d33ab | ||
|
|
f642fb3b99 | ||
|
|
e68dd866a3 | ||
|
|
73d36fdff0 | ||
|
|
5561cd3c77 | ||
|
|
32a9acb913 | ||
|
|
f7f23c6e77 | ||
|
|
3d4edbd9dc | ||
|
|
bdf385f374 | ||
|
|
9f78c3e64b | ||
|
|
f370052815 | ||
|
|
9df4b10067 | ||
|
|
d20517483e | ||
|
|
713ce4719b | ||
|
|
f3d39e7515 | ||
|
|
61783ffc82 | ||
|
|
05c4ad01d5 | ||
|
|
12647dcf30 | ||
|
|
da38f59e62 | ||
|
|
cf4ef54dc5 | ||
|
|
12e9873514 | ||
|
|
f7c0e407ca | ||
|
|
82c7662cdf | ||
|
|
4f0bced53e | ||
|
|
f1b6c9f4aa | ||
|
|
0ab31ab0df | ||
|
|
46e8f0779f | ||
|
|
3fb72a4d20 | ||
|
|
db20f65d7c | ||
|
|
63cfe7b47b | ||
|
|
db590091b3 | ||
|
|
7b25e74418 | ||
|
|
82f303e1c6 | ||
|
|
c038683b54 | ||
|
|
3a37ed6b60 | ||
|
|
706a492218 | ||
|
|
c0be5383de | ||
|
|
3b8ce85092 | ||
|
|
b6298f8602 | ||
|
|
abfec57972 | ||
|
|
470fc97d1f | ||
|
|
8d59caf635 | ||
|
|
acf25aa4d3 | ||
|
|
16de4674ec | ||
|
|
65b0ea792e | ||
|
|
fc6b02f607 | ||
|
|
136d8c39d9 | ||
|
|
24a8b41182 | ||
|
|
810cf4dee8 | ||
|
|
9bf835e810 | ||
|
|
eca37bce38 | ||
|
|
3ee6a2baf2 | ||
|
|
69fa7f238d | ||
|
|
de2306bd12 | ||
|
|
714feeb9a7 | ||
|
|
ca99808fd2 | ||
|
|
f8f8c28fec | ||
|
|
f497867ba5 | ||
|
|
383192784d | ||
|
|
605189bc6e | ||
|
|
c0a2e3674c | ||
|
|
76f0602684 | ||
|
|
477ff12cde | ||
|
|
9c09ad3b62 | ||
|
|
a967afc629 | ||
|
|
dcc1fd3ee4 | ||
|
|
933f020b3c | ||
|
|
f5c02be5bf | ||
|
|
68fbdd474c | ||
|
|
2cbc048352 | ||
|
|
e990ffd4a0 | ||
|
|
743c7c9326 | ||
|
|
067248da75 | ||
|
|
f5c982355a | ||
|
|
f98c68a280 | ||
|
|
773bf0c6bc | ||
|
|
080ab6032c | ||
|
|
350144df29 | ||
|
|
9ac0f11d9a | ||
|
|
8079d456ab | ||
|
|
acf166cf9d | ||
|
|
439d497a13 | ||
|
|
0580932610 | ||
|
|
85399f609c | ||
|
|
4bcfee397b | ||
|
|
34bcb1dd26 | ||
|
|
117d1ed080 | ||
|
|
f324252681 | ||
|
|
0dad06cdfe | ||
|
|
9396288ca2 | ||
|
|
f89f08833e | ||
|
|
79e8962854 | ||
|
|
34e5a7cd24 | ||
|
|
7343c195b7 | ||
|
|
0af041b54e | ||
|
|
92a8a3e91f | ||
|
|
f41575d8b0 | ||
|
|
d93c4a5103 | ||
|
|
6fe9b69aad | ||
|
|
5d162f81c4 | ||
|
|
4771c2810b | ||
|
|
0cd99712fa | ||
|
|
b591af7803 | ||
|
|
171d68ca72 | ||
|
|
bade4f2c6a | ||
|
|
5754782a4e | ||
|
|
decdd54c19 | ||
|
|
ffe47300a1 | ||
|
|
6f9c3c4ff3 | ||
|
|
9b3efffba9 | ||
|
|
003fea52b1 | ||
|
|
2b17c77195 | ||
|
|
c252a50fd7 | ||
|
|
cf8f042a20 | ||
|
|
844bc2d808 | ||
|
|
27f7fa7153 | ||
|
|
b325aa4555 | ||
|
|
c2c3bf0ba4 | ||
|
|
0d977b54f7 | ||
|
|
20860da4b4 | ||
|
|
3ea10b7cf9 | ||
|
|
1ec33863bc | ||
|
|
a260e99090 | ||
|
|
25efdd3d6f | ||
|
|
00a1e18959 | ||
|
|
c59f8adc4a | ||
|
|
1eb83ad812 | ||
|
|
7717f0a6b0 | ||
|
|
5e1fba3603 | ||
|
|
66cc9bc545 | ||
|
|
12aa5838d9 | ||
|
|
4f73534837 | ||
|
|
c4d145835c | ||
|
|
f822ca5b23 | ||
|
|
8aaa45c62a | ||
|
|
2f4f257070 | ||
|
|
97c1e181c5 | ||
|
|
ea80cddd57 | ||
|
|
09a294c219 | ||
|
|
408399eae0 | ||
|
|
391852a102 | ||
|
|
5b37de8fe5 | ||
|
|
7df23ceb74 | ||
|
|
6099f3b015 | ||
|
|
a5cc31783c | ||
|
|
6b34ec3ab9 | ||
|
|
5c333dec33 | ||
|
|
775d095b3c | ||
|
|
7679b5d516 | ||
|
|
7702094053 | ||
|
|
3798d50457 | ||
|
|
95e1e57407 | ||
|
|
93ba4cca68 | ||
|
|
fe4981da21 | ||
|
|
e4f94c4c52 | ||
|
|
708fe514f8 | ||
|
|
11c882380f | ||
|
|
fb93af665d | ||
|
|
0db405f2cc | ||
|
|
fb8000b58b | ||
|
|
1b9d8e068a | ||
|
|
038f73a5f7 | ||
|
|
649b49ff45 | ||
|
|
1418bc454d | ||
|
|
29cc372bfa | ||
|
|
69b00d3782 | ||
|
|
a328e2bf3c | ||
|
|
4c1ea0e421 | ||
|
|
7e01f9c95e | ||
|
|
8b28baabd7 | ||
|
|
f49966d86e | ||
|
|
f4ac7c8e7c | ||
|
|
2b65e1ffc2 | ||
|
|
c81a3fa286 | ||
|
|
44f005077d | ||
|
|
013b6e68ec | ||
|
|
95c964673d | ||
|
|
94ec11db58 | ||
|
|
c4e8dda37c | ||
|
|
e136fb3a4f | ||
|
|
01b985eded | ||
|
|
1f0a35f073 | ||
|
|
2b9b019093 | ||
|
|
10186a9e3d | ||
|
|
89d8fea7d2 | ||
|
|
f623b98858 | ||
|
|
632cee1613 |
19
.github/ccache.sh
vendored
Normal file
19
.github/ccache.sh
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
OS=$(uname)
|
||||||
|
CCACHE_VER=4.4
|
||||||
|
|
||||||
|
case $OS in
|
||||||
|
Darwin )
|
||||||
|
brew install ccache
|
||||||
|
ln -s $(which ccache) ./ccache
|
||||||
|
;;
|
||||||
|
Linux )
|
||||||
|
sudo apt-get install -y ccache
|
||||||
|
ln -s $(which ccache) ./ccache
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
curl -OL https://github.com/ccache/ccache/releases/download/v${CCACHE_VER}/ccache-${CCACHE_VER}-windows-64.zip
|
||||||
|
unzip -j ccache-*-windows-64.zip '*/ccache.exe'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
mkdir ./.ccache
|
||||||
|
./ccache -o compiler_check='%compiler% -dumpmachine; %compiler% -dumpversion'
|
||||||
49
.github/workflows/build.yml
vendored
49
.github/workflows/build.yml
vendored
@@ -10,6 +10,7 @@ on:
|
|||||||
- 'buildSrc/**'
|
- 'buildSrc/**'
|
||||||
- 'build.py'
|
- 'build.py'
|
||||||
- 'gradle.properties'
|
- 'gradle.properties'
|
||||||
|
- '.github/workflows/build.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -21,7 +22,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-latest, windows-latest, macOS-latest ]
|
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||||
|
env:
|
||||||
|
NDK_CCACHE: ${{ github.workspace }}/ccache
|
||||||
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out
|
- name: Check out
|
||||||
@@ -40,46 +44,43 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
|
|
||||||
- name: Set up GitHub env (Windows)
|
- name: Set up ccache
|
||||||
if: runner.os == 'Windows'
|
run: bash .github/ccache.sh
|
||||||
run: |
|
|
||||||
$ndk_ver = Select-String -Path "gradle.properties" -Pattern "^magisk.fullNdkVersion=" | % { $_ -replace ".*=" }
|
|
||||||
echo "ANDROID_SDK_ROOT=$env:ANDROID_SDK_ROOT" >> $env:GITHUB_ENV
|
|
||||||
echo "MAGISK_NDK_VERSION=$ndk_ver" >> $env:GITHUB_ENV
|
|
||||||
echo "GRADLE_OPTS=-Dorg.gradle.daemon=false" >> $env:GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set up GitHub env (Unix)
|
- name: Cache Gradle dependencies
|
||||||
if: runner.os != 'Windows'
|
|
||||||
run: |
|
|
||||||
ndk_ver=$(sed -n 's/^magisk.fullNdkVersion=//p' gradle.properties)
|
|
||||||
echo ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT >> $GITHUB_ENV
|
|
||||||
echo MAGISK_NDK_VERSION=$ndk_ver >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Cache Gradle
|
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
~/.gradle/wrapper
|
~/.gradle/wrapper
|
||||||
|
!~/.gradle/caches/build-cache-*
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||||
restore-keys: ${{ runner.os }}-gradle-
|
restore-keys: ${{ runner.os }}-gradle-
|
||||||
|
|
||||||
- name: Cache NDK
|
- name: Cache build cache
|
||||||
id: ndk-cache
|
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ${{ env.ANDROID_SDK_ROOT }}/ndk/magisk
|
path: |
|
||||||
key: ${{ runner.os }}-ndk-${{ env.MAGISK_NDK_VERSION }}
|
${{ github.workspace }}/.ccache
|
||||||
|
~/.gradle/caches/build-cache-*
|
||||||
|
key: ${{ runner.os }}-build-cache-${{ github.sha }}
|
||||||
|
restore-keys: ${{ runner.os }}-build-cache-
|
||||||
|
|
||||||
- name: Set up NDK
|
- name: Set up NDK
|
||||||
if: steps.ndk-cache.outputs.cache-hit != 'true'
|
run: python build.py -v ndk
|
||||||
run: python build.py ndk
|
|
||||||
|
|
||||||
- name: Build release
|
- name: Build release
|
||||||
run: python build.py -vr all
|
run: |
|
||||||
|
./ccache -zp
|
||||||
|
python build.py -vr all
|
||||||
|
|
||||||
- name: Build debug
|
- name: Build debug
|
||||||
run: python build.py -v all
|
run: |
|
||||||
|
python build.py -v all
|
||||||
|
./ccache -s
|
||||||
|
|
||||||
|
- name: Stop gradle daemon
|
||||||
|
run: ./gradlew --stop
|
||||||
|
|
||||||
# Only upload artifacts built on Linux
|
# Only upload artifacts built on Linux
|
||||||
- name: Upload build artifact
|
- name: Upload build artifact
|
||||||
|
|||||||
26
.github/workflows/issues.yml
vendored
26
.github/workflows/issues.yml
vendored
@@ -1,26 +0,0 @@
|
|||||||
name: Check Issues
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [opened]
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Read latest version code
|
|
||||||
run: |
|
|
||||||
ver=$(sed -n 's/^magisk.versionCode=//p' gradle.properties)
|
|
||||||
echo MAGISK_VERSION_CODE=$ver >> $GITHUB_ENV
|
|
||||||
- if: contains(github.event.issue.body, format('Magisk version code{0} ', ':')) != true
|
|
||||||
id: close
|
|
||||||
name: Close Issue(template)
|
|
||||||
uses: peter-evans/close-issue@v1
|
|
||||||
with:
|
|
||||||
comment: This issue is being automatically closed because it does not follow the issue template.
|
|
||||||
- if: steps.close.conclusion == 'skipped' && contains(github.event.issue.body, format('Magisk version code{0} {1}', ':', env.MAGISK_VERSION_CODE)) != true
|
|
||||||
name: Close Issue(latest canary)
|
|
||||||
uses: peter-evans/close-issue@v1
|
|
||||||
with:
|
|
||||||
comment: This issue is being automatically closed because latest canary Magisk version code is ${{ env.MAGISK_VERSION_CODE }}.
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ out
|
|||||||
*.apk
|
*.apk
|
||||||
/config.prop
|
/config.prop
|
||||||
/update.sh
|
/update.sh
|
||||||
|
/dict.txt
|
||||||
|
|
||||||
# Built binaries
|
# Built binaries
|
||||||
native/out
|
native/out
|
||||||
|
|||||||
11
.gitmodules
vendored
11
.gitmodules
vendored
@@ -6,7 +6,7 @@
|
|||||||
url = https://github.com/topjohnwu/ndk-busybox.git
|
url = https://github.com/topjohnwu/ndk-busybox.git
|
||||||
[submodule "dtc"]
|
[submodule "dtc"]
|
||||||
path = native/jni/external/dtc
|
path = native/jni/external/dtc
|
||||||
url = https://github.com/dgibson/dtc
|
url = https://github.com/dgibson/dtc.git
|
||||||
[submodule "lz4"]
|
[submodule "lz4"]
|
||||||
path = native/jni/external/lz4
|
path = native/jni/external/lz4
|
||||||
url = https://github.com/lz4/lz4.git
|
url = https://github.com/lz4/lz4.git
|
||||||
@@ -31,6 +31,15 @@
|
|||||||
[submodule "libcxx"]
|
[submodule "libcxx"]
|
||||||
path = native/jni/external/libcxx
|
path = native/jni/external/libcxx
|
||||||
url = https://github.com/topjohnwu/libcxx.git
|
url = https://github.com/topjohnwu/libcxx.git
|
||||||
|
[submodule "zlib"]
|
||||||
|
path = native/jni/external/zlib
|
||||||
|
url = https://android.googlesource.com/platform/external/zlib
|
||||||
|
[submodule "parallel-hashmap"]
|
||||||
|
path = native/jni/external/parallel-hashmap
|
||||||
|
url = https://github.com/greg7mdp/parallel-hashmap.git
|
||||||
[submodule "termux-elf-cleaner"]
|
[submodule "termux-elf-cleaner"]
|
||||||
path = tools/termux-elf-cleaner
|
path = tools/termux-elf-cleaner
|
||||||
url = https://github.com/termux/termux-elf-cleaner.git
|
url = https://github.com/termux/termux-elf-cleaner.git
|
||||||
|
[submodule "zopfli"]
|
||||||
|
path = native/jni/external/zopfli
|
||||||
|
url = https://github.com/google/zopfli.git
|
||||||
|
|||||||
26
README.MD
26
README.MD
@@ -2,22 +2,23 @@
|
|||||||
|
|
||||||
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/count/count.json)
|
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/count/count.json)
|
||||||
|
|
||||||
|
#### This is not an officially supported Google product
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
Magisk is a suite of open source tools for customizing Android, supporting devices higher than Android 4.2. It covers fundamental parts of Android customization: root, boot scripts, SELinux patches, AVB2.0 / dm-verity / forceencrypt removals etc.
|
Magisk is a suite of open source software for customizing Android, supporting devices higher than Android 5.0.<br>
|
||||||
|
Some highlight features:
|
||||||
|
|
||||||
Here are some feature highlights:
|
- **MagiskSU**: Provide root access for applications
|
||||||
|
|
||||||
- **MagiskSU**: Provide root access to your device
|
|
||||||
- **Magisk Modules**: Modify read-only partitions by installing modules
|
- **Magisk Modules**: Modify read-only partitions by installing modules
|
||||||
- **MagiskHide**: Hide Magisk from root detections / system integrity checks
|
- **MagiskBoot**: The most complete tool for unpacking and repacking Android boot images
|
||||||
|
|
||||||
## Downloads
|
## Downloads
|
||||||
|
|
||||||
Please note that the only source of official Magisk information and downloads is [Github](https://github.com/topjohnwu/Magisk/). Beware of any other websites.
|
[Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads.
|
||||||
|
|
||||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v22.0)
|
[](https://github.com/topjohnwu/Magisk/releases/tag/v23.0)
|
||||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v22.0)
|
[](https://github.com/topjohnwu/Magisk/releases/tag/v23.0)
|
||||||
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk)
|
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk)
|
||||||
|
|
||||||
## Useful Links
|
## Useful Links
|
||||||
@@ -27,13 +28,6 @@ Please note that the only source of official Magisk information and downloads is
|
|||||||
- [Magisk Documentation](https://topjohnwu.github.io/Magisk/)
|
- [Magisk Documentation](https://topjohnwu.github.io/Magisk/)
|
||||||
- [Magisk Troubleshoot Wiki](https://www.didgeridoohan.com/magisk/HomePage) (by [@Didgeridoohan](https://github.com/Didgeridoohan))
|
- [Magisk Troubleshoot Wiki](https://www.didgeridoohan.com/magisk/HomePage) (by [@Didgeridoohan](https://github.com/Didgeridoohan))
|
||||||
|
|
||||||
## Android Version Support
|
|
||||||
|
|
||||||
- Android 4.2+: MagiskSU and Magisk Modules Only
|
|
||||||
- Android 4.4+: All core features available
|
|
||||||
- Android 6.0+: Guaranteed MagiskHide support
|
|
||||||
- Android 7.0+: Full MagiskHide protection
|
|
||||||
|
|
||||||
## Bug Reports
|
## Bug Reports
|
||||||
|
|
||||||
**Only bug reports from Canary builds will be accepted.**
|
**Only bug reports from Canary builds will be accepted.**
|
||||||
@@ -49,7 +43,7 @@ For Magisk app crashes, record and upload the logcat when the crash occurs.
|
|||||||
- Install Python 3.6+ \
|
- Install Python 3.6+ \
|
||||||
(Windows only: select **'Add Python to PATH'** in installer, and run `pip install colorama` after install)
|
(Windows only: select **'Add Python to PATH'** in installer, and run `pip install colorama` after install)
|
||||||
- Configure to use the JDK bundled in Android Studio:
|
- Configure to use the JDK bundled in Android Studio:
|
||||||
- macOS: `export JAVA_HOME="/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home"`
|
- macOS: `export JAVA_HOME="/Applications/Android Studio.app/Contents/jre/Contents/Home"`
|
||||||
- Linux: `export PATH="/path/to/androidstudio/jre/bin:$PATH"`
|
- Linux: `export PATH="/path/to/androidstudio/jre/bin:$PATH"`
|
||||||
- Windows: Add `C:\Path\To\Android Studio\jre\bin` to environment variable `PATH`
|
- Windows: Add `C:\Path\To\Android Studio\jre\bin` to environment variable `PATH`
|
||||||
- Set environment variable `ANDROID_SDK_ROOT` to the Android SDK folder (can be found in Android Studio settings)
|
- Set environment variable `ANDROID_SDK_ROOT` to the Android SDK folder (can be found in Android Studio settings)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
33
app/proguard-rules.pro
vendored
33
app/proguard-rules.pro
vendored
@@ -16,21 +16,23 @@
|
|||||||
# public *;
|
# public *;
|
||||||
#}
|
#}
|
||||||
|
|
||||||
|
# Parcelable
|
||||||
|
-keepclassmembers class * implements android.os.Parcelable {
|
||||||
|
public static final ** CREATOR;
|
||||||
|
}
|
||||||
|
|
||||||
# Kotlin
|
# Kotlin
|
||||||
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
||||||
public static void check*(...);
|
public static void check*(...);
|
||||||
public static void throw*(...);
|
public static void throw*(...);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Snet
|
|
||||||
-keepclassmembers class com.topjohnwu.magisk.ui.safetynet.SafetyNetHelper { *; }
|
|
||||||
-keep,allowobfuscation interface com.topjohnwu.magisk.ui.safetynet.SafetyNetHelper$Callback
|
|
||||||
-keepclassmembers class * implements com.topjohnwu.magisk.ui.safetynet.SafetyNetHelper$Callback {
|
|
||||||
void onResponse(org.json.JSONObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Stub
|
# Stub
|
||||||
-keep class com.topjohnwu.magisk.core.App { <init>(java.lang.Object); }
|
-keep class com.topjohnwu.magisk.core.App { <init>(java.lang.Object); }
|
||||||
|
-keepclassmembers class androidx.appcompat.app.AppCompatDelegateImpl {
|
||||||
|
boolean mActivityHandlesUiModeChecked;
|
||||||
|
boolean mActivityHandlesUiMode;
|
||||||
|
}
|
||||||
|
|
||||||
# Strip Timber verbose and debug logging
|
# Strip Timber verbose and debug logging
|
||||||
-assumenosideeffects class timber.log.Timber$Tree {
|
-assumenosideeffects class timber.log.Timber$Tree {
|
||||||
@@ -42,7 +44,16 @@
|
|||||||
-repackageclasses 'a'
|
-repackageclasses 'a'
|
||||||
-allowaccessmodification
|
-allowaccessmodification
|
||||||
|
|
||||||
# QOL
|
-obfuscationdictionary ../dict.txt
|
||||||
-dontnote **
|
-classobfuscationdictionary ../dict.txt
|
||||||
-dontwarn com.caverock.androidsvg.**
|
-packageobfuscationdictionary ../dict.txt
|
||||||
-dontwarn ru.noties.markwon.**
|
|
||||||
|
-dontwarn org.bouncycastle.jsse.BCSSLParameters
|
||||||
|
-dontwarn org.bouncycastle.jsse.BCSSLSocket
|
||||||
|
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
|
||||||
|
-dontwarn org.commonmark.ext.gfm.strikethrough.Strikethrough
|
||||||
|
-dontwarn org.conscrypt.Conscrypt*
|
||||||
|
-dontwarn org.conscrypt.ConscryptHostnameVerifier
|
||||||
|
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
|
||||||
|
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
|
||||||
|
-dontwarn org.openjsse.net.ssl.OpenJSSE
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ plugins {
|
|||||||
id("com.android.library")
|
id("com.android.library")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupCommon()
|
||||||
|
|
||||||
android {
|
android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
vectorDrawables.useSupportLibrary = true
|
|
||||||
consumerProguardFiles("proguard-rules.pro")
|
consumerProguardFiles("proguard-rules.pro")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
|
api("io.michaelrocks:paranoid-core:0.3.7")
|
||||||
}
|
}
|
||||||
|
|||||||
4
app/shared/proguard-rules.pro
vendored
4
app/shared/proguard-rules.pro
vendored
@@ -19,7 +19,3 @@
|
|||||||
# If you keep the line number information, uncomment this to
|
# If you keep the line number information, uncomment this to
|
||||||
# hide the original source file name.
|
# hide the original source file name.
|
||||||
#-renamesourcefileattribute SourceFile
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
-keepclassmembers class * extends javax.net.ssl.SSLSocketFactory {
|
|
||||||
** delegate;
|
|
||||||
}
|
|
||||||
|
|||||||
8
app/shared/src/debug/AndroidManifest.xml
Normal file
8
app/shared/src/debug/AndroidManifest.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:usesCleartextTraffic="true"
|
||||||
|
tools:ignore="UnusedAttribute" />
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -7,6 +7,8 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
<uses-permission android:name="android.permission.HIDE_OVERLAY_WINDOWS" />
|
||||||
|
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="29"
|
android:maxSdkVersion="29"
|
||||||
@@ -20,8 +22,6 @@
|
|||||||
android:label="Magisk"
|
android:label="Magisk"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||||
android:usesCleartextTraffic="true"
|
|
||||||
tools:ignore="UnusedAttribute" />
|
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
|
|
||||||
@@ -7,14 +9,10 @@ import java.io.File;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import io.michaelrocks.paranoid.Obfuscate;
|
||||||
|
|
||||||
|
@Obfuscate
|
||||||
public class DynAPK {
|
public class DynAPK {
|
||||||
|
|
||||||
// Indices of the object array
|
|
||||||
private static final int STUB_VERSION_ENTRY = 0;
|
|
||||||
private static final int CLASS_COMPONENT_MAP = 1;
|
|
||||||
|
|
||||||
private static File dynDir;
|
private static File dynDir;
|
||||||
private static Method addAssetPath;
|
private static Method addAssetPath;
|
||||||
|
|
||||||
@@ -38,21 +36,6 @@ public class DynAPK {
|
|||||||
return new File(getDynDir(c), "update.apk");
|
return new File(getDynDir(c), "update.apk");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Data load(Object o) {
|
|
||||||
Object[] arr = (Object[]) o;
|
|
||||||
Data data = new Data();
|
|
||||||
data.version = (int) arr[STUB_VERSION_ENTRY];
|
|
||||||
data.classToComponent = (Map<String, String>) arr[CLASS_COMPONENT_MAP];
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object pack(Data data) {
|
|
||||||
Object[] arr = new Object[2];
|
|
||||||
arr[STUB_VERSION_ENTRY] = data.version;
|
|
||||||
arr[CLASS_COMPONENT_MAP] = data.classToComponent;
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addAssetPath(AssetManager asset, String path) {
|
public static void addAssetPath(AssetManager asset, String path) {
|
||||||
try {
|
try {
|
||||||
if (addAssetPath == null)
|
if (addAssetPath == null)
|
||||||
@@ -62,7 +45,28 @@ public class DynAPK {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Data {
|
public static class Data {
|
||||||
public int version;
|
// Indices of the object array
|
||||||
public Map<String, String> classToComponent;
|
private static final int STUB_VERSION = 0;
|
||||||
|
private static final int CLASS_COMPONENT_MAP = 1;
|
||||||
|
private static final int ROOT_SERVICE = 2;
|
||||||
|
private static final int ARR_SIZE = 3;
|
||||||
|
|
||||||
|
private final Object[] arr;
|
||||||
|
|
||||||
|
public Data() { arr = new Object[ARR_SIZE]; }
|
||||||
|
public Data(Object o) { arr = (Object[]) o; }
|
||||||
|
public Object getObject() { return arr; }
|
||||||
|
|
||||||
|
public int getVersion() { return (int) arr[STUB_VERSION]; }
|
||||||
|
public void setVersion(int version) { arr[STUB_VERSION] = version; }
|
||||||
|
public Map<String, String> getClassToComponent() {
|
||||||
|
// noinspection unchecked
|
||||||
|
return (Map<String, String>) arr[CLASS_COMPONENT_MAP];
|
||||||
|
}
|
||||||
|
public void setClassToComponent(Map<String, String> map) {
|
||||||
|
arr[CLASS_COMPONENT_MAP] = map;
|
||||||
|
}
|
||||||
|
public Class<?> getRootService() { return (Class<?>) arr[ROOT_SERVICE]; }
|
||||||
|
public void setRootService(Class<?> service) { arr[ROOT_SERVICE] = service; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import io.michaelrocks.paranoid.Obfuscate;
|
||||||
|
|
||||||
|
@Obfuscate
|
||||||
|
public class ProviderInstaller {
|
||||||
|
|
||||||
|
public static boolean install(Context context) {
|
||||||
|
try {
|
||||||
|
// Try installing new SSL provider from Google Play Service
|
||||||
|
Context gms = context.createPackageContext("com.google.android.gms",
|
||||||
|
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
|
||||||
|
gms.getClassLoader()
|
||||||
|
.loadClass("com.google.android.gms.common.security.ProviderInstallerImpl")
|
||||||
|
.getMethod("insertProvider", Context.class)
|
||||||
|
.invoke(null, gms);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.net;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
|
||||||
import javax.net.ssl.SSLSocket;
|
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
|
||||||
|
|
||||||
public class NoSSLv3SocketFactory extends SSLSocketFactory {
|
|
||||||
|
|
||||||
private final static SSLSocketFactory delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getDefaultCipherSuites() {
|
|
||||||
return delegate.getDefaultCipherSuites();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getSupportedCipherSuites() {
|
|
||||||
return delegate.getSupportedCipherSuites();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Socket createSafeSocket(Socket socket) {
|
|
||||||
if (socket instanceof SSLSocket)
|
|
||||||
return new SSLSocketWrapper((SSLSocket) socket) {
|
|
||||||
@Override
|
|
||||||
public void setEnabledProtocols(String[] protocols) {
|
|
||||||
List<String> proto = new ArrayList<>(Arrays.asList(getSupportedProtocols()));
|
|
||||||
proto.remove("SSLv3");
|
|
||||||
super.setEnabledProtocols(proto.toArray(new String[0]));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
|
|
||||||
return createSafeSocket(delegate.createSocket(s, host, port, autoClose));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Socket createSocket() throws IOException {
|
|
||||||
return createSafeSocket(delegate.createSocket());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Socket createSocket(String host, int port) throws IOException {
|
|
||||||
return createSafeSocket(delegate.createSocket(host, port));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
|
|
||||||
return createSafeSocket(delegate.createSocket(host, port, localHost, localPort));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
|
||||||
return createSafeSocket(delegate.createSocket(host, port));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
|
|
||||||
return createSafeSocket(delegate.createSocket(address, port, localAddress, localPort));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,48 +1,121 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import android.app.Activity;
|
import static android.content.pm.PackageInstaller.EXTRA_STATUS;
|
||||||
|
import static android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID;
|
||||||
|
import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION;
|
||||||
|
import static android.content.pm.PackageInstaller.STATUS_SUCCESS;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.content.pm.PackageInstaller.Session;
|
||||||
|
import android.content.pm.PackageInstaller.SessionParams;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
import com.topjohnwu.magisk.FileProvider;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class APKInstall {
|
import io.michaelrocks.paranoid.Obfuscate;
|
||||||
|
|
||||||
public static Intent installIntent(Context c, File apk) {
|
@Obfuscate
|
||||||
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
|
public final class APKInstall {
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
// @WorkerThread
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
public static void installapk(Context context, File apk) {
|
||||||
if (Build.VERSION.SDK_INT >= 24) {
|
//noinspection InlinedApi
|
||||||
intent.setData(FileProvider.getUriForFile(c, c.getPackageName() + ".provider", apk));
|
var flag = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE;
|
||||||
} else {
|
var action = APKInstall.class.getName();
|
||||||
//noinspection ResultOfMethodCallIgnored SetWorldReadable
|
var intent = new Intent(action).setPackage(context.getPackageName());
|
||||||
apk.setReadable(true, false);
|
var pending = PendingIntent.getBroadcast(context, 0, intent, flag);
|
||||||
intent.setData(Uri.fromFile(apk));
|
|
||||||
|
var installer = context.getPackageManager().getPackageInstaller();
|
||||||
|
var params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
params.setRequireUserAction(SessionParams.USER_ACTION_NOT_REQUIRED);
|
||||||
|
}
|
||||||
|
try (Session session = installer.openSession(installer.createSession(params))) {
|
||||||
|
OutputStream out = session.openWrite(apk.getName(), 0, apk.length());
|
||||||
|
try (var in = new FileInputStream(apk); out) {
|
||||||
|
transfer(in, out);
|
||||||
|
}
|
||||||
|
session.commit(pending.getIntentSender());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(APKInstall.class.getSimpleName(), "", e);
|
||||||
}
|
}
|
||||||
return intent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void install(Context c, File apk) {
|
public static void transfer(InputStream in, OutputStream out) throws IOException {
|
||||||
c.startActivity(installIntent(c, apk));
|
int size = 8192;
|
||||||
|
var buffer = new byte[size];
|
||||||
|
int read;
|
||||||
|
while ((read = in.read(buffer, 0, size)) >= 0) {
|
||||||
|
out.write(buffer, 0, read);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerInstallReceiver(Context c, BroadcastReceiver r) {
|
public static InstallReceiver register(Context context, String packageName, Runnable onSuccess) {
|
||||||
IntentFilter filter = new IntentFilter();
|
var receiver = new InstallReceiver(context, packageName, onSuccess);
|
||||||
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
|
var filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
|
||||||
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
|
||||||
filter.addDataScheme("package");
|
filter.addDataScheme("package");
|
||||||
c.getApplicationContext().registerReceiver(r, filter);
|
context.registerReceiver(receiver, filter);
|
||||||
|
context.registerReceiver(receiver, new IntentFilter(APKInstall.class.getName()));
|
||||||
|
return receiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void installHideResult(Activity c, File apk) {
|
public static class InstallReceiver extends BroadcastReceiver {
|
||||||
Intent intent = installIntent(c, apk);
|
private final Context context;
|
||||||
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
|
private final String packageName;
|
||||||
c.startActivityForResult(intent, 0); // Ignore result, use install receiver
|
private final Runnable onSuccess;
|
||||||
|
private final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
private Intent intent = null;
|
||||||
|
|
||||||
|
private InstallReceiver(Context context, String packageName, Runnable onSuccess) {
|
||||||
|
this.context = context;
|
||||||
|
this.packageName = packageName;
|
||||||
|
this.onSuccess = onSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context c, Intent i) {
|
||||||
|
if (Intent.ACTION_PACKAGE_ADDED.equals(i.getAction())) {
|
||||||
|
Uri data = i.getData();
|
||||||
|
if (data == null || onSuccess == null) return;
|
||||||
|
String pkg = data.getSchemeSpecificPart();
|
||||||
|
if (pkg.equals(packageName)) {
|
||||||
|
onSuccess.run();
|
||||||
|
context.unregisterReceiver(this);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int status = i.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID);
|
||||||
|
switch (status) {
|
||||||
|
case STATUS_PENDING_USER_ACTION:
|
||||||
|
intent = i.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||||
|
break;
|
||||||
|
case STATUS_SUCCESS:
|
||||||
|
if (onSuccess != null) onSuccess.run();
|
||||||
|
default:
|
||||||
|
context.unregisterReceiver(this);
|
||||||
|
}
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// @WorkerThread @Nullable
|
||||||
|
public Intent waitIntent() {
|
||||||
|
try {
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
latch.await(5, TimeUnit.SECONDS);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ import dalvik.system.DexClassLoader;
|
|||||||
|
|
||||||
public class DynamicClassLoader extends DexClassLoader {
|
public class DynamicClassLoader extends DexClassLoader {
|
||||||
|
|
||||||
private ClassLoader base = Object.class.getClassLoader();
|
private static final ClassLoader base = Object.class.getClassLoader();
|
||||||
|
|
||||||
|
public DynamicClassLoader(File apk) {
|
||||||
|
super(apk.getPath(), apk.getParent(), null, base);
|
||||||
|
}
|
||||||
|
|
||||||
public DynamicClassLoader(File apk, ClassLoader parent) {
|
public DynamicClassLoader(File apk, ClassLoader parent) {
|
||||||
super(apk.getPath(), apk.getParent(), null, parent);
|
super(apk.getPath(), apk.getParent(), null, parent);
|
||||||
@@ -18,7 +22,7 @@ public class DynamicClassLoader extends DexClassLoader {
|
|||||||
@Override
|
@Override
|
||||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
// First check if already loaded
|
// First check if already loaded
|
||||||
Class cls = findLoadedClass(name);
|
Class<?> cls = findLoadedClass(name);
|
||||||
if (cls != null)
|
if (cls != null)
|
||||||
return cls;
|
return cls;
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,8 @@
|
|||||||
android:multiArch="true"
|
android:multiArch="true"
|
||||||
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
|
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
|
||||||
|
|
||||||
<!-- Splash -->
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".core.SplashActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/SplashTheme">
|
android:theme="@style/SplashTheme">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@@ -27,10 +26,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!-- Main -->
|
|
||||||
<activity android:name=".ui.MainActivity" />
|
|
||||||
|
|
||||||
<!-- Superuser -->
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.surequest.SuRequestActivity"
|
android:name=".ui.surequest.SuRequestActivity"
|
||||||
android:directBootAware="true"
|
android:directBootAware="true"
|
||||||
@@ -43,7 +38,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!-- Receiver -->
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".core.Receiver"
|
android:name=".core.Receiver"
|
||||||
android:directBootAware="true"
|
android:directBootAware="true"
|
||||||
@@ -54,15 +48,21 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||||
|
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
||||||
|
|
||||||
<data android:scheme="package" />
|
<data android:scheme="package" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<!-- DownloadService -->
|
<service
|
||||||
<service android:name=".core.download.DownloadService" />
|
android:name=".core.download.DownloadService"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".core.JobService"
|
||||||
|
android:exported="false"
|
||||||
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
|
|
||||||
<!-- FileProvider -->
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".core.Provider"
|
android:name=".core.Provider"
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
@@ -70,23 +70,18 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true" />
|
android:grantUriPermissions="true" />
|
||||||
|
|
||||||
<!-- Hardcode GMS version -->
|
|
||||||
<meta-data
|
|
||||||
android:name="com.google.android.gms.version"
|
|
||||||
android:value="12451000" />
|
|
||||||
|
|
||||||
<!-- Initialize WorkManager on-demand -->
|
|
||||||
<provider
|
|
||||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
|
||||||
android:authorities="${applicationId}.workmanager-init"
|
|
||||||
tools:node="remove"
|
|
||||||
tools:ignore="ExportedContentProvider" />
|
|
||||||
|
|
||||||
<!-- We don't invalidate Room -->
|
<!-- We don't invalidate Room -->
|
||||||
<service
|
<service
|
||||||
android:name="androidx.room.MultiInstanceInvalidationService"
|
android:name="androidx.room.MultiInstanceInvalidationService"
|
||||||
tools:node="remove" />
|
tools:node="remove" />
|
||||||
|
|
||||||
|
<!-- We don't need emoji compat -->
|
||||||
|
<provider
|
||||||
|
android:name="androidx.startup.InitializationProvider"
|
||||||
|
android:authorities="${applicationId}.androidx-startup"
|
||||||
|
android:exported="false"
|
||||||
|
tools:node="remove" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -13,15 +13,14 @@ import androidx.navigation.NavDirections
|
|||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
import com.topjohnwu.magisk.ktx.startAnimations
|
import com.topjohnwu.magisk.ktx.startAnimations
|
||||||
|
|
||||||
abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
|
abstract class BaseFragment<Binding : ViewDataBinding> : Fragment(), ViewModelHolder {
|
||||||
Fragment(), BaseUIComponent<VM> {
|
|
||||||
|
|
||||||
val activity get() = requireActivity() as BaseUIActivity<*, *>
|
val activity get() = requireActivity() as NavigationActivity<*>
|
||||||
protected lateinit var binding: Binding
|
protected lateinit var binding: Binding
|
||||||
protected abstract val layoutRes: Int
|
protected abstract val layoutRes: Int
|
||||||
|
|
||||||
override val viewRoot: View get() = binding.root
|
|
||||||
private val navigation get() = activity.navigation
|
private val navigation get() = activity.navigation
|
||||||
|
open val snackbarAnchorView: View? get() = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -35,7 +34,7 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
|
|||||||
): View? {
|
): View? {
|
||||||
binding = DataBindingUtil.inflate<Binding>(inflater, layoutRes, container, false).also {
|
binding = DataBindingUtil.inflate<Binding>(inflater, layoutRes, container, false).also {
|
||||||
it.setVariable(BR.viewModel, viewModel)
|
it.setVariable(BR.viewModel, viewModel)
|
||||||
it.lifecycleOwner = this
|
it.lifecycleOwner = viewLifecycleOwner
|
||||||
}
|
}
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
@@ -63,7 +62,7 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding.addOnRebindCallback(object : OnRebindCallback<Binding>() {
|
binding.addOnRebindCallback(object : OnRebindCallback<Binding>() {
|
||||||
override fun onPreBind(binding: Binding): Boolean {
|
override fun onPreBind(binding: Binding): Boolean {
|
||||||
this@BaseUIFragment.onPreBind(binding)
|
this@BaseFragment.onPreBind(binding)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -83,9 +82,3 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReselectionTarget {
|
|
||||||
|
|
||||||
fun onReselected()
|
|
||||||
|
|
||||||
}
|
|
||||||
129
app/src/main/java/com/topjohnwu/magisk/arch/BaseMainActivity.kt
Normal file
129
app/src/main/java/com/topjohnwu/magisk/arch/BaseMainActivity.kt
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package com.topjohnwu.magisk.arch
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
|
import androidx.databinding.ViewDataBinding
|
||||||
|
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
||||||
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.core.*
|
||||||
|
import com.topjohnwu.magisk.core.tasks.HideAPK
|
||||||
|
import com.topjohnwu.magisk.di.ServiceLocator
|
||||||
|
import com.topjohnwu.magisk.ui.theme.Theme
|
||||||
|
import com.topjohnwu.magisk.view.MagiskDialog
|
||||||
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
|
import com.topjohnwu.magisk.view.Shortcuts
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
abstract class BaseMainActivity<Binding : ViewDataBinding> : NavigationActivity<Binding>() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private var doPreload = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private val latch = CountDownLatch(1)
|
||||||
|
private val uninstallPkg = registerForActivityResult(UninstallPackage) { latch.countDown() }
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setTheme(Theme.selected.themeRes)
|
||||||
|
|
||||||
|
if (isRunningAsStub && doPreload) {
|
||||||
|
// Manually apply splash theme for stub
|
||||||
|
theme.applyStyle(R.style.StubSplashTheme, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
if (!isRunningAsStub) {
|
||||||
|
val splashScreen = installSplashScreen()
|
||||||
|
splashScreen.setKeepOnScreenCondition { doPreload }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doPreload) {
|
||||||
|
Shell.getShell(null) {
|
||||||
|
if (isRunningAsStub && !it.isRoot) {
|
||||||
|
showInvalidStateMessage()
|
||||||
|
return@getShell
|
||||||
|
}
|
||||||
|
preLoad()
|
||||||
|
runOnUiThread {
|
||||||
|
doPreload = false
|
||||||
|
if (isRunningAsStub) {
|
||||||
|
// Re-launch main activity without splash theme
|
||||||
|
relaunch()
|
||||||
|
} else {
|
||||||
|
showMainUI(savedInstanceState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showMainUI(savedInstanceState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun showMainUI(savedInstanceState: Bundle?)
|
||||||
|
|
||||||
|
private fun showInvalidStateMessage() {
|
||||||
|
runOnUiThread {
|
||||||
|
MagiskDialog(this).apply {
|
||||||
|
setTitle(R.string.unsupport_nonroot_stub_title)
|
||||||
|
setMessage(R.string.unsupport_nonroot_stub_msg)
|
||||||
|
setButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||||
|
text = R.string.install
|
||||||
|
onClick { HideAPK.restore(this@BaseMainActivity) }
|
||||||
|
}
|
||||||
|
setCancelable(false)
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun preLoad() {
|
||||||
|
val prevPkg = intent.getStringExtra(Const.Key.PREV_PKG)
|
||||||
|
|
||||||
|
Config.load(prevPkg)
|
||||||
|
handleRepackage(prevPkg)
|
||||||
|
Notifications.setup(this)
|
||||||
|
JobService.schedule(this)
|
||||||
|
Shortcuts.setupDynamic(this)
|
||||||
|
|
||||||
|
// Pre-fetch network services
|
||||||
|
ServiceLocator.networkService
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleRepackage(pkg: String?) {
|
||||||
|
if (packageName != APPLICATION_ID) {
|
||||||
|
runCatching {
|
||||||
|
// Hidden, remove com.topjohnwu.magisk if exist as it could be malware
|
||||||
|
packageManager.getApplicationInfo(APPLICATION_ID, 0)
|
||||||
|
Shell.su("(pm uninstall $APPLICATION_ID)& >/dev/null 2>&1").exec()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (Config.suManager.isNotEmpty())
|
||||||
|
Config.suManager = ""
|
||||||
|
pkg ?: return
|
||||||
|
if (!Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec().isSuccess) {
|
||||||
|
uninstallPkg.launch(pkg)
|
||||||
|
// Wait for the uninstallation to finish
|
||||||
|
latch.await()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object UninstallPackage : ActivityResultContract<String, Boolean>() {
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
override fun createIntent(context: Context, input: String): Intent {
|
||||||
|
val uri = Uri.Builder().scheme("package").opaquePart(input).build()
|
||||||
|
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, uri)
|
||||||
|
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
|
||||||
|
return intent
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseResult(resultCode: Int, intent: Intent?) = resultCode == RESULT_OK
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.arch
|
|
||||||
|
|
||||||
import android.content.res.Resources
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.KeyEvent
|
|
||||||
import android.view.View
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
|
||||||
import androidx.core.content.res.use
|
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.databinding.ViewDataBinding
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.NavDirections
|
|
||||||
import androidx.navigation.fragment.NavHostFragment
|
|
||||||
import com.topjohnwu.magisk.BR
|
|
||||||
import com.topjohnwu.magisk.core.Config
|
|
||||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
|
||||||
import com.topjohnwu.magisk.ui.inflater.LayoutInflaterFactory
|
|
||||||
import com.topjohnwu.magisk.ui.theme.Theme
|
|
||||||
|
|
||||||
abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
|
||||||
BaseActivity(), BaseUIComponent<VM> {
|
|
||||||
|
|
||||||
protected lateinit var binding: Binding
|
|
||||||
protected abstract val layoutRes: Int
|
|
||||||
protected open val themeRes: Int = Theme.selected.themeRes
|
|
||||||
|
|
||||||
private val navHostFragment by lazy {
|
|
||||||
supportFragmentManager.findFragmentById(navHostId) as? NavHostFragment
|
|
||||||
}
|
|
||||||
private val topFragment get() = navHostFragment?.childFragmentManager?.fragments?.getOrNull(0)
|
|
||||||
protected val currentFragment get() = topFragment as? BaseUIFragment<*, *>
|
|
||||||
|
|
||||||
override val viewRoot: View get() = binding.root
|
|
||||||
open val navigation: NavController? get() = navHostFragment?.navController
|
|
||||||
|
|
||||||
open val navHostId: Int = 0
|
|
||||||
open val snackbarView get() = binding.root
|
|
||||||
|
|
||||||
init {
|
|
||||||
val theme = Config.darkTheme
|
|
||||||
AppCompatDelegate.setDefaultNightMode(theme)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
layoutInflater.factory2 = LayoutInflaterFactory(delegate)
|
|
||||||
|
|
||||||
setTheme(themeRes)
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
startObserveEvents()
|
|
||||||
|
|
||||||
// We need to set the window background explicitly since for whatever reason it's not
|
|
||||||
// propagated upstream
|
|
||||||
obtainStyledAttributes(intArrayOf(android.R.attr.windowBackground))
|
|
||||||
.use { it.getDrawable(0) }
|
|
||||||
.also { window.setBackgroundDrawable(it) }
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
window?.decorView?.let {
|
|
||||||
it.systemUiVisibility = (it.systemUiVisibility
|
|
||||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
|
||||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
|
||||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
window?.decorView?.post {
|
|
||||||
// If navigation bar is short enough (gesture navigation enabled), make it transparent
|
|
||||||
if (window.decorView.rootWindowInsets?.systemWindowInsetBottom ?: 0 < Resources.getSystem().displayMetrics.density * 40) {
|
|
||||||
window.navigationBarColor = Color.TRANSPARENT
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
window.navigationBarDividerColor = Color.TRANSPARENT
|
|
||||||
}
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
window.isNavigationBarContrastEnforced = false
|
|
||||||
window.isStatusBarContrastEnforced = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setContentView() {
|
|
||||||
binding = DataBindingUtil.setContentView<Binding>(this, layoutRes).also {
|
|
||||||
it.setVariable(BR.viewModel, viewModel)
|
|
||||||
it.lifecycleOwner = this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setAccessibilityDelegate(delegate: View.AccessibilityDelegate?) {
|
|
||||||
viewRoot.rootView.accessibilityDelegate = delegate
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
viewModel.requestRefresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
|
||||||
return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onEventDispatched(event: ViewEvent) = when (event) {
|
|
||||||
is ContextExecutor -> event(this)
|
|
||||||
is ActivityExecutor -> event(this)
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBackPressed() {
|
|
||||||
if (navigation == null || currentFragment?.onBackPressed()?.not() == true) {
|
|
||||||
super.onBackPressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun NavDirections.navigate() {
|
|
||||||
navigation?.navigate(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ package com.topjohnwu.magisk.arch
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import androidx.annotation.CallSuper
|
import androidx.annotation.CallSuper
|
||||||
import androidx.core.graphics.Insets
|
|
||||||
import androidx.databinding.Bindable
|
import androidx.databinding.Bindable
|
||||||
import androidx.databinding.Observable
|
import androidx.databinding.Observable
|
||||||
import androidx.databinding.PropertyChangeRegistry
|
import androidx.databinding.PropertyChangeRegistry
|
||||||
@@ -14,16 +13,17 @@ import androidx.navigation.NavDirections
|
|||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
import com.topjohnwu.magisk.databinding.ObservableHost
|
||||||
import com.topjohnwu.magisk.events.*
|
import com.topjohnwu.magisk.databinding.set
|
||||||
import com.topjohnwu.magisk.utils.ObservableHost
|
import com.topjohnwu.magisk.events.BackPressEvent
|
||||||
import com.topjohnwu.magisk.utils.set
|
import com.topjohnwu.magisk.events.NavigationEvent
|
||||||
|
import com.topjohnwu.magisk.events.PermissionEvent
|
||||||
|
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import org.koin.core.KoinComponent
|
|
||||||
|
|
||||||
abstract class BaseViewModel(
|
abstract class BaseViewModel(
|
||||||
initialState: State = State.LOADING
|
initialState: State = State.LOADING
|
||||||
) : ViewModel(), ObservableHost, KoinComponent {
|
) : ViewModel(), ObservableHost {
|
||||||
|
|
||||||
override var callbacks: PropertyChangeRegistry? = null
|
override var callbacks: PropertyChangeRegistry? = null
|
||||||
|
|
||||||
@@ -41,10 +41,6 @@ abstract class BaseViewModel(
|
|||||||
val isConnected get() = Info.isConnected
|
val isConnected get() = Info.isConnected
|
||||||
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
|
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
|
||||||
|
|
||||||
@get:Bindable
|
|
||||||
var insets = Insets.NONE
|
|
||||||
set(value) = set(value, field, { field = it }, BR.insets)
|
|
||||||
|
|
||||||
var state= initialState
|
var state= initialState
|
||||||
set(value) = set(value, field, { field = it }, BR.loading, BR.loaded, BR.loadFailed)
|
set(value) = set(value, field, { field = it }, BR.loading, BR.loaded, BR.loadFailed)
|
||||||
|
|
||||||
@@ -77,15 +73,11 @@ abstract class BaseViewModel(
|
|||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withView(action: BaseActivity.() -> Unit) {
|
|
||||||
ViewActionEvent(action).publish()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun withPermission(permission: String, callback: (Boolean) -> Unit) {
|
fun withPermission(permission: String, callback: (Boolean) -> Unit) {
|
||||||
PermissionEvent(permission, callback).publish()
|
PermissionEvent(permission, callback).publish()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withExternalRW(callback: () -> Unit) {
|
inline fun withExternalRW(crossinline callback: () -> Unit) {
|
||||||
withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) {
|
withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) {
|
||||||
if (!it) {
|
if (!it) {
|
||||||
SnackbarEvent(R.string.external_rw_permission_denied).publish()
|
SnackbarEvent(R.string.external_rw_permission_denied).publish()
|
||||||
@@ -106,8 +98,8 @@ abstract class BaseViewModel(
|
|||||||
_viewEvents.postValue(this)
|
_viewEvents.postValue(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NavDirections.navigate() {
|
fun NavDirections.navigate(pop: Boolean = false) {
|
||||||
_viewEvents.postValue(NavigationEvent(this))
|
_viewEvents.postValue(NavigationEvent(this, pop))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.arch
|
|
||||||
|
|
||||||
import androidx.databinding.ViewDataBinding
|
|
||||||
import com.topjohnwu.magisk.databinding.ComparableRvItem
|
|
||||||
import com.topjohnwu.magisk.databinding.RvItem
|
|
||||||
import com.topjohnwu.magisk.utils.DiffObservableList
|
|
||||||
import com.topjohnwu.magisk.utils.FilterableDiffObservableList
|
|
||||||
import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter
|
|
||||||
import me.tatarka.bindingcollectionadapter2.ItemBinding
|
|
||||||
import me.tatarka.bindingcollectionadapter2.OnItemBind
|
|
||||||
|
|
||||||
fun <T : ComparableRvItem<*>> diffListOf(
|
|
||||||
vararg newItems: T
|
|
||||||
) = diffListOf(newItems.toList())
|
|
||||||
|
|
||||||
fun <T : ComparableRvItem<*>> diffListOf(
|
|
||||||
newItems: List<T>
|
|
||||||
) = DiffObservableList(object : DiffObservableList.Callback<T> {
|
|
||||||
override fun areItemsTheSame(oldItem: T, newItem: T) = oldItem.genericItemSameAs(newItem)
|
|
||||||
override fun areContentsTheSame(oldItem: T, newItem: T) = oldItem.genericContentSameAs(newItem)
|
|
||||||
}).also { it.update(newItems) }
|
|
||||||
|
|
||||||
fun <T : ComparableRvItem<*>> filterableListOf(
|
|
||||||
vararg newItems: T
|
|
||||||
) = FilterableDiffObservableList(object : DiffObservableList.Callback<T> {
|
|
||||||
override fun areItemsTheSame(oldItem: T, newItem: T) = oldItem.genericItemSameAs(newItem)
|
|
||||||
override fun areContentsTheSame(oldItem: T, newItem: T) = oldItem.genericContentSameAs(newItem)
|
|
||||||
}).also { it.update(newItems.toList()) }
|
|
||||||
|
|
||||||
fun <T : RvItem> adapterOf() = object : BindingRecyclerViewAdapter<T>() {
|
|
||||||
override fun onBindBinding(
|
|
||||||
binding: ViewDataBinding,
|
|
||||||
variableId: Int,
|
|
||||||
layoutRes: Int,
|
|
||||||
position: Int,
|
|
||||||
item: T
|
|
||||||
) {
|
|
||||||
super.onBindBinding(binding, variableId, layoutRes, position, item)
|
|
||||||
item.onBindingBound(binding)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <T : RvItem> itemBindingOf(
|
|
||||||
crossinline body: (ItemBinding<*>) -> Unit = {}
|
|
||||||
) = OnItemBind<T> { itemBinding, _, item ->
|
|
||||||
item.bind(itemBinding)
|
|
||||||
body(itemBinding)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.topjohnwu.magisk.arch
|
||||||
|
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import androidx.databinding.ViewDataBinding
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavDirections
|
||||||
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
|
|
||||||
|
abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Binding>() {
|
||||||
|
|
||||||
|
abstract val navHostId: Int
|
||||||
|
|
||||||
|
private val navHostFragment by lazy {
|
||||||
|
supportFragmentManager.findFragmentById(navHostId) as NavHostFragment
|
||||||
|
}
|
||||||
|
|
||||||
|
protected val currentFragment get() =
|
||||||
|
navHostFragment.childFragmentManager.fragments.getOrNull(0) as? BaseFragment<*>
|
||||||
|
|
||||||
|
val navigation: NavController get() = navHostFragment.navController
|
||||||
|
|
||||||
|
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||||
|
return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (currentFragment?.onBackPressed()?.not() == true) {
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NavDirections.navigate() {
|
||||||
|
navigation.navigate(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.arch
|
|
||||||
|
|
||||||
import android.os.Handler
|
|
||||||
import androidx.core.os.postDelayed
|
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
|
||||||
|
|
||||||
interface Queryable {
|
|
||||||
|
|
||||||
val queryDelay: Long
|
|
||||||
val queryHandler: Handler get() = UiThreadHandler.handler
|
|
||||||
|
|
||||||
fun submitQuery() {
|
|
||||||
queryHandler.postDelayed(queryDelay) { query() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun query()
|
|
||||||
}
|
|
||||||
87
app/src/main/java/com/topjohnwu/magisk/arch/UIActivity.kt
Normal file
87
app/src/main/java/com/topjohnwu/magisk/arch/UIActivity.kt
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package com.topjohnwu.magisk.arch
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.content.res.use
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
|
import androidx.databinding.DataBindingUtil
|
||||||
|
import androidx.databinding.ViewDataBinding
|
||||||
|
import com.topjohnwu.magisk.BR
|
||||||
|
import com.topjohnwu.magisk.core.Config
|
||||||
|
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||||
|
import rikka.insets.WindowInsetsHelper
|
||||||
|
import rikka.layoutinflater.view.LayoutInflaterFactory
|
||||||
|
|
||||||
|
abstract class UIActivity<Binding : ViewDataBinding> : BaseActivity(), ViewModelHolder {
|
||||||
|
|
||||||
|
protected lateinit var binding: Binding
|
||||||
|
protected abstract val layoutRes: Int
|
||||||
|
|
||||||
|
open val snackbarView get() = binding.root
|
||||||
|
open val snackbarAnchorView: View? get() = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
val theme = Config.darkTheme
|
||||||
|
AppCompatDelegate.setDefaultNightMode(theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
layoutInflater.factory2 = LayoutInflaterFactory(delegate)
|
||||||
|
.addOnViewCreatedListener(WindowInsetsHelper.LISTENER)
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
startObserveEvents()
|
||||||
|
|
||||||
|
// We need to set the window background explicitly since for whatever reason it's not
|
||||||
|
// propagated upstream
|
||||||
|
obtainStyledAttributes(intArrayOf(android.R.attr.windowBackground))
|
||||||
|
.use { it.getDrawable(0) }
|
||||||
|
.also { window.setBackgroundDrawable(it) }
|
||||||
|
|
||||||
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
window?.decorView?.post {
|
||||||
|
// If navigation bar is short enough (gesture navigation enabled), make it transparent
|
||||||
|
if ((window.decorView.rootWindowInsets?.systemWindowInsetBottom
|
||||||
|
?: 0) < Resources.getSystem().displayMetrics.density * 40) {
|
||||||
|
window.navigationBarColor = Color.TRANSPARENT
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
window.navigationBarDividerColor = Color.TRANSPARENT
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
window.isNavigationBarContrastEnforced = false
|
||||||
|
window.isStatusBarContrastEnforced = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setContentView() {
|
||||||
|
binding = DataBindingUtil.setContentView<Binding>(this, layoutRes).also {
|
||||||
|
it.setVariable(BR.viewModel, viewModel)
|
||||||
|
it.lifecycleOwner = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAccessibilityDelegate(delegate: View.AccessibilityDelegate?) {
|
||||||
|
binding.root.rootView.accessibilityDelegate = delegate
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
viewModel.requestRefresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEventDispatched(event: ViewEvent) = when (event) {
|
||||||
|
is ContextExecutor -> event(this)
|
||||||
|
is ActivityExecutor -> event(this)
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,9 +18,9 @@ interface ContextExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ActivityExecutor {
|
interface ActivityExecutor {
|
||||||
operator fun invoke(activity: BaseUIActivity<*, *>)
|
operator fun invoke(activity: UIActivity<*>)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FragmentExecutor {
|
interface FragmentExecutor {
|
||||||
operator fun invoke(fragment: BaseUIFragment<*, *>)
|
operator fun invoke(fragment: BaseFragment<*>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package com.topjohnwu.magisk.arch
|
package com.topjohnwu.magisk.arch
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
|
||||||
interface BaseUIComponent<VM : BaseViewModel> : LifecycleOwner {
|
interface ViewModelHolder : LifecycleOwner {
|
||||||
|
|
||||||
val viewRoot: View
|
val viewModel: BaseViewModel
|
||||||
val viewModel: VM
|
|
||||||
|
|
||||||
fun startObserveEvents() {
|
fun startObserveEvents() {
|
||||||
viewModel.viewEvents.observe(this) {
|
viewModel.viewEvents.observe(this) {
|
||||||
@@ -1,42 +1,33 @@
|
|||||||
package com.topjohnwu.magisk.core
|
package com.topjohnwu.magisk.core
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
|
||||||
import androidx.multidex.MultiDex
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import com.topjohnwu.magisk.BuildConfig
|
|
||||||
import com.topjohnwu.magisk.DynAPK
|
import com.topjohnwu.magisk.DynAPK
|
||||||
import com.topjohnwu.magisk.core.utils.AppShellInit
|
import com.topjohnwu.magisk.core.utils.*
|
||||||
import com.topjohnwu.magisk.core.utils.BusyBoxInit
|
import com.topjohnwu.magisk.di.ServiceLocator
|
||||||
import com.topjohnwu.magisk.core.utils.IODispatcherExecutor
|
|
||||||
import com.topjohnwu.magisk.core.utils.updateConfig
|
|
||||||
import com.topjohnwu.magisk.di.koinModules
|
|
||||||
import com.topjohnwu.magisk.ktx.unwrap
|
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import org.koin.android.ext.koin.androidContext
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
import org.koin.core.context.startKoin
|
import com.topjohnwu.superuser.ipc.RootService
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
open class App() : Application() {
|
open class App() : Application() {
|
||||||
|
|
||||||
constructor(o: Any) : this() {
|
constructor(o: Any) : this() {
|
||||||
Info.stub = DynAPK.load(o)
|
val data = DynAPK.Data(o)
|
||||||
|
// Add the root service name mapping
|
||||||
|
data.classToComponent[RootRegistry::class.java.name] = data.rootService.name
|
||||||
|
// Send back the actual root service class
|
||||||
|
data.rootService = RootRegistry::class.java
|
||||||
|
Info.stub = data
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
|
||||||
Shell.setDefaultBuilder(Shell.Builder.create()
|
|
||||||
.setFlags(Shell.FLAG_MOUNT_MASTER)
|
|
||||||
.setInitializers(BusyBoxInit::class.java, AppShellInit::class.java)
|
|
||||||
.setTimeout(2))
|
|
||||||
Shell.EXECUTOR = IODispatcherExecutor()
|
|
||||||
|
|
||||||
// Always log full stack trace with Timber
|
// Always log full stack trace with Timber
|
||||||
Timber.plant(Timber.DebugTree())
|
Timber.plant(Timber.DebugTree())
|
||||||
Thread.setDefaultUncaughtExceptionHandler { _, e ->
|
Thread.setDefaultUncaughtExceptionHandler { _, e ->
|
||||||
@@ -45,52 +36,56 @@ open class App() : Application() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun attachBaseContext(base: Context) {
|
override fun attachBaseContext(context: Context) {
|
||||||
// Basic setup
|
Shell.setDefaultBuilder(Shell.Builder.create()
|
||||||
if (BuildConfig.DEBUG)
|
.setFlags(Shell.FLAG_MOUNT_MASTER)
|
||||||
MultiDex.install(base)
|
.setInitializers(ShellInit::class.java)
|
||||||
|
.setTimeout(2))
|
||||||
|
Shell.EXECUTOR = DispatcherExecutor(Dispatchers.IO)
|
||||||
|
|
||||||
// Some context magic
|
// Get the actual ContextImpl
|
||||||
val app: Application
|
val app: Application
|
||||||
val impl: Context
|
val base: Context
|
||||||
if (base is Application) {
|
if (context is Application) {
|
||||||
app = base
|
app = context
|
||||||
impl = base.baseContext
|
base = context.baseContext
|
||||||
} else {
|
} else {
|
||||||
app = this
|
app = this
|
||||||
impl = base
|
base = context
|
||||||
}
|
}
|
||||||
val wrapped = impl.wrap()
|
super.attachBaseContext(base)
|
||||||
super.attachBaseContext(wrapped)
|
ServiceLocator.context = base
|
||||||
|
|
||||||
val info = base.applicationInfo
|
refreshLocale()
|
||||||
val libDir = runCatching {
|
AppApkPath = if (isRunningAsStub) {
|
||||||
info.javaClass.getDeclaredField("secondaryNativeLibraryDir").get(info) as String?
|
DynAPK.current(base).path
|
||||||
}.getOrNull() ?: info.nativeLibraryDir
|
} else {
|
||||||
Const.NATIVE_LIB_DIR = File(libDir)
|
base.packageResourcePath
|
||||||
|
|
||||||
// Normal startup
|
|
||||||
startKoin {
|
|
||||||
androidContext(wrapped)
|
|
||||||
modules(koinModules)
|
|
||||||
}
|
}
|
||||||
AssetHack.init(impl)
|
|
||||||
|
base.resources.patch()
|
||||||
app.registerActivityLifecycleCallbacks(ForegroundTracker)
|
app.registerActivityLifecycleCallbacks(ForegroundTracker)
|
||||||
WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is required as some platforms expect ContextImpl
|
override fun onCreate() {
|
||||||
override fun getBaseContext(): Context {
|
super.onCreate()
|
||||||
return super.getBaseContext().unwrap()
|
RootRegistry.bindTask = RootService.createBindTask(
|
||||||
|
intent<RootRegistry>(),
|
||||||
|
UiThreadHandler.executor,
|
||||||
|
RootRegistry.Connection
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
resources.updateConfig(newConfig)
|
if (resources.configuration.diff(newConfig) != 0) {
|
||||||
|
resources.setConfig(newConfig)
|
||||||
|
}
|
||||||
if (!isRunningAsStub)
|
if (!isRunningAsStub)
|
||||||
super.onConfigurationChanged(newConfig)
|
super.onConfigurationChanged(newConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
object ForegroundTracker : Application.ActivityLifecycleCallbacks {
|
object ForegroundTracker : Application.ActivityLifecycleCallbacks {
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
|
|||||||
@@ -1,20 +1,16 @@
|
|||||||
package com.topjohnwu.magisk.core
|
package com.topjohnwu.magisk.core
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.util.Xml
|
import android.util.Xml
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import com.topjohnwu.magisk.BuildConfig
|
import com.topjohnwu.magisk.BuildConfig
|
||||||
import com.topjohnwu.magisk.core.magiskdb.SettingsDao
|
|
||||||
import com.topjohnwu.magisk.core.magiskdb.StringDao
|
|
||||||
import com.topjohnwu.magisk.core.utils.BiometricHelper
|
|
||||||
import com.topjohnwu.magisk.core.utils.refreshLocale
|
import com.topjohnwu.magisk.core.utils.refreshLocale
|
||||||
import com.topjohnwu.magisk.data.preference.PreferenceModel
|
import com.topjohnwu.magisk.data.preference.PreferenceModel
|
||||||
|
import com.topjohnwu.magisk.data.repository.DBBoolSettingsNoWrite
|
||||||
import com.topjohnwu.magisk.data.repository.DBConfig
|
import com.topjohnwu.magisk.data.repository.DBConfig
|
||||||
import com.topjohnwu.magisk.di.Protected
|
import com.topjohnwu.magisk.di.ServiceLocator
|
||||||
import com.topjohnwu.magisk.ktx.inject
|
|
||||||
import com.topjohnwu.magisk.ui.theme.Theme
|
import com.topjohnwu.magisk.ui.theme.Theme
|
||||||
import org.xmlpull.v1.XmlPullParser
|
import org.xmlpull.v1.XmlPullParser
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -22,9 +18,9 @@ import java.io.InputStream
|
|||||||
|
|
||||||
object Config : PreferenceModel, DBConfig {
|
object Config : PreferenceModel, DBConfig {
|
||||||
|
|
||||||
override val stringDao: StringDao by inject()
|
override val stringDB get() = ServiceLocator.stringDB
|
||||||
override val settingsDao: SettingsDao by inject()
|
override val settingsDB get() = ServiceLocator.settingsDB
|
||||||
override val context: Context by inject(Protected)
|
override val context get() = ServiceLocator.deContext
|
||||||
|
|
||||||
@get:SuppressLint("ApplySharedPref")
|
@get:SuppressLint("ApplySharedPref")
|
||||||
val prefsFile: File get() {
|
val prefsFile: File get() {
|
||||||
@@ -41,6 +37,8 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
const val SU_MULTIUSER_MODE = "multiuser_mode"
|
const val SU_MULTIUSER_MODE = "multiuser_mode"
|
||||||
const val SU_MNT_NS = "mnt_ns"
|
const val SU_MNT_NS = "mnt_ns"
|
||||||
const val SU_BIOMETRIC = "su_biometric"
|
const val SU_BIOMETRIC = "su_biometric"
|
||||||
|
const val ZYGISK = "zygisk"
|
||||||
|
const val DENYLIST = "denylist"
|
||||||
const val SU_MANAGER = "requester"
|
const val SU_MANAGER = "requester"
|
||||||
const val KEYSTORE = "keystore"
|
const val KEYSTORE = "keystore"
|
||||||
|
|
||||||
@@ -63,9 +61,6 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
const val BOOT_ID = "boot_id"
|
const val BOOT_ID = "boot_id"
|
||||||
const val ASKED_HOME = "asked_home"
|
const val ASKED_HOME = "asked_home"
|
||||||
const val DOH = "doh"
|
const val DOH = "doh"
|
||||||
|
|
||||||
// system state
|
|
||||||
const val MAGISKHIDE = "magiskhide"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Value {
|
object Value {
|
||||||
@@ -115,9 +110,10 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
else
|
else
|
||||||
Value.DEFAULT_CHANNEL
|
Value.DEFAULT_CHANNEL
|
||||||
|
|
||||||
@JvmStatic var keepVerity = false
|
@JvmField var keepVerity = false
|
||||||
@JvmStatic var keepEnc = false
|
@JvmField var keepEnc = false
|
||||||
@JvmStatic var recovery = false
|
@JvmField var patchVbmeta = false
|
||||||
|
@JvmField var recovery = false
|
||||||
|
|
||||||
var bootId by preference(Key.BOOT_ID, "")
|
var bootId by preference(Key.BOOT_ID, "")
|
||||||
var askedHome by preference(Key.ASKED_HOME, false)
|
var askedHome by preference(Key.ASKED_HOME, false)
|
||||||
@@ -137,7 +133,6 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
var suTapjack by preference(Key.SU_TAPJACK, true)
|
var suTapjack by preference(Key.SU_TAPJACK, true)
|
||||||
var checkUpdate by preference(Key.CHECK_UPDATES, true)
|
var checkUpdate by preference(Key.CHECK_UPDATES, true)
|
||||||
var doh by preference(Key.DOH, false)
|
var doh by preference(Key.DOH, false)
|
||||||
var magiskHide by preference(Key.MAGISKHIDE, true)
|
|
||||||
var showSystemApp by preference(Key.SHOW_SYSTEM_APP, false)
|
var showSystemApp by preference(Key.SHOW_SYSTEM_APP, false)
|
||||||
|
|
||||||
var customChannelUrl by preference(Key.CUSTOM_CHANNEL, "")
|
var customChannelUrl by preference(Key.CUSTOM_CHANNEL, "")
|
||||||
@@ -153,6 +148,8 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
var suMntNamespaceMode by dbSettings(Key.SU_MNT_NS, Value.NAMESPACE_MODE_REQUESTER)
|
var suMntNamespaceMode by dbSettings(Key.SU_MNT_NS, Value.NAMESPACE_MODE_REQUESTER)
|
||||||
var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY)
|
var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY)
|
||||||
var suBiometric by dbSettings(Key.SU_BIOMETRIC, false)
|
var suBiometric by dbSettings(Key.SU_BIOMETRIC, false)
|
||||||
|
var zygisk by dbSettings(Key.ZYGISK, false)
|
||||||
|
var denyList by DBBoolSettingsNoWrite(Key.DENYLIST, false)
|
||||||
var suManager by dbStrings(Key.SU_MANAGER, "", true)
|
var suManager by dbStrings(Key.SU_MANAGER, "", true)
|
||||||
var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true)
|
var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true)
|
||||||
|
|
||||||
@@ -177,12 +174,6 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
else if (it.toInt() > Value.CANARY_CHANNEL)
|
else if (it.toInt() > Value.CANARY_CHANNEL)
|
||||||
putString(Key.UPDATE_CHANNEL, Value.CANARY_CHANNEL.toString())
|
putString(Key.UPDATE_CHANNEL, Value.CANARY_CHANNEL.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write database configs
|
|
||||||
putString(Key.ROOT_ACCESS, rootMode.toString())
|
|
||||||
putString(Key.SU_MNT_NS, suMntNamespaceMode.toString())
|
|
||||||
putString(Key.SU_MULTIUSER_MODE, suMultiuserMode.toString())
|
|
||||||
putBoolean(Key.SU_BIOMETRIC, BiometricHelper.isEnabled)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user