mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-14 07:07:27 +00:00
Compare commits
187 Commits
manager-v8
...
v22.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
483dbcdc40 | ||
![]() |
a1096b5bf0 | ||
![]() |
5ac0e64edb | ||
![]() |
60b2624607 | ||
![]() |
d2e2847b03 | ||
![]() |
b9669f54f7 | ||
![]() |
8c7bd77d33 | ||
![]() |
ba1ce16b8b | ||
![]() |
68090943f4 | ||
![]() |
a4fb1297b0 | ||
![]() |
860a05abf2 | ||
![]() |
8bb2f356c0 | ||
![]() |
4950020635 | ||
![]() |
0a6140c6eb | ||
![]() |
bba2ac8817 | ||
![]() |
331b1f542f | ||
![]() |
ccb55205e6 | ||
![]() |
9cc91b30b3 | ||
![]() |
e836caf31e | ||
![]() |
beaa1e5be2 | ||
![]() |
ea545bae26 | ||
![]() |
1c9ec2df45 | ||
![]() |
b76c80e2ce | ||
![]() |
236990f4a3 | ||
![]() |
1ed32df20d | ||
![]() |
8476eb9f4b | ||
![]() |
735af7843b | ||
![]() |
ded73e958b | ||
![]() |
6dcb84d4f4 | ||
![]() |
501bc9f438 | ||
![]() |
f88e812b63 | ||
![]() |
be6386c410 | ||
![]() |
2af4fd17c4 | ||
![]() |
f870418bd0 | ||
![]() |
00659e4795 | ||
![]() |
cdda10207e | ||
![]() |
701700279f | ||
![]() |
a9d804724a | ||
![]() |
e033a9ab47 | ||
![]() |
059e5fb8aa | ||
![]() |
a78f255928 | ||
![]() |
1d10e69288 | ||
![]() |
63590d379c | ||
![]() |
5f63e88984 | ||
![]() |
75584e2b19 | ||
![]() |
1426ee2ebd | ||
![]() |
b6643b7bfc | ||
![]() |
721dfdf553 | ||
![]() |
2963747d14 | ||
![]() |
e7350d5041 | ||
![]() |
f37e8f4ca8 | ||
![]() |
594c2accc0 | ||
![]() |
7acfac6a91 | ||
![]() |
0646f48e14 | ||
![]() |
37565fd067 | ||
![]() |
26b2e7dc5d | ||
![]() |
c3313623e4 | ||
![]() |
2089223690 | ||
![]() |
52e1b84d41 | ||
![]() |
8794141b7f | ||
![]() |
f6126dd20e | ||
![]() |
18acfda99b | ||
![]() |
bec5edca84 | ||
![]() |
6fb20b3ee5 | ||
![]() |
eaf4d8064b | ||
![]() |
2a5f5b1bba | ||
![]() |
c538a77937 | ||
![]() |
aa9e7b1ed1 | ||
![]() |
a3066eddab | ||
![]() |
d1729fa787 | ||
![]() |
93961dde2c | ||
![]() |
1024e68eb6 | ||
![]() |
6ae2c9387d | ||
![]() |
fba83e2330 | ||
![]() |
f1295cb7d6 | ||
![]() |
dc61dfbde6 | ||
![]() |
21466426da | ||
![]() |
3f0136362b | ||
![]() |
e92d77bbec | ||
![]() |
07bd36c94b | ||
![]() |
b1dbbdef12 | ||
![]() |
3e479726ec | ||
![]() |
4cc41eccb3 | ||
![]() |
8f08ae59ac | ||
![]() |
e8d4e492d6 | ||
![]() |
b8090a8e18 | ||
![]() |
c609a01e55 | ||
![]() |
c97fb385cd | ||
![]() |
da6c57750e | ||
![]() |
6951d926f7 | ||
![]() |
6623195bd5 | ||
![]() |
ec31bb9a82 | ||
![]() |
8618cc383a | ||
![]() |
4b01e3a3c7 | ||
![]() |
7f748c23c1 | ||
![]() |
963d248cc7 | ||
![]() |
657056e636 | ||
![]() |
9d5efea66e | ||
![]() |
658d74e026 | ||
![]() |
5113f6d375 | ||
![]() |
96405c26d0 | ||
![]() |
4ea5f34bf3 | ||
![]() |
dbd13a2019 | ||
![]() |
06773235da | ||
![]() |
e57556a8af | ||
![]() |
b54b78c29d | ||
![]() |
317336f771 | ||
![]() |
b4e52f6135 | ||
![]() |
f2ca042915 | ||
![]() |
1060dd2906 | ||
![]() |
2e0f7a82fa | ||
![]() |
5798536559 | ||
![]() |
ab9a83c82f | ||
![]() |
c87fdbea0f | ||
![]() |
ec8fffe61c | ||
![]() |
61d52991f1 | ||
![]() |
9100186dce | ||
![]() |
d2bc2cfcf8 | ||
![]() |
5a71998b4e | ||
![]() |
42278f12ff | ||
![]() |
f5593e051c | ||
![]() |
a27e30cf54 | ||
![]() |
79140c7636 | ||
![]() |
1f4c595cd3 | ||
![]() |
b5b62e03af | ||
![]() |
67e2a4720e | ||
![]() |
f5c2d72429 | ||
![]() |
2f5331ab48 | ||
![]() |
7f8257152f | ||
![]() |
0cd80f2556 | ||
![]() |
1717387876 | ||
![]() |
109363ebf6 | ||
![]() |
716c4fa386 | ||
![]() |
9a09b4eb20 | ||
![]() |
95a5b57265 | ||
![]() |
13fbf397d1 | ||
![]() |
20be99ec8a | ||
![]() |
04c53c3578 | ||
![]() |
51bc27a869 | ||
![]() |
71b083794c | ||
![]() |
b100d0c503 | ||
![]() |
76061296c9 | ||
![]() |
bb303d2da1 | ||
![]() |
c91c070343 | ||
![]() |
aec06a6f61 | ||
![]() |
e8ba671fc2 | ||
![]() |
1860e5d133 | ||
![]() |
f2cb3c38fe | ||
![]() |
9a28dd4f6e | ||
![]() |
d2acd59ea8 | ||
![]() |
79dfdb29e7 | ||
![]() |
eb21c8b42e | ||
![]() |
541bb53553 | ||
![]() |
fe8997efae | ||
![]() |
23455c722c | ||
![]() |
5ce29c30d2 | ||
![]() |
70d67728fd | ||
![]() |
e546884b08 | ||
![]() |
b36e6d987d | ||
![]() |
53c3dd5e8b | ||
![]() |
da723b207a | ||
![]() |
e050f77198 | ||
![]() |
540b4b7ea9 | ||
![]() |
bbef22daf7 | ||
![]() |
9ed110c91b | ||
![]() |
a30d510eb1 | ||
![]() |
ef98eaed8f | ||
![]() |
2a257f327c | ||
![]() |
4060c2107c | ||
![]() |
cd23d27048 | ||
![]() |
18b86e4fd2 | ||
![]() |
5f2e22a259 | ||
![]() |
4e97b18977 | ||
![]() |
f9bde347bc | ||
![]() |
947a7d6a2f | ||
![]() |
872ab2e99b | ||
![]() |
90b8813bb7 | ||
![]() |
88d0f63294 | ||
![]() |
79fa0d3a90 | ||
![]() |
8e61080a4a | ||
![]() |
3f9a64417b | ||
![]() |
eb959379e8 | ||
![]() |
41a644afb9 | ||
![]() |
6b42db943d | ||
![]() |
1c325459eb | ||
![]() |
6d88d8ad95 | ||
![]() |
246997f273 |
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## READ BEFORE OPENING ISSUES
|
||||
|
||||
All bug reports require you to **USE CANARY BUILDS**. Please include the version name and version code in the bug report.
|
||||
|
||||
If you experience a bootloop, attach a `dmesg` (kernel logs) when the device refuse to boot. This may very likely require a custom kernel on some devices as `last_kmsg` or `pstore ramoops` are usually not enabled by default. In addition, please also upload the result of `cat /proc/mounts` when your device is working correctly **WITHOUT ROOT**.
|
||||
|
||||
If you experience issues during installation, in recovery, upload the recovery logs, or in Magisk Manager, upload the install logs. Please also upload the `boot.img` or `recovery.img` that you are using for patching.
|
||||
|
||||
If you experience a crash of Magisk Manager, dump the full `logcat` **when the crash happens**. **DO NOT** upload `magisk.log`.
|
||||
|
||||
If you experience other issues related to Magisk, upload `magisk.log`, and preferably also include a boot `logcat` (start dumping `logcat` when the device boots up)
|
||||
|
||||
**DO NOT** open issues regarding root detection.
|
||||
|
||||
**DO NOT** ask for instructions.
|
||||
|
||||
**DO NOT** report issues if you have any modules installed.
|
||||
|
||||
Without following the rules above, your issue will be closed without explanation.
|
15
.github/workflows/build.yml
vendored
15
.github/workflows/build.yml
vendored
@@ -3,6 +3,13 @@ name: Magisk Build
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- 'app/**'
|
||||
- 'native/**'
|
||||
- 'stub/**'
|
||||
- 'buildSrc/**'
|
||||
- 'build.py'
|
||||
- 'gradle.properties'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
@@ -36,13 +43,10 @@ jobs:
|
||||
- name: Set up GitHub env (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
$oldAndroidPath = $env:ANDROID_SDK_ROOT
|
||||
$sdk_root = "C:\Android"
|
||||
New-Item -Path $sdk_root -ItemType SymbolicLink -Value $oldAndroidPath
|
||||
$ndk_ver = Select-String -Path "gradle.properties" -Pattern "^magisk.fullNdkVersion=" | % { $_ -replace ".*=" }
|
||||
echo "ANDROID_SDK_ROOT=$sdk_root" >> $env:GITHUB_ENV
|
||||
echo "ANDROID_HOME=$sdk_root" >> $env:GITHUB_ENV
|
||||
echo "ANDROID_SDK_ROOT=$env:ANDROID_SDK_ROOT" >> $env:GITHUB_ENV
|
||||
echo "MAGISK_NDK_VERSION=$ndk_ver" >> $env:GITHUB_ENV
|
||||
echo "GRADLE_OPTS=-Dorg.gradle.daemon=false" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Set up GitHub env (Unix)
|
||||
if: runner.os != 'Windows'
|
||||
@@ -57,7 +61,6 @@ jobs:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
!~/.gradle/caches/**/*.lock
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||
restore-keys: ${{ runner.os }}-gradle-
|
||||
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -25,6 +25,9 @@
|
||||
[submodule "pcre"]
|
||||
path = native/jni/external/pcre
|
||||
url = https://android.googlesource.com/platform/external/pcre
|
||||
[submodule "xhook"]
|
||||
path = native/jni/external/xhook
|
||||
url = https://github.com/iqiyi/xHook.git
|
||||
[submodule "termux-elf-cleaner"]
|
||||
path = tools/termux-elf-cleaner
|
||||
url = https://github.com/termux/termux-elf-cleaner.git
|
||||
|
10
README.MD
10
README.MD
@@ -15,11 +15,13 @@ Here are some feature highlights:
|
||||
|
||||
## Downloads
|
||||
|
||||
[](https://github.com/topjohnwu/Magisk/releases/download/manager-v8.0.3/MagiskManager-v8.0.3.apk)
|
||||
Please note that the only source of official Magisk information and downloads is [Github](https://github.com/topjohnwu/Magisk/). Beware of any other websites.
|
||||
|
||||
[](https://github.com/topjohnwu/Magisk/releases/download/manager-v8.0.7/MagiskManager-v8.0.7.apk)
|
||||
[](https://raw.githubusercontent.com/topjohnwu/magisk_files/canary/app-debug.apk)
|
||||
<br>
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v20.4)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v21.1)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v21.4)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v21.4)
|
||||
|
||||
## Useful Links
|
||||
|
||||
@@ -60,7 +62,7 @@ For Magisk Manager crashes, record and upload the logcat when the crash occurs.
|
||||
- Run `./build.py ndk` to let the script download and install NDK for you
|
||||
- To start building, run `build.py` to see your options. \
|
||||
For each action, use `-h` to access help (e.g. `./build.py all -h`)
|
||||
- To start development, open the project in Android Studio. Both app (Kotlin/Java) and native (C++/C) source code can be properly developed using the IDE, but *always* use `build.py` for building.
|
||||
- To start development, open the project with Android Studio. The IDE can be used for both app (Kotlin/Java) and native (C++/C) sources.
|
||||
- Optionally, set custom configs with `config.prop`. A sample `config.prop.sample` is provided.
|
||||
- To sign APKs and zips with your own private keys, set signing configs in `config.prop`. For more info, check [Google's Documentation](https://developer.android.com/studio/publish/app-signing.html#generate-key).
|
||||
|
||||
|
5
app/.gitignore
vendored
5
app/.gitignore
vendored
@@ -6,6 +6,7 @@
|
||||
app/release
|
||||
*.hprof
|
||||
.externalNativeBuild/
|
||||
public.certificate.x509.pem
|
||||
private.key.pk8
|
||||
*.apk
|
||||
src/main/assets
|
||||
src/main/jniLibs
|
||||
src/main/resources
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import org.apache.tools.ant.filters.FixCrLfFilter
|
||||
import java.io.PrintStream
|
||||
|
||||
plugins {
|
||||
@@ -22,8 +23,9 @@ android {
|
||||
applicationId = "com.topjohnwu.magisk"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
multiDexEnabled = true
|
||||
versionName = Config.appVersion
|
||||
versionCode = Config.appVersionCode
|
||||
versionName = Config.version
|
||||
versionCode = Config.versionCode
|
||||
ndk.abiFilters("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||
|
||||
javaCompileOptions.annotationProcessorOptions.arguments(
|
||||
mapOf("room.incremental" to "true")
|
||||
@@ -51,13 +53,14 @@ android {
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude("/META-INF/**")
|
||||
exclude("/META-INF/*")
|
||||
exclude("/org/bouncycastle/**")
|
||||
exclude("/kotlin/**")
|
||||
exclude("/kotlinx/**")
|
||||
exclude("/okhttp3/**")
|
||||
exclude("/*.txt")
|
||||
exclude("/*.bin")
|
||||
doNotStrip("**/*.so")
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
@@ -65,10 +68,83 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
tasks["preBuild"]?.dependsOn(tasks.register("copyUtils", Copy::class) {
|
||||
from(rootProject.file("scripts/util_functions.sh"))
|
||||
into("src/main/res/raw")
|
||||
})
|
||||
val syncLibs by tasks.registering(Sync::class) {
|
||||
into("src/main/jniLibs")
|
||||
into("armeabi-v7a") {
|
||||
from(rootProject.file("native/out/armeabi-v7a")) {
|
||||
include("busybox", "magiskboot", "magiskinit", "magisk")
|
||||
rename { if (it == "magisk") "libmagisk32.so" else "lib$it.so" }
|
||||
}
|
||||
from(rootProject.file("native/out/arm64-v8a")) {
|
||||
include("magisk")
|
||||
rename { if (it == "magisk") "libmagisk64.so" else "lib$it.so" }
|
||||
}
|
||||
}
|
||||
into("x86") {
|
||||
from(rootProject.file("native/out/x86")) {
|
||||
include("busybox", "magiskboot", "magiskinit", "magisk")
|
||||
rename { if (it == "magisk") "libmagisk32.so" else "lib$it.so" }
|
||||
}
|
||||
from(rootProject.file("native/out/x86_64")) {
|
||||
include("magisk")
|
||||
rename { if (it == "magisk") "libmagisk64.so" else "lib$it.so" }
|
||||
}
|
||||
}
|
||||
onlyIf {
|
||||
if (inputs.sourceFiles.files.size != 10)
|
||||
throw StopExecutionException("Please build binaries first! (./build.py binary)")
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
val createStubLibs by tasks.registering {
|
||||
dependsOn(syncLibs)
|
||||
doLast {
|
||||
val arm64 = project.file("src/main/jniLibs/arm64-v8a/libstub.so")
|
||||
arm64.parentFile.mkdirs()
|
||||
arm64.createNewFile()
|
||||
val x64 = project.file("src/main/jniLibs/x86_64/libstub.so")
|
||||
x64.parentFile.mkdirs()
|
||||
x64.createNewFile()
|
||||
}
|
||||
}
|
||||
|
||||
val syncAssets by tasks.registering(Sync::class) {
|
||||
dependsOn(createStubLibs)
|
||||
inputs.property("version", Config.version)
|
||||
inputs.property("versionCode", Config.versionCode)
|
||||
into("src/main/assets")
|
||||
from(rootProject.file("scripts")) {
|
||||
include("util_functions.sh", "boot_patch.sh", "uninstaller.sh", "addon.d.sh")
|
||||
}
|
||||
into("chromeos") {
|
||||
from(rootProject.file("tools/futility"))
|
||||
from(rootProject.file("tools/keys")) {
|
||||
include("kernel_data_key.vbprivk", "kernel.keyblock")
|
||||
}
|
||||
}
|
||||
filesMatching("**/util_functions.sh") {
|
||||
filter {
|
||||
it.replace("#MAGISK_VERSION_STUB",
|
||||
"MAGISK_VER='${Config.version}'\n" +
|
||||
"MAGISK_VER_CODE=${Config.versionCode}")
|
||||
}
|
||||
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
}
|
||||
}
|
||||
|
||||
val syncResources by tasks.registering(Sync::class) {
|
||||
dependsOn(syncAssets)
|
||||
into("src/main/resources/META-INF/com/google/android")
|
||||
from(rootProject.file("scripts/update_binary.sh")) {
|
||||
rename { "update-binary" }
|
||||
}
|
||||
from(rootProject.file("scripts/flash_script.sh")) {
|
||||
rename { "updater-script" }
|
||||
}
|
||||
}
|
||||
|
||||
tasks["preBuild"]?.dependsOn(syncResources)
|
||||
|
||||
android.applicationVariants.all {
|
||||
val keysDir = rootProject.file("tools/keys")
|
||||
@@ -125,13 +201,13 @@ dependencies {
|
||||
implementation("${bindingAdapter}:${vBAdapt}")
|
||||
implementation("${bindingAdapter}-recyclerview:${vBAdapt}")
|
||||
|
||||
val vMarkwon = "4.6.0"
|
||||
val vMarkwon = "4.6.1"
|
||||
implementation("io.noties.markwon:core:${vMarkwon}")
|
||||
implementation("io.noties.markwon:html:${vMarkwon}")
|
||||
implementation("io.noties.markwon:image:${vMarkwon}")
|
||||
implementation("com.caverock:androidsvg:1.4")
|
||||
|
||||
val vLibsu = "3.0.2"
|
||||
val vLibsu = "3.1.1"
|
||||
implementation("com.github.topjohnwu.libsu:core:${vLibsu}")
|
||||
implementation("com.github.topjohnwu.libsu:io:${vLibsu}")
|
||||
|
||||
@@ -158,7 +234,7 @@ dependencies {
|
||||
implementation("com.squareup.moshi:moshi:${vMoshi}")
|
||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:${vMoshi}")
|
||||
|
||||
val vRoom = "2.3.0-alpha04"
|
||||
val vRoom = "2.3.0-beta01"
|
||||
implementation("androidx.room:room-runtime:${vRoom}")
|
||||
implementation("androidx.room:room-ktx:${vRoom}")
|
||||
kapt("androidx.room:room-compiler:${vRoom}")
|
||||
@@ -167,17 +243,16 @@ dependencies {
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:${vNav}")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:${vNav}")
|
||||
|
||||
implementation("androidx.biometric:biometric:1.0.1")
|
||||
implementation("androidx.biometric:biometric:1.1.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("androidx.browser:browser:1.3.0")
|
||||
implementation("androidx.preference:preference:1.1.1")
|
||||
implementation("androidx.recyclerview:recyclerview:1.1.0")
|
||||
implementation("androidx.fragment:fragment-ktx:1.2.5")
|
||||
implementation("androidx.work:work-runtime-ktx:2.4.0")
|
||||
implementation("androidx.transition:transition:1.3.1")
|
||||
implementation("androidx.work:work-runtime-ktx:2.5.0")
|
||||
implementation("androidx.transition:transition:1.4.0")
|
||||
implementation("androidx.multidex:multidex:2.0.1")
|
||||
implementation("androidx.core:core-ktx:1.3.2")
|
||||
implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
|
||||
implementation("com.google.android.material:material:1.2.1")
|
||||
implementation("com.google.android.material:material:1.3.0")
|
||||
}
|
||||
|
17
app/proguard-rules.pro
vendored
17
app/proguard-rules.pro
vendored
@@ -18,16 +18,10 @@
|
||||
|
||||
# Kotlin
|
||||
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
||||
public static void checkExpressionValueIsNotNull(...);
|
||||
public static void checkNotNullExpressionValue(...);
|
||||
public static void checkReturnedValueIsNotNull(...);
|
||||
public static void checkFieldIsNotNull(...);
|
||||
public static void checkParameterIsNotNull(...);
|
||||
public static void check*(...);
|
||||
public static void throw*(...);
|
||||
}
|
||||
|
||||
# Stubs
|
||||
-keep class a.* { *; }
|
||||
|
||||
# Snet
|
||||
-keepclassmembers class com.topjohnwu.magisk.ui.safetynet.SafetyNetHelper { *; }
|
||||
-keep,allowobfuscation interface com.topjohnwu.magisk.ui.safetynet.SafetyNetHelper$Callback
|
||||
@@ -35,14 +29,17 @@
|
||||
void onResponse(org.json.JSONObject);
|
||||
}
|
||||
|
||||
# Stub
|
||||
-keep class com.topjohnwu.magisk.core.App { <init>(java.lang.Object); }
|
||||
|
||||
# Strip Timber verbose and debug logging
|
||||
-assumenosideeffects class timber.log.Timber.Tree {
|
||||
-assumenosideeffects class timber.log.Timber$Tree {
|
||||
public void v(**);
|
||||
public void d(**);
|
||||
}
|
||||
|
||||
# Excessive obfuscation
|
||||
-repackageclasses
|
||||
-repackageclasses 'a'
|
||||
-allowaccessmodification
|
||||
|
||||
# QOL
|
||||
|
@@ -1,17 +1,27 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.topjohnwu.shared">
|
||||
package="com.topjohnwu.shared"
|
||||
android:installLocation="internalOnly">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission
|
||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
|
||||
<application
|
||||
android:label="Magisk Manager"
|
||||
android:installLocation="internalOnly"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:allowBackup="false"
|
||||
android:label="Magisk"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
tools:ignore="UnusedAttribute">
|
||||
</application>
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
</manifest>
|
||||
|
@@ -1,7 +1,10 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
@@ -10,20 +13,36 @@ import com.topjohnwu.magisk.FileProvider;
|
||||
import java.io.File;
|
||||
|
||||
public class APKInstall {
|
||||
|
||||
public static Intent installIntent(Context c, File apk) {
|
||||
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
intent.setData(FileProvider.getUriForFile(c, c.getPackageName() + ".provider", apk));
|
||||
} else {
|
||||
//noinspection ResultOfMethodCallIgnored SetWorldReadable
|
||||
apk.setReadable(true, false);
|
||||
intent.setData(Uri.fromFile(apk));
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static void install(Context c, File apk) {
|
||||
c.startActivity(installIntent(c, apk));
|
||||
}
|
||||
|
||||
public static Intent installIntent(Context c, File apk) {
|
||||
Intent install = new Intent(Intent.ACTION_INSTALL_PACKAGE);
|
||||
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
install.setData(FileProvider.getUriForFile(c, c.getPackageName() + ".provider", apk));
|
||||
} else {
|
||||
apk.setReadable(true, false);
|
||||
install.setData(Uri.fromFile(apk));
|
||||
}
|
||||
return install;
|
||||
public static void registerInstallReceiver(Context c, BroadcastReceiver r) {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addDataScheme("package");
|
||||
c.getApplicationContext().registerReceiver(r, filter);
|
||||
}
|
||||
|
||||
public static void installHideResult(Activity c, File apk) {
|
||||
Intent intent = installIntent(c, apk);
|
||||
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
|
||||
c.startActivityForResult(intent, 0); // Ignore result, use install receiver
|
||||
}
|
||||
}
|
||||
|
@@ -3,21 +3,18 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.topjohnwu.magisk">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
|
||||
<application
|
||||
android:name=".core.App"
|
||||
android:extractNativeLibs="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:name="a.e"
|
||||
android:allowBackup="false"
|
||||
android:multiArch="true"
|
||||
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
|
||||
|
||||
<!-- Splash -->
|
||||
<activity
|
||||
android:name="a.c"
|
||||
android:name=".core.SplashActivity"
|
||||
android:theme="@style/SplashTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@@ -30,27 +27,27 @@
|
||||
</activity>
|
||||
|
||||
<!-- Main -->
|
||||
<activity android:name="a.b" />
|
||||
<activity android:name=".ui.MainActivity" />
|
||||
|
||||
<!-- Superuser -->
|
||||
<activity
|
||||
android:name="a.m"
|
||||
android:name=".ui.surequest.SuRequestActivity"
|
||||
android:directBootAware="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="false"
|
||||
tools:ignore="AppLinkUrlError">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Receiver -->
|
||||
<receiver
|
||||
android:name="a.h"
|
||||
android:directBootAware="true">
|
||||
android:name=".core.Receiver"
|
||||
android:directBootAware="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.REBOOT" />
|
||||
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
@@ -62,16 +59,15 @@
|
||||
</receiver>
|
||||
|
||||
<!-- DownloadService -->
|
||||
<service android:name="a.j" />
|
||||
<service android:name=".core.download.DownloadService" />
|
||||
|
||||
<!-- FileProvider -->
|
||||
<provider
|
||||
android:name="a.p"
|
||||
android:name=".core.Provider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:directBootAware="true"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
</provider>
|
||||
android:grantUriPermissions="true" />
|
||||
|
||||
<!-- Hardcode GMS version -->
|
||||
<meta-data
|
||||
@@ -82,16 +78,12 @@
|
||||
<provider
|
||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||
android:authorities="${applicationId}.workmanager-init"
|
||||
tools:node="remove" />
|
||||
tools:node="remove"
|
||||
tools:ignore="ExportedContentProvider" />
|
||||
|
||||
<!-- We don't invalidate Room -->
|
||||
<service
|
||||
android:name="androidx.room.MultiInstanceInvalidationService"
|
||||
tools:node="remove"/>
|
||||
|
||||
<!-- We don't use Device Credentials -->
|
||||
<activity
|
||||
android:name="androidx.biometric.DeviceCredentialHandlerActivity"
|
||||
tools:node="remove" />
|
||||
|
||||
</application>
|
||||
|
@@ -1,32 +0,0 @@
|
||||
@file:JvmName("a")
|
||||
package a
|
||||
|
||||
import com.topjohnwu.magisk.core.App
|
||||
import com.topjohnwu.magisk.core.Provider
|
||||
import com.topjohnwu.magisk.core.Receiver
|
||||
import com.topjohnwu.magisk.core.SplashActivity
|
||||
import com.topjohnwu.magisk.core.download.DownloadService
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
||||
import com.topjohnwu.signing.SignBoot
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
SignBoot.main(args)
|
||||
}
|
||||
|
||||
class b : MainActivity()
|
||||
|
||||
class c : SplashActivity()
|
||||
|
||||
class e : App {
|
||||
constructor() : super()
|
||||
constructor(o: Any) : super(o)
|
||||
}
|
||||
|
||||
class h : Receiver()
|
||||
|
||||
class j : DownloadService()
|
||||
|
||||
class m : SuRequestActivity()
|
||||
|
||||
class p : Provider()
|
@@ -41,6 +41,11 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
activity.supportActionBar?.subtitle = null
|
||||
}
|
||||
|
||||
override fun onEventDispatched(event: ViewEvent) = when(event) {
|
||||
is ContextExecutor -> event(requireContext())
|
||||
is ActivityExecutor -> event(activity)
|
||||
|
@@ -10,8 +10,9 @@ import androidx.multidex.MultiDex
|
||||
import androidx.work.WorkManager
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.DynAPK
|
||||
import com.topjohnwu.magisk.core.utils.AppShellInit
|
||||
import com.topjohnwu.magisk.core.utils.BusyBoxInit
|
||||
import com.topjohnwu.magisk.core.utils.IODispatcherExecutor
|
||||
import com.topjohnwu.magisk.core.utils.RootInit
|
||||
import com.topjohnwu.magisk.core.utils.updateConfig
|
||||
import com.topjohnwu.magisk.di.koinModules
|
||||
import com.topjohnwu.magisk.ktx.unwrap
|
||||
@@ -19,6 +20,7 @@ import com.topjohnwu.superuser.Shell
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.context.startKoin
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
open class App() : Application() {
|
||||
@@ -31,7 +33,7 @@ open class App() : Application() {
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
Shell.setDefaultBuilder(Shell.Builder.create()
|
||||
.setFlags(Shell.FLAG_MOUNT_MASTER)
|
||||
.setInitializers(RootInit::class.java)
|
||||
.setInitializers(BusyBoxInit::class.java, AppShellInit::class.java)
|
||||
.setTimeout(2))
|
||||
Shell.EXECUTOR = IODispatcherExecutor()
|
||||
|
||||
@@ -61,12 +63,18 @@ open class App() : Application() {
|
||||
val wrapped = impl.wrap()
|
||||
super.attachBaseContext(wrapped)
|
||||
|
||||
val info = base.applicationInfo
|
||||
val libDir = runCatching {
|
||||
info.javaClass.getDeclaredField("secondaryNativeLibraryDir").get(info) as String?
|
||||
}.getOrNull() ?: info.nativeLibraryDir
|
||||
Const.NATIVE_LIB_DIR = File(libDir)
|
||||
|
||||
// Normal startup
|
||||
startKoin {
|
||||
androidContext(wrapped)
|
||||
modules(koinModules)
|
||||
}
|
||||
ResMgr.init(impl)
|
||||
AssetHack.init(impl)
|
||||
app.registerActivityLifecycleCallbacks(ForegroundTracker)
|
||||
WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build())
|
||||
}
|
||||
|
@@ -1,13 +1,31 @@
|
||||
package com.topjohnwu.magisk.core
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Process
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import java.io.File
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
object Const {
|
||||
|
||||
val CPU_ABI: String
|
||||
val CPU_ABI_32: String
|
||||
|
||||
init {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
CPU_ABI = Build.SUPPORTED_ABIS[0]
|
||||
CPU_ABI_32 = Build.SUPPORTED_32_BIT_ABIS.firstOrNull() ?: CPU_ABI
|
||||
} else {
|
||||
CPU_ABI = Build.CPU_ABI
|
||||
CPU_ABI_32 = CPU_ABI
|
||||
}
|
||||
}
|
||||
|
||||
// Paths
|
||||
lateinit var MAGISKTMP: String
|
||||
lateinit var NATIVE_LIB_DIR: File
|
||||
val MAGISK_PATH get() = "$MAGISKTMP/modules"
|
||||
const val TMP_FOLDER_PATH = "/dev/tmp"
|
||||
const val TMPDIR = "/dev/tmp"
|
||||
const val MAGISK_LOG = "/cache/magisk.log"
|
||||
|
||||
// Versions
|
||||
@@ -19,24 +37,17 @@ object Const {
|
||||
val USER_ID = Process.myUid() / 100000
|
||||
|
||||
object Version {
|
||||
const val MIN_VERSION = "v19.0"
|
||||
const val MIN_VERCODE = 19000
|
||||
const val MIN_VERSION = "v20.4"
|
||||
const val MIN_VERCODE = 20400
|
||||
|
||||
fun atLeast_20_2() = Info.env.magiskVersionCode >= 20200 || isCanary()
|
||||
fun atLeast_20_4() = Info.env.magiskVersionCode >= 20400 || isCanary()
|
||||
fun atLeast_21_0() = Info.env.magiskVersionCode >= 21000 || isCanary()
|
||||
fun atLeast_21_2() = Info.env.magiskVersionCode >= 21200 || isCanary()
|
||||
fun isCanary() = Info.env.magiskVersionCode % 100 != 0
|
||||
}
|
||||
|
||||
object ID {
|
||||
const val FETCH_ZIP = 2
|
||||
const val SELECT_FILE = 3
|
||||
const val MAX_ACTIVITY_RESULT = 10
|
||||
|
||||
// notifications
|
||||
const val MAGISK_UPDATE_NOTIFICATION_ID = 4
|
||||
const val APK_UPDATE_NOTIFICATION_ID = 5
|
||||
const val HIDE_MANAGER_NOTIFICATION_ID = 8
|
||||
const val UPDATE_NOTIFICATION_CHANNEL = "update"
|
||||
const val PROGRESS_NOTIFICATION_CHANNEL = "progress"
|
||||
const val CHECK_MAGISK_UPDATE_WORKER_ID = "magisk_update"
|
||||
@@ -46,6 +57,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 (BuildConfig.VERSION_CODE % 100 != 0) Info.remote.magisk.note
|
||||
else "https://topjohnwu.github.io/Magisk/releases/${BuildConfig.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/"
|
||||
|
@@ -14,44 +14,38 @@ import android.content.Intent
|
||||
import android.content.res.AssetManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.util.DisplayMetrics
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.topjohnwu.magisk.DynAPK
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.download.DownloadService
|
||||
import com.topjohnwu.magisk.core.utils.refreshLocale
|
||||
import com.topjohnwu.magisk.core.utils.updateConfig
|
||||
import com.topjohnwu.magisk.ktx.forceGetDeclaredField
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
||||
|
||||
fun AssetManager.addAssetPath(path: String) {
|
||||
DynAPK.addAssetPath(this, path)
|
||||
}
|
||||
|
||||
fun Context.wrap(global: Boolean = true): Context =
|
||||
if (global) GlobalResContext(this) else ResContext(this)
|
||||
fun Context.wrap(inject: Boolean = false): Context =
|
||||
if (inject) ReInjectedContext(this) else InjectedContext(this)
|
||||
|
||||
fun Context.wrapJob(): Context = object : GlobalResContext(this) {
|
||||
fun Context.wrapJob(): Context = object : InjectedContext(this) {
|
||||
|
||||
override fun getApplicationContext(): Context {
|
||||
return this
|
||||
}
|
||||
override fun getApplicationContext() = this
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
override fun getSystemService(name: String): Any? {
|
||||
return if (!isRunningAsStub) super.getSystemService(name) else
|
||||
when (name) {
|
||||
Context.JOB_SCHEDULER_SERVICE ->
|
||||
JobSchedulerWrapper(super.getSystemService(name) as JobScheduler)
|
||||
else -> super.getSystemService(name)
|
||||
return super.getSystemService(name).let {
|
||||
when {
|
||||
!isRunningAsStub -> it
|
||||
name == JOB_SCHEDULER_SERVICE -> JobSchedulerWrapper(it as JobScheduler)
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Class<*>.cmp(pkg: String): ComponentName {
|
||||
val name = ClassMap[this].name
|
||||
return ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name)
|
||||
}
|
||||
fun Class<*>.cmp(pkg: String) =
|
||||
ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name)
|
||||
|
||||
inline fun <reified T> Activity.redirect() = Intent(intent)
|
||||
.setComponent(T::class.java.cmp(packageName))
|
||||
@@ -59,34 +53,27 @@ inline fun <reified T> Activity.redirect() = Intent(intent)
|
||||
|
||||
inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName))
|
||||
|
||||
private open class GlobalResContext(base: Context) : ContextWrapper(base) {
|
||||
open val mRes: Resources get() = ResMgr.resource
|
||||
|
||||
override fun getResources(): Resources {
|
||||
return mRes
|
||||
}
|
||||
|
||||
override fun getClassLoader(): ClassLoader {
|
||||
return javaClass.classLoader!!
|
||||
}
|
||||
|
||||
private open class InjectedContext(base: Context) : ContextWrapper(base) {
|
||||
open val res: Resources get() = AssetHack.resource
|
||||
override fun getAssets(): AssetManager = res.assets
|
||||
override fun getResources() = res
|
||||
override fun getClassLoader() = javaClass.classLoader!!
|
||||
override fun createConfigurationContext(config: Configuration): Context {
|
||||
return ResContext(super.createConfigurationContext(config))
|
||||
return super.createConfigurationContext(config).wrap(true)
|
||||
}
|
||||
}
|
||||
|
||||
private class ResContext(base: Context) : GlobalResContext(base) {
|
||||
override val mRes by lazy { base.resources.patch() }
|
||||
|
||||
private class ReInjectedContext(base: Context) : InjectedContext(base) {
|
||||
override val res by lazy { base.resources.patch() }
|
||||
private fun Resources.patch(): Resources {
|
||||
updateConfig()
|
||||
if (isRunningAsStub)
|
||||
assets.addAssetPath(ResMgr.apk)
|
||||
assets.addAssetPath(AssetHack.apk)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
object ResMgr {
|
||||
object AssetHack {
|
||||
|
||||
lateinit var resource: Resources
|
||||
lateinit var apk: String
|
||||
@@ -101,62 +88,39 @@ object ResMgr {
|
||||
apk = context.packageResourcePath
|
||||
}
|
||||
}
|
||||
|
||||
fun newResource(): Resources {
|
||||
val asset = AssetManager::class.java.newInstance()
|
||||
asset.addAssetPath(apk)
|
||||
val config = Configuration(resource.configuration)
|
||||
val metrics = DisplayMetrics()
|
||||
metrics.setTo(resource.displayMetrics)
|
||||
return Resources(asset, metrics, config)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(28)
|
||||
private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() {
|
||||
|
||||
override fun schedule(job: JobInfo): Int {
|
||||
return base.schedule(job.patch())
|
||||
}
|
||||
|
||||
override fun enqueue(job: JobInfo, work: JobWorkItem): Int {
|
||||
return base.enqueue(job.patch(), work)
|
||||
}
|
||||
|
||||
override fun cancel(jobId: Int) {
|
||||
base.cancel(jobId)
|
||||
}
|
||||
|
||||
override fun cancelAll() {
|
||||
base.cancelAll()
|
||||
}
|
||||
|
||||
override fun getAllPendingJobs(): List<JobInfo> {
|
||||
return base.allPendingJobs
|
||||
}
|
||||
|
||||
override fun getPendingJob(jobId: Int): JobInfo? {
|
||||
return base.getPendingJob(jobId)
|
||||
}
|
||||
|
||||
override fun schedule(job: JobInfo) = base.schedule(job.patch())
|
||||
override fun enqueue(job: JobInfo, work: JobWorkItem) = base.enqueue(job.patch(), work)
|
||||
override fun cancel(jobId: Int) = base.cancel(jobId)
|
||||
override fun cancelAll() = base.cancelAll()
|
||||
override fun getAllPendingJobs(): List<JobInfo> = base.allPendingJobs
|
||||
override fun getPendingJob(jobId: Int) = base.getPendingJob(jobId)
|
||||
private fun JobInfo.patch(): JobInfo {
|
||||
// We need to swap out the service of JobInfo
|
||||
val name = service.className
|
||||
val component = ComponentName(
|
||||
service.packageName,
|
||||
Info.stubChk.classToComponent[name] ?: name
|
||||
)
|
||||
// Swap out the service of JobInfo
|
||||
val component = service.run {
|
||||
ComponentName(packageName,
|
||||
Info.stub?.classToComponent?.get(className) ?: className)
|
||||
}
|
||||
javaClass.getDeclaredField("service").apply {
|
||||
isAccessible = true
|
||||
}.set(this, component)
|
||||
|
||||
javaClass.forceGetDeclaredField("service")?.set(this, component)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
private object ClassMap {
|
||||
|
||||
private val map = mapOf(
|
||||
App::class.java to a.e::class.java,
|
||||
MainActivity::class.java to a.b::class.java,
|
||||
SplashActivity::class.java to a.c::class.java,
|
||||
Receiver::class.java to a.h::class.java,
|
||||
DownloadService::class.java to a.j::class.java,
|
||||
SuRequestActivity::class.java to a.m::class.java
|
||||
)
|
||||
|
||||
operator fun get(c: Class<*>) = map.getOrElse(c) { c }
|
||||
}
|
||||
|
||||
// Keep a reference to these resources to prevent it from
|
||||
// being removed when running "remove unused resources"
|
||||
val shouldKeepResources = listOf(
|
||||
|
@@ -1,41 +1,45 @@
|
||||
package com.topjohnwu.magisk.core
|
||||
|
||||
import android.os.Build
|
||||
import androidx.databinding.ObservableBoolean
|
||||
import com.topjohnwu.magisk.DynAPK
|
||||
import com.topjohnwu.magisk.core.model.UpdateInfo
|
||||
import com.topjohnwu.magisk.core.utils.net.NetworkObserver
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import com.topjohnwu.magisk.utils.CachedValue
|
||||
import com.topjohnwu.magisk.ktx.getProperty
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils.fastCmd
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
import java.io.FileInputStream
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
val isRunningAsStub get() = Info.stub != null
|
||||
|
||||
object Info {
|
||||
|
||||
val envRef = CachedValue { loadState() }
|
||||
|
||||
@JvmStatic val env by envRef
|
||||
|
||||
var stub: DynAPK.Data? = null
|
||||
val stubChk: DynAPK.Data
|
||||
get() = stub as DynAPK.Data
|
||||
|
||||
var remote = UpdateInfo()
|
||||
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
|
||||
}
|
||||
|
||||
// Device state
|
||||
var crypto = ""
|
||||
@JvmStatic var isSAR = false
|
||||
@JvmStatic var isAB = false
|
||||
@JvmStatic val env by lazy { loadState() }
|
||||
@JvmField var isSAR = false
|
||||
@JvmField var isAB = false
|
||||
@JvmField val isVirtualAB = getProperty("ro.virtual_ab.enabled", "false") == "true"
|
||||
@JvmStatic val isFDE get() = crypto == "block"
|
||||
@JvmStatic var ramdisk = false
|
||||
@JvmStatic var hasGMS = true
|
||||
@JvmStatic var isPixel = false
|
||||
@JvmStatic val cryptoText get() = crypto.capitalize(Locale.US)
|
||||
@JvmField var ramdisk = false
|
||||
@JvmField var hasGMS = true
|
||||
@JvmField val isPixel = Build.BRAND == "google"
|
||||
@JvmField val isEmulator = getProperty("ro.kernel.qemu", "0") == "1"
|
||||
var crypto = ""
|
||||
var noDataExec = false
|
||||
|
||||
val isConnected by lazy {
|
||||
ObservableBoolean(false).also { field ->
|
||||
@@ -47,14 +51,12 @@ object Info {
|
||||
|
||||
val isNewReboot by lazy {
|
||||
try {
|
||||
FileInputStream("/proc/sys/kernel/random/boot_id").bufferedReader().use {
|
||||
val id = it.readLine()
|
||||
if (id != Config.bootId) {
|
||||
Config.bootId = id
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
val id = File("/proc/sys/kernel/random/boot_id").readText()
|
||||
if (id != Config.bootId) {
|
||||
Config.bootId = id
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
false
|
||||
@@ -73,8 +75,8 @@ object Info {
|
||||
hide: Boolean = false
|
||||
) {
|
||||
val magiskHide get() = Config.magiskHide
|
||||
val magiskVersionCode = when (code) {
|
||||
in Int.MIN_VALUE..Const.Version.MIN_VERCODE -> -1
|
||||
val magiskVersionCode = when {
|
||||
code < Const.Version.MIN_VERCODE -> -1
|
||||
else -> if (Shell.rootAccess()) code else -1
|
||||
}
|
||||
val isUnsupported = code > 0 && code < Const.Version.MIN_VERCODE
|
||||
|
@@ -1,32 +1,30 @@
|
||||
package com.topjohnwu.magisk.core
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import com.topjohnwu.magisk.view.Shortcuts
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
open class SplashActivity : Activity() {
|
||||
open class SplashActivity : BaseActivity() {
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(base.wrap())
|
||||
}
|
||||
private val latch = CountDownLatch(1)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(R.style.SplashTheme)
|
||||
super.onCreate(savedInstanceState)
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
initAndStart()
|
||||
}
|
||||
// Pre-initialize root shell
|
||||
Shell.getShell(null) { initAndStart() }
|
||||
}
|
||||
|
||||
private fun handleRepackage(pkg: String?) {
|
||||
@@ -40,13 +38,26 @@ open class SplashActivity : Activity() {
|
||||
if (Config.suManager.isNotEmpty())
|
||||
Config.suManager = ""
|
||||
pkg ?: return
|
||||
Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec()
|
||||
if (!Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec().isSuccess)
|
||||
uninstallApp(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initAndStart() {
|
||||
// Pre-initialize root shell
|
||||
Shell.getShell()
|
||||
if (isRunningAsStub && !Shell.rootAccess()) {
|
||||
runOnUiThread {
|
||||
MagiskDialog(this)
|
||||
.applyTitle(R.string.unsupport_nonroot_stub_title)
|
||||
.applyMessage(R.string.unsupport_nonroot_stub_msg)
|
||||
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||
titleRes = R.string.install
|
||||
onClick { HideAPK.restore(this@SplashActivity) }
|
||||
}
|
||||
.cancellable(false)
|
||||
.reveal()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val prevPkg = intent.getStringExtra(Const.Key.PREV_PKG)
|
||||
|
||||
@@ -60,11 +71,21 @@ open class SplashActivity : Activity() {
|
||||
get<NetworkService>()
|
||||
|
||||
DONE = true
|
||||
|
||||
redirect<MainActivity>().also { startActivity(it) }
|
||||
startActivity(redirect<MainActivity>())
|
||||
finish()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun uninstallApp(pkg: String) {
|
||||
val uri = Uri.Builder().scheme("package").opaquePart(pkg).build()
|
||||
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, uri)
|
||||
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
|
||||
startActivityForResult(intent) { _, _ ->
|
||||
latch.countDown()
|
||||
}
|
||||
latch.await()
|
||||
}
|
||||
|
||||
companion object {
|
||||
var DONE = false
|
||||
}
|
||||
|
@@ -1,13 +1,11 @@
|
||||
package com.topjohnwu.magisk.core
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import androidx.work.*
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -18,20 +16,15 @@ class UpdateCheckService(context: Context, workerParams: WorkerParameters)
|
||||
private val svc: NetworkService by inject()
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
// Make sure shell initializer was ran
|
||||
withContext(Dispatchers.IO) {
|
||||
Shell.getShell()
|
||||
}
|
||||
return svc.fetchUpdate()?.let {
|
||||
if (BuildConfig.VERSION_CODE < it.app.versionCode)
|
||||
return svc.fetchUpdate()?.run {
|
||||
if (Info.env.isActive && BuildConfig.VERSION_CODE < magisk.versionCode)
|
||||
Notifications.managerUpdate(applicationContext)
|
||||
else if (Info.env.isActive && Info.env.magiskVersionCode < it.magisk.versionCode)
|
||||
Notifications.magiskUpdate(applicationContext)
|
||||
Result.success()
|
||||
} ?: Result.failure()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@SuppressLint("NewApi")
|
||||
fun schedule(context: Context) {
|
||||
if (Config.checkUpdate) {
|
||||
val constraints = Constraints.Builder()
|
||||
|
@@ -14,7 +14,6 @@ import androidx.collection.SparseArrayCompat
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.core.wrap
|
||||
import com.topjohnwu.magisk.ktx.set
|
||||
@@ -26,6 +25,13 @@ typealias ActivityResultCallback = BaseActivity.(Int, Intent?) -> Unit
|
||||
abstract class BaseActivity : AppCompatActivity() {
|
||||
|
||||
private val resultCallbacks by lazy { SparseArrayCompat<ActivityResultCallback>() }
|
||||
private val newRequestCode: Int get() {
|
||||
var requestCode: Int
|
||||
do {
|
||||
requestCode = Random.nextInt(0, 1 shl 15)
|
||||
} while (resultCallbacks.containsKey(requestCode))
|
||||
return requestCode
|
||||
}
|
||||
|
||||
override fun applyOverrideConfiguration(config: Configuration?) {
|
||||
// Force applying our preferred local
|
||||
@@ -34,7 +40,7 @@ abstract class BaseActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(base.wrap(false))
|
||||
super.attachBaseContext(base.wrap(true))
|
||||
}
|
||||
|
||||
fun withPermission(permission: String, builder: PermissionRequestBuilder.() -> Unit) {
|
||||
@@ -49,10 +55,7 @@ abstract class BaseActivity : AppCompatActivity() {
|
||||
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
|
||||
request.onSuccess()
|
||||
} else {
|
||||
var requestCode: Int
|
||||
do {
|
||||
requestCode = Random.nextInt(Const.ID.MAX_ACTIVITY_RESULT + 1, 1 shl 15)
|
||||
} while (resultCallbacks.containsKey(requestCode))
|
||||
val requestCode = newRequestCode
|
||||
resultCallbacks[requestCode] = { result, _ ->
|
||||
if (result > 0)
|
||||
request.onSuccess()
|
||||
@@ -92,7 +95,8 @@ abstract class BaseActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
fun startActivityForResult(intent: Intent, requestCode: Int, callback: ActivityResultCallback) {
|
||||
fun startActivityForResult(intent: Intent, callback: ActivityResultCallback) {
|
||||
val requestCode = newRequestCode
|
||||
resultCallbacks[requestCode] = callback
|
||||
try {
|
||||
startActivityForResult(intent, requestCode)
|
||||
|
@@ -1,31 +0,0 @@
|
||||
package com.topjohnwu.magisk.core.download
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
sealed class Action : Parcelable {
|
||||
|
||||
sealed class Flash : Action() {
|
||||
|
||||
@Parcelize
|
||||
object Primary : Flash()
|
||||
|
||||
@Parcelize
|
||||
object Secondary : Flash()
|
||||
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
object Download : Action()
|
||||
|
||||
@Parcelize
|
||||
object Uninstall : Action()
|
||||
|
||||
@Parcelize
|
||||
object EnvFix : Action()
|
||||
|
||||
@Parcelize
|
||||
data class Patch(val fileUri: Uri) : Action()
|
||||
|
||||
}
|
@@ -8,11 +8,8 @@ import androidx.lifecycle.MutableLiveData
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.ForegroundTracker
|
||||
import com.topjohnwu.magisk.core.base.BaseService
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.checkSum
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||
import com.topjohnwu.magisk.core.utils.ProgressInputStream
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.ktx.withStreams
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -69,18 +66,11 @@ abstract class BaseDownloader : BaseService(), KoinComponent {
|
||||
// -- Download logic
|
||||
|
||||
private suspend fun Subject.startDownload() {
|
||||
val skip = this is Subject.Magisk && file.checkSum("MD5", magisk.md5)
|
||||
if (!skip) {
|
||||
val stream = service.fetchFile(url).toProgressStream(this)
|
||||
when (this) {
|
||||
is Subject.Module -> // Download and process on-the-fly
|
||||
stream.toModule(file, service.fetchInstaller().byteStream())
|
||||
else -> {
|
||||
withStreams(stream, file.outputStream()) { it, out -> it.copyTo(out) }
|
||||
if (this is Subject.Manager)
|
||||
handleAPK(this)
|
||||
}
|
||||
}
|
||||
val stream = service.fetchFile(url).toProgressStream(this)
|
||||
when (this) {
|
||||
is Subject.Module -> // Download and process on-the-fly
|
||||
stream.toModule(file, service.fetchInstaller().byteStream())
|
||||
is Subject.Manager -> handleAPK(this, stream)
|
||||
}
|
||||
val newId = notifyFinish(this)
|
||||
if (ForegroundTracker.hasForeground)
|
||||
|
@@ -7,11 +7,10 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.core.net.toFile
|
||||
import com.topjohnwu.magisk.core.download.Action.*
|
||||
import com.topjohnwu.magisk.core.download.Action.Flash.Secondary
|
||||
import com.topjohnwu.magisk.core.download.Subject.*
|
||||
import com.topjohnwu.magisk.core.download.Action.Flash
|
||||
import com.topjohnwu.magisk.core.download.Subject.Manager
|
||||
import com.topjohnwu.magisk.core.download.Subject.Module
|
||||
import com.topjohnwu.magisk.core.intent
|
||||
import com.topjohnwu.magisk.core.tasks.EnvFixTask
|
||||
import com.topjohnwu.magisk.ui.flash.FlashFragment
|
||||
import com.topjohnwu.magisk.utils.APKInstall
|
||||
import kotlin.random.Random.Default.nextInt
|
||||
@@ -22,25 +21,12 @@ open class DownloadService : BaseDownloader() {
|
||||
private val context get() = this
|
||||
|
||||
override suspend fun onFinish(subject: Subject, id: Int) = when (subject) {
|
||||
is Magisk -> subject.onFinish(id)
|
||||
is Module -> subject.onFinish(id)
|
||||
is Manager -> subject.onFinish(id)
|
||||
}
|
||||
|
||||
private suspend fun Magisk.onFinish(id: Int) = when (val action = action) {
|
||||
Uninstall -> FlashFragment.uninstall(file, id)
|
||||
EnvFix -> {
|
||||
remove(id)
|
||||
EnvFixTask(file).exec()
|
||||
Unit
|
||||
}
|
||||
is Patch -> FlashFragment.patch(file, action.fileUri, id)
|
||||
is Flash -> FlashFragment.flash(file, action is Secondary, id)
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
private fun Module.onFinish(id: Int) = when (action) {
|
||||
is Flash -> FlashFragment.install(file, id)
|
||||
Flash -> FlashFragment.install(file, id)
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
@@ -53,22 +39,13 @@ open class DownloadService : BaseDownloader() {
|
||||
|
||||
override fun Notification.Builder.setIntent(subject: Subject)
|
||||
= when (subject) {
|
||||
is Magisk -> setIntent(subject)
|
||||
is Module -> setIntent(subject)
|
||||
is Manager -> setIntent(subject)
|
||||
}
|
||||
|
||||
private fun Notification.Builder.setIntent(subject: Magisk)
|
||||
= when (val action = subject.action) {
|
||||
Uninstall -> setContentIntent(FlashFragment.uninstallIntent(context, subject.file))
|
||||
is Flash -> setContentIntent(FlashFragment.flashIntent(context, subject.file, action is Secondary))
|
||||
is Patch -> setContentIntent(FlashFragment.patchIntent(context, subject.file, action.fileUri))
|
||||
else -> setContentIntent(Intent())
|
||||
}
|
||||
|
||||
private fun Notification.Builder.setIntent(subject: Module)
|
||||
= when (subject.action) {
|
||||
is Flash -> setContentIntent(FlashFragment.installIntent(context, subject.file))
|
||||
Flash -> setContentIntent(FlashFragment.installIntent(context, subject.file))
|
||||
else -> setContentIntent(Intent())
|
||||
}
|
||||
|
||||
|
@@ -2,19 +2,22 @@ package com.topjohnwu.magisk.core.download
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.net.toFile
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.DynAPK
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||
import com.topjohnwu.magisk.ktx.relaunchApp
|
||||
import com.topjohnwu.magisk.ktx.withStreams
|
||||
import com.topjohnwu.magisk.ktx.writeTo
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
private fun Context.patch(apk: File) {
|
||||
val patched = File(apk.parent, "patched.apk")
|
||||
HideAPK.patch(this, apk.path, patched.path, packageName, applicationInfo.nonLocalizedLabel)
|
||||
HideAPK.patch(this, apk, patched, packageName, applicationInfo.nonLocalizedLabel)
|
||||
apk.delete()
|
||||
patched.renameTo(apk)
|
||||
}
|
||||
@@ -22,30 +25,51 @@ private fun Context.patch(apk: File) {
|
||||
private fun BaseDownloader.notifyHide(id: Int) {
|
||||
update(id) {
|
||||
it.setProgress(0, 0, true)
|
||||
.setContentTitle(getString(R.string.hide_manager_title))
|
||||
.setContentTitle(getString(R.string.hide_app_title))
|
||||
.setContentText("")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun BaseDownloader.handleAPK(subject: Subject.Manager) {
|
||||
val apk = subject.file.toFile()
|
||||
val id = subject.notifyID()
|
||||
private class DupOutputStream(
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun BaseDownloader.handleAPK(subject: Subject.Manager, stream: InputStream) {
|
||||
fun write(output: OutputStream) {
|
||||
val ext = subject.externalFile.outputStream()
|
||||
val o = DupOutputStream(ext, output)
|
||||
withStreams(stream, o) { src, out -> src.copyTo(out) }
|
||||
}
|
||||
|
||||
if (isRunningAsStub) {
|
||||
// Move to upgrade location
|
||||
apk.copyTo(DynAPK.update(this), overwrite = true)
|
||||
apk.delete()
|
||||
if (Info.stubChk.version < subject.stub.versionCode) {
|
||||
notifyHide(id)
|
||||
val apk = subject.file.toFile()
|
||||
val id = subject.notifyID()
|
||||
write(DynAPK.update(this).outputStream())
|
||||
if (Info.stub!!.version < subject.stub.versionCode) {
|
||||
// Also upgrade stub
|
||||
service.fetchFile(subject.stub.link).byteStream().use { it.writeTo(apk) }
|
||||
notifyHide(id)
|
||||
service.fetchFile(subject.stub.link).byteStream().writeTo(apk)
|
||||
patch(apk)
|
||||
} else {
|
||||
// Simply relaunch the app
|
||||
stopSelf()
|
||||
relaunchApp(this)
|
||||
}
|
||||
} else if (packageName != BuildConfig.APPLICATION_ID) {
|
||||
notifyHide(id)
|
||||
patch(apk)
|
||||
} else {
|
||||
write(subject.file.outputStream())
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,9 @@
|
||||
package com.topjohnwu.magisk.core.download
|
||||
|
||||
import android.net.Uri
|
||||
import com.topjohnwu.magisk.ktx.withStreams
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||
import com.topjohnwu.magisk.ktx.forEach
|
||||
import com.topjohnwu.magisk.ktx.withStreams
|
||||
import java.io.InputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
@@ -25,8 +26,7 @@ fun InputStream.toModule(file: Uri, installer: InputStream) {
|
||||
zout.write("#MAGISK\n".toByteArray(charset("UTF-8")))
|
||||
|
||||
var off = -1
|
||||
var entry: ZipEntry? = zin.nextEntry
|
||||
while (entry != null) {
|
||||
zin.forEach { entry ->
|
||||
if (off < 0) {
|
||||
off = entry.name.indexOf('/') + 1
|
||||
}
|
||||
@@ -38,8 +38,6 @@ fun InputStream.toModule(file: Uri, installer: InputStream) {
|
||||
zin.copyTo(zout)
|
||||
}
|
||||
}
|
||||
|
||||
entry = zin.nextEntry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,6 @@ 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.ManagerJson
|
||||
import com.topjohnwu.magisk.core.model.StubJson
|
||||
import com.topjohnwu.magisk.core.model.module.OnlineModule
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
@@ -40,70 +39,26 @@ sealed class Subject : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
class Manager(
|
||||
private val app: ManagerJson = Info.remote.app,
|
||||
private val json: MagiskJson = Info.remote.magisk,
|
||||
val stub: StubJson = Info.remote.stub
|
||||
) : Subject() {
|
||||
override val action get() = Action.Download
|
||||
override val title: String get() = "MagiskManager-${app.version}(${app.versionCode})"
|
||||
override val url: String get() = app.link
|
||||
override val title: String get() = "Magisk-${json.version}(${json.versionCode})"
|
||||
override val url: String get() = json.link
|
||||
|
||||
@IgnoredOnParcel
|
||||
override val file by lazy {
|
||||
cachedFile("manager.apk")
|
||||
}
|
||||
|
||||
val externalFile get() = MediaStoreUtils.getFile("$title.apk").uri
|
||||
}
|
||||
|
||||
abstract class Magisk : Subject() {
|
||||
|
||||
val magisk: MagiskJson = Info.remote.magisk
|
||||
|
||||
@Parcelize
|
||||
private class Internal(
|
||||
override val action: Action
|
||||
) : Magisk() {
|
||||
override val url: String get() = magisk.link
|
||||
override val title: String get() = "Magisk-${magisk.version}(${magisk.versionCode})"
|
||||
|
||||
@IgnoredOnParcel
|
||||
override val file by lazy {
|
||||
cachedFile("magisk.zip")
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
private class Uninstall : Magisk() {
|
||||
override val action get() = Action.Uninstall
|
||||
override val url: String get() = Info.remote.uninstaller.link
|
||||
override val title: String get() = "uninstall.zip"
|
||||
|
||||
@IgnoredOnParcel
|
||||
override val file by lazy {
|
||||
cachedFile(title)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
private class Download : Magisk() {
|
||||
override val action get() = Action.Download
|
||||
override val url: String get() = magisk.link
|
||||
override val title: String get() = "Magisk-${magisk.version}(${magisk.versionCode}).zip"
|
||||
|
||||
@IgnoredOnParcel
|
||||
override val file by lazy {
|
||||
MediaStoreUtils.getFile(title).uri
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
operator fun invoke(config: Action) = when (config) {
|
||||
Action.Download -> Download()
|
||||
Action.Uninstall -> Uninstall()
|
||||
Action.EnvFix, is Action.Flash, is Action.Patch -> Internal(config)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sealed class Action : Parcelable {
|
||||
@Parcelize
|
||||
object Flash : Action()
|
||||
|
||||
@Parcelize
|
||||
object Download : Action()
|
||||
}
|
||||
|
@@ -6,17 +6,11 @@ import kotlinx.parcelize.Parcelize
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class UpdateInfo(
|
||||
val app: ManagerJson = ManagerJson(),
|
||||
val uninstaller: UninstallerJson = UninstallerJson(),
|
||||
val magisk: MagiskJson = MagiskJson(),
|
||||
val stub: StubJson = StubJson()
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class UninstallerJson(
|
||||
val link: String = ""
|
||||
)
|
||||
|
||||
@Parcelize
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MagiskJson(
|
||||
val version: String = "",
|
||||
@@ -24,15 +18,6 @@ data class MagiskJson(
|
||||
val link: String = "",
|
||||
val note: String = "",
|
||||
val md5: String = ""
|
||||
)
|
||||
|
||||
@Parcelize
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ManagerJson(
|
||||
val version: String = "",
|
||||
val versionCode: Int = -1,
|
||||
val link: String = "",
|
||||
val note: String = ""
|
||||
) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
|
@@ -27,13 +27,13 @@ class LocalModule(path: String) : Module() {
|
||||
val dir = "$PERSIST/$id"
|
||||
if (enable) {
|
||||
disableFile.delete()
|
||||
if (Const.Version.isCanary())
|
||||
if (Const.Version.atLeast_21_2())
|
||||
Shell.su("copy_sepolicy_rules").submit()
|
||||
else
|
||||
Shell.su("mkdir -p $dir", "cp -af $ruleFile $dir").submit()
|
||||
} else {
|
||||
!disableFile.createNewFile()
|
||||
if (Const.Version.isCanary())
|
||||
if (Const.Version.atLeast_21_2())
|
||||
Shell.su("copy_sepolicy_rules").submit()
|
||||
else
|
||||
Shell.su("rm -rf $dir").submit()
|
||||
@@ -45,13 +45,13 @@ class LocalModule(path: String) : Module() {
|
||||
set(remove) {
|
||||
if (remove) {
|
||||
removeFile.createNewFile()
|
||||
if (Const.Version.isCanary())
|
||||
if (Const.Version.atLeast_21_2())
|
||||
Shell.su("copy_sepolicy_rules").submit()
|
||||
else
|
||||
Shell.su("rm -rf $PERSIST/$id").submit()
|
||||
} else {
|
||||
!removeFile.delete()
|
||||
if (Const.Version.isCanary())
|
||||
if (Const.Version.atLeast_21_2())
|
||||
Shell.su("copy_sepolicy_rules").submit()
|
||||
else
|
||||
Shell.su("cp -af $ruleFile $PERSIST/$id").submit()
|
||||
|
@@ -1,11 +1,13 @@
|
||||
@file:SuppressLint("InlinedApi")
|
||||
|
||||
package com.topjohnwu.magisk.core.model.su
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.topjohnwu.magisk.core.model.su.SuPolicy.Companion.INTERACTIVE
|
||||
import com.topjohnwu.magisk.ktx.getLabel
|
||||
|
||||
|
||||
data class SuPolicy(
|
||||
var uid: Int,
|
||||
val packageName: String,
|
||||
@@ -38,7 +40,7 @@ fun SuPolicy.toMap() = mapOf(
|
||||
fun Map<String, String>.toPolicy(pm: PackageManager): SuPolicy {
|
||||
val uid = get("uid")?.toIntOrNull() ?: -1
|
||||
val packageName = get("package_name").orEmpty()
|
||||
val info = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES)
|
||||
val info = pm.getApplicationInfo(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES)
|
||||
|
||||
if (info.uid != uid)
|
||||
throw PackageManager.NameNotFoundException()
|
||||
@@ -59,7 +61,7 @@ fun Map<String, String>.toPolicy(pm: PackageManager): SuPolicy {
|
||||
fun Int.toPolicy(pm: PackageManager, policy: Int = INTERACTIVE): SuPolicy {
|
||||
val pkg = pm.getPackagesForUid(this)?.firstOrNull()
|
||||
?: throw PackageManager.NameNotFoundException()
|
||||
val info = pm.getApplicationInfo(pkg, PackageManager.GET_UNINSTALLED_PACKAGES)
|
||||
val info = pm.getApplicationInfo(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES)
|
||||
return SuPolicy(
|
||||
uid = info.uid,
|
||||
packageName = pkg,
|
||||
|
@@ -2,14 +2,13 @@ package com.topjohnwu.magisk.core.tasks
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.core.os.postDelayed
|
||||
import androidx.core.net.toFile
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.displayName
|
||||
import com.topjohnwu.magisk.core.utils.unzip
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
||||
import com.topjohnwu.magisk.core.utils.unzip
|
||||
import com.topjohnwu.magisk.ktx.writeTo
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.KoinComponent
|
||||
@@ -25,61 +24,50 @@ open class FlashZip(
|
||||
private val logs: MutableList<String>
|
||||
): KoinComponent {
|
||||
|
||||
val context: Context by inject()
|
||||
private val installFolder = File(context.cacheDir, "flash").apply {
|
||||
if (!exists()) mkdirs()
|
||||
}
|
||||
private val tmpFile: File = File(installFolder, "install.zip")
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun unzipAndCheck(): Boolean {
|
||||
val parentFile = tmpFile.parentFile ?: return false
|
||||
tmpFile.unzip(parentFile, "META-INF/com/google/android", true)
|
||||
|
||||
val updaterScript = File(parentFile, "updater-script")
|
||||
return Shell
|
||||
.su("grep -q '#MAGISK' $updaterScript")
|
||||
.exec()
|
||||
.isSuccess
|
||||
}
|
||||
private val context: Context by inject()
|
||||
private val installDir = File(context.cacheDir, "flash")
|
||||
private lateinit var zipFile: File
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun flash(): Boolean {
|
||||
console.add("- Copying zip to temp directory")
|
||||
installDir.deleteRecursively()
|
||||
installDir.mkdirs()
|
||||
|
||||
runCatching {
|
||||
mUri.inputStream().writeTo(tmpFile)
|
||||
}.getOrElse {
|
||||
when (it) {
|
||||
is FileNotFoundException -> console.add("! Invalid Uri")
|
||||
is IOException -> console.add("! Cannot copy to cache")
|
||||
zipFile = if (mUri.scheme == "file") {
|
||||
mUri.toFile()
|
||||
} else {
|
||||
File(installDir, "install.zip").also {
|
||||
console.add("- Copying zip to temp directory")
|
||||
try {
|
||||
mUri.inputStream().writeTo(it)
|
||||
} catch (e: IOException) {
|
||||
when (e) {
|
||||
is FileNotFoundException -> console.add("! Invalid Uri")
|
||||
else -> console.add("! Cannot copy to cache")
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
throw it
|
||||
}
|
||||
|
||||
val isMagiskModule = runCatching {
|
||||
unzipAndCheck()
|
||||
val isValid = runCatching {
|
||||
zipFile.unzip(installDir, "META-INF/com/google/android", true)
|
||||
val script = File(installDir, "updater-script")
|
||||
script.readText().contains("#MAGISK")
|
||||
}.getOrElse {
|
||||
console.add("! Unzip error")
|
||||
throw it
|
||||
}
|
||||
|
||||
if (!isMagiskModule) {
|
||||
console.add("! This zip is not a Magisk Module!")
|
||||
if (!isValid) {
|
||||
console.add("! This zip is not a Magisk module!")
|
||||
return false
|
||||
}
|
||||
|
||||
console.add("- Installing ${mUri.displayName}")
|
||||
|
||||
val parentFile = tmpFile.parent ?: return false
|
||||
|
||||
return Shell
|
||||
.su(
|
||||
"cd $parentFile",
|
||||
"BOOTMODE=true sh update-binary dummy 1 $tmpFile"
|
||||
)
|
||||
.to(console, logs)
|
||||
.exec().isSuccess
|
||||
return Shell.su("sh $installDir/update-binary dummy 1 \"$zipFile\"")
|
||||
.to(console, logs).exec().isSuccess
|
||||
}
|
||||
|
||||
open suspend fun exec() = withContext(Dispatchers.IO) {
|
||||
@@ -94,25 +82,7 @@ open class FlashZip(
|
||||
Timber.e(e)
|
||||
false
|
||||
} finally {
|
||||
Shell.su("cd /", "rm -rf ${tmpFile.parent} ${Const.TMP_FOLDER_PATH}").submit()
|
||||
Shell.su("cd /", "rm -rf $installDir ${Const.TMPDIR}").submit()
|
||||
}
|
||||
}
|
||||
|
||||
class Uninstall(
|
||||
uri: Uri,
|
||||
console: MutableList<String>,
|
||||
log: MutableList<String>
|
||||
) : FlashZip(uri, console, log) {
|
||||
|
||||
override suspend fun exec(): Boolean {
|
||||
val success = super.exec()
|
||||
if (success) {
|
||||
UiThreadHandler.handler.postDelayed(3000) {
|
||||
Shell.su("pm uninstall " + context.packageName).exec()
|
||||
}
|
||||
}
|
||||
return success
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,38 +1,41 @@
|
||||
package com.topjohnwu.magisk.core.tasks
|
||||
|
||||
import android.app.ProgressDialog
|
||||
import android.app.Activity
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.widget.Toast
|
||||
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
||||
import com.topjohnwu.magisk.DynAPK
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.*
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.Provider
|
||||
import com.topjohnwu.magisk.core.utils.AXML
|
||||
import com.topjohnwu.magisk.core.utils.Keygen
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.ktx.inject
|
||||
import com.topjohnwu.magisk.ktx.writeTo
|
||||
import com.topjohnwu.magisk.utils.APKInstall
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.signing.JarMap
|
||||
import com.topjohnwu.signing.SignApk
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.lang.ref.WeakReference
|
||||
import java.security.SecureRandom
|
||||
|
||||
object HideAPK {
|
||||
|
||||
private const val ALPHA = "abcdefghijklmnopqrstuvwxyz"
|
||||
private const val ALPHADOTS = "$ALPHA....."
|
||||
private const val APP_NAME = "Magisk Manager"
|
||||
private const val APP_NAME = "Magisk"
|
||||
private const val ANDROID_MANIFEST = "AndroidManifest.xml"
|
||||
|
||||
// Some arbitrary limit
|
||||
@@ -49,7 +52,7 @@ object HideAPK {
|
||||
var next: Char
|
||||
var prev = 0.toChar()
|
||||
for (i in 0 until len) {
|
||||
next = if (prev == '.' || prev == 0.toChar() || i == len - 1) {
|
||||
next = if (prev == '.' || i == 0 || i == len - 1) {
|
||||
ALPHA[random.nextInt(ALPHA.length)]
|
||||
} else {
|
||||
ALPHADOTS[random.nextInt(ALPHADOTS.length)]
|
||||
@@ -59,19 +62,19 @@ object HideAPK {
|
||||
}
|
||||
if (!builder.contains('.')) {
|
||||
// Pick a random index and set it as dot
|
||||
val idx = random.nextInt(len - 1)
|
||||
builder[idx] = '.'
|
||||
val idx = random.nextInt(len - 2)
|
||||
builder[idx + 1] = '.'
|
||||
}
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
fun patch(
|
||||
context: Context,
|
||||
apk: String, out: String,
|
||||
apk: File, out: File,
|
||||
pkg: String, label: CharSequence
|
||||
): Boolean {
|
||||
try {
|
||||
val jar = JarMap.open(apk)
|
||||
val jar = JarMap.open(apk, true)
|
||||
val je = jar.getJarEntry(ANDROID_MANIFEST)
|
||||
val xml = AXML(jar.getRawData(je))
|
||||
|
||||
@@ -90,103 +93,76 @@ object HideAPK {
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun patchAndHide(context: Context, label: String): Boolean {
|
||||
val dlStub = !isRunningAsStub && SDK_INT >= 28 && Const.Version.atLeast_20_2()
|
||||
val src = if (dlStub) {
|
||||
val stub = File(context.cacheDir, "stub.apk")
|
||||
try {
|
||||
svc.fetchFile(Info.remote.stub.link).byteStream().use {
|
||||
it.writeTo(stub)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
stub.path
|
||||
} else {
|
||||
context.packageCodePath
|
||||
}
|
||||
private class WaitPackageReceiver(
|
||||
private val pkg: String,
|
||||
activity: Activity
|
||||
) : BroadcastReceiver() {
|
||||
|
||||
// Generate a new random package name and signature
|
||||
val repack = File(context.cacheDir, "patched.apk")
|
||||
val pkg = genPackageName()
|
||||
Config.keyStoreRaw = ""
|
||||
private val activity = WeakReference(activity)
|
||||
|
||||
if (!patch(context, src, repack.path, pkg, label))
|
||||
return false
|
||||
|
||||
// Install the application
|
||||
if (!Shell.su("adb_pm_install $repack").exec().isSuccess)
|
||||
return false
|
||||
|
||||
context.apply {
|
||||
val intent = packageManager.getLaunchIntentForPackage(pkg) ?: return false
|
||||
Config.suManager = pkg
|
||||
private fun launchApp(): Unit = activity.get()?.run {
|
||||
val intent = packageManager.getLaunchIntentForPackage(pkg) ?: return
|
||||
Config.suManager = if (pkg == APPLICATION_ID) "" else pkg
|
||||
grantUriPermission(pkg, APK_URI, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
grantUriPermission(pkg, PREFS_URI, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
intent.putExtra(Const.Key.PREV_PKG, packageName)
|
||||
startActivity(intent)
|
||||
}
|
||||
finish()
|
||||
} ?: Unit
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
fun hide(context: Context, label: String) {
|
||||
val dialog = ProgressDialog.show(context, context.getString(R.string.hide_manager_title), "", true)
|
||||
GlobalScope.launch {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
patchAndHide(context, label)
|
||||
}
|
||||
if (!result) {
|
||||
Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG)
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun downloadAndRestore(context: Context): Boolean {
|
||||
val apk = if (isRunningAsStub) {
|
||||
DynAPK.current(context)
|
||||
} else {
|
||||
File(context.cacheDir, "manager.apk").also { apk ->
|
||||
try {
|
||||
svc.fetchFile(Info.remote.app.link).byteStream().use {
|
||||
it.writeTo(apk)
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (intent.action ?: return) {
|
||||
Intent.ACTION_PACKAGE_REPLACED, Intent.ACTION_PACKAGE_ADDED -> {
|
||||
val newPkg = intent.data?.encodedSchemeSpecificPart.orEmpty()
|
||||
if (newPkg == pkg) {
|
||||
context.unregisterReceiver(this)
|
||||
launchApp()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Shell.su("adb_pm_install $apk").exec().isSuccess)
|
||||
return false
|
||||
}
|
||||
|
||||
context.apply {
|
||||
val intent = packageManager.getLaunchIntentForPackage(APPLICATION_ID) ?: return false
|
||||
Config.suManager = ""
|
||||
grantUriPermission(APPLICATION_ID, APK_URI, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
grantUriPermission(APPLICATION_ID, PREFS_URI, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
intent.putExtra(Const.Key.PREV_PKG, packageName)
|
||||
startActivity(intent)
|
||||
private suspend fun patchAndHide(activity: Activity, label: String): Boolean {
|
||||
val stub = File(activity.cacheDir, "stub.apk")
|
||||
try {
|
||||
svc.fetchFile(Info.remote.stub.link).byteStream().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()
|
||||
Config.keyStoreRaw = ""
|
||||
|
||||
if (!patch(activity, stub, repack, pkg, label))
|
||||
return false
|
||||
|
||||
// Install and auto launch app
|
||||
APKInstall.registerInstallReceiver(activity, WaitPackageReceiver(pkg, activity))
|
||||
if (!Shell.su("adb_pm_install $repack").exec().isSuccess)
|
||||
APKInstall.installHideResult(activity, repack)
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
fun restore(context: Context) {
|
||||
val dialog = ProgressDialog.show(context, context.getString(R.string.restore_img_msg), "", true)
|
||||
GlobalScope.launch {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
downloadAndRestore(context)
|
||||
}
|
||||
if (!result) {
|
||||
Utils.toast(R.string.restore_manager_fail_toast, Toast.LENGTH_LONG)
|
||||
dialog.dismiss()
|
||||
}
|
||||
suspend fun hide(activity: Activity, label: String) {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
patchAndHide(activity, label)
|
||||
}
|
||||
if (!result) {
|
||||
Utils.toast(R.string.failure, Toast.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
|
||||
fun restore(activity: Activity) {
|
||||
val apk = DynAPK.current(activity)
|
||||
APKInstall.registerInstallReceiver(activity, WaitPackageReceiver(APPLICATION_ID, activity))
|
||||
Shell.su("adb_pm_install $apk").submit {
|
||||
if (!it.isSuccess)
|
||||
APKInstall.installHideResult(activity, apk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,25 +1,23 @@
|
||||
package com.topjohnwu.magisk.core.tasks
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.core.os.postDelayed
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.DynAPK
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.*
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.di.Protected
|
||||
import com.topjohnwu.magisk.events.dialog.EnvFixDialog
|
||||
import com.topjohnwu.magisk.ktx.reboot
|
||||
import com.topjohnwu.magisk.ktx.symlink
|
||||
import com.topjohnwu.magisk.ktx.withStreams
|
||||
import com.topjohnwu.magisk.ktx.writeTo
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.signing.SignBoot
|
||||
import com.topjohnwu.superuser.Shell
|
||||
@@ -37,164 +35,171 @@ import org.kamranzafar.jtar.TarHeader
|
||||
import org.kamranzafar.jtar.TarInputStream
|
||||
import org.kamranzafar.jtar.TarOutputStream
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.get
|
||||
import org.koin.core.inject
|
||||
import timber.log.Timber
|
||||
import java.io.*
|
||||
import java.nio.ByteBuffer
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
abstract class MagiskInstallImpl : KoinComponent {
|
||||
abstract class MagiskInstallImpl protected constructor(
|
||||
protected val console: MutableList<String> = NOPList.getInstance(),
|
||||
private val logs: MutableList<String> = NOPList.getInstance()
|
||||
) : KoinComponent {
|
||||
|
||||
protected lateinit var installDir: File
|
||||
private lateinit var srcBoot: String
|
||||
private lateinit var zipUri: Uri
|
||||
|
||||
protected val console: MutableList<String>
|
||||
private val logs: MutableList<String>
|
||||
private var tarOut: TarOutputStream? = null
|
||||
protected var installDir = File("xxx")
|
||||
private lateinit var srcBoot: File
|
||||
|
||||
private val shell = Shell.getShell()
|
||||
private val service: NetworkService by inject()
|
||||
protected val context: Context by inject()
|
||||
|
||||
protected constructor() {
|
||||
console = NOPList.getInstance()
|
||||
logs = NOPList.getInstance()
|
||||
}
|
||||
|
||||
protected constructor(zip: Uri, out: MutableList<String>, err: MutableList<String>) {
|
||||
console = out
|
||||
logs = err
|
||||
zipUri = zip
|
||||
installDir = File(get<Context>(Protected).filesDir.parent, "install")
|
||||
"rm -rf $installDir".sh()
|
||||
installDir.mkdirs()
|
||||
}
|
||||
protected val context: Context by inject(Protected)
|
||||
private val useRootDir = shell.isRoot && Info.noDataExec
|
||||
|
||||
private fun findImage(): Boolean {
|
||||
srcBoot = "find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
|
||||
if (srcBoot.isEmpty()) {
|
||||
val bootPath = "find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
|
||||
if (bootPath.isEmpty()) {
|
||||
console.add("! Unable to detect target image")
|
||||
return false
|
||||
}
|
||||
console.add("- Target image: $srcBoot")
|
||||
srcBoot = SuFile(bootPath)
|
||||
console.add("- Target image: $bootPath")
|
||||
return true
|
||||
}
|
||||
|
||||
private fun findSecondaryImage(): Boolean {
|
||||
private fun findSecondary(): Boolean {
|
||||
val slot = "echo \$SLOT".fsh()
|
||||
val target = if (slot == "_a") "_b" else "_a"
|
||||
console.add("- Target slot: $target")
|
||||
srcBoot = arrayOf(
|
||||
val bootPath = arrayOf(
|
||||
"SLOT=$target",
|
||||
"find_boot_image",
|
||||
"SLOT=$slot",
|
||||
"echo \"\$BOOTIMAGE\"").fsh()
|
||||
if (srcBoot.isEmpty()) {
|
||||
if (bootPath.isEmpty()) {
|
||||
console.add("! Unable to detect target image")
|
||||
return false
|
||||
}
|
||||
console.add("- Target image: $srcBoot")
|
||||
srcBoot = SuFile(bootPath)
|
||||
console.add("- Target image: $bootPath")
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun extractZip(): Boolean {
|
||||
val arch = if (Build.VERSION.SDK_INT >= 21) {
|
||||
val abis = listOf(*Build.SUPPORTED_ABIS)
|
||||
if (abis.contains("x86")) "x86" else "arm"
|
||||
} else {
|
||||
if (Build.CPU_ABI == "x86") "x86" else "arm"
|
||||
}
|
||||
private fun extractFiles(): Boolean {
|
||||
console.add("- Device platform: ${Const.CPU_ABI}")
|
||||
console.add("- Installing: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
||||
|
||||
console.add("- Device platform: " + Build.CPU_ABI)
|
||||
console.add("- Magisk Manager: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
|
||||
console.add("- Install target: ${Info.remote.magisk.version} (${Info.remote.magisk.versionCode})")
|
||||
installDir = File(context.filesDir.parent, "install")
|
||||
installDir.deleteRecursively()
|
||||
installDir.mkdirs()
|
||||
|
||||
try {
|
||||
ZipInputStream(zipUri.inputStream().buffered()).use { zi ->
|
||||
lateinit var ze: ZipEntry
|
||||
while (zi.nextEntry?.let { ze = it } != null) {
|
||||
if (ze.isDirectory)
|
||||
continue
|
||||
var name: String? = null
|
||||
val names = arrayOf("$arch/", "common/", "META-INF/com/google/android/update-binary")
|
||||
for (n in names) {
|
||||
ze.name.run {
|
||||
if (startsWith(n)) {
|
||||
name = substring(lastIndexOf('/') + 1)
|
||||
}
|
||||
}
|
||||
name ?: continue
|
||||
break
|
||||
}
|
||||
if (name == null && ze.name.startsWith("chromeos/"))
|
||||
name = ze.name
|
||||
name?.also {
|
||||
val dest = if (installDir is SuFile)
|
||||
SuFile(installDir, it)
|
||||
else
|
||||
File(installDir, it)
|
||||
dest.parentFile!!.mkdirs()
|
||||
SuFileOutputStream(dest).use { s -> zi.copyTo(s) }
|
||||
} ?: continue
|
||||
// Extract binaries
|
||||
if (isRunningAsStub) {
|
||||
val zf = ZipFile(DynAPK.current(context))
|
||||
zf.entries().asSequence().filter {
|
||||
!it.isDirectory && it.name.startsWith("lib/${Const.CPU_ABI_32}/")
|
||||
}.forEach {
|
||||
val n = it.name.substring(it.name.lastIndexOf('/') + 1)
|
||||
val name = n.substring(3, n.length - 3)
|
||||
val dest = File(installDir, name)
|
||||
zf.getInputStream(it).writeTo(dest)
|
||||
}
|
||||
} else {
|
||||
val libs = Const.NATIVE_LIB_DIR.listFiles { _, name ->
|
||||
name.startsWith("lib") && name.endsWith(".so")
|
||||
} ?: emptyArray()
|
||||
for (lib in libs) {
|
||||
val name = lib.name.substring(3, lib.name.length - 3)
|
||||
symlink(lib.path, "$installDir/$name")
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
console.add("! Cannot unzip zip")
|
||||
|
||||
// Extract scripts
|
||||
for (script in listOf("util_functions.sh", "boot_patch.sh", "addon.d.sh")) {
|
||||
val dest = File(installDir, script)
|
||||
context.assets.open(script).writeTo(dest)
|
||||
}
|
||||
// Extract chromeos tools
|
||||
File(installDir, "chromeos").mkdir()
|
||||
for (file in listOf("futility", "kernel_data_key.vbprivk", "kernel.keyblock")) {
|
||||
val name = "chromeos/$file"
|
||||
val dest = File(installDir, name)
|
||||
context.assets.open(name).writeTo(dest)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
console.add("! Unable to extract files")
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
|
||||
val init64 = SuFile.open(installDir, "magiskinit64")
|
||||
if (Build.VERSION.SDK_INT >= 21 && Build.SUPPORTED_64_BIT_ABIS.isNotEmpty()) {
|
||||
init64.renameTo(SuFile.open(installDir, "magiskinit"))
|
||||
} else {
|
||||
init64.delete()
|
||||
if (useRootDir) {
|
||||
// Move everything to tmpfs to workaround Samsung bullshit
|
||||
SuFile(Const.TMPDIR).also {
|
||||
arrayOf(
|
||||
"rm -rf $it",
|
||||
"mkdir -p $it",
|
||||
"cp_readlink $installDir $it",
|
||||
"rm -rf $installDir"
|
||||
).sh()
|
||||
installDir = it
|
||||
}
|
||||
}
|
||||
"cd $installDir; chmod 755 *".sh()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun newEntry(name: String, size: Long): TarEntry {
|
||||
// Optimization for SuFile I/O streams to skip an internal trial and error
|
||||
private fun installDirFile(name: String): File {
|
||||
return if (useRootDir)
|
||||
SuFile(installDir, name)
|
||||
else
|
||||
File(installDir, name)
|
||||
}
|
||||
|
||||
private fun InputStream.cleanPump(out: OutputStream) = withStreams(this, out) { src, _ ->
|
||||
src.copyTo(out)
|
||||
}
|
||||
|
||||
private fun newTarEntry(name: String, size: Long): TarEntry {
|
||||
console.add("-- Writing: $name")
|
||||
return TarEntry(TarHeader.createHeader(name, size, 0, false, 420 /* 0644 */))
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun handleTar(input: InputStream, output: OutputStream): OutputStream {
|
||||
private fun processTar(input: InputStream, output: OutputStream): OutputStream {
|
||||
console.add("- Processing tar file")
|
||||
val tarOut = TarOutputStream(output)
|
||||
TarInputStream(input).use { tarIn ->
|
||||
lateinit var entry: TarEntry
|
||||
|
||||
fun decompressedStream() =
|
||||
if (entry.name.contains(".lz4")) LZ4FrameInputStream(tarIn) else tarIn
|
||||
fun decompressedStream(): InputStream {
|
||||
val src = if (entry.name.endsWith(".lz4")) LZ4FrameInputStream(tarIn) else tarIn
|
||||
return object : FilterInputStream(src) {
|
||||
override fun available() = 0 /* Workaround bug in LZ4FrameInputStream */
|
||||
override fun close() { /* Never close src stream */ }
|
||||
}
|
||||
}
|
||||
|
||||
while (tarIn.nextEntry?.let { entry = it } != null) {
|
||||
if (entry.name.contains("boot.img") ||
|
||||
if (entry.name.startsWith("boot.img") ||
|
||||
(Config.recovery && entry.name.contains("recovery.img"))) {
|
||||
val name = entry.name.replace(".lz4", "")
|
||||
console.add("-- Extracting: $name")
|
||||
|
||||
val extract = File(installDir, name)
|
||||
FileOutputStream(extract).use { decompressedStream().copyTo(it) }
|
||||
val extract = installDirFile(name)
|
||||
decompressedStream().cleanPump(SuFileOutputStream.open(extract))
|
||||
} else if (entry.name.contains("vbmeta.img")) {
|
||||
val rawData = ByteArrayOutputStream().let {
|
||||
decompressedStream().copyTo(it)
|
||||
it.toByteArray()
|
||||
}
|
||||
val rawData = decompressedStream().readBytes()
|
||||
// Valid vbmeta.img should be at least 256 bytes
|
||||
if (rawData.size < 256)
|
||||
continue
|
||||
|
||||
// Patch flags to AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED
|
||||
// Patch flags to AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED |
|
||||
// AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED
|
||||
console.add("-- Patching: vbmeta.img")
|
||||
ByteBuffer.wrap(rawData).putInt(120, 2)
|
||||
tarOut.putNextEntry(newEntry("vbmeta.img", rawData.size.toLong()))
|
||||
ByteBuffer.wrap(rawData).putInt(120, 3)
|
||||
tarOut.putNextEntry(newTarEntry("vbmeta.img", rawData.size.toLong()))
|
||||
tarOut.write(rawData)
|
||||
} else {
|
||||
console.add("-- Copying: ${entry.name}")
|
||||
@@ -202,29 +207,32 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
tarIn.copyTo(tarOut, bufferSize = 1024 * 1024)
|
||||
}
|
||||
}
|
||||
val boot = SuFile.open(installDir, "boot.img")
|
||||
val recovery = SuFile.open(installDir, "recovery.img")
|
||||
if (recovery.exists() && boot.exists()) {
|
||||
// Install Magisk to recovery
|
||||
srcBoot = recovery.path
|
||||
// Repack boot image to prevent restore
|
||||
arrayOf(
|
||||
"./magiskboot unpack boot.img",
|
||||
"./magiskboot repack boot.img",
|
||||
"./magiskboot cleanup",
|
||||
"mv new-boot.img boot.img").sh()
|
||||
SuFileInputStream(boot).use {
|
||||
tarOut.putNextEntry(newEntry("boot.img", boot.length()))
|
||||
it.copyTo(tarOut)
|
||||
}
|
||||
boot.delete()
|
||||
} else {
|
||||
if (!boot.exists()) {
|
||||
console.add("! No boot image found")
|
||||
throw IOException()
|
||||
}
|
||||
srcBoot = boot.path
|
||||
}
|
||||
val boot = installDirFile("boot.img")
|
||||
val recovery = installDirFile("recovery.img")
|
||||
if (Config.recovery && recovery.exists() && boot.exists()) {
|
||||
// Install to recovery
|
||||
srcBoot = recovery
|
||||
// Repack boot image to prevent auto restore
|
||||
arrayOf(
|
||||
"cd $installDir",
|
||||
"./magiskboot unpack boot.img",
|
||||
"./magiskboot repack boot.img",
|
||||
"cat new-boot.img > boot.img",
|
||||
"./magiskboot cleanup",
|
||||
"rm -f new-boot.img",
|
||||
"cd /").sh()
|
||||
SuFileInputStream.open(boot).use {
|
||||
tarOut.putNextEntry(newTarEntry("boot.img", boot.length()))
|
||||
it.copyTo(tarOut)
|
||||
}
|
||||
boot.delete()
|
||||
} else {
|
||||
if (!boot.exists()) {
|
||||
console.add("! No boot image found")
|
||||
throw IOException()
|
||||
}
|
||||
srcBoot = boot
|
||||
}
|
||||
return tarOut
|
||||
}
|
||||
@@ -247,20 +255,22 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
val alpha = "abcdefghijklmnopqrstuvwxyz"
|
||||
val alphaNum = "$alpha${alpha.toUpperCase(Locale.ROOT)}0123456789"
|
||||
val random = SecureRandom()
|
||||
val suffix = StringBuilder()
|
||||
for (i in 1..5) {
|
||||
suffix.append(alphaNum[random.nextInt(alphaNum.length)])
|
||||
val filename = StringBuilder("magisk_patched_").run {
|
||||
for (i in 1..5) {
|
||||
append(alphaNum[random.nextInt(alphaNum.length)])
|
||||
}
|
||||
toString()
|
||||
}
|
||||
|
||||
val filename = "magisk_patched_$suffix"
|
||||
outStream = if (magic.contentEquals("ustar".toByteArray())) {
|
||||
// tar file
|
||||
outFile = MediaStoreUtils.getFile("$filename.tar", true)
|
||||
handleTar(src, outFile!!.uri.outputStream())
|
||||
processTar(src, outFile!!.uri.outputStream())
|
||||
} else {
|
||||
// Raw image
|
||||
srcBoot = File(installDir, "boot.img").path
|
||||
// raw image
|
||||
srcBoot = installDirFile("boot.img")
|
||||
console.add("- Copying image to cache")
|
||||
FileOutputStream(srcBoot).use { src.copyTo(it) }
|
||||
src.cleanPump(SuFileOutputStream.open(srcBoot))
|
||||
outFile = MediaStoreUtils.getFile("$filename.img", true)
|
||||
outFile!!.uri.outputStream()
|
||||
}
|
||||
@@ -280,13 +290,13 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
|
||||
// Output file
|
||||
try {
|
||||
val patched = SuFile.open(installDir, "new-boot.img")
|
||||
val newBoot = installDirFile("new-boot.img")
|
||||
if (outStream is TarOutputStream) {
|
||||
val name = if (srcBoot.contains("recovery")) "recovery.img" else "boot.img"
|
||||
outStream.putNextEntry(newEntry(name, patched.length()))
|
||||
val name = if (srcBoot.path.contains("recovery")) "recovery.img" else "boot.img"
|
||||
outStream.putNextEntry(newTarEntry(name, newBoot.length()))
|
||||
}
|
||||
withStreams(SuFileInputStream(patched), outStream) { src, out -> src.copyTo(out) }
|
||||
patched.delete()
|
||||
SuFileInputStream.open(newBoot).cleanPump(outStream)
|
||||
newBoot.delete()
|
||||
|
||||
console.add("")
|
||||
console.add("****************************")
|
||||
@@ -300,118 +310,107 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
return false
|
||||
}
|
||||
|
||||
// Fix up binaries
|
||||
srcBoot.delete()
|
||||
if (shell.isRoot) {
|
||||
"fix_env $installDir".sh()
|
||||
} else {
|
||||
"cp_readlink $installDir".sh()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun patchBoot(): Boolean {
|
||||
var srcNand = ""
|
||||
if ("[ -c $srcBoot ] && nanddump -f boot.img $srcBoot".sh().isSuccess) {
|
||||
srcNand = srcBoot
|
||||
srcBoot = File(installDir, "boot.img").path
|
||||
}
|
||||
|
||||
var isSigned: Boolean
|
||||
try {
|
||||
SuFileInputStream(srcBoot).use {
|
||||
isSigned = SignBoot.verifySignature(it, null)
|
||||
if (isSigned) {
|
||||
console.add("- Boot image is signed with AVB 1.0")
|
||||
var isSigned = false
|
||||
if (srcBoot.let { it !is SuFile || !it.isCharacter }) {
|
||||
try {
|
||||
SuFileInputStream.open(srcBoot).use {
|
||||
if (SignBoot.verifySignature(it, null)) {
|
||||
isSigned = true
|
||||
console.add("- Boot image is signed with AVB 1.0")
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
console.add("! Unable to check signature")
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
console.add("! Unable to check signature")
|
||||
}
|
||||
|
||||
val newBoot = installDirFile("new-boot.img")
|
||||
if (!useRootDir) {
|
||||
// Create output files before hand
|
||||
newBoot.createNewFile()
|
||||
File(installDir, "stock_boot.img").createNewFile()
|
||||
}
|
||||
|
||||
val cmds = arrayOf(
|
||||
"cd $installDir",
|
||||
"KEEPFORCEENCRYPT=${Config.keepEnc} " +
|
||||
"KEEPVERITY=${Config.keepVerity} " +
|
||||
"RECOVERYMODE=${Config.recovery} " +
|
||||
"sh boot_patch.sh $srcBoot")
|
||||
|
||||
if (!cmds.sh().isSuccess)
|
||||
return false
|
||||
}
|
||||
|
||||
if (!("KEEPFORCEENCRYPT=${Config.keepEnc} KEEPVERITY=${Config.keepVerity} " +
|
||||
"RECOVERYMODE=${Config.recovery} sh update-binary " +
|
||||
"sh boot_patch.sh $srcBoot").sh().isSuccess) {
|
||||
return false
|
||||
}
|
||||
val job = shell.newJob().add("./magiskboot cleanup", "cd /")
|
||||
|
||||
if (srcNand.isNotEmpty()) {
|
||||
srcBoot = srcNand
|
||||
}
|
||||
|
||||
val job = Shell.sh(
|
||||
"./magiskboot cleanup",
|
||||
"mv bin/busybox busybox",
|
||||
"rm -rf magisk.apk bin boot.img update-binary",
|
||||
"cd /")
|
||||
|
||||
val patched = File(installDir, "new-boot.img")
|
||||
if (isSigned) {
|
||||
console.add("- Signing boot image with verity keys")
|
||||
val signed = File(installDir, "signed.img")
|
||||
val signed = File.createTempFile("signed", ".img", context.cacheDir)
|
||||
try {
|
||||
withStreams(SuFileInputStream(patched), signed.outputStream().buffered()) {
|
||||
input, out -> SignBoot.doSignature(null, null, input, out, "/boot")
|
||||
val src = SuFileInputStream.open(newBoot).buffered()
|
||||
val out = signed.outputStream().buffered()
|
||||
withStreams(src, out) { _, _ ->
|
||||
SignBoot.doSignature(null, null, src, out, "/boot")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
console.add("! Unable to sign image")
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
|
||||
job.add("mv -f $signed $patched")
|
||||
job.add("cat $signed > $newBoot", "rm -f $signed")
|
||||
}
|
||||
job.exec()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun copySepolicyRules(): Boolean {
|
||||
if (Info.remote.magisk.versionCode >= 21100) return true
|
||||
// Copy existing rules for migration
|
||||
"copy_sepolicy_rules".sh()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun flashBoot(): Boolean {
|
||||
if (!"direct_install $installDir $srcBoot".sh().isSuccess)
|
||||
return false
|
||||
"run_migrations".sh()
|
||||
return true
|
||||
}
|
||||
private fun flashBoot() = "direct_install $installDir $srcBoot".sh().isSuccess
|
||||
|
||||
private suspend fun postOTA(): Boolean {
|
||||
val bootctl = SuFile("/data/adb/bootctl")
|
||||
try {
|
||||
withStreams(service.fetchBootctl().byteStream(), SuFileOutputStream(bootctl)) {
|
||||
it, out -> it.copyTo(out)
|
||||
}
|
||||
val bootctl = File.createTempFile("bootctl", null, context.cacheDir)
|
||||
service.fetchBootctl().byteStream().writeTo(bootctl)
|
||||
"post_ota $bootctl".sh()
|
||||
} catch (e: IOException) {
|
||||
console.add("! Unable to download bootctl")
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
|
||||
"post_ota ${bootctl.parent}".sh()
|
||||
|
||||
console.add("***************************************")
|
||||
console.add(" Next reboot will boot to second slot!")
|
||||
console.add("***************************************")
|
||||
return true
|
||||
}
|
||||
|
||||
private fun String.sh() = Shell.sh(this).to(console, logs).exec()
|
||||
private fun Array<String>.sh() = Shell.sh(*this).to(console, logs).exec()
|
||||
private fun String.fsh() = ShellUtils.fastCmd(this)
|
||||
private fun Array<String>.fsh() = ShellUtils.fastCmd(*this)
|
||||
private fun String.sh() = shell.newJob().add(this).to(console, logs).exec()
|
||||
private fun Array<String>.sh() = shell.newJob().add(*this).to(console, logs).exec()
|
||||
private fun String.fsh() = ShellUtils.fastCmd(shell, this)
|
||||
private fun Array<String>.fsh() = ShellUtils.fastCmd(shell, *this)
|
||||
|
||||
protected fun doPatchFile(patchFile: Uri) = extractZip() && handleFile(patchFile)
|
||||
protected fun doPatchFile(patchFile: Uri) = extractFiles() && handleFile(patchFile)
|
||||
|
||||
protected fun direct() = findImage() && extractZip() && patchBoot() &&
|
||||
copySepolicyRules() && flashBoot()
|
||||
protected fun direct() = findImage() && extractFiles() && patchBoot() && flashBoot()
|
||||
|
||||
protected suspend fun secondSlot() = findSecondaryImage() && extractZip() &&
|
||||
patchBoot() && copySepolicyRules() && flashBoot() && postOTA()
|
||||
protected suspend fun secondSlot() =
|
||||
findSecondary() && extractFiles() && patchBoot() && flashBoot() && postOTA()
|
||||
|
||||
protected fun fixEnv(zip: Uri): Boolean {
|
||||
installDir = SuFile("/data/adb/magisk")
|
||||
Shell.su("rm -rf /data/adb/magisk/*").exec()
|
||||
zipUri = zip
|
||||
return extractZip() && Shell.su("fix_env").exec().isSuccess
|
||||
}
|
||||
protected fun fixEnv() = extractFiles() && "fix_env $installDir".sh().isSuccess
|
||||
|
||||
protected fun uninstall() = "run_uninstaller ${AssetHack.apk}".sh().isSuccess
|
||||
|
||||
@WorkerThread
|
||||
protected abstract suspend fun operations(): Boolean
|
||||
@@ -419,11 +418,10 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
open suspend fun exec() = withContext(Dispatchers.IO) { operations() }
|
||||
}
|
||||
|
||||
sealed class MagiskInstaller(
|
||||
file: Uri,
|
||||
abstract class MagiskInstaller(
|
||||
console: MutableList<String>,
|
||||
logs: MutableList<String>
|
||||
) : MagiskInstallImpl(file, console, logs) {
|
||||
) : MagiskInstallImpl(console, logs) {
|
||||
|
||||
override suspend fun exec(): Boolean {
|
||||
val success = super.exec()
|
||||
@@ -437,46 +435,64 @@ sealed class MagiskInstaller(
|
||||
}
|
||||
|
||||
class Patch(
|
||||
file: Uri,
|
||||
private val uri: Uri,
|
||||
console: MutableList<String>,
|
||||
logs: MutableList<String>
|
||||
) : MagiskInstaller(file, console, logs) {
|
||||
) : MagiskInstaller(console, logs) {
|
||||
override suspend fun operations() = doPatchFile(uri)
|
||||
}
|
||||
|
||||
class SecondSlot(
|
||||
file: Uri,
|
||||
console: MutableList<String>,
|
||||
logs: MutableList<String>
|
||||
) : MagiskInstaller(file, console, logs) {
|
||||
) : MagiskInstaller(console, logs) {
|
||||
override suspend fun operations() = secondSlot()
|
||||
}
|
||||
|
||||
class Direct(
|
||||
file: Uri,
|
||||
console: MutableList<String>,
|
||||
logs: MutableList<String>
|
||||
) : MagiskInstaller(file, console, logs) {
|
||||
) : MagiskInstaller(console, logs) {
|
||||
override suspend fun operations() = direct()
|
||||
}
|
||||
|
||||
}
|
||||
class Emulator(
|
||||
console: MutableList<String>,
|
||||
logs: MutableList<String>
|
||||
) : MagiskInstaller(console, logs) {
|
||||
override suspend fun operations() = fixEnv()
|
||||
}
|
||||
|
||||
class EnvFixTask(
|
||||
private val zip: Uri
|
||||
) : MagiskInstallImpl() {
|
||||
override suspend fun operations() = fixEnv(zip)
|
||||
class Uninstall(
|
||||
console: MutableList<String>,
|
||||
logs: MutableList<String>
|
||||
) : MagiskInstallImpl(console, logs) {
|
||||
override suspend fun operations() = uninstall()
|
||||
|
||||
override suspend fun exec(): Boolean {
|
||||
val success = super.exec()
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(EnvFixDialog.DISMISS))
|
||||
Utils.toast(
|
||||
if (success) R.string.reboot_delay_toast else R.string.setup_fail,
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
if (success)
|
||||
UiThreadHandler.handler.postDelayed(5000) { reboot() }
|
||||
return success
|
||||
override suspend fun exec(): Boolean {
|
||||
val success = super.exec()
|
||||
if (success) {
|
||||
UiThreadHandler.handler.postDelayed(3000) {
|
||||
Shell.su("pm uninstall ${context.packageName}").exec()
|
||||
}
|
||||
}
|
||||
return success
|
||||
}
|
||||
}
|
||||
|
||||
class FixEnv(private val callback: () -> Unit) : MagiskInstallImpl() {
|
||||
override suspend fun operations() = fixEnv()
|
||||
|
||||
override suspend fun exec(): Boolean {
|
||||
val success = super.exec()
|
||||
callback()
|
||||
Utils.toast(
|
||||
if (success) R.string.reboot_delay_toast else R.string.setup_fail,
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
if (success)
|
||||
UiThreadHandler.handler.postDelayed(5000) { reboot() }
|
||||
return success
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,20 +3,16 @@
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.AssetManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.util.DisplayMetrics
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.AssetHack
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.ResMgr
|
||||
import com.topjohnwu.magisk.core.addAssetPath
|
||||
import com.topjohnwu.magisk.ktx.langTagToLocale
|
||||
import com.topjohnwu.magisk.ktx.toLangTag
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
import kotlin.Comparator
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
var currentLocale: Locale = Locale.getDefault()
|
||||
@@ -30,11 +26,8 @@ suspend fun availableLocales() = cachedLocales ?:
|
||||
withContext(Dispatchers.Default) {
|
||||
val compareId = R.string.app_changelog
|
||||
|
||||
// Create a completely new resource to prevent cross talk over app's configs
|
||||
val asset = AssetManager::class.java.newInstance().apply { addAssetPath(ResMgr.apk) }
|
||||
val config = Configuration(ResMgr.resource.configuration)
|
||||
val metrics = DisplayMetrics().apply { setTo(ResMgr.resource.displayMetrics) }
|
||||
val res = Resources(asset, metrics, config)
|
||||
// Create a completely new resource to prevent cross talk over active configs
|
||||
val res = AssetHack.newResource()
|
||||
|
||||
val locales = ArrayList<String>().apply {
|
||||
// Add default locale
|
||||
@@ -45,19 +38,17 @@ withContext(Dispatchers.Default) {
|
||||
add("pt-BR")
|
||||
|
||||
// Then add all supported locales
|
||||
addAll(res.assets.locales)
|
||||
addAll(Resources.getSystem().assets.locales)
|
||||
}.map {
|
||||
it.langTagToLocale()
|
||||
}.distinctBy {
|
||||
config.setLocale(it)
|
||||
res.updateConfiguration(config, metrics)
|
||||
res.updateLocale(it)
|
||||
res.getString(compareId)
|
||||
}.sortedWith(Comparator { a, b ->
|
||||
}.sortedWith { a, b ->
|
||||
a.getDisplayName(a).compareTo(b.getDisplayName(b), true)
|
||||
})
|
||||
}
|
||||
|
||||
config.setLocale(defaultLocale)
|
||||
res.updateConfiguration(config, metrics)
|
||||
res.updateLocale(defaultLocale)
|
||||
val defName = res.getString(R.string.system_default)
|
||||
|
||||
val names = ArrayList<String>(locales.size + 1)
|
||||
@@ -79,6 +70,11 @@ fun Resources.updateConfig(config: Configuration = configuration) {
|
||||
updateConfiguration(config, displayMetrics)
|
||||
}
|
||||
|
||||
fun Resources.updateLocale(locale: Locale) {
|
||||
configuration.setLocale(locale)
|
||||
updateConfiguration(configuration, displayMetrics)
|
||||
}
|
||||
|
||||
fun refreshLocale() {
|
||||
val localeConfig = Config.locale
|
||||
currentLocale = when {
|
||||
@@ -86,5 +82,5 @@ fun refreshLocale() {
|
||||
else -> localeConfig.langTagToLocale()
|
||||
}
|
||||
Locale.setDefault(currentLocale)
|
||||
ResMgr.resource.updateConfig()
|
||||
AssetHack.resource.updateConfig()
|
||||
}
|
||||
|
@@ -1,59 +0,0 @@
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.wrap
|
||||
import com.topjohnwu.magisk.ktx.rawResource
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
|
||||
class RootInit : Shell.Initializer() {
|
||||
|
||||
override fun onInit(context: Context, shell: Shell): Boolean {
|
||||
return init(context.wrap(), shell)
|
||||
}
|
||||
|
||||
fun init(context: Context, shell: Shell): Boolean {
|
||||
shell.newJob().apply {
|
||||
add("export SDK_INT=${Build.VERSION.SDK_INT}")
|
||||
if (Const.Version.atLeast_20_4()) {
|
||||
add("export MAGISKTMP=\$(magisk --path)/.magisk")
|
||||
} else {
|
||||
add("export MAGISKTMP=/sbin/.magisk")
|
||||
}
|
||||
if (Const.Version.atLeast_21_0()) {
|
||||
add("export ASH_STANDALONE=1")
|
||||
add("[ -x /data/adb/magisk/busybox ] && exec /data/adb/magisk/busybox sh")
|
||||
} else {
|
||||
add("export PATH=\"\$MAGISKTMP/busybox:\$PATH\"")
|
||||
}
|
||||
add(context.rawResource(R.raw.manager))
|
||||
if (shell.isRoot) {
|
||||
add(context.rawResource(R.raw.util_functions))
|
||||
}
|
||||
add("mm_init")
|
||||
}.exec()
|
||||
|
||||
fun fastCmd(cmd: String) = ShellUtils.fastCmd(shell, cmd)
|
||||
fun getVar(name: String) = fastCmd("echo \$$name")
|
||||
fun getBool(name: String) = getVar(name).toBoolean()
|
||||
|
||||
Const.MAGISKTMP = getVar("MAGISKTMP")
|
||||
Info.isSAR = getBool("SYSTEM_ROOT")
|
||||
Info.ramdisk = getBool("RAMDISKEXIST")
|
||||
Info.isAB = getBool("ISAB")
|
||||
Info.crypto = getVar("CRYPTOTYPE")
|
||||
Info.isPixel = fastCmd("getprop ro.product.brand") == "google"
|
||||
|
||||
// Default presets
|
||||
Config.recovery = getBool("RECOVERYMODE")
|
||||
Config.keepVerity = getBool("KEEPVERITY")
|
||||
Config.keepEnc = getBool("KEEPFORCEENCRYPT")
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
102
app/src/main/java/com/topjohnwu/magisk/core/utils/ShellInit.kt
Normal file
102
app/src/main/java/com/topjohnwu/magisk/core/utils/ShellInit.kt
Normal file
@@ -0,0 +1,102 @@
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import com.topjohnwu.magisk.DynAPK
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.*
|
||||
import com.topjohnwu.magisk.ktx.cachedFile
|
||||
import com.topjohnwu.magisk.ktx.deviceProtectedContext
|
||||
import com.topjohnwu.magisk.ktx.rawResource
|
||||
import com.topjohnwu.magisk.ktx.writeTo
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import java.io.File
|
||||
import java.util.jar.JarFile
|
||||
|
||||
abstract class BaseShellInit : Shell.Initializer() {
|
||||
final override fun onInit(context: Context, shell: Shell): Boolean {
|
||||
return init(context.wrap(), shell)
|
||||
}
|
||||
|
||||
abstract fun init(context: Context, shell: Shell): Boolean
|
||||
}
|
||||
|
||||
|
||||
class BusyBoxInit : BaseShellInit() {
|
||||
|
||||
override fun init(context: Context, shell: Shell): Boolean {
|
||||
shell.newJob().apply {
|
||||
add("export ASH_STANDALONE=1")
|
||||
|
||||
val localBB: File
|
||||
if (isRunningAsStub) {
|
||||
if (!shell.isRoot)
|
||||
return true
|
||||
val jar = JarFile(DynAPK.current(context))
|
||||
val bb = jar.getJarEntry("lib/${Const.CPU_ABI_32}/libbusybox.so")
|
||||
localBB = context.deviceProtectedContext.cachedFile("busybox")
|
||||
localBB.delete()
|
||||
jar.getInputStream(bb).writeTo(localBB)
|
||||
localBB.setExecutable(true)
|
||||
} else {
|
||||
localBB = File(Const.NATIVE_LIB_DIR, "libbusybox.so")
|
||||
}
|
||||
|
||||
if (shell.isRoot) {
|
||||
add("export MAGISKTMP=\$(magisk --path)/.magisk")
|
||||
// Test if we can properly execute stuff in /data
|
||||
Info.noDataExec = !shell.newJob().add("$localBB true").exec().isSuccess
|
||||
}
|
||||
|
||||
if (Info.noDataExec) {
|
||||
// Copy it out of /data to workaround Samsung bullshit
|
||||
add(
|
||||
"if [ -x \$MAGISKTMP/busybox/busybox ]; then",
|
||||
" cp -af $localBB \$MAGISKTMP/busybox/busybox",
|
||||
" exec \$MAGISKTMP/busybox/busybox sh",
|
||||
"else",
|
||||
" cp -af $localBB /dev/.busybox",
|
||||
" exec /dev/.busybox sh",
|
||||
"fi"
|
||||
)
|
||||
} else {
|
||||
// Directly execute the file
|
||||
add("exec $localBB sh")
|
||||
}
|
||||
}.exec()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
class AppShellInit : BaseShellInit() {
|
||||
|
||||
override fun init(context: Context, shell: Shell): Boolean {
|
||||
|
||||
fun fastCmd(cmd: String) = ShellUtils.fastCmd(shell, cmd)
|
||||
fun getVar(name: String) = fastCmd("echo \$$name")
|
||||
fun getBool(name: String) = getVar(name).toBoolean()
|
||||
|
||||
shell.newJob().apply {
|
||||
add("export API=${Build.VERSION.SDK_INT}")
|
||||
add(context.rawResource(R.raw.manager))
|
||||
if (shell.isRoot) {
|
||||
add(context.assets.open("util_functions.sh"))
|
||||
}
|
||||
add("app_init")
|
||||
}.exec()
|
||||
|
||||
Const.MAGISKTMP = getVar("MAGISKTMP")
|
||||
Info.isSAR = getBool("SYSTEM_ROOT")
|
||||
Info.ramdisk = getBool("RAMDISKEXIST")
|
||||
Info.isAB = getBool("ISAB")
|
||||
Info.crypto = getVar("CRYPTOTYPE")
|
||||
|
||||
// Default presets
|
||||
Config.recovery = getBool("RECOVERYMODE")
|
||||
Config.keepVerity = getBool("KEEPVERITY")
|
||||
Config.keepEnc = getBool("KEEPFORCEENCRYPT")
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
@@ -36,7 +36,7 @@ fun InputStream.unzip(folder: File, path: String, junkPath: Boolean) {
|
||||
dest = SuFile(folder, name)
|
||||
dest.parentFile!!.mkdirs()
|
||||
}
|
||||
SuFileOutputStream(dest).use { out -> zin.copyTo(out) }
|
||||
SuFileOutputStream.open(dest).use { out -> zin.copyTo(out) }
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
|
@@ -8,7 +8,9 @@ import com.topjohnwu.magisk.core.Config.Value.DEFAULT_CHANNEL
|
||||
import com.topjohnwu.magisk.core.Config.Value.STABLE_CHANNEL
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.model.*
|
||||
import com.topjohnwu.magisk.core.model.MagiskJson
|
||||
import com.topjohnwu.magisk.core.model.StubJson
|
||||
import com.topjohnwu.magisk.core.model.UpdateInfo
|
||||
import com.topjohnwu.magisk.data.network.*
|
||||
import retrofit2.HttpException
|
||||
import timber.log.Timber
|
||||
@@ -34,7 +36,6 @@ class NetworkService(
|
||||
Config.updateChannel = BETA_CHANNEL
|
||||
info = fetchBetaUpdate()
|
||||
}
|
||||
Info.remote = info
|
||||
info
|
||||
}
|
||||
|
||||
@@ -47,16 +48,12 @@ class NetworkService(
|
||||
val info = jsd.fetchCanaryUpdate(sha)
|
||||
|
||||
fun genCDNUrl(name: String) = "${Const.Url.JS_DELIVR_URL}${MAGISK_FILES}@${sha}/${name}"
|
||||
fun ManagerJson.updateCopy() = copy(link = genCDNUrl(link), note = genCDNUrl(note))
|
||||
fun MagiskJson.updateCopy() = copy(link = genCDNUrl(link), note = genCDNUrl(note))
|
||||
fun StubJson.updateCopy() = copy(link = genCDNUrl(link))
|
||||
fun UninstallerJson.updateCopy() = copy(link = genCDNUrl(link))
|
||||
|
||||
return info.copy(
|
||||
app = info.app.updateCopy(),
|
||||
magisk = info.magisk.updateCopy(),
|
||||
stub = info.stub.updateCopy(),
|
||||
uninstaller = info.uninstaller.updateCopy()
|
||||
stub = info.stub.updateCopy()
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -1,9 +1,9 @@
|
||||
package com.topjohnwu.magisk.di
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.topjohnwu.magisk.core.ResMgr
|
||||
import com.topjohnwu.magisk.core.AssetHack
|
||||
import com.topjohnwu.magisk.ktx.deviceProtectedContext
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
|
||||
@@ -11,15 +11,9 @@ val SUTimeout = named("su_timeout")
|
||||
val Protected = named("protected")
|
||||
|
||||
val applicationModule = module {
|
||||
factory { ResMgr.resource }
|
||||
factory { AssetHack.resource }
|
||||
factory { get<Context>().packageManager }
|
||||
factory(Protected) { createDEContext(get()) }
|
||||
factory(Protected) { get<Context>().deviceProtectedContext }
|
||||
single(SUTimeout) { get<Context>(Protected).getSharedPreferences("su_timeout", 0) }
|
||||
single { PreferenceManager.getDefaultSharedPreferences(get<Context>(Protected)) }
|
||||
}
|
||||
|
||||
private fun createDEContext(context: Context): Context {
|
||||
return if (Build.VERSION.SDK_INT >= 24)
|
||||
context.createDeviceProtectedStorageContext()
|
||||
else context
|
||||
single { PreferenceManager.getDefaultSharedPreferences(get(Protected)) }
|
||||
}
|
||||
|
@@ -29,6 +29,6 @@ val viewModelModules = module {
|
||||
viewModel { MainViewModel() }
|
||||
|
||||
// Legacy
|
||||
viewModel { (args: FlashFragmentArgs) -> FlashViewModel(args, get()) }
|
||||
viewModel { (args: FlashFragmentArgs) -> FlashViewModel(args) }
|
||||
viewModel { SuRequestViewModel(get(), get(), get(SUTimeout), get()) }
|
||||
}
|
||||
|
@@ -14,20 +14,22 @@ import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.base.ActivityResultCallback
|
||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||
import com.topjohnwu.magisk.core.model.module.OnlineModule
|
||||
import com.topjohnwu.magisk.events.dialog.MarkDownDialog
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.view.MarkDownWindow
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
import com.topjohnwu.magisk.view.Shortcuts
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ViewActionEvent(val action: BaseActivity.() -> Unit) : ViewEvent(), ActivityExecutor {
|
||||
override fun invoke(activity: BaseUIActivity<*, *>) = action(activity)
|
||||
}
|
||||
|
||||
class OpenReadmeEvent(val item: OnlineModule) : ViewEventWithScope(), ContextExecutor {
|
||||
override fun invoke(context: Context) {
|
||||
scope.launch {
|
||||
MarkDownWindow.show(context, null, item::notes)
|
||||
}
|
||||
class OpenReadmeEvent(private val item: OnlineModule) : MarkDownDialog() {
|
||||
override suspend fun getMarkdownText() = item.notes()
|
||||
override fun build(dialog: MagiskDialog) {
|
||||
super.build(dialog)
|
||||
dialog.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||
titleRes = android.R.string.cancel
|
||||
}.cancellable(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +80,7 @@ class MagiskInstallFileEvent(private val callback: ActivityResultCallback)
|
||||
override fun invoke(activity: BaseUIActivity<*, *>) {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).setType("*/*")
|
||||
try {
|
||||
activity.startActivityForResult(intent, Const.ID.SELECT_FILE, callback)
|
||||
activity.startActivityForResult(intent, callback)
|
||||
Utils.toast(R.string.patch_file_msg, Toast.LENGTH_LONG)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
|
||||
@@ -107,10 +109,10 @@ class SelectModuleEvent : ViewEvent(), FragmentExecutor {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).setType("application/zip")
|
||||
try {
|
||||
fragment.apply {
|
||||
activity.startActivityForResult(intent, Const.ID.FETCH_ZIP) { code, intent ->
|
||||
activity.startActivityForResult(intent) { code, intent ->
|
||||
if (code == Activity.RESULT_OK && intent != null) {
|
||||
intent.data?.also {
|
||||
MainDirections.actionFlashFragment(it, Const.Value.FLASH_ZIP).navigate()
|
||||
MainDirections.actionFlashFragment(Const.Value.FLASH_ZIP, it).navigate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,9 @@ abstract class DialogEvent : ViewEvent(), ActivityExecutor {
|
||||
protected lateinit var dialog: MagiskDialog
|
||||
|
||||
override fun invoke(activity: BaseUIActivity<*, *>) {
|
||||
dialog = MagiskDialog(activity).apply(this::build).reveal()
|
||||
dialog = MagiskDialog(activity)
|
||||
.apply { setOwnerActivity(activity) }
|
||||
.apply(this::build).reveal()
|
||||
}
|
||||
|
||||
abstract fun build(dialog: MagiskDialog)
|
||||
|
@@ -1,15 +1,11 @@
|
||||
package com.topjohnwu.magisk.events.dialog
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.download.Action.EnvFix
|
||||
import com.topjohnwu.magisk.core.download.DownloadService
|
||||
import com.topjohnwu.magisk.core.download.Subject.Magisk
|
||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class EnvFixDialog : DialogEvent() {
|
||||
|
||||
@@ -24,20 +20,17 @@ class EnvFixDialog : DialogEvent() {
|
||||
.applyMessage(R.string.setup_msg)
|
||||
.resetButtons()
|
||||
.cancellable(false)
|
||||
val lbm = LocalBroadcastManager.getInstance(dialog.context)
|
||||
lbm.registerReceiver(object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
(dialog.ownerActivity as BaseActivity).lifecycleScope.launch {
|
||||
MagiskInstaller.FixEnv {
|
||||
dialog.dismiss()
|
||||
lbm.unregisterReceiver(this)
|
||||
}
|
||||
}, IntentFilter(DISMISS))
|
||||
DownloadService.start(dialog.context, Magisk(EnvFix))
|
||||
}.exec()
|
||||
}
|
||||
}
|
||||
}
|
||||
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||
titleRes = android.R.string.cancel
|
||||
}
|
||||
.let { Unit }
|
||||
.let { }
|
||||
|
||||
companion object {
|
||||
const val DISMISS = "com.topjohnwu.magisk.ENV_DONE"
|
||||
|
@@ -1,40 +1,41 @@
|
||||
package com.topjohnwu.magisk.events.dialog
|
||||
|
||||
import android.content.Context
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.download.DownloadService
|
||||
import com.topjohnwu.magisk.core.download.Subject
|
||||
import com.topjohnwu.magisk.ktx.res
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
import com.topjohnwu.magisk.view.MarkDownWindow
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.get
|
||||
import org.koin.core.inject
|
||||
import java.io.File
|
||||
|
||||
class ManagerInstallDialog : DialogEvent() {
|
||||
class ManagerInstallDialog : MarkDownDialog() {
|
||||
|
||||
private val svc: NetworkService by inject()
|
||||
|
||||
override suspend fun getMarkdownText(): String {
|
||||
val text = svc.fetchString(Info.remote.magisk.note)
|
||||
// Cache the changelog
|
||||
val context = get<Context>()
|
||||
context.cacheDir.listFiles { _, name -> name.endsWith(".md") }.orEmpty().forEach {
|
||||
it.delete()
|
||||
}
|
||||
File(context.cacheDir, "${Info.remote.magisk.versionCode}.md").writeText(text)
|
||||
return text
|
||||
}
|
||||
|
||||
override fun build(dialog: MagiskDialog) {
|
||||
super.build(dialog)
|
||||
with(dialog) {
|
||||
val subject = Subject.Manager()
|
||||
|
||||
applyTitle(R.string.repo_install_title.res(R.string.app_name.res()))
|
||||
applyMessage(R.string.repo_install_msg.res(subject.title))
|
||||
|
||||
setCancelable(true)
|
||||
|
||||
applyButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||
titleRes = R.string.install
|
||||
onClick { DownloadService.start(context, subject) }
|
||||
onClick { DownloadService.start(context, Subject.Manager()) }
|
||||
}
|
||||
|
||||
if (Info.remote.app.note.isEmpty()) return
|
||||
applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||
titleRes = R.string.app_changelog
|
||||
onClick {
|
||||
GlobalScope.launch(Dispatchers.Main.immediate) {
|
||||
MarkDownWindow.show(context, null, Info.remote.app.note)
|
||||
}
|
||||
}
|
||||
titleRes = android.R.string.cancel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,45 @@
|
||||
package com.topjohnwu.magisk.events.dialog
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
import io.noties.markwon.Markwon
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
import timber.log.Timber
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
abstract class MarkDownDialog : DialogEvent(), KoinComponent {
|
||||
|
||||
private val markwon: Markwon by inject()
|
||||
|
||||
abstract suspend fun getMarkdownText(): String
|
||||
|
||||
@CallSuper
|
||||
override fun build(dialog: MagiskDialog) {
|
||||
with(dialog) {
|
||||
val view = LayoutInflater.from(context).inflate(R.layout.markdown_window_md2, null)
|
||||
applyView(view)
|
||||
(ownerActivity as BaseActivity).lifecycleScope.launch {
|
||||
val tv = view.findViewById<TextView>(R.id.md_txt)
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
markwon.setMarkdown(tv, getMarkdownText())
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException)
|
||||
throw e
|
||||
Timber.e(e)
|
||||
tv.post { tv.setText(R.string.download_file_error) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,7 +14,7 @@ class ModuleInstallDialog(private val item: OnlineModule) : DialogEvent() {
|
||||
with(dialog) {
|
||||
|
||||
fun download(install: Boolean) {
|
||||
val config = if (install) Action.Flash.Primary else Action.Download
|
||||
val config = if (install) Action.Flash else Action.Download
|
||||
val subject = Subject.Module(item, config)
|
||||
DownloadService.start(context, subject)
|
||||
}
|
||||
|
@@ -3,10 +3,7 @@ package com.topjohnwu.magisk.events.dialog
|
||||
import android.app.ProgressDialog
|
||||
import android.widget.Toast
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.download.Action
|
||||
import com.topjohnwu.magisk.core.download.DownloadService
|
||||
import com.topjohnwu.magisk.core.download.Subject
|
||||
import com.topjohnwu.magisk.ui.flash.FlashFragment
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
import com.topjohnwu.superuser.Shell
|
||||
@@ -20,12 +17,10 @@ class UninstallDialog : DialogEvent() {
|
||||
titleRes = R.string.restore_img
|
||||
onClick { restore() }
|
||||
}
|
||||
if (Info.remote.uninstaller.link.isNotEmpty()) {
|
||||
dialog.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||
titleRes = R.string.complete_uninstall
|
||||
onClick { completeUninstall() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@@ -46,7 +41,7 @@ class UninstallDialog : DialogEvent() {
|
||||
}
|
||||
|
||||
private fun completeUninstall() {
|
||||
DownloadService.start(dialog.context, Subject.Magisk(Action.Uninstall))
|
||||
FlashFragment.uninstall()
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -7,9 +7,11 @@ import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.ComponentInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.*
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.content.pm.ServiceInfo.FLAG_ISOLATED_PROCESS
|
||||
import android.content.pm.ServiceInfo.FLAG_USE_APP_ZYGOTE
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.database.Cursor
|
||||
@@ -19,8 +21,8 @@ import android.graphics.drawable.AdaptiveIconDrawable
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.system.Os
|
||||
import android.text.PrecomputedText
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -39,11 +41,13 @@ import androidx.core.widget.TextViewCompat
|
||||
import androidx.databinding.BindingAdapter
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.transition.AutoTransition
|
||||
import androidx.transition.TransitionManager
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.AssetHack
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.ResMgr
|
||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.utils.DynamicClassLoader
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
@@ -53,37 +57,31 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Array as JArray
|
||||
|
||||
val packageName: String get() = get<Context>().packageName
|
||||
|
||||
val ApplicationInfo.processes: List<String> @SuppressLint("InlinedApi") get() {
|
||||
val pm = get<PackageManager>()
|
||||
val appProcessName = processName ?: packageName
|
||||
val baseFlag = MATCH_DISABLED_COMPONENTS or MATCH_UNINSTALLED_PACKAGES
|
||||
val packageInfo = try {
|
||||
val request = GET_ACTIVITIES or GET_SERVICES or GET_RECEIVERS or GET_PROVIDERS
|
||||
pm.getPackageInfo(packageName, baseFlag or request)
|
||||
} catch (e: NameNotFoundException) { // EdXposed hooked, issue#3276
|
||||
return listOf(appProcessName)
|
||||
} catch (e: Exception) {
|
||||
// Exceed binder data transfer limit, fetch each component type separately
|
||||
pm.getPackageInfo(packageName, baseFlag).apply {
|
||||
runCatching { activities = pm.getPackageInfo(packageName, GET_ACTIVITIES).activities }
|
||||
runCatching { services = pm.getPackageInfo(packageName, GET_SERVICES).services }
|
||||
runCatching { receivers = pm.getPackageInfo(packageName, GET_RECEIVERS).receivers }
|
||||
runCatching { providers = pm.getPackageInfo(packageName, GET_PROVIDERS).providers }
|
||||
private lateinit var osSymlink: Method
|
||||
private lateinit var os: Any
|
||||
|
||||
fun symlink(oldPath: String, newPath: String) {
|
||||
if (SDK_INT >= 21) {
|
||||
Os.symlink(oldPath, newPath)
|
||||
} else {
|
||||
if (!::osSymlink.isInitialized) {
|
||||
os = Class.forName("libcore.io.Libcore").getField("os").get(null)!!
|
||||
osSymlink = os.javaClass.getMethod("symlink", String::class.java, String::class.java)
|
||||
}
|
||||
}
|
||||
fun Array<out ComponentInfo>.processNames() = map { it.processName ?: appProcessName }
|
||||
return with(packageInfo) {
|
||||
activities?.processNames().orEmpty() +
|
||||
services?.processNames().orEmpty() +
|
||||
receivers?.processNames().orEmpty() +
|
||||
providers?.processNames().orEmpty()
|
||||
osSymlink.invoke(os, oldPath, newPath)
|
||||
}
|
||||
}
|
||||
|
||||
val ServiceInfo.isIsolated get() = (flags and FLAG_ISOLATED_PROCESS) != 0
|
||||
|
||||
@get:SuppressLint("InlinedApi")
|
||||
val ServiceInfo.useAppZygote get() = (flags and FLAG_USE_APP_ZYGOTE) != 0
|
||||
|
||||
fun Context.rawResource(id: Int) = resources.openRawResource(id)
|
||||
|
||||
fun Context.getBitmap(id: Int): Bitmap {
|
||||
@@ -103,6 +101,11 @@ fun Context.getBitmap(id: Int): Bitmap {
|
||||
return bitmap
|
||||
}
|
||||
|
||||
val Context.deviceProtectedContext: Context get() =
|
||||
if (SDK_INT >= 24) {
|
||||
createDeviceProtectedStorageContext()
|
||||
} else { this }
|
||||
|
||||
fun Intent.startActivity(context: Context) = context.startActivity(this)
|
||||
|
||||
fun Intent.startActivityWithRoot() {
|
||||
@@ -247,15 +250,13 @@ fun Context.colorStateListCompat(@ColorRes id: Int) = try {
|
||||
null
|
||||
}
|
||||
|
||||
fun Context.drawableCompat(@DrawableRes id: Int) = ContextCompat.getDrawable(this, id)
|
||||
fun Context.drawableCompat(@DrawableRes id: Int) = AppCompatResources.getDrawable(this, id)
|
||||
/**
|
||||
* Pass [start] and [end] dimensions, function will return left and right
|
||||
* with respect to RTL layout direction
|
||||
*/
|
||||
fun Context.startEndToLeftRight(start: Int, end: Int): Pair<Int, Int> {
|
||||
if (SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 &&
|
||||
resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||
) {
|
||||
if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
|
||||
return end to start
|
||||
}
|
||||
return start to end
|
||||
@@ -315,8 +316,21 @@ fun ViewGroup.startAnimations() {
|
||||
)
|
||||
}
|
||||
|
||||
val View.activity: Activity get() {
|
||||
var context = context
|
||||
while(true) {
|
||||
if (context !is ContextWrapper)
|
||||
error("View is not attached to activity")
|
||||
if (context is Activity)
|
||||
return context
|
||||
context = context.baseContext
|
||||
}
|
||||
}
|
||||
|
||||
var View.coroutineScope: CoroutineScope
|
||||
get() = getTag(R.id.coroutineScope) as? CoroutineScope ?: GlobalScope
|
||||
get() = getTag(R.id.coroutineScope) as? CoroutineScope
|
||||
?: (activity as? BaseActivity)?.lifecycleScope
|
||||
?: GlobalScope
|
||||
set(value) = setTag(R.id.coroutineScope, value)
|
||||
|
||||
@set:BindingAdapter("precomputedText")
|
||||
@@ -366,6 +380,16 @@ var TextView.precomputedText: CharSequence
|
||||
}
|
||||
|
||||
fun Int.dpInPx(): Int {
|
||||
val scale = ResMgr.resource.displayMetrics.density
|
||||
val scale = AssetHack.resource.displayMetrics.density
|
||||
return (this * scale + 0.5).toInt()
|
||||
}
|
||||
|
||||
@SuppressLint("PrivateApi")
|
||||
fun getProperty(key: String, def: String): String {
|
||||
runCatching {
|
||||
val clazz = Class.forName("android.os.SystemProperties")
|
||||
val get = clazz.getMethod("get", String::class.java, String::class.java)
|
||||
return get.invoke(clazz, key, def) as String
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
@@ -6,14 +6,12 @@ import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) {
|
||||
inline fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) {
|
||||
var entry: ZipEntry? = nextEntry
|
||||
while (entry != null) {
|
||||
callback(entry)
|
||||
@@ -53,7 +51,7 @@ fun String.langTagToLocale(): Locale {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
return Locale.forLanguageTag(this)
|
||||
} else {
|
||||
val tok = split("-".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val tok = split("[-_]".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
if (tok.isEmpty()) {
|
||||
return Locale("")
|
||||
}
|
||||
@@ -110,32 +108,3 @@ fun Locale.toLangTag(): String {
|
||||
|
||||
fun SimpleDateFormat.parseOrNull(date: String) =
|
||||
runCatching { parse(date) }.onFailure { Timber.e(it) }.getOrNull()
|
||||
|
||||
// Reflection hacks
|
||||
|
||||
private val loadClass = ClassLoader::class.java.getMethod("loadClass", String::class.java)
|
||||
private val getDeclaredMethod = Class::class.java.getMethod("getDeclaredMethod",
|
||||
String::class.java, arrayOf<Class<*>>()::class.java)
|
||||
private val getDeclaredField = Class::class.java.getMethod("getDeclaredField", String::class.java)
|
||||
|
||||
fun ClassLoader.forceLoadClass(name: String) =
|
||||
runCatching { loadClass.invoke(this, name) }.getOrNull() as Class<*>?
|
||||
|
||||
fun Class<*>.forceGetDeclaredMethod(name: String, vararg types: Class<*>) =
|
||||
(runCatching { getDeclaredMethod.invoke(this, name, types) }.getOrNull() as Method?)?.also {
|
||||
it.isAccessible = true
|
||||
}
|
||||
|
||||
fun Class<*>.forceGetDeclaredField(name: String) =
|
||||
(runCatching { getDeclaredField.invoke(this, name) }.getOrNull() as Field?)?.also {
|
||||
it.isAccessible = true
|
||||
}
|
||||
|
||||
inline fun <reified T> T.forceGetClass(name: String) =
|
||||
T::class.java.classLoader?.forceLoadClass(name)
|
||||
|
||||
fun Class<*>.forceGetField(name: String): Field? =
|
||||
forceGetDeclaredField(name) ?: superclass?.forceGetField(name)
|
||||
|
||||
fun Class<*>.forceGetMethod(name: String, vararg types: Class<*>): Method? =
|
||||
forceGetDeclaredMethod(name, *types) ?: superclass?.forceGetMethod(name, *types)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package com.topjohnwu.magisk.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
@@ -10,9 +11,7 @@ import android.view.WindowManager
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.navigation.NavDirections
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.topjohnwu.magisk.MainDirections
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIActivity
|
||||
@@ -22,13 +21,12 @@ import com.topjohnwu.magisk.core.*
|
||||
import com.topjohnwu.magisk.databinding.ActivityMainMd2Binding
|
||||
import com.topjohnwu.magisk.ktx.startAnimations
|
||||
import com.topjohnwu.magisk.ui.home.HomeFragmentDirections
|
||||
import com.topjohnwu.magisk.utils.HideBottomViewOnScrollBehavior
|
||||
import com.topjohnwu.magisk.utils.HideTopViewOnScrollBehavior
|
||||
import com.topjohnwu.magisk.utils.HideableBehavior
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
import com.topjohnwu.magisk.view.Shortcuts
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import java.io.File
|
||||
|
||||
class MainViewModel : BaseViewModel()
|
||||
|
||||
@@ -77,12 +75,6 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
|
||||
|
||||
setSupportActionBar(binding.mainToolbar)
|
||||
|
||||
binding.mainToolbarWrapper.updateLayoutParams<CoordinatorLayout.LayoutParams> {
|
||||
behavior = HideTopViewOnScrollBehavior<MaterialCardView>()
|
||||
}
|
||||
binding.mainBottomBar.updateLayoutParams<CoordinatorLayout.LayoutParams> {
|
||||
behavior = HideBottomViewOnScrollBehavior<MaterialCardView>()
|
||||
}
|
||||
binding.mainNavigation.setOnNavigationItemSelectedListener {
|
||||
getScreen(it.itemId)?.navigate()
|
||||
true
|
||||
@@ -91,7 +83,7 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
|
||||
(currentFragment as? ReselectionTarget)?.onReselected()
|
||||
}
|
||||
|
||||
val section = if (intent.action == ACTION_APPLICATION_PREFERENCES) Const.Nav.SETTINGS
|
||||
val section = if (intent.action == Intent.ACTION_APPLICATION_PREFERENCES) Const.Nav.SETTINGS
|
||||
else intent.getStringExtra(Const.Key.OPEN_SECTION)
|
||||
getScreen(section)?.navigate()
|
||||
|
||||
@@ -198,7 +190,37 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
|
||||
.applyTitle(R.string.unsupport_magisk_title)
|
||||
.applyMessage(R.string.unsupport_magisk_msg, Const.Version.MIN_VERSION)
|
||||
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok }
|
||||
.cancellable(true)
|
||||
.cancellable(false)
|
||||
.reveal()
|
||||
}
|
||||
|
||||
if (!Info.isEmulator && Info.env.isActive && System.getenv("PATH")
|
||||
?.split(':')
|
||||
?.filterNot { File("$it/magisk").exists() }
|
||||
?.any { File("$it/su").exists() } == true) {
|
||||
MagiskDialog(this)
|
||||
.applyTitle(R.string.unsupport_general_title)
|
||||
.applyMessage(R.string.unsupport_other_su_msg)
|
||||
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok }
|
||||
.cancellable(false)
|
||||
.reveal()
|
||||
}
|
||||
|
||||
if (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0) {
|
||||
MagiskDialog(this)
|
||||
.applyTitle(R.string.unsupport_general_title)
|
||||
.applyMessage(R.string.unsupport_system_app_msg)
|
||||
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok }
|
||||
.cancellable(false)
|
||||
.reveal()
|
||||
}
|
||||
|
||||
if (applicationInfo.flags and ApplicationInfo.FLAG_EXTERNAL_STORAGE != 0) {
|
||||
MagiskDialog(this)
|
||||
.applyTitle(R.string.unsupport_general_title)
|
||||
.applyMessage(R.string.unsupport_external_storage_msg)
|
||||
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok }
|
||||
.cancellable(false)
|
||||
.reveal()
|
||||
}
|
||||
}
|
||||
@@ -222,11 +244,4 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
|
||||
.reveal()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val ACTION_APPLICATION_PREFERENCES get() =
|
||||
if (Build.VERSION.SDK_INT >= 24) Intent.ACTION_APPLICATION_PREFERENCES
|
||||
else "???"
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -17,13 +17,12 @@ import com.topjohnwu.magisk.ui.MainActivity
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import com.topjohnwu.magisk.MainDirections.Companion.actionFlashFragment as toFlash
|
||||
import com.topjohnwu.magisk.ui.flash.FlashFragmentArgs as args
|
||||
|
||||
class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>() {
|
||||
|
||||
override val layoutRes = R.layout.fragment_flash_md2
|
||||
override val viewModel by viewModel<FlashViewModel> {
|
||||
parametersOf(args.fromBundle(requireArguments()))
|
||||
parametersOf(FlashFragmentArgs.fromBundle(requireArguments()))
|
||||
}
|
||||
|
||||
private var defaultOrientation = -1
|
||||
@@ -32,6 +31,10 @@ class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>()
|
||||
super.onStart()
|
||||
setHasOptionsMenu(true)
|
||||
activity.setTitle(R.string.flash_screen_title)
|
||||
|
||||
viewModel.subtitle.observe(this) {
|
||||
activity.supportActionBar?.setSubtitle(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
@@ -74,7 +77,7 @@ class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>()
|
||||
|
||||
companion object {
|
||||
|
||||
private fun createIntent(context: Context, args: args) =
|
||||
private fun createIntent(context: Context, args: FlashFragmentArgs) =
|
||||
NavDeepLinkBuilder(context)
|
||||
.setGraph(R.navigation.main)
|
||||
.setComponentName(MainActivity::class.java.cmp(context.packageName))
|
||||
@@ -87,59 +90,34 @@ class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>()
|
||||
|
||||
/* Flashing is understood as installing / flashing magisk itself */
|
||||
|
||||
fun flashIntent(context: Context, file: Uri, isSecondSlot: Boolean, id: Int = -1) = args(
|
||||
installer = file,
|
||||
action = flashType(isSecondSlot),
|
||||
dismissId = id
|
||||
).let { createIntent(context, it) }
|
||||
|
||||
fun flash(file: Uri, isSecondSlot: Boolean, id: Int) = toFlash(
|
||||
installer = file,
|
||||
action = flashType(isSecondSlot),
|
||||
dismissId = id
|
||||
fun flash(isSecondSlot: Boolean) = toFlash(
|
||||
action = flashType(isSecondSlot)
|
||||
).let { BaseUIActivity.postDirections(it) }
|
||||
|
||||
/* Patching is understood as injecting img files with magisk */
|
||||
|
||||
fun patchIntent(context: Context, file: Uri, uri: Uri, id: Int = -1) = args(
|
||||
installer = file,
|
||||
fun patch(uri: Uri) = toFlash(
|
||||
action = Const.Value.PATCH_FILE,
|
||||
additionalData = uri,
|
||||
dismissId = id
|
||||
).let { createIntent(context, it) }
|
||||
|
||||
fun patch(file: Uri, uri: Uri, id: Int) = toFlash(
|
||||
installer = file,
|
||||
action = Const.Value.PATCH_FILE,
|
||||
additionalData = uri,
|
||||
dismissId = id
|
||||
additionalData = uri
|
||||
).let { BaseUIActivity.postDirections(it) }
|
||||
|
||||
/* Uninstalling is understood as removing magisk entirely */
|
||||
|
||||
fun uninstallIntent(context: Context, file: Uri, id: Int = -1) = args(
|
||||
installer = file,
|
||||
action = Const.Value.UNINSTALL,
|
||||
dismissId = id
|
||||
).let { createIntent(context, it) }
|
||||
|
||||
fun uninstall(file: Uri, id: Int) = toFlash(
|
||||
installer = file,
|
||||
action = Const.Value.UNINSTALL,
|
||||
dismissId = id
|
||||
fun uninstall() = toFlash(
|
||||
action = Const.Value.UNINSTALL
|
||||
).let { BaseUIActivity.postDirections(it) }
|
||||
|
||||
/* Installing is understood as flashing modules / zips */
|
||||
|
||||
fun installIntent(context: Context, file: Uri, id: Int = -1) = args(
|
||||
installer = file,
|
||||
fun installIntent(context: Context, file: Uri, id: Int = -1) = FlashFragmentArgs(
|
||||
action = Const.Value.FLASH_ZIP,
|
||||
additionalData = file,
|
||||
dismissId = id
|
||||
).let { createIntent(context, it) }
|
||||
|
||||
fun install(file: Uri, id: Int) = toFlash(
|
||||
installer = file,
|
||||
action = Const.Value.FLASH_ZIP,
|
||||
additionalData = file,
|
||||
dismissId = id
|
||||
).let { BaseUIActivity.postDirections(it) }
|
||||
}
|
||||
|
@@ -1,9 +1,10 @@
|
||||
package com.topjohnwu.magisk.ui.flash
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.view.MenuItem
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
@@ -11,6 +12,7 @@ import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.arch.diffListOf
|
||||
import com.topjohnwu.magisk.arch.itemBindingOf
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.tasks.FlashZip
|
||||
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
@@ -25,18 +27,14 @@ import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class FlashViewModel(
|
||||
args: FlashFragmentArgs,
|
||||
private val resources: Resources
|
||||
) : BaseViewModel() {
|
||||
class FlashViewModel(args: FlashFragmentArgs) : BaseViewModel() {
|
||||
|
||||
@get:Bindable
|
||||
var showReboot = Shell.rootAccess()
|
||||
set(value) = set(value, field, { field = it }, BR.showReboot)
|
||||
|
||||
@get:Bindable
|
||||
var behaviorText = resources.getString(R.string.flashing)
|
||||
set(value) = set(value, field, { field = it }, BR.behaviorText)
|
||||
private val _subtitle = MutableLiveData(R.string.flashing)
|
||||
val subtitle get() = _subtitle as LiveData<Int>
|
||||
|
||||
val adapter = RvBindingAdapter<ConsoleItem>()
|
||||
val items = diffListOf<ConsoleItem>()
|
||||
@@ -52,33 +50,35 @@ class FlashViewModel(
|
||||
}
|
||||
|
||||
init {
|
||||
args.dismissId.takeIf { it != -1 }?.also {
|
||||
Notifications.mgr.cancel(it)
|
||||
}
|
||||
val (installer, action, uri) = args
|
||||
startFlashing(installer, uri, action)
|
||||
val (action, uri, id) = args
|
||||
if (id != -1)
|
||||
Notifications.mgr.cancel(id)
|
||||
startFlashing(action, uri)
|
||||
}
|
||||
|
||||
private fun startFlashing(installer: Uri, uri: Uri?, action: String) {
|
||||
private fun startFlashing(action: String, uri: Uri?) {
|
||||
viewModelScope.launch {
|
||||
val result = when (action) {
|
||||
Const.Value.FLASH_ZIP -> {
|
||||
FlashZip(installer, outItems, logItems).exec()
|
||||
FlashZip(uri!!, outItems, logItems).exec()
|
||||
}
|
||||
Const.Value.UNINSTALL -> {
|
||||
showReboot = false
|
||||
FlashZip.Uninstall(installer, outItems, logItems).exec()
|
||||
MagiskInstaller.Uninstall(outItems, logItems).exec()
|
||||
}
|
||||
Const.Value.FLASH_MAGISK -> {
|
||||
MagiskInstaller.Direct(installer, outItems, logItems).exec()
|
||||
if (Info.isEmulator)
|
||||
MagiskInstaller.Emulator(outItems, logItems).exec()
|
||||
else
|
||||
MagiskInstaller.Direct(outItems, logItems).exec()
|
||||
}
|
||||
Const.Value.FLASH_INACTIVE_SLOT -> {
|
||||
MagiskInstaller.SecondSlot(installer, outItems, logItems).exec()
|
||||
MagiskInstaller.SecondSlot(outItems, logItems).exec()
|
||||
}
|
||||
Const.Value.PATCH_FILE -> {
|
||||
uri ?: return@launch
|
||||
showReboot = false
|
||||
MagiskInstaller.Patch(installer, uri, outItems, logItems).exec()
|
||||
MagiskInstaller.Patch(uri, outItems, logItems).exec()
|
||||
}
|
||||
else -> {
|
||||
back()
|
||||
@@ -91,9 +91,9 @@ class FlashViewModel(
|
||||
|
||||
private fun onResult(success: Boolean) {
|
||||
state = if (success) State.LOADED else State.LOADING_FAILED
|
||||
behaviorText = when {
|
||||
success -> resources.getString(R.string.done)
|
||||
else -> resources.getString(R.string.failure)
|
||||
when {
|
||||
success -> _subtitle.postValue(R.string.done)
|
||||
else -> _subtitle.postValue(R.string.failure)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,47 +0,0 @@
|
||||
package com.topjohnwu.magisk.ui.hide
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.ktx.getLabel
|
||||
|
||||
class HideTarget(line: String) {
|
||||
val packageName: String
|
||||
val process: String
|
||||
|
||||
init {
|
||||
val split = line.split(Regex("\\|"), 2)
|
||||
packageName = split[0]
|
||||
process = split.getOrElse(1) { packageName }
|
||||
}
|
||||
}
|
||||
|
||||
class HideAppInfo(info: ApplicationInfo, pm: PackageManager)
|
||||
: ApplicationInfo(info), Comparable<HideAppInfo> {
|
||||
|
||||
val label = info.getLabel(pm)
|
||||
val iconImage: Drawable = info.loadIcon(pm)
|
||||
|
||||
override fun compareTo(other: HideAppInfo) = comparator.compare(this, other)
|
||||
|
||||
companion object {
|
||||
private val comparator = compareBy<HideAppInfo>(
|
||||
{ it.label.toLowerCase(currentLocale) },
|
||||
{ it.packageName }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class HideProcessInfo(
|
||||
val name: String,
|
||||
val packageName: String,
|
||||
val isHidden: Boolean
|
||||
)
|
||||
|
||||
class HideAppTarget(
|
||||
val info: HideAppInfo,
|
||||
val processes: List<HideProcessInfo>
|
||||
) : Comparable<HideAppTarget> {
|
||||
override fun compareTo(other: HideAppTarget) = compareValuesBy(this, other) { it.info }
|
||||
}
|
105
app/src/main/java/com/topjohnwu/magisk/ui/hide/HideInfo.kt
Normal file
105
app/src/main/java/com/topjohnwu/magisk/ui/hide/HideInfo.kt
Normal file
@@ -0,0 +1,105 @@
|
||||
package com.topjohnwu.magisk.ui.hide
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.ComponentInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.*
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.ktx.getLabel
|
||||
import com.topjohnwu.magisk.ktx.isIsolated
|
||||
import com.topjohnwu.magisk.ktx.useAppZygote
|
||||
|
||||
class CmdlineHiddenItem(line: String) {
|
||||
val packageName: String
|
||||
val process: String
|
||||
|
||||
init {
|
||||
val split = line.split(Regex("\\|"), 2)
|
||||
packageName = split[0]
|
||||
process = split.getOrElse(1) { packageName }
|
||||
}
|
||||
}
|
||||
|
||||
const val ISOLATED_MAGIC = "isolated"
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
class HideAppInfo(info: ApplicationInfo, pm: PackageManager, hideList: List<CmdlineHiddenItem>)
|
||||
: ApplicationInfo(info), Comparable<HideAppInfo> {
|
||||
|
||||
val label = info.getLabel(pm)
|
||||
val iconImage: Drawable = info.loadIcon(pm)
|
||||
val processes = fetchProcesses(pm, hideList)
|
||||
|
||||
override fun compareTo(other: HideAppInfo) = comparator.compare(this, other)
|
||||
|
||||
private fun fetchProcesses(
|
||||
pm: PackageManager,
|
||||
hideList: List<CmdlineHiddenItem>
|
||||
): List<HideProcessInfo> {
|
||||
// Fetch full PackageInfo
|
||||
val baseFlag = MATCH_DISABLED_COMPONENTS or MATCH_UNINSTALLED_PACKAGES
|
||||
val packageInfo = try {
|
||||
val request = GET_ACTIVITIES or GET_SERVICES or GET_RECEIVERS or GET_PROVIDERS
|
||||
pm.getPackageInfo(packageName, baseFlag or request)
|
||||
} catch (e: NameNotFoundException) {
|
||||
// EdXposed hooked, issue#3276
|
||||
return emptyList()
|
||||
} catch (e: Exception) {
|
||||
// Exceed binder data transfer limit, fetch each component type separately
|
||||
pm.getPackageInfo(packageName, baseFlag).apply {
|
||||
runCatching { activities = pm.getPackageInfo(packageName, baseFlag or GET_ACTIVITIES).activities }
|
||||
runCatching { services = pm.getPackageInfo(packageName, baseFlag or GET_SERVICES).services }
|
||||
runCatching { receivers = pm.getPackageInfo(packageName, baseFlag or GET_RECEIVERS).receivers }
|
||||
runCatching { providers = pm.getPackageInfo(packageName, baseFlag or GET_PROVIDERS).providers }
|
||||
}
|
||||
}
|
||||
|
||||
val hidden = hideList.filter { it.packageName == packageName || it.packageName == ISOLATED_MAGIC }
|
||||
fun createProcess(name: String, pkg: String = packageName): HideProcessInfo {
|
||||
return HideProcessInfo(name, pkg, hidden.any { it.process == name })
|
||||
}
|
||||
|
||||
var haveAppZygote = false
|
||||
fun Array<out ComponentInfo>.processes() = map { createProcess(it.processName) }
|
||||
fun Array<ServiceInfo>.processes() = map {
|
||||
if (it.isIsolated) {
|
||||
if (it.useAppZygote) {
|
||||
haveAppZygote = true
|
||||
// Using app zygote, don't need to track the process
|
||||
null
|
||||
} else {
|
||||
createProcess("${it.processName}:${it.name}", ISOLATED_MAGIC)
|
||||
}
|
||||
} else {
|
||||
createProcess(it.processName)
|
||||
}
|
||||
}
|
||||
|
||||
return with(packageInfo) {
|
||||
activities?.processes().orEmpty() +
|
||||
services?.processes().orEmpty() +
|
||||
receivers?.processes().orEmpty() +
|
||||
providers?.processes().orEmpty() +
|
||||
listOf(if (haveAppZygote) createProcess("${processName}_zygote") else null)
|
||||
}.filterNotNull().distinctBy { it.name }.sortedBy { it.name }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val comparator = compareBy<HideAppInfo>(
|
||||
{ it.label.toLowerCase(currentLocale) },
|
||||
{ it.packageName }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class HideProcessInfo(
|
||||
val name: String,
|
||||
val packageName: String,
|
||||
var isHidden: Boolean
|
||||
) {
|
||||
val isIsolated get() = name == ISOLATED_MAGIC
|
||||
val isAppZygote get() = name.endsWith("_zygote")
|
||||
}
|
@@ -12,14 +12,13 @@ import com.topjohnwu.magisk.utils.set
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class HideItem(
|
||||
app: HideAppTarget
|
||||
) : ObservableItem<HideItem>(), Comparable<HideItem> {
|
||||
class HideRvItem(
|
||||
val info: HideAppInfo
|
||||
) : ObservableItem<HideRvItem>(), Comparable<HideRvItem> {
|
||||
|
||||
override val layoutRes = R.layout.item_hide_md2
|
||||
override val layoutRes get() = R.layout.item_hide_md2
|
||||
|
||||
val info = app.info
|
||||
val processes = app.processes.map { HideProcessItem(it) }
|
||||
val processes = info.processes.map { HideProcessRvItem(it) }
|
||||
|
||||
@get:Bindable
|
||||
var isExpanded = false
|
||||
@@ -41,11 +40,10 @@ class HideItem(
|
||||
if (value == true) {
|
||||
processes
|
||||
.filterNot { it.isHidden }
|
||||
.filter { isExpanded || it.process.name == it.process.packageName }
|
||||
.filter { isExpanded || it.defaultSelection }
|
||||
} else {
|
||||
processes
|
||||
.filter { it.isHidden }
|
||||
.filter { isExpanded || it.process.name == it.process.packageName }
|
||||
}.forEach { it.toggle() }
|
||||
}
|
||||
|
||||
@@ -69,14 +67,19 @@ class HideItem(
|
||||
else -> null
|
||||
}
|
||||
} else {
|
||||
processes.find { it.isHidden && it.process.name == it.process.packageName } != null
|
||||
val defaultProcesses = processes.filter { it.defaultSelection }
|
||||
when (defaultProcesses.count { it.isHidden }) {
|
||||
0 -> false
|
||||
defaultProcesses.size -> true
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun compareTo(other: HideItem) = comparator.compare(this, other)
|
||||
override fun compareTo(other: HideRvItem) = comparator.compare(this, other)
|
||||
|
||||
companion object {
|
||||
private val comparator = compareBy<HideItem>(
|
||||
private val comparator = compareBy<HideRvItem>(
|
||||
{ it.itemsChecked == 0 },
|
||||
{ it.info }
|
||||
)
|
||||
@@ -84,16 +87,17 @@ class HideItem(
|
||||
|
||||
}
|
||||
|
||||
class HideProcessItem(
|
||||
class HideProcessRvItem(
|
||||
val process: HideProcessInfo
|
||||
) : ObservableItem<HideProcessItem>() {
|
||||
) : ObservableItem<HideProcessRvItem>() {
|
||||
|
||||
override val layoutRes = R.layout.item_hide_process_md2
|
||||
override val layoutRes get() = R.layout.item_hide_process_md2
|
||||
|
||||
@get:Bindable
|
||||
var isHidden = process.isHidden
|
||||
set(value) = set(value, field, { field = it }, BR.hidden) {
|
||||
val arg = if (isHidden) "add" else "rm"
|
||||
var isHidden
|
||||
get() = process.isHidden
|
||||
set(value) = set(value, process.isHidden, { process.isHidden = it }, BR.hidden) {
|
||||
val arg = if (it) "add" else "rm"
|
||||
val (name, pkg) = process
|
||||
Shell.su("magiskhide --$arg $pkg $name").submit()
|
||||
}
|
||||
@@ -102,7 +106,10 @@ class HideProcessItem(
|
||||
isHidden = !isHidden
|
||||
}
|
||||
|
||||
override fun contentSameAs(other: HideProcessItem) = process == other.process
|
||||
override fun itemSameAs(other: HideProcessItem) = process.name == other.process.name
|
||||
val defaultSelection get() =
|
||||
process.isIsolated || process.isAppZygote || process.name == process.packageName
|
||||
|
||||
override fun contentSameAs(other: HideProcessRvItem) = process == other.process
|
||||
override fun itemSameAs(other: HideProcessRvItem) = process.name == other.process.name
|
||||
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package com.topjohnwu.magisk.ui.hide
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
|
||||
import android.os.Process
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@@ -14,7 +15,6 @@ import com.topjohnwu.magisk.arch.itemBindingOf
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import com.topjohnwu.magisk.ktx.packageName
|
||||
import com.topjohnwu.magisk.ktx.processes
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.set
|
||||
import com.topjohnwu.superuser.Shell
|
||||
@@ -45,11 +45,11 @@ class HideViewModel : BaseViewModel(), Queryable {
|
||||
submitQuery()
|
||||
}
|
||||
|
||||
val items = filterableListOf<HideItem>()
|
||||
val itemBinding = itemBindingOf<HideItem> {
|
||||
val items = filterableListOf<HideRvItem>()
|
||||
val itemBinding = itemBindingOf<HideRvItem> {
|
||||
it.bindExtra(BR.viewModel, this)
|
||||
}
|
||||
val itemInternalBinding = itemBindingOf<HideProcessItem> {
|
||||
val itemInternalBinding = itemBindingOf<HideProcessRvItem> {
|
||||
it.bindExtra(BR.viewModel, this)
|
||||
}
|
||||
|
||||
@@ -62,14 +62,13 @@ class HideViewModel : BaseViewModel(), Queryable {
|
||||
state = State.LOADING
|
||||
val (apps, diff) = withContext(Dispatchers.Default) {
|
||||
val pm = get<PackageManager>()
|
||||
val hides = Shell.su("magiskhide --ls").exec().out.map { HideTarget(it) }
|
||||
val apps = pm.getInstalledApplications(PackageManager.MATCH_UNINSTALLED_PACKAGES)
|
||||
val hideList = Shell.su("magiskhide --ls").exec().out.map { CmdlineHiddenItem(it) }
|
||||
val apps = pm.getInstalledApplications(MATCH_UNINSTALLED_PACKAGES)
|
||||
.asSequence()
|
||||
.filter { it.enabled && !blacklist.contains(it.packageName) }
|
||||
.map { HideAppInfo(it, pm) }
|
||||
.map { createTarget(it, hides) }
|
||||
.map { HideAppInfo(it, pm, hideList) }
|
||||
.filter { it.processes.isNotEmpty() }
|
||||
.map { HideItem(it) }
|
||||
.map { HideRvItem(it) }
|
||||
.toList()
|
||||
.sorted()
|
||||
apps to items.calculateDiff(apps)
|
||||
@@ -80,18 +79,6 @@ class HideViewModel : BaseViewModel(), Queryable {
|
||||
|
||||
// ---
|
||||
|
||||
private fun createTarget(info: HideAppInfo, hideList: List<HideTarget>): HideAppTarget {
|
||||
val pkg = info.packageName
|
||||
val hidden = hideList.filter { it.packageName == pkg }
|
||||
val processNames = info.processes.distinct()
|
||||
val processes = processNames.map { name ->
|
||||
HideProcessInfo(name, pkg, hidden.any { name == it.process })
|
||||
}
|
||||
return HideAppTarget(info, processes)
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
override fun query() {
|
||||
items.filter {
|
||||
fun showHidden() = it.itemsChecked != 0
|
||||
|
@@ -2,6 +2,8 @@ package com.topjohnwu.magisk.ui.home
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||
import com.topjohnwu.magisk.core.download.BaseDownloader
|
||||
@@ -22,20 +24,32 @@ class HomeFragment : BaseUIFragment<HomeViewModel, FragmentHomeMd2Binding>() {
|
||||
BaseDownloader.observeProgress(this, viewModel::onProgressUpdate)
|
||||
}
|
||||
|
||||
private fun checkTitle(text: TextView, icon: ImageView) {
|
||||
text.post {
|
||||
if (text.layout.getEllipsisCount(0) != 0) {
|
||||
with (icon) {
|
||||
layoutParams.width = 0
|
||||
layoutParams.height = 0
|
||||
requestLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
// Set barrier reference IDs in code, since resource IDs will be stripped in release mode
|
||||
binding.homeMagiskWrapper.homeMagiskTitleBarrier.referencedIds =
|
||||
intArrayOf(R.id.home_magisk_button, R.id.home_magisk_title, R.id.home_magisk_icon)
|
||||
binding.homeMagiskWrapper.homeMagiskBarrier.referencedIds =
|
||||
intArrayOf(R.id.home_magisk_latest_version, R.id.home_magisk_installed_version)
|
||||
binding.homeManagerWrapper.homeManagerTitleBarrier.referencedIds =
|
||||
intArrayOf(R.id.home_manager_button, R.id.home_manager_title, R.id.home_manager_icon)
|
||||
// If titles are squished, hide icons
|
||||
with(binding.homeMagiskWrapper) {
|
||||
checkTitle(homeMagiskTitle, homeMagiskIcon)
|
||||
}
|
||||
with(binding.homeManagerWrapper) {
|
||||
checkTitle(homeManagerTitle, homeManagerIcon)
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
@@ -46,11 +60,14 @@ class HomeFragment : BaseUIFragment<HomeViewModel, FragmentHomeMd2Binding>() {
|
||||
menu.removeItem(R.id.action_reboot)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.action_settings -> HomeFragmentDirections.actionHomeFragmentToSettingsFragment()
|
||||
.navigate()
|
||||
R.id.action_reboot -> RebootEvent.inflateMenu(activity).show()
|
||||
else -> null
|
||||
}?.let { true } ?: super.onOptionsItemSelected(item)
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_settings ->
|
||||
HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate()
|
||||
R.id.action_reboot -> RebootEvent.inflateMenu(activity).show()
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -9,8 +9,6 @@ import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.download.Subject
|
||||
import com.topjohnwu.magisk.core.download.Subject.Manager
|
||||
import com.topjohnwu.magisk.core.model.MagiskJson
|
||||
import com.topjohnwu.magisk.core.model.ManagerJson
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.events.OpenInappLinkEvent
|
||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||
@@ -18,8 +16,7 @@ import com.topjohnwu.magisk.events.dialog.EnvFixDialog
|
||||
import com.topjohnwu.magisk.events.dialog.ManagerInstallDialog
|
||||
import com.topjohnwu.magisk.events.dialog.UninstallDialog
|
||||
import com.topjohnwu.magisk.ktx.await
|
||||
import com.topjohnwu.magisk.ktx.packageName
|
||||
import com.topjohnwu.magisk.ktx.res
|
||||
import com.topjohnwu.magisk.utils.asTransitive
|
||||
import com.topjohnwu.magisk.utils.set
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -34,41 +31,46 @@ class HomeViewModel(
|
||||
private val svc: NetworkService
|
||||
) : BaseViewModel() {
|
||||
|
||||
val magiskTitleBarrierIds =
|
||||
intArrayOf(R.id.home_magisk_icon, R.id.home_magisk_title, R.id.home_magisk_button)
|
||||
val magiskDetailBarrierIds =
|
||||
intArrayOf(R.id.home_magisk_installed_version, R.id.home_device_details_ramdisk)
|
||||
val appTitleBarrierIds =
|
||||
intArrayOf(R.id.home_manager_icon, R.id.home_manager_title, R.id.home_manager_button)
|
||||
|
||||
@get:Bindable
|
||||
var isNoticeVisible = Config.safetyNotice
|
||||
set(value) = set(value, field, { field = it }, BR.noticeVisible)
|
||||
|
||||
@get:Bindable
|
||||
var stateMagisk = MagiskState.LOADING
|
||||
set(value) = set(value, field, { field = it }, BR.stateMagisk, BR.showUninstall)
|
||||
val stateMagisk = when {
|
||||
!Info.env.isActive -> MagiskState.NOT_INSTALLED
|
||||
Info.env.magiskVersionCode < BuildConfig.VERSION_CODE -> MagiskState.OBSOLETE
|
||||
else -> MagiskState.UP_TO_DATE
|
||||
}
|
||||
|
||||
@get:Bindable
|
||||
var stateManager = MagiskState.LOADING
|
||||
set(value) = set(value, field, { field = it }, BR.stateManager)
|
||||
|
||||
@get:Bindable
|
||||
var magiskRemoteVersion = R.string.loading.res()
|
||||
set(value) = set(value, field, { field = it }, BR.magiskRemoteVersion)
|
||||
|
||||
val magiskInstalledVersion get() =
|
||||
"${Info.env.magiskVersionString} (${Info.env.magiskVersionCode})"
|
||||
val magiskInstalledVersion get() = Info.env.run {
|
||||
if (isActive)
|
||||
"$magiskVersionString ($magiskVersionCode)".asTransitive()
|
||||
else
|
||||
R.string.not_available.asTransitive()
|
||||
}
|
||||
|
||||
@get:Bindable
|
||||
var managerRemoteVersion = R.string.loading.res()
|
||||
var managerRemoteVersion = R.string.loading.asTransitive()
|
||||
set(value) = set(value, field, { field = it }, BR.managerRemoteVersion)
|
||||
|
||||
val managerInstalledVersion = Info.stub?.let {
|
||||
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) (${it.version})"
|
||||
} ?: "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
|
||||
val statePackageName = packageName
|
||||
|
||||
@get:Bindable
|
||||
var stateManagerProgress = 0
|
||||
set(value) = set(value, field, { field = it }, BR.stateManagerProgress)
|
||||
|
||||
@get:Bindable
|
||||
val showUninstall get() = Info.env.isActive && state != State.LOADING
|
||||
|
||||
@get:Bindable
|
||||
val showSafetyNet get() = Info.hasGMS && isConnected.get()
|
||||
|
||||
@@ -80,30 +82,25 @@ class HomeViewModel(
|
||||
|
||||
override fun refresh() = viewModelScope.launch {
|
||||
state = State.LOADING
|
||||
svc.fetchUpdate()?.apply {
|
||||
notifyPropertyChanged(BR.showSafetyNet)
|
||||
Info.getRemote(svc)?.apply {
|
||||
state = State.LOADED
|
||||
stateMagisk = when {
|
||||
!Info.env.isActive -> MagiskState.NOT_INSTALLED
|
||||
magisk.isObsolete -> MagiskState.OBSOLETE
|
||||
else -> MagiskState.UP_TO_DATE
|
||||
}
|
||||
|
||||
stateManager = when {
|
||||
app.isObsolete -> MagiskState.OBSOLETE
|
||||
BuildConfig.VERSION_CODE < magisk.versionCode -> MagiskState.OBSOLETE
|
||||
else -> MagiskState.UP_TO_DATE
|
||||
}
|
||||
|
||||
magiskRemoteVersion =
|
||||
"${magisk.version} (${magisk.versionCode})"
|
||||
managerRemoteVersion =
|
||||
"${app.version} (${app.versionCode}) (${stub.versionCode})"
|
||||
"${magisk.version} (${magisk.versionCode}) (${stub.versionCode})".asTransitive()
|
||||
|
||||
launch {
|
||||
ensureEnv()
|
||||
}
|
||||
} ?: apply { state = State.LOADING_FAILED }
|
||||
notifyPropertyChanged(BR.showUninstall)
|
||||
notifyPropertyChanged(BR.showSafetyNet)
|
||||
} ?: {
|
||||
state = State.LOADING_FAILED
|
||||
managerRemoteVersion = R.string.not_available.asTransitive()
|
||||
}()
|
||||
}
|
||||
|
||||
val showTest = false
|
||||
@@ -115,9 +112,8 @@ class HomeViewModel(
|
||||
}.publish()
|
||||
|
||||
fun onProgressUpdate(progress: Float, subject: Subject) {
|
||||
when (subject) {
|
||||
is Manager -> stateManagerProgress = progress.times(100f).roundToInt()
|
||||
}
|
||||
if (subject is Manager)
|
||||
stateManagerProgress = progress.times(100f).roundToInt()
|
||||
}
|
||||
|
||||
fun onLinkPressed(link: String) = OpenInappLinkEvent(link).publish()
|
||||
@@ -125,17 +121,13 @@ class HomeViewModel(
|
||||
fun onDeletePressed() = UninstallDialog().publish()
|
||||
|
||||
fun onManagerPressed() = when (state) {
|
||||
State.LOADED -> ManagerInstallDialog().publish()
|
||||
State.LOADED -> withExternalRW { ManagerInstallDialog().publish() }
|
||||
State.LOADING -> SnackbarEvent(R.string.loading).publish()
|
||||
else -> SnackbarEvent(R.string.no_connection).publish()
|
||||
}
|
||||
|
||||
fun onMagiskPressed() = when (state) {
|
||||
State.LOADED -> withExternalRW {
|
||||
HomeFragmentDirections.actionHomeFragmentToInstallFragment().publish()
|
||||
}
|
||||
State.LOADING -> SnackbarEvent(R.string.loading).publish()
|
||||
else -> SnackbarEvent(R.string.no_connection).publish()
|
||||
fun onMagiskPressed() = withExternalRW {
|
||||
HomeFragmentDirections.actionHomeFragmentToInstallFragment().publish()
|
||||
}
|
||||
|
||||
fun onSafetyNetPressed() =
|
||||
@@ -160,9 +152,4 @@ class HomeViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private val MagiskJson.isObsolete
|
||||
get() = Info.env.isActive && Info.env.magiskVersionCode < versionCode
|
||||
private val ManagerJson.isObsolete
|
||||
get() = BuildConfig.VERSION_CODE < versionCode
|
||||
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@ import android.view.ViewGroup
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||
import com.topjohnwu.magisk.core.download.BaseDownloader
|
||||
import com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding
|
||||
import com.topjohnwu.magisk.ktx.coroutineScope
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
@@ -23,7 +22,6 @@ class InstallFragment : BaseUIFragment<InstallViewModel, FragmentInstallMd2Bindi
|
||||
|
||||
// Allow markwon to run in viewmodel scope
|
||||
binding.releaseNotes.coroutineScope = viewModel.viewModelScope
|
||||
BaseDownloader.observeProgress(this, viewModel::onProgressUpdate)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
@@ -1,33 +1,35 @@
|
||||
package com.topjohnwu.magisk.ui.install
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.download.Action
|
||||
import com.topjohnwu.magisk.core.download.DownloadService
|
||||
import com.topjohnwu.magisk.core.download.Subject
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.events.MagiskInstallFileEvent
|
||||
import com.topjohnwu.magisk.events.dialog.SecondSlotWarningDialog
|
||||
import com.topjohnwu.magisk.ui.flash.FlashFragment
|
||||
import com.topjohnwu.magisk.utils.set
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.get
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class InstallViewModel(
|
||||
svc: NetworkService
|
||||
) : BaseViewModel(State.LOADED) {
|
||||
) : BaseViewModel() {
|
||||
|
||||
val isRooted = Shell.rootAccess()
|
||||
val skipOptions = Info.ramdisk && !Info.isFDE && Info.isSAR
|
||||
val skipOptions = Info.isEmulator || (Info.ramdisk && !Info.isFDE && Info.isSAR)
|
||||
val noSecondSlot = !isRooted || Info.isPixel || Info.isVirtualAB || !Info.isAB || Info.isEmulator
|
||||
|
||||
@get:Bindable
|
||||
var step = if (skipOptions) 1 else 0
|
||||
@@ -52,10 +54,6 @@ class InstallViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
@get:Bindable
|
||||
var progress = 0
|
||||
set(value) = set(value, field, { field = it }, BR.progress)
|
||||
|
||||
@get:Bindable
|
||||
var data: Uri? = null
|
||||
set(value) = set(value, field, { field = it }, BR.data)
|
||||
@@ -67,41 +65,35 @@ class InstallViewModel(
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
notes = svc.fetchString(Info.remote.magisk.note)
|
||||
val context = get<Context>()
|
||||
File(context.cacheDir, "${BuildConfig.VERSION_CODE}.md").run {
|
||||
notes = when {
|
||||
exists() -> readText()
|
||||
Const.Url.CHANGELOG_URL.isEmpty() -> ""
|
||||
else -> {
|
||||
val text = svc.fetchString(Const.Url.CHANGELOG_URL)
|
||||
writeText(text)
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onProgressUpdate(progress: Float, subject: Subject) {
|
||||
if (subject !is Subject.Magisk) {
|
||||
return
|
||||
}
|
||||
this.progress = progress.times(100).roundToInt()
|
||||
if (this.progress >= 100) {
|
||||
state = State.LOADED
|
||||
} else if (this.progress < -150) {
|
||||
state = State.LOADING_FAILED
|
||||
}
|
||||
}
|
||||
|
||||
fun step(nextStep: Int) {
|
||||
step = nextStep
|
||||
}
|
||||
|
||||
fun install() {
|
||||
DownloadService.start(get(), Subject.Magisk(resolveAction()))
|
||||
when (method) {
|
||||
R.id.method_patch -> FlashFragment.patch(data!!)
|
||||
R.id.method_direct -> FlashFragment.flash(false)
|
||||
R.id.method_inactive_slot -> FlashFragment.flash(true)
|
||||
else -> error("Unknown value")
|
||||
}
|
||||
state = State.LOADING
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
private fun resolveAction() = when (method) {
|
||||
R.id.method_download -> Action.Download
|
||||
R.id.method_patch -> Action.Patch(data!!)
|
||||
R.id.method_direct -> Action.Flash.Primary
|
||||
R.id.method_inactive_slot -> Action.Flash.Secondary
|
||||
else -> error("Unknown value")
|
||||
}
|
||||
}
|
||||
|
@@ -313,10 +313,8 @@ class ModuleViewModel(
|
||||
|
||||
fun infoPressed(item: ModuleItem) {
|
||||
item.repo?.also {
|
||||
if (isConnected.get())
|
||||
OpenReadmeEvent(it).publish()
|
||||
else
|
||||
SnackbarEvent(R.string.no_connection).publish()
|
||||
if (isConnected.get()) OpenReadmeEvent(it).publish()
|
||||
else SnackbarEvent(R.string.no_connection).publish()
|
||||
} ?: return
|
||||
}
|
||||
}
|
||||
|
@@ -61,10 +61,10 @@ object Theme : BaseSettingsItem.Blank() {
|
||||
override val title = R.string.section_theme.asTransitive()
|
||||
}
|
||||
|
||||
// --- Manager
|
||||
// --- App
|
||||
|
||||
object Manager : BaseSettingsItem.Section() {
|
||||
override val title = R.string.manager.asTransitive()
|
||||
object AppSettings : BaseSettingsItem.Section() {
|
||||
override val title = R.string.home_app_title.asTransitive()
|
||||
}
|
||||
|
||||
object ClearRepoCache : BaseSettingsItem.Blank() {
|
||||
@@ -77,8 +77,8 @@ object ClearRepoCache : BaseSettingsItem.Blank() {
|
||||
}
|
||||
|
||||
object Hide : BaseSettingsItem.Input() {
|
||||
override val title = R.string.settings_hide_manager_title.asTransitive()
|
||||
override val description = R.string.settings_hide_manager_summary.asTransitive()
|
||||
override val title = R.string.settings_hide_app_title.asTransitive()
|
||||
override val description = R.string.settings_hide_app_summary.asTransitive()
|
||||
|
||||
override var value = ""
|
||||
set(value) = setV(value, field, { field = it })
|
||||
@@ -106,11 +106,8 @@ object Hide : BaseSettingsItem.Input() {
|
||||
}
|
||||
|
||||
object Restore : BaseSettingsItem.Blank() {
|
||||
override val title = R.string.settings_restore_manager_title.asTransitive()
|
||||
override val description = R.string.settings_restore_manager_summary.asTransitive()
|
||||
override fun refresh() {
|
||||
isEnabled = Info.remote.app.versionCode > 0
|
||||
}
|
||||
override val title = R.string.settings_restore_app_title.asTransitive()
|
||||
override val description = R.string.settings_restore_app_summary.asTransitive()
|
||||
}
|
||||
|
||||
object AddShortcut : BaseSettingsItem.Blank() {
|
||||
@@ -141,21 +138,27 @@ object DownloadPath : BaseSettingsItem.Input() {
|
||||
|
||||
object UpdateChannel : BaseSettingsItem.Selector() {
|
||||
override var value = Config.updateChannel
|
||||
set(value) = setV(value, field, { field = it }) { Config.updateChannel = it }
|
||||
set(value) = setV(value, field, { field = it }) {
|
||||
Config.updateChannel = it
|
||||
Info.remote = Info.EMPTY_REMOTE
|
||||
}
|
||||
|
||||
override val title = R.string.settings_update_channel_title.asTransitive()
|
||||
override val entries: Array<String> = resources.getStringArray(R.array.update_channel).let {
|
||||
if (BuildConfig.DEBUG) it.toMutableList().apply { add("Canary") }.toTypedArray() else it
|
||||
override val entries: Array<String> = resources.getStringArray(R.array.update_channel).apply {
|
||||
if (BuildConfig.VERSION_CODE % 100 == 0)
|
||||
toMutableList().apply { removeAt(Config.Value.CANARY_CHANNEL) }.toTypedArray()
|
||||
}
|
||||
override val description
|
||||
get() = entries.getOrNull(value)?.asTransitive()
|
||||
?: TransitiveText.String(if (value == -1) entries[0] else "Canary")
|
||||
get() = entries.getOrNull(value)?.asTransitive() ?: TransitiveText.String(entries[0])
|
||||
}
|
||||
|
||||
object UpdateChannelUrl : BaseSettingsItem.Input() {
|
||||
override val title = R.string.settings_update_custom.asTransitive()
|
||||
override var value = Config.customChannelUrl
|
||||
set(value) = setV(value, field, { field = it }) { Config.customChannelUrl = it }
|
||||
set(value) = setV(value, field, { field = it }) {
|
||||
Config.customChannelUrl = it
|
||||
Info.remote = Info.EMPTY_REMOTE
|
||||
}
|
||||
override val description get() = value.asTransitive()
|
||||
|
||||
override val inputResult get() = result
|
||||
@@ -312,7 +315,7 @@ object RequestTimeout : BaseSettingsItem.Selector() {
|
||||
|
||||
override var value = selected
|
||||
set(value) = setV(value, field, { field = it }) {
|
||||
Config.suDefaultTimeout = it
|
||||
Config.suDefaultTimeout = entryValues[it].toInt()
|
||||
}
|
||||
|
||||
private val selected: Int
|
||||
|
@@ -21,6 +21,7 @@ import com.topjohnwu.magisk.data.database.RepoDao
|
||||
import com.topjohnwu.magisk.events.AddHomeIconEvent
|
||||
import com.topjohnwu.magisk.events.RecreateEvent
|
||||
import com.topjohnwu.magisk.events.dialog.BiometricEvent
|
||||
import com.topjohnwu.magisk.ktx.activity
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -54,13 +55,17 @@ class SettingsViewModel(
|
||||
|
||||
// Manager
|
||||
list.addAll(listOf(
|
||||
Manager,
|
||||
AppSettings,
|
||||
UpdateChannel, UpdateChannelUrl, DoHToggle, UpdateChecker, DownloadPath
|
||||
))
|
||||
if (Info.env.isActive) {
|
||||
list.add(ClearRepoCache)
|
||||
if (Const.USER_ID == 0 && Info.isConnected.get())
|
||||
list.add(if (hidden) Restore else Hide)
|
||||
if (Build.VERSION.SDK_INT >= 21 && Const.USER_ID == 0) {
|
||||
if (hidden)
|
||||
list.add(Restore)
|
||||
else if (Info.isConnected.get())
|
||||
list.add(Hide)
|
||||
}
|
||||
}
|
||||
|
||||
// Magisk
|
||||
@@ -101,16 +106,18 @@ class SettingsViewModel(
|
||||
is Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().publish()
|
||||
is ClearRepoCache -> clearRepoCache()
|
||||
is SystemlessHosts -> createHosts()
|
||||
is Restore -> HideAPK.restore(view.context)
|
||||
is Restore -> HideAPK.restore(view.activity)
|
||||
is AddShortcut -> AddHomeIconEvent().publish()
|
||||
else -> callback()
|
||||
}
|
||||
|
||||
override fun onItemChanged(view: View, item: BaseSettingsItem) = when (item) {
|
||||
is Language -> RecreateEvent().publish()
|
||||
is UpdateChannel -> openUrlIfNecessary(view)
|
||||
is Hide -> HideAPK.hide(view.context, item.value)
|
||||
else -> Unit
|
||||
override fun onItemChanged(view: View, item: BaseSettingsItem) {
|
||||
when (item) {
|
||||
is Language -> RecreateEvent().publish()
|
||||
is UpdateChannel -> openUrlIfNecessary(view)
|
||||
is Hide -> viewModelScope.launch { HideAPK.hide(view.activity, item.value) }
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun openUrlIfNecessary(view: View) {
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
@@ -13,8 +15,8 @@ import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class HideBottomViewOnScrollBehavior<V : View> : HideBottomViewOnScrollBehavior<V>(),
|
||||
HideableBehavior<V> {
|
||||
class HideBottomViewOnScrollBehavior<V : View>(context: Context, attrs: AttributeSet) :
|
||||
HideBottomViewOnScrollBehavior<V>(), HideableBehavior<V> {
|
||||
|
||||
private var lockState: Boolean = false
|
||||
private var isLaidOut = false
|
||||
@@ -122,4 +124,4 @@ class HideBottomViewOnScrollBehavior<V : View> : HideBottomViewOnScrollBehavior<
|
||||
.setInterpolator(FastOutSlowInInterpolator())
|
||||
.start()
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import android.animation.TimeInterpolator
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewPropertyAnimator
|
||||
@@ -8,9 +10,8 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import com.google.android.material.animation.AnimationUtils
|
||||
|
||||
class HideTopViewOnScrollBehavior<V : View> :
|
||||
CoordinatorLayout.Behavior<V>(),
|
||||
HideableBehavior<V> {
|
||||
class HideTopViewOnScrollBehavior<V : View>(context: Context, attrs: AttributeSet) :
|
||||
CoordinatorLayout.Behavior<V>(), HideableBehavior<V> {
|
||||
|
||||
companion object {
|
||||
private const val STATE_SCROLLED_DOWN = 1
|
||||
@@ -139,4 +140,4 @@ class HideTopViewOnScrollBehavior<V : View> :
|
||||
.setDuration(duration)
|
||||
.withEndAction { currentAnimator = null }
|
||||
.let { currentAnimator = it }
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ import android.widget.TextView
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.caverock.androidsvg.SVG
|
||||
import com.caverock.androidsvg.SVGParseException
|
||||
import com.topjohnwu.magisk.core.ResMgr
|
||||
import com.topjohnwu.magisk.core.AssetHack
|
||||
import com.topjohnwu.superuser.internal.WaitRunnable
|
||||
import io.noties.markwon.AbstractMarkwonPlugin
|
||||
import io.noties.markwon.MarkwonSpansFactory
|
||||
@@ -216,7 +216,7 @@ class MarkwonImagePlugin(okHttp: OkHttpClient) : AbstractMarkwonPlugin() {
|
||||
return PictureDrawable(picture)
|
||||
}
|
||||
|
||||
val density: Float = ResMgr.resource.displayMetrics.density
|
||||
val density: Float = AssetHack.resource.displayMetrics.density
|
||||
|
||||
val width = (w * density + .5f).toInt()
|
||||
val height = (h * density + .5f).toInt()
|
||||
@@ -226,7 +226,7 @@ class MarkwonImagePlugin(okHttp: OkHttpClient) : AbstractMarkwonPlugin() {
|
||||
canvas.scale(density, density)
|
||||
svg.renderToCanvas(canvas)
|
||||
|
||||
return BitmapDrawable(ResMgr.resource, bitmap)
|
||||
return BitmapDrawable(AssetHack.resource, bitmap)
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ import android.content.res.Resources
|
||||
import android.widget.TextView
|
||||
import androidx.databinding.BindingAdapter
|
||||
import androidx.databinding.InverseBindingAdapter
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
|
||||
sealed class TransitiveText {
|
||||
|
||||
@@ -42,13 +41,13 @@ sealed class TransitiveText {
|
||||
}
|
||||
|
||||
|
||||
fun Int.asTransitive(vararg params: Any) = TransitiveText.Res(this, *params)
|
||||
fun CharSequence.asTransitive() = TransitiveText.String(this)
|
||||
fun Int.asTransitive(vararg params: Any): TransitiveText = TransitiveText.Res(this, *params)
|
||||
fun CharSequence.asTransitive(): TransitiveText = TransitiveText.String(this)
|
||||
|
||||
|
||||
@BindingAdapter("android:text")
|
||||
fun TextView.setText(text: TransitiveText) {
|
||||
this.text = text.getText(get())
|
||||
this.text = text.getText(context.resources)
|
||||
}
|
||||
|
||||
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
|
||||
|
@@ -47,7 +47,8 @@ class MagiskDialog(
|
||||
super.onCreate(savedInstanceState)
|
||||
super.setContentView(binding.root)
|
||||
|
||||
val surfaceColor = MaterialColors.getColor(context, R.attr.colorSurfaceSurfaceVariant, javaClass.canonicalName)
|
||||
val default = MaterialColors.getColor(context, R.attr.colorSurface, javaClass.canonicalName)
|
||||
val surfaceColor = MaterialColors.getColor(context, R.attr.colorSurfaceSurfaceVariant, default)
|
||||
val materialShapeDrawable = MaterialShapeDrawable(context, null, R.attr.alertDialogStyle, R.style.MaterialAlertDialog_MaterialComponents)
|
||||
materialShapeDrawable.initializeElevationOverlay(context)
|
||||
materialShapeDrawable.fillColor = ColorStateList.valueOf(surfaceColor)
|
||||
|
@@ -1,54 +0,0 @@
|
||||
package com.topjohnwu.magisk.view
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.TextView
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.ktx.coroutineScope
|
||||
import io.noties.markwon.Markwon
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
import timber.log.Timber
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
object MarkDownWindow : KoinComponent {
|
||||
|
||||
private val svc: NetworkService by inject()
|
||||
private val markwon: Markwon by inject()
|
||||
|
||||
suspend fun show(activity: Context, title: String?, url: String) {
|
||||
show(activity, title) {
|
||||
svc.fetchString(url)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun show(activity: Context, title: String?, input: suspend () -> String) {
|
||||
val view = LayoutInflater.from(activity).inflate(R.layout.markdown_window_md2, null)
|
||||
|
||||
MagiskDialog(activity)
|
||||
.applyTitle(title ?: "")
|
||||
.applyView(view)
|
||||
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||
titleRes = android.R.string.cancel
|
||||
}
|
||||
.reveal()
|
||||
|
||||
val tv = view.findViewById<TextView>(R.id.md_txt)
|
||||
tv.coroutineScope = CoroutineScope(coroutineContext)
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
markwon.setMarkdown(tv, input())
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException)
|
||||
throw e
|
||||
Timber.e(e)
|
||||
tv.post { tv.setText(R.string.download_file_error) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,24 +3,20 @@ package com.topjohnwu.magisk.view
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import androidx.core.app.TaskStackBuilder
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.graphics.drawable.toIcon
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.Const.ID.PROGRESS_NOTIFICATION_CHANNEL
|
||||
import com.topjohnwu.magisk.core.Const.ID.UPDATE_NOTIFICATION_CHANNEL
|
||||
import com.topjohnwu.magisk.core.SplashActivity
|
||||
import com.topjohnwu.magisk.core.cmp
|
||||
import com.topjohnwu.magisk.core.download.DownloadService
|
||||
import com.topjohnwu.magisk.core.download.Subject
|
||||
import com.topjohnwu.magisk.core.intent
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import com.topjohnwu.magisk.ktx.getBitmap
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
object Notifications {
|
||||
|
||||
val mgr by lazy { get<Context>().getSystemService<NotificationManager>()!! }
|
||||
@@ -49,29 +45,11 @@ object Notifications {
|
||||
}
|
||||
}
|
||||
|
||||
fun magiskUpdate(context: Context) {
|
||||
val intent = context.intent<SplashActivity>()
|
||||
.putExtra(Const.Key.OPEN_SECTION, "magisk")
|
||||
val stackBuilder = TaskStackBuilder.create(context)
|
||||
stackBuilder.addParentStack(SplashActivity::class.java.cmp(context.packageName))
|
||||
stackBuilder.addNextIntent(intent)
|
||||
val pendingIntent = stackBuilder.getPendingIntent(
|
||||
Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
|
||||
val builder = updateBuilder(context)
|
||||
.setContentTitle(context.getString(R.string.magisk_update_title))
|
||||
.setContentText(context.getString(R.string.manager_download_install))
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
|
||||
mgr.notify(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, builder.build())
|
||||
}
|
||||
|
||||
fun managerUpdate(context: Context) {
|
||||
val intent = DownloadService.pendingIntent(context, Subject.Manager())
|
||||
|
||||
val builder = updateBuilder(context)
|
||||
.setContentTitle(context.getString(R.string.manager_update_title))
|
||||
.setContentTitle(context.getString(R.string.magisk_update_title))
|
||||
.setContentText(context.getString(R.string.manager_download_install))
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(intent)
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package com.topjohnwu.magisk.view
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ShortcutInfo
|
||||
@@ -8,6 +7,8 @@ import android.content.pm.ShortcutManager
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
@@ -24,16 +25,14 @@ object Shortcuts {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
fun addHomeIcon(context: Context) {
|
||||
val manager = context.getSystemService<ShortcutManager>() ?: return
|
||||
val intent = context.packageManager.getLaunchIntentForPackage(context.packageName) ?: return
|
||||
val info = ShortcutInfo.Builder(context, Const.Nav.HOME)
|
||||
.setShortLabel(context.getString(R.string.app_name))
|
||||
val info = ShortcutInfoCompat.Builder(context, Const.Nav.HOME)
|
||||
.setShortLabel(context.getString(R.string.magisk))
|
||||
.setIntent(intent)
|
||||
.setIcon(context.getIcon(R.drawable.ic_launcher))
|
||||
.setIcon(context.getIconCompat(R.drawable.ic_launcher))
|
||||
.build()
|
||||
manager.requestPinShortcut(info, null)
|
||||
ShortcutManagerCompat.requestPinShortcut(context, info, null)
|
||||
}
|
||||
|
||||
private fun Context.getIconCompat(id: Int): IconCompat {
|
||||
|
@@ -19,18 +19,10 @@ public abstract class JarMap implements Closeable {
|
||||
|
||||
LinkedHashMap<String, JarEntry> entryMap;
|
||||
|
||||
public static JarMap open(String file) throws IOException {
|
||||
return new FileMap(new File(file), true, ZipFile.OPEN_READ);
|
||||
}
|
||||
|
||||
public static JarMap open(File file, boolean verify) throws IOException {
|
||||
return new FileMap(file, verify, ZipFile.OPEN_READ);
|
||||
}
|
||||
|
||||
public static JarMap open(String file, boolean verify) throws IOException {
|
||||
return new FileMap(new File(file), verify, ZipFile.OPEN_READ);
|
||||
}
|
||||
|
||||
public static JarMap open(InputStream is, boolean verify) throws IOException {
|
||||
return new StreamMap(is, verify);
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package com.topjohnwu.signing;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -32,6 +33,7 @@ import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Keep
|
||||
public class SignBoot {
|
||||
|
||||
private static final int BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET = 1632;
|
||||
@@ -88,6 +90,21 @@ public class SignBoot {
|
||||
}
|
||||
}
|
||||
|
||||
private static int fullRead(InputStream in, byte[] b) throws IOException {
|
||||
return fullRead(in, b, 0, b.length);
|
||||
}
|
||||
|
||||
private static int fullRead(InputStream in, byte[] b, int off, int len) throws IOException {
|
||||
int n = 0;
|
||||
while (n < len) {
|
||||
int count = in.read(b, off + n, len - n);
|
||||
if (count <= 0)
|
||||
break;
|
||||
n += count;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
public static boolean doSignature(
|
||||
@Nullable X509Certificate cert, @Nullable PrivateKey key,
|
||||
@NonNull InputStream imgIn, @NonNull OutputStream imgOut, @NonNull String target
|
||||
@@ -96,7 +113,7 @@ public class SignBoot {
|
||||
PushBackRWStream in = new PushBackRWStream(imgIn, imgOut);
|
||||
byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM];
|
||||
// First read the header
|
||||
in.read(hdr);
|
||||
fullRead(in, hdr);
|
||||
int signableSize = getSignableImageSize(hdr);
|
||||
// Unread header
|
||||
in.unread(hdr);
|
||||
@@ -126,7 +143,7 @@ public class SignBoot {
|
||||
try {
|
||||
// Read the header for size
|
||||
byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM];
|
||||
if (imgIn.read(hdr) != hdr.length) {
|
||||
if (fullRead(imgIn, hdr) != hdr.length) {
|
||||
System.err.println("Unable to read image header");
|
||||
return false;
|
||||
}
|
||||
@@ -135,7 +152,7 @@ public class SignBoot {
|
||||
// Read the rest of the image
|
||||
byte[] rawImg = Arrays.copyOf(hdr, signableSize);
|
||||
int remain = signableSize - hdr.length;
|
||||
if (imgIn.read(rawImg, hdr.length, remain) != remain) {
|
||||
if (fullRead(imgIn, rawImg, hdr.length, remain) != remain) {
|
||||
System.err.println("Unable to read image");
|
||||
return false;
|
||||
}
|
||||
|
@@ -3,9 +3,9 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="?colorSecondary" />
|
||||
<solid android:color="@android:color/black" />
|
||||
<corners android:bottomLeftRadius="2dp" android:bottomRightRadius="2dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</selector>
|
||||
</selector>
|
||||
|
@@ -3,9 +3,9 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="?colorSecondary" />
|
||||
<solid android:color="@android:color/black" />
|
||||
<corners android:topLeftRadius="2dp" android:topRightRadius="2dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</selector>
|
||||
</selector>
|
||||
|
@@ -35,6 +35,7 @@
|
||||
style="@style/WidgetFoundation.Appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="com.topjohnwu.magisk.utils.HideTopViewOnScrollBehavior"
|
||||
app:fitsSystemWindowsInsets="top">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
@@ -76,6 +77,7 @@
|
||||
android:layout_marginBottom="@dimen/l1"
|
||||
android:fitsSystemWindows="false"
|
||||
app:layout_fitsSystemWindowsInsets="bottom"
|
||||
app:layout_behavior="com.topjohnwu.magisk.utils.HideBottomViewOnScrollBehavior"
|
||||
tools:layout_marginBottom="64dp">
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
|
@@ -56,29 +56,6 @@
|
||||
app:icon="@drawable/ic_restart"
|
||||
app:iconTint="?colorOnPrimary" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card.Elevated"
|
||||
goneUnless="@{viewModel.loading}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
app:contentPadding="@dimen/l1">
|
||||
|
||||
<TextView
|
||||
movieBehavior="@{viewModel.loading}"
|
||||
movieBehaviorText="@{viewModel.behaviorText}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:fontFamily="monospace"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textColor="?colorOnSurface"
|
||||
android:textStyle="bold"
|
||||
tools:text="Flashing..." />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
||||
|
@@ -108,7 +108,7 @@
|
||||
app:layout_constraintTop_toBottomOf="@+id/home_magisk_wrapper" />
|
||||
|
||||
<Space
|
||||
gone="@{!viewModel.showSafetyNet && !viewModel.showUninstall}"
|
||||
gone="@{!viewModel.showSafetyNet && !Info.env.isActive}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/l1" />
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Outlined.Error"
|
||||
gone="@{!viewModel.showUninstall}"
|
||||
goneUnless="@{Info.env.isActive}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
|
@@ -25,306 +25,218 @@
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
tools:paddingTop="24dp">
|
||||
|
||||
<FrameLayout
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/l_50">
|
||||
|
||||
<LinearLayout
|
||||
goneUnless="@{viewModel.loaded && viewModel.progress <= 0}"
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
gone="@{viewModel.skipOptions}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/l_50">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
gone="@{viewModel.skipOptions}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
style="@style/WidgetFoundation.Icon"
|
||||
isSelected="@{viewModel.step > 0}"
|
||||
android:layout_marginStart="@dimen/l_25"
|
||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/install_options_title"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.step != 0}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.step(1)}"
|
||||
android:text="@string/install_next" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
gone="@{viewModel.step != 0}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@dimen/l_50"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingEnd="3dp"
|
||||
tools:layout_gravity="center">
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
gone="@{Info.isSAR}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.keepVerity}"
|
||||
android:text="@string/keep_dm_verity"
|
||||
tools:checked="true" />
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
goneUnless="@{Info.isFDE}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.keepEnc}"
|
||||
android:text="@string/keep_force_encryption"
|
||||
app:tint="?colorPrimary" />
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
gone="@{Info.ramdisk}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.recovery}"
|
||||
android:text="@string/recovery_mode"
|
||||
app:tint="?colorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
style="@style/WidgetFoundation.Icon"
|
||||
isSelected="@{viewModel.step > 1}"
|
||||
android:layout_marginStart="@dimen/l_25"
|
||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/install_method_title"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.step != 1}"
|
||||
isEnabled="@{viewModel.method == @id/method_patch ? viewModel.data != null : viewModel.method != -1}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.install()}"
|
||||
android:text="@string/install_start"
|
||||
app:icon="@drawable/ic_forth_md2"
|
||||
app:iconGravity="textEnd" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RadioGroup
|
||||
gone="@{viewModel.step != 1}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@dimen/l_50"
|
||||
android:checkedButton="@={viewModel.method}"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingEnd="3dp">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_download"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/download_zip_only"
|
||||
tools:checked="true" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_patch"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/select_patch_file" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_direct"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
gone="@{!viewModel.rooted}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/direct_install" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_inactive_slot"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
gone="@{!viewModel.isRooted || Info.isPixel || !Info.isAB}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/install_inactive_slot" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/release_notes"
|
||||
markdownText="@{viewModel.notes}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="15dp"
|
||||
android:textAppearance="@style/AppearanceFoundation.Caption"
|
||||
android:visibility="gone"
|
||||
tools:maxLines="5"
|
||||
tools:text="@tools:sample/lorem/random"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
goneUnless="@{viewModel.loading}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
android:focusable="false">
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/loading"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title" />
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/l1">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ProgressBar
|
||||
style="@style/WidgetFoundation.ProgressBar"
|
||||
invisible="@{viewModel.progress <= 0}"
|
||||
android:layout_width="100dp"
|
||||
android:layout_gravity="center"
|
||||
android:progress="@{viewModel.progress}" />
|
||||
<ImageView
|
||||
style="@style/WidgetFoundation.Icon"
|
||||
isSelected="@{viewModel.step > 0}"
|
||||
android:layout_marginStart="@dimen/l_25"
|
||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||
|
||||
<ProgressBar
|
||||
style="@style/WidgetFoundation.ProgressBar.Indeterminate"
|
||||
invisibleUnless="@{viewModel.progress <= 0}"
|
||||
android:layout_width="100dp"
|
||||
android:layout_gravity="center" />
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/install_options_title"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</FrameLayout>
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.step != 0}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.step(1)}"
|
||||
android:text="@string/install_next" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
gone="@{viewModel.step != 0}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@dimen/l_50"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingEnd="3dp"
|
||||
tools:layout_gravity="center">
|
||||
|
||||
<LinearLayout
|
||||
goneUnless="@{viewModel.loaded && viewModel.progress >= 100}"
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
gone="@{Info.isSAR}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.keepVerity}"
|
||||
android:text="@string/keep_dm_verity"
|
||||
tools:checked="true" />
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
goneUnless="@{Info.isFDE}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.keepEnc}"
|
||||
android:text="@string/keep_force_encryption"
|
||||
app:tint="?colorPrimary" />
|
||||
|
||||
<CheckBox
|
||||
style="@style/WidgetFoundation.Checkbox"
|
||||
gone="@{Info.ramdisk}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={Config.recovery}"
|
||||
android:text="@string/recovery_mode"
|
||||
app:tint="?colorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/download_complete"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title" />
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
goneUnless="@{viewModel.loadFailed}"
|
||||
<ImageView
|
||||
style="@style/WidgetFoundation.Icon"
|
||||
isSelected="@{viewModel.step > 1}"
|
||||
android:layout_marginStart="@dimen/l_25"
|
||||
app:srcCompat="@drawable/ic_check_circle_md2" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/install_method_title"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.step != 1}"
|
||||
isEnabled="@{viewModel.method == @id/method_patch ? viewModel.data != null : viewModel.method != -1}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.install()}"
|
||||
android:text="@string/install_start"
|
||||
app:icon="@drawable/ic_forth_md2"
|
||||
app:iconGravity="textEnd" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RadioGroup
|
||||
gone="@{viewModel.step != 1}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@dimen/l_50"
|
||||
android:checkedButton="@={viewModel.method}"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingEnd="3dp">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_patch"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/select_patch_file" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_direct"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
gone="@{!viewModel.rooted}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/direct_install" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/method_inactive_slot"
|
||||
style="@style/WidgetFoundation.RadioButton"
|
||||
gone="@{viewModel.noSecondSlot}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/install_inactive_slot" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
gone="@{viewModel.notes.empty}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:focusable="false">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/release_notes"
|
||||
markdownText="@{viewModel.notes}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/download_file_error"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title" />
|
||||
android:layout_margin="15dp"
|
||||
android:textAppearance="@style/AppearanceFoundation.Caption"
|
||||
android:visibility="gone"
|
||||
tools:maxLines="5"
|
||||
tools:text="@tools:sample/lorem/random"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
|
@@ -46,46 +46,52 @@
|
||||
android:text="@string/magisk"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title"
|
||||
android:textColor="?colorPrimary"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/home_magisk_icon"
|
||||
app:layout_constraintEnd_toStartOf="@+id/home_magisk_button"
|
||||
app:layout_constraintStart_toEndOf="@+id/home_magisk_icon"
|
||||
app:layout_constraintTop_toTopOf="@+id/home_magisk_icon" />
|
||||
|
||||
<Button
|
||||
<FrameLayout
|
||||
android:id="@+id/home_magisk_button"
|
||||
style="@style/WidgetFoundation.Button"
|
||||
invisible="@{viewModel.stateMagisk != MagiskState.OBSOLETE}"
|
||||
android:enabled="@{viewModel.stateManager != MagiskState.OBSOLETE}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.onMagiskPressed()}"
|
||||
android:text="@string/update"
|
||||
android:textAllCaps="false"
|
||||
app:icon="@drawable/ic_update_md2"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/home_magisk_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/home_magisk_title"/>
|
||||
app:layout_constraintBottom_toBottomOf="@+id/home_magisk_title"
|
||||
app:layout_constraintTop_toTopOf="@+id/home_magisk_title">
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
invisible="@{viewModel.stateMagisk == MagiskState.OBSOLETE}"
|
||||
android:enabled="@{viewModel.stateManager != MagiskState.OBSOLETE}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.onMagiskPressed()}"
|
||||
android:text="@string/install"
|
||||
android:textAllCaps="false"
|
||||
app:icon="@drawable/ic_install"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/home_magisk_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/home_magisk_title"/>
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button"
|
||||
gone="@{viewModel.stateMagisk != MagiskState.OBSOLETE}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.onMagiskPressed()}"
|
||||
android:text="@string/update"
|
||||
android:textAllCaps="false"
|
||||
android:layout_gravity="end"
|
||||
app:icon="@drawable/ic_update_md2" />
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.stateMagisk == MagiskState.OBSOLETE}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:onClick="@{() -> viewModel.onMagiskPressed()}"
|
||||
android:text="@string/install"
|
||||
android:textAllCaps="false"
|
||||
app:icon="@drawable/ic_install"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/home_magisk_title_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
tools:constraint_referenced_ids="home_magisk_button,home_magisk_title,home_magisk_icon" />
|
||||
app:referencedIds="@{viewModel.magiskTitleBarrierIds}"
|
||||
tools:constraint_referenced_ids="home_magisk_icon,home_magisk_title,home_magisk_button" />
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
@@ -105,41 +111,24 @@
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_magisk_latest_version"
|
||||
android:id="@+id/home_magisk_installed_version"
|
||||
style="@style/W.Home.Item.Top"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
style="@style/W.Home.ItemContent"
|
||||
android:text="@string/home_latest_version" />
|
||||
|
||||
<TextView
|
||||
style="@style/W.Home.ItemContent.Right"
|
||||
android:text="@{viewModel.loadFailed ? @string/not_available : viewModel.magiskRemoteVersion }"
|
||||
tools:text="20.1 (12345)" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_magisk_installed_version"
|
||||
style="@style/W.Home.Item"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/home_magisk_latest_version">
|
||||
|
||||
<TextView
|
||||
style="@style/W.Home.ItemContent"
|
||||
android:text="@string/home_installed_version" />
|
||||
|
||||
<TextView
|
||||
style="@style/W.Home.ItemContent.Right"
|
||||
android:text="@{Info.env.isActive ? viewModel.magiskInstalledVersion : @string/not_available}"
|
||||
tools:text="20.1 (12345)" />
|
||||
android:text="@{viewModel.magiskInstalledVersion}"
|
||||
tools:text="22.0 (22000)" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_device_details_recovery"
|
||||
android:id="@+id/home_device_details_ramdisk"
|
||||
style="@style/W.Home.Item.Bottom"
|
||||
app:layout_constraintStart_toStartOf="@+id/home_magisk_installed_version"
|
||||
app:layout_constraintTop_toBottomOf="@+id/home_magisk_installed_version">
|
||||
@@ -160,7 +149,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="end"
|
||||
tools:constraint_referenced_ids="home_magisk_latest_version,home_magisk_installed_version"/>
|
||||
app:referencedIds="@{viewModel.magiskDetailBarrierIds}"
|
||||
tools:constraint_referenced_ids="home_magisk_installed_version,home_device_details_ramdisk"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_device_details_ab"
|
||||
@@ -181,7 +171,7 @@
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_device_details_sar"
|
||||
style="@style/W.Home.Item"
|
||||
style="@style/W.Home.Item.Bottom"
|
||||
app:layout_constraintStart_toStartOf="@+id/home_device_details_ab"
|
||||
app:layout_constraintTop_toBottomOf="@+id/home_device_details_ab">
|
||||
|
||||
@@ -196,23 +186,6 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_device_details_crypto"
|
||||
style="@style/W.Home.Item.Bottom"
|
||||
app:layout_constraintStart_toStartOf="@+id/home_device_details_sar"
|
||||
app:layout_constraintTop_toBottomOf="@+id/home_device_details_sar">
|
||||
|
||||
<TextView
|
||||
style="@style/W.Home.ItemContent"
|
||||
android:text="Crypto" />
|
||||
|
||||
<TextView
|
||||
style="@style/W.Home.ItemContent.Right"
|
||||
android:text="@{Info.cryptoText}"
|
||||
tools:text="N/A" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
@@ -43,48 +43,56 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:text="@string/manager"
|
||||
android:text="@string/home_app_title"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title"
|
||||
android:textColor="?colorPrimary"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/home_manager_icon"
|
||||
app:layout_constraintEnd_toStartOf="@+id/home_manager_button"
|
||||
app:layout_constraintStart_toEndOf="@+id/home_manager_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/manager" />
|
||||
tools:text="@string/home_app_title" />
|
||||
|
||||
<Button
|
||||
<FrameLayout
|
||||
android:id="@+id/home_manager_button"
|
||||
style="@style/WidgetFoundation.Button"
|
||||
invisible="@{viewModel.stateManager != MagiskState.OBSOLETE}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.onManagerPressed()}"
|
||||
android:text="@string/update"
|
||||
android:textAllCaps="false"
|
||||
app:icon="@drawable/ic_update_md2"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/home_manager_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/home_manager_title"/>
|
||||
app:layout_constraintBottom_toBottomOf="@+id/home_manager_title"
|
||||
app:layout_constraintTop_toTopOf="@+id/home_manager_title">
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
invisible="@{viewModel.stateManager == MagiskState.OBSOLETE}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.onManagerPressed()}"
|
||||
android:text="@string/install"
|
||||
android:textAllCaps="false"
|
||||
app:icon="@drawable/ic_install"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/home_manager_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/home_manager_title"/>
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button"
|
||||
gone="@{viewModel.stateManager != MagiskState.OBSOLETE}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="@{() -> viewModel.onManagerPressed()}"
|
||||
android:text="@string/update"
|
||||
android:textAllCaps="false"
|
||||
android:layout_gravity="end"
|
||||
app:icon="@drawable/ic_update_md2" />
|
||||
|
||||
<Button
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{viewModel.stateManager != MagiskState.UP_TO_DATE}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:onClick="@{() -> viewModel.onManagerPressed()}"
|
||||
android:text="@string/install"
|
||||
android:textAllCaps="false"
|
||||
app:icon="@drawable/ic_install"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/home_manager_title_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
tools:constraint_referenced_ids="home_manager_button,home_manager_title,home_manager_icon" />
|
||||
app:referencedIds="@{viewModel.appTitleBarrierIds}"
|
||||
tools:constraint_referenced_ids="home_manager_icon,home_manager_title,home_manager_button" />
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
@@ -115,8 +123,8 @@
|
||||
|
||||
<TextView
|
||||
style="@style/W.Home.ItemContent.Right"
|
||||
android:text="@{viewModel.loadFailed ? @string/not_available : viewModel.managerRemoteVersion}"
|
||||
tools:text="8.0.0 (123) (10)" />
|
||||
android:text="@{viewModel.managerRemoteVersion}"
|
||||
tools:text="22.0 (22000) (16)" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -133,7 +141,7 @@
|
||||
<TextView
|
||||
style="@style/W.Home.ItemContent.Right"
|
||||
android:text="@{viewModel.managerInstalledVersion}"
|
||||
tools:text="8.0.0 (123) (10)" />
|
||||
tools:text="22.0 (22000) (16)" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -150,7 +158,7 @@
|
||||
<TextView
|
||||
android:id="@+id/home_manager_extra_connection_value"
|
||||
style="@style/W.Home.ItemContent.Right"
|
||||
android:text="@{viewModel.statePackageName}"
|
||||
android:text="@{context.packageName}"
|
||||
tools:text="com.topjohnwu.magisk" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
<variable
|
||||
name="item"
|
||||
type="com.topjohnwu.magisk.ui.hide.HideItem" />
|
||||
type="com.topjohnwu.magisk.ui.hide.HideRvItem" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
@@ -64,7 +64,7 @@
|
||||
app:layout_constraintStart_toEndOf="@+id/hide_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="@string/app_name" />
|
||||
tools:text="@string/magisk" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hide_package"
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
<variable
|
||||
name="item"
|
||||
type="com.topjohnwu.magisk.ui.hide.HideProcessItem" />
|
||||
type="com.topjohnwu.magisk.ui.hide.HideProcessRvItem" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
|
@@ -58,7 +58,7 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/app_name" />
|
||||
tools:text="@string/magisk" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/log_date"
|
||||
|
@@ -29,14 +29,15 @@
|
||||
tools:layout_gravity="center"
|
||||
tools:minHeight="?listPreferredItemHeightSmall">
|
||||
|
||||
<View
|
||||
<ImageView
|
||||
android:id="@+id/track_top"
|
||||
invisible="@{isTop}"
|
||||
android:layout_width="2dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:alpha=".3"
|
||||
android:background="@drawable/bg_line_bottom_rounded"
|
||||
app:tint="?colorSecondary"
|
||||
app:srcCompat="@drawable/bg_line_bottom_rounded"
|
||||
app:layout_constraintBottom_toTopOf="@+id/track_bullet"
|
||||
app:layout_constraintEnd_toEndOf="@+id/track_bullet"
|
||||
app:layout_constraintStart_toStartOf="@+id/track_bullet"
|
||||
@@ -54,14 +55,15 @@
|
||||
app:tint="@color/color_primary_error_transient"
|
||||
tools:srcCompat="@drawable/ic_check_md2" />
|
||||
|
||||
<View
|
||||
<ImageView
|
||||
android:id="@+id/track_bottom"
|
||||
invisible="@{isBottom}"
|
||||
android:layout_width="2dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:alpha=".3"
|
||||
android:background="@drawable/bg_line_top_rounded"
|
||||
app:tint="?colorSecondary"
|
||||
app:srcCompat="@drawable/bg_line_top_rounded"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+id/track_bullet"
|
||||
app:layout_constraintStart_toStartOf="@+id/track_bullet"
|
||||
|
@@ -61,7 +61,7 @@
|
||||
app:layout_constraintEnd_toStartOf="@+id/policy_indicator"
|
||||
app:layout_constraintStart_toEndOf="@+id/policy_app_icon"
|
||||
app:layout_constraintTop_toTopOf="@+id/policy_app_icon"
|
||||
tools:text="@string/app_name" />
|
||||
tools:text="@string/magisk" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/policy_package_name"
|
||||
|
@@ -49,10 +49,6 @@
|
||||
android:label="FlashFragment"
|
||||
tools:layout="@layout/fragment_flash_md2">
|
||||
|
||||
<argument
|
||||
android:name="installer"
|
||||
app:argType="android.net.Uri" />
|
||||
|
||||
<argument
|
||||
android:name="action"
|
||||
app:argType="string" />
|
||||
|
1
app/src/main/res/raw/.gitignore
vendored
1
app/src/main/res/raw/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/util_functions.sh
|
@@ -1,10 +1,8 @@
|
||||
## v8.0.4
|
||||
## v8.0.7
|
||||
|
||||
- A lot of stability changes and minor bug fixes
|
||||
- Collect device properties, app logcat, and Magisk logs when saving logs in the logs menu
|
||||
- Fix sepolicy rule migration when upgrading
|
||||
|
||||
## v8.0.3
|
||||
## v8.0.6
|
||||
|
||||
- Switch to the new Magisk Module Repo setup in preparation to allow 3rd party repos
|
||||
- Add tapjacking protection on Superuser request dialog
|
||||
- Stability changes and bug fixes
|
||||
- Minor UI changes
|
||||
- Update internal scripts
|
||||
|
@@ -7,27 +7,41 @@ run_delay() {
|
||||
}
|
||||
|
||||
env_check() {
|
||||
for file in busybox magisk magiskboot magiskinit util_functions.sh boot_patch.sh; do
|
||||
for file in busybox magiskboot magiskinit util_functions.sh boot_patch.sh; do
|
||||
[ -f $MAGISKBIN/$file ] || return 1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
fix_env() {
|
||||
cd $MAGISKBIN
|
||||
PATH=/system/bin /system/bin/sh update-binary -x
|
||||
./busybox rm -f update-binary magisk.apk
|
||||
./busybox chmod -R 755 .
|
||||
./magiskinit -x magisk magisk
|
||||
cp_readlink() {
|
||||
if [ -z $2 ]; then
|
||||
cd $1
|
||||
else
|
||||
cp -af $1/. $2
|
||||
cd $2
|
||||
fi
|
||||
for file in *; do
|
||||
if [ -L $file ]; then
|
||||
local full=$(readlink -f $file)
|
||||
rm $file
|
||||
cp -af $full $file
|
||||
fi
|
||||
done
|
||||
chmod -R 755 .
|
||||
cd /
|
||||
}
|
||||
|
||||
direct_install() {
|
||||
rm -rf $MAGISKBIN/* 2>/dev/null
|
||||
fix_env() {
|
||||
# Cleanup and make dirs
|
||||
rm -rf $MAGISKBIN/*
|
||||
mkdir -p $MAGISKBIN 2>/dev/null
|
||||
chmod 700 $NVBASE
|
||||
cp -af $1/. $MAGISKBIN
|
||||
rm -f $MAGISKBIN/new-boot.img
|
||||
cp_readlink $1 $MAGISKBIN
|
||||
rm -rf $1
|
||||
chown -R 0:0 $MAGISKBIN
|
||||
}
|
||||
|
||||
direct_install() {
|
||||
echo "- Flashing new boot image"
|
||||
flash_image $1/new-boot.img $2
|
||||
case $? in
|
||||
@@ -40,10 +54,22 @@ direct_install() {
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
rm -rf $1
|
||||
|
||||
rm -f $1/new-boot.img
|
||||
fix_env $1
|
||||
run_migrations
|
||||
copy_sepolicy_rules
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
run_uninstaller() {
|
||||
rm -rf /dev/tmp
|
||||
mkdir -p /dev/tmp/install
|
||||
unzip -o "$1" "assets/*" "lib/*" -d /dev/tmp/install
|
||||
INSTALLER=/dev/tmp/install sh /dev/tmp/install/assets/uninstaller.sh dummy 1 "$1"
|
||||
}
|
||||
|
||||
restore_imgs() {
|
||||
[ -z $SHA1 ] && return 1
|
||||
local BACKUPDIR=/data/magisk_backup_$SHA1
|
||||
@@ -63,15 +89,17 @@ restore_imgs() {
|
||||
}
|
||||
|
||||
post_ota() {
|
||||
cd $1
|
||||
cd /data/adb
|
||||
cp -f $1 bootctl
|
||||
rm -f $1
|
||||
chmod 755 bootctl
|
||||
./bootctl hal-info || return
|
||||
[ $(./bootctl get-current-slot) -eq 0 ] && SLOT_NUM=1 || SLOT_NUM=0
|
||||
./bootctl set-active-boot-slot $SLOT_NUM
|
||||
cat << EOF > post-fs-data.d/post_ota.sh
|
||||
${1}/bootctl mark-boot-successful
|
||||
rm -f ${1}/bootctl
|
||||
rm -f ${1}/post-fs-data.d/post_ota.sh
|
||||
/data/adb/bootctl mark-boot-successful
|
||||
rm -f /data/adb/bootctl
|
||||
rm -f /data/adb/post-fs-data.d/post_ota.sh
|
||||
EOF
|
||||
chmod 755 post-fs-data.d/post_ota.sh
|
||||
cd /
|
||||
@@ -172,8 +200,7 @@ grep_prop() { return; }
|
||||
# Initialize
|
||||
#############
|
||||
|
||||
mm_init() {
|
||||
export BOOTMODE=true
|
||||
app_init() {
|
||||
mount_partitions
|
||||
get_flags
|
||||
run_migrations
|
||||
@@ -183,3 +210,5 @@ mm_init() {
|
||||
# Make sure RECOVERYMODE has value
|
||||
[ -z $RECOVERYMODE ] && RECOVERYMODE=false
|
||||
}
|
||||
|
||||
export BOOTMODE=true
|
||||
|
@@ -14,20 +14,15 @@
|
||||
<!--Home-->
|
||||
<string name="no_connection">لا يوجد إتصال</string>
|
||||
<string name="app_changelog">تفاصيل التحديث</string>
|
||||
<string name="manager">المدير</string>
|
||||
<string name="loading">جارٍ التحميل...</string>
|
||||
<string name="update">تحديث</string>
|
||||
<string name="not_available">غير/متوفر</string>
|
||||
<string name="hide">إخفاء</string>
|
||||
<string name="status">الحالة</string>
|
||||
<string name="home_package">الحزمة</string>
|
||||
|
||||
<string name="home_notice_content">تأكد دوما أنك تستعمل إصدار ماجسيك رسمي، التطبيقات المثبتة من الجهات الخارجية ستسبب لك أضراراً</string>
|
||||
<string name="home_support_title">تبرع لنا</string>
|
||||
<string name="home_item_source">الكود المصدري للتطبيق</string>
|
||||
<string name="home_support_content">مـاجـيسك هي، وستظل دوماً، مجانيةً و مفتوحة المصدر، اظهر اهتمامك لنا لكي نبقيها هكذا بدعم مالي صغير</string>
|
||||
<string name="home_status_normal"> الوضـع الطبيعي</string>
|
||||
<string name="home_status_stub"> وضـع STUB</string>
|
||||
<string name="home_installed_version">تم التثبيت</string>
|
||||
<string name="home_latest_version">آخر إصدار</string>
|
||||
<string name="invalid_update_channel">مصدر التحديث غير صالح</string>
|
||||
@@ -43,7 +38,6 @@
|
||||
<string name="install_next">التالي</string>
|
||||
<string name="install_start">هيا، بنا</string>
|
||||
<string name="manager_download_install">اضغط للتنزيل و التثبيت</string>
|
||||
<string name="download_zip_only">تحميل Zip فقط</string>
|
||||
<string name="direct_install">تثبيت مباشر (موصى بها)</string>
|
||||
<string name="install_inactive_slot">التثبيت على المنطقة الغير نشطة (بعد OTA)</string>
|
||||
<string name="install_inactive_slot_msg">سيُجبر جهازك للاقلاع على المنطقة الغير النشطة بعد إعادة التشغيل!\n استخدم هذا الخيار فقط بعد الانتهاء من OTA. استمرار؟</string>
|
||||
@@ -133,10 +127,6 @@
|
||||
<string name="settings_download_path_message">ستحمل الملفات إلى %1$s</string>
|
||||
<string name="settings_clear_cache_title">حذف الذاكرة المؤقتة للإضافات</string>
|
||||
<string name="settings_clear_cache_summary">حذف المعلومات المخزنة محليا، هذا سيجبر التطبيق على التحديث الإضافات عبر الانترنت</string>
|
||||
<string name="settings_hide_manager_title">إخفاء مـاجـيسك Manager</string>
|
||||
<string name="settings_hide_manager_summary">تغيير اسم حزمة التطبيق لتجنب الكشف</string>
|
||||
<string name="settings_restore_manager_title">استعادة مـاجـيسك Manager</string>
|
||||
<string name="settings_restore_manager_summary">استعادة مـاجـيسك Manager مع اسم الحزمة الأصلي</string>
|
||||
<string name="language">اللغة</string>
|
||||
<string name="system_default">(الأفتراضي)</string>
|
||||
<string name="settings_check_update_title">تحقق من التحديثات</string>
|
||||
@@ -195,10 +185,7 @@
|
||||
<string name="progress_channel">إشعارات التقدم</string>
|
||||
<string name="download_complete">اكتمل التنزيل</string>
|
||||
<string name="download_file_error">فشل تنزيل الملف</string>
|
||||
<string name="download_open_parent">أظهر المجلد الرئيسي</string>
|
||||
<string name="download_open_self">أظهر الملف</string>
|
||||
<string name="magisk_update_title">تحديث مـاجـيسك متوفر!</string>
|
||||
<string name="manager_update_title">تحديث مـاجـيسك Manager متوفر!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">نعم</string>
|
||||
@@ -212,8 +199,6 @@
|
||||
<string name="flashing">يتم الحرق...</string>
|
||||
<string name="done">تم!</string>
|
||||
<string name="failure">فشل!</string>
|
||||
<string name="hide_manager_title">جار إخفاء مـاجـيسك Manager…</string>
|
||||
<string name="hide_manager_fail_toast">فشل إخفاء مـاجـيسك Manager…</string>
|
||||
<string name="open_link_failed_toast">لم يُعثر على تطبيق لفتح الرابط …</string>
|
||||
<string name="complete_uninstall">إلغاء التثبيت بالكامل</string>
|
||||
<string name="restore_img">استعادة الصور</string>
|
||||
@@ -221,13 +206,10 @@
|
||||
<string name="restore_done">تم الأستعادة</string>
|
||||
<string name="restore_fail">النسخة الاحتياطية الأصلية غير موجودة!</string>
|
||||
<string name="proprietary_title">تنزيل شفرة الملكية</string>
|
||||
<string name="proprietary_notice">مـاجـيسك برنامج FOSS(السرعة والأمان)،ولا يحتوي على شفرة SafetyNet الخاصة بكوكل.\n\nهل تسمح لـمـاجـيسك بتحميل ملحق GoogleApiClient لفحص SafetyNet?</string>
|
||||
<string name="setup_fail">فشل التضبيط</string>
|
||||
<string name="env_fix_title"> الإعداد الأضافي مطلوب</string>
|
||||
<string name="env_fix_msg">يحتاج جهازك إلى إعداد إضافي ليتسنى لـمـاجـيسك العمل بشكل صحيح. سيتم تنزيل ملف مضغوط لتثبيت مـاجـيسك هل تريد المتابعة؟</string>
|
||||
<string name="setup_msg">جار تضبيت البيئة</string>
|
||||
<string name="authenticate">تصديق</string>
|
||||
<string name="unsupport_magisk_title">إصدار مـاجـيسك غير مدعوم</string>
|
||||
<string name="unsupport_magisk_msg">إصدار الملف أصغر من %1$s.مـاجـيسك لا يدعمها..\n\nسيتصرف التطبيق كأنك لا تملك روت بجهازك.فضلاًً حدث المعجبين باسرع وقت.</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -43,10 +43,7 @@
|
||||
<string name="progress_channel">Nəticə Bildirişləri</string>
|
||||
<string name="download_complete">Yükləmə bitdi</string>
|
||||
<string name="download_file_error">Faylı yükləmək alınmadı</string>
|
||||
<string name="download_open_parent">Valideyn qovluğunda göstər</string>
|
||||
<string name="download_open_self">Faylı göstər</string>
|
||||
<string name="magisk_update_title">Magisk Yeniləməsi Var!</string>
|
||||
<string name="manager_update_title">Magisk Manager Yeniləməsi Var!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="repo_install_title">%1$s faylını yüklə</string>
|
||||
@@ -57,10 +54,7 @@
|
||||
<string name="repo_cache_cleared">Repo keşi silindi</string>
|
||||
<string name="manager_download_install">Yükləyib quraşdırmaq üçün toxun.</string>
|
||||
<string name="flashing">Qurulur</string>
|
||||
<string name="hide_manager_title">Magisk Manager gizlədilir…</string>
|
||||
<string name="hide_manager_fail_toast">Magisk Manager\'i gizlətmək alınmadı.</string>
|
||||
<string name="open_link_failed_toast">Keçid açmağa heçbir tətbiq tapılmadı.</string>
|
||||
<string name="download_zip_only">Yalnız Zip yüklə</string>
|
||||
<string name="direct_install">Birdəfəlik Yüklə (Tövsiyə olunur)</string>
|
||||
<string name="select_patch_file">Fayl Seç və Yamaqla</string>
|
||||
<string name="install_inactive_slot">Fəal olmayan slota quraşdır (OTA\'dan sonra)</string>
|
||||
@@ -71,20 +65,14 @@
|
||||
<string name="restore_done">Geri qaytarma bitdi!</string>
|
||||
<string name="restore_fail">Stock nüsxə mövcud deyil!</string>
|
||||
<string name="proprietary_title">Özəl kodu yükləyin</string>
|
||||
<string name="proprietary_notice">Magisk Manager açıq lisenziyalıdır və Google\'ın özəl SafetyNet API kodunu ehtiva etmir.\n\Magisk Managerə SafetyNet yoxlamaları üçün tərkibində GoogleApiClient olan əlavəni yükləməyə icazə verirsiniz?</string>
|
||||
<string name="setup_fail">Quraşdırma alınmadı.</string>
|
||||
<string name="env_fix_title">Əlavə Quraşdırma Lazımdır</string>
|
||||
<string name="env_fix_msg">Cihazınızın Magisk\'in düzgün işləməsi üçün əlavə quraşdırmaya ehtiyacı var . Bu Magisk zip faylını endirəcək, davam etmək istəyirsiniz?</string>
|
||||
<string name="setup_title">Əlavə quraşdırma</string>
|
||||
<string name="setup_msg">Quraşdırma yerinə yetirilir…</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_clear_cache_title">Repo Keşini Təmizlə</string>
|
||||
<string name="settings_clear_cache_summary">Onlayn repolar üçün keşlənmiş məlumatı silin. Bu tətbiqi onlayn şəkildə yenilənməyə məcbur edir.</string>
|
||||
<string name="settings_hide_manager_title">Magisk Manager\'i Gizlə</string>
|
||||
<string name="settings_hide_manager_summary">Magisk Manager\'i təsadüfi adla yenidən sıxışdır.</string>
|
||||
<string name="settings_restore_manager_title">Magisk Manager\'i Geri Qaytar</string>
|
||||
<string name="settings_restore_manager_summary">Magisk Manager\'i orjinal sıxışdırma ilə geri qaytar</string>
|
||||
<string name="language">Dil</string>
|
||||
<string name="system_default">(Sistem Dili)</string>
|
||||
<string name="settings_check_update_title">Yeniləmələri Yoxla</string>
|
||||
|
@@ -13,19 +13,14 @@
|
||||
<!--Home-->
|
||||
<string name="no_connection">Злучэнне адсутнічае</string>
|
||||
<string name="app_changelog">Спіс змен</string>
|
||||
<string name="manager">Кіраўнік</string>
|
||||
<string name="loading">Загрузка…</string>
|
||||
<string name="update">Абнавіць</string>
|
||||
<string name="not_available">Не</string>
|
||||
<string name="hide">Схаваць</string>
|
||||
<string name="status">Статус</string>
|
||||
<string name="home_package">Пакунак</string>
|
||||
<string name="home_notice_content">Заўсёды правярайце адкуль вы спампоўваеце Magisk Manager. Пакункі, спампаваныя з невядомых крыніц, могуць змяшчаць шкоднасны код.</string>
|
||||
<string name="home_support_title">Падтрымайце нас</string>
|
||||
<string name="home_item_source">Зыходны код</string>
|
||||
<string name="home_support_content">Magisk ёсць і заўсёды будзе бясплатным праектам з адкрытым зыходным кодам. Але вы заўсёды можаце ахвяраваць нам на распрацоўку.</string>
|
||||
<string name="home_status_normal">Звычайны</string>
|
||||
<string name="home_status_stub">Хаванне</string>
|
||||
<string name="home_installed_version">Усталявана</string>
|
||||
<string name="home_latest_version">Апошні</string>
|
||||
<string name="invalid_update_channel">Хібны канал абнаўлення</string>
|
||||
@@ -41,7 +36,6 @@
|
||||
<string name="install_next">Далей</string>
|
||||
<string name="install_start">Усталяваць</string>
|
||||
<string name="manager_download_install">Націсніце, каб спампаваць і ўсталяваць</string>
|
||||
<string name="download_zip_only">Толькі спампаваць Zip з праграмай</string>
|
||||
<string name="direct_install">Непасрэдная ўсталёўка (рэкамендуецца)</string>
|
||||
<string name="install_inactive_slot">Усталяваць у неактыўны слот (пасля OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Ваша прылада ПРЫМУСОВА перазапусціцца ў неактыўны слот!\nВыкарыстоўвайце гэты параметр толькі пасля завяршэння OTA.\nПрацягнуць?</string>
|
||||
@@ -124,10 +118,6 @@
|
||||
<string name="settings_download_path_message">Файлы будуць спампоўвацца ў %1$s</string>
|
||||
<string name="settings_clear_cache_title">Ачыстка кэшу рэпазіторыя</string>
|
||||
<string name="settings_clear_cache_summary">Ачысціць кэш сеціўных рэпазіторыяў. Гэта прымусіць абнавіць кэш</string>
|
||||
<string name="settings_hide_manager_title">Схаваць Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Перапакваць Magisk Manager з выпадковай назвай і назвай пакунка</string>
|
||||
<string name="settings_restore_manager_title">Аднавіць Magisk Manager</string>
|
||||
<string name="settings_restore_manager_summary">Аднавіць Magisk Manager з арыгінальнай назвай і назвай пакунка</string>
|
||||
<string name="language">Мова</string>
|
||||
<string name="system_default">(Сістэмная)</string>
|
||||
<string name="settings_check_update_title">Правяраць на абнаўленні</string>
|
||||
@@ -186,10 +176,7 @@
|
||||
<string name="progress_channel">Апавяшчэнні пра ход выканання</string>
|
||||
<string name="download_complete">Спампоўка завершаная</string>
|
||||
<string name="download_file_error">Не атрымалася спампаваць файл</string>
|
||||
<string name="download_open_parent">Паказаць у каталозе</string>
|
||||
<string name="download_open_self">Паказаць файл</string>
|
||||
<string name="magisk_update_title">Даступна абнаўленне Magisk!</string>
|
||||
<string name="manager_update_title">Даступна абнаўленне Magisk Manager!</string>
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Так</string>
|
||||
<string name="no">Не</string>
|
||||
@@ -202,8 +189,6 @@
|
||||
<string name="flashing">Усталёўка…</string>
|
||||
<string name="done">Завершана!</string>
|
||||
<string name="failure">Не атрымалася</string>
|
||||
<string name="hide_manager_title">Хаванне Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">Не атрымалася схаваць Magisk Manager.</string>
|
||||
<string name="open_link_failed_toast">Праграмы для адкрыцця спасылкі не знойдзена</string>
|
||||
<string name="complete_uninstall">Поўнае выдаленне</string>
|
||||
<string name="restore_img">Аднавіць раздзелы</string>
|
||||
@@ -211,16 +196,12 @@
|
||||
<string name="restore_done">Аднаўленне завершана!</string>
|
||||
<string name="restore_fail">Рэзервовая копія адсутнічае!</string>
|
||||
<string name="proprietary_title">Спампаваць прапрыетарны код</string>
|
||||
<string name="proprietary_notice">Magisk Manager - праект з адкрытым зыходным кодам, які не змяшчае прапрыетарны код SafetyNet API.\n\nДазволіць Magisk Manager спампаваць пашырэнне SafetyNet(змяшчае GoogleApiClient)?</string>
|
||||
<string name="setup_fail">Усталяваць не атрымалася</string>
|
||||
<string name="env_fix_title">Патрабуецца дадатковая ўсталёўка</string>
|
||||
<string name="env_fix_msg">Вашай прыладзе для карэктнай працы патрабуецца дадатковая ўсталёўка Magisk. Будзе спампаваны Zip Magisk. Працягнуць?</string>
|
||||
<string name="setup_msg">Наладка асяроддзя…</string>
|
||||
<string name="authenticate">Аўтэнтыфікацыя</string>
|
||||
<string name="unsupport_magisk_title">Непадтрымліваемая версія Magisk</string>
|
||||
<string name="unsupport_magisk_msg">Гэтая версія Magisk Manager не падтрымлівае Magisk версіі ніжэй за %1$s.\n\nПраграма будзе працаваць так, як быццам Magisk не ўсталяваны. Як мага хутчэй абнавіце Magisk.</string>
|
||||
<string name="external_rw_permission_denied">Дайце доступ да сховішча, каб уключыць гэтую функцыю</string>
|
||||
<string name="add_shortcut_title">Дадаць цэтлік на хатні экран</string>
|
||||
<string name="add_shortcut_msg">Пасля хавання Magisk Manager яго значок і назву можа быць цяжка разглядзець. Хочаце дадаць цэтлік на хатні экран?</string>
|
||||
<string name="app_not_found">Не знойдзена праграм для апрацоўкі гэтага дзеяння</string>
|
||||
</resources>
|
||||
|
@@ -46,14 +46,10 @@
|
||||
<string name="magisk_update_title">Налице е нова версия на Magisk.</string>
|
||||
<string name="release_notes">Бележки</string>
|
||||
<string name="repo_cache_cleared">Кешът на хранилището е изчистен.</string>
|
||||
<string name="manager_update_title">Налице е нова версия на Magisk Manager.</string>
|
||||
<string name="manager_download_install">Докоснете за изтегляне и инсталиране.</string>
|
||||
<string name="update_channel">Актуализации на Magisk</string>
|
||||
<string name="flashing">Инсталиране</string>
|
||||
<string name="hide_manager_title">Скриване на Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">Скриването на Magisk Manager бе неуспешно.</string>
|
||||
<string name="open_link_failed_toast">Не бе намерено приложение за отваряне на линка.</string>
|
||||
<string name="download_zip_only">Изтегляне само на архив</string>
|
||||
<string name="direct_install">Директно инсталиране (Препоръчва се.)</string>
|
||||
<string name="install_inactive_slot">Инсталиране на неактивен слот (След OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Вашето устройство НАЛОЖИТЕЛНО ще стартира текущия неактивен слот при следващото рестартиране.\nИзползвайте тази опция само след като приключи инсталирането на OTA.\nПродължаване?</string>
|
||||
@@ -64,10 +60,8 @@
|
||||
<string name="restore_done">Възстановяването е успешно!</string>
|
||||
<string name="restore_fail">Не е налице архив на стоковия образ!</string>
|
||||
<string name="proprietary_title">Изтегляне на патентования код</string>
|
||||
<string name="proprietary_notice">Magisk Manager е FOSS и затова не включва патентования код за SafetyNet ППИ на Google.\n\nПозволявате ли на Magisk Manager да изтегли добавката (включва GoogleApiClient) за SafetyNet проверки?</string>
|
||||
<string name="setup_fail">Първоначалната настройка е неуспешна.</string>
|
||||
<string name="env_fix_title">Изисква допълнително настройване</string>
|
||||
<string name="env_fix_msg">Вашето устройство се нуждае от допълнително надстройване на Magisk, за да работи нормално. Ще бъде изтеглен архивът за надстройка на Magisk. Желаете ли да продължите?</string>
|
||||
<string name="setup_title">Допълнително надстройване</string>
|
||||
<string name="setup_msg">Надстройването на средата е в ход…</string>
|
||||
<string name="download_file_error">Грешка при изтеглянето на файла.</string>
|
||||
@@ -75,10 +69,6 @@
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_clear_cache_title">Изчистване кеша на хранилището</string>
|
||||
<string name="settings_clear_cache_summary">Изчистване на кешираната информация на онлайн хранилището за принудителното му обновяване.</string>
|
||||
<string name="settings_hide_manager_title">Скриване на Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Смяна пакетното наименование на Magisk Manager със случайно наименование.</string>
|
||||
<string name="settings_restore_manager_title">Възстановяване на Magisk Manager</string>
|
||||
<string name="settings_restore_manager_summary">Възстановяване на оригиналното пакетно наименование на Magisk Manager.</string>
|
||||
<string name="language">Език</string>
|
||||
<string name="system_default">(Системен)</string>
|
||||
<string name="settings_check_update_title">Проверка за актуализации</string>
|
||||
|
@@ -14,20 +14,17 @@
|
||||
<!--Home-->
|
||||
<string name="no_connection">Connexió no disponible</string>
|
||||
<string name="app_changelog">Registre de canvis</string>
|
||||
<string name="manager">Manager</string>
|
||||
<string name="loading">Carregant…</string>
|
||||
<string name="update">Actualització</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Amagar</string>
|
||||
<string name="status">Estat</string>
|
||||
<string name="home_package">Paquet</string>
|
||||
<string name="home_app_title">App</string>
|
||||
|
||||
<string name="home_notice_content">Asseguris sempre que està utilitzant un Magisk Manager de codi lliure. Mànagers de fonts desconegudes podrien causar accions malicioses.</string>
|
||||
<string name="home_support_title">Doni\'ns suport</string>
|
||||
<string name="home_notice_content">Descarregui Magisk NOMÉS des de la pàgina oficial de GitHub. Fitxers d\'altres fonts desconegudes poden ser maliciosos!</string>
|
||||
<string name="home_support_title">Doni suport</string>
|
||||
<string name="home_item_source">Codi font</string>
|
||||
<string name="home_support_content">Magisk és, i sempre serà, gratis i codi lliure. De totes maneres, pot mostrar el seu interès fent una petita donació.</string>
|
||||
<string name="home_status_normal">Normal</string>
|
||||
<string name="home_status_stub">Stub</string>
|
||||
<string name="home_installed_version">Instal·lat</string>
|
||||
<string name="home_latest_version">Última</string>
|
||||
<string name="invalid_update_channel">Canal d\'actualització invàlid</string>
|
||||
@@ -44,7 +41,6 @@
|
||||
<string name="install_next">Següent</string>
|
||||
<string name="install_start">Endavant</string>
|
||||
<string name="manager_download_install">Premi per baixar i instal·lar</string>
|
||||
<string name="download_zip_only">Únicament baixa el ZIP</string>
|
||||
<string name="direct_install">Instal·lació directa (Recomanat)</string>
|
||||
<string name="install_inactive_slot">Instal·la a la ranura inactiva (Després d\'una OTA)</string>
|
||||
<string name="install_inactive_slot_msg">El teu dispositiu serà FORÇAT a arrancar en l\'actual ranura inactiva després del reinici!\nUtilitza aquesta opció NOMÉS quan l\'OTA s\'hagi fet.\nContinuar?</string>
|
||||
@@ -56,6 +52,7 @@
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Petició de superusuari</string>
|
||||
<string name="touch_filtered_warning">Com que una aplicació està ofuscant la petició de superusuari, Magisk no pot verificar la seva resposta</string>
|
||||
<string name="deny">Denegar</string>
|
||||
<string name="prompt">Preguntar</string>
|
||||
<string name="grant">Permetre</string>
|
||||
@@ -85,7 +82,7 @@
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">No hi ha cap registre. Provi d\'utilitzar aplicacions que requereixen permisos de superusuari.</string>
|
||||
<string name="log_data_magisk_none">Els registre de Magisk estan buits. Això és estrany.</string>
|
||||
<string name="log_data_magisk_none">Els registres de Magisk estan buits. Això és estrany.</string>
|
||||
<string name="menuSaveLog">Desar registre</string>
|
||||
<string name="menuClearLog">Netejar registre ara</string>
|
||||
<string name="logs_cleared">Registre netejat correctament.</string>
|
||||
@@ -97,18 +94,20 @@
|
||||
<string name="safetynet_res_invalid">La resposta és invàlida</string>
|
||||
<string name="safetynet_attest_success">Èxit!</string>
|
||||
<string name="safetynet_attest_failure">Certificació fallida!</string>
|
||||
<string name="safetynet_attest_loading">Només un segon…</string>
|
||||
<string name="safetynet_attest_loading">Esperi uns segons…</string>
|
||||
<string name="safetynet_attest_restart">Intenta de nou</string>
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">Mostra aplicacions del sistema</string>
|
||||
<string name="show_os_app">Mostra aplicacions del SO</string>
|
||||
<string name="hide_filter_hint">Filtra per nom</string>
|
||||
<string name="hide_scroll_up">Vés amunt</string>
|
||||
<string name="hide_filters">Filtres</string>
|
||||
<string name="hide_search">Cerca</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(No hi ha informació)</string>
|
||||
<string name="reboot_userspace">Reinici suau</string>
|
||||
<string name="reboot_recovery">Reiniciar en Mode Recuperació</string>
|
||||
<string name="reboot_bootloader">Reiniciar en Mode Bootloader</string>
|
||||
<string name="reboot_download">Reiniciar en Mode Download</string>
|
||||
@@ -124,7 +123,7 @@
|
||||
<string name="module_section_online">En línia</string>
|
||||
<string name="sorting_order">Ordre de Classificació</string>
|
||||
|
||||
<!--Settings -->
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Mode del tema</string>
|
||||
<string name="settings_dark_mode_message">Seleccioni el mode que més s\'adeqüi al seu estil!</string>
|
||||
<string name="settings_dark_mode_light">Sempre clar</string>
|
||||
@@ -133,11 +132,11 @@
|
||||
<string name="settings_download_path_title">Directori de baixades</string>
|
||||
<string name="settings_download_path_message">Els arxius es desaran a %1$s</string>
|
||||
<string name="settings_clear_cache_title">Netejar memòria cau del repositori</string>
|
||||
<string name="settings_clear_cache_summary">Neteja la informació en memòria cau per als repositoris en línia. Força a l\'aplicació a actualitzar-se en línia.</string>
|
||||
<string name="settings_hide_manager_title">Amagar Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Torna a empaquetar Magisk Manager amb un nom de paquet a l\'atzar</string>
|
||||
<string name="settings_restore_manager_title">Restaurar Magisk Manager</string>
|
||||
<string name="settings_restore_manager_summary">Restaura Magisk Manager amb el nom de paquet original</string>
|
||||
<string name="settings_clear_cache_summary">Neteja la informació en memòria cau per als repositoris en línia. Força a l\'aplicació a actualitzar-se en línia</string>
|
||||
<string name="settings_hide_app_title">Amagar Magisk Manager</string>
|
||||
<string name="settings_hide_app_summary">Torna a empaquetar Magisk Manager amb un nom de paquet a l\'atzar</string>
|
||||
<string name="settings_restore_app_title">Restaurar Magisk Manager</string>
|
||||
<string name="settings_restore_app_summary">Restaura Magisk Manager amb el nom de paquet original</string>
|
||||
<string name="language">Idioma</string>
|
||||
<string name="system_default">(Idioma del sistema)</string>
|
||||
<string name="settings_check_update_title">Comprovar Actualitzacions</string>
|
||||
@@ -170,6 +169,8 @@
|
||||
<string name="superuser_notification">Notificació de superusuari</string>
|
||||
<string name="settings_su_reauth_title">Demanar després d\'una actualització</string>
|
||||
<string name="settings_su_reauth_summary">Demanar permisos de superusuari novament si una aplicació és actualitzada o reinstal·lada</string>
|
||||
<string name="settings_su_tapjack_title">Activa la protecció contra \'TapJacking\'</string>
|
||||
<string name="settings_su_tapjack_summary">El diàleg per donar permisos de superusuari no respondrà mentre estigui ofuscat per alguna altra finestra o superposició</string>
|
||||
<string name="settings_su_biometric_title">Activar autenticació biomètrica</string>
|
||||
<string name="settings_su_biometric_summary">Utilitza l\'autenticació biomètrica per permetre sol·licituds de superusuari</string>
|
||||
<string name="no_biometric">El dispositiu no suporta o no té establerta configuració biomètrica</string>
|
||||
@@ -177,7 +178,7 @@
|
||||
<string name="setting_add_shortcut_summary">Afegeix una bonica drecera a la pantalla d\'inici en cas que el nom i la icona siguin difícils de reconèixer després d\'amagar l\'aplicació.</string>
|
||||
<string name="settings_doh_title">DNS sobre HTTPS</string>
|
||||
<string name="settings_doh_description">Solució per enverinament de DNS en algunes nacions</string>
|
||||
|
||||
|
||||
<string name="multiuser_mode">Mode Multiusuari</string>
|
||||
<string name="settings_owner_only">Només Administrador del Dispositiu</string>
|
||||
<string name="settings_owner_manage">Administrador del Dispositiu</string>
|
||||
@@ -199,10 +200,7 @@
|
||||
<string name="progress_channel">Notificacions de progrés</string>
|
||||
<string name="download_complete">Baixada completada</string>
|
||||
<string name="download_file_error">Error en baixar l\'arxiu</string>
|
||||
<string name="download_open_parent">Mostra\'l al directori principal</string>
|
||||
<string name="download_open_self">Mostra l\'arxiu</string>
|
||||
<string name="magisk_update_title">Actualització de Magisk disponible!</string>
|
||||
<string name="manager_update_title">Actualització de Magisk Manager disponible!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Sí</string>
|
||||
@@ -216,8 +214,7 @@
|
||||
<string name="flashing">Arranjament…</string>
|
||||
<string name="done">Fet!</string>
|
||||
<string name="failure">Fallit</string>
|
||||
<string name="hide_manager_title">Amagant Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">No s\'ha pogut amagar Magisk Manager.</string>
|
||||
<string name="hide_app_title">Amagant Magisk Manager…</string>
|
||||
<string name="open_link_failed_toast">No s\'ha trobat una aplicació per obrir l\'enllaç</string>
|
||||
<string name="complete_uninstall">Desinstal·lació completa</string>
|
||||
<string name="restore_img">Restaura imatges</string>
|
||||
@@ -225,7 +222,7 @@
|
||||
<string name="restore_done">Restauració feta!</string>
|
||||
<string name="restore_fail">La còpia de seguretat de Estock no existeix!</string>
|
||||
<string name="proprietary_title">Baixar codi propietari</string>
|
||||
<string name="proprietary_notice">Magisk Manager és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nVol permetre que Magisk Manager baixi una extensió (que conté la GoogleApiClient) per poder fer la comprovació de SafetyNet?</string>
|
||||
<string name="proprietary_notice">Magisk és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nVol permetre que Magisk Manager baixi una extensió per poder fer la comprovació de SafetyNet?</string>
|
||||
<string name="setup_fail">Instal·lació fallida</string>
|
||||
<string name="env_fix_title">Es requereix instal·lació addicional</string>
|
||||
<string name="env_fix_msg">El teu dispositiu necessita instal·lació addicional per Magisk per funcionar correctament. Es baixarà el ZIP d\'instal·lació de Magisk, vol procedir a la instal·lació ara?</string>
|
||||
|
@@ -1,169 +1,227 @@
|
||||
<resources>
|
||||
|
||||
<!--Welcome Activity-->
|
||||
<!--Sections-->
|
||||
<string name="modules">Moduly</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="logs">Log</string>
|
||||
<string name="superuser">SuperUser</string>
|
||||
<string name="logs">Protokol</string>
|
||||
<string name="settings">Nastavení</string>
|
||||
<string name="refresh">Obnovit lokální data</string>
|
||||
<string name="install">Instalovat</string>
|
||||
<string name="unsupport_magisk_title">Nepodporovaná verze Magisk</string>
|
||||
<string name="section_home">Domů</string>
|
||||
<string name="section_theme">Téma</string>
|
||||
<string name="safetynet">SafetyNet</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="invalid_update_channel">Neplatný kanál aktualizace</string>
|
||||
<string name="safetynet_api_error">Chyba SafetyNet API</string>
|
||||
<string name="safetynet_res_invalid">Odpověď je neplatná.</string>
|
||||
<string name="keep_force_encryption">Udržet "force encryption"</string>
|
||||
<string name="keep_dm_verity">Udržet "AVB 2.0/dm-verity"</string>
|
||||
<string name="uninstall_magisk_title">Odinstalovat Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Všechny moduly budou zakázány/odstraněny. Root bude odstraněn a pokud jsou data dešifrována, můžou být zašifrována.</string>
|
||||
<string name="update">Aktualizovat</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Žádné informace)</string>
|
||||
<string name="reboot_recovery">Restartovat do Recovery</string>
|
||||
<string name="reboot_bootloader">Restartovat do Bootloaderu</string>
|
||||
<string name="reboot_download">Restartovat do Download</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Dostupná aktualizace</string>
|
||||
<string name="home_installed_version">Nainstalováno</string>
|
||||
<string name="sorting_order">Pořadí řazení</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveLog">Uložit log</string>
|
||||
<string name="menuClearLog">Smazat log</string>
|
||||
<string name="logs_cleared">Log byl smazán.</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<!--Home-->
|
||||
<string name="no_connection">Žádné připojení</string>
|
||||
<string name="app_changelog">Seznam změn</string>
|
||||
<string name="loading">Načítání…</string>
|
||||
<string name="update">Aktualizovat</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Skrýt</string>
|
||||
<string name="home_package">Balíček</string>
|
||||
|
||||
<!-- System Components, Notifications -->
|
||||
<string name="update_channel">Aktualizace Magisk</string>
|
||||
<string name="progress_channel">Oznámení o průběhu</string>
|
||||
<string name="download_complete">Stahování dokončeno</string>
|
||||
<string name="download_file_error">Chyba při stahování souboru</string>
|
||||
<string name="magisk_update_title">Aktualizace Magisk je dostupná!</string>
|
||||
<string name="manager_update_title">Aktualizace Magisk Manager je dostupná!</string>
|
||||
<string name="home_support_title">Podpořte nás</string>
|
||||
<string name="home_item_source">Zdroj</string>
|
||||
<string name="home_support_content">Magisk je a vždy bude svobodný s otevřeným kódem. Můžete nám však zaslat malý dar jako poděkování.</string>
|
||||
<string name="home_installed_version">Nainstalovano</string>
|
||||
<string name="home_latest_version">Poslední</string>
|
||||
<string name="invalid_update_channel">Neplatný kanál aktualizace</string>
|
||||
<string name="uninstall_magisk_title">Odinstalovat Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Všechny moduly budou zakázány/odstraněny!\nROOT bude odstraněn!\nPokud jsou data dešifrována, můžou být zašifrována!</string>
|
||||
<string name="home_check_safetynet">Zkontrolovat SafetyNet</string>
|
||||
|
||||
<!-- Installation -->
|
||||
<string name="manager_download_install">Stiskněte pro stažení a instalaci.</string>
|
||||
<string name="download_zip_only">Stáhnout pouze zip</string>
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Ponechat Force Encryption</string>
|
||||
<string name="keep_dm_verity">Ponechat AVB 2.0/DM-Verity</string>
|
||||
<string name="recovery_mode">Režim Recovery</string>
|
||||
<string name="install_options_title">Možnosti</string>
|
||||
<string name="install_method_title">Způsob</string>
|
||||
<string name="install_next">Další</string>
|
||||
<string name="install_start">Spustit</string>
|
||||
<string name="manager_download_install">Stiskněte pro stažení a instalaci</string>
|
||||
<string name="direct_install">Přímá instalace (doporučeno)</string>
|
||||
<string name="install_inactive_slot">Instalace do druhého slotu (po OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Vaše zařízení bude po restartu VYNUCENĚ spuštěno do aktuálního neaktivního slotu!\nTuto možnost použijte pouze po dokončení OTA.\nPokračovat?</string>
|
||||
<string name="install_inactive_slot_msg">Vaše zařízení bude po restartu VYNUCENĚ spuštěno do aktuálního neaktivního slotu!\nTuto možnost použijte pouze po dokončení OTA.\nChcete pokračovat?</string>
|
||||
<string name="setup_title">Další nastavení</string>
|
||||
<string name="select_patch_file">Vybrat a opravit soubor</string>
|
||||
<string name="patch_file_msg">Vyberte obraz raw (*.img) nebo soubor tar pro ODIN (*.tar)</string>
|
||||
<string name="patch_file_msg">Vyberte soubor RAW (*.img) nebo soubor TAR pro ODIN (*.tar)</string>
|
||||
<string name="reboot_delay_toast">Restartování za 5 sekund…</string>
|
||||
<string name="flash_screen_title">Instalace</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="repo_install_title">Instalovat %1$s</string>
|
||||
<string name="repo_install_msg">Chcete nyní nainstalovat %1$s?</string>
|
||||
<string name="download">Stáhnout</string>
|
||||
<string name="reboot">Restartovat</string>
|
||||
<string name="release_notes">Poznámky k vydání</string>
|
||||
<string name="repo_cache_cleared">Mezipaměť smazána</string>
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Požadavek SuperUser</string>
|
||||
<string name="touch_filtered_warning">Protože aplikace zavírá požadavek SuperUser, Magisk nemůže ověřit Vaši odpověď</string>
|
||||
<string name="deny">Zakázat</string>
|
||||
<string name="prompt">Zeptat se</string>
|
||||
<string name="grant">Povolit</string>
|
||||
<string name="su_warning">Povolí plný přístup k Vašemu zařízení.\nZakažte, pokud si nejste jisti!</string>
|
||||
<string name="forever">Trvale</string>
|
||||
<string name="once">Jednou</string>
|
||||
<string name="tenmin">10 minut</string>
|
||||
<string name="twentymin">20 minut</string>
|
||||
<string name="thirtymin">30 minut</string>
|
||||
<string name="sixtymin">60 minut</string>
|
||||
<string name="su_allow_toast">Pro %1$s bylo oprávnění SuperUser povoleno</string>
|
||||
<string name="su_deny_toast">Pro %1$s bylo oprávnění SuperUser zakázáno</string>
|
||||
<string name="su_snack_grant">SuperUser oprávnění pro %1$s je povoleno</string>
|
||||
<string name="su_snack_deny">SuperUser oprávnění pro %1$s je zakázáno</string>
|
||||
<string name="su_snack_notif_on">Oznámení pro %1$s jsou povolena</string>
|
||||
<string name="su_snack_notif_off">Oznámení pro %1$s jsou zakázána</string>
|
||||
<string name="su_snack_log_on">Protokolování %1$s je povoleno</string>
|
||||
<string name="su_snack_log_off">Protokolování %1$s je zakázáno</string>
|
||||
<string name="su_revoke_title">Smazat?</string>
|
||||
<string name="su_revoke_msg">Chcete smazat protokol k oprávnění pro %1$s?</string>
|
||||
<string name="toast">Text</string>
|
||||
<string name="none">Žádné</string>
|
||||
|
||||
<string name="flashing">Instalování</string>
|
||||
<string name="hide_manager_title">Skrytí Magisk Manageru…</string>
|
||||
<string name="hide_manager_fail_toast">Skrytí Magisk Manageru selhalo.</string>
|
||||
<string name="open_link_failed_toast">Nebyla nalezena žádná aplikace pro otevření odkazu.</string>
|
||||
<string name="complete_uninstall">Dokončete odinstalaci</string>
|
||||
<string name="restore_img">Obnovit obrazy</string>
|
||||
<string name="restore_img_msg">Obnovování…</string>
|
||||
<string name="restore_done">Obnovení bylo provedeno!</string>
|
||||
<string name="restore_fail">Výchozí záloha neexistuje!</string>
|
||||
<string name="proprietary_title">Stáhnout proprietární kód</string>
|
||||
<string name="proprietary_notice">Magisk Manager je FOSS a neobsahuje proprietární kód SafetyNet API společnosti Google.\n\nChcete povolit aplikaci Magisk Manager stažení rozšíření (obsahuje GoogleApiClient) pro kontrolu SafetyNet?</string>
|
||||
<string name="setup_fail">Nastavení selhalo.</string>
|
||||
<string name="env_fix_title">Vyžaduje se další nastavení</string>
|
||||
<string name="env_fix_msg">Vaše zařízení potřebuje další nastavení, aby Magisk fungoval správně. Stáhne se instalační zip Magisk, chcete pokračovat?</string>
|
||||
<string name="setup_msg">Nastavení je spuštěno…</string>
|
||||
<string name="superuser_toggle_notification">Oznámení</string>
|
||||
<string name="superuser_toggle_revoke">Smazat</string>
|
||||
<string name="superuser_policy_none">Žádná aplikace nepožádala o oprávnění SuperUser.</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_clear_cache_title">Smazat uchovanou mezipaměť</string>
|
||||
<string name="settings_clear_cache_summary">Smaže informace online použití v mezipaměti, donutí aplikaci obnovit informace online.</string>
|
||||
<string name="settings_hide_manager_title">Skrýt Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Nahradí Magisk Manager náhodným názvem balíčku.</string>
|
||||
<string name="settings_restore_manager_title">Obnovit Magisk Manager</string>
|
||||
<string name="settings_restore_manager_summary">Obnoví Magisk Manager na původní název balíčku.</string>
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Žádné protokoly. Vyzkoušejte nějakou aplikaci vyžadující oprávnění SuperUser.</string>
|
||||
<string name="log_data_magisk_none">Protokoly Magisk jsou prázdné. To je zvláštní!</string>
|
||||
<string name="menuSaveLog">Uložit protokol</string>
|
||||
<string name="menuClearLog">Smazat protokol</string>
|
||||
<string name="logs_cleared">Protokol byl smazán</string>
|
||||
<string name="pid">PID:%1$d</string>
|
||||
<string name="target_uid">Cílové UID: %1$d</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
<string name="safetynet_api_error">Chyba SafetyNet API</string>
|
||||
<string name="safetynet_res_invalid">Odpověď je neplatná</string>
|
||||
<string name="safetynet_attest_success">Úspěšné!</string>
|
||||
<string name="safetynet_attest_failure">Chyba ověření!</string>
|
||||
<string name="safetynet_attest_loading">Čekejte…</string>
|
||||
<string name="safetynet_attest_restart">Zkusit znovu</string>
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Zobrazit systémové aplikace</string>
|
||||
<string name="show_os_app">Zobrazit OS aplikace</string>
|
||||
<string name="hide_filter_hint">Filtrovat podle názvu</string>
|
||||
<string name="hide_scroll_up">Posunout nahoru</string>
|
||||
<string name="hide_filters">Filtry</string>
|
||||
<string name="hide_search">Vyhledávání</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(žádné informace)</string>
|
||||
<string name="reboot_userspace">Restartovat do UserSpace</string>
|
||||
<string name="reboot_recovery">Restartovat do Recovery</string>
|
||||
<string name="reboot_bootloader">Restartovat do Bootloader</string>
|
||||
<string name="reboot_download">Restartovat do Download</string>
|
||||
<string name="reboot_edl">Restartovat do EDL</string>
|
||||
<string name="module_version_author">%1$s od %2$s</string>
|
||||
<string name="module_section_pending">Aktualizace</string>
|
||||
<string name="module_section_pending_action">Aktualizovat vše</string>
|
||||
<string name="module_state_remove">Odstranit</string>
|
||||
<string name="module_state_restore">Obnovit</string>
|
||||
<string name="module_action_install_external">Instalace z úložiště</string>
|
||||
<string name="update_available">Dostupná aktualizace</string>
|
||||
<string name="module_installed">@string/home_installed_version</string>
|
||||
<string name="module_section_online">Online</string>
|
||||
<string name="sorting_order">Seřadit</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Režim motivu</string>
|
||||
<string name="settings_dark_mode_message">Vyberte režim, který nejlépe vyhovuje Vašemu stylu!</string>
|
||||
<string name="settings_dark_mode_light">Světlý</string>
|
||||
<string name="settings_dark_mode_system">Podle systému</string>
|
||||
<string name="settings_dark_mode_dark">Tmavý</string>
|
||||
<string name="settings_download_path_title">Složka pro stahování</string>
|
||||
<string name="settings_download_path_message">Soubory budou uloženy do %1$s.</string>
|
||||
<string name="settings_clear_cache_title">Smazat mezipaměť</string>
|
||||
<string name="settings_clear_cache_summary">Smažete online informace o použití z mezipaměti, a tím na aplikaci vynutíte obnovení online informací.</string>
|
||||
<string name="language">Jazyk</string>
|
||||
<string name="system_default">(Výchozí systémový)</string>
|
||||
<string name="system_default">(výchozí podle systému)</string>
|
||||
<string name="settings_check_update_title">Zkontrolovat aktualizace</string>
|
||||
<string name="settings_check_update_summary">Pravidelně kontrolujte aktualizace na pozadí.</string>
|
||||
<string name="settings_check_update_summary">Povolíte pravidelnou kontrolu aktualizace na pozadí.</string>
|
||||
<string name="settings_update_channel_title">Kanál aktualizace</string>
|
||||
<string name="settings_update_stable">Stabilní</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_custom">Vlastní</string>
|
||||
<string name="settings_update_custom_msg">Vložte vlastní URL</string>
|
||||
<string name="settings_magiskhide_summary">Skrýt Magisk z různých detekcí.</string>
|
||||
<string name="settings_hosts_title">Nesystémoví hostitelé</string>
|
||||
<string name="settings_hosts_summary">Podpora nesystémových hostitelů pro aplikace Adblock.</string>
|
||||
<string name="settings_magiskhide_summary">Skryjete Magisk před různými detekcemi.</string>
|
||||
<string name="settings_hosts_title">Nesystémový hostitel</string>
|
||||
<string name="settings_hosts_summary">Přidáte modul pro podporu nesystémových hostitelů v aplikaci AdBlock.</string>
|
||||
<string name="settings_hosts_toast">Přidán systémový modul hostitelů</string>
|
||||
|
||||
<string name="settings_app_name_hint">Nový název</string>
|
||||
<string name="settings_app_name_helper">Název aplikace bude nahrazen tímto názvem.</string>
|
||||
<string name="settings_app_name_error">Neplatný formát</string>
|
||||
<string name="settings_su_app_adb">Aplikace a ADB</string>
|
||||
<string name="settings_su_app">Pouze aplikace</string>
|
||||
<string name="settings_su_adb">Pouze ADB</string>
|
||||
<string name="settings_su_disable">Zakázáno</string>
|
||||
<string name="settings_su_disable">Zakázat</string>
|
||||
<string name="settings_su_request_10">10 sekund</string>
|
||||
<string name="settings_su_request_15">15 sekund</string>
|
||||
<string name="settings_su_request_20">20 sekund</string>
|
||||
<string name="settings_su_request_30">30 sekund</string>
|
||||
<string name="settings_su_request_45">45 sekund</string>
|
||||
<string name="settings_su_request_60">60 sekund</string>
|
||||
<string name="superuser_access">Přístup Superuser</string>
|
||||
<string name="superuser_access">Přístup SuperUser</string>
|
||||
<string name="auto_response">Automatická odezva</string>
|
||||
<string name="request_timeout">Časový limit požadavku</string>
|
||||
<string name="superuser_notification">Oznámení Superuser</string>
|
||||
<string name="superuser_notification">Oznámení SuperUser</string>
|
||||
<string name="settings_su_reauth_title">Opětovné ověření po aktualizaci</string>
|
||||
<string name="settings_su_reauth_summary">Opětovné ověření oprávnění Superuser po aktualizaci aplikace</string>
|
||||
<string name="settings_su_reauth_summary">Opětovné ověření oprávnění SuperUser po aktualizaci aplikace.</string>
|
||||
<string name="settings_su_tapjack_title">Povolit ochranu před TapJack</string>
|
||||
<string name="settings_su_tapjack_summary">Okno dialogu SuperUser nebude reagovat na kliknutí v případě, že je zavřené nebo překryté jiným oknem.</string>
|
||||
<string name="settings_su_biometric_title">Povolit biometrické ověření</string>
|
||||
<string name="settings_su_biometric_summary">Použijte biometrické ověření pro povolení požadavků SuperUser.</string>
|
||||
<string name="no_biometric">Nepodporované zařízení nebo není biometrické ověření povolené</string>
|
||||
<string name="settings_customization">Přizpůsobit</string>
|
||||
<string name="setting_add_shortcut_summary">Přidejte odkaz na domovskou obrazovku v případě, že se po skrytí aplikace její název a ikona těžko rozpoznávají.</string>
|
||||
<string name="settings_doh_title">DNS nebo HTTPS</string>
|
||||
<string name="settings_doh_description">Řešení pro opravy DNS v některých zemích.</string>
|
||||
|
||||
<string name="multiuser_mode">Režim více uživatelů</string>
|
||||
<string name="settings_owner_only">Pouze vlastník zařízení</string>
|
||||
<string name="settings_owner_manage">Spravováno vlastníkem zařízení</string>
|
||||
<string name="settings_user_independent">Nezávislý uživatel</string>
|
||||
<string name="owner_only_summary">Pouze vlastník má root přístup.</string>
|
||||
<string name="owner_manage_summary">Root přístup spravuje pouze vlastník a přijímá požadavky k přístupu.</string>
|
||||
<string name="user_indepenent_summary">Každý uživatel má svá vlastní pravidla root.</string>
|
||||
<string name="settings_owner_only">Vlastník zařízení</string>
|
||||
<string name="settings_owner_manage">Správce zařízení</string>
|
||||
<string name="settings_user_independent">Všichni uživatelé</string>
|
||||
<string name="owner_only_summary">Pouze vlastník má přístup ROOT.</string>
|
||||
<string name="owner_manage_summary">Přístup ROOT má správce zařízení, který přijímá požadavky k přístupu.</string>
|
||||
<string name="user_indepenent_summary">Každý uživatel má svá vlastní pravidla přístupu ROOT.</string>
|
||||
|
||||
<string name="mount_namespace_mode">Režim připojení jmenného prostoru</string>
|
||||
<string name="settings_ns_global">Globální jmenný prostor</string>
|
||||
<string name="settings_ns_requester">Zděděný jmenný prostor</string>
|
||||
<string name="settings_ns_requester">Odvozený jmenný prostor</string>
|
||||
<string name="settings_ns_isolate">Izolovaný jmenný prostor</string>
|
||||
<string name="global_summary">Všechny relace root používají globální připojení jmenného prostoru.</string>
|
||||
<string name="requester_summary">Kořenové relace dědí jmenný prostor žadatele.</string>
|
||||
<string name="isolate_summary">Každá relace root bude mít svůj vlastní izolovaný jmenný prostor.</string>
|
||||
<string name="global_summary">Všechny relace ROOT používají globální jmenný prostor.</string>
|
||||
<string name="requester_summary">Relace ROOT je odvozena od jmenného prostoru žadatele.</string>
|
||||
<string name="isolate_summary">Každá relace ROOT má svůj vlastní izolovaný jmenný prostor.</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Požadavek Superuser</string>
|
||||
<string name="deny">Zamítnout</string>
|
||||
<string name="prompt">Dotaz</string>
|
||||
<string name="grant">Povolit</string>
|
||||
<string name="su_warning">Povolí plný přístup k vašemu zařízení.\nZamítněte, pokud si nejste jisti!</string>
|
||||
<string name="forever">Navždy</string>
|
||||
<string name="once">Jednou</string>
|
||||
<string name="tenmin">10 minut</string>
|
||||
<string name="twentymin">20 minut</string>
|
||||
<string name="thirtymin">30 minut</string>
|
||||
<string name="sixtymin">60 minut</string>
|
||||
<string name="su_allow_toast">Pro %1$s bylo oprávnění Superuser povoleno</string>
|
||||
<string name="su_deny_toast">Pro %1$s bylo oprávnění Superuser zamítnuto</string>
|
||||
<string name="su_snack_grant">Superuser oprávnění pro %1$s je povoleno</string>
|
||||
<string name="su_snack_deny">Superuser oprávnění pro %1$s je zamítnuto</string>
|
||||
<string name="su_snack_notif_on">Oznámení pro %1$s jsou povolena</string>
|
||||
<string name="su_snack_notif_off">Oznámení pro %1$s jsou zakázána</string>
|
||||
<string name="su_snack_log_on">Logování %1$s je povoleno</string>
|
||||
<string name="su_snack_log_off">Logování %1$s je zakázáno</string>
|
||||
<string name="su_revoke_title">Smazat?</string>
|
||||
<string name="su_revoke_msg">Smazat záznam ohledně oprávnění pro %1$s?</string>
|
||||
<string name="toast">Informační text</string>
|
||||
<string name="none">Žádný</string>
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Aktualizace Magisk</string>
|
||||
<string name="progress_channel">Oznámení o průběhu</string>
|
||||
<string name="download_complete">Stahování dokončeno</string>
|
||||
<string name="download_file_error">Chyba při stahování souboru</string>
|
||||
<string name="magisk_update_title">Aktualizace Magisk je dostupná!</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Cílové UID: %1$d</string>
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">Zobrazit systémové aplikace</string>
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">ANO</string>
|
||||
<string name="no">NE</string>
|
||||
<string name="repo_install_title">Instalovat %1$s</string>
|
||||
<string name="repo_install_msg">Chcete nyní nainstalovat %1$s?</string>
|
||||
<string name="download">Stáhnout</string>
|
||||
<string name="reboot">Restartovat</string>
|
||||
<string name="release_notes">Poznámky k vydání</string>
|
||||
<string name="repo_cache_cleared">Mezipaměť smazána</string>
|
||||
<string name="flashing">Instalování…</string>
|
||||
<string name="done">Hotovo!</string>
|
||||
<string name="failure">Selhalo!</string>
|
||||
<string name="open_link_failed_toast">Nebyla nalezena žádná aplikace pro otevření odkazu.</string>
|
||||
<string name="complete_uninstall">Odinstalovat</string>
|
||||
<string name="restore_img">Obnovit obrazy</string>
|
||||
<string name="restore_img_msg">Obnovování…</string>
|
||||
<string name="restore_done">Obnovení bylo provedeno!</string>
|
||||
<string name="restore_fail">Výchozí záloha neexistuje!</string>
|
||||
<string name="proprietary_title">Stáhnout kód</string>
|
||||
<string name="setup_fail">Nastavení selhalo</string>
|
||||
<string name="env_fix_title">Vyžaduje se další nastavení</string>
|
||||
<string name="setup_msg">Nastavení je spuštěno…</string>
|
||||
<string name="authenticate">Ověřit</string>
|
||||
<string name="unsupport_magisk_title">Nepodporovaná verze Magisk</string>
|
||||
<string name="external_rw_permission_denied">Udělte oprávnění pro povolení této funkce.</string>
|
||||
<string name="add_shortcut_title">Přidat odkaz na domovskou obrazovku</string>
|
||||
<string name="app_not_found">Nelze nalézt žádnou aplikaci, která dokáže provést tuto akci.</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -14,25 +14,23 @@
|
||||
<!--Home-->
|
||||
<string name="no_connection">Keine Verbindung verfügbar</string>
|
||||
<string name="app_changelog">Änderungen</string>
|
||||
<string name="manager">Manager</string>
|
||||
<string name="loading">Lädt…</string>
|
||||
<string name="update">Update</string>
|
||||
<string name="loading">Laden…</string>
|
||||
<string name="update">Aktualisieren</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Verstecken</string>
|
||||
<string name="status">Status</string>
|
||||
<string name="home_package">Paket</string>
|
||||
<string name="home_app_title">App</string>
|
||||
|
||||
<string name="home_notice_content">Benutzen Sie nur den quelloffenen Magisk Manager. Magisk von unbekannten Quellen kann zu schädlichen Aktionen führen.</string>
|
||||
<string name="home_notice_content">Laden sie Magisk NUR von der offiziellen Github Seite herunter. Dateien aus unbekannten Quellen können bösartig sein!</string>
|
||||
<string name="home_support_title">Unterstützen Sie uns</string>
|
||||
<string name="home_item_source">Quelle</string>
|
||||
<string name="home_support_content">Magisk ist und wird immer frei und quelloffen sein. Sie können uns jedoch jederzeit mit einer kleinen Spende unterstützen.</string>
|
||||
<string name="home_status_normal">Normal</string>
|
||||
<string name="home_status_stub">Stub</string>
|
||||
<string name="home_installed_version">Installiert</string>
|
||||
<string name="home_latest_version">Neueste</string>
|
||||
<string name="invalid_update_channel">Ungültiger Update-Kanal</string>
|
||||
<string name="uninstall_magisk_title">Deinstalliere Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Alle Module werden deaktiviert/entfernt!\nRoot wird entfernt!\nIhre Daten werden potentiell verschlüsselt, wenn sie nicht bereits verschlüsselt sind!</string>
|
||||
<string name="home_check_safetynet">SafetyNet überprüfen</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Erzwungene Verschlüsselung beibehalten</string>
|
||||
@@ -40,10 +38,9 @@
|
||||
<string name="recovery_mode">Recovery Modus</string>
|
||||
<string name="install_options_title">Optionen</string>
|
||||
<string name="install_method_title">Methode</string>
|
||||
<string name="install_next">Nächste</string>
|
||||
<string name="install_next">Nächster Schritt</string>
|
||||
<string name="install_start">Los geht\'s</string>
|
||||
<string name="manager_download_install">Tippen zum Herunterladen und Installieren</string>
|
||||
<string name="download_zip_only">Nur Zip herunterladen</string>
|
||||
<string name="direct_install">Direkte Installation (empfohlen)</string>
|
||||
<string name="install_inactive_slot">Installiere in inaktiven Slot (Nach OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Ihr Gerät wird gezwungen, nach einem Neustart in den inaktiven Slot zu booten! Verwenden Sie diese Option nur, wenn OTA abgeschlossen ist.\nWeiter?</string>
|
||||
@@ -51,9 +48,11 @@
|
||||
<string name="select_patch_file">Eine Datei auswählen und patchen</string>
|
||||
<string name="patch_file_msg">Raw Image (*.img) oder ein ODIN tarfile (*.tar) auswählen</string>
|
||||
<string name="reboot_delay_toast">Neustart in 5 Sekunden…</string>
|
||||
<string name="flash_screen_title">Installieren</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Superuser-Anfrage</string>
|
||||
<string name="touch_filtered_warning">Weil eine App eine Superuser-Anfrage verdeckt, kann Magisk Ihre Antwort nicht verifizieren</string>
|
||||
<string name="deny">Verweigern</string>
|
||||
<string name="prompt">Fragen</string>
|
||||
<string name="grant">Gewähren</string>
|
||||
@@ -68,10 +67,10 @@
|
||||
<string name="su_deny_toast">%1$s wurden Superuser-Rechte verweigert</string>
|
||||
<string name="su_snack_grant">Superuser-Rechte für %1$s gewährt</string>
|
||||
<string name="su_snack_deny">Superuser-Rechte für %1$s verweigert</string>
|
||||
<string name="su_snack_notif_on">Benachrichtigungen von %1$s sind aktiviert</string>
|
||||
<string name="su_snack_notif_off">Benachrichtigungen von %1$s sind deaktiviert</string>
|
||||
<string name="su_snack_log_on">Protokollierung von %1$s ist aktiviert</string>
|
||||
<string name="su_snack_log_off">Protokollierung von %1$s ist deaktiviert</string>
|
||||
<string name="su_snack_notif_on">Benachrichtigungen für %1$s sind an</string>
|
||||
<string name="su_snack_notif_off">Benachrichtigungen für %1$s sind aus</string>
|
||||
<string name="su_snack_log_on">Protokollierung für %1$s ist an</string>
|
||||
<string name="su_snack_log_off">Protokollierung für %1$s ist aus</string>
|
||||
<string name="su_revoke_title">Widerrufen?</string>
|
||||
<string name="su_revoke_msg">Bestätigen, dass %1$s-Rechte widerufen werden?</string>
|
||||
<string name="toast">Toast</string>
|
||||
@@ -100,6 +99,7 @@
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">System-Apps anzeigen</string>
|
||||
<string name="show_os_app">OS-Apps anzeigen</string>
|
||||
<string name="hide_filter_hint">Nach Namen filtern</string>
|
||||
<string name="hide_scroll_up">Nach oben scrollen</string>
|
||||
<string name="hide_filters">Filter</string>
|
||||
@@ -107,6 +107,7 @@
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Keine Informationen verfügbar)</string>
|
||||
<string name="reboot_userspace">Sanfter Neustart</string>
|
||||
<string name="reboot_recovery">Neustart zum Recovery</string>
|
||||
<string name="reboot_bootloader">Neustart zum Bootloader</string>
|
||||
<string name="reboot_download">Neustart zum Download</string>
|
||||
@@ -119,8 +120,8 @@
|
||||
<string name="module_action_install_external">Aus dem Speicher installieren</string>
|
||||
<string name="update_available">Update verfügbar</string>
|
||||
<string name="module_installed">@string/home_installed_version</string>
|
||||
<string name="module_section_online">Online</string>
|
||||
<string name="sorting_order">Sortierreihenfolge</string>
|
||||
<string name="external_rw_permission_denied">Gewähre den Speicher-Zugriff, um diese Funtion zu aktivieren.</string>
|
||||
|
||||
<!--Settings -->
|
||||
<string name="settings_dark_mode_title">Themen-Modus</string>
|
||||
@@ -132,12 +133,12 @@
|
||||
<string name="settings_download_path_message">Dateien werden in %1$s gespeichert</string>
|
||||
<string name="settings_clear_cache_title">Repo-Cache leeren</string>
|
||||
<string name="settings_clear_cache_summary">Löscht die zwischengespeicherten Informationen des Online-Repos. Erzwingt eine Aktualisierung</string>
|
||||
<string name="settings_hide_manager_title">Magisk Manager verbergen</string>
|
||||
<string name="settings_hide_manager_summary">Magisk Manager mit zufälligem Paketnamen neu packen</string>
|
||||
<string name="settings_restore_manager_title">Magisk Manager wiederherstellen</string>
|
||||
<string name="settings_restore_manager_summary">Magisk Manager mit ursprünglichem Paketnamen wiederherstellen</string>
|
||||
<string name="settings_hide_app_title">Verstecke die Magisk App</string>
|
||||
<string name="settings_hide_app_summary">Installiere eine Proxy-App mit zufälliger Paket-ID und benutzerdefiniertem App-Label</string>
|
||||
<string name="settings_restore_app_title">Stelle die Magisk App wiederher</string>
|
||||
<string name="settings_restore_app_summary">Blende die App wieder ein und stelle die original APK wieder her</string>
|
||||
<string name="language">Sprache</string>
|
||||
<string name="system_default">(Systemstandard)</string>
|
||||
<string name="system_default">Systemstandard</string>
|
||||
<string name="settings_check_update_title">Auf Aktualisierungen prüfen</string>
|
||||
<string name="settings_check_update_summary">Regelmäßig im Hintergrund auf Aktualisierungen prüfen</string>
|
||||
<string name="settings_update_channel_title">Aktualisierungs-Kanal</string>
|
||||
@@ -166,12 +167,17 @@
|
||||
<string name="auto_response">Automatisch beantworten</string>
|
||||
<string name="request_timeout">Zeitlimit für Anfrage</string>
|
||||
<string name="superuser_notification">Superuser-Benachrichtigung</string>
|
||||
<string name="settings_su_reauth_title">Nach Aktualisierung erneut authentifizieren</string>
|
||||
<string name="settings_su_reauth_summary">Superuser-Zugriff nach App-Aktualisierung erneut abfragen</string>
|
||||
<string name="settings_su_reauth_title">Nach Aktualisierung erneut zertifizieren</string>
|
||||
<string name="settings_su_reauth_summary">Superuser-Berechtigungen nach App-Aktualisierung erneut authentifizieren</string>
|
||||
<string name="settings_su_tapjack_title">Tapjacking-Schutz aktivieren</string>
|
||||
<string name="settings_su_tapjack_summary">Das Dialogfeld der Superuser-Eingabeaufforderung reagiert nicht auf Eingaben, wenn es durch ein anderes Fenster oder Überlagerung verdeckt wird</string>
|
||||
<string name="settings_su_biometric_title">Biometrische Authentifizierung aktivieren</string>
|
||||
<string name="settings_su_biometric_summary">Biometrische Authentifizierung verwenden, um Superuser-Anfragen zu ermöglichen</string>
|
||||
<string name="no_biometric">Nicht unterstütztes Gerät oder keine biometrischen Einstellungen aktiviert</string>
|
||||
<string name="settings_su_biometric_summary">Biometrie verwenden, um Superuser-Anfragen zuzulassen</string>
|
||||
<string name="no_biometric">Gerät unterstützt keine biometrischen Daten oder ist nicht mit diesen konfiguriert</string>
|
||||
<string name="settings_customization">Personalisierung</string>
|
||||
<string name="setting_add_shortcut_summary">Hinzufügen einer hübschen Startbildschirm-Verknüpfung, falls der Name und das Symbol nach dem Ausblenden der App schwer zu erkennen sind</string>
|
||||
<string name="settings_doh_title">DNS über HTTPS</string>
|
||||
<string name="settings_doh_description">Umgehung des DNS-Poisoning in einigen Ländern</string>
|
||||
|
||||
<string name="multiuser_mode">Mehrbenutzermodus</string>
|
||||
<string name="settings_owner_only">Nur der Gerätebesitzer</string>
|
||||
@@ -194,10 +200,7 @@
|
||||
<string name="progress_channel">Fortschrittsmitteilungen</string>
|
||||
<string name="download_complete">Download abgeschlossen</string>
|
||||
<string name="download_file_error">Fehler beim herunterladen der Datei</string>
|
||||
<string name="download_open_parent">Im übergeordneten Ordner anzeigen</string>
|
||||
<string name="download_open_self">Datei anzeigen</string>
|
||||
<string name="magisk_update_title">Magisk Update verfügbar!</string>
|
||||
<string name="manager_update_title">Magisk Manager Update verfügbar!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Ja</string>
|
||||
@@ -211,8 +214,7 @@
|
||||
<string name="flashing">Flashen…</string>
|
||||
<string name="done">Fertig!</string>
|
||||
<string name="failure">Fehlgeschlagen</string>
|
||||
<string name="hide_manager_title">Magisk Manager ausblenden…</string>
|
||||
<string name="hide_manager_fail_toast">Magisk Manager ausblenden fehlgeschlagen.</string>
|
||||
<string name="hide_app_title">Verstecke die Magisk App…</string>
|
||||
<string name="open_link_failed_toast">Keine App zum öffnen des Links gefunden</string>
|
||||
<string name="complete_uninstall">Vollständig deinstallieren</string>
|
||||
<string name="restore_img">Image wiederherstellen</string>
|
||||
@@ -220,13 +222,17 @@
|
||||
<string name="restore_done">Wiederherstellen fertig!</string>
|
||||
<string name="restore_fail">Stock-Sicherung existiert nicht!</string>
|
||||
<string name="proprietary_title">Proprietären Code herunterladen</string>
|
||||
<string name="proprietary_notice">Magisk Manager ist FOSS und enthält nicht den proprietären SafetyNet-API-Code von Google.\n\nWollen Sie Magisk Manager erlauben, eine Erweiterung (enthält GoogleApiClient) für SafetyNet-Prüfungen herunterzuladen?</string>
|
||||
<string name="proprietary_notice">Magisk ist FOSS und enthält keinen Code für Google\'s proprietäre SafetyNet API.\n\nWillst du eine proprietäre Erweiterung für Safetynet Überprüfungen herunterladen?</string>
|
||||
<string name="setup_fail">Setup fehlgeschlagen</string>
|
||||
<string name="env_fix_title">Zusätzliche Einrichtung erforderlich</string>
|
||||
<string name="env_fix_msg">Ihr Gerät muss zusätzlich eingerichtet werden, damit Magisk ordnungsgemäß funktioniert. Es wird das Magisk Setup-Zip herunterladen, möchten Sie jetzt fortfahren?</string>
|
||||
<string name="env_fix_msg">Magisk braucht ein zusätzliches Setup, um auf deinem Gerät zu funktionieren. Willst du fortfahren und dein Gerät neustarten?</string>
|
||||
<string name="setup_msg">Laufzeitumgebung einrichten…</string>
|
||||
<string name="authenticate">Authentifizieren</string>
|
||||
<string name="unsupport_magisk_title">Nicht unterstützte Magisk-Version</string>
|
||||
<string name="unsupport_magisk_msg">Diese Version von Magisk Manager unterstützt keine Magisk-Version kleiner als %1$s.\n\nDie App verhält sich, als ob kein Magisk installiert wäre. Bitte Magisk so schnell wie möglich aktualisieren.</string>
|
||||
<string name="unsupport_magisk_msg">Diese Version der App unterstütz keine Magisk version niedriger, als %1$s.\n\nDie App wird sich verhalten, als wäre Magisk nicht installiert, bitte aktualisiere Magisk so schnell, wie möglich.</string>
|
||||
<string name="external_rw_permission_denied">Speichererlaubnis erteilen, um diese Funktion zu aktivieren</string>
|
||||
<string name="add_shortcut_title">Verknüpfung zum Startbildschirm hinzufügen</string>
|
||||
<string name="add_shortcut_msg">Nachdem du diese App versteckst, wird der Name und das Symbol eventuell schwer zu erkennen sein. Möchten sie zum Startbildschirm eine schöne Verknüpfung hinzufügen?</string>
|
||||
<string name="app_not_found">Keine App gefunden, die diese Aktion verarbeitet</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -1,123 +1,63 @@
|
||||
<resources>
|
||||
<!--Welcome Activity-->
|
||||
<string name="modules">Modules</string>
|
||||
|
||||
<!--Sections-->
|
||||
<string name="modules">Επεκτάσεις</string>
|
||||
<string name="superuser">Υπερχρήστης</string>
|
||||
<string name="logs">Αρχείο Καταγραφής</string>
|
||||
<string name="settings">Ρυθμίσεις</string>
|
||||
<string name="refresh">Ανανέωση τοπικών δεδομένων</string>
|
||||
<string name="install">Εγκατάσταση</string>
|
||||
<string name="section_home">Αρχική</string>
|
||||
<string name="section_theme">Θέματα</string>
|
||||
<string name="safetynet">SafetyNet</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
|
||||
<string name="invalid_update_channel">Λανθασμένο κανάλι ενημέρωσης</string>
|
||||
<string name="safetynet_api_error">Σφάλμα του SafetyNet API</string>
|
||||
<string name="safetynet_res_invalid">Η απόκριση είναι άκυρη</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="keep_force_encryption">Διατήρηση επιβεβλημένης κρυπτογράφησης</string>
|
||||
<string name="keep_dm_verity">Διατήρηση dm-verity</string>
|
||||
<string name="uninstall_magisk_title">Απεγκατάσταση Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Όλα τα modules θα απενεργοποιηθούν/αφαιρεθούν. Το root θα αφαιρεθεί και ενδέχεται να κρυπτογραφηθούν τα δεδομένα σας, εάν δεν είναι κρυπτογραφημένα</string>
|
||||
<string name="update">Ενημέρωση</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Δεν δόθηκαν πληροφορίες)</string>
|
||||
<string name="reboot_recovery">Επανεκκίνηση στο Recovery</string>
|
||||
<string name="reboot_bootloader">Επανεκκίνηση στο Bootloader</string>
|
||||
<string name="reboot_download">Επανεκκίνηση για λήψη</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Διαθέσιμη Ενημέρωση</string>
|
||||
<string name="home_installed_version">Εγκαταστάθηκε</string>
|
||||
<string name="sorting_order">Ταξινόμηση κατά</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveLog">"Αποθήκευση καταγραφής "</string>
|
||||
<string name="menuClearLog">Εκκαθάριση αρχείου καταγραφής τώρα</string>
|
||||
<string name="logs_cleared">Το αρχείο καταγραφής εκκαθαρίστηκε επιτυχώς</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<!--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>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="repo_install_title">Εγκατάσταση %1$s</string>
|
||||
<string name="repo_install_msg">Θέλετε να εγκαταστήσετε το %1$s τώρα;</string>
|
||||
<string name="download">Λήψη</string>
|
||||
<string name="reboot">Επανεκκίνηση</string>
|
||||
<string name="magisk_update_title">Νέα Ενημέρωση Magisk Διαθέσιμη!</string>
|
||||
<string name="release_notes">Σημειώσεις έκδοσης</string>
|
||||
<string name="repo_cache_cleared">Η Repo cache καθαρίστηκε</string>
|
||||
<string name="manager_update_title">Νέα Ενημέρωση Magisk Manager Διαθέσιμη!</string>
|
||||
<string name="home_notice_content">Κάντε λήψη του Magisk ΜΟΝΟ από την επίσημη σελίδα του GitHub. Τα αρχεία από άγνωστες πηγές μπορεί να είναι κακόβουλα!</string>
|
||||
<string name="home_support_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">Όλα τα modules θα απενεργοποιηθούν/αφαιρεθούν. Το root θα αφαιρεθεί και ενδέχεται να κρυπτογραφηθούν τα δεδομένα σας, εάν δεν είναι κρυπτογραφημένα!</string>
|
||||
<string name="home_check_safetynet">Έλεγχος SafetyNet</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Διατήρηση επιβεβλημένης κρυπτογράφησης</string>
|
||||
<string name="keep_dm_verity">Διατήρηση AVB 2.0/dm-verity</string>
|
||||
<string name="recovery_mode">Μέσω 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">Ενημερώσεις Magisk</string>
|
||||
<string name="flashing">Γίνεται flash</string>
|
||||
<string name="hide_manager_title">Κρύβοντας το Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">Η απόκρυψη του Magisk Manager απέτυχε…</string>
|
||||
<string name="download_zip_only">Λήψη Zip Μόνο</string>
|
||||
<string name="direct_install">Απευθείας Εγκατάσταση (Προτείνεται)</string>
|
||||
<string name="complete_uninstall">Πλήρης απεγκατάσταση</string>
|
||||
<string name="restore_done">Η ανάκτηση έγινε!</string>
|
||||
<string name="restore_fail">Δεν υπάρχει αντίγραφο ασφαλείας!</string>
|
||||
<string name="proprietary_title">Λήψη Ιδιόκτητου Κώδικα</string>
|
||||
<string name="proprietary_notice">Το Magisk Manager είναι FOSS οπότε δεν περιέχει της Google τον ιδιόκτητο κώδικα του SafetyNet API.\n\nΕπιτρέπετε στο Magisk Manager να κατεβάσει μια επέκταση (περιέχει το GoogleApiClient) για ελέγχους του SafetyNet?</string>
|
||||
<string name="download_file_error">Σφάλμα στη λήψη του αρχείου</string>
|
||||
<string name="install_inactive_slot">Εγκατάσταση στο ανενεργό Slot (Μετά από OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Η συσκευή σας θα αναγκαστεί να εκκινήσει στο τρέχον ανενεργό Slot μετά από επανεκκίνηση!\nΧρησιμοποιήστε αυτήν την επιλογή μόνο αφού ολοκληρωθεί το OTA.\nΣυνέχεια;</string>
|
||||
<string name="setup_title">Πρόσθετη εγκατάσταση</string>
|
||||
<string name="select_patch_file">Επέλεξε και κάνε Patch ένα αρχείο</string>
|
||||
<string name="patch_file_msg">Επέλεξε μία raw εικόνα (*.img) ή ένα ODIN tarfile (*.tar)</string>
|
||||
<string name="reboot_delay_toast">Επανεκκίνηση σε 5 δευτερόλεπτα…</string>
|
||||
<string name="flash_screen_title">Εγκατάσταση</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_clear_cache_title">Εκκαθάριση προσωρινής μνήμης αποθετηρίων</string>
|
||||
<string name="settings_clear_cache_summary">Καθαρίζει τις κρυφές πληροφορίες για απευθείας συνδεδεμένα αποθετήρια, αναγκάζει την εφαρμογή να κάνει ανανέωση σε απευθείας σύνδεση</string>
|
||||
<string name="settings_hide_manager_title">Απόκρυψη του Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Ανασυγκρότηση του Magisk Manager με τυχαίο όνομα πακέτου</string>
|
||||
<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">Custom</string>
|
||||
<string name="settings_update_custom_msg">Εισαγωγή ενός custom URL</string>
|
||||
<string name="settings_magiskhide_summary">Κρύβει το Magisk από διάφορες ανιχνεύσεις</string>
|
||||
<string name="settings_hosts_title">Systemless hosts</string>
|
||||
<string name="settings_hosts_summary">Υποστήριξη Systemless hosts για εφαρμογές Adblock</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="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">Μόνο ο ιδιοκτήτης έχει πρόσβαση root</string>
|
||||
<string name="owner_manage_summary">Μόνο ο ιδιοκτήτης μπορεί να διαχειριστεί την πρόσβαση root και να δεχτεί προτροπές αίτημάτων</string>
|
||||
<string name="user_indepenent_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">Όλες οι συνεδρίες root χρησιμοποιούν τον καθολικό χώρο oνομάτων προσάρτησης</string>
|
||||
<string name="requester_summary">Οι συνεδρίες root θα κληρονομούν το χώρο ονομάτων του αιτούντα τους</string>
|
||||
<string name="isolate_summary">Κάθε συνεδρία root θα έχει το δικό της απομονωμένο χώρο ονομάτων</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<!--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>
|
||||
<string name="su_warning">Δίνει πλήρη πρόσβαση στη συσκευή σας.\nΑρνηθείτε αν δεν είστε σίγουρος/η!</string>
|
||||
<string name="forever">Πάντα</string>
|
||||
<string name="forever">Μόνιμα</string>
|
||||
<string name="once">Μία φορά</string>
|
||||
<string name="tenmin">10 λεπτά</string>
|
||||
<string name="twentymin">20 λεπτά</string>
|
||||
@@ -136,6 +76,163 @@
|
||||
<string name="toast">Αναδυόμενο παράθυρο</string>
|
||||
<string name="none">Κανένα</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="superuser_toggle_notification">Ειδοποιήσεις</string>
|
||||
<string name="superuser_toggle_revoke">Ανακάλεσε</string>
|
||||
<string name="superuser_policy_none">Καμία εφαρμογή δεν έχει ζητήσει άδεια υπερχρήστη.</string>
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Δεν υπάρχουν αρχεία καταγραφής, δοκιμάστε να χρησιμοποιήσετε περισσότερο τις εφαρμογές με δυνατότητα SU</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>
|
||||
</resources>
|
||||
|
||||
<!--SafetyNet-->
|
||||
<string name="safetynet_api_error">Σφάλμα του SafetyNet API</string>
|
||||
<string name="safetynet_res_invalid">Η απόκριση είναι άκυρη</string>
|
||||
<string name="safetynet_attest_success">Η πιστοποίηση ήταν επιτυχής!</string>
|
||||
<string name="safetynet_attest_failure">Η πιστοποίηση απέτυχε!</string>
|
||||
<string name="safetynet_attest_loading">Μισό λεπτό…</string>
|
||||
<string name="safetynet_attest_restart">Προσπάθησε ξανά</string>
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Εμφάνιση εφαρμογών συστήματος</string>
|
||||
<string name="show_os_app">Εμφάνιση εφαρμογών OS</string>
|
||||
<string name="hide_filter_hint">Φιλτράρισμα</string>
|
||||
<string name="hide_scroll_up">Μετακίνηση</string>
|
||||
<string name="hide_filters">Φίλτρα</string>
|
||||
<string name="hide_search">Αναζήτηση</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Δεν δόθηκαν πληροφορίες)</string>
|
||||
<string name="reboot_userspace">Soft επανεκκίνηση</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="module_version_author">%1$s από %2$s</string>
|
||||
<string name="module_section_pending">Ενημερώσεις</string>
|
||||
<string name="module_section_pending_action">Ενημέρωση όλων</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="module_installed">@string/home_installed_version</string>
|
||||
<string name="module_section_online">Online</string>
|
||||
<string name="sorting_order">Ταξινόμηση κατά</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Λειτουργία θέματος</string>
|
||||
<string name="settings_dark_mode_message">Επιλέξτε τη λειτουργία που ταιριάζει καλύτερα στο στυλ σας!</string>
|
||||
<string name="settings_dark_mode_light">Πάντα Light</string>
|
||||
<string name="settings_dark_mode_system">Ακολούθησε το σύστημα</string>
|
||||
<string name="settings_dark_mode_dark">Πάντα Dark</string>
|
||||
<string name="settings_download_path_title">Διαδρομή λήψης</string>
|
||||
<string name="settings_download_path_message">Τα αρχεία θα αποθηκευτούν στο %1$s</string>
|
||||
<string name="settings_clear_cache_title">Εκκαθάριση προσωρινής μνήμης αποθετηρίων</string>
|
||||
<string name="settings_clear_cache_summary">Καθαρίζει τις κρυφές πληροφορίες για απευθείας συνδεδεμένα αποθετήρια, αναγκάζει την εφαρμογή να κάνει ανανέωση σε απευθείας σύνδεση</string>
|
||||
<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="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">Εισαγωγή ενός custom URL</string>
|
||||
<string name="settings_magiskhide_summary">Κρύβει το Magisk από διάφορες ανιχνεύσεις</string>
|
||||
<string name="settings_hosts_title">Systemless hosts</string>
|
||||
<string name="settings_hosts_summary">Υποστήριξη Systemless hosts για εφαρμογές Adblock</string>
|
||||
<string name="settings_hosts_toast">Προσθήκη αρθρώματος systemless hosts</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">Το παράθυρο διαλόγου προτροπής του υπερχρήστη δεν αποκρίνεται στην είσοδο ενώ κρύβεται από οποιοδήποτε άλλο παράθυρο</string>
|
||||
<string name="settings_su_biometric_title">Ενεργοποίηση βιομετρικού ελέγχου ταυτότητας</string>
|
||||
<string name="settings_su_biometric_summary">Χρησιμοποιήστε βιομετρικό έλεγχο ταυτότητας για να επιτρέψετε αιτήματα υπερχρηστών</string>
|
||||
<string name="no_biometric">Δεν υποστηρίζεται η συσκευή ή δεν υπάρχει καμία βιομετρική ρύθμιση</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 poisoning για μερικά έθνη</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">Μόνο ο ιδιοκτήτης έχει πρόσβαση root</string>
|
||||
<string name="owner_manage_summary">Μόνο ο ιδιοκτήτης μπορεί να διαχειριστεί την πρόσβαση root και να δεχτεί προτροπές αίτημάτων</string>
|
||||
<string name="user_indepenent_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">Όλες οι συνεδρίες root χρησιμοποιούν τον καθολικό χώρο oνομάτων προσάρτησης</string>
|
||||
<string name="requester_summary">Οι συνεδρίες root θα κληρονομούν το χώρο ονομάτων του αιτούντα τους</string>
|
||||
<string name="isolate_summary">Κάθε συνεδρία root θα έχει το δικό της απομονωμένο χώρο ονομάτων</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Ενημερώσεις Magisk</string>
|
||||
<string name="progress_channel">Ειδοποιήσεις προόδου</string>
|
||||
<string name="download_complete">Η λήψη ολοκληρώθηκε</string>
|
||||
<string name="download_file_error">Σφάλμα στη λήψη του αρχείου</string>
|
||||
<string name="magisk_update_title">Νέα Ενημέρωση Magisk Διαθέσιμη!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Ναι</string>
|
||||
<string name="no">Όχι</string>
|
||||
<string name="repo_install_title">Εγκατάσταση %1$s</string>
|
||||
<string name="repo_install_msg">Θέλετε να εγκαταστήσετε το %1$s τώρα;</string>
|
||||
<string name="download">Λήψη</string>
|
||||
<string name="reboot">Επανεκκίνηση</string>
|
||||
<string name="release_notes">Σημειώσεις έκδοσης</string>
|
||||
<string name="repo_cache_cleared">Πραγματοποιήθηκε εκκαθάριση της προσωρινής μνήμης του Repository</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">Επαναφορά Images</string>
|
||||
<string name="restore_img_msg">Επαναφορά…</string>
|
||||
<string name="restore_done">Η επαναφορά πραγματοποιήθηκε</string>
|
||||
<string name="restore_fail">Δεν υπάρχει αντίγραφο ασφαλείας!</string>
|
||||
<string name="proprietary_title">Λήψη Ιδιόκτητου Κώδικα</string>
|
||||
<string name="proprietary_notice">Το Magisk είναι FOSS και δεν περιλαμβάνει κώδικα για το ιδιόκτητο SafetyNet API της Google.\n\nΘέλετε να κατεβάσετε μια ιδιόκτητη επέκταση για ελέγχους SafetyNet;</string>
|
||||
<string name="setup_fail">Η εγκατάσταση απέτυχε</string>
|
||||
<string name="env_fix_title">Απαιτείται πρόσθετη εγκατάσταση</string>
|
||||
<string name="env_fix_msg">Η συσκευή σας χρειάζεται πρόσθετη ρύθμιση για να λειτουργεί σωστά το Magisk. Θέλετε να συνεχίσετε και να επανεκκινήσετε;</string>
|
||||
<string name="setup_msg">Τρέχει η εγκατάσταση περιβάλλοντος…</string>
|
||||
<string name="authenticate">Αυθεντικοποίηση</string>
|
||||
<string name="unsupport_magisk_title">Μη υποστηριζόμενη έκδοση Magisk</string>
|
||||
<string name="unsupport_magisk_msg">Αυτή η έκδοση της εφαρμογής δεν υποστηρίζει έκδοση Magisk χαμηλότερη από %1$s.\n\nΗ εφαρμογή θα συμπεριφέρεται σαν να μην έχει εγκατασταθεί το Magisk, αναβαθμίστε το Magisk το συντομότερο δυνατό.</string>
|
||||
<string name="external_rw_permission_denied">Παραχωρήστε άδεια αποθήκευσης για να ενεργοποιήσετε αυτήν τη λειτουργία</string>
|
||||
<string name="add_shortcut_title">Προσθέστε συντόμευση στην αρχική οθόνη</string>
|
||||
<string name="add_shortcut_msg">Μετά την απόκρυψη αυτής της εφαρμογής, το όνομα και το εικονίδιο της ενδέχεται να είναι δύσκολο να αναγνωριστούν. Θέλετε να προσθέσετε μια όμορφη συντόμευση στην αρχική οθόνη;</string>
|
||||
|
||||
<string name="app_not_found">Δεν βρέθηκε εφαρμογή που να χειρίζεται αυτήν την ενέργεια</string>
|
||||
|
||||
</resources>
|
@@ -14,20 +14,17 @@
|
||||
<!--Home-->
|
||||
<string name="no_connection">Conexión no disponible</string>
|
||||
<string name="app_changelog">Registro de cambios</string>
|
||||
<string name="manager">Manager</string>
|
||||
<string name="loading">Cargando…</string>
|
||||
<string name="update">Actualizar</string>
|
||||
<string name="not_available">No disponible</string>
|
||||
<string name="hide">Ocultar</string>
|
||||
<string name="status">Estado</string>
|
||||
<string name="home_package">Paquete</string>
|
||||
<string name="home_app_title">App</string>
|
||||
|
||||
<string name="home_notice_content">Siempre asegúrate de que estás usando el software de código abierto Magisk Manager. Un Manager de fuente desconocida puede realizar acciones maliciosas.</string>
|
||||
<string name="home_notice_content">Descarga Magisk SÓLO de la página de GitHub oficial. Archivos de fuentes desconocidas pueden resultar maliciosos!</string>
|
||||
<string name="home_support_title">Apóyenos</string>
|
||||
<string name="home_item_source">Código fuente</string>
|
||||
<string name="home_support_content">Magisk es, y siempre será gratuito y de código abierto. Sin embargo, puede apoyarnos enviando una pequeña donación.</string>
|
||||
<string name="home_status_normal">Normal</string>
|
||||
<string name="home_status_stub">Stub</string>
|
||||
<string name="home_installed_version">Instalado</string>
|
||||
<string name="home_latest_version">Última</string>
|
||||
<string name="invalid_update_channel">Canal de actualización inválido</string>
|
||||
@@ -44,7 +41,6 @@
|
||||
<string name="install_next">Siguiente</string>
|
||||
<string name="install_start">Comencemos</string>
|
||||
<string name="manager_download_install">Pulse para descargar e instalar</string>
|
||||
<string name="download_zip_only">Descargar sólo el archivo ZIP</string>
|
||||
<string name="direct_install">Instalación directa (recomendado)</string>
|
||||
<string name="install_inactive_slot">Instalar en ranura inactiva (después de OTA)</string>
|
||||
<string name="install_inactive_slot_msg">¡Su dispositivo será forzado a arrancar en la ranura inactiva actual después de un reinicio!\nSólo use esta opción después de que OTA haya terminado.\n¿Continuar?</string>
|
||||
@@ -56,6 +52,7 @@
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Petición de superusuario</string>
|
||||
<string name="touch_filtered_warning">Como otra app está escondiendo la solicitud de superusuario, Magisk no puede verificar tu respuesta</string>
|
||||
<string name="deny">Denegar</string>
|
||||
<string name="prompt">Preguntar</string>
|
||||
<string name="grant">Permitir</string>
|
||||
@@ -101,7 +98,8 @@
|
||||
<string name="safetynet_attest_restart">Inténtelo de nuevo</string>
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">Mostrar sistema</string>
|
||||
<string name="show_system_app">Mostrar apps del sistema</string>
|
||||
<string name="show_os_app">Mostrar apps del OS</string>
|
||||
<string name="hide_filter_hint">Filtrar por nombre</string>
|
||||
<string name="hide_scroll_up">Subir</string>
|
||||
<string name="hide_filters">Filtros</string>
|
||||
@@ -109,6 +107,7 @@
|
||||
|
||||
<!--Module -->
|
||||
<string name="no_info_provided">(No hay información)</string>
|
||||
<string name="reboot_userspace">Reinicio suave</string>
|
||||
<string name="reboot_recovery">Reiniciar en modo recovery</string>
|
||||
<string name="reboot_bootloader">Reiniciar en modo bootloader</string>
|
||||
<string name="reboot_download">Reiniciar en modo download</string>
|
||||
@@ -134,10 +133,10 @@
|
||||
<string name="settings_download_path_message">Los archivos se guardarán en %1$s</string>
|
||||
<string name="settings_clear_cache_title">Limpiar caché del repositorio</string>
|
||||
<string name="settings_clear_cache_summary">Limpiar la información en caché para los repositorios en línea, fuerza a la aplicación a actualizar en línea</string>
|
||||
<string name="settings_hide_manager_title">Ocultar Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Re-empaquetar Magisk Manager con un nombre de paquete al azar</string>
|
||||
<string name="settings_restore_manager_title">Restaurar Magisk Manager</string>
|
||||
<string name="settings_restore_manager_summary">Restaura Magisk Manager con el paquete original</string>
|
||||
<string name="settings_hide_app_title">Esconder la app de Magisk</string>
|
||||
<string name="settings_hide_app_summary">Instalar una nueva app proxy con una ID de paquete aleatoria y una etiqueta personalizada</string>
|
||||
<string name="settings_restore_app_title">Restaurar la app de Magisk</string>
|
||||
<string name="settings_restore_app_summary">Descubrir la app y restaurarla al APK original</string>
|
||||
<string name="language">Idioma</string>
|
||||
<string name="system_default">(Idioma del sistema)</string>
|
||||
<string name="settings_check_update_title">Comprobar Actualizaciones</string>
|
||||
@@ -201,10 +200,7 @@
|
||||
<string name="progress_channel">Notificaciones de progreso</string>
|
||||
<string name="download_complete">Descarga Completa</string>
|
||||
<string name="download_file_error">Error descargando archivo</string>
|
||||
<string name="download_open_parent">Mostrar en carpeta principal</string>
|
||||
<string name="download_open_self">Mostrar archivo</string>
|
||||
<string name="magisk_update_title">¡Actualización de Magisk disponible!</string>
|
||||
<string name="manager_update_title">¡Actualización de Magisk Manager disponible!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Sí</string>
|
||||
@@ -218,8 +214,7 @@
|
||||
<string name="flashing">Flasheando…</string>
|
||||
<string name="done">¡Hecho!</string>
|
||||
<string name="failure">Ha fallado</string>
|
||||
<string name="hide_manager_title">Ocultando Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">La ocultación de Magisk Manager ha fallado…</string>
|
||||
<string name="hide_app_title">Escondiendo la app de Magisk…</string>
|
||||
<string name="open_link_failed_toast">No se encontró ninguna aplicación para abrir el enlace…</string>
|
||||
<string name="complete_uninstall">Desinstalación completa</string>
|
||||
<string name="restore_img">Restaurar imágenes</string>
|
||||
@@ -227,17 +222,17 @@
|
||||
<string name="restore_done">¡Restauración Terminada!</string>
|
||||
<string name="restore_fail">¡El respaldo de la imagen boot Stock no existe!</string>
|
||||
<string name="proprietary_title">Descargar código propietario</string>
|
||||
<string name="proprietary_notice">Magisk Manager es un software libre por lo que no contiene el código API de SafetyNet (código propietario de Google).\n\n ¿Desea permitir que Magisk Manager descargue una extensión (que contiene GoogleApiClient) para comprobar el estado de SafetyNet?</string>
|
||||
<string name="proprietary_notice">Magisk es FOSS y no incluye código de la API de SafetyNet, propiedad de Google.\n\n¿Quieres descargar una extensión propietaria para las comprobaciones de SafetyNet?</string>
|
||||
<string name="setup_fail">Instalación fallida</string>
|
||||
<string name="env_fix_title">Se requiere una instalación adicional</string>
|
||||
<string name="env_fix_msg">Su dispositivo requiere una instalación adicional para que Magisk funcione correctamente. Se descargará el zip de instalación de Magisk, ¿desea continuar ahora?</string>
|
||||
<string name="env_fix_msg">Tu dispositivo necesita configuración adicional para el correcto funcionamiento de Magisk. ¿Quieres proceder y reiniciar?</string>
|
||||
<string name="setup_msg">Ejecutando configuración de entorno…</string>
|
||||
<string name="authenticate">Autenticar</string>
|
||||
<string name="unsupport_magisk_title">Versión de Magisk no soportada</string>
|
||||
<string name="unsupport_magisk_msg">Esta versión de Magisk Manager no admite versiones de Magisk inferiores a %1$s.\n\nLa aplicación se comportará como si no estuviera instalado Magisk, actualícelo tan pronto como sea posible.</string>
|
||||
<string name="unsupport_magisk_msg">Esta versión de la app no soporta versiones de Magisk inferiores a la %1$s.\n\nLa aplicación se comportará como si no tuvieses Magisk instalado, por favor actualiza Magisk tan pronto como sea posible.</string>
|
||||
<string name="external_rw_permission_denied">Conceder permiso de almacenamiento para habilitar esta funcionalidad</string>
|
||||
<string name="add_shortcut_title">Añadir un atajo a la pantalla de inicio</string>
|
||||
<string name="add_shortcut_msg">Después de esconder el Magisk Manager, su nombre e icono podría ser difícil de reconocer. ¿Quieres añadir un bonito atajo a la pantalla de inicio?</string>
|
||||
<string name="add_shortcut_msg">Tras esconder la app, su nombre e icono pueden resultar difíciles de reconocer. ¿Quieres añadir un acceso directo más bonito a la pantalla principal?</string>
|
||||
<string name="app_not_found">No se ha encontrado ninguna app para manejar esta acción</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -14,20 +14,15 @@
|
||||
<!--Home-->
|
||||
<string name="no_connection">Ühendus puudub</string>
|
||||
<string name="app_changelog">Muudatuste logi</string>
|
||||
<string name="manager">Manager</string>
|
||||
<string name="loading">Laadimine…</string>
|
||||
<string name="update">Uuenda</string>
|
||||
<string name="not_available">puudub</string>
|
||||
<string name="hide">Peida</string>
|
||||
<string name="status">Olek</string>
|
||||
<string name="home_package">Pakett</string>
|
||||
|
||||
<string name="home_notice_content">Veendu alati, et kasutad avatud lähtekoodiga Magisk Manageri. Tundmatust allikast pärinev Manager võib teha pahatahtlikke toiminguid.</string>
|
||||
<string name="home_support_title">Toeta meid</string>
|
||||
<string name="home_item_source">Lähtekood</string>
|
||||
<string name="home_support_content">Magisk on ja jääb alati tasuta ning avatud lähtekoodiga kättesaadavaks. Siiski, sa võid meile väikese annetuse näol toetust üles näidata.</string>
|
||||
<string name="home_status_normal">Tavaline</string>
|
||||
<string name="home_status_stub">Makett</string>
|
||||
<string name="home_installed_version">Installitud</string>
|
||||
<string name="home_latest_version">Viimatine</string>
|
||||
<string name="invalid_update_channel">Sobimatu uuenduste kanal</string>
|
||||
@@ -44,7 +39,6 @@
|
||||
<string name="install_next">Edasi</string>
|
||||
<string name="install_start">Läksime</string>
|
||||
<string name="manager_download_install">Vajuta allalaadimiseks ja installimiseks.</string>
|
||||
<string name="download_zip_only">Laadi ainult ZIP alla</string>
|
||||
<string name="direct_install">Otsene install (soovitatud)</string>
|
||||
<string name="install_inactive_slot">Installi ebaaktiivsesse lahtrisse (pärast üle-õhu uuendust)</string>
|
||||
<string name="install_inactive_slot_msg">Sinu seade SUNNITAKSE peale taaskäivitust käivituma praegusesse ebaaktiivsesse lahtrisse!\nKasuta seda valikut vaid peale üle-õhu uuenduse teostamist.\nJätkad?</string>
|
||||
@@ -134,10 +128,6 @@
|
||||
<string name="settings_download_path_message">Failid salvestatakse kausta %1$s</string>
|
||||
<string name="settings_clear_cache_title">Tühjenda hoidla vahemälu</string>
|
||||
<string name="settings_clear_cache_summary">Tühjenda vahemälus olev teave võrgus olevate hoidlate kohta. See sunnib rakendust võrgust värskendama</string>
|
||||
<string name="settings_hide_manager_title">Peida Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Paki Magisk Manager juhusliku paketi- ja rakenduse nimega ümber</string>
|
||||
<string name="settings_restore_manager_title">Taasta Magisk Manager</string>
|
||||
<string name="settings_restore_manager_summary">Taasta Magisk Manageri originaalpakett</string>
|
||||
<string name="language">Keel</string>
|
||||
<string name="system_default">(Süsteemi vaikesäte)</string>
|
||||
<string name="settings_check_update_title">Kontrolli uuendusi</string>
|
||||
@@ -199,10 +189,7 @@
|
||||
<string name="progress_channel">Edenemise teated</string>
|
||||
<string name="download_complete">Allalaadimine valmis</string>
|
||||
<string name="download_file_error">Faili allalaadimisel esines viga</string>
|
||||
<string name="download_open_parent">Kuva ülemkaustas</string>
|
||||
<string name="download_open_self">Kuva fail</string>
|
||||
<string name="magisk_update_title">Magiski uuendus on saadaval!</string>
|
||||
<string name="manager_update_title">Magisk Manageri uuendus on saadaval!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Jah</string>
|
||||
@@ -216,8 +203,6 @@
|
||||
<string name="flashing">Välgutamine…</string>
|
||||
<string name="done">Valmis!</string>
|
||||
<string name="failure">Ebaõnnestus</string>
|
||||
<string name="hide_manager_title">Magisk Manageri peitmine…</string>
|
||||
<string name="hide_manager_fail_toast">Magisk Manageri peitmine ebaõnnestus.</string>
|
||||
<string name="open_link_failed_toast">Lingi avamiseks sobivat rakendust ei leitud.</string>
|
||||
<string name="complete_uninstall">Täielik eemaldus</string>
|
||||
<string name="restore_img">Taasta tõmmised</string>
|
||||
@@ -225,17 +210,13 @@
|
||||
<string name="restore_done">Taastamine valmis!</string>
|
||||
<string name="restore_fail">Originaalne varundus puudub!</string>
|
||||
<string name="proprietary_title">Laadi alla suletud koodi</string>
|
||||
<string name="proprietary_notice">Magisk Manager on vaba ja avatud lähtekoodiga ning ei sisalda Google\'i suletud SafetyNeti API koodi.\n\nKas lubad Magisk Manageril SafetyNeti kontrollide jaoks laadida alla laiendus (sisaldab GoogleApiClienti)?</string>
|
||||
<string name="setup_fail">Seadistus ebaõnnnestus</string>
|
||||
<string name="env_fix_title">Vajab lisaseadistust</string>
|
||||
<string name="env_fix_msg">Sinu seade vajab Magiski korralikuks toimimiseks lisaseadistust. Laadime alla Magiski seadistus-ZIPi, kas soovid kohe jätkata?</string>
|
||||
<string name="setup_msg">Käivitan keskkonnaseadistust…</string>
|
||||
<string name="authenticate">Autendi</string>
|
||||
<string name="unsupport_magisk_title">Mittetoetatud Magiski versioon</string>
|
||||
<string name="unsupport_magisk_msg">See Magisk Manageri versioon ei toeta Magiski versiooni, mis on vanem kui %1$s.\n\nRakendus käitub nii, nagu Magisk ei oleks paigaldatud, palun uuenda Magisk nii ruttu kui võimalik.</string>
|
||||
<string name="external_rw_permission_denied">Selle funktsionaalsuse lubamiseks anna mäluruumi haldamise luba</string>
|
||||
<string name="add_shortcut_title">Lisa avakuvale otsetee</string>
|
||||
<string name="add_shortcut_msg">Pärast Magisk Manageri peitmist võib selle nimi ja ikoon muutuda raskelt tuvastatavaks. Kas soovid avakuvale ilusa otsetee lisada?</string>
|
||||
<string name="app_not_found">Selle tegevuse teostamiseks ei leitud ühtegi rakendust</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -14,20 +14,15 @@
|
||||
<!--Home-->
|
||||
<string name="no_connection">هیچ اتصالی وجود ندارد</string>
|
||||
<string name="app_changelog">تغییرات</string>
|
||||
<string name="manager">مدیریت</string>
|
||||
<string name="loading">در حال بارگذاری…</string>
|
||||
<string name="update">بروزرسانی کردن</string>
|
||||
<string name="not_available">غیر/قابل دسترسی</string>
|
||||
<string name="hide">پنهان کردن</string>
|
||||
<string name="status">وضعیت</string>
|
||||
<string name="home_package">پکیج</string>
|
||||
|
||||
<string name="home_notice_content">همیشه اطمینان حاصل کنید که از نسخه متن باز Magisk Manager استفاده می کنید. توجه داشته باشد نصب این نرم افزار از منبع ناشناخته میتواند به دستگاه شما آسیب برساند و اطلاعات شما را در معرض خطر قرار دهد.</string>
|
||||
<string name="home_support_title">حمایت ما</string>
|
||||
<string name="home_item_source">منبع</string>
|
||||
<string name="home_support_content">این برنامه (Magisk) رایگان و متن باز است و همیشه خواهد ماند. اگرچه شما میتواند با دونیت خود نشان دهد که به ما اهمیت می دهید.</string>
|
||||
<string name="home_status_normal">وضعیت عادی</string>
|
||||
<string name="home_status_stub">وضعیت Stub</string>
|
||||
<string name="home_installed_version">نصب شده</string>
|
||||
<string name="home_latest_version">آخرین نسخه</string>
|
||||
<string name="invalid_update_channel">کانال بروزرسانی نامعتبر است</string>
|
||||
@@ -44,7 +39,6 @@
|
||||
<string name="install_next">بعدی</string>
|
||||
<string name="install_start">بزن بریم</string>
|
||||
<string name="manager_download_install">برای دانلود و نصب فشار دهید</string>
|
||||
<string name="download_zip_only">فقط zip را دانلود شود</string>
|
||||
<string name="direct_install">نصب مستقیم (توصیه می شود)</string>
|
||||
<string name="install_inactive_slot">نصب در حافظه غیر فعال (بعد OTA)</string>
|
||||
<string name="install_inactive_slot_msg">بعد از راه اندازی مجدد دستگاه شما مجبور به راه اندازی در حافظه غیرفعال فعلی می شود! \nفقط پس از انجام OTA از این گزینه استفاده کنید. \nادامه می دهید؟</string>
|
||||
@@ -131,10 +125,6 @@
|
||||
<string name="settings_download_path_message">فایل ها در %1$s ذخیره خواهند شد.</string>
|
||||
<string name="settings_clear_cache_title">پاک کردن حافظه پنهان</string>
|
||||
<string name="settings_clear_cache_summary">پاک کردن اطلاعات ذخیره شده برای ریپوسیتوری های آنلاین. این کار برنامه را مجبور به دریافت دوباره اطللاعات هنگام آنلاین شدن میکند</string>
|
||||
<string name="settings_hide_manager_title">پنهان کردن Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">پکیج بندی مجدد Magisk Manager با نام تصادفی برای پکیج ها و برنامه ها</string>
|
||||
<string name="settings_restore_manager_title">بازگرداندن Magisk Manager</string>
|
||||
<string name="settings_restore_manager_summary">بازگرداندن Magisk Manager نام اصلی پکیج ها و برنامه ها</string>
|
||||
<string name="language">زبان</string>
|
||||
<string name="system_default">(پیش فرض سیستم)</string>
|
||||
<string name="settings_check_update_title">چک کردن بروز رسانی ها</string>
|
||||
@@ -194,10 +184,7 @@
|
||||
<string name="progress_channel">Progress اعلان</string>
|
||||
<string name="download_complete">دانلود کامل شد</string>
|
||||
<string name="download_file_error">خطا در دانلود فایل</string>
|
||||
<string name="download_open_parent">نمایش پوشه</string>
|
||||
<string name="download_open_self">نمایش فایل</string>
|
||||
<string name="magisk_update_title">بروزرسانی Magisk در دسترس است!</string>
|
||||
<string name="manager_update_title">بروزرسانی Magisk Manager در دسترس است!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">بله</string>
|
||||
@@ -211,8 +198,6 @@
|
||||
<string name="flashing">ر حال فلش کردن…</string>
|
||||
<string name="done">تمام!</string>
|
||||
<string name="failure">ناموفق</string>
|
||||
<string name="hide_manager_title">قایم شدن Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">پنهان کردن Magisk Manager ناموفق بود.</string>
|
||||
<string name="open_link_failed_toast">هیچ برنامه ای برای باز کردن لینک یافت نشد</string>
|
||||
<string name="complete_uninstall">کامل کردن حذف</string>
|
||||
<string name="restore_img">بازیابی تصاویر</string>
|
||||
@@ -220,16 +205,12 @@
|
||||
<string name="restore_done">بازیابی انجام شد!</string>
|
||||
<string name="restore_fail">نسخه پشتیبان استک موجود نیست!</string>
|
||||
<string name="proprietary_title">دانلود کد اختصاصی</string>
|
||||
<string name="proprietary_notice">برنامه Magisk Manager رایگان و متن باز است و حاوی کد اختصاصی Google\'s SafetyNet API نیست.\n\nآیا به Magisk Manager اجازه برای بررسی اکستنشن SafetyNet (شامل GoogleApiClient) می دهید؟</string>
|
||||
<string name="setup_fail">نصب انجام نشد</string>
|
||||
<string name="env_fix_title">به تنظیمات اضافی نیاز دارد</string>
|
||||
<string name="env_fix_msg">دستگاه شما برای کار صحیح Magisk به تنظیمات اضافی نیاز دارد. این کار نصب Magisk zip را بارگیری می کند. میخوای الان ادامه بدی؟</string>
|
||||
<string name="setup_msg">راه اندازی محیط نصب…</string>
|
||||
<string name="authenticate">اعتبار دادن</string>
|
||||
<string name="unsupport_magisk_title">نسخه پشتیبانی نشده Magisk</string>
|
||||
<string name="unsupport_magisk_msg">این نسخه از Magisk Manager از نسخه Magisk کمتر از پشتیبانی نمی کند %1$s.\n\nاین برنامه به گونه ای رفتار می کند که گویی هیچ Magisk نصب نشده است ، لطفاً در اسرع وقت Magisk را ارتقا دهید.</string>
|
||||
<string name="external_rw_permission_denied">برای فعال کردن این قابلیت ، اجازه دسترسی به حافظه بدهید</string>
|
||||
<string name="add_shortcut_title">اضافه کردن میانبر را به صفحه</string>
|
||||
<string name="add_shortcut_msg">پس از پنهان کردن ManagerMagisk ، ممکن است تشخیص نام و نماد آن دشوار باشد. آیا می خواهید یک میانبر زیبا به صفحه اصلی اضافه کنید؟</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -14,20 +14,16 @@
|
||||
<!--Home-->
|
||||
<string name="no_connection">Aucune connexion disponible</string>
|
||||
<string name="app_changelog">Journal des modifications</string>
|
||||
<string name="manager">Manager</string>
|
||||
<string name="loading">Chargement…</string>
|
||||
<string name="update">Mettre à jour</string>
|
||||
<string name="not_available">non disponible</string>
|
||||
<string name="hide">Masquer</string>
|
||||
<string name="status">État</string>
|
||||
<string name="home_package">Paquet</string>
|
||||
|
||||
<string name="home_notice_content">Assurez‑vous de toujours utiliser la version libre de Magisk Manager. Une version provenant d’une source inconnue peut effectuer des actions malveillantes.</string>
|
||||
<string name="home_notice_content">Téléchargez Magisk SEULEMENT depuis la page Github officielle. Les fichiers provenant de sources inconnues peuvent être malveillants !</string>
|
||||
<string name="home_support_title">Soutenez‑nous</string>
|
||||
<string name="home_item_source">Sources</string>
|
||||
<string name="home_support_content">Magisk est, et sera toujours, libre et open source. Vous pouvez cependant nous témoigner de votre soutien en envoyant un petit don.</string>
|
||||
<string name="home_status_normal">Normal</string>
|
||||
<string name="home_status_stub">Stub</string>
|
||||
<string name="home_installed_version">Installé</string>
|
||||
<string name="home_latest_version">Dernière</string>
|
||||
<string name="invalid_update_channel">Canal de mise à jour invalide</string>
|
||||
@@ -44,7 +40,6 @@
|
||||
<string name="install_next">Suivant</string>
|
||||
<string name="install_start">C’est parti</string>
|
||||
<string name="manager_download_install">Appuyez pour le télécharger et l’installer</string>
|
||||
<string name="download_zip_only">Télécharger le ZIP sans l’installer</string>
|
||||
<string name="direct_install">Installation directe (recommandée)</string>
|
||||
<string name="install_inactive_slot">Installer dans l’espace inactif (après mise à jour OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Votre appareil sera obligatoirement réamorcé à partir de l’espace (slot) actuellement inactif après son redémarrage !\nN’utilisez uniquement cette option qu’après que la mise à jour OTA a été effectuée.\nVoulez‑vous continuer ?</string>
|
||||
@@ -137,10 +132,10 @@
|
||||
<string name="settings_download_path_message">Les fichiers seront enregistrés sous %1$s</string>
|
||||
<string name="settings_clear_cache_title">Vider le cache des dépôts</string>
|
||||
<string name="settings_clear_cache_summary">Effacer les informations en cache concerant les dépôts en ligne. Ceci force l’application à télécharger des informations à jour.</string>
|
||||
<string name="settings_hide_manager_title">Masquer Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Réempaqueter Magisk Manager avec des noms de paquet et d’application aléatoires.</string>
|
||||
<string name="settings_restore_manager_title">Restaurer Magisk Manager</string>
|
||||
<string name="settings_restore_manager_summary">Restaurer Magisk Manager avec ses noms de paquet et d’application d’origine.</string>
|
||||
<string name="settings_hide_app_title">Masquer l’application Magisk</string>
|
||||
<string name="settings_hide_app_summary">Installer une application intermédiaire avec un ID de package aléatoire et un nom personnalisé</string>
|
||||
<string name="settings_restore_app_title">Restorer l’application Magisk</string>
|
||||
<string name="settings_restore_app_summary">Démasquer l’application et la restaurer à son APK d’origine</string>
|
||||
<string name="language">Langue</string>
|
||||
<string name="system_default">(langue par défaut du système)</string>
|
||||
<string name="settings_check_update_title">Vérifier les mises à jour</string>
|
||||
@@ -204,10 +199,7 @@
|
||||
<string name="progress_channel">Notifications de progression</string>
|
||||
<string name="download_complete">Téléchargement terminé</string>
|
||||
<string name="download_file_error">Erreur lors du téléchargement du fichier</string>
|
||||
<string name="download_open_parent">Afficher le dossier parent</string>
|
||||
<string name="download_open_self">Afficher le fichier</string>
|
||||
<string name="magisk_update_title">Une mise à jour de Magisk est disponible !</string>
|
||||
<string name="manager_update_title">Une mise à jour de Magisk Manager est disponible !</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Oui</string>
|
||||
@@ -221,27 +213,25 @@
|
||||
<string name="flashing">Écriture dans la mémoire Flash…</string>
|
||||
<string name="done">Terminé !</string>
|
||||
<string name="failure">Erreur</string>
|
||||
<string name="hide_manager_title">Masquage de Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">Le masquage de Magisk Manager a échoué.</string>
|
||||
<string name="hide_app_title">Masquage de l’application Magisk…</string>
|
||||
<string name="open_link_failed_toast">Aucune application permettant d’ouvrir le lien n’a été trouvée</string>
|
||||
<string name="restore_manager_fail_toast">Échec de la restauration de Magisk Manager</string>
|
||||
<string name="complete_uninstall">Désinstallation complète</string>
|
||||
<string name="restore_img">Restauration des images</string>
|
||||
<string name="restore_img_msg">Restauration…</string>
|
||||
<string name="restore_done">Restauration effectuée !</string>
|
||||
<string name="restore_fail">La sauvegarde par défaut n’existe pas !</string>
|
||||
<string name="proprietary_title">Téléchargement de code propriétaire</string>
|
||||
<string name="proprietary_notice">Magisk Manager est un logiciel libre et ne contient pas le code propriétaire de l’API SafetyNet de Google. Autorisez‑vous Magisk Manager à télécharger une extension (contenant GoogleApiClient) permettant d’effectuer les contrôles SafetyNet ?</string>
|
||||
<string name="proprietary_notice">L’application Magisk est un logiciel libre et ne contient pas de code propriétaire de l’API SafetyNet de Google. Souhaitez-vous télécharger une extension permettant d’effectuer les contrôles SafetyNet ?</string>
|
||||
<string name="setup_fail">Échec de l’installation</string>
|
||||
<string name="env_fix_title">Installation supplémentaire requise</string>
|
||||
<string name="env_fix_msg">Votre appareil a besoin d’une installation supplémentaire afin que Magisk fonctionne correctement. Un fichier ZIP d’installation pour Magisk doit être téléchargé. Voulez‑vous effectuer cette installation maintenant ?</string>
|
||||
<string name="env_fix_msg">Votre appareil a besoin d’une installation supplémentaire afin que Magisk fonctionne correctement. Voulez-vous effectuer cette installation maintenant et redémarrer ?</string>
|
||||
<string name="setup_msg">Configuration de l’environnement en cours…</string>
|
||||
<string name="authenticate">Authentification</string>
|
||||
<string name="unsupport_magisk_title">Version de Magisk non prise en charge</string>
|
||||
<string name="unsupport_magisk_msg">Cette version de Magisk Manager ne prend pas en charge les versions de Magisk inférieures à %1$s.\n\nL’application se comportera comme si Magisk n’était pas installé. Veuillez mettre à jour Magisk aussi vite que possible.</string>
|
||||
<string name="unsupport_magisk_msg">Cette version de Magisk ne prend pas en charge les versions de Magisk inférieures à %1$s.\n\nL’application se comportera comme si Magisk n’était pas installé. Veuillez mettre à jour Magisk aussi vite que possible.</string>
|
||||
<string name="external_rw_permission_denied">Permettre l’accès au stockage pour activer cette fonctionnalité</string>
|
||||
<string name="add_shortcut_title">Ajouter un raccourci à l’écran d’accueil</string>
|
||||
<string name="add_shortcut_msg">Après avoir caché Magisk Manager, son nom et son icône peuvent devenir difficiles à reconnaître. Voulez‑vous ajouter un joli raccourci vers l’écran d’accueil ?</string>
|
||||
<string name="add_shortcut_msg">Après avoir masqué Magisk, son nom et son icône peuvent devenir difficiles à reconnaître. Voulez‑vous ajouter un joli raccourci vers l’écran d’accueil ?</string>
|
||||
<string name="app_not_found">Aucune application trouvée pour traiter cette action</string>
|
||||
|
||||
</resources>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user