mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-13 10:37:48 +00:00
Compare commits
334 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9476e7282d | ||
![]() |
251c3c3e0e | ||
![]() |
cd0eca20b0 | ||
![]() |
6839cb9ab2 | ||
![]() |
d11a3397d8 | ||
![]() |
975120d6a6 | ||
![]() |
e489b3b6dd | ||
![]() |
589a270b8d | ||
![]() |
7961be5cfa | ||
![]() |
959430e030 | ||
![]() |
2923c8ccd1 | ||
![]() |
7df4a9d74f | ||
![]() |
bf4ed295da | ||
![]() |
a5fca960dc | ||
![]() |
f99912b9db | ||
![]() |
a54bdb54e4 | ||
![]() |
cd9851a1fe | ||
![]() |
9ca469898c | ||
![]() |
0665549473 | ||
![]() |
9d7a14b335 | ||
![]() |
62e29fee74 | ||
![]() |
e472db552b | ||
![]() |
466e4bd4e1 | ||
![]() |
4cf525c588 | ||
![]() |
c8aec2510d | ||
![]() |
ccbfe0e66e | ||
![]() |
23ea28de6f | ||
![]() |
55c3ee3a6f | ||
![]() |
2a42ca2b8f | ||
![]() |
a897e82fa4 | ||
![]() |
ffa15831d3 | ||
![]() |
a344ebf28c | ||
![]() |
78f7fa348e | ||
![]() |
d8c448b99d | ||
![]() |
d4b83b6a44 | ||
![]() |
e5d36d1d24 | ||
![]() |
ff18cb8e70 | ||
![]() |
37a9724a54 | ||
![]() |
d660401063 | ||
![]() |
88541d6f49 | ||
![]() |
ecd6129fe5 | ||
![]() |
6dfe9df9e2 | ||
![]() |
e81de7ec36 | ||
![]() |
c78da1ce24 | ||
![]() |
7b2d40987c | ||
![]() |
3a37e8c9c5 | ||
![]() |
58b405bce1 | ||
![]() |
810174ef73 | ||
![]() |
690a5ac033 | ||
![]() |
89aad31f7e | ||
![]() |
7124db98e3 | ||
![]() |
0860e859f7 | ||
![]() |
04008949b8 | ||
![]() |
39f2940bd1 | ||
![]() |
1460317ebd | ||
![]() |
12340c9bd5 | ||
![]() |
c4590fe2ba | ||
![]() |
b36066bbcd | ||
![]() |
65d1c5827c | ||
![]() |
1d675c8b2e | ||
![]() |
0b494ed7df | ||
![]() |
48d9fc24eb | ||
![]() |
83426f7f36 | ||
![]() |
0e86d4dbcb | ||
![]() |
5e050d7456 | ||
![]() |
898580bf90 | ||
![]() |
86621a4c46 | ||
![]() |
a834e72b71 | ||
![]() |
d8cf42af16 | ||
![]() |
8c79d66b7b | ||
![]() |
fada8b148a | ||
![]() |
dc0acea47c | ||
![]() |
78d1200608 | ||
![]() |
527bbc0368 | ||
![]() |
4c89c7e2b3 | ||
![]() |
adbea7e313 | ||
![]() |
76962f965e | ||
![]() |
a4b8c5e46b | ||
![]() |
83c707439c | ||
![]() |
25dd6121f4 | ||
![]() |
67f35ad027 | ||
![]() |
0c4b8afbc5 | ||
![]() |
34b30d7ce1 | ||
![]() |
2215088973 | ||
![]() |
8b7fb6cdde | ||
![]() |
94c7dbedf2 | ||
![]() |
b1dc47a047 | ||
![]() |
62b1310d97 | ||
![]() |
0a86916d3a | ||
![]() |
9907ce57aa | ||
![]() |
b92626cacc | ||
![]() |
5a762f0a8e | ||
![]() |
5dd7a7d804 | ||
![]() |
7831f40691 | ||
![]() |
4f4b1ff885 | ||
![]() |
97901979dd | ||
![]() |
287316842c | ||
![]() |
608786e8f3 | ||
![]() |
9684a35cab | ||
![]() |
e3e4202954 | ||
![]() |
23c2054d46 | ||
![]() |
a20a2a8fa0 | ||
![]() |
a2896be4a6 | ||
![]() |
e9220a28d9 | ||
![]() |
cf12087e21 | ||
![]() |
00c1b36837 | ||
![]() |
03e034795d | ||
![]() |
79c0fafe43 | ||
![]() |
d499819ba0 | ||
![]() |
86da917174 | ||
![]() |
30bd7d6555 | ||
![]() |
e5a12f0f5f | ||
![]() |
c85a8434c6 | ||
![]() |
427a1ca4e5 | ||
![]() |
22884e173a | ||
![]() |
d1829308e9 | ||
![]() |
73840f8721 | ||
![]() |
c7d1af9805 | ||
![]() |
4ad26d3dfb | ||
![]() |
0c70b7670c | ||
![]() |
f44d044095 | ||
![]() |
5c1cb13472 | ||
![]() |
3327fc668e | ||
![]() |
610945ac54 | ||
![]() |
ddf5474917 | ||
![]() |
6ba1685ade | ||
![]() |
e02b5f7868 | ||
![]() |
ab2e5d1e7e | ||
![]() |
f3fef7bfe4 | ||
![]() |
c34c7838bb | ||
![]() |
c8a16b0e0c | ||
![]() |
14f9ed91a1 | ||
![]() |
7a207d4ccf | ||
![]() |
92a42d901f | ||
![]() |
084d89fcce | ||
![]() |
55b036c071 | ||
![]() |
30e79310ab | ||
![]() |
f063fa5054 | ||
![]() |
7bd901273c | ||
![]() |
c1e061603b | ||
![]() |
cb08504fe5 | ||
![]() |
c0a1fb77be | ||
![]() |
4864c1112a | ||
![]() |
9ddeab034b | ||
![]() |
c4847ed288 | ||
![]() |
b8f1523fb2 | ||
![]() |
fb7fa8a6b3 | ||
![]() |
9c7d359093 | ||
![]() |
eb54bc1fd7 | ||
![]() |
d4a0286e13 | ||
![]() |
83e66767ff | ||
![]() |
7dc010749b | ||
![]() |
8e8d013b1b | ||
![]() |
bba0373808 | ||
![]() |
1fa318dc8c | ||
![]() |
6edc5e2037 | ||
![]() |
1523ed9f78 | ||
![]() |
8e604d2ab8 | ||
![]() |
2aba7247a9 | ||
![]() |
e66fe8533e | ||
![]() |
b03fbb3917 | ||
![]() |
c2ece62e4c | ||
![]() |
8c972dcf34 | ||
![]() |
50af14f2a3 | ||
![]() |
e0a356b319 | ||
![]() |
c09a792958 | ||
![]() |
0bbfe7f44d | ||
![]() |
a396abf565 | ||
![]() |
1e3edb8883 | ||
![]() |
3b8b61bf35 | ||
![]() |
6f90456036 | ||
![]() |
f56fd4e215 | ||
![]() |
aa35aac5d5 | ||
![]() |
1f162b819d | ||
![]() |
52ef1d1cb2 | ||
![]() |
f14e3a89cc | ||
![]() |
95d3eac2e0 | ||
![]() |
8e73536e02 | ||
![]() |
12a0870bc9 | ||
![]() |
6ff82c4e86 | ||
![]() |
c64de35375 | ||
![]() |
ee5283f4e8 | ||
![]() |
bd0e954fea | ||
![]() |
675471a49e | ||
![]() |
c90e73ccec | ||
![]() |
a43c1267d8 | ||
![]() |
e8958c6b5c | ||
![]() |
e8a3bf82c6 | ||
![]() |
27fd79176a | ||
![]() |
28d86a3454 | ||
![]() |
c6c1a17ae6 | ||
![]() |
2b47d47215 | ||
![]() |
0e82df9e10 | ||
![]() |
893821ad88 | ||
![]() |
6b80fbfa99 | ||
![]() |
8c3c7d0194 | ||
![]() |
b94a3d9f2f | ||
![]() |
442d0b5ddc | ||
![]() |
494615d9a0 | ||
![]() |
afbfb81837 | ||
![]() |
3ed4e258a3 | ||
![]() |
dddd41c95b | ||
![]() |
5f2ca81e86 | ||
![]() |
c9eac0c438 | ||
![]() |
b6b34f7612 | ||
![]() |
e55c413261 | ||
![]() |
0399cde50a | ||
![]() |
019eb03823 | ||
![]() |
363410e1c0 | ||
![]() |
fc2ef21660 | ||
![]() |
18cb659ff3 | ||
![]() |
63231d97ce | ||
![]() |
9ac81a8a25 | ||
![]() |
79af2787ae | ||
![]() |
f5f9b285c0 | ||
![]() |
6c05f2ae85 | ||
![]() |
29043e1684 | ||
![]() |
b73d4a7022 | ||
![]() |
ad95e8951b | ||
![]() |
bf591fca12 | ||
![]() |
dcf027884d | ||
![]() |
584f3820fe | ||
![]() |
3c7c46307a | ||
![]() |
4d80361805 | ||
![]() |
9a74e19117 | ||
![]() |
b1e17706a4 | ||
![]() |
caad129d69 | ||
![]() |
da58571ce5 | ||
![]() |
2aa7f1c094 | ||
![]() |
823e31a91b | ||
![]() |
fb926ae302 | ||
![]() |
e0489eeffd | ||
![]() |
dc9d5a4cac | ||
![]() |
143743d0b0 | ||
![]() |
563f0d5ad5 | ||
![]() |
c99f4a591b | ||
![]() |
449204e380 | ||
![]() |
a85c4c6528 | ||
![]() |
d203a6fff6 | ||
![]() |
6c612d66d7 | ||
![]() |
540253a55b | ||
![]() |
15b7c4ccd1 | ||
![]() |
442d5335ea | ||
![]() |
8a80eea597 | ||
![]() |
5e35703091 | ||
![]() |
b7ca73f431 | ||
![]() |
a14fc90f07 | ||
![]() |
c913f7ec74 | ||
![]() |
7f6c9e8411 | ||
![]() |
bb02ea3a20 | ||
![]() |
3981c9665e | ||
![]() |
88628fdf3c | ||
![]() |
0469817781 | ||
![]() |
a786801141 | ||
![]() |
ab86732c89 | ||
![]() |
59622d1688 | ||
![]() |
58a25a3e2b | ||
![]() |
15dca29a87 | ||
![]() |
46980819c0 | ||
![]() |
4fb6a7268c | ||
![]() |
c05e963f37 | ||
![]() |
7f7f625864 | ||
![]() |
b25aa8295a | ||
![]() |
15a605765c | ||
![]() |
b575c95710 | ||
![]() |
a48a9c858a | ||
![]() |
0d8d6290a3 | ||
![]() |
4dcd733ddd | ||
![]() |
b62835cbeb | ||
![]() |
6ea740b5ab | ||
![]() |
7ab98dd5ac | ||
![]() |
fc8b3400fc | ||
![]() |
54428ba415 | ||
![]() |
95d1e69d8e | ||
![]() |
a0f13ab49f | ||
![]() |
c3e8405020 | ||
![]() |
a93593ea66 | ||
![]() |
23eff70883 | ||
![]() |
110dd4a8b9 | ||
![]() |
d9c2bffc9f | ||
![]() |
049db49dc8 | ||
![]() |
7c1d2ec61e | ||
![]() |
a1b2830c06 | ||
![]() |
82d1d19267 | ||
![]() |
4d4195c02d | ||
![]() |
5637a258fc | ||
![]() |
ee6810f417 | ||
![]() |
7098248c64 | ||
![]() |
0d31d356ef | ||
![]() |
b782e7dcb7 | ||
![]() |
a4671b4698 | ||
![]() |
7edd8be169 | ||
![]() |
24650eefe4 | ||
![]() |
8e1a44e7eb | ||
![]() |
2722875190 | ||
![]() |
3ca6d06f69 | ||
![]() |
10e47248de | ||
![]() |
e73ff679ac | ||
![]() |
53e401fa2d | ||
![]() |
d2768357da | ||
![]() |
a6c2ba7c1e | ||
![]() |
aae5b466fb | ||
![]() |
2b7be8b949 | ||
![]() |
b6511a510d | ||
![]() |
704541aef2 | ||
![]() |
005560a4c5 | ||
![]() |
231a5d1853 | ||
![]() |
9e2b59060d | ||
![]() |
08ea937f7c | ||
![]() |
2baedf74d1 | ||
![]() |
32faa4ced6 | ||
![]() |
ccdb0b5d13 | ||
![]() |
8506b672ad | ||
![]() |
ce2e33bb20 | ||
![]() |
6707b72260 | ||
![]() |
5885b8c20d | ||
![]() |
820710c086 | ||
![]() |
51cf196bf7 | ||
![]() |
dadba44cf9 | ||
![]() |
2ce4a5543b | ||
![]() |
9112a3a4f5 | ||
![]() |
24615afda1 | ||
![]() |
c5778f398b | ||
![]() |
4eb4097b9b | ||
![]() |
c512496847 | ||
![]() |
506961a10d | ||
![]() |
3414415907 | ||
![]() |
dc2ae7cfd1 | ||
![]() |
2e86d21c29 | ||
![]() |
2654382c43 | ||
![]() |
9e26b73813 | ||
![]() |
10cd13bf80 | ||
![]() |
f10ee5f887 | ||
![]() |
47cc532d96 |
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -12,13 +12,11 @@
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
tools/** binary
|
||||
tools/rustup-wrapper/** -binary
|
||||
tools/elf-cleaner/** -binary
|
||||
*.jar binary
|
||||
*.exe binary
|
||||
*.apk binary
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.ttf binary
|
||||
|
||||
# Help GitHub detect languages
|
||||
native/jni/external/** linguist-vendored
|
||||
native/jni/systemproperties/** linguist-language=C++
|
||||
|
27
.github/actions/setup/action.yml
vendored
27
.github/actions/setup/action.yml
vendored
@@ -26,6 +26,15 @@ runs:
|
||||
|
||||
- name: Cache sccache
|
||||
uses: actions/cache@v4
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
with:
|
||||
path: .sccache
|
||||
key: sccache-${{ runner.os }}-${{ github.sha }}
|
||||
restore-keys: sccache-${{ runner.os }}-
|
||||
|
||||
- name: Restore sccache
|
||||
uses: actions/cache/restore@v4
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
with:
|
||||
path: .sccache
|
||||
key: sccache-${{ runner.os }}-${{ github.sha }}
|
||||
@@ -36,7 +45,7 @@ runs:
|
||||
env:
|
||||
SCCACHE_DIRECT: false
|
||||
SCCACHE_DIR: ${{ github.workspace }}/.sccache
|
||||
SCCACHE_CACHE_SIZE: 2G
|
||||
SCCACHE_CACHE_SIZE: ${{ inputs.is-asset-build == 'true' && '2G' || '300M' }}
|
||||
SCCACHE_IDLE_TIMEOUT: 0
|
||||
run: |
|
||||
bash $GITHUB_ACTION_PATH/sccache.sh
|
||||
@@ -55,18 +64,18 @@ runs:
|
||||
|
||||
- name: Cache Gradle dependencies
|
||||
uses: actions/cache@v4
|
||||
if: inputs.is-asset-build == 'true'
|
||||
if: ${{ inputs.is-asset-build == 'true' && github.event_name != 'pull_request' }}
|
||||
with:
|
||||
path: |
|
||||
.gradle/caches
|
||||
.gradle/wrapper
|
||||
!.gradle/caches/build-cache-*
|
||||
key: gradle-cache-${{ hashFiles('gradle/**') }}
|
||||
key: gradle-cache-${{ hashFiles('app/gradle/**') }}
|
||||
restore-keys: gradle-cache-
|
||||
|
||||
- name: Restore Gradle dependencies
|
||||
uses: actions/cache/restore@v4
|
||||
if: inputs.is-asset-build == 'false'
|
||||
if: ${{ inputs.is-asset-build == 'false' || github.event_name == 'pull_request' }}
|
||||
with:
|
||||
path: |
|
||||
.gradle/caches
|
||||
@@ -78,19 +87,17 @@ runs:
|
||||
|
||||
- name: Cache Gradle build cache
|
||||
uses: actions/cache@v4
|
||||
if: inputs.is-asset-build == 'true'
|
||||
if: ${{ inputs.is-asset-build == 'true' && github.event_name != 'pull_request' }}
|
||||
with:
|
||||
path: |
|
||||
.gradle/caches/build-cache-*
|
||||
path: .gradle/caches/build-cache-*
|
||||
key: gradle-build-cache-${{ github.sha }}
|
||||
restore-keys: gradle-build-cache-
|
||||
|
||||
- name: Restore Gradle build cache
|
||||
uses: actions/cache/restore@v4
|
||||
if: inputs.is-asset-build == 'false'
|
||||
if: ${{ inputs.is-asset-build == 'false' || github.event_name == 'pull_request' }}
|
||||
with:
|
||||
path: |
|
||||
.gradle/caches/build-cache-*
|
||||
path: .gradle/caches/build-cache-*
|
||||
key: gradle-build-cache-${{ github.sha }}
|
||||
restore-keys: gradle-build-cache-
|
||||
enableCrossOsArchive: true
|
||||
|
37
.github/workflows/build.yml
vendored
37
.github/workflows/build.yml
vendored
@@ -6,9 +6,7 @@ on:
|
||||
paths:
|
||||
- "app/**"
|
||||
- "native/**"
|
||||
- "buildSrc/**"
|
||||
- "build.py"
|
||||
- "gradle.properties"
|
||||
- ".github/workflows/build.yml"
|
||||
pull_request:
|
||||
branches: [master]
|
||||
@@ -17,7 +15,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build Magisk artifacts
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
@@ -38,7 +36,7 @@ jobs:
|
||||
run: ./build.py -v all
|
||||
|
||||
- name: Stop gradle daemon
|
||||
run: ./gradlew --stop
|
||||
run: ./app/gradlew --stop
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -60,7 +58,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-latest, ubuntu-latest]
|
||||
os: [windows-2025, ubuntu-24.04]
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v4
|
||||
@@ -74,20 +72,21 @@ jobs:
|
||||
run: python build.py -v -c .github/ci.prop all
|
||||
|
||||
- name: Stop gradle daemon
|
||||
run: ./gradlew --stop
|
||||
run: ./app/gradlew --stop
|
||||
|
||||
avd-test:
|
||||
name: Test API ${{ matrix.version }} (x86_64)
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
|
||||
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, "36.0-CANARY"]
|
||||
type: [""]
|
||||
include:
|
||||
- version: "Baklava"
|
||||
type: "google_apis"
|
||||
- version: "36.0-CANARY"
|
||||
type: "google_apis_ps16k"
|
||||
|
||||
steps:
|
||||
- name: Check out
|
||||
@@ -109,7 +108,7 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
AVD_TEST_LOG: 1
|
||||
run: scripts/avd_test.sh ${{ matrix.version }} ${{ matrix.type }}
|
||||
run: scripts/avd.sh test ${{ matrix.version }} ${{ matrix.type }}
|
||||
|
||||
- name: Upload logs on error
|
||||
if: ${{ failure() }}
|
||||
@@ -122,8 +121,9 @@ jobs:
|
||||
|
||||
avd-test-32:
|
||||
name: Test API ${{ matrix.version }} (x86)
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -150,7 +150,7 @@ jobs:
|
||||
env:
|
||||
FORCE_32_BIT: 1
|
||||
AVD_TEST_LOG: 1
|
||||
run: scripts/avd_test.sh ${{ matrix.version }}
|
||||
run: scripts/avd.sh test ${{ matrix.version }}
|
||||
|
||||
- name: Upload logs on error
|
||||
if: ${{ failure() }}
|
||||
@@ -161,20 +161,19 @@ jobs:
|
||||
kernel.log
|
||||
logcat.log
|
||||
|
||||
cf_test:
|
||||
cf-test:
|
||||
name: Test ${{ matrix.device }}
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
env:
|
||||
CF_HOME: /home/runner/aosp_cf_phone
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- branch: "aosp-main"
|
||||
device: "aosp_cf_x86_64_phone"
|
||||
- branch: "aosp-main-throttled"
|
||||
device: "aosp_cf_x86_64_phone_pgagnostic"
|
||||
- branch: "aosp-android-latest-release"
|
||||
device: "aosp_cf_x86_64_only_phone"
|
||||
|
||||
steps:
|
||||
- name: Check out
|
||||
@@ -193,7 +192,7 @@ jobs:
|
||||
|
||||
- name: Run Cuttlefish test
|
||||
timeout-minutes: 10
|
||||
run: su $USER -c 'scripts/cuttlefish.sh test'
|
||||
run: sudo -E -u $USER scripts/cuttlefish.sh test
|
||||
|
||||
- name: Upload logs on error
|
||||
if: ${{ failure() }}
|
||||
|
10
.gitignore
vendored
10
.gitignore
vendored
@@ -2,19 +2,13 @@ out
|
||||
*.zip
|
||||
*.jks
|
||||
*.apk
|
||||
*.log
|
||||
/config.prop
|
||||
/notes.md
|
||||
/update.sh
|
||||
/app/dict.txt
|
||||
|
||||
# Built binaries
|
||||
native/out
|
||||
|
||||
# Android Studio / Gradle
|
||||
# Android Studio
|
||||
*.iml
|
||||
.gradle
|
||||
.idea
|
||||
.kotlin
|
||||
/local.properties
|
||||
/build
|
||||
/captures
|
||||
|
12
.gitmodules
vendored
12
.gitmodules
vendored
@@ -4,21 +4,12 @@
|
||||
[submodule "lz4"]
|
||||
path = native/src/external/lz4
|
||||
url = https://github.com/lz4/lz4.git
|
||||
[submodule "bzip2"]
|
||||
path = native/src/external/bzip2
|
||||
url = https://github.com/nemequ/bzip2.git
|
||||
[submodule "xz"]
|
||||
path = native/src/external/xz
|
||||
url = https://github.com/xz-mirror/xz.git
|
||||
[submodule "libcxx"]
|
||||
path = native/src/external/libcxx
|
||||
url = https://github.com/topjohnwu/libcxx.git
|
||||
[submodule "zlib"]
|
||||
path = native/src/external/zlib
|
||||
url = https://android.googlesource.com/platform/external/zlib
|
||||
[submodule "zopfli"]
|
||||
path = native/src/external/zopfli
|
||||
url = https://github.com/google/zopfli.git
|
||||
[submodule "cxx-rs"]
|
||||
path = native/src/external/cxx-rs
|
||||
url = https://github.com/topjohnwu/cxx.git
|
||||
@@ -31,6 +22,3 @@
|
||||
[submodule "crt0"]
|
||||
path = native/src/external/crt0
|
||||
url = https://github.com/topjohnwu/crt0.git
|
||||
[submodule "termux-elf-cleaner"]
|
||||
path = tools/termux-elf-cleaner
|
||||
url = https://github.com/termux/termux-elf-cleaner.git
|
||||
|
@@ -16,13 +16,7 @@ Some highlight features:
|
||||
|
||||
## Downloads
|
||||
|
||||
[Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads.
|
||||
|
||||
Click the icon below to download Magisk apk.
|
||||
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/canary-28003)
|
||||
[Github](https://github.com/topjohnwu/Magisk/releases) is the only source where you can get official Magisk information and downloads.
|
||||
|
||||
## Useful Links
|
||||
|
||||
|
7
app/.gitignore
vendored
Normal file
7
app/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/dict.txt
|
||||
|
||||
# Gradle
|
||||
.gradle
|
||||
.kotlin
|
||||
/local.properties
|
||||
/build
|
@@ -35,7 +35,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":app:core"))
|
||||
implementation(project(":core"))
|
||||
coreLibraryDesugaring(libs.jdk.libs)
|
||||
|
||||
implementation(libs.indeterminate.checkbox)
|
||||
|
@@ -1,10 +1,13 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.view.KeyEvent
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDirections
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.navOptions
|
||||
import com.topjohnwu.magisk.utils.AccessibilityUtils
|
||||
|
||||
abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Binding>() {
|
||||
|
||||
@@ -31,7 +34,17 @@ abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Bindin
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun navigate(directions: NavDirections, navigation: NavController, cr: ContentResolver) {
|
||||
if (AccessibilityUtils.isAnimationEnabled(cr)) {
|
||||
navigation.navigate(directions)
|
||||
} else {
|
||||
navigation.navigate(directions, navOptions {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun NavDirections.navigate() {
|
||||
navigation.navigate(this)
|
||||
navigate(this, navigation, contentResolver)
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ import androidx.core.widget.ImageViewCompat
|
||||
import androidx.databinding.BindingAdapter
|
||||
import androidx.databinding.InverseBindingAdapter
|
||||
import androidx.databinding.InverseBindingListener
|
||||
import androidx.databinding.InverseMethod
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
@@ -33,9 +34,11 @@ import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.slider.Slider
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||
import com.topjohnwu.magisk.utils.TextHolder
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
import com.topjohnwu.widget.IndeterminateCheckBox
|
||||
@@ -306,3 +309,38 @@ fun TextView.setText(text: TextHolder) {
|
||||
fun Spinner.setAdapter(items: Array<Any>, layoutRes: Int) {
|
||||
adapter = ArrayAdapter(context, layoutRes, items)
|
||||
}
|
||||
|
||||
@BindingAdapter("labelFormatter")
|
||||
fun Slider.setLabelFormatter(formatter: (Float) -> Int) {
|
||||
setLabelFormatter { value -> resources.getString(formatter(value)) }
|
||||
}
|
||||
|
||||
@InverseBindingAdapter(attribute = "android:value")
|
||||
fun Slider.getValueBinding() = value
|
||||
|
||||
@BindingAdapter("android:valueAttrChanged")
|
||||
fun Slider.setListener(attrChange: InverseBindingListener) {
|
||||
addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
|
||||
override fun onStartTrackingTouch(slider: Slider) = Unit
|
||||
override fun onStopTrackingTouch(slider: Slider) = attrChange.onChange()
|
||||
})
|
||||
}
|
||||
|
||||
@InverseMethod("sliderValueToPolicy")
|
||||
fun policyToSliderValue(policy: Int): Float {
|
||||
return when (policy) {
|
||||
SuPolicy.DENY -> 1f
|
||||
SuPolicy.RESTRICT -> 2f
|
||||
SuPolicy.ALLOW -> 3f
|
||||
else -> 1f
|
||||
}
|
||||
}
|
||||
|
||||
fun sliderValueToPolicy(value: Float): Int {
|
||||
return when (value) {
|
||||
1f -> SuPolicy.DENY
|
||||
2f -> SuPolicy.RESTRICT
|
||||
3f -> SuPolicy.ALLOW
|
||||
else -> SuPolicy.DENY
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.dialog
|
||||
import com.topjohnwu.magisk.core.AppContext
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.R
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.download.DownloadEngine
|
||||
import com.topjohnwu.magisk.core.download.Subject
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
@@ -11,15 +10,10 @@ import java.io.File
|
||||
|
||||
class ManagerInstallDialog : MarkDownDialog() {
|
||||
|
||||
private val svc get() = ServiceLocator.networkService
|
||||
|
||||
override suspend fun getMarkdownText(): String {
|
||||
val text = svc.fetchString(Info.remote.magisk.note)
|
||||
val text = Info.update.note
|
||||
// Cache the changelog
|
||||
AppContext.cacheDir.listFiles { _, name -> name.endsWith(".md") }.orEmpty().forEach {
|
||||
it.delete()
|
||||
}
|
||||
File(AppContext.cacheDir, "${Info.remote.magisk.versionCode}.md").writeText(text)
|
||||
File(AppContext.cacheDir, "${Info.update.versionCode}.md").writeText(text)
|
||||
return text
|
||||
}
|
||||
|
||||
|
@@ -17,6 +17,8 @@ import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.download.DownloadEngine
|
||||
import com.topjohnwu.magisk.databinding.FragmentHomeMd2Binding
|
||||
import com.topjohnwu.magisk.core.R as CoreR
|
||||
import androidx.navigation.findNavController
|
||||
import com.topjohnwu.magisk.arch.NavigationActivity
|
||||
|
||||
class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
|
||||
|
||||
@@ -68,7 +70,13 @@ class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
|
||||
override fun onMenuItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_settings ->
|
||||
HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate()
|
||||
activity?.let {
|
||||
NavigationActivity.navigate(
|
||||
HomeFragmentDirections.actionHomeFragmentToSettingsFragment(),
|
||||
it.findNavController(R.id.main_nav_host),
|
||||
it.contentResolver,
|
||||
)
|
||||
}
|
||||
R.id.action_reboot -> activity?.let { RebootMenu.inflate(it).show() }
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
@@ -91,16 +91,15 @@ class HomeViewModel(
|
||||
|
||||
override suspend fun doLoadWork() {
|
||||
appState = State.LOADING
|
||||
Info.getRemote(svc)?.apply {
|
||||
Info.fetchUpdate(svc)?.apply {
|
||||
appState = when {
|
||||
BuildConfig.APP_VERSION_CODE < magisk.versionCode -> State.OUTDATED
|
||||
BuildConfig.APP_VERSION_CODE < versionCode -> State.OUTDATED
|
||||
else -> State.UP_TO_DATE
|
||||
}
|
||||
|
||||
val isDebug = Config.updateChannel == Config.Value.DEBUG_CHANNEL
|
||||
managerRemoteVersion =
|
||||
("${magisk.version} (${magisk.versionCode})" +
|
||||
if (isDebug) " (D)" else "").asText()
|
||||
("$version (${versionCode})" + if (isDebug) " (D)" else "").asText()
|
||||
} ?: run {
|
||||
appState = State.INVALID
|
||||
managerRemoteVersion = CoreR.string.not_available.asText()
|
||||
|
@@ -41,7 +41,7 @@ object RebootMenu {
|
||||
activity.getSystemService<PowerManager>()?.isRebootingUserspaceSupported == true) {
|
||||
menu.menu.findItem(R.id.action_reboot_userspace).isVisible = true
|
||||
}
|
||||
if (Const.Version.isCanary()) {
|
||||
if (Const.Version.atLeast_28_0()) {
|
||||
menu.menu.findItem(R.id.action_reboot_safe_mode).isChecked = Config.bootloop >= 2
|
||||
} else {
|
||||
menu.menu.findItem(R.id.action_reboot_safe_mode).isVisible = false
|
||||
|
@@ -14,9 +14,8 @@ import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.core.AppContext
|
||||
import com.topjohnwu.magisk.core.BuildConfig
|
||||
import com.topjohnwu.magisk.core.BuildConfig.APP_VERSION_CODE
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.base.ContentResultCallback
|
||||
import com.topjohnwu.magisk.core.ktx.toast
|
||||
@@ -70,17 +69,16 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val file = File(AppContext.cacheDir, "${BuildConfig.APP_VERSION_CODE}.md")
|
||||
val text = when {
|
||||
file.exists() -> file.readText()
|
||||
Const.Url.CHANGELOG_URL.isEmpty() -> ""
|
||||
val noteFile = File(AppContext.cacheDir, "${APP_VERSION_CODE}.md")
|
||||
val noteText = when {
|
||||
noteFile.exists() -> noteFile.readText()
|
||||
else -> {
|
||||
val str = svc.fetchString(Const.Url.CHANGELOG_URL)
|
||||
file.writeText(str)
|
||||
str
|
||||
val note = svc.fetchUpdate(APP_VERSION_CODE).note
|
||||
noteFile.writeText(note)
|
||||
note
|
||||
}
|
||||
}
|
||||
val spanned = markwon.toMarkdown(text)
|
||||
val spanned = markwon.toMarkdown(noteText)
|
||||
withContext(Dispatchers.Main) {
|
||||
notes = spanned
|
||||
}
|
||||
@@ -100,13 +98,15 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
|
||||
}
|
||||
|
||||
override fun onSaveState(state: Bundle) {
|
||||
state.putParcelable(INSTALL_STATE_KEY, InstallState(
|
||||
methodId,
|
||||
step,
|
||||
Config.keepVerity,
|
||||
Config.keepEnc,
|
||||
Config.recovery
|
||||
))
|
||||
state.putParcelable(
|
||||
INSTALL_STATE_KEY, InstallState(
|
||||
methodId,
|
||||
step,
|
||||
Config.keepVerity,
|
||||
Config.keepEnc,
|
||||
Config.recovery
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onRestoreState(state: Bundle) {
|
||||
@@ -124,6 +124,7 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
|
||||
override fun onActivityLaunch() {
|
||||
AppContext.toast(CoreR.string.patch_file_msg, Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
override fun onActivityResult(result: Uri) {
|
||||
uri.value = result
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.HorizontalScrollView
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.isVisible
|
||||
import com.topjohnwu.magisk.R
|
||||
@@ -12,6 +13,7 @@ import com.topjohnwu.magisk.arch.BaseFragment
|
||||
import com.topjohnwu.magisk.arch.viewModel
|
||||
import com.topjohnwu.magisk.databinding.FragmentLogMd2Binding
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.utils.AccessibilityUtils
|
||||
import com.topjohnwu.magisk.utils.MotionRevealHelper
|
||||
import rikka.recyclerview.addEdgeSpacing
|
||||
import rikka.recyclerview.addItemSpacing
|
||||
@@ -56,6 +58,11 @@ class LogFragment : BaseFragment<FragmentLogMd2Binding>(), MenuProvider {
|
||||
addItemSpacing(R.dimen.l1, R.dimen.l_50, R.dimen.l1)
|
||||
fixEdgeEffect()
|
||||
}
|
||||
|
||||
if (!AccessibilityUtils.isAnimationEnabled(requireContext().contentResolver)) {
|
||||
val scrollView = view.findViewById<HorizontalScrollView>(R.id.log_scroll_magisk)
|
||||
scrollView.setOverScrollMode(View.OVER_SCROLL_NEVER)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -147,19 +147,11 @@ object UpdateChannel : BaseSettingsItem.Selector() {
|
||||
get() = Config.updateChannel
|
||||
set(value) {
|
||||
Config.updateChannel = value
|
||||
Info.remote = Info.EMPTY_REMOTE
|
||||
Info.resetUpdate()
|
||||
}
|
||||
|
||||
override val title = CoreR.string.settings_update_channel_title.asText()
|
||||
|
||||
override val entryRes = CoreR.array.update_channel
|
||||
override fun entries(res: Resources): Array<String> {
|
||||
return super.entries(res).let {
|
||||
if (!Const.APP_IS_CANARY && !BuildConfig.DEBUG)
|
||||
it.copyOfRange(0, Config.Value.CANARY_CHANNEL)
|
||||
else it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object UpdateChannelUrl : BaseSettingsItem.Input() {
|
||||
@@ -169,7 +161,7 @@ object UpdateChannelUrl : BaseSettingsItem.Input() {
|
||||
get() = Config.customChannelUrl
|
||||
set(value) {
|
||||
Config.customChannelUrl = value
|
||||
Info.remote = Info.EMPTY_REMOTE
|
||||
Info.resetUpdate()
|
||||
notifyPropertyChanged(BR.description)
|
||||
}
|
||||
|
||||
@@ -330,6 +322,12 @@ object Reauthenticate : BaseSettingsItem.Toggle() {
|
||||
override var value by Config::suReAuth
|
||||
|
||||
override fun refresh() {
|
||||
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Info.showSuperUser
|
||||
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O
|
||||
}
|
||||
}
|
||||
|
||||
object Restrict : BaseSettingsItem.Toggle() {
|
||||
override val title = CoreR.string.settings_su_restrict_title.asText()
|
||||
override val description = CoreR.string.settings_su_restrict_summary.asText()
|
||||
override var value by Config::suRestrict
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.core.AppContext
|
||||
import com.topjohnwu.magisk.core.BuildConfig
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.R
|
||||
@@ -21,11 +22,11 @@ import com.topjohnwu.magisk.core.ktx.activity
|
||||
import com.topjohnwu.magisk.core.ktx.toast
|
||||
import com.topjohnwu.magisk.core.tasks.AppMigration
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.magisk.databinding.bindExtra
|
||||
import com.topjohnwu.magisk.events.AddHomeIconEvent
|
||||
import com.topjohnwu.magisk.events.AuthEvent
|
||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
@@ -82,6 +83,9 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
// Can hide overlay windows on 12.0+
|
||||
list.remove(Tapjack)
|
||||
}
|
||||
if (Const.Version.atLeast_30_1()) {
|
||||
list.add(Restrict)
|
||||
}
|
||||
}
|
||||
|
||||
return list
|
||||
@@ -92,7 +96,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
DownloadPath -> withExternalRW(doAction)
|
||||
UpdateChecker -> withPostNotificationPermission(doAction)
|
||||
Authentication -> AuthEvent(doAction).publish()
|
||||
Hide, Restore -> withInstallPermission(doAction)
|
||||
AutomaticResponse -> if (Config.suAuth) AuthEvent(doAction).publish() else doAction()
|
||||
else -> doAction()
|
||||
}
|
||||
}
|
||||
@@ -126,7 +130,8 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
}
|
||||
|
||||
private fun createHosts() {
|
||||
Shell.cmd("add_hosts_module").submit {
|
||||
viewModelScope.launch {
|
||||
RootUtils.addSystemlessHosts()
|
||||
AppContext.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
|
@@ -4,11 +4,13 @@ import android.graphics.drawable.Drawable
|
||||
import androidx.databinding.Bindable
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||
import com.topjohnwu.magisk.databinding.DiffItem
|
||||
import com.topjohnwu.magisk.databinding.ItemWrapper
|
||||
import com.topjohnwu.magisk.databinding.ObservableRvItem
|
||||
import com.topjohnwu.magisk.databinding.set
|
||||
import com.topjohnwu.magisk.core.R as CoreR
|
||||
|
||||
class PolicyRvItem(
|
||||
private val viewModel: SuperuserViewModel,
|
||||
@@ -33,14 +35,34 @@ class PolicyRvItem(
|
||||
var isExpanded = false
|
||||
set(value) = set(value, field, { field = it }, BR.expanded)
|
||||
|
||||
val showSlider = Config.suRestrict || item.policy == SuPolicy.RESTRICT
|
||||
|
||||
@get:Bindable
|
||||
var isEnabled
|
||||
get() = item.policy == SuPolicy.ALLOW
|
||||
get() = item.policy >= SuPolicy.ALLOW
|
||||
set(value) = setImpl(value, isEnabled) {
|
||||
notifyPropertyChanged(BR.enabled)
|
||||
viewModel.togglePolicy(this, value)
|
||||
viewModel.updatePolicy(this, if (it) SuPolicy.ALLOW else SuPolicy.DENY)
|
||||
}
|
||||
|
||||
@get:Bindable
|
||||
var sliderValue
|
||||
get() = item.policy
|
||||
set(value) = setImpl(value, sliderValue) {
|
||||
notifyPropertyChanged(BR.sliderValue)
|
||||
notifyPropertyChanged(BR.enabled)
|
||||
viewModel.updatePolicy(this, it)
|
||||
}
|
||||
|
||||
val sliderValueToPolicyString: (Float) -> Int = { value ->
|
||||
when (value.toInt()) {
|
||||
1 -> CoreR.string.deny
|
||||
2 -> CoreR.string.restrict
|
||||
3 -> CoreR.string.grant
|
||||
else -> CoreR.string.deny
|
||||
}
|
||||
}
|
||||
|
||||
@get:Bindable
|
||||
var shouldNotify
|
||||
get() = item.notification
|
||||
|
@@ -156,15 +156,16 @@ class SuperuserViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun togglePolicy(item: PolicyRvItem, enable: Boolean) {
|
||||
fun updatePolicy(item: PolicyRvItem, policy: Int) {
|
||||
val items = itemsPolicies.filter { it.item.uid == item.item.uid }
|
||||
fun updateState() {
|
||||
viewModelScope.launch {
|
||||
val res = if (enable) R.string.su_snack_grant else R.string.su_snack_deny
|
||||
item.item.policy = if (enable) SuPolicy.ALLOW else SuPolicy.DENY
|
||||
val res = if (policy >= SuPolicy.ALLOW) R.string.su_snack_grant else R.string.su_snack_deny
|
||||
item.item.policy = policy
|
||||
db.update(item.item)
|
||||
items.forEach {
|
||||
it.notifyPropertyChanged(BR.enabled)
|
||||
it.notifyPropertyChanged(BR.sliderValue)
|
||||
}
|
||||
SnackbarEvent(res.asText(item.appName)).publish()
|
||||
}
|
||||
|
@@ -0,0 +1,14 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.provider.Settings
|
||||
|
||||
class AccessibilityUtils {
|
||||
companion object {
|
||||
fun isAnimationEnabled(cr: ContentResolver): Boolean {
|
||||
return !(Settings.Global.getFloat(cr, Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f) == 0.0f
|
||||
&& Settings.Global.getFloat(cr, Settings.Global.TRANSITION_ANIMATION_SCALE, 1.0f) == 0.0f
|
||||
&& Settings.Global.getFloat(cr, Settings.Global.WINDOW_ANIMATION_SCALE, 1.0f) == 0.0f)
|
||||
}
|
||||
}
|
||||
}
|
@@ -16,6 +16,7 @@
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/log_scroll_magisk"
|
||||
gone="@{viewModel.loading}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@@ -25,7 +25,7 @@
|
||||
|
||||
<include
|
||||
android:id="@+id/log_track_container"
|
||||
bullet="@{item.log.action == 2 ? R.drawable.ic_check_md2 : R.drawable.ic_close_md2}"
|
||||
bullet="@{item.log.action >= 2 ? R.drawable.ic_check_md2 : R.drawable.ic_close_md2}"
|
||||
isBottom="@{item.isBottom}"
|
||||
isSelected="@{item.log.action != 2}"
|
||||
isTop="@{item.isTop}"
|
||||
|
@@ -5,6 +5,8 @@
|
||||
|
||||
<data>
|
||||
|
||||
<import type="com.topjohnwu.magisk.databinding.DataBindingAdaptersKt" />
|
||||
|
||||
<variable
|
||||
name="item"
|
||||
type="com.topjohnwu.magisk.ui.superuser.PolicyRvItem" />
|
||||
@@ -85,16 +87,32 @@
|
||||
app:layout_constraintVertical_bias="0"
|
||||
tools:text="com.topjohnwu.magisk" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<FrameLayout
|
||||
android:id="@+id/policy_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:checked="@={item.enabled}"
|
||||
android:nextFocusLeft="@id/policy"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
gone="@{item.showSlider}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={item.enabled}" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
goneUnless="@{item.showSlider}"
|
||||
labelFormatter="@{item.sliderValueToPolicyString}"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:stepSize="1"
|
||||
android:value="@={DataBindingAdaptersKt.policyToSliderValue(item.sliderValue)}"
|
||||
android:valueFrom="1"
|
||||
android:valueTo="3" />
|
||||
</FrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
@@ -1,5 +1,11 @@
|
||||
tasks.register("clean") {
|
||||
plugins {
|
||||
id("MagiskPlugin")
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete::class) {
|
||||
delete(rootProject.layout.buildDirectory)
|
||||
|
||||
subprojects.forEach {
|
||||
dependsOn(":app:${it.name}:clean")
|
||||
dependsOn(":${it.name}:clean")
|
||||
}
|
||||
}
|
||||
|
@@ -18,17 +18,12 @@ gradlePlugin {
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
languageVersion = KotlinVersion.KOTLIN_2_0
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("gradle-plugin", libs.versions.kotlin.get()))
|
||||
implementation(libs.android.gradle.plugin)
|
||||
implementation(libs.ksp.plugin)
|
||||
implementation(libs.navigation.safe.args.plugin)
|
||||
implementation(libs.lsparanoid.plugin)
|
||||
implementation(libs.moshi.plugin)
|
||||
implementation(libs.jgit)
|
||||
}
|
77
app/buildSrc/src/main/java/AddCommentTask.kt
Normal file
77
app/buildSrc/src/main/java/AddCommentTask.kt
Normal file
@@ -0,0 +1,77 @@
|
||||
import com.android.build.api.artifact.ArtifactTransformationRequest
|
||||
import com.android.build.api.dsl.ApkSigningConfig
|
||||
import com.android.builder.internal.packaging.IncrementalPackager
|
||||
import com.android.tools.build.apkzlib.sign.SigningExtension
|
||||
import com.android.tools.build.apkzlib.sign.SigningOptions
|
||||
import com.android.tools.build.apkzlib.zfile.ZFiles
|
||||
import com.android.tools.build.apkzlib.zip.ZFileOptions
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.io.File
|
||||
import java.security.KeyStore
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.jar.JarFile
|
||||
|
||||
abstract class AddCommentTask: DefaultTask() {
|
||||
@get:Input
|
||||
abstract val comment: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val signingConfig: Property<ApkSigningConfig>
|
||||
|
||||
@get:InputFiles
|
||||
abstract val apkFolder: DirectoryProperty
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outFolder: DirectoryProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val transformationRequest: Property<ArtifactTransformationRequest<AddCommentTask>>
|
||||
|
||||
@TaskAction
|
||||
fun taskAction() = transformationRequest.get().submit(this) { artifact ->
|
||||
val inFile = File(artifact.outputFile)
|
||||
val outFile = outFolder.file(inFile.name).get().asFile
|
||||
|
||||
val privateKey = signingConfig.get().getPrivateKey()
|
||||
val signingOptions = SigningOptions.builder()
|
||||
.setMinSdkVersion(0)
|
||||
.setV1SigningEnabled(true)
|
||||
.setV2SigningEnabled(true)
|
||||
.setKey(privateKey.privateKey)
|
||||
.setCertificates(privateKey.certificate as X509Certificate)
|
||||
.setValidation(SigningOptions.Validation.ASSUME_INVALID)
|
||||
.build()
|
||||
val options = ZFileOptions().apply {
|
||||
noTimestamps = true
|
||||
autoSortFiles = true
|
||||
}
|
||||
outFile.parentFile?.mkdirs()
|
||||
inFile.copyTo(outFile, overwrite = true)
|
||||
ZFiles.apk(outFile, options).use {
|
||||
SigningExtension(signingOptions).register(it)
|
||||
it.eocdComment = comment.get().toByteArray()
|
||||
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
|
||||
it.get(IncrementalPackager.VERSION_CONTROL_INFO_ENTRY_PATH)?.delete()
|
||||
it.get(JarFile.MANIFEST_NAME)?.delete()
|
||||
}
|
||||
|
||||
outFile
|
||||
}
|
||||
|
||||
private fun ApkSigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
|
||||
val keyStore = KeyStore.getInstance(storeType ?: KeyStore.getDefaultType())
|
||||
storeFile!!.inputStream().use {
|
||||
keyStore.load(it, storePassword!!.toCharArray())
|
||||
}
|
||||
val keyPwdArray = keyPassword!!.toCharArray()
|
||||
val entry = keyStore.getEntry(keyAlias!!, KeyStore.PasswordProtection(keyPwdArray))
|
||||
return entry as KeyStore.PrivateKeyEntry
|
||||
}
|
||||
}
|
@@ -7,8 +7,10 @@ import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.Opcodes.ASM9
|
||||
|
||||
private const val ZIP_ENTRY_CLASS_NAME = "java.util.zip.ZipEntry"
|
||||
private const val DESUGAR_CLASS_NAME = "com.topjohnwu.magisk.core.utils.Desugar"
|
||||
private const val ZIP_ENTRY_CLASS_NAME = "java.util.zip.ZipEntry"
|
||||
private const val ZIP_OUT_STREAM_CLASS_NAME = "org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream"
|
||||
private const val ZIP_UTIL_CLASS_NAME = "org/apache/commons/compress/archivers/zip/ZipUtil"
|
||||
private const val ZIP_ENTRY_GET_TIME_DESC = "()Ljava/nio/file/attribute/FileTime;"
|
||||
private const val DESUGAR_GET_TIME_DESC =
|
||||
"(Ljava/util/zip/ZipEntry;)Ljava/nio/file/attribute/FileTime;"
|
||||
@@ -20,27 +22,29 @@ abstract class DesugarClassVisitorFactory : AsmClassVisitorFactory<Instrumentati
|
||||
classContext: ClassContext,
|
||||
nextClassVisitor: ClassVisitor
|
||||
): ClassVisitor {
|
||||
return DesugarClassVisitor(classContext, nextClassVisitor)
|
||||
return if (classContext.currentClassData.className == ZIP_OUT_STREAM_CLASS_NAME) {
|
||||
ZipEntryPatcher(classContext, ZipOutputStreamPatcher(nextClassVisitor))
|
||||
} else {
|
||||
ZipEntryPatcher(classContext, nextClassVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isInstrumentable(classData: ClassData) = classData.className != DESUGAR_CLASS_NAME
|
||||
|
||||
class DesugarClassVisitor(private val classContext: ClassContext, cv: ClassVisitor) :
|
||||
ClassVisitor(ASM9, cv) {
|
||||
// Patch ALL references to ZipEntry#getXXXTime
|
||||
class ZipEntryPatcher(
|
||||
private val classContext: ClassContext,
|
||||
cv: ClassVisitor
|
||||
) : ClassVisitor(ASM9, cv) {
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor {
|
||||
return DesugarMethodVisitor(
|
||||
super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
)
|
||||
}
|
||||
) = MethodPatcher(super.visitMethod(access, name, descriptor, signature, exceptions))
|
||||
|
||||
inner class DesugarMethodVisitor(mv: MethodVisitor?) :
|
||||
MethodVisitor(ASM9, mv) {
|
||||
inner class MethodPatcher(mv: MethodVisitor?) : MethodVisitor(ASM9, mv) {
|
||||
override fun visitMethodInsn(
|
||||
opcode: Int,
|
||||
owner: String,
|
||||
@@ -75,4 +79,44 @@ abstract class DesugarClassVisitorFactory : AsmClassVisitorFactory<Instrumentati
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Patch ZipArchiveOutputStream#copyFromZipInputStream
|
||||
class ZipOutputStreamPatcher(cv: ClassVisitor) : ClassVisitor(ASM9, cv) {
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String?>?
|
||||
): MethodVisitor? {
|
||||
return if (name == "copyFromZipInputStream") {
|
||||
MethodPatcher(super.visitMethod(access, name, descriptor, signature, exceptions))
|
||||
} else {
|
||||
super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
}
|
||||
}
|
||||
|
||||
class MethodPatcher(mv: MethodVisitor?) : MethodVisitor(ASM9, mv) {
|
||||
override fun visitMethodInsn(
|
||||
opcode: Int,
|
||||
owner: String,
|
||||
name: String,
|
||||
descriptor: String?,
|
||||
isInterface: Boolean
|
||||
) {
|
||||
if (owner == ZIP_UTIL_CLASS_NAME && name == "checkRequestedFeatures") {
|
||||
// Redirect
|
||||
mv.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
DESUGAR_CLASS_NAME.replace('.', '/'),
|
||||
name,
|
||||
descriptor,
|
||||
false
|
||||
)
|
||||
} else {
|
||||
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,6 +5,12 @@ import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import java.io.File
|
||||
import java.util.Properties
|
||||
import java.util.Random
|
||||
|
||||
// Set non-zero value here to fix the random seed for reproducible builds
|
||||
// CI builds are always reproducible
|
||||
val RAND_SEED = if (System.getenv("CI") != null) 42 else 0
|
||||
lateinit var RANDOM: Random
|
||||
|
||||
private val props = Properties()
|
||||
private var commitHash = ""
|
||||
@@ -14,7 +20,7 @@ private val defaultAbis = setOf("armeabi-v7a", "x86", "arm64-v8a", "x86_64")
|
||||
object Config {
|
||||
operator fun get(key: String): String? {
|
||||
val v = props[key] as? String ?: return null
|
||||
return if (v.isBlank()) null else v
|
||||
return v.ifBlank { null }
|
||||
}
|
||||
|
||||
fun contains(key: String) = get(key) != null
|
||||
@@ -28,19 +34,25 @@ object Config {
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.rootFile(path: String): File {
|
||||
val file = File(path)
|
||||
return if (file.isAbsolute) file
|
||||
else File(rootProject.file(".."), path)
|
||||
}
|
||||
|
||||
class MagiskPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) = project.applyPlugin()
|
||||
|
||||
private fun Project.applyPlugin() {
|
||||
initRandom(rootProject.file("app/dict.txt"))
|
||||
initRandom(rootProject.file("dict.txt"))
|
||||
props.clear()
|
||||
rootProject.file("gradle.properties").inputStream().use { props.load(it) }
|
||||
val configPath: String? by this
|
||||
val config = configPath?.let { File(it) } ?: rootProject.file("config.prop")
|
||||
val config = rootFile(configPath ?: "config.prop")
|
||||
if (config.exists())
|
||||
config.inputStream().use { props.load(it) }
|
||||
|
||||
val repo = FileRepository(rootProject.file(".git"))
|
||||
val repo = FileRepository(rootFile(".git"))
|
||||
val refId = repo.refDatabase.exactRef("HEAD").objectId
|
||||
commitHash = repo.newObjectReader().abbreviate(refId, 8).name()
|
||||
}
|
330
app/buildSrc/src/main/java/Setup.kt
Normal file
330
app/buildSrc/src/main/java/Setup.kt
Normal file
@@ -0,0 +1,330 @@
|
||||
import com.android.build.api.artifact.SingleArtifact
|
||||
import com.android.build.api.instrumentation.FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
|
||||
import com.android.build.api.instrumentation.InstrumentationScope
|
||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import com.android.build.gradle.LibraryExtension
|
||||
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
|
||||
import org.apache.tools.ant.filters.FixCrLfFilter
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Copy
|
||||
import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.tasks.StopExecutionException
|
||||
import org.gradle.api.tasks.Sync
|
||||
import org.gradle.kotlin.dsl.assign
|
||||
import org.gradle.kotlin.dsl.exclude
|
||||
import org.gradle.kotlin.dsl.filter
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.getValue
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.security.MessageDigest
|
||||
import java.util.HexFormat
|
||||
import java.util.zip.Deflater
|
||||
import java.util.zip.DeflaterOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
private fun Project.androidBase(configure: Action<BaseExtension>) =
|
||||
extensions.configure("android", configure)
|
||||
|
||||
private fun Project.android(configure: Action<BaseAppModuleExtension>) =
|
||||
extensions.configure("android", configure)
|
||||
|
||||
internal val Project.androidApp: BaseAppModuleExtension
|
||||
get() = extensions["android"] as BaseAppModuleExtension
|
||||
|
||||
private val Project.androidLib: LibraryExtension
|
||||
get() = extensions["android"] as LibraryExtension
|
||||
|
||||
internal val Project.androidComponents
|
||||
get() = extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
|
||||
|
||||
fun Project.setupCommon() {
|
||||
androidBase {
|
||||
compileSdkVersion(36)
|
||||
buildToolsVersion = "36.0.0"
|
||||
ndkPath = "$sdkDirectory/ndk/magisk"
|
||||
ndkVersion = "28.1.13356709"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 23
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
resources {
|
||||
excludes += arrayOf(
|
||||
"/META-INF/*",
|
||||
"/META-INF/androidx/**",
|
||||
"/META-INF/versions/**",
|
||||
"/org/bouncycastle/**",
|
||||
"/org/apache/commons/**",
|
||||
"/kotlin/**",
|
||||
"/kotlinx/**",
|
||||
"/okhttp3/**",
|
||||
"/*.txt",
|
||||
"/*.bin",
|
||||
"/*.json",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk7")
|
||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk8")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_21
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.downloadFile(url: String, checksum: String): File {
|
||||
val file = layout.buildDirectory.file(checksum).get().asFile
|
||||
if (file.exists()) {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
file.inputStream().use { md.update(it.readAllBytes()) }
|
||||
val hash = HexFormat.of().formatHex(md.digest())
|
||||
if (hash != checksum) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
if (!file.exists()) {
|
||||
file.parentFile.mkdirs()
|
||||
URI(url).toURL().openStream().use { dl ->
|
||||
file.outputStream().use {
|
||||
dl.copyTo(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
const val BUSYBOX_DOWNLOAD_URL =
|
||||
"https://github.com/topjohnwu/magisk-files/releases/download/files/busybox-1.36.1.1.zip"
|
||||
const val BUSYBOX_ZIP_CHECKSUM =
|
||||
"b4d0551feabaf314e53c79316c980e8f66432e9fb91a69dbbf10a93564b40951"
|
||||
|
||||
fun Project.setupCoreLib() {
|
||||
setupCommon()
|
||||
|
||||
androidLib.libraryVariants.all {
|
||||
val variant = name
|
||||
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
||||
val abiList = Config.abiList
|
||||
|
||||
val syncLibs = tasks.register("sync${variantCapped}JniLibs", Sync::class) {
|
||||
into("src/$variant/jniLibs")
|
||||
for (abi in abiList) {
|
||||
into(abi) {
|
||||
from(rootFile("native/out/$abi")) {
|
||||
include("magiskboot", "magiskinit", "magiskpolicy", "magisk", "libinit-ld.so")
|
||||
rename { if (it.endsWith(".so")) it else "lib$it.so" }
|
||||
}
|
||||
}
|
||||
}
|
||||
from(zipTree(downloadFile(BUSYBOX_DOWNLOAD_URL, BUSYBOX_ZIP_CHECKSUM)))
|
||||
include(abiList.map { "$it/libbusybox.so" })
|
||||
onlyIf {
|
||||
if (inputs.sourceFiles.files.size != abiList.size * 6)
|
||||
throw StopExecutionException("Please build binaries first! (./build.py binary)")
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.getByPath("merge${variantCapped}JniLibFolders").dependsOn(syncLibs)
|
||||
|
||||
val syncResources = tasks.register("sync${variantCapped}Resources", Sync::class) {
|
||||
into("src/$variant/resources/META-INF/com/google/android")
|
||||
from(rootFile("scripts/update_binary.sh")) {
|
||||
rename { "update-binary" }
|
||||
}
|
||||
from(rootFile("scripts/flash_script.sh")) {
|
||||
rename { "updater-script" }
|
||||
}
|
||||
}
|
||||
|
||||
processJavaResourcesProvider.configure { dependsOn(syncResources) }
|
||||
|
||||
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
|
||||
val stubApk = stubTask.outputs.files.asFileTree.filter {
|
||||
it.name.endsWith(".apk")
|
||||
}
|
||||
|
||||
val syncAssets = tasks.register("sync${variantCapped}Assets", Sync::class) {
|
||||
dependsOn(stubTask)
|
||||
inputs.property("version", Config.version)
|
||||
inputs.property("versionCode", Config.versionCode)
|
||||
into("src/$variant/assets")
|
||||
from(rootFile("scripts")) {
|
||||
include("util_functions.sh", "boot_patch.sh", "addon.d.sh",
|
||||
"app_functions.sh", "uninstaller.sh", "module_installer.sh")
|
||||
}
|
||||
from(rootFile("tools/bootctl"))
|
||||
into("chromeos") {
|
||||
from(rootFile("tools/futility"))
|
||||
from(rootFile("tools/keys")) {
|
||||
include("kernel_data_key.vbprivk", "kernel.keyblock")
|
||||
}
|
||||
}
|
||||
from(stubApk) {
|
||||
rename { "stub.apk" }
|
||||
}
|
||||
filesMatching("**/util_functions.sh") {
|
||||
filter {
|
||||
it.replace(
|
||||
"#MAGISK_VERSION_STUB",
|
||||
"MAGISK_VER='${Config.version}'\nMAGISK_VER_CODE=${Config.versionCode}"
|
||||
)
|
||||
}
|
||||
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
}
|
||||
}
|
||||
mergeAssetsProvider.configure { dependsOn(syncAssets) }
|
||||
}
|
||||
|
||||
tasks.named<Delete>("clean") {
|
||||
delete.addAll(listOf("src/main/jniLibs", "src/main/resources", "src/debug", "src/release"))
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.setupAppCommon() {
|
||||
setupCommon()
|
||||
|
||||
android {
|
||||
signingConfigs {
|
||||
Config["keyStore"]?.also {
|
||||
create("config") {
|
||||
storeFile = rootFile(it)
|
||||
storePassword = Config["keyStorePass"]
|
||||
keyAlias = Config["keyAlias"]
|
||||
keyPassword = Config["keyPass"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
targetSdk = 36
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt")
|
||||
)
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
val config = signingConfigs.findByName("config") ?: signingConfigs["debug"]
|
||||
debug {
|
||||
signingConfig = config
|
||||
}
|
||||
release {
|
||||
signingConfig = config
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
disable += "MissingTranslation"
|
||||
checkReleaseBuilds = false
|
||||
}
|
||||
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
packaging {
|
||||
jniLibs {
|
||||
useLegacyPackaging = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
androidComponents.onVariants { variant ->
|
||||
val commentTask = tasks.register(
|
||||
"comment${variant.name.replaceFirstChar { it.uppercase() }}",
|
||||
AddCommentTask::class.java
|
||||
)
|
||||
val transformationRequest = variant.artifacts.use(commentTask)
|
||||
.wiredWithDirectories(AddCommentTask::apkFolder, AddCommentTask::outFolder)
|
||||
.toTransformMany(SingleArtifact.APK)
|
||||
val signingConfig = androidApp.buildTypes.getByName(variant.buildType!!).signingConfig
|
||||
commentTask.configure {
|
||||
this.transformationRequest = transformationRequest
|
||||
this.signingConfig = signingConfig
|
||||
this.comment = "version=${Config.version}\n" +
|
||||
"versionCode=${Config.versionCode}\n" +
|
||||
"stubVersion=${Config.stubVersion}\n"
|
||||
this.outFolder.set(layout.buildDirectory.dir("outputs/apk/${variant.name}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.setupMainApk() {
|
||||
setupAppCommon()
|
||||
|
||||
android {
|
||||
namespace = "com.topjohnwu.magisk"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.topjohnwu.magisk"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
versionName = Config.version
|
||||
versionCode = Config.versionCode
|
||||
ndk {
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64", "riscv64")
|
||||
debugSymbolLevel = "FULL"
|
||||
}
|
||||
}
|
||||
|
||||
androidComponents.onVariants { variant ->
|
||||
variant.instrumentation.apply {
|
||||
setAsmFramesComputationMode(COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS)
|
||||
transformClassesWith(
|
||||
DesugarClassVisitorFactory::class.java, InstrumentationScope.ALL) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const val LSPOSED_DOWNLOAD_URL =
|
||||
"https://github.com/LSPosed/LSPosed/releases/download/v1.9.2/LSPosed-v1.9.2-7024-zygisk-release.zip"
|
||||
const val LSPOSED_CHECKSUM =
|
||||
"0ebc6bcb465d1c4b44b7220ab5f0252e6b4eb7fe43da74650476d2798bb29622"
|
||||
|
||||
const val SHAMIKO_DOWNLOAD_URL =
|
||||
"https://github.com/LSPosed/LSPosed.github.io/releases/download/shamiko-383/Shamiko-v1.2.1-383-release.zip"
|
||||
const val SHAMIKO_CHECKSUM =
|
||||
"93754a038c2d8f0e985bad45c7303b96f70a93d8335060e50146f028d3a9b13f"
|
||||
|
||||
fun Project.setupTestApk() {
|
||||
setupAppCommon()
|
||||
|
||||
androidApp.applicationVariants.all {
|
||||
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
||||
val dlTask by tasks.register("download${variantCapped}Lsposed", Sync::class) {
|
||||
from(downloadFile(LSPOSED_DOWNLOAD_URL, LSPOSED_CHECKSUM)) {
|
||||
rename { "lsposed.zip" }
|
||||
}
|
||||
from(downloadFile(SHAMIKO_DOWNLOAD_URL, SHAMIKO_CHECKSUM)) {
|
||||
rename { "shamiko.zip" }
|
||||
}
|
||||
into("src/${this@all.name}/assets")
|
||||
}
|
||||
mergeAssetsProvider.configure { dependsOn(dlTask) }
|
||||
}
|
||||
}
|
@@ -1,8 +1,11 @@
|
||||
import com.android.build.api.artifact.SingleArtifact
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
@@ -10,22 +13,25 @@ import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.assign
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import java.security.SecureRandom
|
||||
import java.util.Random
|
||||
import java.util.zip.Deflater
|
||||
import java.util.zip.DeflaterOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipOutputStream
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.CipherOutputStream
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import kotlin.random.asKotlinRandom
|
||||
|
||||
// Set non-zero value here to fix the random seed for reproducible builds
|
||||
// CI builds are always reproducible
|
||||
val RAND_SEED = if (System.getenv("CI") != null) 42 else 0
|
||||
private lateinit var RANDOM: Random
|
||||
private val kRANDOM get() = RANDOM.asKotlinRandom()
|
||||
|
||||
private val c1 = mutableListOf<String>()
|
||||
@@ -72,7 +78,7 @@ private fun PrintStream.byteField(name: String, bytes: ByteArray) {
|
||||
}
|
||||
|
||||
@CacheableTask
|
||||
abstract class ManifestUpdater: DefaultTask() {
|
||||
private abstract class ManifestUpdater: DefaultTask() {
|
||||
@get:Input
|
||||
abstract val applicationId: Property<String>
|
||||
|
||||
@@ -182,9 +188,7 @@ abstract class ManifestUpdater: DefaultTask() {
|
||||
}
|
||||
|
||||
|
||||
fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
||||
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
|
||||
|
||||
private fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
||||
val classNameGenerator = sequence {
|
||||
fun notJavaKeyword(name: String) = when (name) {
|
||||
"do", "if", "for", "int", "new", "try" -> false
|
||||
@@ -228,7 +232,7 @@ fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
||||
genClass("StubApplication", appOutDir)
|
||||
}
|
||||
|
||||
fun genEncryptedResources(res: ByteArray, outDir: File) {
|
||||
private fun genEncryptedResources(res: ByteArray, outDir: File) {
|
||||
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
|
||||
mainPkgDir.mkdirs()
|
||||
|
||||
@@ -259,3 +263,86 @@ fun genEncryptedResources(res: ByteArray, outDir: File) {
|
||||
it.println("}")
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.setupStubApk() {
|
||||
setupAppCommon()
|
||||
|
||||
androidComponents.onVariants { variant ->
|
||||
val variantName = variant.name
|
||||
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
|
||||
val manifestUpdater =
|
||||
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
|
||||
dependsOn("generate${variantCapped}ObfuscatedClass")
|
||||
applicationId = variant.applicationId
|
||||
appClassDir.set(layout.buildDirectory.dir("generated/source/app/$variantName"))
|
||||
factoryClassDir.set(layout.buildDirectory.dir("generated/source/factory/$variantName"))
|
||||
}
|
||||
variant.artifacts.use(manifestUpdater)
|
||||
.wiredWithFiles(
|
||||
ManifestUpdater::mergedManifest,
|
||||
ManifestUpdater::outputManifest)
|
||||
.toTransform(SingleArtifact.MERGED_MANIFEST)
|
||||
}
|
||||
|
||||
androidApp.applicationVariants.all {
|
||||
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
||||
val variantLowered = name.lowercase()
|
||||
val outFactoryClassDir = layout.buildDirectory.file("generated/source/factory/${variantLowered}").get().asFile
|
||||
val outAppClassDir = layout.buildDirectory.file("generated/source/app/${variantLowered}").get().asFile
|
||||
val outResDir = layout.buildDirectory.dir("generated/source/res/${variantLowered}").get().asFile
|
||||
val aapt = File(androidApp.sdkDirectory, "build-tools/${androidApp.buildToolsVersion}/aapt2")
|
||||
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
||||
"${variantLowered}/process${variantCapped}Resources/linked-resources-binary-format-${variantLowered}.ap_").get().asFile
|
||||
|
||||
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedClass") {
|
||||
inputs.property("seed", RAND_SEED)
|
||||
outputs.dirs(outFactoryClassDir, outAppClassDir)
|
||||
doLast {
|
||||
outFactoryClassDir.mkdirs()
|
||||
outAppClassDir.mkdirs()
|
||||
genStubClasses(outFactoryClassDir, outAppClassDir)
|
||||
}
|
||||
}
|
||||
registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
|
||||
|
||||
val processResourcesTask = tasks.named("process${variantCapped}Resources") {
|
||||
outputs.dir(outResDir)
|
||||
doLast {
|
||||
val apkTmp = File("${apk}.tmp")
|
||||
providers.exec {
|
||||
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
|
||||
}.result.get()
|
||||
|
||||
val bos = ByteArrayOutputStream()
|
||||
ZipFile(apkTmp).use { src ->
|
||||
ZipOutputStream(apk.outputStream()).use {
|
||||
it.setLevel(Deflater.BEST_COMPRESSION)
|
||||
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
|
||||
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
|
||||
it.closeEntry()
|
||||
}
|
||||
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
|
||||
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
|
||||
}
|
||||
}
|
||||
apkTmp.delete()
|
||||
genEncryptedResources(bos.toByteArray(), outResDir)
|
||||
}
|
||||
}
|
||||
|
||||
registerJavaGeneratingTask(processResourcesTask, outResDir)
|
||||
}
|
||||
// Override optimizeReleaseResources task
|
||||
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
||||
"release/processReleaseResources/linked-resources-binary-format-release.ap_").get().asFile
|
||||
val optRes = layout.buildDirectory.file("intermediates/optimized_processed_res/" +
|
||||
"release/optimizeReleaseResources/resources-release-optimize.ap_").get().asFile
|
||||
afterEvaluate {
|
||||
tasks.named("optimizeReleaseResources") {
|
||||
doLast { apk.copyTo(optRes, true) }
|
||||
}
|
||||
}
|
||||
tasks.named<Delete>("clean") {
|
||||
delete.addAll(listOf("src/debug/AndroidManifest.xml", "src/release/AndroidManifest.xml"))
|
||||
}
|
||||
}
|
5
app/core/.gitignore
vendored
5
app/core/.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
/build
|
||||
src/*/assets
|
||||
src/*/jniLibs
|
||||
src/*/resources
|
||||
src/debug
|
||||
src/release
|
||||
|
@@ -2,6 +2,7 @@ plugins {
|
||||
id("com.android.library")
|
||||
kotlin("android")
|
||||
kotlin("plugin.parcelize")
|
||||
id("dev.zacsweers.moshix")
|
||||
id("com.google.devtools.ksp")
|
||||
}
|
||||
|
||||
@@ -26,10 +27,15 @@ android {
|
||||
aidl = true
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":app:shared"))
|
||||
api(project(":shared"))
|
||||
coreLibraryDesugaring(libs.jdk.libs)
|
||||
|
||||
api(libs.timber)
|
||||
api(libs.markwon.core)
|
||||
@@ -48,9 +54,6 @@ dependencies {
|
||||
implementation(libs.okhttp.logging)
|
||||
implementation(libs.okhttp.dnsoverhttps)
|
||||
|
||||
implementation(libs.moshi)
|
||||
ksp(libs.moshi.codegen)
|
||||
|
||||
implementation(libs.room.runtime)
|
||||
implementation(libs.room.ktx)
|
||||
ksp(libs.room.compiler)
|
||||
@@ -60,5 +63,10 @@ dependencies {
|
||||
implementation(libs.activity)
|
||||
implementation(libs.collection.ktx)
|
||||
implementation(libs.profileinstaller)
|
||||
implementation(libs.lifecycle.process)
|
||||
|
||||
// We also implement all our tests in this module.
|
||||
// However, we don't want to bundle test dependencies.
|
||||
// That's why we make it compileOnly.
|
||||
compileOnly(libs.test.junit)
|
||||
compileOnly(libs.test.uiautomator)
|
||||
}
|
||||
|
11
app/core/proguard-rules.pro
vendored
11
app/core/proguard-rules.pro
vendored
@@ -37,12 +37,5 @@
|
||||
-flattenpackagehierarchy
|
||||
-allowaccessmodification
|
||||
|
||||
-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
|
||||
-dontwarn org.junit.**
|
||||
-dontwarn org.apache.**
|
||||
|
@@ -14,7 +14,6 @@
|
||||
<application
|
||||
android:name=".App"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:multiArch="true"
|
||||
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning"
|
||||
tools:remove="android:appComponentFactory">
|
||||
|
||||
|
@@ -6,4 +6,5 @@ package com.topjohnwu.magisk.core.utils;
|
||||
interface IRootUtils {
|
||||
android.app.ActivityManager.RunningAppProcessInfo getAppProcess(int pid);
|
||||
IBinder getFileSystem();
|
||||
boolean addSystemlessHosts();
|
||||
}
|
||||
|
@@ -16,7 +16,6 @@ import com.topjohnwu.magisk.StubApk
|
||||
import com.topjohnwu.magisk.core.base.UntrackedActivity
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
import com.topjohnwu.magisk.core.utils.NetworkObserver
|
||||
import com.topjohnwu.magisk.core.utils.ProcessLifecycle
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.magisk.core.utils.ShellInit
|
||||
import com.topjohnwu.superuser.Shell
|
||||
@@ -40,6 +39,7 @@ object AppContext : ContextWrapper(null),
|
||||
|
||||
private var ref = WeakReference<Activity>(null)
|
||||
private lateinit var application: Application
|
||||
private lateinit var networkObserver: NetworkObserver
|
||||
|
||||
init {
|
||||
// Always log full stack trace with Timber
|
||||
@@ -56,6 +56,10 @@ object AppContext : ContextWrapper(null),
|
||||
LocaleSetting.instance.updateResource(resources)
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {
|
||||
networkObserver.postCurrentState()
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
if (activity is UntrackedActivity) return
|
||||
ref = WeakReference(activity)
|
||||
@@ -102,8 +106,7 @@ object AppContext : ContextWrapper(null),
|
||||
val lm = getSystemService(LocaleManager::class.java)
|
||||
lm.overrideLocaleConfig = LocaleSetting.localeConfig
|
||||
}
|
||||
ProcessLifecycle.init(this)
|
||||
NetworkObserver.init(this)
|
||||
networkObserver = NetworkObserver.init(this)
|
||||
if (!BuildConfig.DEBUG && !isRunningAsStub) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
ProfileInstaller.writeProfile(this@AppContext)
|
||||
@@ -120,7 +123,6 @@ object AppContext : ContextWrapper(null),
|
||||
}
|
||||
|
||||
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {}
|
||||
override fun onActivityStarted(activity: Activity) {}
|
||||
override fun onActivityStopped(activity: Activity) {}
|
||||
override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {}
|
||||
override fun onActivityDestroyed(activity: Activity) {}
|
||||
|
@@ -32,8 +32,9 @@ object Config : PreferenceConfig, DBConfig {
|
||||
const val SU_NOTIFICATION = "su_notification"
|
||||
const val SU_REAUTH = "su_reauth"
|
||||
const val SU_TAPJACK = "su_tapjack"
|
||||
const val SU_RESTRICT = "su_restrict"
|
||||
const val CHECK_UPDATES = "check_update"
|
||||
const val UPDATE_CHANNEL = "update_channel"
|
||||
const val RELEASE_CHANNEL = "release_channel"
|
||||
const val CUSTOM_CHANNEL = "custom_channel"
|
||||
const val LOCALE = "locale"
|
||||
const val DARK_THEME = "dark_theme_extended"
|
||||
@@ -48,7 +49,7 @@ object Config : PreferenceConfig, DBConfig {
|
||||
SU_AUTO_RESPONSE, SU_REAUTH, SU_TAPJACK)
|
||||
}
|
||||
|
||||
object Value {
|
||||
object OldValue {
|
||||
// Update channels
|
||||
const val DEFAULT_CHANNEL = -1
|
||||
const val STABLE_CHANNEL = 0
|
||||
@@ -56,6 +57,15 @@ object Config : PreferenceConfig, DBConfig {
|
||||
const val CUSTOM_CHANNEL = 2
|
||||
const val CANARY_CHANNEL = 3
|
||||
const val DEBUG_CHANNEL = 4
|
||||
}
|
||||
|
||||
object Value {
|
||||
// Update channels
|
||||
const val DEFAULT_CHANNEL = -1
|
||||
const val STABLE_CHANNEL = 0
|
||||
const val BETA_CHANNEL = 1
|
||||
const val DEBUG_CHANNEL = 2
|
||||
const val CUSTOM_CHANNEL = 3
|
||||
|
||||
// root access mode
|
||||
const val ROOT_ACCESS_DISABLED = 0
|
||||
@@ -83,17 +93,9 @@ object Config : PreferenceConfig, DBConfig {
|
||||
const val SU_AUTO_ALLOW = 2
|
||||
|
||||
// su timeout
|
||||
val TIMEOUT_LIST = intArrayOf(0, -1, 10, 20, 30, 60)
|
||||
val TIMEOUT_LIST = longArrayOf(0, -1, 10, 20, 30, 60)
|
||||
}
|
||||
|
||||
private val defaultChannel =
|
||||
if (BuildConfig.DEBUG)
|
||||
Value.DEBUG_CHANNEL
|
||||
else if (Const.APP_IS_CANARY)
|
||||
Value.CANARY_CHANNEL
|
||||
else
|
||||
Value.DEFAULT_CHANNEL
|
||||
|
||||
@JvmField var keepVerity = false
|
||||
@JvmField var keepEnc = false
|
||||
@JvmField var recovery = false
|
||||
@@ -109,7 +111,7 @@ object Config : PreferenceConfig, DBConfig {
|
||||
private var checkUpdatePrefs by preference(Key.CHECK_UPDATES, true)
|
||||
private var localePrefs by preference(Key.LOCALE, "")
|
||||
var doh by preference(Key.DOH, false)
|
||||
var updateChannel by preferenceStrInt(Key.UPDATE_CHANNEL, defaultChannel)
|
||||
var updateChannel by preference(Key.RELEASE_CHANNEL, Value.DEFAULT_CHANNEL)
|
||||
var customChannelUrl by preference(Key.CUSTOM_CHANNEL, "")
|
||||
var downloadDir by preference(Key.DOWNLOAD_DIR, "")
|
||||
var randName by preference(Key.RAND_NAME, true)
|
||||
@@ -146,8 +148,10 @@ object Config : PreferenceConfig, DBConfig {
|
||||
}
|
||||
var suReAuth by preference(Key.SU_REAUTH, false)
|
||||
var suTapjack by preference(Key.SU_TAPJACK, true)
|
||||
var suRestrict by preference(Key.SU_RESTRICT, false)
|
||||
|
||||
private const val SU_FINGERPRINT = "su_fingerprint"
|
||||
private const val UPDATE_CHANNEL = "update_channel"
|
||||
|
||||
fun toBundle(): Bundle {
|
||||
val map = prefs.all - Key.NO_MIGRATION
|
||||
@@ -183,17 +187,23 @@ object Config : PreferenceConfig, DBConfig {
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
// Settings migration
|
||||
// Migrate su_fingerprint
|
||||
if (prefs.getBoolean(SU_FINGERPRINT, false))
|
||||
suBiometric = true
|
||||
remove(SU_FINGERPRINT)
|
||||
prefs.getString(Key.UPDATE_CHANNEL, null).also {
|
||||
if (it == null ||
|
||||
it.toInt() > Value.DEBUG_CHANNEL ||
|
||||
it.toInt() < Value.DEFAULT_CHANNEL) {
|
||||
putString(Key.UPDATE_CHANNEL, defaultChannel.toString())
|
||||
|
||||
// Migrate update_channel
|
||||
prefs.getString(UPDATE_CHANNEL, null)?.let {
|
||||
val channel = when (it.toInt()) {
|
||||
OldValue.STABLE_CHANNEL -> Value.STABLE_CHANNEL
|
||||
OldValue.CANARY_CHANNEL, OldValue.BETA_CHANNEL -> Value.BETA_CHANNEL
|
||||
OldValue.DEBUG_CHANNEL -> Value.DEBUG_CHANNEL
|
||||
OldValue.CUSTOM_CHANNEL -> Value.CUSTOM_CHANNEL
|
||||
else -> Value.DEFAULT_CHANNEL
|
||||
}
|
||||
putInt(Key.RELEASE_CHANNEL, channel)
|
||||
}
|
||||
remove(UPDATE_CHANNEL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package com.topjohnwu.magisk.core
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Process
|
||||
import com.topjohnwu.magisk.core.BuildConfig.APP_VERSION_CODE
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
object Const {
|
||||
@@ -14,23 +15,22 @@ object Const {
|
||||
else Build.SUPPORTED_32_BIT_ABIS.firstOrNull()
|
||||
|
||||
// Paths
|
||||
const val MAGISK_PATH = "/data/adb/modules"
|
||||
const val MODULE_PATH = "/data/adb/modules"
|
||||
const val TMPDIR = "/dev/tmp"
|
||||
const val MAGISK_LOG = "/cache/magisk.log"
|
||||
|
||||
// Misc
|
||||
val USER_ID = Process.myUid() / 100000
|
||||
val APP_IS_CANARY get() = Version.isCanary(BuildConfig.APP_VERSION_CODE)
|
||||
|
||||
object Version {
|
||||
const val MIN_VERSION = "v22.0"
|
||||
const val MIN_VERCODE = 22000
|
||||
|
||||
private fun isCanary() = (Info.env.versionCode % 100) != 0
|
||||
fun atLeast_24_0() = Info.env.versionCode >= 24000 || isCanary()
|
||||
fun atLeast_25_0() = Info.env.versionCode >= 25000 || isCanary()
|
||||
fun isCanary() = isCanary(Info.env.versionCode)
|
||||
|
||||
fun isCanary(ver: Int) = ver > 0 && ver % 100 != 0
|
||||
fun atLeast_28_0() = Info.env.versionCode >= 28000 || isCanary()
|
||||
fun atLeast_30_1() = Info.env.versionCode >= 30100 || isCanary()
|
||||
}
|
||||
|
||||
object ID {
|
||||
@@ -42,13 +42,9 @@ object Const {
|
||||
const val PATREON_URL = "https://www.patreon.com/topjohnwu"
|
||||
const val SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk"
|
||||
|
||||
val CHANGELOG_URL = if (APP_IS_CANARY) Info.remote.magisk.note
|
||||
else "https://topjohnwu.github.io/Magisk/releases/${BuildConfig.APP_VERSION_CODE}.md"
|
||||
|
||||
const val GITHUB_RAW_URL = "https://raw.githubusercontent.com/"
|
||||
const val GITHUB_API_URL = "https://api.github.com/"
|
||||
const val GITHUB_PAGE_URL = "https://topjohnwu.github.io/magisk-files/"
|
||||
const val JS_DELIVR_URL = "https://cdn.jsdelivr.net/gh/"
|
||||
const val INVALID_URL = "https://example.com/"
|
||||
}
|
||||
|
||||
object Key {
|
||||
|
@@ -19,12 +19,18 @@ object Info {
|
||||
|
||||
var stub: StubApk.Data? = null
|
||||
|
||||
val EMPTY_REMOTE = UpdateInfo()
|
||||
var remote = EMPTY_REMOTE
|
||||
suspend fun getRemote(svc: NetworkService): UpdateInfo? {
|
||||
return if (remote === EMPTY_REMOTE) {
|
||||
svc.fetchUpdate()?.apply { remote = this }
|
||||
} else remote
|
||||
private val EMPTY_UPDATE = UpdateInfo()
|
||||
var update = EMPTY_UPDATE
|
||||
private set
|
||||
|
||||
suspend fun fetchUpdate(svc: NetworkService): UpdateInfo? {
|
||||
return if (update === EMPTY_UPDATE) {
|
||||
svc.fetchUpdate()?.apply { update = this }
|
||||
} else update
|
||||
}
|
||||
|
||||
fun resetUpdate() {
|
||||
update = EMPTY_UPDATE
|
||||
}
|
||||
|
||||
var isRooted = false
|
||||
@@ -47,7 +53,6 @@ object Info {
|
||||
private set
|
||||
private var crypto = ""
|
||||
|
||||
var hasGMS = true
|
||||
val isEmulator =
|
||||
Build.DEVICE.contains("vsoc")
|
||||
|| getProperty("ro.kernel.qemu", "0") == "1"
|
||||
|
@@ -11,6 +11,7 @@ import androidx.core.content.getSystemService
|
||||
import com.topjohnwu.magisk.core.base.BaseJobService
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.download.DownloadEngine
|
||||
import com.topjohnwu.magisk.core.download.DownloadSession
|
||||
import com.topjohnwu.magisk.core.download.Subject
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -25,7 +26,7 @@ class JobService : BaseJobService() {
|
||||
@TargetApi(value = 34)
|
||||
inner class Session(
|
||||
private var params: JobParameters
|
||||
) : DownloadEngine.Session {
|
||||
) : DownloadSession {
|
||||
|
||||
override val context get() = this@JobService
|
||||
val engine = DownloadEngine(this)
|
||||
@@ -74,9 +75,8 @@ class JobService : BaseJobService() {
|
||||
|
||||
private fun checkUpdate(params: JobParameters): Boolean {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
ServiceLocator.networkService.fetchUpdate()?.let {
|
||||
Info.remote = it
|
||||
if (Info.env.isActive && BuildConfig.APP_VERSION_CODE < it.magisk.versionCode)
|
||||
Info.fetchUpdate(ServiceLocator.networkService)?.let {
|
||||
if (Info.env.isActive && BuildConfig.APP_VERSION_CODE < it.versionCode)
|
||||
Notifications.updateAvailable()
|
||||
jobFinished(params, false)
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.core
|
||||
import android.os.Bundle
|
||||
import com.topjohnwu.magisk.core.base.BaseProvider
|
||||
import com.topjohnwu.magisk.core.su.SuCallbackHandler
|
||||
import com.topjohnwu.magisk.core.su.TestHandler
|
||||
|
||||
class Provider : BaseProvider() {
|
||||
|
||||
@@ -13,7 +12,7 @@ class Provider : BaseProvider() {
|
||||
SuCallbackHandler.run(context!!, method, extras)
|
||||
Bundle.EMPTY
|
||||
}
|
||||
else -> TestHandler.run(method)
|
||||
else -> Bundle.EMPTY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,9 +7,10 @@ import androidx.core.app.ServiceCompat
|
||||
import androidx.core.content.IntentCompat
|
||||
import com.topjohnwu.magisk.core.base.BaseService
|
||||
import com.topjohnwu.magisk.core.download.DownloadEngine
|
||||
import com.topjohnwu.magisk.core.download.DownloadSession
|
||||
import com.topjohnwu.magisk.core.download.Subject
|
||||
|
||||
class Service : BaseService(), DownloadEngine.Session {
|
||||
class Service : BaseService(), DownloadSession {
|
||||
|
||||
private var mEngine: DownloadEngine? = null
|
||||
override val context get() = this
|
||||
|
@@ -1,45 +0,0 @@
|
||||
package com.topjohnwu.magisk.core.data
|
||||
|
||||
import com.topjohnwu.magisk.core.model.BranchInfo
|
||||
import com.topjohnwu.magisk.core.model.ModuleJson
|
||||
import com.topjohnwu.magisk.core.model.UpdateInfo
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Headers
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Streaming
|
||||
import retrofit2.http.Url
|
||||
|
||||
private const val BRANCH = "branch"
|
||||
private const val REPO = "repo"
|
||||
private const val FILE = "file"
|
||||
|
||||
interface GithubPageServices {
|
||||
|
||||
@GET
|
||||
suspend fun fetchUpdateJSON(@Url file: String): UpdateInfo
|
||||
}
|
||||
|
||||
interface RawServices {
|
||||
|
||||
@GET
|
||||
@Streaming
|
||||
suspend fun fetchFile(@Url url: String): ResponseBody
|
||||
|
||||
@GET
|
||||
suspend fun fetchString(@Url url: String): String
|
||||
|
||||
@GET
|
||||
suspend fun fetchModuleJson(@Url url: String): ModuleJson
|
||||
|
||||
}
|
||||
|
||||
interface GithubApiServices {
|
||||
|
||||
@GET("repos/{$REPO}/branches/{$BRANCH}")
|
||||
@Headers("Accept: application/vnd.github.v3+json")
|
||||
suspend fun fetchBranch(
|
||||
@Path(REPO, encoded = true) repo: String,
|
||||
@Path(BRANCH) branch: String
|
||||
): BranchInfo
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
package com.topjohnwu.magisk.core.data
|
||||
|
||||
import com.topjohnwu.magisk.core.model.ModuleJson
|
||||
import com.topjohnwu.magisk.core.model.Release
|
||||
import com.topjohnwu.magisk.core.model.UpdateJson
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Headers
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
import retrofit2.http.Streaming
|
||||
import retrofit2.http.Url
|
||||
|
||||
interface RawUrl {
|
||||
|
||||
@GET
|
||||
@Streaming
|
||||
suspend fun fetchFile(@Url url: String): ResponseBody
|
||||
|
||||
@GET
|
||||
suspend fun fetchString(@Url url: String): String
|
||||
|
||||
@GET
|
||||
suspend fun fetchModuleJson(@Url url: String): ModuleJson
|
||||
|
||||
@GET
|
||||
suspend fun fetchUpdateJson(@Url url: String): UpdateJson
|
||||
}
|
||||
|
||||
interface GithubApiServices {
|
||||
|
||||
@GET("/repos/{owner}/{repo}/releases")
|
||||
@Headers("Accept: application/vnd.github+json")
|
||||
suspend fun fetchReleases(
|
||||
@Path("owner") owner: String = "topjohnwu",
|
||||
@Path("repo") repo: String = "Magisk",
|
||||
@Query("per_page") per: Int = 10,
|
||||
@Query("page") page: Int = 1,
|
||||
): Response<MutableList<Release>>
|
||||
|
||||
@GET("/repos/{owner}/{repo}/releases/latest")
|
||||
@Headers("Accept: application/vnd.github+json")
|
||||
suspend fun fetchLatestRelease(
|
||||
@Path("owner") owner: String = "topjohnwu",
|
||||
@Path("repo") repo: String = "Magisk",
|
||||
): Release
|
||||
}
|
@@ -7,9 +7,13 @@ import kotlinx.coroutines.withContext
|
||||
|
||||
open class MagiskDB {
|
||||
|
||||
suspend fun <R> exec(
|
||||
class Literal(
|
||||
val str: String
|
||||
)
|
||||
|
||||
suspend inline fun <R> exec(
|
||||
query: String,
|
||||
mapper: suspend (Map<String, String>) -> R
|
||||
crossinline mapper: (Map<String, String>) -> R
|
||||
): List<R> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val out = Shell.cmd("magisk --sqlite '$query'").await().out
|
||||
@@ -18,13 +22,15 @@ open class MagiskDB {
|
||||
.map { it.split("=", limit = 2) }
|
||||
.filter { it.size == 2 }
|
||||
.associate { it[0] to it[1] }
|
||||
.let { mapper(it) }
|
||||
.let(mapper)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend inline fun exec(query: String) {
|
||||
exec(query) {}
|
||||
suspend fun exec(query: String) {
|
||||
withContext(Dispatchers.IO) {
|
||||
Shell.cmd("magisk --sqlite '$query'").await()
|
||||
}
|
||||
}
|
||||
|
||||
fun Map<String, Any>.toQuery(): String {
|
||||
@@ -33,6 +39,7 @@ open class MagiskDB {
|
||||
when (it) {
|
||||
is Boolean -> if (it) "1" else "0"
|
||||
is Number -> it.toString()
|
||||
is Literal -> it.str
|
||||
else -> "\"$it\""
|
||||
}
|
||||
}
|
||||
|
@@ -3,24 +3,24 @@ package com.topjohnwu.magisk.core.data.magiskdb
|
||||
import com.topjohnwu.magisk.core.AppContext
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private const val SELECT_QUERY = "SELECT (until - strftime(\"%s\", \"now\")) AS remain, *"
|
||||
|
||||
class PolicyDao : MagiskDB() {
|
||||
|
||||
suspend fun deleteOutdated() {
|
||||
val nowSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())
|
||||
val query = "DELETE FROM ${Table.POLICY} WHERE " +
|
||||
"(until > 0 AND until < $nowSeconds) OR until < 0"
|
||||
"(until > 0 AND until < strftime(\"%s\", \"now\")) OR until < 0"
|
||||
exec(query)
|
||||
}
|
||||
|
||||
suspend fun delete(uid: Int) {
|
||||
val query = "DELETE FROM ${Table.POLICY} WHERE uid == $uid"
|
||||
val query = "DELETE FROM ${Table.POLICY} WHERE uid=$uid"
|
||||
exec(query)
|
||||
}
|
||||
|
||||
suspend fun fetch(uid: Int): SuPolicy? {
|
||||
val query = "SELECT * FROM ${Table.POLICY} WHERE uid == $uid LIMIT = 1"
|
||||
val query = "$SELECT_QUERY FROM ${Table.POLICY} WHERE uid=$uid LIMIT 1"
|
||||
return exec(query, ::toPolicy).firstOrNull()
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ class PolicyDao : MagiskDB() {
|
||||
}
|
||||
|
||||
suspend fun fetchAll(): List<SuPolicy> {
|
||||
val query = "SELECT * FROM ${Table.POLICY} WHERE uid/100000 == ${Const.USER_ID}"
|
||||
val query = "$SELECT_QUERY FROM ${Table.POLICY} WHERE uid/100000=${Const.USER_ID}"
|
||||
return exec(query, ::toPolicy).filterNotNull()
|
||||
}
|
||||
|
||||
@@ -43,8 +43,15 @@ class PolicyDao : MagiskDB() {
|
||||
val uid = map["uid"]?.toInt() ?: return null
|
||||
val policy = SuPolicy(uid)
|
||||
|
||||
map["until"]?.toLong()?.let { until ->
|
||||
if (until <= 0) {
|
||||
policy.remain = until
|
||||
} else {
|
||||
map["remain"]?.toLong()?.let { policy.remain = it }
|
||||
}
|
||||
}
|
||||
|
||||
map["policy"]?.toInt()?.let { policy.policy = it }
|
||||
map["until"]?.toLong()?.let { policy.until = it }
|
||||
map["logging"]?.toInt()?.let { policy.logging = it != 0 }
|
||||
map["notification"]?.toInt()?.let { policy.notification = it != 0 }
|
||||
return policy
|
||||
|
@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.core.data.magiskdb
|
||||
class SettingsDao : MagiskDB() {
|
||||
|
||||
suspend fun delete(key: String) {
|
||||
val query = "DELETE FROM ${Table.SETTINGS} WHERE key == \"$key\""
|
||||
val query = "DELETE FROM ${Table.SETTINGS} WHERE key=\"$key\""
|
||||
exec(query)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class SettingsDao : MagiskDB() {
|
||||
}
|
||||
|
||||
suspend fun fetch(key: String, default: Int = -1): Int {
|
||||
val query = "SELECT value FROM ${Table.SETTINGS} WHERE key == \"$key\" LIMIT 1"
|
||||
val query = "SELECT value FROM ${Table.SETTINGS} WHERE key=\"$key\" LIMIT 1"
|
||||
return exec(query) { it["value"]?.toInt() }.firstOrNull() ?: default
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.core.data.magiskdb
|
||||
class StringDao : MagiskDB() {
|
||||
|
||||
suspend fun delete(key: String) {
|
||||
val query = "DELETE FROM ${Table.STRINGS} WHERE key == \"$key\""
|
||||
val query = "DELETE FROM ${Table.STRINGS} WHERE key=\"$key\""
|
||||
exec(query)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class StringDao : MagiskDB() {
|
||||
}
|
||||
|
||||
suspend fun fetch(key: String, default: String = ""): String {
|
||||
val query = "SELECT value FROM ${Table.STRINGS} WHERE key == \"$key\" LIMIT 1"
|
||||
val query = "SELECT value FROM ${Table.STRINGS} WHERE key=\"$key\" LIMIT 1"
|
||||
return exec(query) { it["value"] }.firstOrNull() ?: default
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ import com.squareup.moshi.Moshi
|
||||
import com.topjohnwu.magisk.ProviderInstaller
|
||||
import com.topjohnwu.magisk.core.BuildConfig
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.model.DateTimeAdapter
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
import okhttp3.Cache
|
||||
import okhttp3.ConnectionSpec
|
||||
@@ -72,15 +72,13 @@ fun createOkHttpClient(context: Context): OkHttpClient {
|
||||
chain.proceed(request.build())
|
||||
}
|
||||
|
||||
if (!ProviderInstaller.install(context)) {
|
||||
Info.hasGMS = false
|
||||
}
|
||||
ProviderInstaller.install(context)
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
fun createMoshiConverterFactory(): MoshiConverterFactory {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val moshi = Moshi.Builder().add(DateTimeAdapter()).build()
|
||||
return MoshiConverterFactory.create(moshi)
|
||||
}
|
||||
|
||||
|
@@ -35,8 +35,8 @@ object ServiceLocator {
|
||||
val markwon by lazy { createMarkwon(AppContext) }
|
||||
val networkService by lazy {
|
||||
NetworkService(
|
||||
createApiService(retrofit, Const.Url.GITHUB_PAGE_URL),
|
||||
createApiService(retrofit, Const.Url.GITHUB_RAW_URL),
|
||||
createApiService(retrofit, Const.Url.INVALID_URL),
|
||||
createApiService(retrofit, Const.Url.GITHUB_API_URL),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@ import android.app.PendingIntent
|
||||
import android.app.job.JobInfo
|
||||
import android.app.job.JobScheduler
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
@@ -16,7 +15,6 @@ import androidx.collection.isNotEmpty
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.topjohnwu.magisk.StubApk
|
||||
import com.topjohnwu.magisk.core.AppContext
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.JobService
|
||||
@@ -25,18 +23,8 @@ import com.topjohnwu.magisk.core.base.IActivityExtension
|
||||
import com.topjohnwu.magisk.core.cmp
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.intent
|
||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||
import com.topjohnwu.magisk.core.ktx.cachedFile
|
||||
import com.topjohnwu.magisk.core.ktx.copyAll
|
||||
import com.topjohnwu.magisk.core.ktx.copyAndClose
|
||||
import com.topjohnwu.magisk.core.ktx.forEach
|
||||
import com.topjohnwu.magisk.core.ktx.set
|
||||
import com.topjohnwu.magisk.core.ktx.withStreams
|
||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
||||
import com.topjohnwu.magisk.core.tasks.AppMigration
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||
import com.topjohnwu.magisk.core.utils.ProgressInputStream
|
||||
import com.topjohnwu.magisk.utils.APKInstall
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -44,13 +32,7 @@ import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.ResponseBody
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipInputStream
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
/**
|
||||
* This class drives the execution of file downloads and notification management.
|
||||
@@ -69,16 +51,7 @@ import java.util.zip.ZipOutputStream
|
||||
* For API 23 - 33, we use a foreground service as a session.
|
||||
* For API 34 and higher, we use user-initiated job services as a session.
|
||||
*/
|
||||
class DownloadEngine(
|
||||
private val session: Session
|
||||
) {
|
||||
|
||||
interface Session {
|
||||
val context: Context
|
||||
|
||||
fun attachNotification(id: Int, builder: Notification.Builder)
|
||||
fun onDownloadComplete()
|
||||
}
|
||||
class DownloadEngine(session: DownloadSession) : DownloadSession by session, DownloadNotifier {
|
||||
|
||||
companion object {
|
||||
const val ACTION = "com.topjohnwu.magisk.DOWNLOAD"
|
||||
@@ -99,33 +72,35 @@ class DownloadEngine(
|
||||
}
|
||||
}
|
||||
|
||||
private fun createIntent(context: Context, subject: Subject) =
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
context.intent<com.topjohnwu.magisk.core.Receiver>()
|
||||
.setAction(ACTION)
|
||||
.putExtra(SUBJECT_KEY, subject)
|
||||
} else {
|
||||
context.intent<com.topjohnwu.magisk.core.Service>()
|
||||
.setAction(ACTION)
|
||||
.putExtra(SUBJECT_KEY, subject)
|
||||
}
|
||||
private fun createBroadcastIntent(context: Context, subject: Subject) =
|
||||
context.intent<com.topjohnwu.magisk.core.Receiver>()
|
||||
.setAction(ACTION)
|
||||
.putExtra(SUBJECT_KEY, subject)
|
||||
|
||||
private fun createServiceIntent(context: Context, subject: Subject) =
|
||||
context.intent<com.topjohnwu.magisk.core.Service>()
|
||||
.setAction(ACTION)
|
||||
.putExtra(SUBJECT_KEY, subject)
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
fun getPendingIntent(context: Context, subject: Subject): PendingIntent {
|
||||
val flag = PendingIntent.FLAG_IMMUTABLE or
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or
|
||||
PendingIntent.FLAG_ONE_SHOT
|
||||
val intent = createIntent(context, subject)
|
||||
return if (Build.VERSION.SDK_INT >= 34) {
|
||||
// On API 34+, download tasks are handled with a user-initiated job.
|
||||
// However, there is no way to schedule a new job directly with a pending intent.
|
||||
// As a workaround, we send the subject to a broadcast receiver and have it
|
||||
// schedule the job for us.
|
||||
val intent = createBroadcastIntent(context, subject)
|
||||
PendingIntent.getBroadcast(context, REQUEST_CODE, intent, flag)
|
||||
} else if (Build.VERSION.SDK_INT >= 26) {
|
||||
PendingIntent.getForegroundService(context, REQUEST_CODE, intent, flag)
|
||||
} else {
|
||||
PendingIntent.getService(context, REQUEST_CODE, intent, flag)
|
||||
val intent = createServiceIntent(context, subject)
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
PendingIntent.getForegroundService(context, REQUEST_CODE, intent, flag)
|
||||
} else {
|
||||
PendingIntent.getService(context, REQUEST_CODE, intent, flag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +115,7 @@ class DownloadEngine(
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
fun start(context: Context, subject: Subject) {
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
val scheduler = context.getSystemService<JobScheduler>()!!
|
||||
@@ -152,24 +128,29 @@ class DownloadEngine(
|
||||
.setTransientExtras(extras)
|
||||
.build()
|
||||
scheduler.schedule(info)
|
||||
} else if (Build.VERSION.SDK_INT >= 26) {
|
||||
context.startForegroundService(createIntent(context, subject))
|
||||
} else {
|
||||
context.startService(createIntent(context, subject))
|
||||
val intent = createServiceIntent(context, subject)
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
context.startForegroundService(intent)
|
||||
} else {
|
||||
context.startService(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val notifications = SparseArrayCompat<Notification.Builder>()
|
||||
private var attachedId = -1
|
||||
private val job = Job()
|
||||
private val processor = DownloadProcessor(this)
|
||||
private val network get() = ServiceLocator.networkService
|
||||
|
||||
fun download(subject: Subject) {
|
||||
notifyUpdate(subject.notifyId)
|
||||
CoroutineScope(job + Dispatchers.IO).launch {
|
||||
try {
|
||||
val stream = network.fetchFile(subject.url).toProgressStream(subject)
|
||||
when (subject) {
|
||||
is Subject.App -> handleApp(stream, subject)
|
||||
is Subject.Module -> handleModule(stream, subject.file)
|
||||
else -> stream.copyAndClose(subject.file.outputStream())
|
||||
}
|
||||
processor.handle(stream, subject)
|
||||
val activity = AppContext.foregroundActivity
|
||||
if (activity != null && subject.autoLaunch) {
|
||||
notifyRemove(subject.notifyId)
|
||||
@@ -187,16 +168,13 @@ class DownloadEngine(
|
||||
@Synchronized
|
||||
fun reattach() {
|
||||
val builder = notifications[attachedId] ?: return
|
||||
session.attachNotification(attachedId, builder)
|
||||
attachNotification(attachedId, builder)
|
||||
}
|
||||
|
||||
private val notifications = SparseArrayCompat<Notification.Builder>()
|
||||
private var attachedId = -1
|
||||
|
||||
private val job = Job()
|
||||
|
||||
private val context get() = session.context
|
||||
private val network get() = ServiceLocator.networkService
|
||||
private fun attach(id: Int, notification: Notification.Builder) {
|
||||
attachedId = id
|
||||
attachNotification(id, notification)
|
||||
}
|
||||
|
||||
private fun finalNotify(id: Int, editor: (Notification.Builder) -> Unit): Int {
|
||||
val notification = notifyRemove(id)?.also(editor) ?: return -1
|
||||
@@ -223,19 +201,14 @@ class DownloadEngine(
|
||||
subject.pendingIntent(context)?.let { intent -> it.setContentIntent(intent) }
|
||||
}
|
||||
|
||||
private fun attachNotification(id: Int, notification: Notification.Builder) {
|
||||
attachedId = id
|
||||
session.attachNotification(id, notification)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun notifyUpdate(id: Int, editor: (Notification.Builder) -> Unit = {}) {
|
||||
override fun notifyUpdate(id: Int, editor: (Notification.Builder) -> Unit) {
|
||||
val notification = (notifications[id] ?: Notifications.startProgress("").also {
|
||||
notifications[id] = it
|
||||
}).apply(editor)
|
||||
|
||||
if (attachedId < 0)
|
||||
attachNotification(id, notification)
|
||||
attach(id, notification)
|
||||
else
|
||||
Notifications.mgr.notify(id, notification.build())
|
||||
}
|
||||
@@ -255,11 +228,11 @@ class DownloadEngine(
|
||||
// There are still remaining notifications, pick one and attach to the session
|
||||
val anotherId = notifications.keyAt(0)
|
||||
val notification = notifications.valueAt(0)
|
||||
attachNotification(anotherId, notification)
|
||||
attach(anotherId, notification)
|
||||
} else {
|
||||
// No more notifications left, terminate the session
|
||||
attachedId = -1
|
||||
session.onDownloadComplete()
|
||||
onDownloadComplete()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,90 +241,6 @@ class DownloadEngine(
|
||||
return n
|
||||
}
|
||||
|
||||
private suspend fun handleApp(stream: InputStream, subject: Subject.App) {
|
||||
val external = subject.file.outputStream()
|
||||
|
||||
if (isRunningAsStub) {
|
||||
val updateApk = StubApk.update(context)
|
||||
try {
|
||||
// Download full APK to stub update path
|
||||
stream.copyAndClose(TeeOutputStream(external, updateApk.outputStream()))
|
||||
|
||||
// Also upgrade stub
|
||||
notifyUpdate(subject.notifyId) {
|
||||
it.setProgress(0, 0, true)
|
||||
.setContentTitle(context.getString(R.string.hide_app_title))
|
||||
.setContentText("")
|
||||
}
|
||||
|
||||
// Extract stub
|
||||
val zf = ZipFile(updateApk)
|
||||
val apk = context.cachedFile("stub.apk")
|
||||
apk.delete()
|
||||
zf.getInputStream(zf.getEntry("assets/stub.apk")).writeTo(apk)
|
||||
zf.close()
|
||||
|
||||
// Patch and install
|
||||
subject.intent = AppMigration.upgradeStub(context, apk)
|
||||
?: throw IOException("HideAPK patch error")
|
||||
apk.delete()
|
||||
} catch (e: Exception) {
|
||||
// If any error occurred, do not let stub load the new APK
|
||||
updateApk.delete()
|
||||
throw e
|
||||
}
|
||||
} else {
|
||||
val session = APKInstall.startSession(context)
|
||||
stream.copyAndClose(TeeOutputStream(external, session.openStream(context)))
|
||||
subject.intent = session.waitIntent()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleModule(src: InputStream, file: Uri) {
|
||||
val input = ZipInputStream(src)
|
||||
val output = ZipOutputStream(file.outputStream())
|
||||
|
||||
withStreams(input, output) { zin, zout ->
|
||||
zout.putNextEntry(ZipEntry("META-INF/"))
|
||||
zout.putNextEntry(ZipEntry("META-INF/com/"))
|
||||
zout.putNextEntry(ZipEntry("META-INF/com/google/"))
|
||||
zout.putNextEntry(ZipEntry("META-INF/com/google/android/"))
|
||||
zout.putNextEntry(ZipEntry("META-INF/com/google/android/update-binary"))
|
||||
context.assets.open("module_installer.sh").use { it.copyAll(zout) }
|
||||
|
||||
zout.putNextEntry(ZipEntry("META-INF/com/google/android/updater-script"))
|
||||
zout.write("#MAGISK\n".toByteArray())
|
||||
|
||||
zin.forEach { entry ->
|
||||
val path = entry.name
|
||||
if (path.isNotEmpty() && !path.startsWith("META-INF")) {
|
||||
zout.putNextEntry(ZipEntry(path))
|
||||
if (!entry.isDirectory) {
|
||||
zin.copyAll(zout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TeeOutputStream(
|
||||
private val o1: OutputStream,
|
||||
private val o2: OutputStream
|
||||
) : OutputStream() {
|
||||
override fun write(b: Int) {
|
||||
o1.write(b)
|
||||
o2.write(b)
|
||||
}
|
||||
override fun write(b: ByteArray?, off: Int, len: Int) {
|
||||
o1.write(b, off, len)
|
||||
o2.write(b, off, len)
|
||||
}
|
||||
override fun close() {
|
||||
o1.close()
|
||||
o2.close()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ResponseBody.toProgressStream(subject: Subject): InputStream {
|
||||
val max = contentLength()
|
||||
val total = max.toFloat() / 1048576
|
||||
|
@@ -0,0 +1,122 @@
|
||||
package com.topjohnwu.magisk.core.download
|
||||
|
||||
import android.net.Uri
|
||||
import com.topjohnwu.magisk.StubApk
|
||||
import com.topjohnwu.magisk.core.R
|
||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||
import com.topjohnwu.magisk.core.ktx.cachedFile
|
||||
import com.topjohnwu.magisk.core.ktx.copyAll
|
||||
import com.topjohnwu.magisk.core.ktx.copyAndClose
|
||||
import com.topjohnwu.magisk.core.ktx.withInOut
|
||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
||||
import com.topjohnwu.magisk.core.tasks.AppMigration
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||
import com.topjohnwu.magisk.utils.APKInstall
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
class DownloadProcessor(notifier: DownloadNotifier) : DownloadNotifier by notifier {
|
||||
|
||||
suspend fun handle(stream: InputStream, subject: Subject) {
|
||||
when (subject) {
|
||||
is Subject.App -> handleApp(stream, subject)
|
||||
is Subject.Module -> handleModule(stream, subject.file)
|
||||
else -> stream.copyAndClose(subject.file.outputStream())
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun handleApp(stream: InputStream, subject: Subject.App) {
|
||||
val external = subject.file.outputStream()
|
||||
|
||||
if (isRunningAsStub) {
|
||||
val updateApk = StubApk.update(context)
|
||||
try {
|
||||
// Download full APK to stub update path
|
||||
stream.copyAndClose(TeeOutputStream(external, updateApk.outputStream()))
|
||||
|
||||
// Also upgrade stub
|
||||
notifyUpdate(subject.notifyId) {
|
||||
it.setProgress(0, 0, true)
|
||||
.setContentTitle(context.getString(R.string.hide_app_title))
|
||||
.setContentText("")
|
||||
}
|
||||
|
||||
// Extract stub
|
||||
val apk = context.cachedFile("stub.apk")
|
||||
ZipFile.Builder().setFile(updateApk).get().use { zf ->
|
||||
apk.delete()
|
||||
zf.getInputStream(zf.getEntry("assets/stub.apk")).writeTo(apk)
|
||||
}
|
||||
|
||||
// Patch and install
|
||||
subject.intent = AppMigration.upgradeStub(context, apk)
|
||||
?: throw IOException("HideAPK patch error")
|
||||
apk.delete()
|
||||
} catch (e: Exception) {
|
||||
// If any error occurred, do not let stub load the new APK
|
||||
updateApk.delete()
|
||||
throw e
|
||||
}
|
||||
} else {
|
||||
val session = APKInstall.startSession(context)
|
||||
stream.copyAndClose(TeeOutputStream(external, session.openStream(context)))
|
||||
subject.intent = session.waitIntent()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun handleModule(src: InputStream, file: Uri) {
|
||||
val tmp = context.cachedFile("module.zip")
|
||||
try {
|
||||
// First download the entire zip into cache so we can process it
|
||||
src.writeTo(tmp)
|
||||
|
||||
val input = ZipFile.Builder().setFile(tmp).get()
|
||||
val output = ZipArchiveOutputStream(file.outputStream())
|
||||
withInOut(input, output) { zin, zout ->
|
||||
zout.putArchiveEntry(ZipArchiveEntry("META-INF/"))
|
||||
zout.closeArchiveEntry()
|
||||
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/"))
|
||||
zout.closeArchiveEntry()
|
||||
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/"))
|
||||
zout.closeArchiveEntry()
|
||||
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/"))
|
||||
zout.closeArchiveEntry()
|
||||
|
||||
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/update-binary"))
|
||||
context.assets.open("module_installer.sh").use { it.copyAll(zout) }
|
||||
zout.closeArchiveEntry()
|
||||
|
||||
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/updater-script"))
|
||||
zout.write("#MAGISK\n".toByteArray())
|
||||
zout.closeArchiveEntry()
|
||||
|
||||
// Then simply copy all entries to output
|
||||
zin.copyRawEntries(zout) { entry -> !entry.name.startsWith("META-INF") }
|
||||
}
|
||||
} finally {
|
||||
tmp.delete()
|
||||
}
|
||||
}
|
||||
|
||||
private class TeeOutputStream(
|
||||
private val o1: OutputStream,
|
||||
private val o2: OutputStream
|
||||
) : OutputStream() {
|
||||
override fun write(b: Int) {
|
||||
o1.write(b)
|
||||
o2.write(b)
|
||||
}
|
||||
override fun write(b: ByteArray?, off: Int, len: Int) {
|
||||
o1.write(b, off, len)
|
||||
o2.write(b, off, len)
|
||||
}
|
||||
override fun close() {
|
||||
o1.close()
|
||||
o2.close()
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package com.topjohnwu.magisk.core.download
|
||||
|
||||
import android.app.Notification
|
||||
import android.content.Context
|
||||
|
||||
interface DownloadSession {
|
||||
val context: Context
|
||||
fun attachNotification(id: Int, builder: Notification.Builder)
|
||||
fun onDownloadComplete()
|
||||
}
|
||||
|
||||
interface DownloadNotifier {
|
||||
val context: Context
|
||||
fun notifyUpdate(id: Int, editor: (Notification.Builder) -> Unit = {})
|
||||
}
|
@@ -8,7 +8,7 @@ import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import androidx.core.net.toUri
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.model.MagiskJson
|
||||
import com.topjohnwu.magisk.core.model.UpdateInfo
|
||||
import com.topjohnwu.magisk.core.model.module.OnlineModule
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
@@ -38,7 +38,7 @@ abstract class Subject : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
class App(
|
||||
private val json: MagiskJson = Info.remote.magisk,
|
||||
private val json: UpdateInfo = Info.update,
|
||||
override val notifyId: Int = Notifications.nextId()
|
||||
) : Subject() {
|
||||
override val title: String get() = "Magisk-${json.version}(${json.versionCode})"
|
||||
|
@@ -2,7 +2,11 @@ package com.topjohnwu.magisk.core.ktx
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.*
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
@@ -23,7 +27,6 @@ import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.magisk.utils.APKInstall
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
import java.io.File
|
||||
import kotlin.String
|
||||
|
||||
fun Context.getBitmap(id: Int): Bitmap {
|
||||
var drawable = getDrawable(id)!!
|
||||
@@ -106,7 +109,7 @@ fun PackageManager.getPackageInfo(uid: Int, pid: Int): PackageInfo? {
|
||||
return null
|
||||
}
|
||||
// Try to find package name from PID
|
||||
val proc = RootUtils.obj?.getAppProcess(pid)
|
||||
val proc = RootUtils.getAppProcess(pid)
|
||||
if (proc == null) {
|
||||
if (uid == Process.SHELL_UID) {
|
||||
// It is possible that some apps installed are sharing UID with shell.
|
||||
|
@@ -8,33 +8,25 @@ import kotlinx.coroutines.flow.flatMapMerge
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.lang.reflect.Field
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import java.util.Collections
|
||||
import java.util.Locale
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
inline fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) {
|
||||
var entry: ZipEntry? = nextEntry
|
||||
while (entry != null) {
|
||||
callback(entry)
|
||||
entry = nextEntry
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <In : InputStream, Out : OutputStream> withStreams(
|
||||
inStream: In,
|
||||
outStream: Out,
|
||||
inline fun <In : Closeable, Out : Closeable> withInOut(
|
||||
input: In,
|
||||
output: Out,
|
||||
withBoth: (In, Out) -> Unit
|
||||
) {
|
||||
inStream.use { reader ->
|
||||
outStream.use { writer ->
|
||||
input.use { reader ->
|
||||
output.use { writer ->
|
||||
withBoth(reader, writer)
|
||||
}
|
||||
}
|
||||
@@ -64,7 +56,7 @@ suspend inline fun InputStream.copyAndClose(
|
||||
out: OutputStream,
|
||||
bufferSize: Int = DEFAULT_BUFFER_SIZE,
|
||||
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
) = withStreams(this, out) { i, o -> i.copyAll(o, bufferSize, dispatcher) }
|
||||
) = withInOut(this, out) { i, o -> i.copyAll(o, bufferSize, dispatcher) }
|
||||
|
||||
@Throws(IOException::class)
|
||||
suspend inline fun InputStream.writeTo(
|
||||
@@ -92,19 +84,15 @@ inline fun <T, R> Flow<T>.concurrentMap(crossinline transform: suspend (T) -> R)
|
||||
}
|
||||
}
|
||||
|
||||
fun Long.toTime(format: DateFormat) = format.format(this).orEmpty()
|
||||
fun Long.toTime(format: DateTimeFormatter): String = format.format(Instant.ofEpochMilli(this))
|
||||
|
||||
// Some devices don't allow filenames containing ":"
|
||||
val timeFormatStandard by lazy {
|
||||
SimpleDateFormat(
|
||||
"yyyy-MM-dd'T'HH.mm.ss",
|
||||
Locale.ROOT
|
||||
)
|
||||
val timeFormatStandard: DateTimeFormatter by lazy {
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH.mm.ss").withZone(ZoneId.systemDefault())
|
||||
}
|
||||
val timeDateFormat: DateFormat by lazy {
|
||||
DateFormat.getDateTimeInstance(
|
||||
DateFormat.DEFAULT,
|
||||
DateFormat.DEFAULT,
|
||||
Locale.ROOT
|
||||
)
|
||||
val timeDateFormat: DateTimeFormatter by lazy {
|
||||
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withZone(ZoneId.systemDefault())
|
||||
}
|
||||
val dateFormat: DateTimeFormatter by lazy {
|
||||
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withZone(ZoneId.systemDefault())
|
||||
}
|
||||
|
@@ -1,17 +1,22 @@
|
||||
package com.topjohnwu.magisk.core.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.squareup.moshi.FromJson
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.JsonQualifier
|
||||
import com.squareup.moshi.ToJson
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.time.Instant
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class UpdateInfo(
|
||||
val magisk: MagiskJson = MagiskJson(),
|
||||
class UpdateJson(
|
||||
val magisk: UpdateInfo = UpdateInfo(),
|
||||
)
|
||||
|
||||
@Parcelize
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MagiskJson(
|
||||
data class UpdateInfo(
|
||||
val version: String = "",
|
||||
val versionCode: Int = -1,
|
||||
val link: String = "",
|
||||
@@ -27,11 +32,37 @@ data class ModuleJson(
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CommitInfo(
|
||||
val sha: String
|
||||
data class ReleaseAssets(
|
||||
val name: String,
|
||||
@Json(name = "browser_download_url") val url: String,
|
||||
)
|
||||
|
||||
class DateTimeAdapter {
|
||||
@ToJson
|
||||
fun toJson(date: Instant): String {
|
||||
return date.toString()
|
||||
}
|
||||
|
||||
@FromJson
|
||||
fun fromJson(date: String): Instant {
|
||||
return Instant.parse(date)
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class BranchInfo(
|
||||
val commit: CommitInfo
|
||||
)
|
||||
data class Release(
|
||||
@Json(name = "tag_name") val tag: String,
|
||||
val name: String,
|
||||
val prerelease: Boolean,
|
||||
val assets: List<ReleaseAssets>,
|
||||
val body: String,
|
||||
@Json(name = "created_at") val createdTime: Instant,
|
||||
) {
|
||||
val versionCode: Int get() {
|
||||
return if (tag[0] == 'v') {
|
||||
(tag.drop(1).toFloat() * 1000).toInt()
|
||||
} else {
|
||||
tag.drop(7).toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,14 +5,15 @@ import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.nio.ExtendedFile
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
data class LocalModule(
|
||||
private val path: String,
|
||||
val base: ExtendedFile,
|
||||
) : Module() {
|
||||
private val svc get() = ServiceLocator.networkService
|
||||
|
||||
@@ -24,20 +25,18 @@ data class LocalModule(
|
||||
var description: String = ""
|
||||
var updateInfo: OnlineModule? = null
|
||||
var outdated = false
|
||||
|
||||
private var updateUrl: String = ""
|
||||
private val removeFile = RootUtils.fs.getFile(path, "remove")
|
||||
private val disableFile = RootUtils.fs.getFile(path, "disable")
|
||||
private val updateFile = RootUtils.fs.getFile(path, "update")
|
||||
private val riruFolder = RootUtils.fs.getFile(path, "riru")
|
||||
private val zygiskFolder = RootUtils.fs.getFile(path, "zygisk")
|
||||
private val unloaded = RootUtils.fs.getFile(zygiskFolder, "unloaded")
|
||||
|
||||
val updated: Boolean get() = updateFile.exists()
|
||||
val isRiru: Boolean get() = (id == "riru-core") || riruFolder.exists()
|
||||
val isZygisk: Boolean get() = zygiskFolder.exists()
|
||||
val zygiskUnloaded: Boolean get() = unloaded.exists()
|
||||
val hasAction: Boolean;
|
||||
private val removeFile = base.getChildFile("remove")
|
||||
private val disableFile = base.getChildFile("disable")
|
||||
private val updateFile = base.getChildFile("update")
|
||||
val zygiskFolder = base.getChildFile("zygisk")
|
||||
|
||||
val updated get() = updateFile.exists()
|
||||
val isRiru = (id == "riru-core") || base.getChildFile("riru").exists()
|
||||
val isZygisk = zygiskFolder.exists()
|
||||
val zygiskUnloaded = zygiskFolder.getChildFile("unloaded").exists()
|
||||
val hasAction = base.getChildFile("action.sh").exists()
|
||||
|
||||
var enable: Boolean
|
||||
get() = !disableFile.exists()
|
||||
@@ -90,19 +89,16 @@ data class LocalModule(
|
||||
|
||||
init {
|
||||
runCatching {
|
||||
parseProps(Shell.cmd("dos2unix < $path/module.prop").exec().out)
|
||||
parseProps(Shell.cmd("dos2unix < $base/module.prop").exec().out)
|
||||
}
|
||||
|
||||
if (id.isEmpty()) {
|
||||
val sep = path.lastIndexOf('/')
|
||||
id = path.substring(sep + 1)
|
||||
id = base.name
|
||||
}
|
||||
|
||||
if (name.isEmpty()) {
|
||||
name = id
|
||||
}
|
||||
|
||||
hasAction = RootUtils.fs.getFile(path, "action.sh").exists()
|
||||
}
|
||||
|
||||
suspend fun fetch(): Boolean {
|
||||
@@ -125,14 +121,14 @@ data class LocalModule(
|
||||
|
||||
companion object {
|
||||
|
||||
fun loaded() = RootUtils.fs.getFile(Const.MAGISK_PATH).exists()
|
||||
fun loaded() = RootUtils.fs.getFile(Const.MODULE_PATH).exists()
|
||||
|
||||
suspend fun installed() = withContext(Dispatchers.IO) {
|
||||
RootUtils.fs.getFile(Const.MAGISK_PATH)
|
||||
RootUtils.fs.getFile(Const.MODULE_PATH)
|
||||
.listFiles()
|
||||
.orEmpty()
|
||||
.filter { !it.isFile && !it.isHidden }
|
||||
.map { LocalModule("${Const.MAGISK_PATH}/${it.name}") }
|
||||
.map { LocalModule(it) }
|
||||
.sortedBy { it.name.lowercase(Locale.ROOT) }
|
||||
}
|
||||
}
|
||||
|
@@ -1,22 +1,33 @@
|
||||
package com.topjohnwu.magisk.core.model.su
|
||||
|
||||
class SuPolicy(val uid: Int) {
|
||||
import com.topjohnwu.magisk.core.data.magiskdb.MagiskDB
|
||||
|
||||
class SuPolicy(
|
||||
val uid: Int,
|
||||
var policy: Int = QUERY,
|
||||
var remain: Long = -1L,
|
||||
var logging: Boolean = true,
|
||||
var notification: Boolean = true,
|
||||
) {
|
||||
companion object {
|
||||
const val INTERACTIVE = 0
|
||||
const val QUERY = 0
|
||||
const val DENY = 1
|
||||
const val ALLOW = 2
|
||||
const val RESTRICT = 3
|
||||
}
|
||||
|
||||
var policy: Int = INTERACTIVE
|
||||
var until: Long = -1L
|
||||
var logging: Boolean = true
|
||||
var notification: Boolean = true
|
||||
|
||||
fun toMap(): MutableMap<String, Any> = mutableMapOf(
|
||||
"uid" to uid,
|
||||
"policy" to policy,
|
||||
"until" to until,
|
||||
"logging" to logging,
|
||||
"notification" to notification
|
||||
)
|
||||
fun toMap(): MutableMap<String, Any> {
|
||||
val until = if (remain <= 0) {
|
||||
remain
|
||||
} else {
|
||||
MagiskDB.Literal("(strftime(\"%s\", \"now\") + $remain)")
|
||||
}
|
||||
return mutableMapOf(
|
||||
"uid" to uid,
|
||||
"policy" to policy,
|
||||
"until" to until,
|
||||
"logging" to logging,
|
||||
"notification" to notification
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -1,46 +1,112 @@
|
||||
package com.topjohnwu.magisk.core.repository
|
||||
|
||||
import com.topjohnwu.magisk.core.BuildConfig
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Config.Value.BETA_CHANNEL
|
||||
import com.topjohnwu.magisk.core.Config.Value.CANARY_CHANNEL
|
||||
import com.topjohnwu.magisk.core.Config.Value.CUSTOM_CHANNEL
|
||||
import com.topjohnwu.magisk.core.Config.Value.DEBUG_CHANNEL
|
||||
import com.topjohnwu.magisk.core.Config.Value.DEFAULT_CHANNEL
|
||||
import com.topjohnwu.magisk.core.Config.Value.STABLE_CHANNEL
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.data.GithubPageServices
|
||||
import com.topjohnwu.magisk.core.data.RawServices
|
||||
import com.topjohnwu.magisk.core.data.GithubApiServices
|
||||
import com.topjohnwu.magisk.core.data.RawUrl
|
||||
import com.topjohnwu.magisk.core.ktx.dateFormat
|
||||
import com.topjohnwu.magisk.core.model.Release
|
||||
import com.topjohnwu.magisk.core.model.ReleaseAssets
|
||||
import com.topjohnwu.magisk.core.model.UpdateInfo
|
||||
import retrofit2.HttpException
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
|
||||
class NetworkService(
|
||||
private val pages: GithubPageServices,
|
||||
private val raw: RawServices
|
||||
private val raw: RawUrl,
|
||||
private val api: GithubApiServices,
|
||||
) {
|
||||
suspend fun fetchUpdate() = safe {
|
||||
var info = when (Config.updateChannel) {
|
||||
DEFAULT_CHANNEL, STABLE_CHANNEL -> fetchStableUpdate()
|
||||
DEFAULT_CHANNEL -> if (BuildConfig.DEBUG) fetchDebugUpdate() else fetchStableUpdate()
|
||||
STABLE_CHANNEL -> fetchStableUpdate()
|
||||
BETA_CHANNEL -> fetchBetaUpdate()
|
||||
CANARY_CHANNEL -> fetchCanaryUpdate()
|
||||
DEBUG_CHANNEL -> fetchDebugUpdate()
|
||||
CUSTOM_CHANNEL -> fetchCustomUpdate(Config.customChannelUrl)
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
if (info.magisk.versionCode < Info.env.versionCode &&
|
||||
Config.updateChannel == DEFAULT_CHANNEL) {
|
||||
if (info.versionCode < Info.env.versionCode &&
|
||||
Config.updateChannel == DEFAULT_CHANNEL &&
|
||||
!BuildConfig.DEBUG
|
||||
) {
|
||||
Config.updateChannel = BETA_CHANNEL
|
||||
info = fetchBetaUpdate()
|
||||
}
|
||||
info
|
||||
}
|
||||
|
||||
// UpdateInfo
|
||||
private suspend fun fetchStableUpdate() = pages.fetchUpdateJSON("stable.json")
|
||||
private suspend fun fetchBetaUpdate() = pages.fetchUpdateJSON("beta.json")
|
||||
private suspend fun fetchCanaryUpdate() = pages.fetchUpdateJSON("canary.json")
|
||||
private suspend fun fetchDebugUpdate() = pages.fetchUpdateJSON("debug.json")
|
||||
private suspend fun fetchCustomUpdate(url: String) = pages.fetchUpdateJSON(url)
|
||||
suspend fun fetchUpdate(version: Int) = findRelease { it.versionCode == version }.asInfo()
|
||||
|
||||
// Keep going through all release pages until we find a match
|
||||
private suspend inline fun findRelease(predicate: (Release) -> Boolean): Release? {
|
||||
var page = 1
|
||||
while (true) {
|
||||
val response = api.fetchReleases(page = page)
|
||||
val releases = response.body() ?: throw HttpException(response)
|
||||
// Remove all non Magisk releases
|
||||
releases.removeAll { it.tag[0] != 'v' && !it.tag.startsWith("canary") }
|
||||
// Make sure it's sorted correctly
|
||||
releases.sortByDescending { it.createdTime }
|
||||
releases.find(predicate)?.let { return it }
|
||||
if (response.headers()["link"]?.contains("rel=\"next\"", ignoreCase = true) == true) {
|
||||
page += 1
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun Release?.asInfo(
|
||||
selector: (ReleaseAssets) -> Boolean = {
|
||||
// Default selector picks the non-debug APK
|
||||
it.name.run { endsWith(".apk") && !contains("debug") }
|
||||
}): UpdateInfo {
|
||||
return if (this == null) UpdateInfo()
|
||||
else if (tag[0] == 'v') asPublicInfo(selector)
|
||||
else asCanaryInfo(selector)
|
||||
}
|
||||
|
||||
private inline fun Release.asPublicInfo(selector: (ReleaseAssets) -> Boolean): UpdateInfo {
|
||||
val version = tag.drop(1)
|
||||
val date = dateFormat.format(createdTime)
|
||||
return UpdateInfo(
|
||||
version = version,
|
||||
versionCode = versionCode,
|
||||
link = assets.find(selector)!!.url,
|
||||
note = "## $date $name\n\n$body"
|
||||
)
|
||||
}
|
||||
|
||||
private inline fun Release.asCanaryInfo(selector: (ReleaseAssets) -> Boolean): UpdateInfo {
|
||||
return UpdateInfo(
|
||||
version = name.substring(8, 16),
|
||||
versionCode = versionCode,
|
||||
link = assets.find(selector)!!.url,
|
||||
note = "## $name\n\n$body"
|
||||
)
|
||||
}
|
||||
|
||||
// Version number: debug == beta >= stable
|
||||
|
||||
// Find the latest non-prerelease
|
||||
private suspend fun fetchStableUpdate() = api.fetchLatestRelease().asInfo()
|
||||
|
||||
// Find the latest release, regardless whether it's prerelease
|
||||
private suspend fun fetchBetaUpdate() = findRelease { true }.asInfo()
|
||||
|
||||
private suspend fun fetchDebugUpdate() =
|
||||
findRelease { true }.asInfo { it.name == "app-debug.apk" }
|
||||
|
||||
private suspend fun fetchCustomUpdate(url: String): UpdateInfo {
|
||||
val info = raw.fetchUpdateJson(url).magisk
|
||||
return info.let { it.copy(note = raw.fetchString(it.note)) }
|
||||
}
|
||||
|
||||
private inline fun <T> safe(factory: () -> T): T? {
|
||||
return try {
|
||||
|
@@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ByteArrayStream extends ByteArrayOutputStream {
|
||||
|
||||
@@ -27,4 +28,8 @@ public class ByteArrayStream extends ByteArrayOutputStream {
|
||||
public ByteArrayInputStream getInputStream() {
|
||||
return new ByteArrayInputStream(buf, 0, count);
|
||||
}
|
||||
|
||||
public ByteBuffer toByteBuffer() {
|
||||
return ByteBuffer.wrap(buf, 0, count);
|
||||
}
|
||||
}
|
||||
|
@@ -510,7 +510,7 @@ public class SignApk {
|
||||
privateKey[0] = key;
|
||||
|
||||
// Generate, in memory, an APK signed using standard JAR Signature Scheme.
|
||||
ByteArrayOutputStream v1SignedApkBuf = new ByteArrayOutputStream();
|
||||
ByteArrayStream v1SignedApkBuf = new ByteArrayStream();
|
||||
JarOutputStream outputJar = new JarOutputStream(v1SignedApkBuf);
|
||||
// Use maximum compression for compressed entries because the APK lives forever on
|
||||
// the system partition.
|
||||
@@ -519,8 +519,7 @@ public class SignApk {
|
||||
copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
|
||||
signFile(manifest, publicKey, privateKey, timestamp, outputJar);
|
||||
outputJar.close();
|
||||
ByteBuffer v1SignedApk = ByteBuffer.wrap(v1SignedApkBuf.toByteArray());
|
||||
v1SignedApkBuf.reset();
|
||||
ByteBuffer v1SignedApk = v1SignedApkBuf.toByteBuffer();
|
||||
|
||||
ByteBuffer[] outputChunks;
|
||||
List<ApkSignerV2.SignerConfig> signerConfigs = createV2SignerConfigs(privateKey, publicKey,
|
||||
|
@@ -70,7 +70,7 @@ object SuCallbackHandler {
|
||||
}.getOrNull() ?: createSuLog(fromUid, toUid, pid, command, policy, target, seContext, gids)
|
||||
|
||||
if (notify)
|
||||
notify(context, log.action == SuPolicy.ALLOW, log.appName)
|
||||
notify(context, log.action >= SuPolicy.ALLOW, log.appName)
|
||||
|
||||
runBlocking { ServiceLocator.logRepo.insert(log) }
|
||||
}
|
||||
@@ -86,7 +86,7 @@ object SuCallbackHandler {
|
||||
pm.getPackageInfo(uid, pid)?.applicationInfo?.getLabel(pm)
|
||||
}.getOrNull() ?: "[UID] $uid"
|
||||
|
||||
notify(context, policy == SuPolicy.ALLOW, appName)
|
||||
notify(context, policy >= SuPolicy.ALLOW, appName)
|
||||
}
|
||||
|
||||
private fun notify(context: Context, granted: Boolean, appName: String) {
|
||||
|
@@ -62,7 +62,7 @@ class SuRequestHandler(
|
||||
return false
|
||||
}
|
||||
output = File(fifo)
|
||||
policy = SuPolicy(uid)
|
||||
policy = policyDB.fetch(uid) ?: SuPolicy(uid)
|
||||
try {
|
||||
pkgInfo = pm.getPackageInfo(uid, pid) ?: PackageInfo().apply {
|
||||
val name = pm.getNameForUid(uid) ?: throw PackageManager.NameNotFoundException()
|
||||
@@ -81,15 +81,17 @@ class SuRequestHandler(
|
||||
return true
|
||||
}
|
||||
|
||||
suspend fun respond(action: Int, time: Int) {
|
||||
val until = if (time > 0)
|
||||
TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) +
|
||||
TimeUnit.MINUTES.toSeconds(time.toLong())
|
||||
else
|
||||
time.toLong()
|
||||
|
||||
policy.policy = action
|
||||
policy.until = until
|
||||
suspend fun respond(action: Int, time: Long) {
|
||||
if (action == SuPolicy.ALLOW && Config.suRestrict) {
|
||||
policy.policy = SuPolicy.RESTRICT
|
||||
} else {
|
||||
policy.policy = action
|
||||
}
|
||||
if (time >= 0) {
|
||||
policy.remain = TimeUnit.MINUTES.toSeconds(time)
|
||||
} else {
|
||||
policy.remain = time
|
||||
}
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
@@ -100,7 +102,7 @@ class SuRequestHandler(
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
}
|
||||
if (until >= 0) {
|
||||
if (time >= 0) {
|
||||
policyDB.update(policy)
|
||||
}
|
||||
}
|
||||
|
@@ -1,80 +0,0 @@
|
||||
package com.topjohnwu.magisk.core.su
|
||||
|
||||
import android.os.Bundle
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.superuser.CallbackList
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Runnable
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import timber.log.Timber
|
||||
|
||||
object TestHandler {
|
||||
|
||||
object LogList : CallbackList<String>(Runnable::run) {
|
||||
override fun onAddElement(e: String) {
|
||||
Timber.i(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun run(method: String): Bundle {
|
||||
var reason: String? = null
|
||||
|
||||
fun prerequisite(): Boolean {
|
||||
// Make sure the Magisk app can get root
|
||||
val shell = Shell.getShell()
|
||||
if (!shell.isRoot) {
|
||||
reason = "shell not root"
|
||||
return false
|
||||
}
|
||||
|
||||
// Make sure the root service is running
|
||||
RootUtils.Connection.await()
|
||||
return true
|
||||
}
|
||||
|
||||
fun setup(): Boolean {
|
||||
return runBlocking {
|
||||
MagiskInstaller.Emulator(LogList, LogList).exec()
|
||||
}
|
||||
}
|
||||
|
||||
fun test(): Boolean {
|
||||
// Make sure Zygisk works correctly
|
||||
if (!Info.isZygiskEnabled) {
|
||||
reason = "zygisk not enabled"
|
||||
return false
|
||||
}
|
||||
|
||||
// Clear existing grant for ADB shell
|
||||
runBlocking {
|
||||
ServiceLocator.policyDB.delete(2000)
|
||||
Config.suAutoResponse = Config.Value.SU_AUTO_ALLOW
|
||||
Config.prefs.edit().commit()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
val result = prerequisite() && runCatching {
|
||||
when (method) {
|
||||
"setup" -> setup()
|
||||
"test" -> test()
|
||||
else -> {
|
||||
reason = "unknown method"
|
||||
false
|
||||
}
|
||||
}
|
||||
}.getOrElse {
|
||||
reason = it.stackTraceToString()
|
||||
false
|
||||
}
|
||||
|
||||
return Bundle().apply {
|
||||
putBoolean("result", result)
|
||||
if (reason != null) putString("reason", reason)
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@ import android.app.Activity
|
||||
import android.app.ActivityOptions
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import com.topjohnwu.magisk.StubApk
|
||||
@@ -13,7 +14,6 @@ import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.R
|
||||
import com.topjohnwu.magisk.core.ktx.await
|
||||
import com.topjohnwu.magisk.core.ktx.copyAndClose
|
||||
import com.topjohnwu.magisk.core.ktx.toast
|
||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
||||
import com.topjohnwu.magisk.core.signing.JarMap
|
||||
@@ -23,11 +23,9 @@ import com.topjohnwu.magisk.core.utils.Keygen
|
||||
import com.topjohnwu.magisk.utils.APKInstall
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Runnable
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.security.SecureRandom
|
||||
@@ -38,6 +36,7 @@ object AppMigration {
|
||||
private const val ALPHA = "abcdefghijklmnopqrstuvwxyz"
|
||||
private const val ALPHADOTS = "$ALPHA....."
|
||||
private const val ANDROID_MANIFEST = "AndroidManifest.xml"
|
||||
private const val TEST_PKG_NAME = "$APP_PACKAGE_NAME.test"
|
||||
|
||||
// Some arbitrary limit
|
||||
const val MAX_LABEL_LENGTH = 32
|
||||
@@ -133,21 +132,15 @@ object AppMigration {
|
||||
val je = jar.getJarEntry(ANDROID_MANIFEST)
|
||||
val xml = AXML(jar.getRawData(je))
|
||||
val generator = classNameGenerator()
|
||||
|
||||
if (!xml.patchStrings {
|
||||
for (i in it.indices) {
|
||||
val s = it[i]
|
||||
if (s.contains(APP_PACKAGE_NAME)) {
|
||||
it[i] = s.replace(APP_PACKAGE_NAME, pkg)
|
||||
} else if (s.contains(PLACEHOLDER)) {
|
||||
it[i] = generator.next()
|
||||
} else if (s == origLabel) {
|
||||
it[i] = label.toString()
|
||||
}
|
||||
val p = xml.patchStrings {
|
||||
when {
|
||||
it.contains(APP_PACKAGE_NAME) -> it.replace(APP_PACKAGE_NAME, pkg)
|
||||
it.contains(PLACEHOLDER) -> generator.next()
|
||||
it == origLabel -> label.toString()
|
||||
else -> it
|
||||
}
|
||||
}) {
|
||||
return false
|
||||
}
|
||||
if (!p) return false
|
||||
|
||||
// Write apk changes
|
||||
jar.getOutputStream(je).use { it.write(xml.bytes) }
|
||||
@@ -161,52 +154,87 @@ object AppMigration {
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchApp(activity: Activity, pkg: String) {
|
||||
val intent = activity.packageManager.getLaunchIntentForPackage(pkg) ?: return
|
||||
private fun patchTest(apk: File, out: File, pkg: String): Boolean {
|
||||
try {
|
||||
JarMap.open(apk, true).use { jar ->
|
||||
val je = jar.getJarEntry(ANDROID_MANIFEST)
|
||||
val xml = AXML(jar.getRawData(je))
|
||||
val p = xml.patchStrings {
|
||||
when (it) {
|
||||
APP_PACKAGE_NAME -> pkg
|
||||
TEST_PKG_NAME -> "$pkg.test"
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
if (!p) return false
|
||||
|
||||
// Write apk changes
|
||||
jar.getOutputStream(je).use { it.write(xml.bytes) }
|
||||
val keys = Keygen()
|
||||
out.outputStream().use { SignApk.sign(keys.cert, keys.key, jar, it) }
|
||||
return true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchApp(context: Context, pkg: String) {
|
||||
val intent = context.packageManager.getLaunchIntentForPackage(pkg) ?: return
|
||||
intent.putExtra(Const.Key.PREV_CONFIG, Config.toBundle())
|
||||
val options = ActivityOptions.makeBasic()
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
options.setShareIdentityEnabled(true)
|
||||
}
|
||||
activity.startActivity(intent, options.toBundle())
|
||||
activity.finish()
|
||||
context.startActivity(intent, options.toBundle())
|
||||
if (context is Activity) {
|
||||
context.finish()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun patchAndHide(activity: Activity, label: String, onFailure: Runnable): Boolean {
|
||||
val stub = File(activity.cacheDir, "stub.apk")
|
||||
suspend fun patchAndHide(context: Context, label: String, pkg: String? = null): Boolean {
|
||||
val stub = File(context.cacheDir, "stub.apk")
|
||||
try {
|
||||
activity.assets.open("stub.apk").writeTo(stub)
|
||||
context.assets.open("stub.apk").writeTo(stub)
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
|
||||
// Generate a new random package name and signature
|
||||
val repack = File(activity.cacheDir, "patched.apk")
|
||||
val pkg = genPackageName()
|
||||
// Generate a new random signature and package name if needed
|
||||
val pkg = pkg ?: genPackageName()
|
||||
Config.keyStoreRaw = ""
|
||||
|
||||
if (!patch(activity, stub, FileOutputStream(repack), pkg, label))
|
||||
return false
|
||||
// Check and patch the test APK
|
||||
try {
|
||||
val info = context.packageManager.getApplicationInfo(TEST_PKG_NAME, 0)
|
||||
val testApk = File(info.sourceDir)
|
||||
val testRepack = File(context.cacheDir, "test.apk")
|
||||
if (!patchTest(testApk, testRepack, pkg))
|
||||
return false
|
||||
val cmd = "adb_pm_install $testRepack $pkg.test"
|
||||
if (!Shell.cmd(cmd).exec().isSuccess)
|
||||
return false
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
}
|
||||
|
||||
val repack = File(context.cacheDir, "patched.apk")
|
||||
repack.outputStream().use {
|
||||
if (!patch(context, stub, it, pkg, label))
|
||||
return false
|
||||
}
|
||||
|
||||
// Install and auto launch app
|
||||
val session = APKInstall.startSession(activity, pkg, onFailure) {
|
||||
val cmd = "adb_pm_install $repack $pkg"
|
||||
if (Shell.cmd(cmd).exec().isSuccess) {
|
||||
Config.suManager = pkg
|
||||
Shell.cmd("touch $AppApkPath").exec()
|
||||
launchApp(activity, pkg)
|
||||
}
|
||||
|
||||
val cmd = "adb_pm_install $repack $pkg"
|
||||
if (Shell.cmd(cmd).exec().isSuccess) return true
|
||||
|
||||
try {
|
||||
repack.inputStream().copyAndClose(session.openStream(activity))
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
launchApp(context, pkg)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
session.waitIntent()?.let { activity.startActivity(it) } ?: return false
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@@ -217,14 +245,25 @@ object AppMigration {
|
||||
setCancelable(false)
|
||||
show()
|
||||
}
|
||||
val onFailure = Runnable {
|
||||
val success = withContext(Dispatchers.IO) {
|
||||
patchAndHide(activity, label)
|
||||
}
|
||||
if (!success) {
|
||||
dialog.dismiss()
|
||||
activity.toast(R.string.failure, Toast.LENGTH_LONG)
|
||||
}
|
||||
val success = withContext(Dispatchers.IO) {
|
||||
patchAndHide(activity, label, onFailure)
|
||||
}
|
||||
|
||||
suspend fun restoreApp(context: Context): Boolean {
|
||||
val apk = StubApk.current(context)
|
||||
val cmd = "adb_pm_install $apk $APP_PACKAGE_NAME"
|
||||
if (Shell.cmd(cmd).await().isSuccess) {
|
||||
Config.suManager = ""
|
||||
Shell.cmd("touch $AppApkPath").exec()
|
||||
launchApp(context, APP_PACKAGE_NAME)
|
||||
return true
|
||||
}
|
||||
if (!success) onFailure.run()
|
||||
return false
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@@ -235,30 +274,10 @@ object AppMigration {
|
||||
setCancelable(false)
|
||||
show()
|
||||
}
|
||||
val onFailure = Runnable {
|
||||
dialog.dismiss()
|
||||
if (!restoreApp(activity)) {
|
||||
activity.toast(R.string.failure, Toast.LENGTH_LONG)
|
||||
}
|
||||
val apk = StubApk.current(activity)
|
||||
val session = APKInstall.startSession(activity, APP_PACKAGE_NAME, onFailure) {
|
||||
Config.suManager = ""
|
||||
Shell.cmd("touch $AppApkPath").exec()
|
||||
launchApp(activity, APP_PACKAGE_NAME)
|
||||
dialog.dismiss()
|
||||
}
|
||||
val cmd = "adb_pm_install $apk $APP_PACKAGE_NAME"
|
||||
if (Shell.cmd(cmd).await().isSuccess) return
|
||||
val success = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
apk.inputStream().copyAndClose(session.openStream(activity))
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
return@withContext false
|
||||
}
|
||||
session.waitIntent()?.let { activity.startActivity(it) } ?: return@withContext false
|
||||
return@withContext true
|
||||
}
|
||||
if (!success) onFailure.run()
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
suspend fun upgradeStub(context: Context, apk: File): Intent? {
|
||||
|
@@ -7,7 +7,6 @@ import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.displayName
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
||||
import com.topjohnwu.magisk.core.utils.unzip
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -47,20 +46,14 @@ open class FlashZip(
|
||||
}
|
||||
}
|
||||
|
||||
val isValid = try {
|
||||
zipFile.unzip(installDir, "META-INF/com/google/android", true)
|
||||
val script = File(installDir, "updater-script")
|
||||
script.readText().contains("#MAGISK")
|
||||
try {
|
||||
val binary = File(installDir, "update-binary")
|
||||
AppContext.assets.open("module_installer.sh").use { it.writeTo(binary) }
|
||||
} catch (e: IOException) {
|
||||
console.add("! Unzip error")
|
||||
throw e
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
console.add("! This zip is not a Magisk module!")
|
||||
return false
|
||||
}
|
||||
|
||||
console.add("- Installing ${mUri.displayName}")
|
||||
|
||||
return Shell.cmd("sh $installDir/update-binary dummy 1 \'$zipFile\'")
|
||||
|
@@ -17,7 +17,6 @@ import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||
import com.topjohnwu.magisk.core.ktx.copyAll
|
||||
import com.topjohnwu.magisk.core.ktx.copyAndClose
|
||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
||||
import com.topjohnwu.magisk.core.utils.DummyList
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
@@ -47,7 +46,6 @@ import java.io.OutputStream
|
||||
import java.io.PushbackInputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.security.SecureRandom
|
||||
import java.util.Arrays
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
@@ -134,7 +132,6 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
if (entry != null) {
|
||||
val magisk32 = File(installDir, "magisk32")
|
||||
zf.getInputStream(entry).writeTo(magisk32)
|
||||
magisk32.setExecutable(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,11 +146,15 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
Os.symlink(lib.path, "$installDir/$name")
|
||||
}
|
||||
|
||||
// Also symlink magisk32 on 64-bit devices that supports 32-bit
|
||||
val lib32 = info.javaClass.getDeclaredField("secondaryNativeLibraryDir")
|
||||
.get(info) as String?
|
||||
if (lib32 != null) {
|
||||
Os.symlink("$lib32/libmagisk.so", "$installDir/magisk32");
|
||||
// Also extract magisk32 on 64-bit devices that supports 32-bit
|
||||
val abi32 = Const.CPU_ABI_32
|
||||
if (Process.is64Bit() && abi32 != null) {
|
||||
val name = "lib/$abi32/libmagisk.so"
|
||||
val entry = javaClass.classLoader!!.getResourceAsStream(name)
|
||||
if (entry != null) {
|
||||
val magisk32 = File(installDir, "magisk32")
|
||||
entry.writeTo(magisk32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +192,8 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun InputStream.copyAndCloseOut(out: OutputStream) = out.use { copyAll(it) }
|
||||
private suspend fun InputStream.copyAndCloseOut(out: OutputStream) =
|
||||
out.use { copyAll(it, 1024 * 1024) }
|
||||
|
||||
private class NoAvailableStream(s: InputStream) : FilterInputStream(s) {
|
||||
// Make sure available is never called on the actual stream and always return 0
|
||||
@@ -225,8 +227,8 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
console.add("- Processing tar file")
|
||||
var entry: TarArchiveEntry? = tarIn.nextEntry
|
||||
|
||||
fun TarArchiveEntry.decompressedStream(): InputStream {
|
||||
val stream = if (name.endsWith(".lz4"))
|
||||
fun decompressedStream(): InputStream {
|
||||
val stream = if (tarIn.currentEntry.name.endsWith(".lz4"))
|
||||
FramedLZ4CompressorInputStream(tarIn, true) else tarIn
|
||||
return NoAvailableStream(stream)
|
||||
}
|
||||
@@ -252,9 +254,9 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
|
||||
if (bootItem != null) {
|
||||
console.add("-- Extracting: ${bootItem.name}")
|
||||
entry.decompressedStream().copyAndCloseOut(bootItem.file.newOutputStream())
|
||||
decompressedStream().copyAndCloseOut(bootItem.file.newOutputStream())
|
||||
} else if (entry.name.contains("vbmeta.img")) {
|
||||
val rawData = entry.decompressedStream().readBytes()
|
||||
val rawData = decompressedStream().readBytes()
|
||||
// Valid vbmeta.img should be at least 256 bytes
|
||||
if (rawData.size < 256)
|
||||
continue
|
||||
@@ -287,7 +289,7 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
} else {
|
||||
console.add("-- Copying : ${entry.name}")
|
||||
tarOut.putArchiveEntry(entry)
|
||||
tarIn.copyAll(tarOut, bufferSize = 1024 * 1024)
|
||||
tarIn.copyAll(tarOut)
|
||||
tarOut.closeArchiveEntry()
|
||||
}
|
||||
entry = tarIn.nextEntry ?: break
|
||||
@@ -429,7 +431,7 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
|
||||
// Process input file
|
||||
try {
|
||||
PushbackInputStream(uri.inputStream(), 512).use { src ->
|
||||
PushbackInputStream(uri.inputStream().buffered(1024 * 1024), 512).use { src ->
|
||||
val head = ByteArray(512)
|
||||
if (src.read(head) != head.size) {
|
||||
console.add("! Invalid input file")
|
||||
@@ -438,12 +440,13 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
src.unread(head)
|
||||
|
||||
val magic = head.copyOf(4)
|
||||
val tarMagic = Arrays.copyOfRange(head, 257, 262)
|
||||
val tarMagic = head.copyOfRange(257, 262)
|
||||
|
||||
srcBoot = if (tarMagic.contentEquals("ustar".toByteArray())) {
|
||||
// tar file
|
||||
outFile = MediaStoreUtils.getFile("$destName.tar")
|
||||
outStream = TarArchiveOutputStream(outFile.uri.outputStream()).also {
|
||||
val os = outFile.uri.outputStream().buffered(1024 * 1024)
|
||||
outStream = TarArchiveOutputStream(os).also {
|
||||
it.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR)
|
||||
it.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU)
|
||||
}
|
||||
@@ -500,7 +503,7 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
bootItem.file = newBoot
|
||||
bootItem.copyTo(outStream as TarArchiveOutputStream)
|
||||
} else {
|
||||
newBoot.newInputStream().copyAndClose(outStream)
|
||||
newBoot.newInputStream().use { it.copyAll(outStream, 1024 * 1024) }
|
||||
}
|
||||
newBoot.delete()
|
||||
|
||||
@@ -514,6 +517,8 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
outFile.delete()
|
||||
Timber.e(e)
|
||||
return false
|
||||
} finally {
|
||||
outStream.close()
|
||||
}
|
||||
|
||||
// Fix up binaries
|
||||
@@ -597,7 +602,9 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
if (result)
|
||||
return true
|
||||
|
||||
Shell.cmd("rm -rf $installDir").submit()
|
||||
// Not every operation initializes installDir
|
||||
if (::installDir.isInitialized)
|
||||
Shell.cmd("rm -rf $installDir").submit()
|
||||
return false
|
||||
}
|
||||
|
||||
|
@@ -29,7 +29,7 @@ class AXML(b: ByteArray) {
|
||||
* Followed by an array of uint32_t with size = number of strings
|
||||
* Each entry points to an offset into the string data
|
||||
*/
|
||||
fun patchStrings(patchFn: (Array<String>) -> Unit): Boolean {
|
||||
fun patchStrings(mapFn: (String) -> String): Boolean {
|
||||
val buffer = ByteBuffer.wrap(bytes).order(LITTLE_ENDIAN)
|
||||
|
||||
fun findStringPool(): Int {
|
||||
@@ -65,7 +65,9 @@ class AXML(b: ByteArray) {
|
||||
}
|
||||
|
||||
val strArr = strList.toTypedArray()
|
||||
patchFn(strArr)
|
||||
for (i in strArr.indices) {
|
||||
strArr[i] = mapFn(strArr[i])
|
||||
}
|
||||
|
||||
// Write everything before string data, will patch values later
|
||||
val baos = RawByteStream()
|
||||
|
@@ -2,6 +2,10 @@ package com.topjohnwu.magisk.core.utils;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipUtil;
|
||||
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
@@ -29,4 +33,15 @@ public class Desugar {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Within {@link ZipArchiveOutputStream#copyFromZipInputStream}, we redirect the method call
|
||||
* {@link ZipUtil#checkRequestedFeatures} to this method. This is safe because the only usage
|
||||
* of copyFromZipInputStream is in {@link ZipArchiveOutputStream#addRawArchiveEntry},
|
||||
* which does not need to actually understand the content of the zip entry. By removing
|
||||
* this feature check, we can modify zip files using unsupported compression methods.
|
||||
*/
|
||||
public static void checkRequestedFeatures(final ZipArchiveEntry ze) {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
@@ -11,13 +11,10 @@ import android.net.NetworkRequest
|
||||
import android.os.PowerManager
|
||||
import androidx.collection.ArraySet
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.ktx.registerRuntimeReceiver
|
||||
|
||||
class NetworkObserver(context: Context): DefaultLifecycleObserver {
|
||||
class NetworkObserver(context: Context) {
|
||||
private val manager = context.getSystemService<ConnectivityManager>()!!
|
||||
|
||||
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
|
||||
@@ -55,20 +52,17 @@ class NetworkObserver(context: Context): DefaultLifecycleObserver {
|
||||
manager.registerNetworkCallback(request, networkCallback)
|
||||
val filter = IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)
|
||||
context.applicationContext.registerRuntimeReceiver(receiver, filter)
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
||||
}
|
||||
|
||||
override fun onStart(owner: LifecycleOwner) {
|
||||
postCurrentState()
|
||||
}
|
||||
|
||||
private fun postCurrentState() {
|
||||
postValue(manager.getNetworkCapabilities(manager.activeNetwork)
|
||||
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ?: false)
|
||||
fun postCurrentState() {
|
||||
postValue(
|
||||
manager.getNetworkCapabilities(manager.activeNetwork)
|
||||
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) == true
|
||||
)
|
||||
}
|
||||
|
||||
private fun postValue(b: Boolean) {
|
||||
Info.remote = Info.EMPTY_REMOTE
|
||||
Info.resetUpdate()
|
||||
Info.isConnected.postValue(b)
|
||||
}
|
||||
|
||||
|
@@ -1,15 +0,0 @@
|
||||
package com.topjohnwu.magisk.core.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LifecycleDispatcher;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
|
||||
// Use Java to bypass Kotlin internal visibility modifier
|
||||
public class ProcessLifecycle {
|
||||
public static void init(@NonNull Context context) {
|
||||
LifecycleDispatcher.init(context);
|
||||
ProcessLifecycleOwner.init$lifecycle_process_release(context);
|
||||
}
|
||||
}
|
@@ -7,11 +7,14 @@ import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import android.system.Os
|
||||
import androidx.core.content.getSystemService
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import com.topjohnwu.superuser.ipc.RootService
|
||||
import com.topjohnwu.superuser.nio.FileSystemManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.concurrent.locks.AbstractQueuedSynchronizer
|
||||
@@ -43,16 +46,7 @@ class RootUtils(stub: Any?) : RootService() {
|
||||
return object : IRootUtils.Stub() {
|
||||
override fun getAppProcess(pid: Int) = safe(null) { getAppProcessImpl(pid) }
|
||||
override fun getFileSystem(): IBinder = FileSystemManager.getService()
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> safe(default: T, block: () -> T): T {
|
||||
return try {
|
||||
block()
|
||||
} catch (e: Throwable) {
|
||||
// The process died unexpectedly
|
||||
Timber.e(e)
|
||||
default
|
||||
override fun addSystemlessHosts() = safe(false) { addSystemlessHostsImpl() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +72,26 @@ class RootUtils(stub: Any?) : RootService() {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun addSystemlessHostsImpl(): Boolean {
|
||||
val module = File(Const.MODULE_PATH, "hosts")
|
||||
if (module.exists()) return true
|
||||
val hosts = File(module, "system/etc/hosts")
|
||||
if (!hosts.parentFile.mkdirs()) return false
|
||||
File(module, "module.prop").outputStream().writer().use {
|
||||
it.write("""
|
||||
id=hosts
|
||||
name=Systemless Hosts
|
||||
version=1.0
|
||||
versionCode=1
|
||||
author=Magisk
|
||||
description=Magisk app built-in systemless hosts module
|
||||
""".trimIndent())
|
||||
}
|
||||
File("/system/etc/hosts").copyTo(hosts)
|
||||
File(module, "update").createNewFile()
|
||||
return true
|
||||
}
|
||||
|
||||
object Connection : AbstractQueuedSynchronizer(), ServiceConnection {
|
||||
init {
|
||||
state = 1
|
||||
@@ -131,11 +145,25 @@ class RootUtils(stub: Any?) : RootService() {
|
||||
return field
|
||||
}
|
||||
private set
|
||||
var obj: IRootUtils? = null
|
||||
private var obj: IRootUtils? = null
|
||||
get() {
|
||||
Connection.await()
|
||||
return field
|
||||
}
|
||||
private set
|
||||
|
||||
fun getAppProcess(pid: Int) = safe(null) { obj?.getAppProcess(pid) }
|
||||
|
||||
suspend fun addSystemlessHosts() =
|
||||
withContext(Dispatchers.IO) { safe(false) { obj?.addSystemlessHosts() ?: false } }
|
||||
|
||||
private inline fun <T> safe(default: T, block: () -> T): T {
|
||||
return try {
|
||||
block()
|
||||
} catch (e: Throwable) {
|
||||
// The process died unexpectedly
|
||||
Timber.e(e)
|
||||
default
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,50 +0,0 @@
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import com.topjohnwu.magisk.core.ktx.copyAll
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
@Throws(IOException::class)
|
||||
suspend fun File.unzip(folder: File, path: String = "", junkPath: Boolean = false) {
|
||||
ZipFile.Builder().setFile(this).get().use { zip ->
|
||||
for (entry in zip.entries) {
|
||||
if (!entry.name.startsWith(path) || entry.isDirectory) {
|
||||
// Ignore directories, only create files
|
||||
continue
|
||||
}
|
||||
val name = if (junkPath)
|
||||
entry.name.substring(entry.name.lastIndexOf('/') + 1)
|
||||
else
|
||||
entry.name
|
||||
val dest = File(folder, name)
|
||||
dest.parentFile?.mkdirs()
|
||||
dest.outputStream().use { out -> zip.getInputStream(entry).copyAll(out) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
suspend fun InputStream.unzip(folder: File, path: String = "", junkPath: Boolean = false) {
|
||||
ZipArchiveInputStream(this).use { zin ->
|
||||
var entry: ZipArchiveEntry
|
||||
while (true) {
|
||||
entry = zin.nextEntry ?: break
|
||||
if (!entry.name.startsWith(path) || entry.isDirectory) {
|
||||
// Ignore directories, only create files
|
||||
continue
|
||||
}
|
||||
val name = if (junkPath)
|
||||
entry.name.substring(entry.name.lastIndexOf('/') + 1)
|
||||
else
|
||||
entry.name
|
||||
|
||||
val dest = File(folder, name)
|
||||
dest.parentFile?.mkdirs()
|
||||
dest.outputStream().use { out -> zin.copyAll(out) }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,161 @@
|
||||
package com.topjohnwu.magisk.test
|
||||
|
||||
import android.os.ParcelFileDescriptor.AutoCloseInputStream
|
||||
import androidx.annotation.Keep
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.Until
|
||||
import com.topjohnwu.magisk.core.model.module.LocalModule
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.magisk.test.Environment.Companion.EMPTY_ZYGISK
|
||||
import com.topjohnwu.magisk.test.Environment.Companion.INVALID_ZYGISK
|
||||
import com.topjohnwu.magisk.test.Environment.Companion.MOUNT_TEST
|
||||
import com.topjohnwu.magisk.test.Environment.Companion.REMOVE_TEST
|
||||
import com.topjohnwu.magisk.test.Environment.Companion.SEPOLICY_RULE
|
||||
import com.topjohnwu.magisk.test.Environment.Companion.UPGRADE_TEST
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assume.assumeTrue
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@Keep
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AdditionalTest : BaseTest {
|
||||
|
||||
companion object {
|
||||
private const val SHELL_PKG = "com.android.shell"
|
||||
private const val LSPOSED_CATEGORY = "org.lsposed.manager.LAUNCH_MANAGER"
|
||||
private const val LSPOSED_PKG = "org.lsposed.manager"
|
||||
|
||||
private lateinit var modules: List<LocalModule>
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun before() {
|
||||
BaseTest.prerequisite()
|
||||
runBlocking {
|
||||
modules = LocalModule.installed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
device.pressHome()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModuleCount() {
|
||||
var expected = 4
|
||||
if (Environment.mount()) expected++
|
||||
if (Environment.preinit()) expected++
|
||||
if (Environment.lsposed()) expected++
|
||||
if (Environment.shamiko()) expected++
|
||||
assertEquals("Module count incorrect", expected, modules.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLsposed() {
|
||||
assumeTrue(Environment.lsposed())
|
||||
|
||||
val module = modules.find { it.id == "zygisk_lsposed" }
|
||||
assertNotNull("zygisk_lsposed is not installed", module)
|
||||
module!!
|
||||
assertFalse("zygisk_lsposed is not enabled", module.zygiskUnloaded)
|
||||
|
||||
// Launch lsposed manager to ensure the module is active
|
||||
uiAutomation.executeShellCommand(
|
||||
"am start -c $LSPOSED_CATEGORY $SHELL_PKG/.BugreportWarningActivity"
|
||||
).let { pfd -> AutoCloseInputStream(pfd).use { it.readBytes() } }
|
||||
|
||||
val pattern = Pattern.compile("$LSPOSED_PKG:id/.*")
|
||||
assertNotNull(
|
||||
"LSPosed manager launch failed",
|
||||
device.wait(Until.hasObject(By.res(pattern)), TimeUnit.SECONDS.toMillis(10))
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModuleMount() {
|
||||
assumeTrue(Environment.mount())
|
||||
|
||||
assertNotNull("$MOUNT_TEST is not installed", modules.find { it.id == MOUNT_TEST })
|
||||
assertTrue(
|
||||
"/system/fonts/newfile should exist",
|
||||
RootUtils.fs.getFile("/system/fonts/newfile").exists()
|
||||
)
|
||||
assertFalse(
|
||||
"/system/bin/screenrecord should not exist",
|
||||
RootUtils.fs.getFile("/system/bin/screenrecord").exists()
|
||||
)
|
||||
val egg = RootUtils.fs.getFile("/system/app/EasterEgg").list() ?: arrayOf()
|
||||
assertArrayEquals(
|
||||
"/system/app/EasterEgg should be replaced",
|
||||
egg,
|
||||
arrayOf("newfile")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSepolicyRule() {
|
||||
assumeTrue(Environment.preinit())
|
||||
|
||||
assertNotNull("$SEPOLICY_RULE is not installed", modules.find { it.id == SEPOLICY_RULE })
|
||||
assertTrue(
|
||||
"Module sepolicy.rule is not applied",
|
||||
Shell.cmd("magiskpolicy --print-rules | grep -q magisk_test").exec().isSuccess
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEmptyZygiskModule() {
|
||||
val module = modules.find { it.id == EMPTY_ZYGISK }
|
||||
assertNotNull("$EMPTY_ZYGISK is not installed", module)
|
||||
module!!
|
||||
assertTrue("$EMPTY_ZYGISK should be zygisk unloaded", module.zygiskUnloaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInvalidZygiskModule() {
|
||||
val module = modules.find { it.id == INVALID_ZYGISK }
|
||||
assertNotNull("$INVALID_ZYGISK is not installed", module)
|
||||
module!!
|
||||
assertTrue("$INVALID_ZYGISK should be zygisk unloaded", module.zygiskUnloaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveModule() {
|
||||
assertNull("$REMOVE_TEST should be removed", modules.find { it.id == REMOVE_TEST })
|
||||
assertTrue(
|
||||
"Uninstaller of $REMOVE_TEST should be run",
|
||||
RootUtils.fs.getFile(Environment.REMOVE_TEST_MARKER).exists()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModuleUpgrade() {
|
||||
val module = modules.find { it.id == UPGRADE_TEST }
|
||||
assertNotNull("$UPGRADE_TEST is not installed", module)
|
||||
module!!
|
||||
assertFalse("$UPGRADE_TEST should be disabled", module.enable)
|
||||
assertTrue(
|
||||
"$UPGRADE_TEST should be updated",
|
||||
module.base.getChildFile("post-fs-data.sh").exists()
|
||||
)
|
||||
assertFalse(
|
||||
"$UPGRADE_TEST should be updated",
|
||||
module.base.getChildFile("service.sh").exists()
|
||||
)
|
||||
}
|
||||
}
|
27
app/core/src/main/java/com/topjohnwu/magisk/test/BaseTest.kt
Normal file
27
app/core/src/main/java/com/topjohnwu/magisk/test/BaseTest.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.topjohnwu.magisk.test
|
||||
|
||||
import android.app.Instrumentation
|
||||
import android.app.UiAutomation
|
||||
import android.content.Context
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import org.junit.Assert.assertTrue
|
||||
|
||||
interface BaseTest {
|
||||
val instrumentation: Instrumentation
|
||||
get() = InstrumentationRegistry.getInstrumentation()
|
||||
val appContext: Context get() = instrumentation.targetContext
|
||||
val testContext: Context get() = instrumentation.context
|
||||
val uiAutomation: UiAutomation get() = instrumentation.uiAutomation
|
||||
val device: UiDevice get() = UiDevice.getInstance(instrumentation)
|
||||
|
||||
companion object {
|
||||
fun prerequisite() {
|
||||
assertTrue("Should have root access", Shell.getShell().isRoot)
|
||||
// Make sure the root service is running
|
||||
RootUtils.Connection.await()
|
||||
}
|
||||
}
|
||||
}
|
288
app/core/src/main/java/com/topjohnwu/magisk/test/Environment.kt
Normal file
288
app/core/src/main/java/com/topjohnwu/magisk/test/Environment.kt
Normal file
@@ -0,0 +1,288 @@
|
||||
package com.topjohnwu.magisk.test
|
||||
|
||||
import android.app.Notification
|
||||
import android.os.Build
|
||||
import androidx.annotation.Keep
|
||||
import androidx.core.net.toUri
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.topjohnwu.magisk.core.BuildConfig.APP_PACKAGE_NAME
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.download.DownloadNotifier
|
||||
import com.topjohnwu.magisk.core.download.DownloadProcessor
|
||||
import com.topjohnwu.magisk.core.ktx.cachedFile
|
||||
import com.topjohnwu.magisk.core.model.module.LocalModule
|
||||
import com.topjohnwu.magisk.core.tasks.AppMigration
|
||||
import com.topjohnwu.magisk.core.tasks.FlashZip
|
||||
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.superuser.CallbackList
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.nio.ExtendedFile
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
|
||||
@Keep
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class Environment : BaseTest {
|
||||
|
||||
companion object {
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun before() = BaseTest.prerequisite()
|
||||
|
||||
// The kernel running on emulators < API 26 does not play well with
|
||||
// magic mount. Skip mount_test on those legacy platforms.
|
||||
fun mount(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= 26
|
||||
}
|
||||
|
||||
// It is possible that there are no suitable preinit partition to use
|
||||
fun preinit(): Boolean {
|
||||
return Shell.cmd("magisk --preinit-device").exec().isSuccess
|
||||
}
|
||||
|
||||
fun lsposed(): Boolean {
|
||||
return Build.VERSION.SDK_INT in 27..34
|
||||
}
|
||||
|
||||
fun shamiko(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= 27
|
||||
}
|
||||
|
||||
private const val MODULE_UPDATE_PATH = "/data/adb/modules_update"
|
||||
private const val MODULE_ERROR = "Module zip processing incorrect"
|
||||
const val MOUNT_TEST = "mount_test"
|
||||
const val SEPOLICY_RULE = "sepolicy_rule"
|
||||
const val INVALID_ZYGISK = "invalid_zygisk"
|
||||
const val REMOVE_TEST = "remove_test"
|
||||
const val REMOVE_TEST_MARKER = "/dev/.remove_test_removed"
|
||||
const val EMPTY_ZYGISK = "empty_zygisk"
|
||||
const val UPGRADE_TEST = "upgrade_test"
|
||||
}
|
||||
|
||||
object TimberLog : CallbackList<String>(Runnable::run) {
|
||||
override fun onAddElement(e: String) {
|
||||
Timber.i(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkModuleZip(file: File) {
|
||||
// Make sure module processing is correct
|
||||
ZipFile.Builder().setFile(file).get().use { zip ->
|
||||
val meta = zip.entries
|
||||
.asSequence()
|
||||
.filter { it.name.startsWith("META-INF") }
|
||||
.toMutableList()
|
||||
assertEquals(MODULE_ERROR, 6, meta.size)
|
||||
|
||||
val binary = zip.getInputStream(
|
||||
zip.getEntry("META-INF/com/google/android/update-binary")
|
||||
).use { it.readBytes() }
|
||||
val ref = appContext.assets.open("module_installer.sh").use { it.readBytes() }
|
||||
assertArrayEquals(MODULE_ERROR, ref, binary)
|
||||
|
||||
val script = zip.getInputStream(
|
||||
zip.getEntry("META-INF/com/google/android/updater-script")
|
||||
).use { it.readBytes() }
|
||||
assertArrayEquals(MODULE_ERROR, "#MAGISK\n".toByteArray(), script)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupMountTest(root: ExtendedFile) {
|
||||
val error = "$MOUNT_TEST setup failed"
|
||||
val path = root.getChildFile(MOUNT_TEST)
|
||||
|
||||
// Create /system/fonts/newfile
|
||||
val etc = path.getChildFile("system").getChildFile("fonts")
|
||||
assertTrue(error, etc.mkdirs())
|
||||
assertTrue(error, etc.getChildFile("newfile").createNewFile())
|
||||
|
||||
// Create /system/app/EasterEgg/.replace
|
||||
val egg = path.getChildFile("system").getChildFile("app").getChildFile("EasterEgg")
|
||||
assertTrue(error, egg.mkdirs())
|
||||
assertTrue(error, egg.getChildFile(".replace").createNewFile())
|
||||
|
||||
// Create /system/app/EasterEgg/newfile
|
||||
assertTrue(error, egg.getChildFile("newfile").createNewFile())
|
||||
|
||||
// Delete /system/bin/screenrecord
|
||||
val bin = path.getChildFile("system").getChildFile("bin")
|
||||
assertTrue(error, bin.mkdirs())
|
||||
assertTrue(error, Shell.cmd("mknod $bin/screenrecord c 0 0").exec().isSuccess)
|
||||
|
||||
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||
}
|
||||
|
||||
private fun setupSystemlessHost() {
|
||||
val error = "hosts setup failed"
|
||||
assertTrue(error, runBlocking { RootUtils.addSystemlessHosts() })
|
||||
assertTrue(error, RootUtils.fs.getFile(Const.MODULE_PATH).getChildFile("hosts").exists())
|
||||
}
|
||||
|
||||
private fun setupSepolicyRuleModule(root: ExtendedFile) {
|
||||
val error = "$SEPOLICY_RULE setup failed"
|
||||
val path = root.getChildFile(SEPOLICY_RULE)
|
||||
assertTrue(error, path.mkdirs())
|
||||
|
||||
// Add sepolicy patch
|
||||
PrintStream(path.getChildFile("sepolicy.rule").newOutputStream()).use {
|
||||
it.println("type magisk_test domain")
|
||||
}
|
||||
|
||||
assertTrue(error, Shell.cmd(
|
||||
"set_default_perm $path",
|
||||
"copy_preinit_files"
|
||||
).exec().isSuccess)
|
||||
}
|
||||
|
||||
private fun setupEmptyZygiskModule(root: ExtendedFile) {
|
||||
val error = "$EMPTY_ZYGISK setup failed"
|
||||
val path = root.getChildFile(EMPTY_ZYGISK)
|
||||
|
||||
// Create an empty zygisk folder
|
||||
val module = LocalModule(path)
|
||||
assertTrue(error, module.zygiskFolder.mkdirs())
|
||||
}
|
||||
|
||||
private fun setupInvalidZygiskModule(root: ExtendedFile) {
|
||||
val error = "$INVALID_ZYGISK setup failed"
|
||||
val path = root.getChildFile(INVALID_ZYGISK)
|
||||
|
||||
// Create invalid zygisk libraries
|
||||
val module = LocalModule(path)
|
||||
assertTrue(error, module.zygiskFolder.mkdirs())
|
||||
assertTrue(error, module.zygiskFolder.getChildFile("armeabi-v7a.so").createNewFile())
|
||||
assertTrue(error, module.zygiskFolder.getChildFile("arm64-v8a.so").createNewFile())
|
||||
assertTrue(error, module.zygiskFolder.getChildFile("x86.so").createNewFile())
|
||||
assertTrue(error, module.zygiskFolder.getChildFile("x86_64.so").createNewFile())
|
||||
|
||||
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||
}
|
||||
|
||||
private fun setupRemoveModule(root: ExtendedFile) {
|
||||
val error = "$REMOVE_TEST setup failed"
|
||||
val path = root.getChildFile(REMOVE_TEST)
|
||||
|
||||
// Create a new module but mark is as "remove"
|
||||
val module = LocalModule(path)
|
||||
assertTrue(error, path.mkdirs())
|
||||
// Create uninstaller script
|
||||
path.getChildFile("uninstall.sh").newOutputStream().writer().use {
|
||||
it.write("touch $REMOVE_TEST_MARKER")
|
||||
}
|
||||
assertTrue(error, path.getChildFile("service.sh").createNewFile())
|
||||
module.remove = true
|
||||
|
||||
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||
}
|
||||
|
||||
private fun setupUpgradeModule(root: ExtendedFile, update: ExtendedFile) {
|
||||
val error = "$UPGRADE_TEST setup failed"
|
||||
val oldPath = root.getChildFile(UPGRADE_TEST)
|
||||
val newPath = update.getChildFile(UPGRADE_TEST)
|
||||
|
||||
// Create an existing module but mark as "disable
|
||||
val module = LocalModule(oldPath)
|
||||
assertTrue(error, oldPath.mkdirs())
|
||||
module.enable = false
|
||||
// Install service.sh into the old module
|
||||
assertTrue(error, oldPath.getChildFile("service.sh").createNewFile())
|
||||
|
||||
// Create an upgrade module
|
||||
assertTrue(error, newPath.mkdirs())
|
||||
// Install post-fs-data.sh into the new module
|
||||
assertTrue(error, newPath.getChildFile("post-fs-data.sh").createNewFile())
|
||||
|
||||
assertTrue(error, Shell.cmd(
|
||||
"set_default_perm $oldPath",
|
||||
"set_default_perm $newPath",
|
||||
).exec().isSuccess)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setupEnvironment() {
|
||||
runBlocking {
|
||||
assertTrue(
|
||||
"Magisk setup failed",
|
||||
MagiskInstaller.Emulator(TimberLog, TimberLog).exec()
|
||||
)
|
||||
}
|
||||
|
||||
val notify = object : DownloadNotifier {
|
||||
override val context = appContext
|
||||
override fun notifyUpdate(id: Int, editor: (Notification.Builder) -> Unit) {}
|
||||
}
|
||||
val processor = DownloadProcessor(notify)
|
||||
|
||||
val shamiko = appContext.cachedFile("shamiko.zip")
|
||||
runBlocking {
|
||||
testContext.assets.open("shamiko.zip").use {
|
||||
processor.handleModule(it, shamiko.toUri())
|
||||
}
|
||||
checkModuleZip(shamiko)
|
||||
if (shamiko()) {
|
||||
assertTrue(
|
||||
"Shamiko installation failed",
|
||||
FlashZip(shamiko.toUri(), TimberLog, TimberLog).exec()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val lsp = appContext.cachedFile("lsposed.zip")
|
||||
runBlocking {
|
||||
testContext.assets.open("lsposed.zip").use {
|
||||
processor.handleModule(it, lsp.toUri())
|
||||
}
|
||||
checkModuleZip(lsp)
|
||||
if (lsposed()) {
|
||||
assertTrue(
|
||||
"LSPosed installation failed",
|
||||
FlashZip(lsp.toUri(), TimberLog, TimberLog).exec()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val root = RootUtils.fs.getFile(Const.MODULE_PATH)
|
||||
val update = RootUtils.fs.getFile(MODULE_UPDATE_PATH)
|
||||
if (mount()) { setupMountTest(update) }
|
||||
if (preinit()) { setupSepolicyRuleModule(update) }
|
||||
setupSystemlessHost()
|
||||
setupEmptyZygiskModule(update)
|
||||
setupInvalidZygiskModule(update)
|
||||
setupRemoveModule(root)
|
||||
setupUpgradeModule(root, update)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setupAppHide() {
|
||||
runBlocking {
|
||||
assertTrue(
|
||||
"App hiding failed",
|
||||
AppMigration.patchAndHide(
|
||||
context = appContext,
|
||||
label = "Settings",
|
||||
pkg = "repackaged.$APP_PACKAGE_NAME"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setupAppRestore() {
|
||||
runBlocking {
|
||||
assertTrue(
|
||||
"App restoration failed",
|
||||
AppMigration.restoreApp(appContext)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
package com.topjohnwu.magisk.test
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import android.os.ParcelFileDescriptor.AutoCloseInputStream
|
||||
import androidx.annotation.Keep
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Keep
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MagiskAppTest : BaseTest {
|
||||
|
||||
companion object {
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun before() = BaseTest.prerequisite()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testZygisk() {
|
||||
assertTrue("Zygisk should be enabled", Info.isZygiskEnabled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSuRequest() {
|
||||
// Bypass the need to actually show a dialog
|
||||
Config.suAutoResponse = Config.Value.SU_AUTO_ALLOW
|
||||
Config.prefs.edit().commit()
|
||||
|
||||
// Inject an undetermined + mute logging policy for ADB shell
|
||||
val policy = SuPolicy(
|
||||
uid = 2000,
|
||||
logging = false,
|
||||
notification = false,
|
||||
remain = 0L
|
||||
)
|
||||
runBlocking {
|
||||
ServiceLocator.policyDB.update(policy)
|
||||
}
|
||||
|
||||
val filter = IntentFilter(Intent.ACTION_VIEW)
|
||||
filter.addCategory(Intent.CATEGORY_DEFAULT)
|
||||
val monitor = instrumentation.addMonitor(filter, null, false)
|
||||
|
||||
// Try to call su from ADB shell
|
||||
val cmd = if (Build.VERSION.SDK_INT < 24) {
|
||||
// API 23 runs executeShellCommand as root
|
||||
"/system/xbin/su 2000 su -c id"
|
||||
} else {
|
||||
"su -c id"
|
||||
}
|
||||
val pfd = uiAutomation.executeShellCommand(cmd)
|
||||
|
||||
// Make sure SuRequestActivity is launched
|
||||
val suRequest = monitor.waitForActivityWithTimeout(TimeUnit.SECONDS.toMillis(10))
|
||||
assertNotNull("SuRequestActivity is not launched", suRequest)
|
||||
|
||||
// Check that the request went through
|
||||
AutoCloseInputStream(pfd).reader().use {
|
||||
assertTrue(
|
||||
"Cannot grant root permission from shell",
|
||||
it.readText().contains("uid=0")
|
||||
)
|
||||
}
|
||||
|
||||
// Check that the database is updated
|
||||
runBlocking {
|
||||
val policy = ServiceLocator.policyDB.fetch(2000)
|
||||
?: throw AssertionError("PolicyDB is invalid")
|
||||
assertEquals("Policy for shell is incorrect", SuPolicy.ALLOW, policy.policy)
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,25 +6,25 @@
|
||||
<string name="logs">السجلات</string>
|
||||
<string name="settings">الإعدادات</string>
|
||||
<string name="install">تثبيت</string>
|
||||
<string name="section_home">الأساسي</string>
|
||||
<string name="section_theme">السِمات</string>
|
||||
<string name="section_home">الصفحة الرئيسية</string>
|
||||
<string name="section_theme">المظهر</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">لا يوجد إتصال</string>
|
||||
<string name="app_changelog">تفاصيل التحديث</string>
|
||||
<string name="loading">جارٍ التحميل...</string>
|
||||
<string name="update">تحديث</string>
|
||||
<string name="not_available">غير/متوفر</string>
|
||||
<string name="not_available">غير متوفر</string>
|
||||
<string name="hide">إخفاء</string>
|
||||
<string name="home_package">الحزمة</string>
|
||||
|
||||
<string name="home_support_title">تبرع لنا</string>
|
||||
<string name="home_item_source">الكود المصدري للتطبيق</string>
|
||||
<string name="home_support_content">مـاجـيسك هي، وستظل دوماً، مجانيةً و مفتوحة المصدر، اظهر اهتمامك لنا لكي نبقيها هكذا بدعم مالي صغير</string>
|
||||
<string name="home_support_content">ماجيسك هو، وسيظل دوماً، مجانياّ و مفتوح المصدر، اظهر اهتمامك لنا لكي نبقيه هكذا بدعم مالي صغير</string>
|
||||
<string name="home_installed_version">تم التثبيت</string>
|
||||
<string name="home_latest_version">آخر إصدار</string>
|
||||
<string name="invalid_update_channel">مصدر التحديث غير صالح</string>
|
||||
<string name="uninstall_magisk_title">إلغاء تثبيت Magisk</string>
|
||||
<string name="uninstall_magisk_title">إلغاء تثبيت ماجيسك</string>
|
||||
<string name="uninstall_magisk_msg">ستُعطل/ستُحذف جميع الإضافات. سيُحذف الروت، وربما ستشفر بياناتك إذا لم تكن غير مشفرة حالياً.</string>
|
||||
|
||||
<!--Install-->
|
||||
@@ -34,7 +34,7 @@
|
||||
<string name="install_options_title">الخيارات</string>
|
||||
<string name="install_method_title">الطريقة</string>
|
||||
<string name="install_next">التالي</string>
|
||||
<string name="install_start">هيا، بنا</string>
|
||||
<string name="install_start">هيا بنا</string>
|
||||
<string name="manager_download_install">اضغط للتنزيل و التثبيت</string>
|
||||
<string name="direct_install">تثبيت مباشر (موصى بها)</string>
|
||||
<string name="install_inactive_slot">التثبيت على المنطقة الغير نشطة (بعد OTA)</string>
|
||||
@@ -87,15 +87,15 @@
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">إظهار برامج النظام</string>
|
||||
<string name="hide_filter_hint">البحث بالإسم</string>
|
||||
<string name="hide_filter_hint">البحث بالاسم</string>
|
||||
<string name="hide_search">ابحث</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(لم توفر معلومات)</string>
|
||||
<string name="no_info_provided">(لا تتوفر معلومات)</string>
|
||||
<string name="reboot_recovery">إعادة التشغيل إلى Recovery</string>
|
||||
<string name="reboot_bootloader">إعادة التشغيل إلى Bootloader</string>
|
||||
<string name="reboot_download">إعادة التشغيل إلى وضـع Odin</string>
|
||||
<string name="reboot_edl">إعادة التشغيل إلى وضـعية EDL</string>
|
||||
<string name="reboot_edl">إعادة التشغيل إلى EDL</string>
|
||||
<string name="module_version_author">%1$sبواسطة%2$s</string>
|
||||
<string name="module_state_remove">إزالة </string>
|
||||
<string name="module_state_restore">إسترجاع</string>
|
||||
@@ -104,15 +104,15 @@
|
||||
<string name="external_rw_permission_denied">امنحني إذن الولوج للذاكرة الداخلية</string>
|
||||
|
||||
<!--Settings -->
|
||||
<string name="settings_dark_mode_title">وضـعية الِسمات</string>
|
||||
<string name="settings_dark_mode_message">حدد الوضـع الذي يناسب ذوقك</string>
|
||||
<string name="settings_dark_mode_title">المظهر</string>
|
||||
<string name="settings_dark_mode_message">حدد المظهر الذي يناسب ذوقك</string>
|
||||
<string name="settings_dark_mode_light">الوضـع المضيء</string>
|
||||
<string name="settings_dark_mode_system">اتبّع النظام</string>
|
||||
<string name="settings_dark_mode_dark">وضـع الظلام</string>
|
||||
<string name="settings_dark_mode_dark">الوضع المظلم</string>
|
||||
<string name="settings_download_path_title">مسار التحميل</string>
|
||||
<string name="settings_download_path_message">ستحمل الملفات إلى %1$s</string>
|
||||
<string name="language">اللغة</string>
|
||||
<string name="system_default">(الأفتراضي)</string>
|
||||
<string name="system_default">(الإفتراضي)</string>
|
||||
<string name="settings_check_update_title">تحقق من التحديثات</string>
|
||||
<string name="settings_check_update_summary">التحقق من التحديثات في الخلفية بشكل دوري</string>
|
||||
<string name="settings_update_channel_title">مصدر التحديثات</string>
|
||||
@@ -123,8 +123,8 @@
|
||||
<string name="settings_hosts_title">موانع الاعلانات</string>
|
||||
<string name="settings_hosts_summary">حجب الاعلانات دون تعديل النظام</string>
|
||||
<string name="settings_hosts_toast">تم تمكين خاصية حجب الاعلانات</string>
|
||||
<string name="settings_app_name_hint">الإسم الجديد</string>
|
||||
<string name="settings_app_name_helper">التطبيق الجديد سوف يملك هذا الإسم</string>
|
||||
<string name="settings_app_name_hint">الاسم الجديد</string>
|
||||
<string name="settings_app_name_helper">التطبيق الجديد سوف يملك هذا الاسم</string>
|
||||
<string name="settings_app_name_error">الصيغة غير مقبولة</string>
|
||||
<string name="settings_su_app_adb">التطبيقات و ADB</string>
|
||||
<string name="settings_su_app">التطبيقات فقط</string>
|
||||
@@ -140,51 +140,51 @@
|
||||
<string name="auto_response">الفعل التلقائي</string>
|
||||
<string name="request_timeout">المهلة قبل الفعل التلقائي</string>
|
||||
<string name="superuser_notification">إشعارات طلبات الروت</string>
|
||||
<string name="settings_su_reauth_title">إعادة المصادقة بعد الترقية</string>
|
||||
<string name="settings_su_reauth_summary">أعد مصادقة صلاحيات الروت بعد إجراء ترقيات للتطبيق</string>
|
||||
<string name="settings_su_reauth_title">إعادة المصادقة بعد التحديث</string>
|
||||
<string name="settings_su_reauth_summary">أعد مصادقة صلاحيات الروت بعد تحديث التطبيق</string>
|
||||
<string name="settings_customization">تخصيص</string>
|
||||
|
||||
<string name="multiuser_mode">نمط مستخدمين متعددين</string>
|
||||
<string name="multiuser_mode">نمط المستخدم المزدوج</string>
|
||||
<string name="settings_owner_only">مالك الجهاز فقط</string>
|
||||
<string name="settings_owner_manage">المالك هو من يحدد</string>
|
||||
<string name="settings_user_independent">مستقل </string>
|
||||
<string name="settings_user_independent">مستقل</string>
|
||||
<string name="owner_only_summary">للمالك فقط له صلاحيات الروت</string>
|
||||
<string name="owner_manage_summary">فقط المالك من يرفض و يمنح صلاحيات الروت</string>
|
||||
<string name="user_independent_summary">كل مستخدم له قواعد روت خاصة به</string>
|
||||
|
||||
<string name="mount_namespace_mode">نمط Mount Namespace</string>
|
||||
<string name="settings_ns_global">نمط Namespace العام</string>
|
||||
<string name="settings_ns_requester">نمط NameSpace المتوارث</string>
|
||||
<string name="settings_ns_isolate">نمط NameSpace المعزول</string>
|
||||
<string name="settings_ns_global">نمط Namespace عام</string>
|
||||
<string name="settings_ns_requester">نمط NameSpace متوارث</string>
|
||||
<string name="settings_ns_isolate">نمط NameSpace معزول</string>
|
||||
<string name="global_summary">جميع الجلسات الروت تستخدم NameSpace العام</string>
|
||||
<string name="requester_summary">جميع الجلسات الروت تستخدم NameSpace المتوارث</string>
|
||||
<string name="isolate_summary">جميع الجلسات الروت تستخدم NameSpace المعزول</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">تحديثات Magisk</string>
|
||||
<string name="update_channel">تحديثات ماجيسك</string>
|
||||
<string name="progress_channel">إشعارات التقدم</string>
|
||||
<string name="download_complete">اكتمل التنزيل</string>
|
||||
<string name="download_file_error">فشل تنزيل الملف</string>
|
||||
<string name="magisk_update_title">تحديث مـاجـيسك متوفر!</string>
|
||||
<string name="magisk_update_title">تحديث ماجيسك متوفر!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">نعم</string>
|
||||
<string name="no">لا</string>
|
||||
<string name="download">تنزيل</string>
|
||||
<string name="reboot">إعادة التشغيل</string>
|
||||
<string name="release_notes">معلومات الأصدار الجديد</string>
|
||||
<string name="flashing">يتم الحرق...</string>
|
||||
<string name="release_notes">معلومات الإصدار الجديد</string>
|
||||
<string name="flashing">يتم التثبيت...</string>
|
||||
<string name="done">تم!</string>
|
||||
<string name="failure">فشل!</string>
|
||||
<string name="open_link_failed_toast">لم يُعثر على تطبيق لفتح الرابط …</string>
|
||||
<string name="complete_uninstall">إلغاء التثبيت بالكامل</string>
|
||||
<string name="restore_img">استعادة الصور</string>
|
||||
<string name="restore_img_msg">جار الأستعادة…</string>
|
||||
<string name="restore_done">تم الأستعادة</string>
|
||||
<string name="restore_fail">النسخة الاحتياطية الأصلية غير موجودة!</string>
|
||||
<string name="setup_fail">فشل التضبيط</string>
|
||||
<string name="env_fix_title"> الإعداد الأضافي مطلوب</string>
|
||||
<string name="setup_msg">جار تضبيت البيئة</string>
|
||||
<string name="unsupport_magisk_title">إصدار مـاجـيسك غير مدعوم</string>
|
||||
<string name="restore_img_msg">جار الإستعادة…</string>
|
||||
<string name="restore_done">تم الإستعادة</string>
|
||||
<string name="restore_fail">النسخة الإحتياطية الأصلية غير موجودة!</string>
|
||||
<string name="setup_fail">فشل الإعداد</string>
|
||||
<string name="env_fix_title">الإعداد الإضافي مطلوب</string>
|
||||
<string name="setup_msg">جار إعداد البيئة</string>
|
||||
<string name="unsupport_magisk_title">إصدار ماجيسك غير مدعوم</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<string name="modules">Módulos</string>
|
||||
<string name="superuser">Superusuariu</string>
|
||||
<string name="logs">Rexistru</string>
|
||||
<string name="settings">Axustes</string>
|
||||
<string name="settings">Configuración</string>
|
||||
<string name="install">Instalar</string>
|
||||
<string name="section_home">Aniciu</string>
|
||||
<string name="section_theme">Estilos</string>
|
||||
@@ -15,7 +15,7 @@
|
||||
<string name="loading">Cargando…</string>
|
||||
<string name="update">Anovar</string>
|
||||
<string name="not_available">N/D</string>
|
||||
<string name="hide">Anubrir</string>
|
||||
<string name="hide">Esconder</string>
|
||||
<string name="home_package">Paquete</string>
|
||||
<string name="home_app_title">Aplicación</string>
|
||||
<string name="home_notice_content">Baxa Magisk NAMÁS dende la páxina oficial de GitHub. ¡Los ficheros de fontes desconocíes puen ser maliciosos!</string>
|
||||
@@ -39,10 +39,10 @@
|
||||
<string name="manager_download_install">Primi equí pa baxalu ya instalalu</string>
|
||||
<string name="direct_install">Instalación direuta (aconséyase)</string>
|
||||
<string name="install_inactive_slot">Instalar na ralura inactiva (darréu del OTA)</string>
|
||||
<string name="install_inactive_slot_msg">¡El preséu va arrincar OBLIGATORIAMENTE na ralura inactiva darréu de reaniciar!\nUsa esta opción namás dempués d\'acabar l\'anovamientu per OTA.\n¿Quies siguir?</string>
|
||||
<string name="install_inactive_slot_msg">¡El preséu va arrincar OBLIGATORIAMENTE na ralura inactiva dempués de reaniciar!\nUsa esta opción namás dempués d\'acabar l\'anovamientu per OTA.\n¿Quies siguir?</string>
|
||||
<string name="setup_title">Configuración adicional</string>
|
||||
<string name="select_patch_file">Seleicionar y parchiar un ficheru</string>
|
||||
<string name="patch_file_msg">Seleiciona una imaxe en bruto (*.img) o un archivu d\'ODIN (*.tar)</string>
|
||||
<string name="patch_file_msg">Seleiciona una imaxe en bruto (*.img), un archivu d\'ODIN (*.tar) o un ficheru payload.bin (*.bin)</string>
|
||||
<string name="reboot_delay_toast">Reaniciando en 5 segundos…</string>
|
||||
<string name="flash_screen_title">Instalación</string>
|
||||
<!--Superuser-->
|
||||
@@ -81,6 +81,9 @@
|
||||
<string name="logs_cleared">El rexistru borróse correutamente</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">UID de destín: %1$d</string>
|
||||
<string name="target_pid">Mount ns target PID: %s</string>
|
||||
<string name="selinux_context">Contestu de SELinux: %s</string>
|
||||
<string name="supp_group">Grupu suplementariu: %s</string>
|
||||
<!--SafetyNet-->
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Aplicaciones del sistema</string>
|
||||
@@ -94,15 +97,19 @@
|
||||
<string name="reboot_bootloader">Reaniciar al cargador d\'arrinque</string>
|
||||
<string name="reboot_download">Reaniciar al mou de descarga</string>
|
||||
<string name="reboot_edl">Reaniciar al mou EDL</string>
|
||||
<string name="reboot_safe_mode">Mou seguru</string>
|
||||
<string name="module_version_author">%1$s por %2$s</string>
|
||||
<string name="module_state_remove">Quitar</string>
|
||||
<string name="module_action">Aición</string>
|
||||
<string name="module_state_restore">Restaurar</string>
|
||||
<string name="module_action_install_external">Instalar dende l\'almacenamientu</string>
|
||||
<string name="update_available">Hai un anovamientu disponible</string>
|
||||
<string name="suspend_text_riru">Suspendióse\'l módulu porque s\'activó «%1$s»</string>
|
||||
<string name="suspend_text_zygisk">Suspendióse\'l módulu porque nun s\'activó «%1$s»</string>
|
||||
<string name="zygisk_module_unloaded">El módulu de Zygisk nun cargó pola mor d\'haber incompatibilidaes</string>
|
||||
<string name="zygisk_module_unloaded">El módulu de Zygisk nun cargó por haber incompatibilidaes</string>
|
||||
<string name="module_empty">Nun hai nengún módulu instaláu</string>
|
||||
<string name="confirm_install">¿Quies instalar el módulu «%1$s»?</string>
|
||||
<string name="confirm_install_title">Confirmación de la instalación</string>
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Mou del estilu</string>
|
||||
<string name="settings_dark_mode_message">¡Seleiciona\'l mou que meyor s\'adaute al to estilu!</string>
|
||||
@@ -111,7 +118,7 @@
|
||||
<string name="settings_dark_mode_dark">Escuridá</string>
|
||||
<string name="settings_download_path_title">Camín de les descargues</string>
|
||||
<string name="settings_download_path_message">Los ficheros van guardase en «%1$s»</string>
|
||||
<string name="settings_hide_app_title">Anubrir Magisk</string>
|
||||
<string name="settings_hide_app_title">Esconder Magisk</string>
|
||||
<string name="settings_hide_app_summary">Instala una aplicación intermedia con una ID y una etiqueta al debalu</string>
|
||||
<string name="settings_restore_app_title">Restaurar el mou visible</string>
|
||||
<string name="settings_restore_app_summary">Fai que l\'aplicación orixinal vuelva ser visible</string>
|
||||
@@ -149,14 +156,19 @@
|
||||
<string name="auto_response">Rempuesta automática</string>
|
||||
<string name="request_timeout">Tiempu d\'espera de les solicitúes</string>
|
||||
<string name="superuser_notification">Avisu de superusuariu</string>
|
||||
<string name="settings_su_reauth_title">Volver autenticar darréu d\'anovar</string>
|
||||
<string name="settings_su_reauth_title">Volver autenticar dempués d\'anovar</string>
|
||||
<string name="settings_su_reauth_summary">Vuelve pidir los permisos de superusuariu dempués d\'anovar les aplicaciones</string>
|
||||
<string name="settings_su_tapjack_title">Proteición escontra\'l tapjacking</string>
|
||||
<string name="settings_su_tapjack_summary">El diálogu de concesión de permisos de superusuariu nun respuende a la entrada mentanto lu torgue otra ventana o superposición</string>
|
||||
<string name="settings_su_auth_title">Autenticación d\'usuariu</string>
|
||||
<string name="settings_su_auth_summary">Pide l\'autenticación demientres les solicitúes de superusuariu</string>
|
||||
<string name="settings_su_auth_insecure">Nun se configuró nengún métodu d\'autenticación nel preséu</string>
|
||||
<string name="settings_customization">Personalización</string>
|
||||
<string name="setting_add_shortcut_summary">Amiesta un atayu a la pantalla d\'aniciu en casu de que\'l nome y l\'iconu seyan difíciles de reconocer darréu d\'anubrir l\'aplicación</string>
|
||||
<string name="setting_add_shortcut_summary">Amiesta un atayu a la pantalla d\'aniciu en casu de que\'l nome y l\'iconu seyan difíciles de reconocer dempués d\'esconder l\'aplicación</string>
|
||||
<string name="settings_doh_title">DNS per HTTPS</string>
|
||||
<string name="settings_doh_description">Una igua alternativa pal envelenamientu de DNS en dalgunos países</string>
|
||||
<string name="settings_random_name_title">Nome de la salida aleatoriu</string>
|
||||
<string name="settings_random_name_description">Fai que\'l nome de ficheru de la salida de les imáxenes parchiaes y los ficheros tar seya aleatoriu pa impidir la deteición</string>
|
||||
<string name="multiuser_mode">Mou de multiusuariu</string>
|
||||
<string name="settings_owner_only">Namás el propietariu del preséu</string>
|
||||
<string name="settings_owner_manage">El propietariu xestionáu del preséu</string>
|
||||
@@ -186,11 +198,14 @@
|
||||
<string name="repo_install_title">Instalación de: %1$s %2$s (%3$d)</string>
|
||||
<string name="download">Baxar</string>
|
||||
<string name="reboot">Reaniciar</string>
|
||||
<string name="close">Zarrar</string>
|
||||
<string name="release_notes">Notes de la versión</string>
|
||||
<string name="flashing">Flaxando…</string>
|
||||
<string name="running">Executando…</string>
|
||||
<string name="done">¡Fecho!</string>
|
||||
<string name="done_action">Completóse l\'aición de: %1$s</string>
|
||||
<string name="failure">¡Falló!</string>
|
||||
<string name="hide_app_title">Anubriendo l\'aplicación Magisk…</string>
|
||||
<string name="hide_app_title">Escondiendo l\'aplicación Magisk…</string>
|
||||
<string name="open_link_failed_toast">Nun s\'atopó nenguna aplicación p\'abrir l\'enllaz</string>
|
||||
<string name="complete_uninstall">Desinstalar dafechu</string>
|
||||
<string name="restore_img">Restaurar les imáxenes</string>
|
||||
@@ -200,6 +215,7 @@
|
||||
<string name="setup_fail">La configuración falló</string>
|
||||
<string name="env_fix_title">Configuración adicional</string>
|
||||
<string name="env_fix_msg">El preséu precisa una configuración adicional pa que Magisk funcione afayadizamente. ¿Quies siguir y reaniciar?</string>
|
||||
<string name="env_full_fix_msg">El preséu precisa volver flaxar Magisk pa que funcione afayadizamente. Volvi instalar Magisk dientro de l\'aplicación porque\'l mou de recuperación nun pue consiguir la información correuta del preséu.</string>
|
||||
<string name="setup_msg">Executando la configuración del entornu…</string>
|
||||
<string name="unsupport_magisk_title">Versión non compatible</string>
|
||||
<string name="unsupport_magisk_msg">Esta versión de l\'aplicación nun ye compatible coles versiones de Magisk anteriores a la %1$s.\n\nL\'aplicación va comportase como si Magisk nun tuviere instaláu, anueva Magisk namás que puedas.</string>
|
||||
@@ -207,12 +223,13 @@
|
||||
<string name="unsupport_system_app_msg">Esta aplicación nun se pue executar nel espaciu del sistema. Volvi instalala mas nel espaciu del usuariu.</string>
|
||||
<string name="unsupport_other_su_msg">Detectóse un binariu «su» que nun ye de Magisk. Quita cualesquier solución de root y/o volvi instalar Magisk.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk ta instaláu nel almacenamientu esternu. Movi l\'aplicación al almacenamientu internu, por favor.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Magisk nun pue siguir funcionando nel mou anubríu darréu que se perdió\'l root. Restaura\'l mou visible de l\'aplicación.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Magisk nun pue siguir funcionando nel mou escondíu darréu que se perdió\'l root. Restaura\'l mou visible de l\'aplicación.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Concede\'l permisu d\'almacenamientu p\'activar esta funcionalidá</string>
|
||||
<string name="post_notifications_denied">Concede\'l permisu de los avisos p\'activar esta función</string>
|
||||
<string name="install_unknown_denied">Permite la instalación d\'aplicaciones desconocíes p\'activar esta funcionalidá</string>
|
||||
<string name="add_shortcut_title">Amestar un atayu a la pantalla d\'aniciu</string>
|
||||
<string name="add_shortcut_msg">Darréu d\'anubrir esta aplicación, el so nome ya iconu van ser difíciles de reconocer. ¿Quies amestar un atayu a la pantalla d\'aniciu?</string>
|
||||
<string name="add_shortcut_msg">Dempués d\'esconder esta aplicación, el so nome ya iconu van ser difíciles de reconocer. ¿Quies amestar un atayu a la pantalla d\'aniciu?</string>
|
||||
<string name="app_not_found">Nun s\'atopó nenguna aplicación pa remanar esta aición</string>
|
||||
<string name="reboot_apply_change">Reanicia p\'aplicar los cambeos</string>
|
||||
<string name="restore_app_confirmation">Esta aición va restaurar l\'aplicación orixinal y desanicia la intermedia. ¿De xuru que quies facelo?</string>
|
||||
|
252
app/core/src/main/res/values-b+sr+Latn/strings.xml
Normal file
252
app/core/src/main/res/values-b+sr+Latn/strings.xml
Normal file
@@ -0,0 +1,252 @@
|
||||
<resources>
|
||||
<!--Author: Radoš Milićev (https://github.com/rammba)-->
|
||||
|
||||
<!--Sections-->
|
||||
<string name="modules">Moduli</string>
|
||||
<string name="superuser">Super-korisnik</string>
|
||||
<string name="logs">Logovi</string>
|
||||
<string name="settings">Podešavanja</string>
|
||||
<string name="install">Instalacija</string>
|
||||
<string name="section_home">Početno</string>
|
||||
<string name="section_theme">Teme</string>
|
||||
<string name="denylist">Lista zabrana</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">Nedostupna konekcija</string>
|
||||
<string name="app_changelog">Promene u aplikaciji</string>
|
||||
<string name="loading">Učitavanje…</string>
|
||||
<string name="update">Ažuriranje</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Sakrij</string>
|
||||
<string name="home_package">Paket</string>
|
||||
<string name="home_app_title">Apl.</string>
|
||||
|
||||
<string name="home_notice_content">Preuzmite Magisk SAMO sa zvanične GitHub stranice. Fajlovi iz nepoznatih izvora mogu biti maliciozni!</string>
|
||||
<string name="home_support_title">Podržite nas</string>
|
||||
<string name="home_follow_title">Zapratite nas</string>
|
||||
<string name="home_item_source">Izvor</string>
|
||||
<string name="home_support_content">Magisk jeste i uvek će biti besplatan i open source. Možete pokazati da vam je stalo svojom donacijom.</string>
|
||||
<string name="home_installed_version">Instalirano</string>
|
||||
<string name="home_latest_version">Najnovije</string>
|
||||
<string name="invalid_update_channel">Nevalidan kanal ažuriranja</string>
|
||||
<string name="uninstall_magisk_title">Deinstaliraj Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Svi moduli će biti onemogućeni/uklonjeni!\nKoren će biti uklonjen!\nSvako neenkriptovano interno skladište će upotrebom Magisk-a biti ponovo enkriptovano!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Zadrži forsiranu enkripciju</string>
|
||||
<string name="keep_dm_verity">Zadrži AVB 2.0/dm-verity</string>
|
||||
<string name="recovery_mode">Režim oporavka</string>
|
||||
<string name="install_options_title">Opcije</string>
|
||||
<string name="install_method_title">Metod</string>
|
||||
<string name="install_next">Naredno</string>
|
||||
<string name="install_start">Počnimo</string>
|
||||
<string name="manager_download_install">Pritisni da preuzmeš i instaliraš</string>
|
||||
<string name="direct_install">Direktna instalacija (Preporučeno)</string>
|
||||
<string name="install_inactive_slot">Instalacija na neaktivan slot (Nakon OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Vaš uređaj će biti FORSIRAN da se pokrene na trenutno neaktivnom slotu nakon ponovnog pokretanja!\nKoristite opciju samo kad se OTA završi.\nNastavi?</string>
|
||||
<string name="setup_title">Dodatne postavke</string>
|
||||
<string name="select_patch_file">Izaberite fajl</string>
|
||||
<string name="patch_file_msg">Izaberite sliku (*.img) ili ODIN tarfile (*.tar) ili payload.bin (*.bin)</string>
|
||||
<string name="reboot_delay_toast">Ponovo pokretanje za 5 sekundi…</string>
|
||||
<string name="flash_screen_title">Instalacija</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Super-korisnički zahtev</string>
|
||||
<string name="touch_filtered_warning">Magisk ne može da verifikuje vaš odgovor, jer aplikacija prikriva super-korisnički zahtev</string>
|
||||
<string name="deny">Zabrani</string>
|
||||
<string name="prompt">Zahtev</string>
|
||||
<string name="grant">Dozvoli</string>
|
||||
<string name="su_warning">Pruža potpun pristup vašem uređaju.\nZabranite ako niste sigurni!</string>
|
||||
<string name="forever">Zauvek</string>
|
||||
<string name="once">Jednom</string>
|
||||
<string name="tenmin">10 min</string>
|
||||
<string name="twentymin">20 min</string>
|
||||
<string name="thirtymin">30 min</string>
|
||||
<string name="sixtymin">60 min</string>
|
||||
<string name="su_allow_toast">%1$s je dobio prava na super-korisnika</string>
|
||||
<string name="su_deny_toast">%1$s nije dobio prava na super-korisnika</string>
|
||||
<string name="su_snack_grant">Super-korisnička prava od %1$s su pružena</string>
|
||||
<string name="su_snack_deny">Super-korisnička prava od %1$s su odbijena</string>
|
||||
<string name="su_snack_notif_on">Notifikacije od %1$s su omogućene</string>
|
||||
<string name="su_snack_notif_off">Notifikacije od %1$s su onemogućene</string>
|
||||
<string name="su_snack_log_on">Logovanje za %1$s je omogućeno</string>
|
||||
<string name="su_snack_log_off">Logovanje za %1$s je onemogućeno</string>
|
||||
<string name="su_revoke_title">Opozovi?</string>
|
||||
<string name="su_revoke_msg">Potvrdi da opozoveš prava na super-korisnika od %1$s?</string>
|
||||
<string name="toast">Toast</string>
|
||||
<string name="none">Ništa</string>
|
||||
|
||||
<string name="superuser_toggle_notification">Notifikacije</string>
|
||||
<string name="superuser_toggle_revoke">Opozovi</string>
|
||||
<string name="superuser_policy_none">Nijedna aplikacija nije tražila permisije za super-korisnika još uvek.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Nemate logova, pokušajte koristiti korenske aplikacije više</string>
|
||||
<string name="log_data_magisk_none">Magisk logovi su prazni, to je čudno</string>
|
||||
<string name="menuSaveLog">Sačuvaj log</string>
|
||||
<string name="menuClearLog">Ukloni log</string>
|
||||
<string name="logs_cleared">Log uspešno uklonjen</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Ciljani UID: %1$d</string>
|
||||
<string name="target_pid">Mount ns cinjani PID: %s</string>
|
||||
<string name="selinux_context">SELinux kontekst: %s</string>
|
||||
<string name="supp_group">Dopunska grupa: %s</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Prikaži sistemske apl.</string>
|
||||
<string name="show_os_app">Prikaži apl. OS-a</string>
|
||||
<string name="hide_filter_hint">Filtriraj po imenu</string>
|
||||
<string name="hide_search">Pretraga</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Bez informacija)</string>
|
||||
<string name="reboot_userspace">Lako ponovo pokretanje</string>
|
||||
<string name="reboot_recovery">Ponovo pokreni za oporavak</string>
|
||||
<string name="reboot_bootloader">Ponovo pokreni za bootloader</string>
|
||||
<string name="reboot_download">Ponovo pokreni za preuzimanje</string>
|
||||
<string name="reboot_edl">Ponovo pokreni za EDL</string>
|
||||
<string name="reboot_safe_mode">Siguran mod</string>
|
||||
<string name="module_version_author">%1$s od %2$s</string>
|
||||
<string name="module_state_remove">Ukloni</string>
|
||||
<string name="module_action">Akcija</string>
|
||||
<string name="module_state_restore">Povrati</string>
|
||||
<string name="module_action_install_external">Instaliraj iz skladišta</string>
|
||||
<string name="update_available">Ažuriranje dostupno</string>
|
||||
<string name="suspend_text_riru">Modul je suspendovan jer je %1$s omogućeno</string>
|
||||
<string name="suspend_text_zygisk">Modul je suspendovan jer %1$s nije omogućeno</string>
|
||||
<string name="zygisk_module_unloaded">Zygisk modul nije učitan zbog nekompatibilnosti</string>
|
||||
<string name="module_empty">Nijedan modul nije instaliran</string>
|
||||
<string name="confirm_install">Instaliraj modul %1$s?</string>
|
||||
<string name="confirm_install_title">Potvrda instalacije</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Tema</string>
|
||||
<string name="settings_dark_mode_message">Izaberite temu koja vam najviše odgovara!</string>
|
||||
<string name="settings_dark_mode_light">Uvek svetlo</string>
|
||||
<string name="settings_dark_mode_system">Prati sistem</string>
|
||||
<string name="settings_dark_mode_dark">Uvek tamno</string>
|
||||
<string name="settings_download_path_title">Putanja za preuzimanje</string>
|
||||
<string name="settings_download_path_message">Fajlovi će biti sačuvani na %1$s</string>
|
||||
<string name="settings_hide_app_title">Sakrij Magisk apl.</string>
|
||||
<string name="settings_hide_app_summary">Instaliraj proxy aplikaciju sa nasumičnim ID-jem paketa i prilagođenom labelom</string>
|
||||
<string name="settings_restore_app_title">Povrati Magisk apl.</string>
|
||||
<string name="settings_restore_app_summary">Otkrij apl. i povrati originalni APK</string>
|
||||
<string name="language">Jezik</string>
|
||||
<string name="system_default">(Podrazumevano sistemski)</string>
|
||||
<string name="settings_check_update_title">Proveri ažuriranja</string>
|
||||
<string name="settings_check_update_summary">Periodično proveri ažuriranja u pozadini</string>
|
||||
<string name="settings_update_channel_title">Kanal ažuriranja</string>
|
||||
<string name="settings_update_stable">Stabilno</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_custom">Prilagođeno</string>
|
||||
<string name="settings_update_custom_msg">Unesi prilagođeni URL kanala</string>
|
||||
<string name="settings_zygisk_summary">Pokreni delove Magisk-a u zygote daemon-u</string>
|
||||
<string name="settings_denylist_title">Sprovedi listu zabrana</string>
|
||||
<string name="settings_denylist_summary">Procesi na listi zabrana će povratiti sve Magisk izmene</string>
|
||||
<string name="settings_denylist_config_title">Konfiguriši listu zabrana</string>
|
||||
<string name="settings_denylist_config_summary">Izaberi procese koji će biti na listi zabrana</string>
|
||||
<string name="settings_hosts_title">Bezsistemski domaćini (hosts)</string>
|
||||
<string name="settings_hosts_summary">Podrška bezsistemskih domaćina za aplikacije blokiranja reklama</string>
|
||||
<string name="settings_hosts_toast">Modul bezsistemskih domaćina dodat</string>
|
||||
<string name="settings_app_name_hint">Novo ime</string>
|
||||
<string name="settings_app_name_helper">Apl. će biti spakovana pod ovim imenom</string>
|
||||
<string name="settings_app_name_error">Nevalidan format</string>
|
||||
<string name="settings_su_app_adb">Aplikacije i ADB</string>
|
||||
<string name="settings_su_app">Samo aplikacije</string>
|
||||
<string name="settings_su_adb">Samo ADB</string>
|
||||
<string name="settings_su_disable">Onemogućeno</string>
|
||||
<string name="settings_su_request_10">10 sekundi</string>
|
||||
<string name="settings_su_request_15">15 sekundi</string>
|
||||
<string name="settings_su_request_20">20 sekundi</string>
|
||||
<string name="settings_su_request_30">30 sekundi</string>
|
||||
<string name="settings_su_request_45">45 sekundi</string>
|
||||
<string name="settings_su_request_60">60 sekundi</string>
|
||||
<string name="superuser_access">Pristup super-korisnika</string>
|
||||
<string name="auto_response">Automatski odgovor</string>
|
||||
<string name="request_timeout">Istek zahteva</string>
|
||||
<string name="superuser_notification">Notifikacije super-korisnika</string>
|
||||
<string name="settings_su_reauth_title">Ponovo odobri nakon ažuriranja</string>
|
||||
<string name="settings_su_reauth_summary">Ponovo traži permisije super-korisnika nakon ažuriranja aplikacija</string>
|
||||
<string name="settings_su_tapjack_title">Zaštita od tapjacking-a</string>
|
||||
<string name="settings_su_tapjack_summary">Prompt dijalog super-korisnika neće reagovati dok je prikriven drugim prozorom ili overlay-em</string>
|
||||
<string name="settings_su_auth_title">Autentifikacija korisnika</string>
|
||||
<string name="settings_su_auth_summary">Traži autentifikaciju korisnika tokom zahteva super-korisnika</string>
|
||||
<string name="settings_su_auth_insecure">Nijedan metod autentifikacije nije podešen na uređaju</string>
|
||||
<string name="settings_customization">Prilagođavanje</string>
|
||||
<string name="setting_add_shortcut_summary">Dodaj lepu prečicu na početni ekran u slučaju da se ime i ikonica ne prepoznaju lako nakon skrivanja aplikacije</string>
|
||||
<string name="settings_doh_title">DNS preko HTTPS-a</string>
|
||||
<string name="settings_doh_description">Zaobilazno rešenje DNS trovanja u nekim nacijama</string>
|
||||
<string name="settings_random_name_title">Nasumično ime na izlazu</string>
|
||||
<string name="settings_random_name_description">Nasumično ime izlaznog fajla slika i tar fajlova radi sprečavanja detekcije</string>
|
||||
|
||||
<string name="multiuser_mode">Višekorisnički režim</string>
|
||||
<string name="settings_owner_only">Samo vlasnik uređaja</string>
|
||||
<string name="settings_owner_manage">Određeno od strane vlasnika</string>
|
||||
<string name="settings_user_independent">Nezavisno od korisnika</string>
|
||||
<string name="owner_only_summary">Samo vlasnik ima pristup korenu</string>
|
||||
<string name="owner_manage_summary">Samo vlasnik može da pristupa korenu i da prima zahteve za njega</string>
|
||||
<string name="user_independent_summary">Svaki korisnik ima svoja pravila korena</string>
|
||||
|
||||
<string name="mount_namespace_mode">Mount režim namespace-a</string>
|
||||
<string name="settings_ns_global">Globalni namespace</string>
|
||||
<string name="settings_ns_requester">Nasleđeni namespace</string>
|
||||
<string name="settings_ns_isolate">Izolovani namespace</string>
|
||||
<string name="global_summary">Sve korenske sesije koriste globalni mount namespace</string>
|
||||
<string name="requester_summary">Korenske sesije će naslediti namespace od podnosioca zahteva</string>
|
||||
<string name="isolate_summary">Svaka korenska sesija će imati svoj izolovani namespace</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Ažuriranja Magisk-a</string>
|
||||
<string name="progress_channel">Notifikacije o progresu</string>
|
||||
<string name="updated_channel">Ažuriranje završeno</string>
|
||||
<string name="download_complete">Preuzimanje završeno</string>
|
||||
<string name="download_file_error">Greška pri preuzimanju fajla</string>
|
||||
<string name="magisk_update_title">Ažuriranje Magisk-a dostupno!</string>
|
||||
<string name="updated_title">Magisk je ažuriran</string>
|
||||
<string name="updated_text">Klikni da otvoriš aplikaciju</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Da</string>
|
||||
<string name="no">Ne</string>
|
||||
<string name="repo_install_title">Instaliraj %1$s %2$s(%3$d)</string>
|
||||
<string name="download">Preuzmi</string>
|
||||
<string name="reboot">Ponovo pokreni</string>
|
||||
<string name="close">Zatvori</string>
|
||||
<string name="release_notes">Release notes</string>
|
||||
<string name="flashing">Flešovanje…</string>
|
||||
<string name="running">Pokretanje…</string>
|
||||
<string name="done">Završeno!</string>
|
||||
<string name="done_action">Pokretanje akcije %1$s završeno</string>
|
||||
<string name="failure">Neuspešno!</string>
|
||||
<string name="hide_app_title">Skrivanje Magisk aplikacije…</string>
|
||||
<string name="open_link_failed_toast">Nije pronađena aplikacija za otvaranje linka</string>
|
||||
<string name="complete_uninstall">Kompletna deinstalacija</string>
|
||||
<string name="restore_img">Povrati slike</string>
|
||||
<string name="restore_img_msg">Povratak…</string>
|
||||
<string name="restore_done">Povratak uspešan!</string>
|
||||
<string name="restore_fail">Fabrički bekap ne postoji!</string>
|
||||
<string name="setup_fail">Neuspešna postavka</string>
|
||||
<string name="env_fix_title">Potrebno dodatno podešavanje</string>
|
||||
<string name="env_fix_msg">Vaš uređaj zahteva dodatno podešavanje da bi Magisk radio kako treba. Da li želite nastaviti i pokrenuti ponovo?</string>
|
||||
<string name="env_full_fix_msg">Vaš uređaj zahteva ponovno flešovanje da bi Magisk radio kako treba. Reinstalirajte Magisk kroz aplikaciju, režim oporavka ne može dobiti tačne informacije o uređaju.</string>
|
||||
<string name="setup_msg">Pokretanje podešavanja okruženja…</string>
|
||||
<string name="unsupport_magisk_title">Nepodržana verzija Magisk-a</string>
|
||||
<string name="unsupport_magisk_msg">Ova verzija aplikacije ne podržava Magisk verzije manje od %1$s.\n\nAplikacija će se ponašati kao da Magisk nije instaliran, molimo ažurirajte Magisk što pre.</string>
|
||||
<string name="unsupport_general_title">Nenormalno stanje</string>
|
||||
<string name="unsupport_system_app_msg">Pokretanje aplikacije kao sistemske nije podržano. Molimo postavite aplikaciju da bude korisnička.</string>
|
||||
<string name="unsupport_other_su_msg">Detektovan \"su\" binary koji nije Magisk-ov. Molimo uklonite konkurentno korensko rešenje i/ili reinstalirajte Magisk.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk je instaliran na eksterno skladište. Molimo pomerite apl. u interno skladište.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Skrivena Magisk aplikacija ne može nastaviti sa radom jer je koren izgubljen. Molimo povratite originalni APK.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Dozvolite permisiju za skladište da biste omogućili ovu funkcionalnost</string>
|
||||
<string name="post_notifications_denied">Dozvolite permisiju za notifikacije da biste omogućili ovu funkcionalnost</string>
|
||||
<string name="install_unknown_denied">Dozvolite "instaliranje nepoznatih aplikacija" da biste omogućili ovu funkcionalnost</string>
|
||||
<string name="add_shortcut_title">Dodaj prečicu na početni ekran</string>
|
||||
<string name="add_shortcut_msg">Nakon skrivanja aplikacije, njeno ime i ikonicu ćete teško prepoznati. Želite li dodati lepu prečicu na početni ekran?</string>
|
||||
<string name="app_not_found">Nije pronađena aplikacija za ovu akciju</string>
|
||||
<string name="reboot_apply_change">Ponovo pokreni da primeniš izmene</string>
|
||||
<string name="restore_app_confirmation">Ovo će vratiti skrivenu aplikaciju na originalnu. Da li stvarno to želite?</string>
|
||||
|
||||
</resources>
|
@@ -3,7 +3,7 @@
|
||||
<!--Sections-->
|
||||
<string name="modules">מודולים</string>
|
||||
<string name="superuser">משתמש על</string>
|
||||
<string name="logs">יומני רישום</string>
|
||||
<string name="logs">Log</string>
|
||||
<string name="settings">הגדרות</string>
|
||||
<string name="install">התקנה</string>
|
||||
<string name="section_home">בית</string>
|
||||
@@ -12,10 +12,10 @@
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">אין חיבור זמין</string>
|
||||
<string name="app_changelog">יומן שינויים</string>
|
||||
<string name="app_changelog">רשימת שינויים</string>
|
||||
<string name="loading">טוען…</string>
|
||||
<string name="update">עדכון</string>
|
||||
<string name="not_available">ל/ז</string>
|
||||
<string name="not_available">לא זמין</string>
|
||||
<string name="hide">הסתרה</string>
|
||||
<string name="home_package">חבילה</string>
|
||||
<string name="home_app_title">יישום</string>
|
||||
@@ -23,28 +23,28 @@
|
||||
<string name="home_support_title">תמיכה בנו</string>
|
||||
<string name="home_follow_title">עקבו אחרינו</string>
|
||||
<string name="home_item_source">מקור</string>
|
||||
<string name="home_support_content">Magisk היה ותמיד יהיה בעל קוד מקור פתוח. עם זאת באפשרותך להראות לנו שאכפת לך על ידי שליחת תרומה קטנה.</string>
|
||||
<string name="home_support_content">Magisk היה ותמיד יהיה בקוד פתוח. עם זאת באפשרותך להראות לנו שאכפת לך על ידי שליחת תרומה קטנה.</string>
|
||||
<string name="home_installed_version">מותקנת</string>
|
||||
<string name="home_latest_version">אחרונה</string>
|
||||
<string name="invalid_update_channel">ערוץ עדכון לא חוקי</string>
|
||||
<string name="uninstall_magisk_title">הסרת Magisk</string>
|
||||
<string name="uninstall_magisk_msg">כל המודולים יושבתו/יוסרו!\nגישת שורש תושבת!\nהנתונים שלך עשויים להיות מוצפנים אם לא הוצפנו כבר!</string>
|
||||
<string name="uninstall_magisk_msg">כל המודולים יושבתו/יוסרו!\nגישת Root תושבת!\nהנתונים שלך עשויים להיות מוצפנים אם לא הוצפנו כבר!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">שמירה על הצפנה בכח</string>
|
||||
<string name="keep_dm_verity">שמירה על AVB 2.0/dm-verity</string>
|
||||
<string name="recovery_mode">מצב שחזור</string>
|
||||
<string name="recovery_mode">מצב Recovery</string>
|
||||
<string name="install_options_title">אפשרויות</string>
|
||||
<string name="install_method_title">שיטה</string>
|
||||
<string name="install_next">הבא</string>
|
||||
<string name="install_start">צא לדרך</string>
|
||||
<string name="manager_download_install">לחיצה להורדה והתקנה</string>
|
||||
<string name="direct_install">התקנה ישירה (מומלץ)</string>
|
||||
<string name="install_inactive_slot">התקנה לחריץ לא פעיל (לאחר OTA)</string>
|
||||
<string name="install_inactive_slot_msg">ההתקן שלך ייאלץ אתחול לחריץ הלא פעיל הנוכחי שלך לאחר הפעלה מחדש!\nיש להשתמש באפשרות זו רק לאחר ביצוע OTA בלבד.\nלהמשיך?</string>
|
||||
<string name="install_inactive_slot">התקנה לסלוט לא פעיל (לאחר OTA)</string>
|
||||
<string name="install_inactive_slot_msg">ההתקן שלך ייאלץ אתחול לסלוט הלא פעיל הנוכחי שלך לאחר הפעלה מחדש!\nיש להשתמש באפשרות זו רק לאחר ביצוע OTA בלבד.\nלהמשיך?</string>
|
||||
<string name="setup_title">התקנה נוספת</string>
|
||||
<string name="select_patch_file">בחירה והתקנת קובץ</string>
|
||||
<string name="patch_file_msg">בחירת תמונה גולמית (*.img) או ODIN tarfile (*.tar) או payload.bin (*.bin)</string>
|
||||
<string name="patch_file_msg">בחירת קובץ גולמי (*.img) או ODIN tarfile (*.tar) או payload.bin (*.bin)</string>
|
||||
<string name="reboot_delay_toast">מאתחל בעוד 5 שניות…</string>
|
||||
<string name="flash_screen_title">התקנה</string>
|
||||
|
||||
@@ -67,22 +67,22 @@
|
||||
<string name="su_snack_deny">הרשאות משתמש על עבור %1$s נשללו</string>
|
||||
<string name="su_snack_notif_on">התראות של %1$s מופעלות</string>
|
||||
<string name="su_snack_notif_off">התראות של %1$s מושבתות</string>
|
||||
<string name="su_snack_log_on">יומני רישום עבור %1$s פועלות</string>
|
||||
<string name="su_snack_log_off">יומני רישום עבור %1$s מושבתות</string>
|
||||
<string name="su_snack_log_on">Log עבור %1$s מופעל</string>
|
||||
<string name="su_snack_log_off">Log עבור %1$s מושבת</string>
|
||||
<string name="su_revoke_title">להסיר?</string>
|
||||
<string name="su_revoke_msg">נא לאשר שלילת הרשאות עבור %1$s?</string>
|
||||
<string name="toast">הרמת כוסית</string>
|
||||
<string name="toast">התראה</string>
|
||||
<string name="none">ללא</string>
|
||||
<string name="superuser_toggle_notification">התראות</string>
|
||||
<string name="superuser_toggle_revoke">הסרה</string>
|
||||
<string name="superuser_policy_none">טרם נתבקשו הרשאות משתמש על על ידי יישומים</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">הינך ללא יומן, יש לנסות להשתמש יותר ביישומי השורש שלך</string>
|
||||
<string name="log_data_magisk_none">יומני רישום Magisk ריקים, זה מוזר</string>
|
||||
<string name="menuSaveLog">שמירת יומן רישום</string>
|
||||
<string name="menuClearLog">ניקוי יומן רישום כעת</string>
|
||||
<string name="logs_cleared">יומני רישום נוקו בהצלחה</string>
|
||||
<string name="log_data_none">אין Log, יש לנסות להשתמש יותר ביישומי הRoot שלך</string>
|
||||
<string name="log_data_magisk_none">Log Magisk ריק, זה מוזר</string>
|
||||
<string name="menuSaveLog">שמירת Log</string>
|
||||
<string name="menuClearLog">ניקוי Log כעת</string>
|
||||
<string name="logs_cleared">Log נוקה בהצלחה</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">יעד UID: %1$d</string>
|
||||
<string name="target_pid">מציב ns יעד PID: %s</string>
|
||||
@@ -100,15 +100,15 @@
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(לא סופק מידע)</string>
|
||||
<string name="reboot_userspace">אתחול רך</string>
|
||||
<string name="reboot_recovery">אתחול למצב שחזור</string>
|
||||
<string name="reboot_bootloader">אתחול לטוען האתחול</string>
|
||||
<string name="reboot_recovery">אתחול למצב Recovery</string>
|
||||
<string name="reboot_bootloader">אתחול לBootloader</string>
|
||||
<string name="reboot_download">אתחול למצב הורדה</string>
|
||||
<string name="reboot_edl">אתחול למצב EDL</string>
|
||||
<string name="reboot_safe_mode">מצב בטוח</string>
|
||||
<string name="module_version_author">%1$s מאת %2$s</string>
|
||||
<string name="module_state_remove">הסרה</string>
|
||||
<string name="module_action">פעולה</string>
|
||||
<string name="module_state_restore">שחזור</string>
|
||||
<string name="module_state_restore">שיחזור</string>
|
||||
<string name="module_action_install_external">התקנה מהאחסון</string>
|
||||
<string name="update_available">עדכונים זמינים</string>
|
||||
<string name="suspend_text_riru">מודול מושעה כי %1$s מופעל</string>
|
||||
@@ -129,7 +129,7 @@
|
||||
<string name="settings_hide_app_title">הסתרת היישום Magisk</string>
|
||||
<string name="settings_hide_app_summary">התקנת יישום מתווך עם מזהה חבילה אקראי ותווית שם מותאמת אישית</string>
|
||||
<string name="settings_restore_app_title">שיחזור היישום Magisk</string>
|
||||
<string name="settings_restore_app_summary">ביטול הסתרת היישום ושחזור אל ה-APK המקורי</string>
|
||||
<string name="settings_restore_app_summary">ביטול הסתרת היישום ושיחזור אל ה-APK המקורי</string>
|
||||
<string name="language">שפה</string>
|
||||
<string name="system_default">(ברירת מחדל מערכת)</string>
|
||||
<string name="settings_check_update_title">בדיקת עדכונים</string>
|
||||
@@ -144,9 +144,9 @@
|
||||
<string name="settings_denylist_summary">כל השינויים של Magisk יוחזרו לתהליכים ברשימת הדחייה</string>
|
||||
<string name="settings_denylist_config_title">הגדרת רשימת הדחייה</string>
|
||||
<string name="settings_denylist_config_summary">בחירת התהליכים שייכללו ברשימת הדחייה</string>
|
||||
<string name="settings_hosts_title">מארחים חסרי מערכת</string>
|
||||
<string name="settings_hosts_summary">מארחים חסרי מערכת תומכים ביישומים חוסמי פרסומות</string>
|
||||
<string name="settings_hosts_toast">הוספת מודול מארחים חסרי מערכת</string>
|
||||
<string name="settings_hosts_title">hosts חסרי מערכת</string>
|
||||
<string name="settings_hosts_summary">hosts חסרי מערכת תומכים ביישומים חוסמי פרסומות</string>
|
||||
<string name="settings_hosts_toast">הוספת מודול hosts חסרי מערכת</string>
|
||||
<string name="settings_app_name_hint">שם חדש</string>
|
||||
<string name="settings_app_name_helper">היישום יארז מחדש בשם זה</string>
|
||||
<string name="settings_app_name_error">פורמט לא חוקי</string>
|
||||
@@ -181,16 +181,16 @@
|
||||
<string name="settings_owner_only">בעל ההתקן בלבד</string>
|
||||
<string name="settings_owner_manage">אחראי ניהול ההתקן</string>
|
||||
<string name="settings_user_independent">משתמש עצמאי</string>
|
||||
<string name="owner_only_summary">לבעלים בלבד ישנה גישת שורש</string>
|
||||
<string name="owner_manage_summary">הבעלים בלבד יכול לנהל גישת שורש ולקבל הנחיות לבקשה</string>
|
||||
<string name="user_independent_summary">לכל משתמש יש כללי שורש נפרדים משלו</string>
|
||||
<string name="owner_only_summary">לבעלים בלבד ישנה גישת Root</string>
|
||||
<string name="owner_manage_summary">הבעלים בלבד יכול לנהל גישת Root ולקבל הנחיות לבקשה</string>
|
||||
<string name="user_independent_summary">לכל משתמש יש כללי Root נפרדים משלו</string>
|
||||
<string name="mount_namespace_mode">מצב הצבת מרחב שם</string>
|
||||
<string name="settings_ns_global">מרחב שם גלובלי</string>
|
||||
<string name="settings_ns_requester">מרחב שם מורש</string>
|
||||
<string name="settings_ns_isolate">מרחב שם מבודד</string>
|
||||
<string name="global_summary">כלל חיבורי השורש משתמשים במרחב שם הגלובלי</string>
|
||||
<string name="requester_summary">חיבורי השורש יירשו את מרחב השם של המבקש</string>
|
||||
<string name="isolate_summary">לכל חיבור שורש יהיה מרחב שם מבודד</string>
|
||||
<string name="global_summary">כלל חיבורי הRoot משתמשים במרחב שם הגלובלי</string>
|
||||
<string name="requester_summary">חיבורי הRoot יירשו את מרחב השם של המבקש</string>
|
||||
<string name="isolate_summary">לכל חיבור Root יהיה מרחב שם מבודד</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">עדכוני Magisk</string>
|
||||
@@ -225,7 +225,7 @@
|
||||
<string name="setup_fail">ההתקנה כשלה</string>
|
||||
<string name="env_fix_title">דורש התקנה נוספת</string>
|
||||
<string name="env_fix_msg">ההתקן שלך זקוק להתקנה נוספת כדי ש-Magisk יפעל כראוי. האם ברצונך להמשיך ולהפעיל מחדש?</string>
|
||||
<string name="env_full_fix_msg">ההתקן שלך זקוק לצריבה מחדש של Magisk כדי לעבוד כראוי. נא להתקין מחדש את Magisk בתוך היישום, מצב השחזור אינו יכול לקבל מידע נכון על ההתקן.</string>
|
||||
<string name="env_full_fix_msg">ההתקן שלך זקוק לצריבה מחדש של Magisk כדי לעבוד כראוי. נא להתקין מחדש את Magisk בתוך היישום, מצב הRecovery אינו יכול לקבל מידע נכון על ההתקן.</string>
|
||||
<string name="setup_msg">הגדרת סביבת ריצה…</string>
|
||||
<string name="unsupport_magisk_title">גרסת Magisk אינה נתמכת</string>
|
||||
<string name="unsupport_magisk_msg">גרסה זו של היישום אינה תומכת ביישום Magisk הנמוך מ- %1$s.\n\nהיישום יתנהג כאילו Magisk אינו מותקן,יש לשדרג את Magisk במהירות האפשריות.</string>
|
||||
@@ -233,7 +233,7 @@
|
||||
<string name="unsupport_system_app_msg">הפעלת יישום זה כיישום מערכת אינה נתמך. נא להשיב את היישום כיישום משתמש.</string>
|
||||
<string name="unsupport_other_su_msg">התגלתה פקודת \"su\" שאינה שייכת ליישום Magisk נא להסיר את הפקודה שאינה נתמכת.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk מותקן באחסון החיצוני. נא להעביר את היישום לאחסון הפנימי.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">היישום אינו יכול להמשיך לעבוד במצב הנסתר מכיוון שגישת השורש אבדה. נא לשחזר אותו חזרה ל-APK המקורי.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">היישום אינו יכול להמשיך לעבוד במצב הנסתר מכיוון שגישת הRoot אבדה. נא לשחזר אותו חזרה ל-APK המקורי.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">הענת הרשאת אחסון להפעלת פונקציה זו</string>
|
||||
<string name="post_notifications_denied">הענקת הרשאה להתראות כדי להפעיל פונקציה זו</string>
|
||||
|
252
app/core/src/main/res/values-ku/strings.xml
Normal file
252
app/core/src/main/res/values-ku/strings.xml
Normal file
@@ -0,0 +1,252 @@
|
||||
<resources>
|
||||
|
||||
<!--Sections-->
|
||||
<string name="modules">زیادکراوەکان</string>
|
||||
<string name="superuser">سوپەر یوسەر</string>
|
||||
<string name="logs">تۆمارەکان</string>
|
||||
<string name="settings">ڕێکخستنەکان</string>
|
||||
<string name="install">دامەزراندن</string>
|
||||
<string name="section_home">ماڵەوە</string>
|
||||
<string name="section_theme">ڕووکارەکان</string>
|
||||
<string name="denylist">پێڕستی ڕێگەپێنەدراوەکان</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">هێڵ بەردەست نییە</string>
|
||||
<string name="app_changelog">گۆڕانکارییەکان</string>
|
||||
<string name="loading">کردنەوە…</string>
|
||||
<string name="update">بەرزکردنەوە</string>
|
||||
<string name="not_available">نییە</string>
|
||||
<string name="hide">شاردنەوە</string>
|
||||
<string name="home_package">پاکێج</string>
|
||||
<string name="home_app_title">ئەپ</string>
|
||||
|
||||
<string name="home_notice_content">تەنها لە گیتهەبی فەرمی ماجیسک دابگرە، لە شوێنی تر لەوانەیە زیانبەخش بێت</string>
|
||||
<string name="home_support_title">پشتگیریمان بکە</string>
|
||||
<string name="home_follow_title">شوێنمان بکەوە</string>
|
||||
<string name="home_item_source">سەرچاوە</string>
|
||||
<string name="home_support_content">ماجیسک بە خۆڕاییە و هەر واش ئەمێنێتەوە، بەهەرحاڵ ئەتوانیت پشتگیرییەکمان بکەی بۆ گرنگی پێدان</string>
|
||||
<string name="home_installed_version">داگیراوە</string>
|
||||
<string name="home_latest_version">دوایین وەشان</string>
|
||||
<string name="invalid_update_channel">کەناڵێکی نوێکردنەوەی هەڵە</string>
|
||||
<string name="uninstall_magisk_title">سڕینەوەی ماجیسک</string>
|
||||
<string name="uninstall_magisk_msg">هەموو زیادکراوەکان دەسڕێنەوە، ڕۆت دەسڕێتەوە، و هەر ڕەمزێنراوێک بەهۆی ماجیسک کرابێ لادەچێت!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">ڕەمزاندنی بەزۆر بهێڵەوە</string>
|
||||
<string name="keep_dm_verity">بهێڵەوە AVB 2.0/dm-verity</string>
|
||||
<string name="recovery_mode">دۆخی ڕیکەڤەڕی</string>
|
||||
<string name="install_options_title">هەڵبژاردنەکان</string>
|
||||
<string name="install_method_title">ڕێگای</string>
|
||||
<string name="install_next">دواتر</string>
|
||||
<string name="install_start">با بیکەین</string>
|
||||
<string name="manager_download_install">بۆ داگرتن و ڕێکخستن کرتە بکە</string>
|
||||
<string name="direct_install">داگرتنی ڕاستەوخۆ(پێشنیارکراوە)</string>
|
||||
<string name="install_inactive_slot">دایبگرە بۆ خانەی ناچالاک(پاش OTA)</string>
|
||||
<string name="install_inactive_slot_msg">ئێستا ئامێرەکەت دەچێتە خانە ناچالاکەکە، ئەمە بەکاربهێنە تەنها دوای ئەپدەیت کردن لە ڕێگەی OTA، بەردەوام دەبیت؟</string>
|
||||
<string name="setup_title">ڕێکخستنی زیاتر</string>
|
||||
<string name="select_patch_file">فایلێک هەڵبژێرە و پینەی بکە</string>
|
||||
<string name="patch_file_msg">تکایە فایلێکی Tar یان img یان payload.bin هەڵبژێرە</string>
|
||||
<string name="reboot_delay_toast">ڕێستارت کردنەوە لە ماوەی ٥ چرکە…</string>
|
||||
<string name="flash_screen_title">ڕێکخستن</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">داواکاری سوپەریوسەر</string>
|
||||
<string name="touch_filtered_warning">ئەپێک لە سەر شاشەکەیە، ناتوانین دڵنیا بینەوە</string>
|
||||
<string name="deny">ڕەتکردنەوە</string>
|
||||
<string name="prompt">داواکاری</string>
|
||||
<string name="grant">ڕێگەپێدان</string>
|
||||
<string name="su_warning">ڕێگەپێدان بۆ تەواوی ئامێرەکەت، گەر دڵنیا نیت ڕەتی بکەوە</string>
|
||||
<string name="forever">بۆ هەمیشە</string>
|
||||
<string name="once">بۆ یەکجار</string>
|
||||
<string name="tenmin">بۆ ١٠ خولەک</string>
|
||||
<string name="twentymin">بۆ ٢٠ خولەک</string>
|
||||
<string name="thirtymin">بۆ ٣٠ خولەک</string>
|
||||
<string name="sixtymin">بۆ ٦٠ خولەک</string>
|
||||
<string name="su_allow_toast">%1$s ڕێگەپێدانی سوپەریوسەری بۆ زیادکرا</string>
|
||||
<string name="su_deny_toast">%1$s ڕێگەپێدانی سوپەریوسەر ڕەتکرایەوە</string>
|
||||
<string name="su_snack_grant">ڕێگەپێدانی سوپەریوسەری %1$s بۆ درا</string>
|
||||
<string name="su_snack_deny">ڕێگەپێدانی سوپەریوسەر %1$s ڕەتکرایەوە</string>
|
||||
<string name="su_snack_notif_on">ئاگەدارکردنەوەکانی %1$s کارا کراوە</string>
|
||||
<string name="su_snack_notif_off">ئاگەدارکردنەوەکانی %1$s کوژاوەتەوە</string>
|
||||
<string name="su_snack_log_on">تۆمارەکانی %1$s کراوەتەوە</string>
|
||||
<string name="su_snack_log_off">تۆمارەکانی %1$s کوژاوەتەوە</string>
|
||||
<string name="su_revoke_title">لابردن؟</string>
|
||||
<string name="su_revoke_msg">دڵنیابەوە بۆ لابردنی سوپەریوسەر بۆ %1$s </string>
|
||||
<string name="toast">هێنانەسەر</string>
|
||||
<string name="none">هیچ</string>
|
||||
|
||||
<string name="superuser_toggle_notification">ئاگادارییەکان</string>
|
||||
<string name="superuser_toggle_revoke">لابردن</string>
|
||||
<string name="superuser_policy_none">هیچ ئەپێک تا ئێستا داوای سوپەریوسەری نەکردووە</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">هیچ تۆمارێک نییە، ئەو ئەپانەی ڕۆتیان پێویستە زوزو بەکاریبێنە</string>
|
||||
<string name="log_data_magisk_none">تۆمارەکانی ماجیسک بەتاڵن، باشە بۆ؟</string>
|
||||
<string name="menuSaveLog">تۆمارەکان هەڵبگرە</string>
|
||||
<string name="menuClearLog">تۆمارەکان بسڕەوە</string>
|
||||
<string name="logs_cleared">بەسەرکەوتویی تۆمارەکان سڕانەوە</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">ئامانج UID: %1$d</string>
|
||||
<string name="target_pid">Mount ns target PID: %s</string>
|
||||
<string name="selinux_context">SELinux context: %s</string>
|
||||
<string name="supp_group">Supplementary group: %s</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">پیشاندانی ئەپەکانی سیستەم</string>
|
||||
<string name="show_os_app">پیشاندانی ئەپەکان</string>
|
||||
<string name="hide_filter_hint">پاڵاوتنی بەپێی ناو</string>
|
||||
<string name="hide_search">گەڕان</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(هیچ زانیارییەک نییە)</string>
|
||||
<string name="reboot_userspace">ڕێستارت کردنەوە</string>
|
||||
<string name="reboot_recovery">چوونە ناو ڕیکەڤەری</string>
|
||||
<string name="reboot_bootloader">چوونە ناو بووتلۆدەر</string>
|
||||
<string name="reboot_download">چوونە ناو داونلۆد</string>
|
||||
<string name="reboot_edl">چوونە ناو EDL</string>
|
||||
<string name="reboot_safe_mode">دۆخی پارێزراو</string>
|
||||
<string name="module_version_author">%1$s by %2$s</string>
|
||||
<string name="module_state_remove">سڕینەوە</string>
|
||||
<string name="module_action">کارا</string>
|
||||
<string name="module_state_restore">گەڕاندنەوە</string>
|
||||
<string name="module_action_install_external">لە بیرگەکەتەوە ڕێکی بخە</string>
|
||||
<string name="update_available">وەشانی نوێ بەردەستە</string>
|
||||
<string name="suspend_text_riru">زیادکراوەکە کار ناکات چونکە %1$s کراوەتەوە</string>
|
||||
<string name="suspend_text_zygisk">زیادکراوەکە کارناکات چونکە %1$s نەکراوەتەوە</string>
|
||||
<string name="zygisk_module_unloaded">زیادکراوی Zygisk بەهۆی نەگونجان کارناکات</string>
|
||||
<string name="module_empty">هیچ زیادکراوێک دانەبەزیوە</string>
|
||||
<string name="confirm_install">دابەزاندنی زیادکراو %1$s?</string>
|
||||
<string name="confirm_install_title">دڵنیابوونەوە لە دابەزاندن</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">جۆری ڕووکار</string>
|
||||
<string name="settings_dark_mode_message">حەزت لە کامەی بوو ئەوە هەڵبژێرە</string>
|
||||
<string name="settings_dark_mode_light">هەمیشە دۆخی ڕوناک</string>
|
||||
<string name="settings_dark_mode_system">با بەگوێرەی سیستەمەکە بێت!</string>
|
||||
<string name="settings_dark_mode_dark">هەمیشە دۆخی تاریک</string>
|
||||
<string name="settings_download_path_title">شوێنی داگرتنەکە</string>
|
||||
<string name="settings_download_path_message">فایلەکان هەڵدەگیرێن لە %1$s</string>
|
||||
<string name="settings_hide_app_title">شاردنەوەی ئەپی ماجیسک</string>
|
||||
<string name="settings_hide_app_summary">داگرتنی ماجیسک بە ناوی جیاوە</string>
|
||||
<string name="settings_restore_app_title">ئەپە ڕەسەنەکە بهێنەوە</string>
|
||||
<string name="settings_restore_app_summary">ئەپەکە دەربخەوە و ڕەسەنڵ</string>
|
||||
<string name="language">زمان</string>
|
||||
<string name="system_default">(وەک هی ئامێرەکە)</string>
|
||||
<string name="settings_check_update_title">گەڕان بەدوای نوێکاری</string>
|
||||
<string name="settings_check_update_summary">گەڕان بەدوای نوێکاری خۆکارانە</string>
|
||||
<string name="settings_update_channel_title">کەناڵی نوێکاری</string>
|
||||
<string name="settings_update_stable">جێگیر</string>
|
||||
<string name="settings_update_beta">پێشوەختە(بێتا)</string>
|
||||
<string name="settings_update_custom">تایبەت</string>
|
||||
<string name="settings_update_custom_msg">بەستەرێکی تایبەت دابنێ</string>
|
||||
<string name="settings_zygisk_summary">کارپێکردنی بەشێکی ماجیسک لە zygote daemon</string>
|
||||
<string name="settings_denylist_title">پێڕستی نەرێنی کراوەکان</string>
|
||||
<string name="settings_denylist_summary">هەر ئەپێک لە پێڕستی نەرێنییەکان کاریگەریەکانی ماجیسکی لەسەر نییە</string>
|
||||
<string name="settings_denylist_config_title">دەستکاریکردنی پێڕستی نەرێنیکراوەکان</string>
|
||||
<string name="settings_denylist_config_summary">ئەو ئەپە هەڵبژێرە کە دەتەوێت نەرێنیی بکەیت</string>
|
||||
<string name="settings_hosts_title">هۆستی ناسیستەمی</string>
|
||||
<string name="settings_hosts_summary"> هۆستی ناسیستەمی بۆ لابردنی ڕیکلامەکان</string>
|
||||
<string name="settings_hosts_toast"> هۆستی ناسیستەمی زیادکرا</string>
|
||||
<string name="settings_app_name_hint">ناوی نوێ</string>
|
||||
<string name="settings_app_name_helper">ئەپەکە بەم ناوەوە دروست دەکرێتەوە</string>
|
||||
<string name="settings_app_name_error">هەڵەیە</string>
|
||||
<string name="settings_su_app_adb">ئەپەکان و ADB</string>
|
||||
<string name="settings_su_app">تەنها ئەپەکان</string>
|
||||
<string name="settings_su_adb">ADB تەنها</string>
|
||||
<string name="settings_su_disable">ناچالاک کراوە</string>
|
||||
<string name="settings_su_request_10">10 چرکە</string>
|
||||
<string name="settings_su_request_15">15 چرکە</string>
|
||||
<string name="settings_su_request_20">20 چرکە</string>
|
||||
<string name="settings_su_request_30">30 چرکە</string>
|
||||
<string name="settings_su_request_45">45 چرکە</string>
|
||||
<string name="settings_su_request_60">60 چرکە</string>
|
||||
<string name="superuser_access">دەسەڵاتی سوپەریوسەر</string>
|
||||
<string name="auto_response">وەڵامدانەوەی خۆکارانە</string>
|
||||
<string name="request_timeout">ماوەی وەڵامدانەوە</string>
|
||||
<string name="superuser_notification">ئاگەدارییەکانی سوپەریوسەر</string>
|
||||
<string name="settings_su_reauth_title">پرسیاربکەوە دوای هەر نوێکردنەوەیەک</string>
|
||||
<string name="settings_su_reauth_summary">دوای نوێکردنەوەی ئەپەکان دووبارە پرسیار بکەوە بۆ دەسەڵاتی سوپەریوسەر</string>
|
||||
<string name="settings_su_tapjack_title">پارێزگاری کردن لە ئەگەری دەستلێدانی تر</string>
|
||||
<string name="settings_su_tapjack_summary"> کاتێک ئەپێکی تر بەسەر شاشەکەوەیە، سوپەر یوسەر وەڵام ناداتەوە لە کردنی هەر بژاردەیەک لەبەر پارێزراوی</string>
|
||||
<string name="settings_su_auth_title">دڵنیاکردنەوەی کەسی</string>
|
||||
<string name="settings_su_auth_summary">داواکاری بکە بۆ دڵنیاکردنەوەی کەسی لەکاتی داواکاری سوپەریوسەر</string>
|
||||
<string name="settings_su_auth_insecure">هیچ دڵنیاکردنەوەیەک نییە</string>
|
||||
<string name="settings_customization">دەستکاریکردن</string>
|
||||
<string name="setting_add_shortcut_summary">یەک ئایکۆنی جوان زیادبکە بۆ سەر شاشەکە ئەگەر قورس بوو ئەوەی خۆی بدۆزیتەوە</string>
|
||||
<string name="settings_doh_title">DNS بەسەر HTTPS</string>
|
||||
<string name="settings_doh_description">Workaround DNS خراپە لە هەندێک شوێن</string>
|
||||
<string name="settings_random_name_title">ناوێک لەخۆیەوە</string>
|
||||
<string name="settings_random_name_description">دانانی ناوێک لەخۆوە تاوەکوو ئاشکرا نەبێت</string>
|
||||
|
||||
<string name="multiuser_mode">دۆخی فرەبەکارهێنەر</string>
|
||||
<string name="settings_owner_only">تەنها خاوەنی ئامێر</string>
|
||||
<string name="settings_owner_manage">خاوەنی ئامێر</string>
|
||||
<string name="settings_user_independent">بەکارهێنەری سەربەخۆ</string>
|
||||
<string name="owner_only_summary">تەنها خاوەنەکە دۆخی ڕۆتی هەیە</string>
|
||||
<string name="owner_manage_summary">تەنها خاوەنەکە دەسەڵاتی بەکارهێنانی ڕۆتی هەیە</string>
|
||||
<string name="user_independent_summary">هەر بەکارهێنەرێک یاسای جیاوازی هەیە</string>
|
||||
|
||||
<string name="mount_namespace_mode">چونە دۆخی بۆشایی ناو</string>
|
||||
<string name="settings_ns_global">بۆشاییناوی گشتی</string>
|
||||
<string name="settings_ns_requester">بۆشایی ناوی خۆیی</string>
|
||||
<string name="settings_ns_isolate">بۆشایی ناوی جیا</string>
|
||||
<string name="global_summary">هەمو ڕۆتەکان ناوی گشتی بەکار ئەهێنن</string>
|
||||
<string name="requester_summary">هەمو ڕۆتەکان ناوی خۆیی بەکار ئەهێنن</string>
|
||||
<string name="isolate_summary">هەمو ڕۆتەکان ناوی جیا بەکار ئەهێنن</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">نوێکردنەوەکانی ماجیسک</string>
|
||||
<string name="progress_channel">ئاگادارییە کاراکان</string>
|
||||
<string name="updated_channel">نوێکردنەوە سەرکەوتووبوو</string>
|
||||
<string name="download_complete">داگرتن سەرکەوتووبوو</string>
|
||||
<string name="download_file_error">هەڵەیەک رووی دا لەکاتی داگرتنی فایلەکە</string>
|
||||
<string name="magisk_update_title">وەشانی نوێی ماجیسک ئامادەیە!</string>
|
||||
<string name="updated_title">ماجیسک نوێکراوە!</string>
|
||||
<string name="updated_text">کرتە بکە بۆ کردنەوەی ئەپ</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">بەڵێ</string>
|
||||
<string name="no">نەخێر</string>
|
||||
<string name="repo_install_title">داگرتن %1$s %2$s(%3$d)</string>
|
||||
<string name="download">داگرتن</string>
|
||||
<string name="reboot">ڕێستارت</string>
|
||||
<string name="close">داخستن</string>
|
||||
<string name="release_notes">نێبینییەکان</string>
|
||||
<string name="flashing">فلاش کردن</string>
|
||||
<string name="running">کار کردن...</string>
|
||||
<string name="done">تەواو!</string>
|
||||
<string name="done_action">کارکردنی %1$s تەواو بوو</string>
|
||||
<string name="failure">Failed!</string>
|
||||
<string name="hide_app_title">شاردنەوەی ئەپی ماجیسک…</string>
|
||||
<string name="open_link_failed_toast">هیچ ئەپێک تییە تا لینکەکەی پێ بکرێتەوە</string>
|
||||
<string name="complete_uninstall">سڕینەوەی تەواوی</string>
|
||||
<string name="restore_img">هێنانەوەی img</string>
|
||||
<string name="restore_img_msg">هێنانەوە…</string>
|
||||
<string name="restore_done">هاتەوە!</string>
|
||||
<string name="restore_fail">هیچ فایلێکی هەڵگیراوت نیە!</string>
|
||||
<string name="setup_fail">ڕێکخستن شکستی هێنا</string>
|
||||
<string name="env_fix_title">پێویستی بە ڕێکخستنی زیاترە</string>
|
||||
<string name="env_fix_msg">مۆبایلەکەت پێویستی بە ڕێکخستنی زیاترە، ئایا ئەتەوێت بەردەوام بیت و ڕێستارتی بکەیتەوە؟</string>
|
||||
<string name="env_full_fix_msg"> پێویستە دوبارە ماجیسک دابگریتەوە بۆ ئەوەی بەباشی کاربکات تکایە ماجیسک دابگرەوە لەناو ئەپەکە خۆی چونکە لە ڕیکەڤەرییەوە ناتوانرێ زانیاری تەواو لەسەر ئامێرەکە دەستبخرێت </string>
|
||||
<string name="setup_msg">دەستپێکردن....</string>
|
||||
<string name="unsupport_magisk_title">وەشانی ماجیسک پاڵپشتینەکراوە</string>
|
||||
<string name="unsupport_magisk_msg">وەشانی ماجیسکەکەت زۆر کۆنە وەک ئەوە وایە هەر نەبێت، تکایە نوێی بکەوە بە زوترین کات</string>
|
||||
<string name="unsupport_general_title">باری نائاسایی</string>
|
||||
<string name="unsupport_system_app_msg">ئەم ئەپە وەکو ئەپی سیستەم کارناکات، تکایە بیگۆڕەوە بۆ ئەپی ئاسایی</string>
|
||||
<string name="unsupport_other_su_msg"> \"su\" binary یەکی بێگانە دۆزرایەوە، تکایە جگە لە ماجیسک ئەپی تر بەکارمەهێنە بۆ ڕۆت کردن </string>
|
||||
<string name="unsupport_external_storage_msg">ماجیسک لە بیرگەی دەرەکی داگیراوە، تکایە بیبەوە بۆ ناوەکی</string>
|
||||
<string name="unsupport_nonroot_stub_msg">ئەپە شاراوەکە کار ناکات چونکە ڕۆتەکە نەماوە، تکایە ئەپە ڕەسەنەکە بگەڕێنەوە</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">ڕەزامەندی بیرگە بدە تاوەکوو ئەمە کار بکات</string>
|
||||
<string name="post_notifications_denied">ڕەزامەندی ئاگاداری بکە تاوەکوو ئەمە کار بکات</string>
|
||||
<string name="install_unknown_denied">ڕەزامەندی "install unknown apps" تاوەکوو کار بکات</string>
|
||||
<string name="add_shortcut_title">زیادی بکە بۆ سەر شاشە</string>
|
||||
<string name="add_shortcut_msg">دوای شاردنەوەی ئەم ئەپە، ئەتەوێت یەک ئایکۆنی جوان زیادبکەیت بۆ سەر شاشەکە ئەگەر قورس بوو ئەوەی خۆی بدۆزیتەوە؟</string>
|
||||
<string name="app_not_found">هیچ ئەپێک نەدۆزرایەوە تاوەکوو ئەم کارەی پێ بکرێت</string>
|
||||
<string name="reboot_apply_change">ڕێستارت بکە تاوەکوو کاریگەریەکان کار بکەن</string>
|
||||
<string name="restore_app_confirmation">ئەمە ئەپە ڕەسەنەکە ئەهێنێتەوە، دڵنیایت لە کردنی؟</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -53,8 +53,9 @@
|
||||
<string name="touch_filtered_warning">Como um app está ocultando uma solicitação de SuperUsuário, o Magisk não pode verificar sua resposta.</string>
|
||||
<string name="deny">Negar</string>
|
||||
<string name="prompt">Perguntar</string>
|
||||
<string name="restrict">Restringir</string>
|
||||
<string name="grant">Permitir</string>
|
||||
<string name="su_warning">Permite acesso total ao seu aparelho.\nNão permita se você não tiver certeza do que está fazendo!</string>
|
||||
<string name="su_warning">Permite acesso total ao seu dispositivo.\nNão permita se você não tiver certeza do que está fazendo!</string>
|
||||
<string name="forever">Sempre</string>
|
||||
<string name="once">Uma vez</string>
|
||||
<string name="tenmin">10 mins</string>
|
||||
@@ -63,19 +64,19 @@
|
||||
<string name="sixtymin">60 mins</string>
|
||||
<string name="su_allow_toast">%1$s foi permitido o acesso de SuperUsuário</string>
|
||||
<string name="su_deny_toast">%1$s foi negado o acesso de SuperUsuário</string>
|
||||
<string name="su_snack_grant">Os acessos de SuperUsuário de %1$s foram concedidos</string>
|
||||
<string name="su_snack_grant">Os acessos de SuperUsuário de %1$s foram permitidos</string>
|
||||
<string name="su_snack_deny">Os acessos de SuperUsuário de %1$s foram negados</string>
|
||||
<string name="su_snack_notif_on">As notificações de %1$s foram ativadas</string>
|
||||
<string name="su_snack_notif_off">As notificações de %1$s foram desativadas</string>
|
||||
<string name="su_snack_log_on">Os registros de %1$s foram ativados</string>
|
||||
<string name="su_snack_log_off">Os registros de %1$s foram desativados</string>
|
||||
<string name="su_revoke_title">Revogar?</string>
|
||||
<string name="su_revoke_msg">Confirmar a remoção do acesso de SuperUsuário de %1$s?</string>
|
||||
<string name="su_revoke_msg">Confirme para revogar os acessos de SuperUsuário de %1$s</string>
|
||||
<string name="toast">Notificação (Pop-up)</string>
|
||||
<string name="none">Nenhum</string>
|
||||
<string name="superuser_toggle_notification">Notificações</string>
|
||||
<string name="superuser_toggle_revoke">Revogar</string>
|
||||
<string name="superuser_policy_none">Nenhum app solicitou permissão de SuperUsuário ainda</string>
|
||||
<string name="superuser_policy_none">Nenhum app solicitou permissão de SuperUsuário ainda.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Não há registros. Tente usar mais seus apps root.</string>
|
||||
@@ -135,6 +136,7 @@
|
||||
<string name="settings_update_channel_title">Canal de atualização</string>
|
||||
<string name="settings_update_stable">Estável</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_debug">Debug</string>
|
||||
<string name="settings_update_custom">Personalizado</string>
|
||||
<string name="settings_update_custom_msg">Insira um URL de canal personalizado</string>
|
||||
<string name="settings_zygisk_summary">Executa partes do Magisk no Zygote</string>
|
||||
@@ -169,6 +171,8 @@
|
||||
<string name="settings_su_auth_title">Autenticação de usuário</string>
|
||||
<string name="settings_su_auth_summary">Solicite autenticação de usuário durante solicitações de SuperUsuário</string>
|
||||
<string name="settings_su_auth_insecure">Nenhum método de autenticação está configurado no dispositivo</string>
|
||||
<string name="settings_su_restrict_title">Restringir recursos root</string>
|
||||
<string name="settings_su_restrict_summary">Restringirá novos apps de SuperUsuário por padrão. Aviso: isso quebrará a maioria dos apps. Não ative se você não souber o que está fazendo.</string>
|
||||
<string name="settings_customization">Personalizações</string>
|
||||
<string name="setting_add_shortcut_summary">Adicione um atalho na tela inicial, caso o nome e o ícone sejam difíceis de reconhecer logo após ocultar o app.</string>
|
||||
<string name="settings_doh_title">DNS sobre HTTPS</string>
|
||||
@@ -235,7 +239,7 @@
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Conceda permissão de armazenamento para ativar esta funcionalidade</string>
|
||||
<string name="post_notifications_denied">Conceda permissão às notificações para ativar esta funcionalidade</string>
|
||||
<string name="install_unknown_denied">Permita a opção "Instalar apps de fontes desconhecidas" para ativar esta funcionalidade</string>
|
||||
<string name="install_unknown_denied">Permita a opção \"Instalar apps de fontes desconhecidas\" para ativar esta funcionalidade</string>
|
||||
<string name="add_shortcut_title">Adicionar atalho à tela inicial</string>
|
||||
<string name="add_shortcut_msg">Após ocultar o app do Magisk, seu nome e ícone ficarão difíceis de reconhecer. Deseja adicionar um atalho na tela inicial?</string>
|
||||
<string name="app_not_found">Nenhum app encontrado para realizar esta ação</string>
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<!--Sections-->
|
||||
<string name="modules">Módulos</string>
|
||||
<string name="superuser">SuperUsuário</string>
|
||||
<string name="logs">Registros</string>
|
||||
<string name="logs">Registos</string>
|
||||
<string name="settings">Configurações</string>
|
||||
<string name="install">Instalar</string>
|
||||
<string name="section_home">Início</string>
|
||||
@@ -11,50 +11,51 @@
|
||||
<string name="denylist">Lista de negação</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">Nenhuma conexão disponível</string>
|
||||
<string name="app_changelog">Registro de alterações</string>
|
||||
<string name="loading">Carregando…</string>
|
||||
<string name="no_connection">Nenhuma ligação disponível</string>
|
||||
<string name="app_changelog">Registo de alterações</string>
|
||||
<string name="loading">A carregar…</string>
|
||||
<string name="update">Atualizar</string>
|
||||
<string name="not_available">Não disponível</string>
|
||||
<string name="hide">Ocultar</string>
|
||||
<string name="home_package">Pacote</string>
|
||||
<string name="home_app_title">App</string>
|
||||
<string name="home_notice_content">Baixe o Magisk SOMENTE pela página oficial do GitHub. Arquivos de fontes desconhecidas podem ser maliciosos!</string>
|
||||
<string name="home_notice_content">Descarregue o Magisk APENAS na página oficial do GitHub. Os ficheiros de fontes desconhecidas podem ser maliciosos!</string>
|
||||
<string name="home_support_title">Apoie-nos</string>
|
||||
<string name="home_follow_title">Siga-nos</string>
|
||||
<string name="home_item_source">Fonte</string>
|
||||
<string name="home_support_content">Magisk sempre foi e sempre será, gratuito e de código aberto. No entanto, você pode nos ajudar enviando uma pequena doação.</string>
|
||||
<string name="home_support_content">Magisk sempre foi e sempre será, gratuito e de código aberto. No entanto, você pode ajudar-nos enviando uma pequena doação.</string>
|
||||
<string name="home_installed_version">Instalado</string>
|
||||
<string name="home_latest_version">Recente</string>
|
||||
<string name="invalid_update_channel">Canal de atualização inválido</string>
|
||||
<string name="uninstall_magisk_title">Desinstalar Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Todos os módulos serão desativados/removidos!\nO root será removido!\nSeus dados não criptografados devido ao uso do Magisk, serão re-criptografados!</string>
|
||||
<string name="uninstall_magisk_msg">Todos os módulos serão desativados/removidos!\nO root será removido!\nOs seus dados não encriptados devido à utilização do Magisk, serão re-encriptados!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Manter criptografia forçada</string>
|
||||
<string name="keep_force_encryption">Manter encriptação forçada</string>
|
||||
<string name="keep_dm_verity">Manter AVB 2.0/dm-verity</string>
|
||||
<string name="recovery_mode">Modo Recovery</string>
|
||||
<string name="install_options_title">Opções</string>
|
||||
<string name="install_method_title">Método</string>
|
||||
<string name="install_next">Próximo</string>
|
||||
<string name="install_start">Vamos lá</string>
|
||||
<string name="manager_download_install">Toque para baixar e instalar</string>
|
||||
<string name="manager_download_install">Toque para descarregar e instalar</string>
|
||||
<string name="direct_install">Instalação direta (recomendada)</string>
|
||||
<string name="install_inactive_slot">Instalar no slot inativo (após o OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Seu dispositivo será FORÇADO a inicializar no slot inativo atual após uma reinicialização!\nSó use esta opção após a conclusão do OTA.\nDeseja continuar?</string>
|
||||
<string name="install_inactive_slot_msg">O seu dispositivo será FORÇADO a inicializar no slot inativo atual após um reinício!\nSó use esta opção após a conclusão do OTA.\nDeseja continuar?</string>
|
||||
<string name="setup_title">Configuração adicional</string>
|
||||
<string name="select_patch_file">Selecione e corrija um arquivo</string>
|
||||
<string name="patch_file_msg">Selecione um arquivo imagem (*.img) ou um arquivo tar (*.tar) ou um arquivo payload.bin (*.bin)</string>
|
||||
<string name="reboot_delay_toast">Reiniciando em 5 segundos…</string>
|
||||
<string name="select_patch_file">Selecione e corrija um ficheiro</string>
|
||||
<string name="patch_file_msg">Selecione um ficheiro de imagem (*.img) ou um ficheiro tar (*.tar) ou um ficheiro payload.bin (*.bin)</string>
|
||||
<string name="reboot_delay_toast">A reiniciar em 5 segundos…</string>
|
||||
<string name="flash_screen_title">Instalação</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Solicitação de SuperUsuário</string>
|
||||
<string name="touch_filtered_warning">Como um app está ocultando uma solicitação de SuperUsuário, o Magisk não pode verificar sua resposta.</string>
|
||||
<string name="touch_filtered_warning">Como um app está a ocultar um pedido de SuperUsuário, o Magisk não consegue verificar a sua resposta.</string>
|
||||
<string name="deny">Negar</string>
|
||||
<string name="prompt">Perguntar</string>
|
||||
<string name="restrict">Restringir</string>
|
||||
<string name="grant">Permitir</string>
|
||||
<string name="su_warning">Permite acesso total ao seu aparelho.\nNão permita se você não tiver certeza do que está fazendo!</string>
|
||||
<string name="su_warning">Permite o acesso total ao seu dispositivo.\nNão o permita se não tiver a certeza do que está a fazer!</string>
|
||||
<string name="forever">Sempre</string>
|
||||
<string name="once">Uma vez</string>
|
||||
<string name="tenmin">10 mins</string>
|
||||
@@ -63,26 +64,26 @@
|
||||
<string name="sixtymin">60 mins</string>
|
||||
<string name="su_allow_toast">%1$s foi permitido o acesso de SuperUsuário</string>
|
||||
<string name="su_deny_toast">%1$s foi negado o acesso de SuperUsuário</string>
|
||||
<string name="su_snack_grant">Os acessos de SuperUsuário de %1$s foram concedidos</string>
|
||||
<string name="su_snack_grant">Os acessos de SuperUsuário de %1$s foram permitidos</string>
|
||||
<string name="su_snack_deny">Os acessos de SuperUsuário de %1$s foram negados</string>
|
||||
<string name="su_snack_notif_on">As notificações de %1$s foram ativadas</string>
|
||||
<string name="su_snack_notif_off">As notificações de %1$s foram desativadas</string>
|
||||
<string name="su_snack_log_on">Os registros de %1$s foram ativados</string>
|
||||
<string name="su_snack_log_off">Os registros de %1$s foram desativados</string>
|
||||
<string name="su_snack_log_on">Os registos de %1$s foram ativados</string>
|
||||
<string name="su_snack_log_off">Os registos de %1$s foram desativados</string>
|
||||
<string name="su_revoke_title">Revogar?</string>
|
||||
<string name="su_revoke_msg">Confirmar a remoção do acesso de SuperUsuário de %1$s?</string>
|
||||
<string name="su_revoke_msg">Confirme para revogar os acessos de SuperUsuário de %1$s</string>
|
||||
<string name="toast">Notificação (Pop-up)</string>
|
||||
<string name="none">Nenhum</string>
|
||||
<string name="superuser_toggle_notification">Notificações</string>
|
||||
<string name="superuser_toggle_revoke">Revogar</string>
|
||||
<string name="superuser_policy_none">Nenhum app solicitou permissão de SuperUsuário ainda</string>
|
||||
<string name="superuser_policy_none">Nenhum app solicitou permissão de SuperUsuário ainda.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Não há registros. Tente usar mais seus apps root.</string>
|
||||
<string name="log_data_magisk_none">Os registros do Magisk estão vazios, isso é estranho.</string>
|
||||
<string name="menuSaveLog">Salvar registros</string>
|
||||
<string name="menuClearLog">Limpar registros agora</string>
|
||||
<string name="logs_cleared">Registros limpo com sucesso</string>
|
||||
<string name="log_data_none">Não há registos. Tente utilizar mais seus apps root.</string>
|
||||
<string name="log_data_magisk_none">Os registos do Magisk estão vazios, isto é estranho.</string>
|
||||
<string name="menuSaveLog">Salvar registos</string>
|
||||
<string name="menuClearLog">Limpar registos agora</string>
|
||||
<string name="logs_cleared">Registos limpo com sucesso</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Alvo UID: %1$d</string>
|
||||
<string name="target_pid">Alvo PID: %s</string>
|
||||
@@ -118,25 +119,26 @@
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Modo do tema</string>
|
||||
<string name="settings_dark_mode_message">Selecione o modo mais adequado para você!</string>
|
||||
<string name="settings_dark_mode_message">Selecione o modo mais adequado para si!</string>
|
||||
<string name="settings_dark_mode_light">Sempre claro</string>
|
||||
<string name="settings_dark_mode_system">Seguir sistema</string>
|
||||
<string name="settings_dark_mode_dark">Sempre escuro</string>
|
||||
<string name="settings_download_path_title">Caminho para baixar</string>
|
||||
<string name="settings_download_path_message">Os arquivos serão salvos em %1$s</string>
|
||||
<string name="settings_download_path_title">Caminho para descarregar</string>
|
||||
<string name="settings_download_path_message">Os ficheiros serão salvos em %1$s</string>
|
||||
<string name="settings_hide_app_title">Ocultar app do Magisk</string>
|
||||
<string name="settings_hide_app_summary">Instala o app oculto com ID aleatório e nome personalizado</string>
|
||||
<string name="settings_restore_app_title">Restaurar app do Magisk</string>
|
||||
<string name="settings_restore_app_summary">Desoculta o app do Magisk e restaura o APK original</string>
|
||||
<string name="language">Idioma</string>
|
||||
<string name="system_default">(Padrão do sistema)</string>
|
||||
<string name="system_default">(Predefinição do sistema)</string>
|
||||
<string name="settings_check_update_title">Verificar por atualizações</string>
|
||||
<string name="settings_check_update_summary">Verifique automaticamente se há atualizações ao abrir o app</string>
|
||||
<string name="settings_update_channel_title">Canal de atualização</string>
|
||||
<string name="settings_update_stable">Estável</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_debug">Debug</string>
|
||||
<string name="settings_update_custom">Personalizado</string>
|
||||
<string name="settings_update_custom_msg">Insira um URL de canal personalizado</string>
|
||||
<string name="settings_update_custom_msg">Introduza um URL de canal personalizado</string>
|
||||
<string name="settings_zygisk_summary">Executa partes do Magisk no Zygote</string>
|
||||
<string name="settings_denylist_title">Aplicar lista de negação</string>
|
||||
<string name="settings_denylist_summary">Os processos na lista de negação terão todas as modificações do Magisk revertidas</string>
|
||||
@@ -167,35 +169,37 @@
|
||||
<string name="settings_su_tapjack_title">Proteção contra atividades sobrepostas</string>
|
||||
<string name="settings_su_tapjack_summary">A caixa de diálogo do SuperUsuário não responderá à entrada enquanto estiver oculta por qualquer outra janela ou sobreposição</string>
|
||||
<string name="settings_su_auth_title">Autenticação de usuário</string>
|
||||
<string name="settings_su_auth_summary">Solicite autenticação de usuário durante solicitações de SuperUsuário</string>
|
||||
<string name="settings_su_auth_summary">Solicite autenticação de usuário durante pedidos de SuperUsuário</string>
|
||||
<string name="settings_su_auth_insecure">Nenhum método de autenticação está configurado no dispositivo</string>
|
||||
<string name="settings_su_restrict_title">Restringir recursos root</string>
|
||||
<string name="settings_su_restrict_summary">Restringirá novos apps de SuperUsuário por predefinição. Aviso: isto quebrará a maioria dos apps. Não ative se você não souber o que está a fazer.</string>
|
||||
<string name="settings_customization">Personalizações</string>
|
||||
<string name="setting_add_shortcut_summary">Adicione um atalho na tela inicial, caso o nome e o ícone sejam difíceis de reconhecer logo após ocultar o app.</string>
|
||||
<string name="setting_add_shortcut_summary">Adicione um atalho no ecrã inicial, caso o nome e o ícone sejam difíceis de reconhecer logo após ocultar o app.</string>
|
||||
<string name="settings_doh_title">DNS sobre HTTPS</string>
|
||||
<string name="settings_doh_description">Solução alternativa para envenenamento de DNS em alguns países</string>
|
||||
<string name="settings_random_name_title">Randomizar nome de saída</string>
|
||||
<string name="settings_random_name_description">Randomize o nome do arquivo de saída de imagens corrigidas e arquivos tar (*.tar) para evitar a detecção</string>
|
||||
<string name="settings_random_name_description">Randomize o nome do ficheiro de saída de imagens corrigidas e ficheiros tar (*.tar) para evitar a deteção</string>
|
||||
<string name="multiuser_mode">Modo multiusuário</string>
|
||||
<string name="settings_owner_only">Somente proprietário do dispositivo</string>
|
||||
<string name="settings_owner_manage">Gerenciado pelo proprietário do dispositivo</string>
|
||||
<string name="settings_owner_manage">Gerido pelo proprietário do dispositivo</string>
|
||||
<string name="settings_user_independent">Independente do usuário</string>
|
||||
<string name="owner_only_summary">Somente o proprietário tem acesso root</string>
|
||||
<string name="owner_manage_summary">Somente o proprietário pode gerenciar o acesso root e receber pedidos de solicitação</string>
|
||||
<string name="owner_manage_summary">Somente o proprietário pode gerir o acesso root e receber pedidos de solicitação</string>
|
||||
<string name="user_independent_summary">Cada usuário tem suas próprias regras de root separadas</string>
|
||||
<string name="mount_namespace_mode">Montar namespace</string>
|
||||
<string name="settings_ns_global">Global</string>
|
||||
<string name="settings_ns_requester">Herdado</string>
|
||||
<string name="settings_ns_isolate">Individual</string>
|
||||
<string name="global_summary">Todas as sessões root usam o namespace de montagem global</string>
|
||||
<string name="requester_summary">As sessões root herdarão o namespace do solicitante</string>
|
||||
<string name="isolate_summary">Cada sessão root terá seu próprio namespace individual</string>
|
||||
<string name="global_summary">Todas as sessões root utilizam o namespace de montagem global</string>
|
||||
<string name="requester_summary">As sessões root herdarão o namespace do requerente</string>
|
||||
<string name="isolate_summary">Cada sessão root terá o seu próprio namespace individual</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Atualizações do Magisk</string>
|
||||
<string name="progress_channel">Notificações de progresso</string>
|
||||
<string name="updated_channel">Atualização concluída</string>
|
||||
<string name="download_complete">Download concluído</string>
|
||||
<string name="download_file_error">Erro ao baixar arquivo</string>
|
||||
<string name="download_file_error">Erro ao descarregar ficheiro</string>
|
||||
<string name="magisk_update_title">Atualização do Magisk disponível!</string>
|
||||
<string name="updated_title">Magisk atualizado</string>
|
||||
<string name="updated_text">Toque para abrir o app</string>
|
||||
@@ -209,37 +213,37 @@
|
||||
<string name="close">Fechar</string>
|
||||
<string name="release_notes">Notas da atualização</string>
|
||||
<string name="flashing">Flashando…</string>
|
||||
<string name="running">Executando…</string>
|
||||
<string name="running">A executar…</string>
|
||||
<string name="done">Concluído!</string>
|
||||
<string name="done_action">Ação de execução de %1$s concluída</string>
|
||||
<string name="failure">Falhou!</string>
|
||||
<string name="hide_app_title">Ocultando o app do Magisk…</string>
|
||||
<string name="hide_app_title">A ocultar o app do Magisk…</string>
|
||||
<string name="open_link_failed_toast">Nenhum app encontrado para abrir o link</string>
|
||||
<string name="complete_uninstall">Desinstalação completa</string>
|
||||
<string name="restore_img">Restaurar imagens</string>
|
||||
<string name="restore_img_msg">Restaurando…</string>
|
||||
<string name="restore_img_msg">A restaurar…</string>
|
||||
<string name="restore_done">Restauração concluída!</string>
|
||||
<string name="restore_fail">O backup original não existe!</string>
|
||||
<string name="setup_fail">Falha na instalação</string>
|
||||
<string name="env_fix_title">Configuração adicional exigida</string>
|
||||
<string name="env_fix_msg">Seu dispositivo exige uma configuração adicional para o Magisk funcionar corretamente. Deseja continuar e reiniciar?</string>
|
||||
<string name="env_full_fix_msg">Seu dispositivo precisa refazer o flash do Magisk para funcionar corretamente. Por favor, reinstale o Magisk no app, o modo Recovery não pode obter as devidas informações do dispositivo.</string>
|
||||
<string name="setup_msg">Executando a configuração do ambiente…</string>
|
||||
<string name="env_full_fix_msg">O seu dispositivo precisa de refazer o flash do Magisk para funcionar corretamente. Por favor, reinstale o Magisk no app, o modo Recovery não consegue obter as devidas informações do dispositivo.</string>
|
||||
<string name="setup_msg">A executar a configuração do ambiente…</string>
|
||||
<string name="unsupport_magisk_title">Versão do Magisk não suportada</string>
|
||||
<string name="unsupport_magisk_msg">Esta versão do app não suporta a versão do Magisk inferior a %1$s.\n\nO app irá se comportar como se nenhum Magisk estivesse sido instalado. Por favor, atualize o Magisk assim que possível.</string>
|
||||
<string name="unsupport_magisk_msg">Esta versão do app não suporta versões do Magisk inferiores a %1$s.\n\nO app irá comportar-se como se nenhum Magisk estivesse instalado. Por favor, atualize o Magisk assim que possível.</string>
|
||||
<string name="unsupport_general_title">Estado anormal</string>
|
||||
<string name="unsupport_system_app_msg">Não há suporte para executar este app como um app do sistema. Por favor, reverta o app para um app de usuário.</string>
|
||||
<string name="unsupport_other_su_msg">Não foi possível detectar o binário \"su\" do Magisk. Por favor, remova qualquer outro root concorrente e/ou reinstale o Magisk.</string>
|
||||
<string name="unsupport_external_storage_msg">O app do Magisk está instalado no armazenamento externo. Por favor, mova o app para o armazenamento interno.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">O app oculto do Magisk não pode continuar funcionando porque o root foi perdido. Por favor, restaure o APK original.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">O app oculto do Magisk não pode continuar a funcionar porque o root foi perdido. Por favor, restaure o APK original.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Conceda permissão de armazenamento para ativar esta funcionalidade</string>
|
||||
<string name="post_notifications_denied">Conceda permissão às notificações para ativar esta funcionalidade</string>
|
||||
<string name="install_unknown_denied">Permita a opção "Instalar apps de fontes desconhecidas" para ativar esta funcionalidade</string>
|
||||
<string name="add_shortcut_title">Adicionar atalho à tela inicial</string>
|
||||
<string name="add_shortcut_msg">Após ocultar o app do Magisk, seu nome e ícone ficarão difíceis de reconhecer. Deseja adicionar um atalho na tela inicial?</string>
|
||||
<string name="install_unknown_denied">Permita a opção \"Instalar apps de fontes desconhecidas\" para ativar esta funcionalidade</string>
|
||||
<string name="add_shortcut_title">Adicionar atalho ao ecrã inicial</string>
|
||||
<string name="add_shortcut_msg">Após ocultar o app do Magisk, o seu nome e ícone serão difíceis de reconhecer. Deseja adicionar um atalho no ecrã inicial?</string>
|
||||
<string name="app_not_found">Nenhum app encontrado para realizar esta ação</string>
|
||||
<string name="reboot_apply_change">Reinicie para aplicar as mudanças</string>
|
||||
<string name="reboot_apply_change">Reinicie para aplicar as alterações</string>
|
||||
<string name="restore_app_confirmation">Isso irá restaurar o app oculto do Magisk de volta para o app original. Deseja realmente fazer isso?</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -1,99 +1,58 @@
|
||||
<resources>
|
||||
<!--Universal-->
|
||||
<!--Author: Radoš Milićev (https://github.com/rammba)-->
|
||||
|
||||
<!--Welcome Activity-->
|
||||
<!--Sections-->
|
||||
<string name="modules">Модули</string>
|
||||
<string name="superuser">Супер-корисник</string>
|
||||
<string name="logs">Дневник</string>
|
||||
<string name="logs">Логови</string>
|
||||
<string name="settings">Подешавања</string>
|
||||
<string name="install">Инсталација</string>
|
||||
<string name="section_home">Почетно</string>
|
||||
<string name="section_theme">Теме</string>
|
||||
<string name="denylist">Листа забрана</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="invalid_update_channel">Непостојећи Канал Ажурирања</string>
|
||||
<!--Home-->
|
||||
<string name="no_connection">Недоступна конекција</string>
|
||||
<string name="app_changelog">Промене у апликацији</string>
|
||||
<string name="loading">Учитавање…</string>
|
||||
<string name="update">Ажурирање</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Сакриј</string>
|
||||
<string name="home_package">Пакет</string>
|
||||
<string name="home_app_title">Апл.</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="home_notice_content">Преузмите Magisk САМО са званичне GitHub странице. Фајлови из непознатих извора могу бити малициозни!</string>
|
||||
<string name="home_support_title">Подржите нас</string>
|
||||
<string name="home_follow_title">Запратите нас</string>
|
||||
<string name="home_item_source">Извор</string>
|
||||
<string name="home_support_content">Magisk јесте и увек ће бити бесплатан и open source. Можете показати да вам је стало својом донацијом.</string>
|
||||
<string name="home_installed_version">Инсталирано</string>
|
||||
<string name="home_latest_version">Најновије</string>
|
||||
<string name="invalid_update_channel">Невалидан канал ажурирања</string>
|
||||
<string name="uninstall_magisk_title">Деинсталирај Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Сви модули ће бити онемогућени/уклоњени!\nКорен ће бити уклоњен!\nСвако неенкриптовано интерно складиште ће употребом Magisk-а бити поново енкриптовано!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Задржи форсирану енкрипцију</string>
|
||||
<string name="keep_dm_verity">Задржи AVB 2.0/dm-verity</string>
|
||||
<string name="uninstall_magisk_title">Унинсталирај Магиск</string>
|
||||
<string name="uninstall_magisk_msg">Сви модули ће бити онеспособљени/уклоњени. Корен ће бити уклоњен, и потенцијално енкриптовати твоје податке уколико већ нису енкриптовани</string>
|
||||
<string name="update">Ажурирање</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Без информација)</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Ажурирање доступно</string>
|
||||
<string name="home_installed_version">Инсталирано</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveLog">Сачувај дневник</string>
|
||||
<string name="menuClearLog">Обриши дневник</string>
|
||||
<string name="logs_cleared">Дневник успешно креиран</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<string name="app_changelog">Дневник промена апликације</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="repo_install_title">Инсталирај %1$s %2$s(%3$d)</string>
|
||||
<string name="download">Преузми</string>
|
||||
<string name="reboot">Рестартуј</string>
|
||||
<string name="magisk_update_title">Нови Адбејт Магиска Доступан!</string>
|
||||
<string name="release_notes">Белешке обљављивања</string>
|
||||
<string name="recovery_mode">Режим опоравка</string>
|
||||
<string name="install_options_title">Опције</string>
|
||||
<string name="install_method_title">Метод</string>
|
||||
<string name="install_next">Наредно</string>
|
||||
<string name="install_start">Почнимо</string>
|
||||
<string name="manager_download_install">Притисни да преузмеш и инсталираш</string>
|
||||
<string name="update_channel">Магиск Ажурирање</string>
|
||||
<string name="flashing">Флешовање</string>
|
||||
<string name="direct_install">Директна Инсталација (Препоручено)</string>
|
||||
<string name="complete_uninstall">Комплетна Унинсталација</string>
|
||||
<string name="restore_done">Повратак успешан!</string>
|
||||
<string name="restore_fail">Фабрички бекап не постоји!</string>
|
||||
<string name="download_file_error">Грешка при преузимању фајла</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="language">Језик</string>
|
||||
<string name="system_default">(Фабрички Система)</string>
|
||||
<string name="settings_update_channel_title">Канал Ажурирања</string>
|
||||
<string name="settings_update_stable">Стабилан</string>
|
||||
<string name="settings_update_beta">Бета</string>
|
||||
<string name="settings_update_custom">По наруџби</string>
|
||||
<string name="settings_update_custom_msg">Унеси наруџбени УРЛ</string>
|
||||
<string name="settings_hosts_title">Без-системски домаћини (hosts)</string>
|
||||
<string name="settings_hosts_summary">Подршка без-системских домаћина за апликације за блокирање реклама</string>
|
||||
|
||||
<string name="settings_su_app_adb">Апликације и АДБ</string>
|
||||
<string name="settings_su_app">Само Апликације</string>
|
||||
<string name="settings_su_adb">Само АДБ</string>
|
||||
<string name="settings_su_disable">Онемогућено</string>
|
||||
<string name="settings_su_request_10">10 секунди</string>
|
||||
<string name="settings_su_request_15">15 секунди</string>
|
||||
<string name="settings_su_request_20">20 секунди</string>
|
||||
<string name="settings_su_request_30">30 секунди</string>
|
||||
<string name="settings_su_request_45">45 секунди</string>
|
||||
<string name="settings_su_request_60">60 секунди</string>
|
||||
<string name="superuser_access">Приступ Супер-кориснику</string>
|
||||
<string name="auto_response">Аутоматски одговор</string>
|
||||
<string name="request_timeout">Истек захтева</string>
|
||||
<string name="superuser_notification">Нотификације Супер-корисника</string>
|
||||
<string name="settings_su_reauth_title">Поново одобри после ажурирања</string>
|
||||
<string name="settings_su_reauth_summary">Поново одобри дозволу Супер-корисника после ажурирања апликације</string>
|
||||
|
||||
<string name="multiuser_mode">Више-кориснички режим</string>
|
||||
<string name="settings_owner_only">Власник уређаја само</string>
|
||||
<string name="settings_owner_manage">Одређено од стране власника</string>
|
||||
<string name="settings_user_independent">Независно од корисника</string>
|
||||
<string name="owner_only_summary">Само власник има приступ корену</string>
|
||||
<string name="owner_manage_summary">Само власник може да приступа корену и да прими захтеве за њега</string>
|
||||
<string name="user_independent_summary">Сваки корисник има своја правила корена</string>
|
||||
|
||||
<string name="mount_namespace_mode">Постоље режима имена простора</string>
|
||||
<string name="settings_ns_global">Глобално име простора</string>
|
||||
<string name="settings_ns_requester">Наслеђено име простора</string>
|
||||
<string name="settings_ns_isolate">Ограђено име простора</string>
|
||||
<string name="global_summary">Све коренске сесије користе глобално име простора</string>
|
||||
<string name="requester_summary">Коренске сесије ће наследити свој простор именовања</string>
|
||||
<string name="isolate_summary">Свака коренска сесија ће имати свој изоловани простор именовања</string>
|
||||
<string name="direct_install">Директна инсталација (Препоручено)</string>
|
||||
<string name="install_inactive_slot">Инсталација на неактиван слот (Након OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Ваш уређај ће бити ФОРСИРАН да се покрене на тренутно неактивном слоту након поновног покретања!\nКористите опцију само кад се OTA заврши.\nНастави?</string>
|
||||
<string name="setup_title">Додатне поставке</string>
|
||||
<string name="select_patch_file">Изаберите фајл</string>
|
||||
<string name="patch_file_msg">Изаберите слику (*.img) или ODIN tarfile (*.tar) или payload.bin (*.bin)</string>
|
||||
<string name="reboot_delay_toast">Поново покретање за 5 секунди…</string>
|
||||
<string name="flash_screen_title">Инсталација</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Супер-кориснички захтев</string>
|
||||
<string name="touch_filtered_warning">Magisk не може да верификује ваш одговор, јер апликација прикрива супер-кориснички захтев</string>
|
||||
<string name="deny">Забрани</string>
|
||||
<string name="prompt">Захтев</string>
|
||||
<string name="grant">Дозволи</string>
|
||||
@@ -104,20 +63,190 @@
|
||||
<string name="twentymin">20 мин</string>
|
||||
<string name="thirtymin">30 мин</string>
|
||||
<string name="sixtymin">60 мин</string>
|
||||
<string name="su_allow_toast">%1$s је добио права на Супер-корисника</string>
|
||||
<string name="su_deny_toast">%1$s није добио права на Супер-корисника</string>
|
||||
<string name="su_allow_toast">%1$s је добио права на супер-корисника</string>
|
||||
<string name="su_deny_toast">%1$s није добио права на супер-корисника</string>
|
||||
<string name="su_snack_grant">Супер-корисничка права од %1$s су пружена</string>
|
||||
<string name="su_snack_deny">Супер-корисничка права од %1$s су одбијена</string>
|
||||
<string name="su_snack_notif_on">Нотификације од %1$s су омогућене</string>
|
||||
<string name="su_snack_notif_off">Нотификације од %1$s су онемогућене</string>
|
||||
<string name="su_snack_log_on">Записивање у дневник за %1$s је омогућено</string>
|
||||
<string name="su_snack_log_off">Записивање у дневник за %1$s је онемогућено</string>
|
||||
<string name="su_snack_log_on">Логовање за %1$s је омогућено</string>
|
||||
<string name="su_snack_log_off">Логовање за %1$s је онемогућено</string>
|
||||
<string name="su_revoke_title">Опозови?</string>
|
||||
<string name="su_revoke_msg">Потврди да опозовеш права од %1$s?</string>
|
||||
<string name="toast">Тост</string>
|
||||
<string name="none">Ниједан</string>
|
||||
<string name="su_revoke_msg">Потврди да опозовеш права на супер-корисника од %1$s?</string>
|
||||
<string name="toast">Toast</string>
|
||||
<string name="none">Ништа</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="target_uid">Циљани УИД: %1$d</string>
|
||||
<string name="superuser_toggle_notification">Нотификације</string>
|
||||
<string name="superuser_toggle_revoke">Опозови</string>
|
||||
<string name="superuser_policy_none">Ниједна апликација није тражила пермисије за супер-корисника још увек.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Немате логова, покушајте користити коренске апликације више</string>
|
||||
<string name="log_data_magisk_none">Magisk логови су празни, то је чудно</string>
|
||||
<string name="menuSaveLog">Сачувај лог</string>
|
||||
<string name="menuClearLog">Уклони лог</string>
|
||||
<string name="logs_cleared">Лог успешно уклоњен</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Циљани UID: %1$d</string>
|
||||
<string name="target_pid">Mount ns цињани PID: %s</string>
|
||||
<string name="selinux_context">SELinux контекст: %s</string>
|
||||
<string name="supp_group">Допунска група: %s</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Прикажи системске апл.</string>
|
||||
<string name="show_os_app">Прикажи апл. ОС-а</string>
|
||||
<string name="hide_filter_hint">Филтрирај по имену</string>
|
||||
<string name="hide_search">Претрага</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Без информација)</string>
|
||||
<string name="reboot_userspace">Лако поново покретање</string>
|
||||
<string name="reboot_recovery">Поново покрени за опоравак</string>
|
||||
<string name="reboot_bootloader">Поново покрени за bootloader</string>
|
||||
<string name="reboot_download">Поново покрени за преузимање</string>
|
||||
<string name="reboot_edl">Поново покрени за EDL</string>
|
||||
<string name="reboot_safe_mode">Сигуран мод</string>
|
||||
<string name="module_version_author">%1$s од %2$s</string>
|
||||
<string name="module_state_remove">Уклони</string>
|
||||
<string name="module_action">Акција</string>
|
||||
<string name="module_state_restore">Поврати</string>
|
||||
<string name="module_action_install_external">Инсталирај из складишта</string>
|
||||
<string name="update_available">Ажурирање доступно</string>
|
||||
<string name="suspend_text_riru">Модул је суспендован јер је %1$s омогућено</string>
|
||||
<string name="suspend_text_zygisk">Модул је суспендован јер %1$s није омогућено</string>
|
||||
<string name="zygisk_module_unloaded">Zygisk модул није учитан због некомпатибилности</string>
|
||||
<string name="module_empty">Ниједан модул није инсталиран</string>
|
||||
<string name="confirm_install">Инсталирај модул %1$s?</string>
|
||||
<string name="confirm_install_title">Потврда инсталације</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Тема</string>
|
||||
<string name="settings_dark_mode_message">Изаберите тему која вам највише одговара!</string>
|
||||
<string name="settings_dark_mode_light">Увек светло</string>
|
||||
<string name="settings_dark_mode_system">Прати систем</string>
|
||||
<string name="settings_dark_mode_dark">Увек тамно</string>
|
||||
<string name="settings_download_path_title">Путања за преузимање</string>
|
||||
<string name="settings_download_path_message">Фајлови ће бити сачувани на %1$s</string>
|
||||
<string name="settings_hide_app_title">Сакриј Magisk апл.</string>
|
||||
<string name="settings_hide_app_summary">Инсталирај proxy апликацију са насумичним ID-јем пакета и прилагођеном лабелом</string>
|
||||
<string name="settings_restore_app_title">Поврати Magisk апл.</string>
|
||||
<string name="settings_restore_app_summary">Откриј апл. и поврати оригинални APK</string>
|
||||
<string name="language">Језик</string>
|
||||
<string name="system_default">(Подразумевано системски)</string>
|
||||
<string name="settings_check_update_title">Провери ажурирања</string>
|
||||
<string name="settings_check_update_summary">Периодично провери ажурирања у позадини</string>
|
||||
<string name="settings_update_channel_title">Канал ажурирања</string>
|
||||
<string name="settings_update_stable">Стабилно</string>
|
||||
<string name="settings_update_beta">Бета</string>
|
||||
<string name="settings_update_custom">Прилагођено</string>
|
||||
<string name="settings_update_custom_msg">Унеси прилагођени URL канала</string>
|
||||
<string name="settings_zygisk_summary">Покрени делове Magisk-а у zygote daemon-у</string>
|
||||
<string name="settings_denylist_title">Спроведи листу забрана</string>
|
||||
<string name="settings_denylist_summary">Процеси на листи забрана ће повратити све Magisk измене</string>
|
||||
<string name="settings_denylist_config_title">Конфигуриши листу забрана</string>
|
||||
<string name="settings_denylist_config_summary">Изабери процесе који ће бити на листи забрана</string>
|
||||
<string name="settings_hosts_title">Безсистемски домаћини (hosts)</string>
|
||||
<string name="settings_hosts_summary">Подршка безсистемских домаћина за апликације блокирања реклама</string>
|
||||
<string name="settings_hosts_toast">Модул безсистемских домаћина додат</string>
|
||||
<string name="settings_app_name_hint">Ново име</string>
|
||||
<string name="settings_app_name_helper">Апл. ће бити спакована под овим именом</string>
|
||||
<string name="settings_app_name_error">Невалидан формат</string>
|
||||
<string name="settings_su_app_adb">Апликације и ADB</string>
|
||||
<string name="settings_su_app">Само апликације</string>
|
||||
<string name="settings_su_adb">Само ADB</string>
|
||||
<string name="settings_su_disable">Онемогућено</string>
|
||||
<string name="settings_su_request_10">10 секунди</string>
|
||||
<string name="settings_su_request_15">15 секунди</string>
|
||||
<string name="settings_su_request_20">20 секунди</string>
|
||||
<string name="settings_su_request_30">30 секунди</string>
|
||||
<string name="settings_su_request_45">45 секунди</string>
|
||||
<string name="settings_su_request_60">60 секунди</string>
|
||||
<string name="superuser_access">Приступ супер-корисника</string>
|
||||
<string name="auto_response">Аутоматски одговор</string>
|
||||
<string name="request_timeout">Истек захтева</string>
|
||||
<string name="superuser_notification">Нотификације супер-корисника</string>
|
||||
<string name="settings_su_reauth_title">Поново одобри након ажурирања</string>
|
||||
<string name="settings_su_reauth_summary">Поново тражи пермисије супер-корисника након ажурирања апликација</string>
|
||||
<string name="settings_su_tapjack_title">Заштита од tapjacking-а</string>
|
||||
<string name="settings_su_tapjack_summary">Prompt дијалог супер-корисника неће реаговати док је прикривен другим прозором или overlay-ем</string>
|
||||
<string name="settings_su_auth_title">Аутентификација корисника</string>
|
||||
<string name="settings_su_auth_summary">Тражи аутентификацију корисника током захтева супер-корисника</string>
|
||||
<string name="settings_su_auth_insecure">Ниједан метод аутентификације није подешен на уређају</string>
|
||||
<string name="settings_customization">Прилагођавање</string>
|
||||
<string name="setting_add_shortcut_summary">Додај лепу пречицу на почетни екран у случају да се име и иконица не препознају лако након скривања апликације</string>
|
||||
<string name="settings_doh_title">DNS преко HTTPS-а</string>
|
||||
<string name="settings_doh_description">Заобилазно решење DNS тровања у неким нацијама</string>
|
||||
<string name="settings_random_name_title">Насумично име на излазу</string>
|
||||
<string name="settings_random_name_description">Насумично име излазног фајла слика и tar фајлова ради спречавања детекције</string>
|
||||
|
||||
<string name="multiuser_mode">Вишекориснички режим</string>
|
||||
<string name="settings_owner_only">Само власник уређаја</string>
|
||||
<string name="settings_owner_manage">Одређено од стране власника</string>
|
||||
<string name="settings_user_independent">Независно од корисника</string>
|
||||
<string name="owner_only_summary">Само власник има приступ корену</string>
|
||||
<string name="owner_manage_summary">Само власник може да приступа корену и да прима захтеве за њега</string>
|
||||
<string name="user_independent_summary">Сваки корисник има своја правила корена</string>
|
||||
|
||||
<string name="mount_namespace_mode">Mount режим namespace-а</string>
|
||||
<string name="settings_ns_global">Глобални namespace</string>
|
||||
<string name="settings_ns_requester">Наслеђени namespace</string>
|
||||
<string name="settings_ns_isolate">Изоловани namespace</string>
|
||||
<string name="global_summary">Све коренске сесије користе глобални mount namespace</string>
|
||||
<string name="requester_summary">Коренске сесије ће наследити namespace од подносиоца захтева</string>
|
||||
<string name="isolate_summary">Свака коренска сесија ће имати свој изоловани namespace</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Ажурирања Magisk-а</string>
|
||||
<string name="progress_channel">Нотификације о прогресу</string>
|
||||
<string name="updated_channel">Ажурирање завршено</string>
|
||||
<string name="download_complete">Преузимање завршено</string>
|
||||
<string name="download_file_error">Грешка при преузимању фајла</string>
|
||||
<string name="magisk_update_title">Ажурирање Magisk-а доступно!</string>
|
||||
<string name="updated_title">Magisk је ажуриран</string>
|
||||
<string name="updated_text">Кликни да отвориш апликацију</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Да</string>
|
||||
<string name="no">Не</string>
|
||||
<string name="repo_install_title">Инсталирај %1$s %2$s(%3$d)</string>
|
||||
<string name="download">Преузми</string>
|
||||
<string name="reboot">Поново покрени</string>
|
||||
<string name="close">Затвори</string>
|
||||
<string name="release_notes">Release notes</string>
|
||||
<string name="flashing">Флешовање…</string>
|
||||
<string name="running">Покретање…</string>
|
||||
<string name="done">Завршено!</string>
|
||||
<string name="done_action">Покретање акције %1$s завршено</string>
|
||||
<string name="failure">Неуспешно!</string>
|
||||
<string name="hide_app_title">Скривање Magisk апликације…</string>
|
||||
<string name="open_link_failed_toast">Није пронађена апликација за отварање линка</string>
|
||||
<string name="complete_uninstall">Комплетна деинсталација</string>
|
||||
<string name="restore_img">Поврати слике</string>
|
||||
<string name="restore_img_msg">Повратак…</string>
|
||||
<string name="restore_done">Повратак успешан!</string>
|
||||
<string name="restore_fail">Фабрички бекап не постоји!</string>
|
||||
<string name="setup_fail">Неуспешна поставка</string>
|
||||
<string name="env_fix_title">Потребно додатно подешавање</string>
|
||||
<string name="env_fix_msg">Ваш уређај захтева додатно подешавање да би Magisk радио како треба. Да ли желите наставити и покренути поново?</string>
|
||||
<string name="env_full_fix_msg">Ваш уређај захтева поновно флешовање да би Magisk радио како треба. Реинсталирајте Magisk кроз апликацију, режим опоравка не може добити тачне информације о уређају.</string>
|
||||
<string name="setup_msg">Покретање подешавања окружења…</string>
|
||||
<string name="unsupport_magisk_title">Неподржана верзија Magisk-а</string>
|
||||
<string name="unsupport_magisk_msg">Ова верзија апликације не подржава Magisk верзије мање од %1$s.\n\nАпликација ће се понашати као да Magisk није инсталиран, молимо ажурирајте Magisk што пре.</string>
|
||||
<string name="unsupport_general_title">Ненормално стање</string>
|
||||
<string name="unsupport_system_app_msg">Покретање апликације као системске није подржано. Молимо поставите апликацију да буде корисничка.</string>
|
||||
<string name="unsupport_other_su_msg">Детектован \"su\" binary који није Magisk-ов. Молимо уклоните конкурентно коренско решење и/или реинсталирајте Magisk.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk је инсталиран на екстерно складиште. Молимо померите апл. у интерно складиште.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Скривена Magisk апликација не може наставити са радом јер је корен изгубљен. Молимо повратите оригинални APK.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Дозволите пермисију за складиште да бисте омогућили ову функционалност</string>
|
||||
<string name="post_notifications_denied">Дозволите пермисију за нотификације да бисте омогућили ову функционалност</string>
|
||||
<string name="install_unknown_denied">Дозволите "инсталирање непознатих апликација" да бисте омогућили ову функционалност</string>
|
||||
<string name="add_shortcut_title">Додај пречицу на почетни екран</string>
|
||||
<string name="add_shortcut_msg">Након скривања апликације, њено име и иконицу ћете тешко препознати. Желите ли додати лепу пречицу на почетни екран?</string>
|
||||
<string name="app_not_found">Није пронађена апликација за ову акцију</string>
|
||||
<string name="reboot_apply_change">Поново покрени да примениш измене</string>
|
||||
<string name="restore_app_confirmation">Ово ће вратити скривену апликацију на оригиналну. Да ли стварно то желите?</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -5,116 +5,114 @@
|
||||
<string name="superuser">Süper Kullanıcı</string>
|
||||
<string name="logs">Günlükler</string>
|
||||
<string name="settings">Ayarlar</string>
|
||||
<string name="install">Yükle</string>
|
||||
<string name="install">Kur</string>
|
||||
<string name="section_home">Ana Sayfa</string>
|
||||
<string name="section_theme">Temalar</string>
|
||||
<string name="denylist">Reddetme Listesi</string>
|
||||
<string name="denylist">Red Listesi</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">Bağlantı yok</string>
|
||||
<string name="no_connection">Bağlantı Yok</string>
|
||||
<string name="app_changelog">Değişiklik Günlüğü</string>
|
||||
<string name="loading">Yükleniyor…</string>
|
||||
<string name="update">Güncelle</string>
|
||||
<string name="not_available">Yok</string>
|
||||
<string name="not_available">Mevcut Değil</string>
|
||||
<string name="hide">Gizle</string>
|
||||
<string name="home_package">Paket</string>
|
||||
<string name="home_app_title">Uygulama</string>
|
||||
|
||||
<string name="home_notice_content">Magisk\'i YALNIZCA resmi GitHub sayfasından indirin. Bilinmeyen kaynaklardan gelen dosyalar zararlı olabilir!</string>
|
||||
<string name="home_support_title">Bizi Destekleyin</string>
|
||||
<string name="home_notice_content">Magisk\'i YALNIZCA resmi GitHub sayfasından indirin. Bilinmeyen kaynaklardan gelen dosyalar kötü amaçlı olabilir!</string>
|
||||
<string name="home_support_title">Bize Destek Olun</string>
|
||||
<string name="home_follow_title">Bizi Takip Edin</string>
|
||||
<string name="home_item_source">Kaynak</string>
|
||||
<string name="home_support_content">Magisk her zaman ücretsiz ve açık kaynak olacaktır. Ancak, bir bağış yaparak bize destek olabilirsiniz.</string>
|
||||
<string name="home_installed_version">Yüklü Sürüm</string>
|
||||
<string name="home_latest_version">En Son Sürüm</string>
|
||||
<string name="invalid_update_channel">Geçersiz Güncelleme Kanalı</string>
|
||||
<string name="home_support_content">Magisk ücretsizdir, açık kaynaklıdır ve her zaman öyle kalacaktır. Ancak, bağış yaparak bize değer verdiğinizi gösterebilirsiniz.</string>
|
||||
<string name="home_installed_version">Yüklü</string>
|
||||
<string name="home_latest_version">En Son</string>
|
||||
<string name="invalid_update_channel">Geçersiz güncelleme kanalı</string>
|
||||
<string name="uninstall_magisk_title">Magisk\'i Kaldır</string>
|
||||
<string name="uninstall_magisk_msg">Tüm modüller devre dışı bırakılacak/kaldırılacak!\nKök kaldırılacak!\nMagisk kullanılarak şifrelenmemiş herhangi bir dahili depolama yeniden şifrelenecek!</string>
|
||||
<string name="uninstall_magisk_msg">Tüm modüller devre dışı bırakılacak/kaldırılacak!\nRoot kaldırılacak!\nMagisk kullanılarak şifresi çözülen dahili depolama birimleri yeniden şifrelenecek!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Zorla şifrelemeyi koru</string>
|
||||
<string name="keep_dm_verity">AVB 2.0/dm-verity\'yi koru</string>
|
||||
<string name="keep_force_encryption">Zorunlu Şifrelemeyi Koru</string>
|
||||
<string name="keep_dm_verity">AVB 2.0/dm-verity Koru</string>
|
||||
<string name="recovery_mode">Kurtarma Modu</string>
|
||||
<string name="install_options_title">Seçenekler</string>
|
||||
<string name="install_method_title">Yöntem</string>
|
||||
<string name="install_next">Sonraki</string>
|
||||
<string name="install_start">Hadi başlayalım</string>
|
||||
<string name="manager_download_install">İndirmek ve yüklemek için basın</string>
|
||||
<string name="direct_install">Doğrudan Yükleme (Önerilir)</string>
|
||||
<string name="install_inactive_slot">Etkin Olmayan Slot\'a Yükle (OTA Sonrası)</string>
|
||||
<string name="install_inactive_slot_msg">Cihazınız yeniden başlatıldıktan sonra zorunlu olarak mevcut etkin olmayan slota önyükleme yapılacaktır!\nBu seçeneği yalnızca OTA tamamlandıktan sonra kullanın.\nDevam etmek istiyor musunuz?</string>
|
||||
<string name="install_start">Hadi Başlayalım</string>
|
||||
<string name="manager_download_install">İndirmek ve kurmak için basın</string>
|
||||
<string name="direct_install">Doğrudan kurulum (Önerilen)</string>
|
||||
<string name="install_inactive_slot">Etkin olmayan slota kur (OTA sonrası)</string>
|
||||
<string name="install_inactive_slot_msg">Cihazınız, yeniden başlattıktan sonra mevcut etkin olmayan slota önyükleme yapmaya ZORLANACAKTIR!\nBu seçeneği yalnızca OTA güncellemesi yapıldıktan sonra kullanın.\nDevam edilsin mi?</string>
|
||||
<string name="setup_title">Ek Kurulum</string>
|
||||
<string name="select_patch_file">Bir Dosya Seç ve Yama Yap</string>
|
||||
<string name="patch_file_msg">Ham bir görüntü (*.img) veya bir ODIN tar dosyası (*.tar) veya bir payload.bin (*.bin) seçin</string>
|
||||
<string name="select_patch_file">Bir dosya seçin ve yamalayın</string>
|
||||
<string name="patch_file_msg">Ham bir imaj (*.img), bir ODIN tar dosyası (*.tar) veya bir payload.bin (*.bin) seçin</string>
|
||||
<string name="reboot_delay_toast">5 saniye içinde yeniden başlatılıyor…</string>
|
||||
<string name="flash_screen_title">Yükleniyor</string>
|
||||
<string name="flash_screen_title">Kuruluyor</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Süper Kullanıcı İsteği</string>
|
||||
<string name="touch_filtered_warning">Bir uygulama bir Süper Kullanıcı isteğini engellediği için Magisk yanıtınızı doğrulayamıyor</string>
|
||||
<string name="touch_filtered_warning">Bir uygulama Süper Kullanıcı isteğini engellediği için Magisk yanıtınızı doğrulayamıyor.</string>
|
||||
<string name="deny">Reddet</string>
|
||||
<string name="prompt">İstem</string>
|
||||
<string name="prompt">Sor</string>
|
||||
<string name="restrict">Kısıtla</string>
|
||||
<string name="grant">İzin Ver</string>
|
||||
<string name="su_warning">Cihazınıza tam erişim sağlar.\nEmin değilseniz reddedin!</string>
|
||||
<string name="forever">Daima</string>
|
||||
<string name="once">Bir kez</string>
|
||||
<string name="tenmin">10 dakika</string>
|
||||
<string name="twentymin">20 dakika</string>
|
||||
<string name="thirtymin">30 dakika</string>
|
||||
<string name="sixtymin">60 dakika</string>
|
||||
<string name="su_allow_toast">%1$s uygulamasının süper kullanıcı hakları verildi</string>
|
||||
<string name="su_deny_toast">%1$s uygulamasının süper kullanıcı hakları reddedildi</string>
|
||||
<string name="su_snack_grant">%1$s uygulamasının süper kullanıcı hakları verildi</string>
|
||||
<string name="su_snack_deny">%1$s uygulamasının süper kullanıcı hakları reddedildi</string>
|
||||
<string name="su_snack_notif_on">%1$s uygulamasının bildirimleri etkinleştirildi</string>
|
||||
<string name="su_snack_notif_off">%1$s uygulamasının bildirimleri devre dışı bırakıldı</string>
|
||||
<string name="su_snack_log_on">%1$s uygulamasının günlüğü etkinleştirildi</string>
|
||||
<string name="su_snack_log_off">%1$s uygulamasının günlüğü devre dışı bırakıldı</string>
|
||||
<string name="su_revoke_title">İptal Et?</string>
|
||||
<string name="su_revoke_msg">%1$s uygulamasının süper kullanıcı haklarını iptal etmek istediğinize emin misiniz?</string>
|
||||
<string name="toast">Bildirim</string>
|
||||
<string name="forever">Her Zaman</string>
|
||||
<string name="once">Bir Kez</string>
|
||||
<string name="tenmin">10 Dakika</string>
|
||||
<string name="twentymin">20 Dakika</string>
|
||||
<string name="thirtymin">30 Dakika</string>
|
||||
<string name="sixtymin">60 Dakika</string>
|
||||
<string name="su_allow_toast">%1$s uygulamasına Süper Kullanıcı hakları verildi</string>
|
||||
<string name="su_deny_toast">%1$s uygulamasının Süper Kullanıcı hakları reddedildi</string>
|
||||
<string name="su_snack_grant">%1$s uygulamasının Süper Kullanıcı hakları verildi</string>
|
||||
<string name="su_snack_deny">%1$s uygulamasının Süper Kullanıcı hakları reddedildi</string>
|
||||
<string name="su_snack_notif_on">%1$s bildirimleri etkinleştirildi</string>
|
||||
<string name="su_snack_notif_off">%1$s bildirimleri devre dışı bırakıldı</string>
|
||||
<string name="su_snack_log_on">%1$s için günlük kaydı etkinleştirildi</string>
|
||||
<string name="su_snack_log_off">%1$s için günlük kaydı devre dışı bırakıldı</string>
|
||||
<string name="su_revoke_title">İptal Edilsin mi?</string>
|
||||
<string name="su_revoke_msg">%1$s uygulamasının Süper Kullanıcı haklarını iptal etmeyi onaylayın</string>
|
||||
<string name="toast">Bildirim Penceresi</string>
|
||||
<string name="none">Yok</string>
|
||||
|
||||
<string name="superuser_toggle_notification">Bildirimler</string>
|
||||
<string name="superuser_toggle_revoke">İptal Et</string>
|
||||
<string name="superuser_policy_none">Henüz hiçbir uygulama Süper Kullanıcı izni istemedi.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Günlük kullanmıyorsunuz, root (kök) uygulamalarınızı daha çok kullanmayı deneyin</string>
|
||||
<string name="log_data_magisk_none">Magisk günlükleri boş, bu tuhaf</string>
|
||||
<string name="menuSaveLog">Günlüğü kaydet</string>
|
||||
<string name="menuClearLog">Günlüğü şimdi temizle</string>
|
||||
<string name="logs_cleared">Günlük kaydı başarıyla temizlendi</string>
|
||||
<string name="log_data_none">Günlüğünüz temiz. Root uygulamalarınızı daha fazla kullanmayı deneyin.</string>
|
||||
<string name="log_data_magisk_none">Magisk günlükleri boş, bu garip.</string>
|
||||
<string name="menuSaveLog">Günlüğü Kaydet</string>
|
||||
<string name="menuClearLog">Günlüğü Şimdi Temizle</string>
|
||||
<string name="logs_cleared">Günlük başarıyla temizlendi</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Hedef UID: %1$d</string>
|
||||
<string name="target_pid">Mount ns hedef PID: %s</string>
|
||||
<string name="target_pid">Hedef PID: %s</string>
|
||||
<string name="selinux_context">SELinux bağlamı: %s</string>
|
||||
<string name="supp_group">Ek grup: %s</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Sistem uygulamalarını göster</string>
|
||||
<string name="show_os_app">İşletim sistemi uygulamalarını göster</string>
|
||||
<string name="hide_filter_hint">İsme göre filtrele</string>
|
||||
<string name="show_system_app">Sistem Uygulamalarını Göster</string>
|
||||
<string name="show_os_app">İS Uygulamalarını Göster</string>
|
||||
<string name="hide_filter_hint">Ada göre filtrele</string>
|
||||
<string name="hide_search">Ara</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Bilgi verilmedi)</string>
|
||||
<string name="reboot_userspace">Hızlı yeniden başlat</string>
|
||||
<string name="reboot_recovery">Kurtarma modunda yeniden başlat</string>
|
||||
<string name="reboot_bootloader">Önyükleyici modunda yeniden başlat</string>
|
||||
<string name="reboot_download">İndirme modunda yeniden başlat</string>
|
||||
<string name="reboot_edl">EDL modunda yeniden başlat</string>
|
||||
<string name="reboot_safe_mode">Güvenli mod</string>
|
||||
<string name="module_version_author">%1$s / %2$s</string>
|
||||
<string name="no_info_provided">(Bilgi sağlanmadı)</string>
|
||||
<string name="reboot_userspace">Hızlı Yeniden Başlat</string>
|
||||
<string name="reboot_recovery">Kurtarma Modunda Yeniden Başlat</string>
|
||||
<string name="reboot_bootloader">Önyükleyici Modunda Yeniden Başlat</string>
|
||||
<string name="reboot_download">Download Modunda Yeniden Başlat</string>
|
||||
<string name="reboot_edl">EDL Modunda Yeniden Başlat</string>
|
||||
<string name="reboot_safe_mode">Güvenli Mod</string>
|
||||
<string name="module_version_author">%1$s, Geliştirici: %2$s</string>
|
||||
<string name="module_state_remove">Kaldır</string>
|
||||
<string name="module_action">Eylem</string>
|
||||
<string name="module_state_restore">Geri Yükle</string>
|
||||
<string name="module_action_install_external">Depolamadan yükle</string>
|
||||
<string name="update_available">Güncelleme Mevcut</string>
|
||||
<string name="update_available">Güncelleme mevcut</string>
|
||||
<string name="suspend_text_riru">Modül, %1$s etkin olduğu için askıya alındı</string>
|
||||
<string name="suspend_text_zygisk">Modül, %1$s etkin olmadığı için askıya alındı</string>
|
||||
<string name="zygisk_module_unloaded">Uyumsuzluk nedeniyle Zygisk modülü yüklenmedi</string>
|
||||
<string name="zygisk_module_unloaded">Zygisk modülü uyumsuzluk nedeniyle yüklenmedi</string>
|
||||
<string name="module_empty">Yüklü modül yok</string>
|
||||
<string name="confirm_install">%1$s modülü yüklensin mi?</string>
|
||||
<string name="confirm_install_title">Yükleme Onayı</string>
|
||||
@@ -127,121 +125,125 @@
|
||||
<string name="settings_dark_mode_dark">Her Zaman Karanlık</string>
|
||||
<string name="settings_download_path_title">İndirme Yolu</string>
|
||||
<string name="settings_download_path_message">Dosyalar %1$s konumuna kaydedilecek</string>
|
||||
<string name="settings_hide_app_title">Magisk uygulamasını gizle</string>
|
||||
<string name="settings_hide_app_summary">Rastgele bir paket kimliği ve özel uygulama etiketi olan bir vekil (proxy) uygulaması yükleyin</string>
|
||||
<string name="settings_restore_app_title">Magisk uygulamasını geri yükle</string>
|
||||
<string name="settings_restore_app_summary">Uygulamayı göster ve orijinal APK\'yı geri yükle</string>
|
||||
<string name="settings_hide_app_title">Magisk Uygulamasını Gizle</string>
|
||||
<string name="settings_hide_app_summary">Rastgele bir paket kimliği ve özel uygulama etiketi ile bir proxy uygulaması yükleyin</string>
|
||||
<string name="settings_restore_app_title">Magisk Uygulamasını Geri Yükle</string>
|
||||
<string name="settings_restore_app_summary">Uygulamanın gizliliğini kaldırın ve orijinal APK\'yi geri yükleyin</string>
|
||||
<string name="language">Dil</string>
|
||||
<string name="system_default">(Sistem Varsayılanı)</string>
|
||||
<string name="settings_check_update_title">Güncellemeleri Kontrol Et</string>
|
||||
<string name="settings_check_update_summary">Arka planda düzenli olarak güncellemeleri kontrol et</string>
|
||||
<string name="settings_check_update_summary">Arka planda periyodik olarak güncellemeleri kontrol et</string>
|
||||
<string name="settings_update_channel_title">Güncelleme Kanalı</string>
|
||||
<string name="settings_update_stable">Kararlı</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_debug">Hata Ayıklama</string>
|
||||
<string name="settings_update_custom">Özel</string>
|
||||
<string name="settings_update_custom_msg">Özel kanal URL\'si girin</string>
|
||||
<string name="settings_zygisk_summary">Magisk\'in bazı bölümlerini zygote daemon\'unda çalıştır</string>
|
||||
<string name="settings_denylist_title">Reddetme Listesini Zorla</string>
|
||||
<string name="settings_denylist_summary">Reddetme Listesindeki işlemler tüm Magisk değişikliklerini geri alacak</string>
|
||||
<string name="settings_denylist_config_title">Reddetme Listesini Yapılandır</string>
|
||||
<string name="settings_denylist_config_summary">Reddetme Listesine dahil edilecek işlemleri seçin</string>
|
||||
<string name="settings_hosts_title">Sistemsiz ana makineler (systemless hosts)</string>
|
||||
<string name="settings_hosts_summary">Reklam engelleme uygulamaları için sistemsiz ana makineler (systemless hosts) desteği</string>
|
||||
<string name="settings_hosts_toast">Sistemsiz ana makineler (systemless hosts) modülü eklendi</string>
|
||||
<string name="settings_update_custom_msg">Özel bir kanal URL\'si girin</string>
|
||||
<string name="settings_zygisk_summary">Magisk\'in bazı kısımlarını Zygote daemon içinde çalıştırın</string>
|
||||
<string name="settings_denylist_title">Red Listesini Uygula</string>
|
||||
<string name="settings_denylist_summary">Red listesindeki işlemlerin tüm Magisk değişiklikleri geri alınacak</string>
|
||||
<string name="settings_denylist_config_title">Red Listesini Yapılandır</string>
|
||||
<string name="settings_denylist_config_summary">Red listesine dahil edilecek işlemleri seçin</string>
|
||||
<string name="settings_hosts_title">Sistemsiz Hosts</string>
|
||||
<string name="settings_hosts_summary">Reklam engelleme uygulamaları için sistemsiz hosts desteği</string>
|
||||
<string name="settings_hosts_toast">Sistemsiz hosts modülü eklendi</string>
|
||||
<string name="settings_app_name_hint">Yeni ad</string>
|
||||
<string name="settings_app_name_helper">Uygulama bu isimle yeniden paketlenecek</string>
|
||||
<string name="settings_app_name_helper">Uygulama bu adla yeniden paketlenecek</string>
|
||||
<string name="settings_app_name_error">Geçersiz format</string>
|
||||
<string name="settings_su_app_adb">Uygulamalar ve ADB</string>
|
||||
<string name="settings_su_app">Sadece Uygulamalar</string>
|
||||
<string name="settings_su_adb">Sadece ADB</string>
|
||||
<string name="settings_su_disable">Devre Dışı</string>
|
||||
<string name="settings_su_request_10">10 saniye</string>
|
||||
<string name="settings_su_request_15">15 saniye</string>
|
||||
<string name="settings_su_request_20">20 saniye</string>
|
||||
<string name="settings_su_request_30">30 saniye</string>
|
||||
<string name="settings_su_request_45">45 saniye</string>
|
||||
<string name="settings_su_request_60">60 saniye</string>
|
||||
<string name="settings_su_request_10">10 Saniye</string>
|
||||
<string name="settings_su_request_15">15 Saniye</string>
|
||||
<string name="settings_su_request_20">20 Saniye</string>
|
||||
<string name="settings_su_request_30">30 Saniye</string>
|
||||
<string name="settings_su_request_45">45 Saniye</string>
|
||||
<string name="settings_su_request_60">60 Saniye</string>
|
||||
<string name="superuser_access">Süper Kullanıcı Erişimi</string>
|
||||
<string name="auto_response">Otomatik Yanıt</string>
|
||||
<string name="request_timeout">İstek Zaman Aşımı</string>
|
||||
<string name="superuser_notification">Süper Kullanıcı Bildirimi</string>
|
||||
<string name="settings_su_reauth_title">Yükseltme sonrası yeniden doğrulama yap</string>
|
||||
<string name="settings_su_reauth_summary">Uygulama güncellemelerinden sonra Süper Kullanıcı izinlerini tekrar iste</string>
|
||||
<string name="settings_su_tapjack_title">Tapjacking Koruması</string>
|
||||
<string name="settings_su_tapjack_summary">Süper Kullanıcı istemi diyalogu, başka bir pencere veya katman tarafından gizlendiğinde girdilere yanıt vermeyecektir</string>
|
||||
<string name="settings_su_auth_title">Kullanıcı Kimlik Doğrulama</string>
|
||||
<string name="settings_su_auth_summary">Süper Kullanıcı isteklerinde kullanıcı kimlik doğrulaması iste</string>
|
||||
<string name="settings_su_reauth_title">Yükseltmeden Sonra Yeniden Kimlik Doğrula</string>
|
||||
<string name="settings_su_reauth_summary">Uygulamaları yükselttikten sonra tekrar Süper Kullanıcı izinlerini iste</string>
|
||||
<string name="settings_su_tapjack_title">Dokunma Saldırısı Koruması</string>
|
||||
<string name="settings_su_tapjack_summary">Süper Kullanıcı istek penceresi, başka bir pencere veya katman tarafından engellendiğinde girdilere yanıt vermeyecektir</string>
|
||||
<string name="settings_su_auth_title">Kullanıcı Kimlik Doğrulaması</string>
|
||||
<string name="settings_su_auth_summary">Süper Kullanıcı istekleri sırasında kullanıcı kimlik doğrulaması iste</string>
|
||||
<string name="settings_su_auth_insecure">Cihazda yapılandırılmış bir kimlik doğrulama yöntemi yok</string>
|
||||
<string name="settings_customization">Özelleştir</string>
|
||||
<string name="setting_add_shortcut_summary">Uygulamayı gizledikten sonra adı ve simgeyi tanımakta zorlanıyorsanız ana ekrana güzel bir kısayol ekle</string>
|
||||
<string name="settings_doh_title">DNS üzerinden HTTPS</string>
|
||||
<string name="settings_doh_description">Bazı ülkelerde DNS zehirlemesine karşı geçici çözüm</string>
|
||||
<string name="settings_random_name_title">Çıkış adını rastgele seç</string>
|
||||
<string name="settings_random_name_description">Algılamayı önlemek için yamalı resimlerin ve tar dosyalarının çıkış dosya adını rastgele seç</string>
|
||||
|
||||
<string name="multiuser_mode">Çok Kullanıcılı Mod</string>
|
||||
<string name="settings_owner_only">Yalnızca Cihaz Sahibi</string>
|
||||
<string name="settings_owner_manage">Cihaz Sahibi Yönetiminde</string>
|
||||
<string name="settings_su_restrict_title">Root Yeteneklerini Kısıtla</string>
|
||||
<string name="settings_su_restrict_summary">Yeni Süper Kullanıcı uygulamalarını varsayılan olarak kısıtlayacaktır. Uyarı: Bu, çoğu uygulamayı bozacaktır. Ne yaptığınızı bilmiyorsanız etkinleştirmeyin.</string>
|
||||
<string name="settings_customization">Özelleştirme</string>
|
||||
<string name="setting_add_shortcut_summary">Uygulamayı gizledikten sonra adı ve simgesi tanınması zor olursa ana ekrana güzel bir kısayol ekleyin</string>
|
||||
<string name="settings_doh_title">HTTPS üzerinden DNS</string>
|
||||
<string name="settings_doh_description">Bazı ülkelerdeki DNS zehirlenmesini aşmak için geçici çözüm</string>
|
||||
<string name="settings_random_name_title">Çıktı Adını Rastgele Yap</string>
|
||||
<string name="settings_random_name_description">Tespit edilmesini önlemek için yamalanmış imajların ve tar dosyalarının çıktı dosya adını rastgele yapın</string>
|
||||
<string name="multiuser_mode">Çoklu Kullanıcı Modu</string>
|
||||
<string name="settings_owner_only">Sadece Cihaz Sahibi</string>
|
||||
<string name="settings_owner_manage">Cihaz Sahibi Tarafından Yönetilen</string>
|
||||
<string name="settings_user_independent">Kullanıcıdan Bağımsız</string>
|
||||
<string name="owner_only_summary">Yalnızca sahip kök (root) erişimine sahiptir</string>
|
||||
<string name="owner_manage_summary">Yalnızca sahip kök (root) erişimini yönetebilir ve istek uyarılarını alabilir</string>
|
||||
<string name="user_independent_summary">Her kullanıcının kendi ayrı kök (root) kuralları vardır</string>
|
||||
|
||||
<string name="owner_only_summary">Sadece cihaz sahibi root erişimine sahiptir</string>
|
||||
<string name="owner_manage_summary">Sadece cihaz sahibi root erişimini yönetebilir ve istekleri alabilir</string>
|
||||
<string name="user_independent_summary">Her kullanıcının kendi ayrı root kuralları vardır</string>
|
||||
<string name="mount_namespace_mode">Bağlama Ad Alanı Modu</string>
|
||||
<string name="settings_ns_global">Küresel Ad Alanı</string>
|
||||
<string name="settings_ns_global">Genel Ad Alanı</string>
|
||||
<string name="settings_ns_requester">Ad Alanını Devral</string>
|
||||
<string name="settings_ns_isolate">İzolasyon Ad Alanı</string>
|
||||
<string name="global_summary">Tüm kök (root) oturumları küresel bağlama ad alanını kullanır</string>
|
||||
<string name="requester_summary">Kök (root) oturumları isteyicisinin ad alanını devralacak</string>
|
||||
<string name="isolate_summary">Her kök (root) oturumu kendi izole ad alanına sahip olacak</string>
|
||||
<string name="settings_ns_isolate">Yalıtılmış Ad Alanı</string>
|
||||
<string name="global_summary">Tüm root oturumları genel bağlama ad alanını kullanır</string>
|
||||
<string name="requester_summary">Root oturumları, istekçilerinin ad alanını devralır</string>
|
||||
<string name="isolate_summary">Her root oturumunun kendi yalıtılmış ad alanı olacaktır</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Magisk Güncellemeleri</string>
|
||||
<string name="progress_channel">İlerleme Bildirimleri</string>
|
||||
<string name="updated_channel">Güncelleme Tamamlandı</string>
|
||||
<string name="update_channel">Magisk güncellemeleri</string>
|
||||
<string name="progress_channel">İlerleme bildirimleri</string>
|
||||
<string name="updated_channel">Güncelleme tamamlandı</string>
|
||||
<string name="download_complete">İndirme tamamlandı</string>
|
||||
<string name="download_file_error">Dosya indirilirken hata oluştu</string>
|
||||
<string name="magisk_update_title">Magisk Güncellemesi Mevcut!</string>
|
||||
<string name="updated_title">Magisk Güncellendi</string>
|
||||
<string name="magisk_update_title">Magisk güncellemesi mevcut!</string>
|
||||
<string name="updated_title">Magisk güncellendi</string>
|
||||
<string name="updated_text">Uygulamayı açmak için dokunun</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Mevcut</string>
|
||||
<string name="no">Mevcut Değil</string>
|
||||
<string name="repo_install_title">%1$s %2$s(%3$d) Kur</string>
|
||||
<string name="yes">Evet</string>
|
||||
<string name="no">Hayır</string>
|
||||
<string name="repo_install_title">%1$s %2$s(%3$d) Yüklensin mi?</string>
|
||||
<string name="download">İndir</string>
|
||||
<string name="reboot">Yeniden Başlat</string>
|
||||
<string name="close">Kapat</string>
|
||||
<string name="release_notes">Sürüm Notları</string>
|
||||
<string name="flashing">Yükleniyor...</string>
|
||||
<string name="done">Tamamlandı!</string>
|
||||
<string name="flashing">Flaşlanıyor…</string>
|
||||
<string name="running">Çalışıyor…</string>
|
||||
<string name="done">Bitti!</string>
|
||||
<string name="done_action">%1$s eylemi tamamlandı</string>
|
||||
<string name="failure">Başarısız!</string>
|
||||
<string name="hide_app_title">Magisk uygulaması gizleniyor…</string>
|
||||
<string name="open_link_failed_toast">Bağlantıyı açmak için uygulama bulunamadı</string>
|
||||
<string name="open_link_failed_toast">Bağlantıyı açacak bir uygulama bulunamadı</string>
|
||||
<string name="complete_uninstall">Tamamen Kaldır</string>
|
||||
<string name="restore_img">Görüntüleri Geri Yükle</string>
|
||||
<string name="restore_img_msg">Geri Yükleniyor...</string>
|
||||
<string name="restore_img">İmajları Geri Yükle</string>
|
||||
<string name="restore_img_msg">Geri yükleniyor…</string>
|
||||
<string name="restore_done">Geri yükleme tamamlandı!</string>
|
||||
<string name="restore_fail">Stok yedeği mevcut değil!</string>
|
||||
<string name="setup_fail">Kurulum başarısız oldu</string>
|
||||
<string name="env_fix_title">Ek Ayar Gerekiyor</string>
|
||||
<string name="env_fix_msg">Magisk\'in düzgün çalışabilmesi için cihazınızın ek ayarlar yapması gerekiyor. Devam etmek ve yeniden başlatmak istiyor musunuz?</string>
|
||||
<string name="env_full_fix_msg">Cihazınızın düzgün çalışabilmesi için Magisk\'in yeniden yüklenmesi gerekiyor. Lütfen Magisk\'i uygulama içinde yeniden yükleyin, kurtarma modu doğru cihaz bilgilerini alamaz.</string>
|
||||
<string name="setup_msg">Ortam kurulumu yapılıyor...</string>
|
||||
<string name="env_fix_title">Ek Kurulum Gerektiriyor</string>
|
||||
<string name="env_fix_msg">Cihazınızın Magisk\'in düzgün çalışması için ek kuruluma ihtiyacı var. Devam edip yeniden başlatmak ister misiniz?</string>
|
||||
<string name="env_full_fix_msg">Cihazınızın düzgün çalışması için Magisk\'i yeniden flaşlamanız gerekiyor. Lütfen Magisk\'i uygulama içinden yeniden kurun, Kurtarma modu doğru cihaz bilgisini alamaz.</string>
|
||||
<string name="setup_msg">Çalışma ortamı kuruluyor…</string>
|
||||
<string name="unsupport_magisk_title">Desteklenmeyen Magisk Sürümü</string>
|
||||
<string name="unsupport_magisk_msg">Bu uygulama sürümü, %1$s altındaki Magisk sürümlerini desteklemiyor.\n\nUygulama, Magisk yüklü değilmiş gibi davranacaktır, lütfen en kısa sürede Magisk\'i güncelleyin.</string>
|
||||
<string name="unsupport_magisk_msg">Uygulamanın bu sürümü, %1$s sürümünden daha düşük Magisk sürümlerini desteklemiyor.\n\nUygulama, Magisk yüklü değilmiş gibi davranacaktır. Lütfen Magisk\'i en kısa sürede güncelleyin.</string>
|
||||
<string name="unsupport_general_title">Anormal Durum</string>
|
||||
<string name="unsupport_system_app_msg">Bu uygulamanın sistem uygulaması olarak çalıştırılması desteklenmiyor. Lütfen uygulamayı kullanıcı uygulamasına geri döndürün.</string>
|
||||
<string name="unsupport_other_su_msg">Magisk\'ten gelmeyen bir "su" ikili dosyası tespit edildi. Lütfen herhangi bir rakip kök (root) çözümünü kaldırın ve/veya Magisk\'i yeniden yükleyin.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk harici depolamaya yüklendi. Lütfen uygulamayı dahili depolamaya taşıyın.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Gizli Magisk uygulaması kök (root) erişimi kaybolduğu için çalışmaya devam edemez. Lütfen orijinal APK\'yı geri yükleyin.</string>
|
||||
<string name="unsupport_system_app_msg">Bu uygulamanın bir sistem uygulaması olarak çalıştırılması desteklenmiyor. Lütfen uygulamayı bir kullanıcı uygulamasına geri döndürün.</string>
|
||||
<string name="unsupport_other_su_msg">Magisk\'e ait olmayan bir "su" ikili dosyası tespit edildi. Lütfen rakip root çözümlerini kaldırın ve/veya Magisk\'i yeniden yükleyin.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk harici depolamaya kurulmuş. Lütfen uygulamayı dahili depolamaya taşıyın.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Gizlenmiş Magisk uygulaması, root kaybolduğu için çalışmaya devam edemiyor. Lütfen orijinal APK\'yi geri yükleyin.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Bu işlevselliği etkinleştirmek için depolama izni verin</string>
|
||||
<string name="post_notifications_denied">Bu işlevselliği etkinleştirmek için bildirim izni verin</string>
|
||||
<string name="install_unknown_denied">Bu işlevselliği etkinleştirmek için "bilinmeyen uygulamaları yükle" iznini verin</string>
|
||||
<string name="add_shortcut_title">Ana ekrana kısayol ekle</string>
|
||||
<string name="add_shortcut_msg">Bu uygulamayı gizledikten sonra adı ve simgesi tanınmayabilir. Ana ekrana güzel bir kısayol eklemek ister misiniz?</string>
|
||||
<string name="app_not_found">Bu işlemi gerçekleştirecek uygulama bulunamadı</string>
|
||||
<string name="external_rw_permission_denied">Bu işlevi etkinleştirmek için depolama izni verin</string>
|
||||
<string name="post_notifications_denied">Bu işlevi etkinleştirmek için bildirim izni verin</string>
|
||||
<string name="install_unknown_denied">Bu işlevi etkinleştirmek için "Bilinmeyen uygulamaları yükle" iznini verin</string>
|
||||
<string name="add_shortcut_title">Ana Ekrana Kısayol Ekle</string>
|
||||
<string name="add_shortcut_msg">Bu uygulamayı gizledikten sonra adı ve simgesi tanınması zor olabilir. Ana ekrana güzel bir kısayol eklemek ister misiniz?</string>
|
||||
<string name="app_not_found">Bu eylemi gerçekleştirecek bir uygulama bulunamadı</string>
|
||||
<string name="reboot_apply_change">Değişiklikleri uygulamak için yeniden başlatın</string>
|
||||
<string name="restore_app_confirmation">Bu, gizli uygulamayı orijinal uygulamaya geri yükleyecektir. Gerçekten bunu yapmak istiyor musunuz?</string>
|
||||
<string name="restore_app_confirmation">Bu, gizlenmiş uygulamayı orijinal uygulamaya geri yükleyecektir. Bunu gerçekten yapmak istiyor musunuz?</string>
|
||||
|
||||
</resources>
|
||||
|
234
app/core/src/main/res/values-ur/strings.xml
Normal file
234
app/core/src/main/res/values-ur/strings.xml
Normal file
@@ -0,0 +1,234 @@
|
||||
<resources>
|
||||
|
||||
<string name="modules">ماڈیولز</string>
|
||||
<string name="superuser">سپر یوزر</string>
|
||||
<string name="logs">لاگز</string>
|
||||
<string name="settings">سیٹنگز</string>
|
||||
<string name="install">انسٹال کریں</string>
|
||||
<string name="section_home">ہوم</string>
|
||||
<string name="section_theme">تھیمز</string>
|
||||
<string name="denylist">مسترد فہرست</string>
|
||||
|
||||
<string name="no_connection">کوئی کنکشن دستیاب نہیں</string>
|
||||
<string name="app_changelog">ایپ چینج لاگ</string>
|
||||
<string name="loading">لوڈ ہو رہا ہے…</string>
|
||||
<string name="update">اپ ڈیٹ</string>
|
||||
<string name="not_available">دستیاب نہیں</string>
|
||||
<string name="hide">چھپائیں</string>
|
||||
<string name="home_package">پیکیج</string>
|
||||
<string name="home_app_title">ایپ</string>
|
||||
|
||||
<string name="home_notice_content">صرف آفیشل گٹ ہب پیج سے Magisk ڈاؤن لوڈ کریں۔ نامعلوم ذرائع سے فائلیں نقصان دہ ہو سکتی ہیں!</string>
|
||||
<string name="home_support_title">ہماری مدد کریں</string>
|
||||
<string name="home_follow_title">ہمیں فالو کریں</string>
|
||||
<string name="home_item_source">ذریعہ</string>
|
||||
<string name="home_support_content">Magisk ہمیشہ مفت اور اوپن سورس رہے گا۔ تاہم، آپ عطیہ کر کے ہمیں اپنی دیکھ بھال کا اظہار کر سکتے ہیں۔</string>
|
||||
<string name="home_installed_version">انسٹال شدہ</string>
|
||||
<string name="home_latest_version">تازہ ترین</string>
|
||||
<string name="invalid_update_channel">غلط اپ ڈیٹ چینل</string>
|
||||
<string name="uninstall_magisk_title">Magisk اَن انسٹال کریں</string>
|
||||
<string name="uninstall_magisk_msg">تمام ماڈیولز غیر فعال/ہٹا دیے جائیں گے!\nروٹ ہٹا دیا جائے گا!\nMagisk کے استعمال کے ذریعے غیر اینکرپٹ کی گئی کوئی بھی اندرونی اسٹوریج دوبارہ اینکرپٹ ہو جائے گی!</string>
|
||||
|
||||
<string name="keep_force_encryption">جبری انکرپشن کو محفوظ رکھیں</string>
|
||||
<string name="keep_dm_verity">AVB 2.0/dm-verity کو محفوظ رکھیں</string>
|
||||
<string name="recovery_mode">ریکوری موڈ</string>
|
||||
<string name="install_options_title">آپشنز</string>
|
||||
<string name="install_method_title">طریقہ</string>
|
||||
<string name="install_next">اگلا</string>
|
||||
<string name="install_start">چلو شروع کرتے ہیں</string>
|
||||
<string name="manager_download_install">ڈاؤن لوڈ اور انسٹال کرنے کے لیے دبائیں</string>
|
||||
<string name="direct_install">براہ راست انسٹال (تجویز کردہ)</string>
|
||||
<string name="install_inactive_slot">غیر فعال سلاٹ میں انسٹال کریں (OTA کے بعد)</string>
|
||||
<string name="install_inactive_slot_msg">ریبوٹ کے بعد آپ کا آلہ زبردستی موجودہ غیر فعال سلاٹ میں بوٹ ہو جائے گا!\nیہ آپشن صرف OTA مکمل ہونے کے بعد استعمال کریں۔\nجاری رکھیں؟</string>
|
||||
<string name="setup_title">اضافی سیٹ اپ</string>
|
||||
<string name="select_patch_file">پیچ فائل منتخب اور پیچ کریں</string>
|
||||
<string name="patch_file_msg">ایک خام امیج (*.img) یا ایک ODIN ٹار فائل (*.tar) یا ایک payload.bin (*.bin) منتخب کریں</string>
|
||||
<string name="reboot_delay_toast">5 سیکنڈ میں ریبوٹ ہو رہا ہے…</string>
|
||||
<string name="flash_screen_title">انسٹالیشن</string>
|
||||
|
||||
<string name="su_request_title">سپر یوزر کی درخواست</string>
|
||||
<string name="touch_filtered_warning">چونکہ ایک ایپ سپر یوزر کی درخواست کو دھندلا کر رہی ہے، اس لیے Magisk آپ کے جواب کی تصدیق نہیں کر سکتا</string>
|
||||
<string name="deny">منع کریں</string>
|
||||
<string name="prompt">پوچھیں</string>
|
||||
<string name="grant">اجازت دیں</string>
|
||||
<string name="su_warning">اپنے آلے تک مکمل رسائی کی اجازت دیں۔\nاگر آپ کو یقین نہیں ہے تو منع کریں!</string>
|
||||
<string name="forever">ہمیشہ کے لیے</string>
|
||||
<string name="once">ایک بار</string>
|
||||
<string name="tenmin">10 منٹ</string>
|
||||
<string name="twentymin">20 منٹ</string>
|
||||
<string name="thirtymin">30 منٹ</string>
|
||||
<string name="sixtymin">60 منٹ</string>
|
||||
<string name="su_allow_toast">%1$s کو سپر یوزر کے حقوق دیئے گئے</string>
|
||||
<string name="su_deny_toast">%1$s کو سپر یوزر کے حقوق سے انکار کر دیا گیا</string>
|
||||
<string name="su_snack_grant">%1$s کے سپر یوزر حقوق دیئے گئے</string>
|
||||
<string name="su_snack_deny">%1$s کے سپر یوزر حقوق سے انکار کر دیا گیا</string>
|
||||
<string name="su_snack_notif_on">%1$s کی اطلاعات فعال ہیں</string>
|
||||
<string name="su_snack_notif_off">%1$s کی اطلاعات غیر فعال ہیں</string>
|
||||
<string name="su_snack_log_on">%1$s کی لاگنگ فعال ہے</string>
|
||||
<string name="su_snack_log_off">%1$s کی لاگنگ غیر فعال ہے</string>
|
||||
<string name="su_revoke_title">منسوخ کریں؟</string>
|
||||
<string name="su_revoke_msg">%1$s کے سپر یوزر حقوق منسوخ کرنے کی تصدیق کریں</string>
|
||||
<string name="toast">ٹوسٹ</string>
|
||||
<string name="none">کوئی نہیں</string>
|
||||
|
||||
<string name="superuser_toggle_notification">اطلاعات</string>
|
||||
<string name="superuser_toggle_revoke">منسوخ کریں</string>
|
||||
<string name="superuser_policy_none">ابھی تک کسی ایپ نے سپر یوزر کی اجازت نہیں مانگی ہے۔</string>
|
||||
|
||||
<string name="log_data_none">آپ لاگ فری ہیں، اپنی روٹ ایپس کو مزید استعمال کرنے کی کوشش کریں</string>
|
||||
<string name="log_data_magisk_none">Magisk لاگز خالی ہیں، یہ عجیب بات ہے</string>
|
||||
<string name="menuSaveLog">لاگ محفوظ کریں</string>
|
||||
<string name="menuClearLog">ابھی لاگ صاف کریں</string>
|
||||
<string name="logs_cleared">لاگ کامیابی سے صاف ہو گیا</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">ٹارگٹ UID: %1$d</string>
|
||||
<string name="target_pid">ماؤنٹ این ایس ٹارگٹ PID: %s</string>
|
||||
<string name="selinux_context">SELinux سیاق و سباق: %s</string>
|
||||
<string name="supp_group">اضافی گروپ: %s</string>
|
||||
|
||||
<string name="show_system_app">سسٹم ایپس دکھائیں</string>
|
||||
<string name="show_os_app">OS ایپس دکھائیں</string>
|
||||
<string name="hide_filter_hint">نام سے فلٹر کریں</string>
|
||||
<string name="hide_search">تلاش کریں</string>
|
||||
|
||||
<string name="no_info_provided">(کوئی معلومات فراہم نہیں کی گئی)</string>
|
||||
<string name="reboot_userspace">سافٹ ریبوٹ</string>
|
||||
<string name="reboot_recovery">ریکوری میں ریبوٹ کریں</string>
|
||||
<string name="reboot_bootloader">بوٹ لوڈر میں ریبوٹ کریں</string>
|
||||
<string name="reboot_download">ڈاؤن لوڈ میں ریبوٹ کریں</string>
|
||||
<string name="reboot_edl">EDL میں ریبوٹ کریں</string>
|
||||
<string name="reboot_safe_mode">محفوظ موڈ</string>
|
||||
<string name="module_version_author">%2$s کی جانب سے %1$s</string>
|
||||
<string name="module_state_remove">ہٹائیں</string>
|
||||
<string name="module_state_restore">بحال کریں</string>
|
||||
<string name="module_action_install_external">اسٹوریج سے انسٹال کریں</string>
|
||||
<string name="update_available">اپ ڈیٹ دستیاب ہے</string>
|
||||
<string name="suspend_text_riru">ماڈیول معطل کر دیا گیا کیونکہ %1$s فعال ہے</string>
|
||||
<string name="suspend_text_zygisk">ماڈیول معطل کر دیا گیا کیونکہ %1$s فعال نہیں ہے</string>
|
||||
<string name="zygisk_module_unloaded">غیر مطابقت کی وجہ سے Zygisk ماڈیول لوڈ نہیں ہوا</string>
|
||||
<string name="module_empty">کوئی ماڈیول انسٹال نہیں ہے</string>
|
||||
<string name="confirm_install">ماڈیول %1$s انسٹال کریں؟</string>
|
||||
<string name="confirm_install_title">انسٹالیشن کی تصدیق</string>
|
||||
|
||||
<string name="settings_dark_mode_title">تھیم موڈ</string>
|
||||
<string name="settings_dark_mode_message">وہ موڈ منتخب کریں جو آپ کے انداز کے مطابق ہو!</string>
|
||||
<string name="settings_dark_mode_light">ہمیشہ لائٹ</string>
|
||||
<string name="settings_dark_mode_system">سسٹم کی پیروی کریں</string>
|
||||
<string name="settings_dark_mode_dark">ہمیشہ ڈارک</string>
|
||||
<string name="settings_download_path_title">ڈاؤن لوڈ کا راستہ</string>
|
||||
<string name="settings_download_path_message">فائلیں %1$s میں محفوظ کی جائیں گی</string>
|
||||
<string name="settings_hide_app_title">Magisk ایپ کو چھپائیں</string>
|
||||
<string name="settings_hide_app_summary">ایک رینڈم پیکیج ID اور کسٹم ایپ لیبل کے ساتھ ایک پراکسی ایپ انسٹال کریں</string>
|
||||
<string name="settings_restore_app_title">Magisk ایپ کو بحال کریں</string>
|
||||
<string name="settings_restore_app_summary">ایپ کو ان ہائیڈ کریں اور اصل APK کو بحال کریں</string>
|
||||
<string name="language">زبان</string>
|
||||
<string name="system_default">(سسٹم ڈیفالٹ)</string>
|
||||
<string name="settings_check_update_title">اپ ڈیٹس چیک کریں</string>
|
||||
<string name="settings_check_update_summary">وقفے وقفے سے پس منظر میں اپ ڈیٹس چیک کریں</string>
|
||||
<string name="settings_update_channel_title">اپ ڈیٹ چینل</string>
|
||||
<string name="settings_update_stable">مستحکم</string>
|
||||
<string name="settings_update_beta">بیٹا</string>
|
||||
<string name="settings_update_custom">کسٹم</string>
|
||||
<string name="settings_update_custom_msg">ایک کسٹم چینل URL درج کریں</string>
|
||||
<string name="settings_zygisk_summary">zygote ڈیمن میں Magisk کے کچھ حصے چلائیں</string>
|
||||
<string name="settings_denylist_title">مسترد فہرست نافذ کریں</string>
|
||||
<string name="settings_denylist_summary">مسترد فہرست میں موجود عملوں میں Magisk کی تمام ترامیم کو کالعدم قرار دیا جائے گا</string>
|
||||
<string name="settings_denylist_config_title">مسترد فہرست کی تشکیل کریں</string>
|
||||
<string name="settings_denylist_config_summary">مسترد فہرست میں شامل کرنے کے لیے عمل منتخب کریں</string>
|
||||
<string name="settings_hosts_title">سسٹم لیس ہوسٹس</string>
|
||||
<string name="settings_hosts_summary">ایڈ بلاکنگ ایپس کے لیے سسٹم لیس ہوسٹس سپورٹ</string>
|
||||
<string name="settings_hosts_toast">سسٹم لیس ہوسٹس ماڈیول شامل کیا گیا</string>
|
||||
<string name="settings_app_name_hint">نیا نام</string>
|
||||
<string name="settings_app_name_helper">ایپ کو اس نام سے دوبارہ پیک کیا جائے گا</string>
|
||||
<string name="settings_app_name_error">غلط فارمیٹ</string>
|
||||
<string name="settings_su_app_adb">ایپس اور ADB</string>
|
||||
<string name="settings_su_app">صرف ایپس</string>
|
||||
<string name="settings_su_adb">صرف ADB</string>
|
||||
<string name="settings_su_disable">غیر فعال</string>
|
||||
<string name="settings_su_request_10">10 سیکنڈ</string>
|
||||
<string name="settings_su_request_15">15 سیکنڈ</string>
|
||||
<string name="settings_su_request_20">20 سیکنڈ</string>
|
||||
<string name="settings_su_request_30">30 سیکنڈ</string>
|
||||
<string name="settings_su_request_45">45 سیکنڈ</string>
|
||||
<string name="settings_su_request_60">60 سیکنڈ</string>
|
||||
<string name="superuser_access">سپر یوزر تک رسائی</string>
|
||||
<string name="auto_response">خودکار جواب</string>
|
||||
<string name="request_timeout">درخواست کا وقت ختم</string>
|
||||
<string name="superuser_notification">سپر یوزر اطلاع</string>
|
||||
<string name="settings_su_reauth_title">اپ گریڈ کے بعد دوبارہ تصدیق کریں</string>
|
||||
<string name="settings_su_reauth_summary">ایپس کو اپ گریڈ کرنے کے بعد دوبارہ سپر یوزر کی اجازتیں مانگیں</string>
|
||||
<string name="settings_su_tapjack_title">ٹیپ جیکنگ سے تحفظ</string>
|
||||
<string name="settings_su_tapjack_summary">سپر یوزر پرامپٹ ڈائیلاگ کسی دوسری ونڈو یا اوورلے سے دھندلا ہونے کے دوران ان پٹ کا جواب نہیں دے گا</string>
|
||||
<string name="settings_su_auth_title">صارف کی تصدیق</string>
|
||||
<string name="settings_su_auth_summary">سپر یوزر کی درخواستوں کے دوران صارف کی تصدیق طلب کریں</string>
|
||||
<string name="settings_su_auth_insecure">آلے پر کوئی تصدیقی طریقہ کار تشکیل نہیں دیا گیا ہے</string>
|
||||
<string name="settings_customization">حسب ضرورت سازی</string>
|
||||
<string name="setting_add_shortcut_summary">اگر ایپ کو چھپانے کے بعد اس کا نام اور آئیکن پہچاننا مشکل ہو جائے تو ہوم اسکرین پر ایک خوبصورت شارٹ کٹ شامل کریں</string>
|
||||
<string name="settings_doh_title">HTTPS پر DNS</string>
|
||||
<string name="settings_doh_description">بعض ممالک میں DNS زہر آلودگی کا حل</string>
|
||||
<string name="settings_random_name_title">بے ترتیب آؤٹ پٹ نام</string>
|
||||
<string name="settings_random_name_description">پتہ لگانے سے بچنے کے لیے پیچ شدہ امیجز اور ٹار فائلوں کے آؤٹ پٹ فائل کے نام کو بے ترتیب بنائیں</string>
|
||||
|
||||
<string name="multiuser_mode">ملٹی یوزر موڈ</string>
|
||||
<string name="settings_owner_only">صرف آلہ کا مالک</string>
|
||||
<string name="settings_owner_manage">آلہ کے مالک کے زیر انتظام</string>
|
||||
<string name="settings_user_independent">صارف سے آزاد</string>
|
||||
<string name="owner_only_summary">صرف مالک کو روٹ تک رسائی حاصل ہے</string>
|
||||
<string name="owner_manage_summary">صرف مالک روٹ تک رسائی کا انتظام کر سکتا ہے اور درخواست کے اشارے وصول کر سکتا ہے</string>
|
||||
<string name="user_independent_summary">ہر صارف کے اپنے علیحدہ روٹ کے قواعد ہیں</string>
|
||||
|
||||
<string name="mount_namespace_mode">ماؤنٹ نیم اسپیس موڈ</string>
|
||||
<string name="settings_ns_global">عالمی نیم اسپیس</string>
|
||||
<string name="settings_ns_requester">نیم اسپیس وراثت میں حاصل کریں</string>
|
||||
<string name="settings_ns_isolate">آئسولیٹڈ نیم اسپیس</string>
|
||||
<string name="isolate_summary">ہر روٹ سیشن کی اپنی آئسولیٹڈ نیم اسپیس ہوگی</string>
|
||||
<string name="update_channel">Magisk اپ ڈیٹس</string>
|
||||
<string name="progress_channel">پیش رفت کی اطلاعات</string>
|
||||
<string name="updated_channel">اپ ڈیٹ مکمل</string>
|
||||
<string name="download_complete">ڈاؤن لوڈ مکمل</string>
|
||||
<string name="download_file_error">فائل ڈاؤن لوڈ کرنے میں خرابی</string>
|
||||
<string name="magisk_update_title">Magisk اپ ڈیٹ دستیاب ہے!</string>
|
||||
<string name="updated_title">Magisk اپ ڈیٹ ہو گیا</string>
|
||||
<string name="updated_text">ایپ کھولنے کے لیے ٹیپ کریں</string>
|
||||
<string name="yes">ہاں</string>
|
||||
<string name="no">نہیں</string>
|
||||
<string name="repo_install_title">%1$s %2$s (%3$d) انسٹال کریں</string>
|
||||
<string name="download">ڈاؤن لوڈ</string>
|
||||
<string name="reboot">ریبوٹ</string>
|
||||
<string name="close">بند کریں</string>
|
||||
<string name="release_notes">ریلیز نوٹ</string>
|
||||
<string name="flashing">فلیش ہو رہا ہے…</string>
|
||||
<string name="done">ہو گیا!</string>
|
||||
<string name="failure">ناکام!</string>
|
||||
<string name="hide_app_title">Magisk ایپ کو چھپایا جا رہا ہے…</string>
|
||||
<string name="open_link_failed_toast">لنک کھولنے کے لیے کوئی ایپ نہیں ملی</string>
|
||||
<string name="complete_uninstall">مکمل ان انسٹال</string>
|
||||
<string name="restore_img">تصاویر بحال کریں</string>
|
||||
<string name="restore_img_msg">بحال ہو رہا ہے…</string>
|
||||
<string name="restore_done">بحالی مکمل ہو گئی!</string>
|
||||
<string name="restore_fail">اسٹاک بیک اپ موجود نہیں ہے!</string>
|
||||
<string name="setup_fail">سیٹ اپ ناکام ہو گیا</string>
|
||||
<string name="env_full_fix_msg">Magisk کو صحیح طریقے سے کام کرنے کے لیے آپ کے آلے کو دوبارہ فلیش کرنے کی ضرورت ہے۔ براہ کرم ایپ کے اندر Magisk کو دوبارہ انسٹال کریں، ریکوری موڈ درست ڈیوائس کی معلومات حاصل نہیں کر سکتا۔</string>
|
||||
<string name="env_fix_title">اضافی سیٹ اپ درکار ہے</string>
|
||||
<string name="env_fix_msg">Magisk کو صحیح طریقے سے کام کرنے کے لیے آپ کے آلے کو اضافی سیٹ اپ کی ضرورت ہے۔ کیا آپ جاری رکھنا چاہتے ہیں اور ریبوٹ کرنا چاہتے ہیں؟</string>
|
||||
<string name="setup_msg">ماحول کا سیٹ اپ چل رہا ہے…</string>
|
||||
<string name="unsupport_magisk_title">Magisk کا غیر تعاون یافتہ ورژن</string>
|
||||
<string name="unsupport_magisk_msg">ایپ کا یہ ورژن %1$s سے کم Magisk ورژنز کو سپورٹ نہیں کرتا ہے۔\n\nایپ ایسے برتاؤ کرے گی جیسے کوئی Magisk انسٹال نہیں ہے، براہ کرم جلد از جلد Magisk کو اپ گریڈ کریں۔</string>
|
||||
<string name="unsupport_general_title">غیر معمولی حالت</string>
|
||||
<string name="unsupport_system_app_msg">سسٹم ایپ کے طور پر اس ایپ کو چلانا تعاون یافتہ نہیں ہے۔ براہ کرم ایپ کو صارف ایپ میں واپس کریں۔</string>
|
||||
<string name="unsupport_other_su_msg">Magisk کے علاوہ کسی اور "su" بائنری کا پتہ چلا ہے۔ براہ کرم کسی بھی مسابقتی روٹ حل کو ہٹا دیں اور/یا Magisk کو دوبارہ انسٹال کریں۔</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk بیرونی اسٹوریج پر انسٹال ہے۔ براہ کرم ایپ کو اندرونی اسٹوریج میں منتقل کریں۔</string>
|
||||
<string name="unsupport_nonroot_stub_msg">چھپی ہوئی Magisk ایپ کام جاری نہیں رکھ سکتی کیونکہ روٹ ختم ہو گیا ہے۔ براہ کرم اصل APK کو بحال کریں۔</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">اس فعالیت کو فعال کرنے کے لیے اسٹوریج کی اجازت دیں</string>
|
||||
<string name="post_notifications_denied">اس فعالیت کو فعال کرنے کے لیے اطلاعات کی اجازت دیں</string>
|
||||
<string name="install_unknown_denied">اس فعالیت کو فعال کرنے کے لیے "نامعلوم ایپس انسٹال کریں" کی اجازت دیں</string>
|
||||
<string name="add_shortcut_title">ہوم اسکرین پر شارٹ کٹ شامل کریں</string>
|
||||
<string name="add_shortcut_msg">اس ایپ کو چھپانے کے بعد، اس کا نام اور آئیکن پہچاننا مشکل ہو سکتا ہے۔ کیا آپ ہوم اسکرین پر ایک خوبصورت شارٹ کٹ شامل کرنا چاہتے ہیں؟</string>
|
||||
<string name="app_not_found">اس عمل کو سنبھالنے کے لیے کوئی ایپ نہیں ملی</string>
|
||||
<string name="reboot_apply_change">تبدیلیاں لاگو کرنے کے لیے ریبوٹ کریں</string>
|
||||
<string name="restore_app_confirmation">یہ چھپی ہوئی ایپ کو اصل ایپ میں بحال کردے گا۔ کیا آپ واقعی ایسا کرنا چاہتے ہیں؟</string>
|
||||
|
||||
</resources>
|
||||
|
||||
|
@@ -54,6 +54,7 @@
|
||||
<string name="touch_filtered_warning">由于某个应用遮挡了超级用户请求界面,因此 Magisk 无法验证您的回应</string>
|
||||
<string name="deny">拒绝</string>
|
||||
<string name="prompt">提示</string>
|
||||
<string name="restrict">受限</string>
|
||||
<string name="grant">允许</string>
|
||||
<string name="su_warning">将授予对该设备的最高权限。\n如果不确定,请拒绝!</string>
|
||||
<string name="forever">永久</string>
|
||||
@@ -173,6 +174,8 @@
|
||||
<string name="settings_su_auth_title">身份验证</string>
|
||||
<string name="settings_su_auth_summary">对超级用户请求验证身份</string>
|
||||
<string name="settings_su_auth_insecure">设备未配置验证方式</string>
|
||||
<string name="settings_su_restrict_title">限制超级用户权能</string>
|
||||
<string name="settings_su_restrict_summary">默认限制新的超级用户应用。警告,这会破坏大多数应用,不建议启用。</string>
|
||||
<string name="settings_customization">个性化</string>
|
||||
<string name="setting_add_shortcut_summary">在隐藏后难以识别名称和图标的情况下,添加快捷方式到桌面</string>
|
||||
<string name="settings_doh_title">安全 DNS(DoH)</string>
|
||||
|
@@ -63,9 +63,7 @@
|
||||
<string-array name="update_channel">
|
||||
<item>@string/settings_update_stable</item>
|
||||
<item>@string/settings_update_beta</item>
|
||||
<item>@string/settings_update_debug</item>
|
||||
<item>@string/settings_update_custom</item>
|
||||
<item>Canary</item>
|
||||
<item>Debug</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
@@ -19,41 +19,41 @@
|
||||
<string name="hide">Hide</string>
|
||||
<string name="home_package">Package</string>
|
||||
<string name="home_app_title">App</string>
|
||||
|
||||
<string name="home_notice_content">Download Magisk ONLY from the official GitHub page. Files from unknown sources can be malicious!</string>
|
||||
<string name="home_support_title">Support Us</string>
|
||||
<string name="home_follow_title">Follow Us</string>
|
||||
<string name="home_item_source">Source</string>
|
||||
<string name="home_support_content">Magisk is, and always will be, free, and open source. You can however show us that you care by making a donation.</string>
|
||||
<string name="home_support_content">Magisk is, and always will be, free, and open source. However, you can show us that you care by making a donation.</string>
|
||||
<string name="home_installed_version">Installed</string>
|
||||
<string name="home_latest_version">Latest</string>
|
||||
<string name="invalid_update_channel">Invalid Update Channel</string>
|
||||
<string name="invalid_update_channel">Invalid update channel</string>
|
||||
<string name="uninstall_magisk_title">Uninstall Magisk</string>
|
||||
<string name="uninstall_magisk_msg">All modules will be disabled/removed!\nRoot will be removed!\nAny internal storage unencrypted through the use of Magisk will be re-encrypted!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Preserve force encryption</string>
|
||||
<string name="keep_dm_verity">Preserve AVB 2.0/dm-verity</string>
|
||||
<string name="recovery_mode">Recovery Mode</string>
|
||||
<string name="recovery_mode">Recovery mode</string>
|
||||
<string name="install_options_title">Options</string>
|
||||
<string name="install_method_title">Method</string>
|
||||
<string name="install_next">Next</string>
|
||||
<string name="install_start">Let\'s go</string>
|
||||
<string name="manager_download_install">Press to download and install</string>
|
||||
<string name="direct_install">Direct Install (Recommended)</string>
|
||||
<string name="install_inactive_slot">Install to Inactive Slot (After OTA)</string>
|
||||
<string name="direct_install">Direct install (Recommended)</string>
|
||||
<string name="install_inactive_slot">Install to inactive slot (After OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Your device will be FORCED to boot to the current inactive slot after a reboot!\nOnly use this option after OTA is done.\nContinue?</string>
|
||||
<string name="setup_title">Additional Setup</string>
|
||||
<string name="select_patch_file">Select and Patch a File</string>
|
||||
<string name="setup_title">Additional setup</string>
|
||||
<string name="select_patch_file">Select and patch a file</string>
|
||||
<string name="patch_file_msg">Select a raw image (*.img) or an ODIN tarfile (*.tar) or a payload.bin (*.bin)</string>
|
||||
<string name="reboot_delay_toast">Rebooting in 5 seconds…</string>
|
||||
<string name="flash_screen_title">Installation</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Superuser Request</string>
|
||||
<string name="touch_filtered_warning">Because an app is obscuring a Superuser request, Magisk can\'t verify your response</string>
|
||||
<string name="su_request_title">Superuser request</string>
|
||||
<string name="touch_filtered_warning">Because an app is obscuring a Superuser request, Magisk can\'t verify your response.</string>
|
||||
<string name="deny">Deny</string>
|
||||
<string name="prompt">Prompt</string>
|
||||
<string name="restrict">Restrict</string>
|
||||
<string name="grant">Grant</string>
|
||||
<string name="su_warning">Grants full access to your device.\nDeny if you\'re not sure!</string>
|
||||
<string name="forever">Forever</string>
|
||||
@@ -74,25 +74,22 @@
|
||||
<string name="su_revoke_msg">Confirm to revoke %1$s Superuser rights</string>
|
||||
<string name="toast">Toast</string>
|
||||
<string name="none">None</string>
|
||||
|
||||
<string name="superuser_toggle_notification">Notifications</string>
|
||||
<string name="superuser_toggle_revoke">Revoke</string>
|
||||
<string name="superuser_policy_none">No apps have asked for Superuser permission yet.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">You\'re log-free, try using your root apps more</string>
|
||||
<string name="log_data_magisk_none">Magisk logs are empty, that\'s weird</string>
|
||||
<string name="log_data_none">You\'re log-free. Try using your root apps more.</string>
|
||||
<string name="log_data_magisk_none">Magisk logs are empty, that\'s weird.</string>
|
||||
<string name="menuSaveLog">Save log</string>
|
||||
<string name="menuClearLog">Clear log now</string>
|
||||
<string name="logs_cleared">Log successfully cleared</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Target UID: %1$d</string>
|
||||
<string name="target_pid">Mount ns target PID: %s</string>
|
||||
<string name="target_pid">Target PID: %s</string>
|
||||
<string name="selinux_context">SELinux context: %s</string>
|
||||
<string name="supp_group">Supplementary group: %s</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Show system apps</string>
|
||||
<string name="show_os_app">Show OS apps</string>
|
||||
@@ -112,20 +109,20 @@
|
||||
<string name="module_action">Action</string>
|
||||
<string name="module_state_restore">Restore</string>
|
||||
<string name="module_action_install_external">Install from storage</string>
|
||||
<string name="update_available">Update Available</string>
|
||||
<string name="update_available">Update available</string>
|
||||
<string name="suspend_text_riru">Module suspended because %1$s is enabled</string>
|
||||
<string name="suspend_text_zygisk">Module suspended because %1$s is not enabled</string>
|
||||
<string name="suspend_text_zygisk">Module suspended because %1$s isn\'t enabled</string>
|
||||
<string name="zygisk_module_unloaded">Zygisk module not loaded due to incompatibility</string>
|
||||
<string name="module_empty">No module installed</string>
|
||||
<string name="confirm_install">Install module %1$s?</string>
|
||||
<string name="confirm_install_title">Install Confirmation</string>
|
||||
<string name="confirm_install_title">Install confirmation</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Theme Mode</string>
|
||||
<string name="settings_dark_mode_title">Theme mode</string>
|
||||
<string name="settings_dark_mode_message">Select mode which best suits your style!</string>
|
||||
<string name="settings_dark_mode_light">Always Light</string>
|
||||
<string name="settings_dark_mode_system">Follow System</string>
|
||||
<string name="settings_dark_mode_dark">Always Dark</string>
|
||||
<string name="settings_dark_mode_light">Always light</string>
|
||||
<string name="settings_dark_mode_system">Follow system</string>
|
||||
<string name="settings_dark_mode_dark">Always dark</string>
|
||||
<string name="settings_download_path_title">Download path</string>
|
||||
<string name="settings_download_path_message">Files will be saved to %1$s</string>
|
||||
<string name="settings_hide_app_title">Hide the Magisk app</string>
|
||||
@@ -133,15 +130,16 @@
|
||||
<string name="settings_restore_app_title">Restore the Magisk app</string>
|
||||
<string name="settings_restore_app_summary">Unhide the app and restore the original APK</string>
|
||||
<string name="language">Language</string>
|
||||
<string name="system_default">(System Default)</string>
|
||||
<string name="settings_check_update_title">Check Updates</string>
|
||||
<string name="system_default">(System default)</string>
|
||||
<string name="settings_check_update_title">Check for updates</string>
|
||||
<string name="settings_check_update_summary">Periodically check for updates in the background</string>
|
||||
<string name="settings_update_channel_title">Update Channel</string>
|
||||
<string name="settings_update_channel_title">Update channel</string>
|
||||
<string name="settings_update_stable">Stable</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_debug">Debug</string>
|
||||
<string name="settings_update_custom">Custom</string>
|
||||
<string name="settings_update_custom_msg">Insert a custom channel URL</string>
|
||||
<string name="settings_zygisk_summary">Run parts of Magisk in the zygote daemon</string>
|
||||
<string name="settings_zygisk_summary">Run parts of Magisk in the Zygote daemon</string>
|
||||
<string name="settings_denylist_title">Enforce DenyList</string>
|
||||
<string name="settings_denylist_summary">Processes on the denylist will have all Magisk modifications reverted</string>
|
||||
<string name="settings_denylist_config_title">Configure DenyList</string>
|
||||
@@ -162,48 +160,48 @@
|
||||
<string name="settings_su_request_30">30 seconds</string>
|
||||
<string name="settings_su_request_45">45 seconds</string>
|
||||
<string name="settings_su_request_60">60 seconds</string>
|
||||
<string name="superuser_access">Superuser Access</string>
|
||||
<string name="auto_response">Automatic Response</string>
|
||||
<string name="request_timeout">Request Timeout</string>
|
||||
<string name="superuser_notification">Superuser Notification</string>
|
||||
<string name="superuser_access">Superuser access</string>
|
||||
<string name="auto_response">Automatic response</string>
|
||||
<string name="request_timeout">Request timeout</string>
|
||||
<string name="superuser_notification">Superuser notification</string>
|
||||
<string name="settings_su_reauth_title">Reauthenticate after upgrade</string>
|
||||
<string name="settings_su_reauth_summary">Ask for Superuser permissions again after upgrading apps</string>
|
||||
<string name="settings_su_tapjack_title">Tapjacking Protection</string>
|
||||
<string name="settings_su_tapjack_summary">The Superuser prompt dialog will not respond to input while obscured by any other window or overlay</string>
|
||||
<string name="settings_su_auth_title">User Authentication</string>
|
||||
<string name="settings_su_tapjack_title">Tapjacking protection</string>
|
||||
<string name="settings_su_tapjack_summary">The Superuser prompt dialog won\'t respond to input while obscured by any other window or overlay</string>
|
||||
<string name="settings_su_auth_title">User authentication</string>
|
||||
<string name="settings_su_auth_summary">Ask for user authentication during Superuser requests</string>
|
||||
<string name="settings_su_auth_insecure">No authentication method is configured on the device</string>
|
||||
<string name="settings_su_restrict_title">Restrict root capabilities</string>
|
||||
<string name="settings_su_restrict_summary">Will restrict new Superuser apps by default. Warning: this will break most apps. Don\'t enable it unless you know what you\'re doing.</string>
|
||||
<string name="settings_customization">Customization</string>
|
||||
<string name="setting_add_shortcut_summary">Add a pretty shortcut to the home screen in case the name and icon are difficult to recognize after hiding the app</string>
|
||||
<string name="settings_doh_title">DNS over HTTPS</string>
|
||||
<string name="settings_doh_description">Workaround DNS poisoning in some nations</string>
|
||||
<string name="settings_random_name_title">Randomize output name</string>
|
||||
<string name="settings_random_name_description">Randomize the output file name of patched images and tar files to prevent detection</string>
|
||||
|
||||
<string name="multiuser_mode">Multiuser Mode</string>
|
||||
<string name="settings_owner_only">Device Owner Only</string>
|
||||
<string name="settings_owner_manage">Device Owner Managed</string>
|
||||
<string name="settings_user_independent">User-Independent</string>
|
||||
<string name="multiuser_mode">Multiuser mode</string>
|
||||
<string name="settings_owner_only">Device owner only</string>
|
||||
<string name="settings_owner_manage">Device owner managed</string>
|
||||
<string name="settings_user_independent">User-independent</string>
|
||||
<string name="owner_only_summary">Only owner has root access</string>
|
||||
<string name="owner_manage_summary">Only owner can manage root access and receive request prompts</string>
|
||||
<string name="user_independent_summary">Each user has their own separate root rules</string>
|
||||
|
||||
<string name="mount_namespace_mode">Mount Namespace Mode</string>
|
||||
<string name="settings_ns_global">Global Namespace</string>
|
||||
<string name="settings_ns_requester">Inherit Namespace</string>
|
||||
<string name="settings_ns_isolate">Isolated Namespace</string>
|
||||
<string name="mount_namespace_mode">Mount namespace mode</string>
|
||||
<string name="settings_ns_global">Global namespace</string>
|
||||
<string name="settings_ns_requester">Inherit namespace</string>
|
||||
<string name="settings_ns_isolate">Isolated namespace</string>
|
||||
<string name="global_summary">All root sessions use the global mount namespace</string>
|
||||
<string name="requester_summary">Root sessions will inherit their requester\'s namespace</string>
|
||||
<string name="isolate_summary">Each root session will have its own isolated namespace</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Magisk Updates</string>
|
||||
<string name="progress_channel">Progress Notifications</string>
|
||||
<string name="updated_channel">Update Complete</string>
|
||||
<string name="update_channel">Magisk updates</string>
|
||||
<string name="progress_channel">Progress notifications</string>
|
||||
<string name="updated_channel">Update complete</string>
|
||||
<string name="download_complete">Download complete</string>
|
||||
<string name="download_file_error">Error downloading file</string>
|
||||
<string name="magisk_update_title">Magisk Update Available!</string>
|
||||
<string name="updated_title">Magisk Updated</string>
|
||||
<string name="magisk_update_title">Magisk update available!</string>
|
||||
<string name="updated_title">Magisk updated</string>
|
||||
<string name="updated_text">Tap to open app</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
@@ -221,27 +219,27 @@
|
||||
<string name="failure">Failed!</string>
|
||||
<string name="hide_app_title">Hiding the Magisk app…</string>
|
||||
<string name="open_link_failed_toast">No app found to open the link</string>
|
||||
<string name="complete_uninstall">Complete Uninstall</string>
|
||||
<string name="restore_img">Restore Images</string>
|
||||
<string name="complete_uninstall">Complete uninstall</string>
|
||||
<string name="restore_img">Restore images</string>
|
||||
<string name="restore_img_msg">Restoring…</string>
|
||||
<string name="restore_done">Restoration done!</string>
|
||||
<string name="restore_fail">Stock backup does not exist!</string>
|
||||
<string name="restore_fail">Stock backup doesn\'t exist!</string>
|
||||
<string name="setup_fail">Setup failed</string>
|
||||
<string name="env_fix_title">Requires Additional Setup</string>
|
||||
<string name="env_fix_title">Requires additional setup</string>
|
||||
<string name="env_fix_msg">Your device needs additional setup for Magisk to work properly. Do you want to proceed and reboot?</string>
|
||||
<string name="env_full_fix_msg">Your device needs reflash Magisk to work properly. Please reinstall Magisk within app, recovery mode cannot get correct device info.</string>
|
||||
<string name="env_full_fix_msg">Your device needs reflash Magisk to work properly. Please reinstall Magisk within app, Recovery mode cannot get correct device info.</string>
|
||||
<string name="setup_msg">Running environment setup…</string>
|
||||
<string name="unsupport_magisk_title">Unsupported Magisk Version</string>
|
||||
<string name="unsupport_magisk_msg">This version of the app does not support Magisk versions lower than %1$s.\n\nThe app will behave as if no Magisk is installed, please upgrade Magisk as soon as possible.</string>
|
||||
<string name="unsupport_general_title">Abnormal State</string>
|
||||
<string name="unsupport_system_app_msg">Running this app as a system app is not supported. Please revert the app to a user app.</string>
|
||||
<string name="unsupport_magisk_title">Unsupported Magisk version</string>
|
||||
<string name="unsupport_magisk_msg">This version of the app doesn\'t support Magisk versions lower than %1$s.\n\nThe app will behave as if no Magisk is installed. Please update Magisk as soon as possible.</string>
|
||||
<string name="unsupport_general_title">Abnormal state</string>
|
||||
<string name="unsupport_system_app_msg">Running this app as a system app isn\'t supported. Please revert the app to a user app.</string>
|
||||
<string name="unsupport_other_su_msg">A \"su\" binary not from Magisk has been detected. Please remove any competing root solution and/or reinstall Magisk.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk is installed to external storage. Please move the app to internal storage.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">The hidden Magisk app cannot continue to work because root was lost. Please restore the original APK.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Grant storage permission to enable this functionality</string>
|
||||
<string name="post_notifications_denied">Grant notifications permission to enable this functionality</string>
|
||||
<string name="install_unknown_denied">Allow "install unknown apps" to enable this functionality</string>
|
||||
<string name="install_unknown_denied">Allow \"Install unknown apps\" to enable this functionality</string>
|
||||
<string name="add_shortcut_title">Add shortcut to home screen</string>
|
||||
<string name="add_shortcut_msg">After hiding this app, its name and icon might become difficult to recognize. Do you want to add a pretty shortcut to the home screen?</string>
|
||||
<string name="app_not_found">No app found to handle this action</string>
|
||||
|
@@ -30,5 +30,4 @@ android.nonFinalResIds=false
|
||||
|
||||
# Magisk
|
||||
magisk.stubVersion=40
|
||||
magisk.versionCode=28100
|
||||
magisk.ondkVersion=r27.4
|
||||
magisk.versionCode=30200
|
@@ -1,18 +1,17 @@
|
||||
[versions]
|
||||
kotlin = "2.0.21"
|
||||
android = "8.7.3"
|
||||
ksp = "2.0.21-1.0.28"
|
||||
kotlin = "2.2.0"
|
||||
android = "8.12.0"
|
||||
ksp = "2.2.0-2.0.2"
|
||||
rikka = "1.3.0"
|
||||
navigation = "2.8.4"
|
||||
navigation = "2.9.3"
|
||||
libsu = "6.0.0"
|
||||
moshi = "1.15.1"
|
||||
okhttp = "4.12.0"
|
||||
retrofit = "2.11.0"
|
||||
room = "2.6.1"
|
||||
okhttp = "5.1.0"
|
||||
retrofit = "3.0.0"
|
||||
room = "2.7.2"
|
||||
|
||||
[libraries]
|
||||
bcpkix = { module = "org.bouncycastle:bcpkix-jdk18on", version = "1.79" }
|
||||
commons-compress = { module = "org.apache.commons:commons-compress", version = "1.27.1" }
|
||||
bcpkix = { module = "org.bouncycastle:bcpkix-jdk18on", version = "1.81" }
|
||||
commons-compress = { module = "org.apache.commons:commons-compress", version = "1.28.0" }
|
||||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||
retrofit-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofit" }
|
||||
retrofit-scalars = { module = "com.squareup.retrofit2:converter-scalars", version.ref = "retrofit" }
|
||||
@@ -20,31 +19,32 @@ markwon-core = { module = "io.noties.markwon:core", version = "4.6.2" }
|
||||
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||
okhttp-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp" }
|
||||
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
|
||||
moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
|
||||
moshi-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }
|
||||
timber = { module = "com.jakewharton.timber:timber", version = "5.0.1" }
|
||||
jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version = "6.10.0.202406032230-r" }
|
||||
jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version = "7.1.0.202411261347-r" }
|
||||
|
||||
# AndroidX
|
||||
activity = { module = "androidx.activity:activity", version = "1.9.3" }
|
||||
appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.0" }
|
||||
core-ktx = { module = "androidx.core:core-ktx", version = "1.15.0" }
|
||||
activity = { module = "androidx.activity:activity", version = "1.10.1" }
|
||||
appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.1" }
|
||||
core-ktx = { module = "androidx.core:core-ktx", version = "1.16.0" }
|
||||
core-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.0.1" }
|
||||
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.2.0" }
|
||||
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.5" }
|
||||
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.2.1" }
|
||||
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.8" }
|
||||
navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigation" }
|
||||
navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigation" }
|
||||
profileinstaller = { module = "androidx.profileinstaller:profileinstaller", version = "1.4.1" }
|
||||
recyclerview = { module = "androidx.recyclerview:recyclerview", version = "1.3.2" }
|
||||
recyclerview = { module = "androidx.recyclerview:recyclerview", version = "1.4.0" }
|
||||
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
||||
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||
swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version = "1.1.0" }
|
||||
transition = { module = "androidx.transition:transition", version = "1.5.1" }
|
||||
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.4.5" }
|
||||
lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version = "2.8.7" }
|
||||
transition = { module = "androidx.transition:transition", version = "1.6.0" }
|
||||
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.5.0" }
|
||||
material = { module = "com.google.android.material:material", version = "1.12.0" }
|
||||
jdk-libs = { module = "com.android.tools:desugar_jdk_libs_nio", version = "2.1.3" }
|
||||
jdk-libs = { module = "com.android.tools:desugar_jdk_libs_nio", version = "2.1.5" }
|
||||
test-runner = { module = "androidx.test:runner", version = "1.7.0" }
|
||||
test-rules = { module = "androidx.test:rules", version = "1.7.0" }
|
||||
test-junit = { module = "androidx.test.ext:junit", version = "1.3.0" }
|
||||
test-uiautomator = { module = "androidx.test.uiautomator:uiautomator", version = "2.3.0" }
|
||||
|
||||
# topjohnwu
|
||||
indeterminate-checkbox = { module = "com.github.topjohnwu:indeterminate-checkbox", version = "1.0.7" }
|
||||
@@ -62,5 +62,6 @@ android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref
|
||||
ksp-plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
|
||||
navigation-safe-args-plugin = { module = "androidx.navigation:navigation-safe-args-gradle-plugin", version.ref = "navigation" }
|
||||
lsparanoid-plugin = { module = "org.lsposed.lsparanoid:gradle-plugin", version = "0.6.0" }
|
||||
moshi-plugin = { module = "dev.zacsweers.moshix:dev.zacsweers.moshix.gradle.plugin", version = "0.31.0" }
|
||||
|
||||
[plugins]
|
BIN
app/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
app/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
9
gradlew → app/gradlew
vendored
9
gradlew → app/gradlew
vendored
@@ -86,8 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -115,7 +114,7 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -206,7 +205,7 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
@@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
4
gradlew.bat → app/gradlew.bat
vendored
4
gradlew.bat → app/gradlew.bat
vendored
@@ -70,11 +70,11 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user