mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-16 23:27:26 +00:00
Compare commits
85 Commits
v21.1
...
manager-v8
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b6144ae582 | ||
![]() |
afe17c73b4 | ||
![]() |
b51b884fc7 | ||
![]() |
d3e4b29e62 | ||
![]() |
24059e7403 | ||
![]() |
107a2a6682 | ||
![]() |
56b4ab6672 | ||
![]() |
4662454938 | ||
![]() |
db4f78d463 | ||
![]() |
880de21596 | ||
![]() |
622dd84c9e | ||
![]() |
f983bfc883 | ||
![]() |
45cdb3fdb0 | ||
![]() |
9a707236b8 | ||
![]() |
e9e6ad3bb0 | ||
![]() |
ab78a81d15 | ||
![]() |
18340099b7 | ||
![]() |
a013696a41 | ||
![]() |
8a2a6d9232 | ||
![]() |
12aa6d86e4 | ||
![]() |
7d08969d28 | ||
![]() |
dda4aa8488 | ||
![]() |
cdaef3d801 | ||
![]() |
9159166128 | ||
![]() |
dc0882e043 | ||
![]() |
c811f015ef | ||
![]() |
d8f0b66fe1 | ||
![]() |
dc3d57deba | ||
![]() |
d089698475 | ||
![]() |
8ed2dd6687 | ||
![]() |
50305ca1fe | ||
![]() |
3e91567636 | ||
![]() |
0b4dd63d36 | ||
![]() |
38d0f85deb | ||
![]() |
c5b452f369 | ||
![]() |
6ce9225f52 | ||
![]() |
13a8820603 | ||
![]() |
503997a09a | ||
![]() |
17efdff134 | ||
![]() |
984f32f994 | ||
![]() |
eee7f097e3 | ||
![]() |
086059ec30 | ||
![]() |
7ff22c68c7 | ||
![]() |
1232113772 | ||
![]() |
039d4936cb | ||
![]() |
784dd80965 | ||
![]() |
1ffe9bd83b | ||
![]() |
0c28b23224 | ||
![]() |
ec1af9dc1e | ||
![]() |
ff4cea229a | ||
![]() |
3f81f9371f | ||
![]() |
60e89a7d22 | ||
![]() |
c50daa5c9e | ||
![]() |
58d00ab863 | ||
![]() |
ce916459c5 | ||
![]() |
4094d560ab | ||
![]() |
4dbf7eb04b | ||
![]() |
a39577c44d | ||
![]() |
125ee46685 | ||
![]() |
ce84f1762c | ||
![]() |
a687d1347b | ||
![]() |
6d9db20614 | ||
![]() |
c62dfc1bcc | ||
![]() |
aabe2696fe | ||
![]() |
ae0d605310 | ||
![]() |
2a694596b5 | ||
![]() |
ff0a76606e | ||
![]() |
dead74801d | ||
![]() |
ab207a1bb3 | ||
![]() |
f152e8c33d | ||
![]() |
797ba4fbf4 | ||
![]() |
a848f10bba | ||
![]() |
552ec1eb35 | ||
![]() |
1385d2a4f4 | ||
![]() |
3b5c9abf7a | ||
![]() |
e0fa032bd3 | ||
![]() |
7b69650fcd | ||
![]() |
08a8df489f | ||
![]() |
9f35a8a520 | ||
![]() |
0df891b336 | ||
![]() |
385853a290 | ||
![]() |
fa3ef8a1c1 | ||
![]() |
c93ada03c7 | ||
![]() |
0064b01ae0 | ||
![]() |
1469b82aa2 |
87
.github/workflows/build.yml
vendored
Normal file
87
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
name: Magisk Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest, macOS-latest ]
|
||||
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
|
||||
- name: Set up Python 3
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- 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 "MAGISK_NDK_VERSION=$ndk_ver" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Set up GitHub env (Unix)
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
ndk_ver=$(sed -n 's/^magisk.fullNdkVersion=//p' gradle.properties)
|
||||
echo ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT >> $GITHUB_ENV
|
||||
echo MAGISK_NDK_VERSION=$ndk_ver >> $GITHUB_ENV
|
||||
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
!~/.gradle/caches/**/*.lock
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||
restore-keys: ${{ runner.os }}-gradle-
|
||||
|
||||
- name: Cache NDK
|
||||
id: ndk-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.ANDROID_SDK_ROOT }}/ndk/magisk
|
||||
key: ${{ runner.os }}-ndk-${{ env.MAGISK_NDK_VERSION }}
|
||||
|
||||
- name: Set up NDK
|
||||
if: steps.ndk-cache.outputs.cache-hit != 'true'
|
||||
run: python build.py ndk
|
||||
|
||||
- name: Build release
|
||||
run: python build.py -vr all
|
||||
|
||||
- name: Build debug
|
||||
run: python build.py -v all
|
||||
|
||||
# Only upload artifacts built on Linux
|
||||
- name: Upload build artifact
|
||||
if: runner.os == 'Linux'
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ github.sha }}
|
||||
path: out
|
@@ -15,11 +15,11 @@ Here are some feature highlights:
|
||||
|
||||
## Downloads
|
||||
|
||||
[](https://github.com/topjohnwu/Magisk/releases/download/manager-v8.0.2/MagiskManager-v8.0.2.apk)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/download/manager-v8.0.3/MagiskManager-v8.0.3.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.0)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v21.1)
|
||||
|
||||
## Useful Links
|
||||
|
||||
@@ -58,11 +58,11 @@ For Magisk Manager crashes, record and upload the logcat when the crash occurs.
|
||||
- Windows: Add `C:\Path\To\Android Studio\jre\bin` to environment variable `PATH`
|
||||
- Set environment variable `ANDROID_SDK_ROOT` to the Android SDK folder (can be found in Android Studio settings)
|
||||
- Run `./build.py ndk` to let the script download and install NDK for you
|
||||
- Set configurations in `config.prop`. A sample `config.prop.sample` is provided.
|
||||
- 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.
|
||||
- `build.py` builds in debug mode by default. If you want release builds (with `-r, --release`), you need a Java Keystore to sign APKs and zips. For more information, check [Google's Documentation](https://developer.android.com/studio/publish/app-signing.html#generate-key).
|
||||
- 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).
|
||||
|
||||
## Translation Contributions
|
||||
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import java.io.PrintStream
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
kotlin("android.extensions")
|
||||
kotlin("plugin.parcelize")
|
||||
kotlin("kapt")
|
||||
id("androidx.navigation.safeargs.kotlin")
|
||||
}
|
||||
@@ -20,12 +22,11 @@ android {
|
||||
applicationId = "com.topjohnwu.magisk"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
multiDexEnabled = true
|
||||
versionName = Config["appVersion"]
|
||||
versionCode = Config["appVersionCode"]?.toInt()
|
||||
buildConfigField("int", "LATEST_MAGISK", Config["versionCode"] ?: "Integer.MAX_VALUE")
|
||||
versionName = Config.appVersion
|
||||
versionCode = Config.appVersionCode
|
||||
|
||||
javaCompileOptions.annotationProcessorOptions.arguments(
|
||||
mapOf("room.incremental" to "true")
|
||||
mapOf("room.incremental" to "true")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -64,28 +65,61 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
androidExtensions {
|
||||
isExperimental = true
|
||||
}
|
||||
|
||||
val copyUtils = tasks.register("copyUtils", Copy::class) {
|
||||
tasks["preBuild"]?.dependsOn(tasks.register("copyUtils", Copy::class) {
|
||||
from(rootProject.file("scripts/util_functions.sh"))
|
||||
into("src/main/res/raw")
|
||||
}
|
||||
})
|
||||
|
||||
tasks["preBuild"]?.dependsOn(copyUtils)
|
||||
android.applicationVariants.all {
|
||||
val keysDir = rootProject.file("tools/keys")
|
||||
val outSrcDir = File(buildDir, "generated/source/keydata/$name")
|
||||
val outSrc = File(outSrcDir, "com/topjohnwu/signing/KeyData.java")
|
||||
|
||||
fun PrintStream.newField(name: String, file: File) {
|
||||
println("public static byte[] $name() {")
|
||||
print("byte[] buf = {")
|
||||
val bytes = file.readBytes()
|
||||
print(bytes.joinToString(",") { "(byte)(${it.toInt() and 0xff})" })
|
||||
println("};")
|
||||
println("return buf;")
|
||||
println("}")
|
||||
}
|
||||
|
||||
val genSrcTask = tasks.register("generate${name.capitalize()}KeyData") {
|
||||
inputs.dir(keysDir)
|
||||
outputs.file(outSrc)
|
||||
doLast {
|
||||
outSrc.parentFile.mkdirs()
|
||||
PrintStream(outSrc).use {
|
||||
it.println("package com.topjohnwu.signing;")
|
||||
it.println("public final class KeyData {")
|
||||
|
||||
it.newField("testCert", File(keysDir, "testkey.x509.pem"))
|
||||
it.newField("testKey", File(keysDir, "testkey.pk8"))
|
||||
it.newField("verityCert", File(keysDir, "verity.x509.pem"))
|
||||
it.newField("verityKey", File(keysDir, "verity.pk8"))
|
||||
|
||||
it.println("}")
|
||||
}
|
||||
}
|
||||
}
|
||||
registerJavaGeneratingTask(genSrcTask.get(), outSrcDir)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(project(":app:shared"))
|
||||
implementation(project(":app:signing"))
|
||||
|
||||
implementation("com.github.topjohnwu:jtar:1.0.0")
|
||||
implementation("com.github.topjohnwu:indeterminate-checkbox:1.0.7")
|
||||
implementation("com.github.topjohnwu:lz4-java:1.7.1")
|
||||
implementation("com.jakewharton.timber:timber:4.7.1")
|
||||
|
||||
val vBC = "1.68"
|
||||
implementation("org.bouncycastle:bcprov-jdk15on:${vBC}")
|
||||
implementation("org.bouncycastle:bcpkix-jdk15on:${vBC}")
|
||||
|
||||
val vBAdapt = "4.0.0"
|
||||
val bindingAdapter = "me.tatarka.bindingcollectionadapter2:bindingcollectionadapter"
|
||||
implementation("${bindingAdapter}:${vBAdapt}")
|
||||
@@ -124,7 +158,7 @@ dependencies {
|
||||
implementation("com.squareup.moshi:moshi:${vMoshi}")
|
||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:${vMoshi}")
|
||||
|
||||
val vRoom = "2.3.0-alpha03"
|
||||
val vRoom = "2.3.0-alpha04"
|
||||
implementation("androidx.room:room-runtime:${vRoom}")
|
||||
implementation("androidx.room:room-ktx:${vRoom}")
|
||||
kapt("androidx.room:room-compiler:${vRoom}")
|
||||
@@ -136,7 +170,7 @@ dependencies {
|
||||
implementation("androidx.biometric:biometric:1.0.1")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("androidx.browser:browser:1.2.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")
|
||||
|
1
app/signing/.gitignore
vendored
1
app/signing/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
@@ -1,35 +0,0 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
|
||||
plugins {
|
||||
id("java-library")
|
||||
id("java")
|
||||
id("com.github.johnrengelman.shadow") version "6.0.0"
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
val jar by tasks.getting(Jar::class) {
|
||||
manifest {
|
||||
attributes["Main-Class"] = "com.topjohnwu.signing.ZipSigner"
|
||||
}
|
||||
}
|
||||
|
||||
val shadowJar by tasks.getting(ShadowJar::class) {
|
||||
archiveBaseName.set("zipsigner")
|
||||
archiveClassifier.set(null as String?)
|
||||
archiveVersion.set("4.0")
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
|
||||
|
||||
api("org.bouncycastle:bcprov-jdk15on:1.67")
|
||||
api("org.bouncycastle:bcpkix-jdk15on:1.67")
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
package com.topjohnwu.signing;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class BootSigner {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length > 0 && "-verify".equals(args[0])) {
|
||||
String certPath = "";
|
||||
if (args.length >= 2) {
|
||||
/* args[1] is the path to a public key certificate */
|
||||
certPath = args[1];
|
||||
}
|
||||
boolean signed = SignBoot.verifySignature(System.in,
|
||||
certPath.isEmpty() ? null : new FileInputStream(certPath));
|
||||
System.exit(signed ? 0 : 1);
|
||||
} else if (args.length > 0 && "-sign".equals(args[0])) {
|
||||
InputStream cert = null;
|
||||
InputStream key = null;
|
||||
String name = "/boot";
|
||||
|
||||
if (args.length >= 3) {
|
||||
cert = new FileInputStream(args[1]);
|
||||
key = new FileInputStream(args[2]);
|
||||
}
|
||||
if (args.length == 2) {
|
||||
name = args[1];
|
||||
} else if (args.length >= 4) {
|
||||
name = args[3];
|
||||
}
|
||||
|
||||
boolean success = SignBoot.doSignature(name, System.in, System.out, cert, key);
|
||||
System.exit(success ? 0 : 1);
|
||||
} else {
|
||||
System.err.println(
|
||||
"BootSigner <actions> [args]\n" +
|
||||
"Input from stdin, outputs to stdout\n" +
|
||||
"\n" +
|
||||
"Actions:\n" +
|
||||
" -verify [x509.pem]\n" +
|
||||
" verify image, cert is optional\n" +
|
||||
" -sign [x509.pem] [pk8] [name]\n" +
|
||||
" sign image, name, cert and key pair are optional\n" +
|
||||
" name should be /boot (default) or /recovery\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,81 +0,0 @@
|
||||
package com.topjohnwu.signing;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Security;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public class ZipSigner {
|
||||
|
||||
private static void usage() {
|
||||
System.err.println("ZipSigner usage:");
|
||||
System.err.println(" zipsigner.jar input.jar output.jar");
|
||||
System.err.println(" sign jar with AOSP test keys");
|
||||
System.err.println(" zipsigner.jar x509.pem pk8 input.jar output.jar");
|
||||
System.err.println(" sign jar with certificate / private key pair");
|
||||
System.err.println(" zipsigner.jar keyStore keyStorePass alias keyPass input.jar output.jar");
|
||||
System.err.println(" sign jar with Java KeyStore");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
private static void sign(JarMap input, FileOutputStream output) throws Exception {
|
||||
sign(SignApk.class.getResourceAsStream("/keys/testkey.x509.pem"),
|
||||
SignApk.class.getResourceAsStream("/keys/testkey.pk8"), input, output);
|
||||
}
|
||||
|
||||
private static void sign(InputStream certIs, InputStream keyIs,
|
||||
JarMap input, FileOutputStream output) throws Exception {
|
||||
X509Certificate cert = CryptoUtils.readCertificate(certIs);
|
||||
PrivateKey key = CryptoUtils.readPrivateKey(keyIs);
|
||||
SignApk.sign(cert, key, input, output);
|
||||
}
|
||||
|
||||
private static void sign(String keyStore, String keyStorePass, String alias, String keyPass,
|
||||
JarMap in, FileOutputStream out) throws Exception {
|
||||
KeyStore ks;
|
||||
try {
|
||||
ks = KeyStore.getInstance("JKS");
|
||||
try (InputStream is = new FileInputStream(keyStore)) {
|
||||
ks.load(is, keyStorePass.toCharArray());
|
||||
}
|
||||
} catch (KeyStoreException|IOException|CertificateException|NoSuchAlgorithmException e) {
|
||||
ks = KeyStore.getInstance("PKCS12");
|
||||
try (InputStream is = new FileInputStream(keyStore)) {
|
||||
ks.load(is, keyStorePass.toCharArray());
|
||||
}
|
||||
}
|
||||
X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
|
||||
PrivateKey key = (PrivateKey) ks.getKey(alias, keyPass.toCharArray());
|
||||
SignApk.sign(cert, key, in, out);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 2 && args.length != 4 && args.length != 6)
|
||||
usage();
|
||||
|
||||
Security.insertProviderAt(new BouncyCastleProvider(), 1);
|
||||
|
||||
try (JarMap in = JarMap.open(args[args.length - 2], false);
|
||||
FileOutputStream out = new FileOutputStream(args[args.length - 1])) {
|
||||
if (args.length == 2) {
|
||||
sign(in, out);
|
||||
} else if (args.length == 4) {
|
||||
try (InputStream cert = new FileInputStream(args[0]);
|
||||
InputStream key = new FileInputStream(args[1])) {
|
||||
sign(cert, key, in, out);
|
||||
}
|
||||
} else if (args.length == 6) {
|
||||
sign(args[0], args[1], args[2], args[3], in, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@
|
||||
<application
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:name="a.e"
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
|
||||
|
||||
<!-- Splash -->
|
||||
|
@@ -8,10 +8,10 @@ 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.BootSigner
|
||||
import com.topjohnwu.signing.SignBoot
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
BootSigner.main(args)
|
||||
SignBoot.main(args)
|
||||
}
|
||||
|
||||
class b : MainActivity()
|
||||
|
@@ -1,5 +1,8 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
@@ -14,6 +17,7 @@ import androidx.navigation.fragment.NavHostFragment
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||
import com.topjohnwu.magisk.ui.inflater.LayoutInflaterFactory
|
||||
import com.topjohnwu.magisk.ui.theme.Theme
|
||||
|
||||
abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
@@ -41,6 +45,8 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
layoutInflater.factory2 = LayoutInflaterFactory(delegate)
|
||||
|
||||
setTheme(themeRes)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -59,6 +65,31 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
directionsDispatcher.value = null
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
window?.decorView?.let {
|
||||
it.systemUiVisibility = (it.systemUiVisibility
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
window?.decorView?.post {
|
||||
// If navigation bar is short enough (gesture navigation enabled), make it transparent
|
||||
if (window.decorView.rootWindowInsets?.systemWindowInsetBottom ?: 0 < Resources.getSystem().displayMetrics.density * 40) {
|
||||
window.navigationBarColor = Color.TRANSPARENT
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
window.navigationBarDividerColor = Color.TRANSPARENT
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
window.isStatusBarContrastEnforced = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setContentView() {
|
||||
@@ -66,8 +97,6 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
it.setVariable(BR.viewModel, viewModel)
|
||||
it.lifecycleOwner = this
|
||||
}
|
||||
|
||||
ensureInsets()
|
||||
}
|
||||
|
||||
fun setAccessibilityDelegate(delegate: View.AccessibilityDelegate?) {
|
||||
|
@@ -1,12 +1,9 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
|
||||
interface BaseUIComponent<VM : BaseViewModel>: LifecycleOwner {
|
||||
interface BaseUIComponent<VM : BaseViewModel> : LifecycleOwner {
|
||||
|
||||
val viewRoot: View
|
||||
val viewModel: VM
|
||||
@@ -17,47 +14,8 @@ interface BaseUIComponent<VM : BaseViewModel>: LifecycleOwner {
|
||||
}
|
||||
}
|
||||
|
||||
fun consumeSystemWindowInsets(insets: Insets): Insets? = null
|
||||
|
||||
/**
|
||||
* Called for all [ViewEvent]s published by associated viewModel.
|
||||
*/
|
||||
fun onEventDispatched(event: ViewEvent) {}
|
||||
|
||||
fun ensureInsets() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(viewRoot) { _, insets ->
|
||||
insets.asInsets()
|
||||
.also { viewModel.insets = it }
|
||||
.let { consumeSystemWindowInsets(it) }
|
||||
?.subtractBy(insets) ?: insets
|
||||
}
|
||||
if (ViewCompat.isAttachedToWindow(viewRoot)) {
|
||||
ViewCompat.requestApplyInsets(viewRoot)
|
||||
} else {
|
||||
viewRoot.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
||||
override fun onViewDetachedFromWindow(v: View) = Unit
|
||||
override fun onViewAttachedToWindow(v: View) {
|
||||
ViewCompat.requestApplyInsets(v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun WindowInsetsCompat.asInsets() = Insets.of(
|
||||
systemWindowInsetLeft,
|
||||
systemWindowInsetTop,
|
||||
systemWindowInsetRight,
|
||||
systemWindowInsetBottom
|
||||
)
|
||||
|
||||
private fun Insets.subtractBy(insets: WindowInsetsCompat) =
|
||||
WindowInsetsCompat.Builder(insets).setSystemWindowInsets(
|
||||
Insets.of(
|
||||
insets.systemWindowInsetLeft - left,
|
||||
insets.systemWindowInsetTop - top,
|
||||
insets.systemWindowInsetRight - right,
|
||||
insets.systemWindowInsetBottom - bottom
|
||||
)
|
||||
).build()
|
||||
|
||||
}
|
||||
|
@@ -24,8 +24,6 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
override val viewRoot: View get() = binding.root
|
||||
private val navigation get() = activity.navigation
|
||||
|
||||
override fun consumeSystemWindowInsets(insets: Insets) = insets
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
startObserveEvents()
|
||||
@@ -65,7 +63,6 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
return true
|
||||
}
|
||||
})
|
||||
ensureInsets()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@@ -18,7 +18,6 @@ import com.topjohnwu.magisk.ktx.inject
|
||||
import com.topjohnwu.magisk.ui.theme.Theme
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
object Config : PreferenceModel, DBConfig {
|
||||
@@ -159,12 +158,13 @@ object Config : PreferenceModel, DBConfig {
|
||||
|
||||
private const val SU_FINGERPRINT = "su_fingerprint"
|
||||
|
||||
fun load(pkg: String) {
|
||||
try {
|
||||
fun load(pkg: String?) {
|
||||
// Only try to load prefs when fresh install and a previous package name is set
|
||||
if (pkg != null && prefs.all.isEmpty()) runCatching {
|
||||
context.contentResolver.openInputStream(Provider.PREFS_URI(pkg))?.use {
|
||||
prefs.edit { parsePrefs(it) }
|
||||
}
|
||||
} catch (e: IOException) {}
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
// Settings migration
|
||||
|
@@ -16,8 +16,6 @@ object Const {
|
||||
const val BOOTCTL_REVISION = "18ab78817087c337ae0edd1ecac38aec49217880"
|
||||
|
||||
// Misc
|
||||
const val ANDROID_MANIFEST = "AndroidManifest.xml"
|
||||
const val MAGISK_INSTALL_LOG_FILENAME = "magisk_install_log_%s.log"
|
||||
val USER_ID = Process.myUid() / 100000
|
||||
|
||||
object Version {
|
||||
@@ -58,7 +56,7 @@ object Const {
|
||||
object Key {
|
||||
// intents
|
||||
const val OPEN_SECTION = "section"
|
||||
const val HIDDEN_PKG = "hidden_pkg"
|
||||
const val PREV_PKG = "prev_pkg"
|
||||
}
|
||||
|
||||
object Value {
|
||||
|
@@ -48,10 +48,10 @@ open class SplashActivity : Activity() {
|
||||
// Pre-initialize root shell
|
||||
Shell.getShell()
|
||||
|
||||
val hiddenPackage = intent.getStringExtra(Const.Key.HIDDEN_PKG)
|
||||
val prevPkg = intent.getStringExtra(Const.Key.PREV_PKG)
|
||||
|
||||
Config.load(hiddenPackage ?: APPLICATION_ID)
|
||||
handleRepackage(hiddenPackage)
|
||||
Config.load(prevPkg)
|
||||
handleRepackage(prevPkg)
|
||||
Notifications.setup(this)
|
||||
UpdateCheckService.schedule(this)
|
||||
Shortcuts.setupDynamic(this)
|
||||
|
@@ -2,7 +2,7 @@ package com.topjohnwu.magisk.core.download
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
sealed class Action : Parcelable {
|
||||
|
||||
|
@@ -12,8 +12,8 @@ import com.topjohnwu.magisk.core.model.module.OnlineModule
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
import com.topjohnwu.magisk.ktx.cachedFile
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import kotlinx.android.parcel.IgnoredOnParcel
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
private fun cachedFile(name: String) = get<Context>().cachedFile(name).apply { delete() }.toUri()
|
||||
|
||||
@@ -101,7 +101,6 @@ sealed class Subject : Parcelable {
|
||||
Action.Download -> Download()
|
||||
Action.Uninstall -> Uninstall()
|
||||
Action.EnvFix, is Action.Flash, is Action.Patch -> Internal(config)
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@ package com.topjohnwu.magisk.core.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.squareup.moshi.JsonClass
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class UpdateInfo(
|
||||
|
@@ -7,7 +7,7 @@ import com.topjohnwu.magisk.core.model.ModuleJson
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import com.topjohnwu.magisk.ktx.legalFilename
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
|
@@ -33,6 +33,7 @@ object HideAPK {
|
||||
private const val ALPHA = "abcdefghijklmnopqrstuvwxyz"
|
||||
private const val ALPHADOTS = "$ALPHA....."
|
||||
private const val APP_NAME = "Magisk Manager"
|
||||
private const val ANDROID_MANIFEST = "AndroidManifest.xml"
|
||||
|
||||
// Some arbitrary limit
|
||||
const val MAX_LABEL_LENGTH = 32
|
||||
@@ -71,7 +72,7 @@ object HideAPK {
|
||||
): Boolean {
|
||||
try {
|
||||
val jar = JarMap.open(apk)
|
||||
val je = jar.getJarEntry(Const.ANDROID_MANIFEST)
|
||||
val je = jar.getJarEntry(ANDROID_MANIFEST)
|
||||
val xml = AXML(jar.getRawData(je))
|
||||
|
||||
if (!xml.findAndPatch(APPLICATION_ID to pkg, APP_NAME to label.toString()))
|
||||
@@ -123,6 +124,7 @@ object HideAPK {
|
||||
Config.suManager = 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)
|
||||
}
|
||||
|
||||
@@ -167,7 +169,7 @@ object HideAPK {
|
||||
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.HIDDEN_PKG, packageName)
|
||||
intent.putExtra(Const.Key.PREV_PKG, packageName)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
|
@@ -8,8 +8,10 @@ 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.R
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||
@@ -40,6 +42,8 @@ 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
|
||||
|
||||
@@ -107,6 +111,8 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
}
|
||||
|
||||
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})")
|
||||
|
||||
try {
|
||||
ZipInputStream(zipUri.inputStream().buffered()).use { zi ->
|
||||
@@ -225,7 +231,7 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
|
||||
private fun handleFile(uri: Uri): Boolean {
|
||||
val outStream: OutputStream
|
||||
val outFile: MediaStoreUtils.UriFile
|
||||
var outFile: MediaStoreUtils.UriFile? = null
|
||||
|
||||
// Process input file
|
||||
try {
|
||||
@@ -237,27 +243,40 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
return false
|
||||
}
|
||||
src.reset()
|
||||
|
||||
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 = "magisk_patched_$suffix"
|
||||
outStream = if (magic.contentEquals("ustar".toByteArray())) {
|
||||
outFile = MediaStoreUtils.getFile("magisk_patched.tar")
|
||||
handleTar(src, outFile.uri.outputStream())
|
||||
outFile = MediaStoreUtils.getFile("$filename.tar", true)
|
||||
handleTar(src, outFile!!.uri.outputStream())
|
||||
} else {
|
||||
// Raw image
|
||||
srcBoot = File(installDir, "boot.img").path
|
||||
console.add("- Copying image to cache")
|
||||
FileOutputStream(srcBoot).use { src.copyTo(it) }
|
||||
outFile = MediaStoreUtils.getFile("magisk_patched.img")
|
||||
outFile.uri.outputStream()
|
||||
outFile = MediaStoreUtils.getFile("$filename.img", true)
|
||||
outFile!!.uri.outputStream()
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
console.add("! Process error")
|
||||
outFile?.delete()
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
|
||||
// Patch file
|
||||
if (!patchBoot())
|
||||
if (!patchBoot()) {
|
||||
outFile!!.delete()
|
||||
return false
|
||||
}
|
||||
|
||||
// Output file
|
||||
try {
|
||||
@@ -276,6 +295,7 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
console.add("****************************")
|
||||
} catch (e: IOException) {
|
||||
console.add("! Failed to output to $outFile")
|
||||
outFile!!.delete()
|
||||
Timber.e(e)
|
||||
return false
|
||||
}
|
||||
@@ -325,7 +345,7 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
val signed = File(installDir, "signed.img")
|
||||
try {
|
||||
withStreams(SuFileInputStream(patched), signed.outputStream().buffered()) {
|
||||
input, out -> SignBoot.doSignature("/boot", input, out, null, null)
|
||||
input, out -> SignBoot.doSignature(null, null, input, out, "/boot")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
console.add("! Unable to sign image")
|
||||
@@ -339,6 +359,13 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
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
|
||||
@@ -373,10 +400,11 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
|
||||
protected fun doPatchFile(patchFile: Uri) = extractZip() && handleFile(patchFile)
|
||||
|
||||
protected fun direct() = findImage() && extractZip() && patchBoot() && flashBoot()
|
||||
protected fun direct() = findImage() && extractZip() && patchBoot() &&
|
||||
copySepolicyRules() && flashBoot()
|
||||
|
||||
protected suspend fun secondSlot() =
|
||||
findSecondaryImage() && extractZip() && patchBoot() && flashBoot() && postOTA()
|
||||
protected suspend fun secondSlot() = findSecondaryImage() && extractZip() &&
|
||||
patchBoot() && copySepolicyRules() && flashBoot() && postOTA()
|
||||
|
||||
protected fun fixEnv(zip: Uri): Boolean {
|
||||
installDir = SuFile("/data/adb/magisk")
|
||||
|
@@ -7,10 +7,12 @@ import android.util.Base64OutputStream
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.signing.CryptoUtils.readCertificate
|
||||
import com.topjohnwu.signing.CryptoUtils.readPrivateKey
|
||||
import com.topjohnwu.signing.KeyData
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
|
||||
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.math.BigInteger
|
||||
import java.security.KeyPairGenerator
|
||||
@@ -58,10 +60,10 @@ class Keygen(context: Context) : CertKeyProvider {
|
||||
|
||||
class TestProvider : CertKeyProvider {
|
||||
override val cert by lazy {
|
||||
readCertificate(javaClass.getResourceAsStream("/keys/testkey.x509.pem"))
|
||||
readCertificate(ByteArrayInputStream(KeyData.testCert()))
|
||||
}
|
||||
override val key by lazy {
|
||||
readPrivateKey(javaClass.getResourceAsStream("/keys/testkey.pk8"))
|
||||
readPrivateKey(ByteArrayInputStream(KeyData.testKey()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentUris
|
||||
import android.content.ContentValues
|
||||
@@ -40,15 +39,17 @@ object MediaStoreUtils {
|
||||
|
||||
private val relativePath get() = relativePath(Config.downloadDir)
|
||||
|
||||
@RequiresApi(api = 29)
|
||||
@RequiresApi(api = 30)
|
||||
@Throws(IOException::class)
|
||||
private fun insertFile(displayName: String): MediaStoreFile {
|
||||
val values = ContentValues()
|
||||
values.put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath)
|
||||
values.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName)
|
||||
|
||||
// before Android 11, MediaStore can not rename new file when file exists,
|
||||
// insert will return null. use newFile() instead.
|
||||
// When a file with the same name exists and was not created by us:
|
||||
// - Before Android 11, insert will return null
|
||||
// - On Android 11+, the system will automatically create a new name
|
||||
// Thus the reason to restrict this method call to API 30+
|
||||
val fileUri = cr.insert(tableUri, values) ?: throw IOException("Can't insert $displayName.")
|
||||
|
||||
val projection = arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DATA)
|
||||
@@ -65,14 +66,8 @@ object MediaStoreUtils {
|
||||
throw IOException("Can't insert $displayName.")
|
||||
}
|
||||
|
||||
@RequiresApi(api = 29)
|
||||
private fun queryFile(displayName: String): UriFile? {
|
||||
if (Build.VERSION.SDK_INT < 30) {
|
||||
// Fallback to file based I/O pre Android 11
|
||||
val parent = File(Environment.getExternalStorageDirectory(), relativePath)
|
||||
parent.mkdirs()
|
||||
return LegacyUriFile(File(parent, displayName))
|
||||
}
|
||||
|
||||
val projection = arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DATA)
|
||||
// Before Android 10, we wrote the DISPLAY_NAME field when insert, so it can be used.
|
||||
val selection = "${MediaStore.MediaColumns.DISPLAY_NAME} == ?"
|
||||
@@ -92,11 +87,17 @@ object MediaStoreUtils {
|
||||
return null
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Throws(IOException::class)
|
||||
fun getFile(displayName: String): UriFile {
|
||||
return queryFile(displayName) ?:
|
||||
/* this code path will never happen pre 29 */ insertFile(displayName)
|
||||
fun getFile(displayName: String, skipQuery: Boolean = false): UriFile {
|
||||
if (Build.VERSION.SDK_INT < 30) {
|
||||
// Fallback to file based I/O pre Android 11
|
||||
val parent = File(Environment.getExternalStorageDirectory(), relativePath)
|
||||
parent.mkdirs()
|
||||
return LegacyUriFile(File(parent, displayName))
|
||||
}
|
||||
|
||||
return if (skipQuery) insertFile(displayName)
|
||||
else queryFile(displayName) ?: insertFile(displayName)
|
||||
}
|
||||
|
||||
fun Uri.inputStream() = cr.openInputStream(this) ?: throw FileNotFoundException()
|
||||
|
193
app/src/main/java/com/topjohnwu/magisk/ktx/RecyclerView.kt
Normal file
193
app/src/main/java/com/topjohnwu/magisk/ktx/RecyclerView.kt
Normal file
@@ -0,0 +1,193 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.topjohnwu.magisk.ktx
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import android.widget.EdgeEffect
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.topjohnwu.magisk.R
|
||||
|
||||
fun RecyclerView.addInvalidateItemDecorationsObserver() {
|
||||
|
||||
adapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||
invalidateItemDecorations()
|
||||
}
|
||||
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
invalidateItemDecorations()
|
||||
}
|
||||
|
||||
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
|
||||
invalidateItemDecorations()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun RecyclerView.addVerticalPadding(paddingTop: Int = 0, paddingBottom: Int = 0) {
|
||||
addItemDecoration(VerticalPaddingDecoration(paddingTop, paddingBottom))
|
||||
}
|
||||
|
||||
private class VerticalPaddingDecoration(private val paddingTop: Int = 0, private val paddingBottom: Int = 0) : RecyclerView.ItemDecoration() {
|
||||
|
||||
private var allowTop: Boolean = true
|
||||
private var allowBottom: Boolean = true
|
||||
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
val adapter = parent.adapter ?: return
|
||||
val position = parent.getChildAdapterPosition(view)
|
||||
val count = adapter.itemCount
|
||||
if (position == 0 && allowTop) {
|
||||
outRect.top = paddingTop
|
||||
} else if (position == count - 1 && allowBottom) {
|
||||
outRect.bottom = paddingBottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun RecyclerView.addSimpleItemDecoration(
|
||||
left: Int = 0,
|
||||
top: Int = 0,
|
||||
right: Int = 0,
|
||||
bottom: Int = 0,
|
||||
) {
|
||||
addItemDecoration(SimpleItemDecoration(left, top, right, bottom))
|
||||
}
|
||||
|
||||
private class SimpleItemDecoration(
|
||||
private val left: Int = 0,
|
||||
private val top: Int = 0,
|
||||
private val right: Int = 0,
|
||||
private val bottom: Int = 0
|
||||
) : RecyclerView.ItemDecoration() {
|
||||
|
||||
private var allowLeft: Boolean = true
|
||||
private var allowTop: Boolean = true
|
||||
private var allowRight: Boolean = true
|
||||
private var allowBottom: Boolean = true
|
||||
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
if (parent.adapter == null) {
|
||||
return
|
||||
}
|
||||
if (allowLeft) {
|
||||
outRect.left = left
|
||||
}
|
||||
if (allowTop) {
|
||||
outRect.top = top
|
||||
}
|
||||
if (allowRight) {
|
||||
outRect.right = right
|
||||
}
|
||||
if (allowBottom) {
|
||||
outRect.top = bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun RecyclerView.fixEdgeEffect(overScrollIfContentScrolls: Boolean = true, alwaysClipToPadding: Boolean = true) {
|
||||
if (overScrollIfContentScrolls) {
|
||||
val listener = OverScrollIfContentScrollsListener()
|
||||
addOnLayoutChangeListener(listener)
|
||||
setTag(R.id.tag_rikka_recyclerView_OverScrollIfContentScrollsListener, listener)
|
||||
} else {
|
||||
val listener = getTag(R.id.tag_rikka_recyclerView_OverScrollIfContentScrollsListener) as? OverScrollIfContentScrollsListener
|
||||
if (listener != null) {
|
||||
removeOnLayoutChangeListener(listener)
|
||||
setTag(R.id.tag_rikka_recyclerView_OverScrollIfContentScrollsListener, null)
|
||||
}
|
||||
}
|
||||
|
||||
edgeEffectFactory = if (alwaysClipToPadding && !clipToPadding) {
|
||||
AlwaysClipToPaddingEdgeEffectFactory()
|
||||
} else {
|
||||
RecyclerView.EdgeEffectFactory()
|
||||
}
|
||||
}
|
||||
|
||||
private class OverScrollIfContentScrollsListener : View.OnLayoutChangeListener {
|
||||
private var show = true
|
||||
override fun onLayoutChange(v: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) {
|
||||
if (shouldDrawOverScroll(v as RecyclerView) != show) {
|
||||
show = !show
|
||||
if (show) {
|
||||
v.setOverScrollMode(View.OVER_SCROLL_IF_CONTENT_SCROLLS)
|
||||
} else {
|
||||
v.setOverScrollMode(View.OVER_SCROLL_NEVER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldDrawOverScroll(recyclerView: RecyclerView): Boolean {
|
||||
if (recyclerView.layoutManager == null || recyclerView.adapter == null || recyclerView.adapter!!.itemCount == 0) {
|
||||
return false
|
||||
}
|
||||
if (recyclerView.layoutManager is LinearLayoutManager) {
|
||||
val itemCount = recyclerView.layoutManager!!.itemCount
|
||||
val firstPosition: Int = (recyclerView.layoutManager as LinearLayoutManager?)!!.findFirstCompletelyVisibleItemPosition()
|
||||
val lastPosition: Int = (recyclerView.layoutManager as LinearLayoutManager?)!!.findLastCompletelyVisibleItemPosition()
|
||||
return firstPosition != 0 || lastPosition != itemCount - 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private class AlwaysClipToPaddingEdgeEffectFactory : RecyclerView.EdgeEffectFactory() {
|
||||
|
||||
override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect {
|
||||
|
||||
return object : EdgeEffect(view.context) {
|
||||
private var ensureSize = false
|
||||
|
||||
private fun ensureSize() {
|
||||
if (ensureSize) return
|
||||
ensureSize = true
|
||||
|
||||
when (direction) {
|
||||
DIRECTION_LEFT -> {
|
||||
setSize(view.measuredHeight - view.paddingTop - view.paddingBottom,
|
||||
view.measuredWidth - view.paddingLeft - view.paddingRight)
|
||||
}
|
||||
DIRECTION_TOP -> {
|
||||
setSize(view.measuredWidth - view.paddingLeft - view.paddingRight,
|
||||
view.measuredHeight - view.paddingTop - view.paddingBottom)
|
||||
}
|
||||
DIRECTION_RIGHT -> {
|
||||
setSize(view.measuredHeight - view.paddingTop - view.paddingBottom,
|
||||
view.measuredWidth - view.paddingLeft - view.paddingRight)
|
||||
}
|
||||
DIRECTION_BOTTOM -> {
|
||||
setSize(view.measuredWidth - view.paddingLeft - view.paddingRight,
|
||||
view.measuredHeight - view.paddingTop - view.paddingBottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun draw(c: Canvas): Boolean {
|
||||
ensureSize()
|
||||
|
||||
val restore = c.save()
|
||||
when (direction) {
|
||||
DIRECTION_LEFT -> {
|
||||
c.translate(view.paddingBottom.toFloat(), 0f)
|
||||
}
|
||||
DIRECTION_TOP -> {
|
||||
c.translate(view.paddingLeft.toFloat(), view.paddingTop.toFloat())
|
||||
}
|
||||
DIRECTION_RIGHT -> {
|
||||
c.translate(-view.paddingTop.toFloat(), 0f)
|
||||
}
|
||||
DIRECTION_BOTTOM -> {
|
||||
c.translate(view.paddingRight.toFloat(), view.paddingBottom.toFloat())
|
||||
}
|
||||
}
|
||||
val res = super.draw(c)
|
||||
c.restoreToCount(restore)
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,7 +10,6 @@ import android.view.WindowManager
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.setPadding
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.navigation.NavDirections
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
@@ -26,9 +25,9 @@ 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 com.topjohnwu.superuser.Shell
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
class MainViewModel : BaseViewModel()
|
||||
@@ -39,14 +38,6 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
|
||||
override val viewModel by viewModel<MainViewModel>()
|
||||
override val navHost: Int = R.id.main_nav_host
|
||||
|
||||
//This temporarily fixes unwanted feature of BottomNavigationView - where the view applies
|
||||
//padding on itself given insets are not consumed beforehand. Unfortunately the listener
|
||||
//implementation doesn't favor us against the design library, so on re-create it's often given
|
||||
//upper hand.
|
||||
private val navObserver = ViewTreeObserver.OnGlobalLayoutListener {
|
||||
binding.mainNavigation.setPadding(0)
|
||||
}
|
||||
|
||||
private var isRootFragment = true
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -100,8 +91,6 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
|
||||
(currentFragment as? ReselectionTarget)?.onReselected()
|
||||
}
|
||||
|
||||
binding.mainNavigation.viewTreeObserver.addOnGlobalLayoutListener(navObserver)
|
||||
|
||||
val section = if (intent.action == ACTION_APPLICATION_PREFERENCES) Const.Nav.SETTINGS
|
||||
else intent.getStringExtra(Const.Key.OPEN_SECTION)
|
||||
getScreen(section)?.navigate()
|
||||
@@ -116,16 +105,11 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
binding.mainNavigation.menu.apply {
|
||||
findItem(R.id.superuserFragment)?.isEnabled = Info.env.isActive
|
||||
findItem(R.id.superuserFragment)?.isEnabled = Utils.showSuperUser()
|
||||
findItem(R.id.logFragment)?.isEnabled = Info.env.isActive
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
binding.mainNavigation.viewTreeObserver.removeOnGlobalLayoutListener(navObserver)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> onBackPressed()
|
||||
|
@@ -13,18 +13,17 @@ import com.topjohnwu.magisk.arch.itemBindingOf
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.tasks.FlashZip
|
||||
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||
import com.topjohnwu.magisk.databinding.RvBindingAdapter
|
||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.ktx.*
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||
import com.topjohnwu.magisk.utils.set
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import com.topjohnwu.superuser.CallbackList
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class FlashViewModel(
|
||||
args: FlashFragmentArgs,
|
||||
@@ -106,18 +105,16 @@ class FlashViewModel(
|
||||
}
|
||||
|
||||
private fun savePressed() = withExternalRW {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val name = Const.MAGISK_INSTALL_LOG_FILENAME.format(now.toTime(timeFormatStandard))
|
||||
val file = MediaStoreUtils.getFile(name)
|
||||
file.uri.outputStream().bufferedWriter().use { writer ->
|
||||
logItems.forEach {
|
||||
writer.write(it)
|
||||
writer.newLine()
|
||||
}
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val name = "magisk_install_log_%s.log".format(now.toTime(timeFormatStandard))
|
||||
val file = MediaStoreUtils.getFile(name, true)
|
||||
file.uri.outputStream().bufferedWriter().use { writer ->
|
||||
logItems.forEach {
|
||||
writer.write(it)
|
||||
writer.newLine()
|
||||
}
|
||||
SnackbarEvent(file.toString()).publish()
|
||||
}
|
||||
SnackbarEvent(file.toString()).publish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,9 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||
import com.topjohnwu.magisk.databinding.FragmentHideMd2Binding
|
||||
import com.topjohnwu.magisk.ktx.addSimpleItemDecoration
|
||||
import com.topjohnwu.magisk.ktx.addVerticalPadding
|
||||
import com.topjohnwu.magisk.ktx.fixEdgeEffect
|
||||
import com.topjohnwu.magisk.ktx.hideKeyboard
|
||||
import com.topjohnwu.magisk.utils.MotionRevealHelper
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
@@ -49,6 +52,21 @@ class HideFragment : BaseUIFragment<HideViewModel, FragmentHideMd2Binding>() {
|
||||
}
|
||||
})
|
||||
|
||||
val resource = requireContext().resources
|
||||
val l_50 = resource.getDimensionPixelSize(R.dimen.l_50)
|
||||
val l1 = resource.getDimensionPixelSize(R.dimen.l1)
|
||||
binding.hideContent.addVerticalPadding(
|
||||
l_50,
|
||||
l1 + resource.getDimensionPixelSize(R.dimen.internal_action_bar_size)
|
||||
)
|
||||
binding.hideContent.addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
binding.hideContent.fixEdgeEffect()
|
||||
|
||||
val lama = binding.hideContent.layoutManager ?: return
|
||||
lama.isAutoMeasureEnabled = false
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package com.topjohnwu.magisk.ui.home
|
||||
|
||||
import android.os.Build
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
@@ -68,8 +67,7 @@ class HomeViewModel(
|
||||
set(value) = set(value, field, { field = it }, BR.stateManagerProgress)
|
||||
|
||||
@get:Bindable
|
||||
val showUninstall get() =
|
||||
Info.env.magiskVersionCode > 0 && stateMagisk != MagiskState.LOADING && isConnected.get()
|
||||
val showUninstall get() = Info.env.isActive && state != State.LOADING
|
||||
|
||||
@get:Bindable
|
||||
val showSafetyNet get() = Info.hasGMS && isConnected.get()
|
||||
@@ -82,8 +80,6 @@ class HomeViewModel(
|
||||
|
||||
override fun refresh() = viewModelScope.launch {
|
||||
state = State.LOADING
|
||||
notifyPropertyChanged(BR.showUninstall)
|
||||
notifyPropertyChanged(BR.showSafetyNet)
|
||||
svc.fetchUpdate()?.apply {
|
||||
state = State.LOADED
|
||||
stateMagisk = when {
|
||||
@@ -93,7 +89,6 @@ class HomeViewModel(
|
||||
}
|
||||
|
||||
stateManager = when {
|
||||
!app.isUpdateChannelCorrect && isConnected.get() -> MagiskState.NOT_INSTALLED
|
||||
app.isObsolete -> MagiskState.OBSOLETE
|
||||
else -> MagiskState.UP_TO_DATE
|
||||
}
|
||||
@@ -107,6 +102,8 @@ class HomeViewModel(
|
||||
ensureEnv()
|
||||
}
|
||||
} ?: apply { state = State.LOADING_FAILED }
|
||||
notifyPropertyChanged(BR.showUninstall)
|
||||
notifyPropertyChanged(BR.showSafetyNet)
|
||||
}
|
||||
|
||||
val showTest = false
|
||||
@@ -127,14 +124,18 @@ class HomeViewModel(
|
||||
|
||||
fun onDeletePressed() = UninstallDialog().publish()
|
||||
|
||||
fun onManagerPressed() =
|
||||
if (isConnected.get()) ManagerInstallDialog().publish()
|
||||
else SnackbarEvent(R.string.no_connection).publish()
|
||||
fun onManagerPressed() = when (state) {
|
||||
State.LOADED -> ManagerInstallDialog().publish()
|
||||
State.LOADING -> SnackbarEvent(R.string.loading).publish()
|
||||
else -> SnackbarEvent(R.string.no_connection).publish()
|
||||
}
|
||||
|
||||
fun onMagiskPressed() = if (isConnected.get()) withExternalRW {
|
||||
HomeFragmentDirections.actionHomeFragmentToInstallFragment().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 onSafetyNetPressed() =
|
||||
@@ -150,17 +151,7 @@ class HomeViewModel(
|
||||
MagiskState.NOT_INSTALLED,
|
||||
MagiskState.LOADING
|
||||
)
|
||||
|
||||
// Don't bother checking env when magisk is not installed, loading or already has been shown
|
||||
if (
|
||||
invalidStates.any { it == stateMagisk } ||
|
||||
shownDialog ||
|
||||
// don't care for emulators either
|
||||
Build.DEVICE.orEmpty().contains("generic") ||
|
||||
Build.PRODUCT.orEmpty().contains("generic")
|
||||
) {
|
||||
return
|
||||
}
|
||||
if (invalidStates.any { it == stateMagisk } || shownDialog) return
|
||||
|
||||
val result = Shell.su("env_check").await()
|
||||
if (!result.isSuccess) {
|
||||
@@ -171,8 +162,6 @@ class HomeViewModel(
|
||||
|
||||
private val MagiskJson.isObsolete
|
||||
get() = Info.env.isActive && Info.env.magiskVersionCode < versionCode
|
||||
private val ManagerJson.isUpdateChannelCorrect
|
||||
get() = versionCode > 0
|
||||
private val ManagerJson.isObsolete
|
||||
get() = BuildConfig.VERSION_CODE < versionCode
|
||||
|
||||
|
@@ -0,0 +1,88 @@
|
||||
package com.topjohnwu.magisk.ui.inflater
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.InflateException
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.collection.SimpleArrayMap
|
||||
import java.lang.reflect.Constructor
|
||||
|
||||
open class LayoutInflaterFactory(private val delegate: AppCompatDelegate) : LayoutInflater.Factory2 {
|
||||
|
||||
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
|
||||
return onCreateView(null, name, context, attrs)
|
||||
}
|
||||
|
||||
override fun onCreateView(parent: View?, name: String, context: Context, attrs: AttributeSet): View? {
|
||||
val view = delegate.createView(parent, name, context, attrs)
|
||||
?: LayoutInflaterFactoryDefaultImpl.createViewFromTag(context, name, attrs)
|
||||
onViewCreated(view, parent, name, context, attrs)
|
||||
return view
|
||||
}
|
||||
|
||||
open fun onViewCreated(view: View?, parent: View?, name: String, context: Context, attrs: AttributeSet) {
|
||||
if (view == null) return
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
WindowInsetsHelper.attach(view, attrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object LayoutInflaterFactoryDefaultImpl {
|
||||
|
||||
private val constructorSignature = arrayOf(
|
||||
Context::class.java, AttributeSet::class.java)
|
||||
|
||||
private val classPrefixList = arrayOf(
|
||||
"android.widget.",
|
||||
"android.view.",
|
||||
"android.webkit."
|
||||
)
|
||||
|
||||
private val constructorMap = SimpleArrayMap<String, Constructor<out View?>>()
|
||||
|
||||
fun createViewFromTag(context: Context, name: String, attrs: AttributeSet): View? {
|
||||
var name = name
|
||||
if (name == "view") {
|
||||
name = attrs.getAttributeValue(null, "class")
|
||||
}
|
||||
return try {
|
||||
if (-1 == name.indexOf('.')) {
|
||||
for (prefix in classPrefixList) {
|
||||
val view: View? = createViewByPrefix(context, name, attrs, prefix)
|
||||
if (view != null) {
|
||||
return view
|
||||
}
|
||||
}
|
||||
null
|
||||
} else {
|
||||
createViewByPrefix(context, name, attrs, null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(ClassNotFoundException::class, InflateException::class)
|
||||
private fun createViewByPrefix(context: Context, name: String, attrs: AttributeSet, prefix: String?): View? {
|
||||
var constructor = constructorMap[name]
|
||||
return try {
|
||||
if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it
|
||||
val clazz = Class.forName(
|
||||
if (prefix != null) prefix + name else name,
|
||||
false,
|
||||
context.classLoader).asSubclass(View::class.java)
|
||||
constructor = clazz.getConstructor(*constructorSignature)
|
||||
constructorMap.put(name, constructor)
|
||||
}
|
||||
constructor!!.isAccessible = true
|
||||
constructor.newInstance(context, attrs)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,284 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.topjohnwu.magisk.ui.inflater
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.annotation.TargetApi
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity.*
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.OnApplyWindowInsetsListener
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.topjohnwu.magisk.R
|
||||
|
||||
private typealias ApplyInsetsCallback<T> = (insets: Insets, left: Boolean, top: Boolean, right: Boolean, bottom: Boolean) -> T
|
||||
|
||||
private class ApplyInsets(private val out: Rect) : ApplyInsetsCallback<Unit> {
|
||||
|
||||
override fun invoke(insets: Insets, left: Boolean, top: Boolean, right: Boolean, bottom: Boolean) {
|
||||
out.left += if (left) insets.left else 0
|
||||
out.top += if (top) insets.top else 0
|
||||
out.right += if (right) insets.right else 0
|
||||
out.bottom += if (bottom) insets.bottom else 0
|
||||
}
|
||||
}
|
||||
|
||||
private class ConsumeInsets : ApplyInsetsCallback<Insets> {
|
||||
|
||||
override fun invoke(insets: Insets, left: Boolean, top: Boolean, right: Boolean, bottom: Boolean): Insets {
|
||||
val insetsLeft = if (left) 0 else insets.left
|
||||
val insetsTop = if (top) 0 else insets.top
|
||||
val insetsRight = if (right) 0 else insets.right
|
||||
val insetsBottom = if (bottom) 0 else insets.bottom
|
||||
return Insets.of(insetsLeft, insetsTop, insetsRight, insetsBottom)
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
open class WindowInsetsHelper private constructor(
|
||||
private val view: View,
|
||||
private val fitSystemWindows: Int,
|
||||
private val layout_fitsSystemWindowsInsets: Int,
|
||||
private val consumeSystemWindows: Int) : OnApplyWindowInsetsListener {
|
||||
|
||||
internal var initialPaddingLeft: Int = view.paddingLeft
|
||||
internal var initialPaddingTop: Int = view.paddingTop
|
||||
internal var initialPaddingRight: Int = view.paddingRight
|
||||
internal var initialPaddingBottom: Int = view.paddingBottom
|
||||
|
||||
private var initialMargin = false
|
||||
internal var initialMarginLeft: Int = 0
|
||||
internal var initialMarginTop: Int = 0
|
||||
internal var initialMarginRight: Int = 0
|
||||
internal var initialMarginBottom: Int = 0
|
||||
internal var initialMarginStart: Int = 0
|
||||
internal var initialMarginEnd: Int = 0
|
||||
|
||||
private var lastInsets: WindowInsetsCompat? = null
|
||||
|
||||
open fun setInitialPadding(left: Int, top: Int, right: Int, bottom: Int) {
|
||||
initialPaddingLeft = left
|
||||
initialPaddingTop = top
|
||||
initialPaddingRight = right
|
||||
initialPaddingBottom = bottom
|
||||
|
||||
lastInsets?.let { applyWindowInsets(it) }
|
||||
}
|
||||
|
||||
open fun setInitialPaddingRelative(start: Int, top: Int, end: Int, bottom: Int) {
|
||||
val isRTL = view.layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||
if (isRTL) {
|
||||
setInitialPadding(start, top, end, bottom)
|
||||
} else {
|
||||
setInitialPadding(start, top, end, bottom)
|
||||
}
|
||||
}
|
||||
|
||||
open fun setInitialMargin(left: Int, top: Int, right: Int, bottom: Int) {
|
||||
initialPaddingLeft = left
|
||||
initialPaddingTop = top
|
||||
initialPaddingRight = right
|
||||
initialPaddingBottom = bottom
|
||||
|
||||
lastInsets?.let { applyWindowInsets(it) }
|
||||
}
|
||||
|
||||
open fun setInitialMarginRelative(start: Int, top: Int, end: Int, bottom: Int) {
|
||||
initialMarginStart = start
|
||||
initialMarginTop = top
|
||||
initialMarginEnd = end
|
||||
initialMarginBottom = bottom
|
||||
|
||||
lastInsets?.let { applyWindowInsets(it) }
|
||||
}
|
||||
|
||||
@SuppressLint("RtlHardcoded")
|
||||
private fun <T> applyInsets(insets: Insets, fit: Int, callback: ApplyInsetsCallback<T>): T {
|
||||
val relativeMode = (fit and RELATIVE_LAYOUT_DIRECTION) == RELATIVE_LAYOUT_DIRECTION
|
||||
|
||||
val isRTL = view.layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||
|
||||
val left: Boolean
|
||||
val top = fit and TOP == TOP
|
||||
val right: Boolean
|
||||
val bottom = fit and BOTTOM == BOTTOM
|
||||
|
||||
if (relativeMode) {
|
||||
val start = fit and START == START
|
||||
val end = fit and END == END
|
||||
left = (!isRTL && start) || (isRTL && end)
|
||||
right = (!isRTL && end) || (isRTL && start)
|
||||
} else {
|
||||
left = fit and LEFT == LEFT
|
||||
right = fit and RIGHT == RIGHT
|
||||
}
|
||||
|
||||
return callback.invoke(insets, left, top, right, bottom)
|
||||
}
|
||||
|
||||
private fun applyWindowInsets(windowInsets: WindowInsetsCompat): WindowInsetsCompat {
|
||||
if (fitSystemWindows != 0) {
|
||||
val padding = Rect(initialPaddingLeft, initialPaddingTop, initialPaddingRight, initialPaddingBottom)
|
||||
applyInsets(windowInsets.systemWindowInsets, fitSystemWindows, ApplyInsets(padding))
|
||||
view.setPadding(padding.left, padding.top, padding.right, padding.bottom)
|
||||
}
|
||||
|
||||
if (layout_fitsSystemWindowsInsets != 0) {
|
||||
if (!initialMargin) {
|
||||
initialMarginLeft = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.leftMargin ?: 0
|
||||
initialMarginTop = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.topMargin ?: 0
|
||||
initialMarginRight = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.rightMargin ?: 0
|
||||
initialMarginBottom = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.bottomMargin ?: 0
|
||||
initialMarginStart = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.marginStart ?: 0
|
||||
initialMarginEnd = (view.layoutParams as? ViewGroup.MarginLayoutParams)?.marginEnd ?: 0
|
||||
initialMargin = true
|
||||
}
|
||||
|
||||
val margin = if ((layout_fitsSystemWindowsInsets and RELATIVE_LAYOUT_DIRECTION) == RELATIVE_LAYOUT_DIRECTION)
|
||||
Rect(initialMarginLeft, initialMarginTop, initialMarginRight, initialMarginBottom)
|
||||
else
|
||||
Rect(initialMarginStart, initialMarginTop, initialMarginEnd, initialMarginBottom)
|
||||
|
||||
applyInsets(windowInsets.systemWindowInsets, layout_fitsSystemWindowsInsets, ApplyInsets(margin))
|
||||
|
||||
val lp = view.layoutParams
|
||||
if (lp is ViewGroup.MarginLayoutParams) {
|
||||
lp.topMargin = margin.top
|
||||
lp.bottomMargin = margin.bottom
|
||||
|
||||
if ((layout_fitsSystemWindowsInsets and RELATIVE_LAYOUT_DIRECTION) == RELATIVE_LAYOUT_DIRECTION) {
|
||||
lp.marginStart = margin.left
|
||||
lp.marginEnd = margin.right
|
||||
} else {
|
||||
lp.leftMargin = margin.left
|
||||
lp.rightMargin = margin.right
|
||||
}
|
||||
|
||||
view.layoutParams = lp
|
||||
}
|
||||
}
|
||||
|
||||
val systemWindowInsets = if (consumeSystemWindows != 0) applyInsets(windowInsets.systemWindowInsets, consumeSystemWindows, ConsumeInsets()) else windowInsets.systemWindowInsets
|
||||
|
||||
return WindowInsetsCompat.Builder(windowInsets)
|
||||
.setSystemWindowInsets(systemWindowInsets)
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun onApplyWindowInsets(view: View, insets: WindowInsetsCompat): WindowInsetsCompat {
|
||||
if (lastInsets == insets) {
|
||||
return insets
|
||||
}
|
||||
|
||||
lastInsets = insets
|
||||
|
||||
return applyWindowInsets(insets)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun attach(view: View, attrs: AttributeSet) {
|
||||
val a = view.context.obtainStyledAttributes(attrs, R.styleable.WindowInsetsHelper, 0, 0)
|
||||
val edgeToEdge = a.getBoolean(R.styleable.WindowInsetsHelper_edgeToEdge, false)
|
||||
val fitsSystemWindowsInsets = a.getInt(R.styleable.WindowInsetsHelper_fitsSystemWindowsInsets, 0)
|
||||
val layout_fitsSystemWindowsInsets = a.getInt(R.styleable.WindowInsetsHelper_layout_fitsSystemWindowsInsets, 0)
|
||||
val consumeSystemWindowsInsets = a.getInt(R.styleable.WindowInsetsHelper_consumeSystemWindowsInsets, 0)
|
||||
a.recycle()
|
||||
|
||||
attach(view, edgeToEdge, fitsSystemWindowsInsets, layout_fitsSystemWindowsInsets, consumeSystemWindowsInsets)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun attach(view: View, edgeToEdge: Boolean, fitsSystemWindowsInsets: Int, layout_fitsSystemWindowsInsets: Int, consumeSystemWindowsInsets: Int) {
|
||||
if (edgeToEdge) {
|
||||
view.systemUiVisibility = (view.systemUiVisibility
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
|
||||
}
|
||||
|
||||
if (fitsSystemWindowsInsets == 0 && layout_fitsSystemWindowsInsets == 0 && consumeSystemWindowsInsets == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
val listener = WindowInsetsHelper(view, fitsSystemWindowsInsets, layout_fitsSystemWindowsInsets, consumeSystemWindowsInsets)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(view, listener)
|
||||
view.setTag(R.id.tag_rikka_material_WindowInsetsHelper, listener)
|
||||
|
||||
if (!view.isAttachedToWindow) {
|
||||
view.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
||||
override fun onViewAttachedToWindow(v: View) {
|
||||
v.removeOnAttachStateChangeListener(this)
|
||||
v.requestApplyInsets()
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(v: View) = Unit
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val View.windowInsetsHelper: WindowInsetsHelper?
|
||||
get() {
|
||||
val value = getTag(R.id.tag_rikka_material_WindowInsetsHelper)
|
||||
return if (value is WindowInsetsHelper) value else null
|
||||
}
|
||||
|
||||
val View.initialPaddingLeft: Int
|
||||
get() = windowInsetsHelper?.initialPaddingLeft ?: 0
|
||||
|
||||
val View.initialPaddingTop: Int
|
||||
get() = windowInsetsHelper?.initialPaddingTop ?: 0
|
||||
|
||||
val View.initialPaddingRight: Int
|
||||
get() = windowInsetsHelper?.initialPaddingRight ?: 0
|
||||
|
||||
val View.initialPaddingBottom: Int
|
||||
get() = windowInsetsHelper?.initialPaddingBottom ?: 0
|
||||
|
||||
val View.initialPaddingStart: Int
|
||||
get() = if (layoutDirection == View.LAYOUT_DIRECTION_RTL) initialPaddingRight else initialPaddingLeft
|
||||
|
||||
val View.initialPaddingEnd: Int
|
||||
get() = if (layoutDirection == View.LAYOUT_DIRECTION_RTL) initialPaddingLeft else initialPaddingRight
|
||||
|
||||
fun View.setInitialPadding(left: Int, top: Int, right: Int, bottom: Int) {
|
||||
windowInsetsHelper?.setInitialPadding(left, top, right, bottom)
|
||||
}
|
||||
|
||||
fun View.setInitialPaddingRelative(start: Int, top: Int, end: Int, bottom: Int) {
|
||||
windowInsetsHelper?.setInitialPaddingRelative(start, top, end, bottom)
|
||||
}
|
||||
|
||||
val View.initialMarginLeft: Int
|
||||
get() = windowInsetsHelper?.initialMarginLeft ?: 0
|
||||
|
||||
val View.initialMarginTop: Int
|
||||
get() = windowInsetsHelper?.initialMarginTop ?: 0
|
||||
|
||||
val View.initialMarginRight: Int
|
||||
get() = windowInsetsHelper?.initialMarginRight ?: 0
|
||||
|
||||
val View.initialMarginBottom: Int
|
||||
get() = windowInsetsHelper?.initialMarginBottom ?: 0
|
||||
|
||||
val View.initialMarginStart: Int
|
||||
get() = windowInsetsHelper?.initialMarginStart ?: 0
|
||||
|
||||
val View.initialMarginEnd: Int
|
||||
get() = windowInsetsHelper?.initialMarginEnd ?: 0
|
||||
|
||||
fun View.setInitialMargin(left: Int, top: Int, right: Int, bottom: Int) {
|
||||
windowInsetsHelper?.setInitialMargin(left, top, right, bottom)
|
||||
}
|
||||
|
||||
fun View.setInitialMarginRelative(start: Int, top: Int, end: Int, bottom: Int) {
|
||||
windowInsetsHelper?.setInitialMarginRelative(start, top, end, bottom)
|
||||
}
|
@@ -9,6 +9,9 @@ import androidx.core.view.isVisible
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||
import com.topjohnwu.magisk.databinding.FragmentLogMd2Binding
|
||||
import com.topjohnwu.magisk.ktx.addSimpleItemDecoration
|
||||
import com.topjohnwu.magisk.ktx.addVerticalPadding
|
||||
import com.topjohnwu.magisk.ktx.fixEdgeEffect
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.utils.MotionRevealHelper
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
@@ -42,6 +45,21 @@ class LogFragment : BaseUIFragment<LogViewModel, FragmentLogMd2Binding>() {
|
||||
binding.logFilterToggle.setOnClickListener {
|
||||
isMagiskLogVisible = true
|
||||
}
|
||||
|
||||
val resource = requireContext().resources
|
||||
val l_50 = resource.getDimensionPixelSize(R.dimen.l_50)
|
||||
val l1 = resource.getDimensionPixelSize(R.dimen.l1)
|
||||
binding.logFilterSuperuser.logSuperuser.addVerticalPadding(
|
||||
0,
|
||||
l1
|
||||
)
|
||||
binding.logFilterSuperuser.logSuperuser.addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
binding.logFilterSuperuser.logSuperuser.fixEdgeEffect()
|
||||
}
|
||||
|
||||
|
||||
|
@@ -3,20 +3,24 @@ package com.topjohnwu.magisk.ui.log
|
||||
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.arch.diffListOf
|
||||
import com.topjohnwu.magisk.arch.itemBindingOf
|
||||
import com.topjohnwu.magisk.data.repository.LogRepository
|
||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||
import com.topjohnwu.magisk.data.repository.LogRepository
|
||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.ktx.now
|
||||
import com.topjohnwu.magisk.ktx.timeFormatStandard
|
||||
import com.topjohnwu.magisk.ktx.toTime
|
||||
import com.topjohnwu.magisk.utils.set
|
||||
import com.topjohnwu.magisk.view.TextItem
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
|
||||
class LogViewModel(
|
||||
private val repo: LogRepository
|
||||
@@ -54,14 +58,23 @@ class LogViewModel(
|
||||
|
||||
fun saveMagiskLog() = withExternalRW {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val now = Calendar.getInstance()
|
||||
val filename = "magisk_log_%04d%02d%02d_%02d%02d%02d.log".format(
|
||||
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
|
||||
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
||||
now.get(Calendar.MINUTE), now.get(Calendar.SECOND)
|
||||
)
|
||||
val logFile = MediaStoreUtils.getFile(filename)
|
||||
logFile.uri.outputStream().writer().use { it.write(consoleText) }
|
||||
val filename = "magisk_log_%s.log".format(now.toTime(timeFormatStandard))
|
||||
val logFile = MediaStoreUtils.getFile(filename, true)
|
||||
logFile.uri.outputStream().bufferedWriter().use { file ->
|
||||
file.write("---System Properties---\n\n")
|
||||
|
||||
ProcessBuilder("getprop").start()
|
||||
.inputStream.reader().use { it.copyTo(file) }
|
||||
|
||||
file.write("\n---Magisk Logs---\n")
|
||||
file.write("${Info.env.magiskVersionString} (${Info.env.magiskVersionCode})\n\n")
|
||||
file.write(consoleText)
|
||||
|
||||
file.write("\n---Manager Logs---\n")
|
||||
file.write("${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})\n\n")
|
||||
ProcessBuilder("logcat", "-d").start()
|
||||
.inputStream.reader().use { it.copyTo(file) }
|
||||
}
|
||||
SnackbarEvent(logFile.toString()).publish()
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ import com.topjohnwu.magisk.arch.ReselectionTarget
|
||||
import com.topjohnwu.magisk.arch.ViewEvent
|
||||
import com.topjohnwu.magisk.core.download.BaseDownloader
|
||||
import com.topjohnwu.magisk.databinding.FragmentModuleMd2Binding
|
||||
import com.topjohnwu.magisk.ktx.hideKeyboard
|
||||
import com.topjohnwu.magisk.ktx.*
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener
|
||||
import com.topjohnwu.magisk.utils.MotionRevealHelper
|
||||
@@ -62,6 +62,40 @@ class ModuleFragment : BaseUIFragment<ModuleViewModel, FragmentModuleMd2Binding>
|
||||
if (newState != RecyclerView.SCROLL_STATE_IDLE) hideKeyboard()
|
||||
}
|
||||
})
|
||||
|
||||
val resource = requireContext().resources
|
||||
val l_50 = resource.getDimensionPixelSize(R.dimen.l_50)
|
||||
val l1 = resource.getDimensionPixelSize(R.dimen.l1)
|
||||
binding.moduleList.apply {
|
||||
addVerticalPadding(
|
||||
l_50,
|
||||
l1 + l_50 + resource.getDimensionPixelSize(R.dimen.internal_action_bar_size)
|
||||
)
|
||||
addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
fixEdgeEffect()
|
||||
post {
|
||||
addInvalidateItemDecorationsObserver()
|
||||
}
|
||||
}
|
||||
|
||||
binding.moduleFilterInclude.moduleFilterList.apply {
|
||||
addVerticalPadding(
|
||||
l_50,
|
||||
l_50
|
||||
)
|
||||
addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
fixEdgeEffect()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@@ -158,7 +158,7 @@ sealed class BaseSettingsItem : ObservableItem<BaseSettingsItem>() {
|
||||
runCatching { getStringArray(id) }.getOrDefault(emptyArray())
|
||||
|
||||
override fun onPressed(view: View, callback: Callback) {
|
||||
if (entries.isEmpty() || entryValues.isEmpty()) return
|
||||
if (entries.isEmpty()) return
|
||||
super.onPressed(view, callback)
|
||||
}
|
||||
|
||||
|
@@ -5,6 +5,9 @@ import android.view.View
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||
import com.topjohnwu.magisk.databinding.FragmentSettingsMd2Binding
|
||||
import com.topjohnwu.magisk.ktx.addSimpleItemDecoration
|
||||
import com.topjohnwu.magisk.ktx.addVerticalPadding
|
||||
import com.topjohnwu.magisk.ktx.fixEdgeEffect
|
||||
import com.topjohnwu.magisk.ktx.setOnViewReadyListener
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
@@ -24,6 +27,21 @@ class SettingsFragment : BaseUIFragment<SettingsViewModel, FragmentSettingsMd2Bi
|
||||
binding.settingsList.setOnViewReadyListener {
|
||||
binding.settingsList.scrollToPosition(0)
|
||||
}
|
||||
|
||||
val resource = requireContext().resources
|
||||
val l_50 = resource.getDimensionPixelSize(R.dimen.l_50)
|
||||
val l1 = resource.getDimensionPixelSize(R.dimen.l1)
|
||||
binding.settingsList.addVerticalPadding(
|
||||
0,
|
||||
l1
|
||||
)
|
||||
binding.settingsList.addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
binding.settingsList.fixEdgeEffect()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@@ -20,6 +20,7 @@ import com.topjohnwu.magisk.databinding.DialogSettingsAppNameBinding
|
||||
import com.topjohnwu.magisk.databinding.DialogSettingsDownloadPathBinding
|
||||
import com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import com.topjohnwu.magisk.utils.TransitiveText
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.asTransitive
|
||||
import com.topjohnwu.magisk.utils.set
|
||||
@@ -143,11 +144,12 @@ object UpdateChannel : BaseSettingsItem.Selector() {
|
||||
set(value) = setV(value, field, { field = it }) { Config.updateChannel = it }
|
||||
|
||||
override val title = R.string.settings_update_channel_title.asTransitive()
|
||||
override val entries
|
||||
get() = resources.getStringArray(R.array.update_channel).let {
|
||||
if (BuildConfig.DEBUG) it.toMutableList().apply { add("Canary") }.toTypedArray() else it
|
||||
}
|
||||
override val entryValRes = R.array.value_array
|
||||
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 description
|
||||
get() = entries.getOrNull(value)?.asTransitive()
|
||||
?: TransitiveText.String(if (value == -1) entries[0] else "Canary")
|
||||
}
|
||||
|
||||
object UpdateChannelUrl : BaseSettingsItem.Input() {
|
||||
@@ -256,22 +258,20 @@ object Superuser : BaseSettingsItem.Section() {
|
||||
object AccessMode : BaseSettingsItem.Selector() {
|
||||
override val title = R.string.superuser_access.asTransitive()
|
||||
override val entryRes = R.array.su_access
|
||||
override val entryValRes = R.array.value_array
|
||||
|
||||
override var value = Config.rootMode
|
||||
set(value) = setV(value, field, { field = it }) {
|
||||
Config.rootMode = entryValues[it].toInt()
|
||||
Config.rootMode = it
|
||||
}
|
||||
}
|
||||
|
||||
object MultiuserMode : BaseSettingsItem.Selector() {
|
||||
override val title = R.string.multiuser_mode.asTransitive()
|
||||
override val entryRes = R.array.multiuser_mode
|
||||
override val entryValRes = R.array.value_array
|
||||
|
||||
override var value = Config.suMultiuserMode
|
||||
set(value) = setV(value, field, { field = it }) {
|
||||
Config.suMultiuserMode = entryValues[it].toInt()
|
||||
Config.suMultiuserMode = it
|
||||
}
|
||||
|
||||
override val description
|
||||
@@ -285,11 +285,10 @@ object MultiuserMode : BaseSettingsItem.Selector() {
|
||||
object MountNamespaceMode : BaseSettingsItem.Selector() {
|
||||
override val title = R.string.mount_namespace_mode.asTransitive()
|
||||
override val entryRes = R.array.namespace
|
||||
override val entryValRes = R.array.value_array
|
||||
|
||||
override var value = Config.suMntNamespaceMode
|
||||
set(value) = setV(value, field, { field = it }) {
|
||||
Config.suMntNamespaceMode = entryValues[it].toInt()
|
||||
Config.suMntNamespaceMode = it
|
||||
}
|
||||
|
||||
override val description
|
||||
@@ -299,11 +298,10 @@ object MountNamespaceMode : BaseSettingsItem.Selector() {
|
||||
object AutomaticResponse : BaseSettingsItem.Selector() {
|
||||
override val title = R.string.auto_response.asTransitive()
|
||||
override val entryRes = R.array.auto_response
|
||||
override val entryValRes = R.array.value_array
|
||||
|
||||
override var value = Config.suAutoResponse
|
||||
set(value) = setV(value, field, { field = it }) {
|
||||
Config.suAutoResponse = entryValues[it].toInt()
|
||||
Config.suAutoResponse = it
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +312,7 @@ object RequestTimeout : BaseSettingsItem.Selector() {
|
||||
|
||||
override var value = selected
|
||||
set(value) = setV(value, field, { field = it }) {
|
||||
Config.suDefaultTimeout = entryValues[it].toInt()
|
||||
Config.suDefaultTimeout = it
|
||||
}
|
||||
|
||||
private val selected: Int
|
||||
@@ -324,10 +322,9 @@ object RequestTimeout : BaseSettingsItem.Selector() {
|
||||
object SUNotification : BaseSettingsItem.Selector() {
|
||||
override val title = R.string.superuser_notification.asTransitive()
|
||||
override val entryRes = R.array.su_notification
|
||||
override val entryValRes = R.array.value_array
|
||||
|
||||
override var value = Config.suNotification
|
||||
set(value) = setV(value, field, { field = it }) {
|
||||
Config.suNotification = entryValues[it].toInt()
|
||||
Config.suNotification = it
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,13 @@
|
||||
package com.topjohnwu.magisk.ui.superuser
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseUIFragment
|
||||
import com.topjohnwu.magisk.databinding.FragmentSuperuserMd2Binding
|
||||
import com.topjohnwu.magisk.ktx.addSimpleItemDecoration
|
||||
import com.topjohnwu.magisk.ktx.addVerticalPadding
|
||||
import com.topjohnwu.magisk.ktx.fixEdgeEffect
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
class SuperuserFragment : BaseUIFragment<SuperuserViewModel, FragmentSuperuserMd2Binding>() {
|
||||
@@ -15,6 +20,25 @@ class SuperuserFragment : BaseUIFragment<SuperuserViewModel, FragmentSuperuserMd
|
||||
activity.title = resources.getString(R.string.superuser)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val resource = requireContext().resources
|
||||
val l_50 = resource.getDimensionPixelSize(R.dimen.l_50)
|
||||
val l1 = resource.getDimensionPixelSize(R.dimen.l1)
|
||||
binding.superuserList.addVerticalPadding(
|
||||
l_50,
|
||||
l1
|
||||
)
|
||||
binding.superuserList.addSimpleItemDecoration(
|
||||
left = l1,
|
||||
top = l_50,
|
||||
right = l1,
|
||||
bottom = l_50,
|
||||
)
|
||||
binding.superuserList.fixEdgeEffect()
|
||||
}
|
||||
|
||||
override fun onPreBind(binding: FragmentSuperuserMd2Binding) {}
|
||||
|
||||
}
|
||||
|
@@ -2,25 +2,24 @@ package com.topjohnwu.magisk.view
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.InsetDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatDialog
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.databinding.PropertyChangeRegistry
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.itemBindingOf
|
||||
@@ -47,36 +46,22 @@ class MagiskDialog(
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
super.setContentView(binding.root)
|
||||
|
||||
val surfaceColor = MaterialColors.getColor(context, R.attr.colorSurfaceSurfaceVariant, javaClass.canonicalName)
|
||||
val materialShapeDrawable = MaterialShapeDrawable(context, null, R.attr.alertDialogStyle, R.style.MaterialAlertDialog_MaterialComponents)
|
||||
materialShapeDrawable.initializeElevationOverlay(context)
|
||||
materialShapeDrawable.fillColor = ColorStateList.valueOf(surfaceColor)
|
||||
materialShapeDrawable.elevation = context.resources.getDimension(R.dimen.margin_generic)
|
||||
materialShapeDrawable.setCornerSize(context.resources.getDimension(R.dimen.l_50))
|
||||
|
||||
val inset = context.resources.getDimensionPixelSize(R.dimen.appcompat_dialog_background_inset)
|
||||
window?.apply {
|
||||
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
setLayout(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
|
||||
val paddingTop = binding.root.paddingTop
|
||||
val paddingBottom = binding.root.paddingBottom
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, insets ->
|
||||
view.updatePadding(
|
||||
top = paddingTop + insets.systemWindowInsetTop,
|
||||
bottom = paddingBottom + insets.systemWindowInsetBottom
|
||||
)
|
||||
insets
|
||||
setBackgroundDrawable(InsetDrawable(materialShapeDrawable, inset, inset, inset, inset))
|
||||
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setCancelable(flag: Boolean) {
|
||||
val listener = if (!flag) {
|
||||
null
|
||||
} else {
|
||||
setCanceledOnTouchOutside(true)
|
||||
View.OnClickListener { dismiss() }
|
||||
}
|
||||
binding.dialogBaseOutsideContainer.setOnClickListener(listener)
|
||||
}
|
||||
|
||||
inner class Data: ObservableHost {
|
||||
inner class Data : ObservableHost {
|
||||
override var callbacks: PropertyChangeRegistry? = null
|
||||
|
||||
@get:Bindable
|
||||
@@ -88,7 +73,7 @@ class MagiskDialog(
|
||||
set(value) = set(value, field, { field = it }, BR.title)
|
||||
|
||||
@get:Bindable
|
||||
var message : CharSequence = ""
|
||||
var message: CharSequence = ""
|
||||
set(value) = set(value, field, { field = it }, BR.message)
|
||||
|
||||
val buttonPositive = Button()
|
||||
@@ -101,7 +86,7 @@ class MagiskDialog(
|
||||
POSITIVE, NEUTRAL, NEGATIVE, IDGAF
|
||||
}
|
||||
|
||||
inner class Button: ObservableHost {
|
||||
inner class Button : ObservableHost {
|
||||
override var callbacks: PropertyChangeRegistry? = null
|
||||
|
||||
@get:Bindable
|
||||
|
@@ -1,5 +1,8 @@
|
||||
package com.topjohnwu.signing;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1Encodable;
|
||||
import org.bouncycastle.asn1.ASN1EncodableVector;
|
||||
import org.bouncycastle.asn1.ASN1InputStream;
|
||||
@@ -14,6 +17,7 @@ import org.bouncycastle.asn1.DERSequence;
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -84,8 +88,10 @@ public class SignBoot {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean doSignature(String target, InputStream imgIn, OutputStream imgOut,
|
||||
InputStream cert, InputStream key) {
|
||||
public static boolean doSignature(
|
||||
@Nullable X509Certificate cert, @Nullable PrivateKey key,
|
||||
@NonNull InputStream imgIn, @NonNull OutputStream imgOut, @NonNull String target
|
||||
) {
|
||||
try {
|
||||
PushBackRWStream in = new PushBackRWStream(imgIn, imgOut);
|
||||
byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM];
|
||||
@@ -96,16 +102,16 @@ public class SignBoot {
|
||||
in.unread(hdr);
|
||||
BootSignature bootsig = new BootSignature(target, signableSize);
|
||||
if (cert == null) {
|
||||
cert = SignBoot.class.getResourceAsStream("/keys/verity.x509.pem");
|
||||
cert = CryptoUtils.readCertificate(
|
||||
new ByteArrayInputStream(KeyData.verityCert()));
|
||||
}
|
||||
X509Certificate certificate = CryptoUtils.readCertificate(cert);
|
||||
bootsig.setCertificate(certificate);
|
||||
bootsig.setCertificate(cert);
|
||||
if (key == null) {
|
||||
key = SignBoot.class.getResourceAsStream("/keys/verity.pk8");
|
||||
key = CryptoUtils.readPrivateKey(
|
||||
new ByteArrayInputStream(KeyData.verityKey()));
|
||||
}
|
||||
PrivateKey privateKey = CryptoUtils.readPrivateKey(key);
|
||||
byte[] sig = bootsig.sign(privateKey, in, signableSize);
|
||||
bootsig.setSignature(sig, CryptoUtils.getSignatureAlgorithmIdentifier(privateKey));
|
||||
byte[] sig = bootsig.sign(key, in, signableSize);
|
||||
bootsig.setSignature(sig, CryptoUtils.getSignatureAlgorithmIdentifier(key));
|
||||
byte[] encoded_bootsig = bootsig.getEncoded();
|
||||
imgOut.write(encoded_bootsig);
|
||||
imgOut.flush();
|
||||
@@ -116,7 +122,7 @@ public class SignBoot {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean verifySignature(InputStream imgIn, InputStream certIn) {
|
||||
public static boolean verifySignature(InputStream imgIn, X509Certificate cert) {
|
||||
try {
|
||||
// Read the header for size
|
||||
byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM];
|
||||
@@ -142,8 +148,8 @@ public class SignBoot {
|
||||
}
|
||||
|
||||
BootSignature bootsig = new BootSignature(signature);
|
||||
if (certIn != null) {
|
||||
bootsig.setCertificate(CryptoUtils.readCertificate(certIn));
|
||||
if (cert != null) {
|
||||
bootsig.setCertificate(cert);
|
||||
}
|
||||
if (bootsig.verify(rawImg, signableSize)) {
|
||||
System.err.println("Signature is VALID");
|
||||
@@ -314,4 +320,46 @@ public class SignBoot {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length > 0 && "-verify".equals(args[0])) {
|
||||
X509Certificate cert = null;
|
||||
if (args.length >= 2) {
|
||||
// args[1] is the path to a public key certificate
|
||||
cert = CryptoUtils.readCertificate(new FileInputStream(args[1]));
|
||||
}
|
||||
boolean signed = SignBoot.verifySignature(System.in, cert);
|
||||
System.exit(signed ? 0 : 1);
|
||||
} else if (args.length > 0 && "-sign".equals(args[0])) {
|
||||
X509Certificate cert = null;
|
||||
PrivateKey key = null;
|
||||
String name = "/boot";
|
||||
|
||||
if (args.length >= 3) {
|
||||
cert = CryptoUtils.readCertificate(new FileInputStream(args[1]));
|
||||
key = CryptoUtils.readPrivateKey(new FileInputStream(args[2]));
|
||||
}
|
||||
if (args.length == 2) {
|
||||
name = args[1];
|
||||
} else if (args.length >= 4) {
|
||||
name = args[3];
|
||||
}
|
||||
|
||||
boolean result = SignBoot.doSignature(cert, key, System.in, System.out, name);
|
||||
System.exit(result ? 0 : 1);
|
||||
} else {
|
||||
System.err.println(
|
||||
"BootSigner <actions> [args]\n" +
|
||||
"Input from stdin, output to stdout\n" +
|
||||
"\n" +
|
||||
"Actions:\n" +
|
||||
" -verify [x509.pem]\n" +
|
||||
" verify image. cert is optional.\n" +
|
||||
" -sign [x509.pem] [pk8] [name]\n" +
|
||||
" sign image. name and the cert/key pair are optional.\n" +
|
||||
" name should be either /boot (default) or /recovery.\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -17,8 +17,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@{viewModel.insets.left}"
|
||||
android:paddingRight="@{viewModel.insets.right}"
|
||||
app:consumeSystemWindowsInsets="start|end"
|
||||
app:edgeToEdge="true"
|
||||
app:fitsSystemWindowsInsets="start|end"
|
||||
tools:ignore="RtlHardcoded">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
@@ -34,7 +35,7 @@
|
||||
style="@style/WidgetFoundation.Appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@{viewModel.insets.top}">
|
||||
app:fitsSystemWindowsInsets="top">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/main_toolbar"
|
||||
@@ -72,7 +73,9 @@
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@{(int) @dimen/l1 + viewModel.insets.bottom}"
|
||||
android:layout_marginBottom="@dimen/l1"
|
||||
android:fitsSystemWindows="false"
|
||||
app:layout_fitsSystemWindowsInsets="bottom"
|
||||
tools:layout_marginBottom="64dp">
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
@@ -81,6 +84,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:textStyle="bold"
|
||||
android:fitsSystemWindows="false"
|
||||
android:paddingBottom="0dp"
|
||||
app:fitsSystemWindowsInsets="start|end"
|
||||
app:elevation="0dp"
|
||||
app:itemHorizontalTranslationEnabled="false"
|
||||
app:itemIconTint="@color/color_menu_tint"
|
||||
|
@@ -12,219 +12,185 @@
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/dialog_base_outside_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:layout_width="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/dialog_base_start"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_begin="16dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/dialog_base_end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_end="16dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dialog_base_icon"
|
||||
style="@style/WidgetFoundation.Image.Big"
|
||||
gone="@{data.icon == null}"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:src="@{data.icon}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/dialog_base_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_max="400dp">
|
||||
tools:src="@drawable/ic_delete_md2" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card.Elevated"
|
||||
<TextView
|
||||
android:id="@+id/dialog_base_title"
|
||||
gone="@{data.title.length == 0}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:gravity="center"
|
||||
android:text="@{data.title}"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title"
|
||||
app:layout_constraintEnd_toEndOf="@+id/dialog_base_end"
|
||||
app:layout_constraintStart_toStartOf="@+id/dialog_base_start"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dialog_base_icon"
|
||||
tools:lines="1"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:overScrollMode="ifContentScrolls"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/dialog_base_space"
|
||||
app:layout_constraintEnd_toEndOf="@+id/dialog_base_end"
|
||||
app:layout_constraintStart_toStartOf="@+id/dialog_base_start"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dialog_base_title">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
app:cardElevation="@dimen/margin_generic"
|
||||
app:cardUseCompatPadding="true">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<TextView
|
||||
android:id="@+id/dialog_base_message"
|
||||
gone="@{data.message.length == 0}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="match_parent"
|
||||
android:text="@{data.message}"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
tools:lines="3"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/dialog_base_start"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_begin="16dp" />
|
||||
<FrameLayout
|
||||
android:id="@+id/dialog_base_container"
|
||||
gone="@{data.message.length != 0}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/dialog_base_end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_end="16dp" />
|
||||
</FrameLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dialog_base_icon"
|
||||
style="@style/WidgetFoundation.Image.Big"
|
||||
gone="@{data.icon == null}"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:src="@{data.icon}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/dialog_base_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_delete_md2" />
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dialog_base_title"
|
||||
gone="@{data.title.length == 0}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/l1"
|
||||
android:gravity="center"
|
||||
android:text="@{data.title}"
|
||||
android:textAppearance="@style/AppearanceFoundation.Title"
|
||||
app:layout_constraintEnd_toEndOf="@+id/dialog_base_end"
|
||||
app:layout_constraintStart_toStartOf="@+id/dialog_base_start"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dialog_base_icon"
|
||||
tools:lines="1"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
<Space
|
||||
android:id="@+id/dialog_base_space"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/l_50"
|
||||
app:layout_constraintBottom_toTopOf="@+id/dialog_base_buttons"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/l_50"
|
||||
android:overScrollMode="ifContentScrolls"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/dialog_base_space"
|
||||
app:layout_constraintEnd_toEndOf="@+id/dialog_base_end"
|
||||
app:layout_constraintStart_toStartOf="@+id/dialog_base_start"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dialog_base_title">
|
||||
<androidx.appcompat.widget.ButtonBarLayout
|
||||
android:id="@+id/dialog_base_buttons"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="bottom|center_horizontal"
|
||||
android:layoutDirection="locale"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="4dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<Button
|
||||
android:id="@+id/dialog_base_button_4"
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{data.buttonIDGAF.icon == 0 && data.buttonIDGAF.title.length == 0}"
|
||||
isEnabled="@{data.buttonIDGAF.isEnabled()}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:clickable="@{data.buttonIDGAF.isEnabled()}"
|
||||
android:focusable="@{data.buttonIDGAF.isEnabled()}"
|
||||
android:onClick="@{() -> data.buttonIDGAF.clicked()}"
|
||||
android:text="@{data.buttonIDGAF.title}"
|
||||
app:icon="@{data.buttonIDGAF.icon}"
|
||||
tools:icon="@drawable/ic_bug_md2"
|
||||
tools:text="Button 1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dialog_base_message"
|
||||
gone="@{data.message.length == 0}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@{data.message}"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
tools:lines="3"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
<Button
|
||||
android:id="@+id/dialog_base_button_2"
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{data.buttonNeutral.icon == 0 && data.buttonNeutral.title.length == 0}"
|
||||
isEnabled="@{data.buttonNeutral.isEnabled()}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:clickable="@{data.buttonNeutral.isEnabled()}"
|
||||
android:focusable="@{data.buttonNeutral.isEnabled()}"
|
||||
android:onClick="@{() -> data.buttonNeutral.clicked()}"
|
||||
android:text="@{data.buttonNeutral.title}"
|
||||
app:icon="@{data.buttonNeutral.icon}"
|
||||
app:iconGravity="textStart"
|
||||
tools:icon="@drawable/ic_bug_md2"
|
||||
tools:text="Button 1" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/dialog_base_container"
|
||||
gone="@{data.message.length != 0}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<Space
|
||||
android:id="@+id/spacer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="invisible" />
|
||||
|
||||
</FrameLayout>
|
||||
<Button
|
||||
android:id="@+id/dialog_base_button_3"
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{data.buttonNegative.icon == 0 && data.buttonNegative.title.length == 0}"
|
||||
isEnabled="@{data.buttonNegative.isEnabled()}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:clickable="@{data.buttonNegative.isEnabled()}"
|
||||
android:focusable="@{data.buttonNegative.isEnabled()}"
|
||||
android:onClick="@{() -> data.buttonNegative.clicked()}"
|
||||
android:text="@{data.buttonNegative.title}"
|
||||
app:icon="@{data.buttonNegative.icon}"
|
||||
tools:icon="@drawable/ic_bug_md2"
|
||||
tools:text="Button 1" />
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
<Button
|
||||
android:id="@+id/dialog_base_button_1"
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{data.buttonPositive.icon == 0 && data.buttonPositive.title.length == 0}"
|
||||
isEnabled="@{data.buttonPositive.isEnabled()}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:clickable="@{data.buttonPositive.isEnabled()}"
|
||||
android:focusable="@{data.buttonPositive.isEnabled()}"
|
||||
android:onClick="@{() -> data.buttonPositive.clicked()}"
|
||||
android:text="@{data.buttonPositive.title}"
|
||||
app:icon="@{data.buttonPositive.icon}"
|
||||
app:iconGravity="textStart"
|
||||
tools:icon="@drawable/ic_bug_md2"
|
||||
tools:text="Button 1" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/dialog_base_space"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/l_50"
|
||||
app:layout_constraintBottom_toTopOf="@+id/dialog_base_buttons"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.ButtonBarLayout
|
||||
android:id="@+id/dialog_base_buttons"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="bottom|center_horizontal"
|
||||
android:layoutDirection="locale"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="4dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<Button
|
||||
android:id="@+id/dialog_base_button_4"
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{data.buttonIDGAF.icon == 0 && data.buttonIDGAF.title.length == 0}"
|
||||
isEnabled="@{data.buttonIDGAF.isEnabled()}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:clickable="@{data.buttonIDGAF.isEnabled()}"
|
||||
android:filterTouchesWhenObscured="true"
|
||||
android:focusable="@{data.buttonIDGAF.isEnabled()}"
|
||||
android:onClick="@{() -> data.buttonIDGAF.clicked()}"
|
||||
android:text="@{data.buttonIDGAF.title}"
|
||||
app:icon="@{data.buttonIDGAF.icon}"
|
||||
tools:icon="@drawable/ic_bug_md2"
|
||||
tools:text="Button 1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/dialog_base_button_2"
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{data.buttonNeutral.icon == 0 && data.buttonNeutral.title.length == 0}"
|
||||
isEnabled="@{data.buttonNeutral.isEnabled()}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:clickable="@{data.buttonNeutral.isEnabled()}"
|
||||
android:filterTouchesWhenObscured="true"
|
||||
android:focusable="@{data.buttonNeutral.isEnabled()}"
|
||||
android:onClick="@{() -> data.buttonNeutral.clicked()}"
|
||||
android:text="@{data.buttonNeutral.title}"
|
||||
app:icon="@{data.buttonNeutral.icon}"
|
||||
app:iconGravity="textStart"
|
||||
tools:icon="@drawable/ic_bug_md2"
|
||||
tools:text="Button 1" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/spacer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/dialog_base_button_3"
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{data.buttonNegative.icon == 0 && data.buttonNegative.title.length == 0}"
|
||||
isEnabled="@{data.buttonNegative.isEnabled()}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:clickable="@{data.buttonNegative.isEnabled()}"
|
||||
android:filterTouchesWhenObscured="true"
|
||||
android:focusable="@{data.buttonNegative.isEnabled()}"
|
||||
android:onClick="@{() -> data.buttonNegative.clicked()}"
|
||||
android:text="@{data.buttonNegative.title}"
|
||||
app:icon="@{data.buttonNegative.icon}"
|
||||
tools:icon="@drawable/ic_bug_md2"
|
||||
tools:text="Button 1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/dialog_base_button_1"
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
gone="@{data.buttonPositive.icon == 0 && data.buttonPositive.title.length == 0}"
|
||||
isEnabled="@{data.buttonPositive.isEnabled()}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:clickable="@{data.buttonPositive.isEnabled()}"
|
||||
android:filterTouchesWhenObscured="true"
|
||||
android:focusable="@{data.buttonPositive.isEnabled()}"
|
||||
android:onClick="@{() -> data.buttonPositive.clicked()}"
|
||||
android:text="@{data.buttonPositive.title}"
|
||||
app:icon="@{data.buttonPositive.icon}"
|
||||
app:iconGravity="textStart"
|
||||
tools:icon="@drawable/ic_bug_md2"
|
||||
tools:text="Button 1" />
|
||||
|
||||
</androidx.appcompat.widget.ButtonBarLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</FrameLayout>
|
||||
</androidx.appcompat.widget.ButtonBarLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</layout>
|
||||
|
@@ -18,7 +18,8 @@
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
|
||||
android:layout_marginTop="@dimen/internal_action_bar_size"
|
||||
app:layout_fitsSystemWindowsInsets="top"
|
||||
tools:layout_marginTop="@dimen/internal_action_bar_size">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
@@ -31,9 +32,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@{viewModel.insets.left}"
|
||||
android:paddingRight="@{viewModel.insets.right}"
|
||||
android:paddingBottom="@{viewModel.insets.bottom}"
|
||||
app:fitsSystemWindowsInsets="start|end|bottom"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:listitem="@layout/item_console_md2" />
|
||||
@@ -46,12 +45,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/l1"
|
||||
android:layout_marginBottom="@{(int) @dimen/l1 + viewModel.insets.bottom}"
|
||||
android:layout_marginBottom="@dimen/l1"
|
||||
android:onClick="@{() -> viewModel.restartPressed()}"
|
||||
android:text="@string/reboot"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="?colorOnPrimary"
|
||||
android:textStyle="bold"
|
||||
app:layout_fitsSystemWindowsInsets="bottom"
|
||||
app:backgroundTint="?colorPrimary"
|
||||
app:icon="@drawable/ic_restart"
|
||||
app:iconTint="?colorOnPrimary" />
|
||||
|
@@ -17,7 +17,6 @@
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/hide_content"
|
||||
dividerVertical="@{@drawable/divider_l_50}"
|
||||
invisibleUnless="@{viewModel.loaded || !viewModel.items.empty}"
|
||||
itemBinding="@{viewModel.itemBinding}"
|
||||
items="@{viewModel.items}"
|
||||
@@ -25,10 +24,8 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/l1"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size + (int) @dimen/l1}"
|
||||
android:paddingEnd="@dimen/l1"
|
||||
android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/internal_action_bar_size + (int) @dimen/l1}"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_hide_md2"
|
||||
tools:paddingTop="40dp" />
|
||||
@@ -41,8 +38,9 @@
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@{viewModel.insets.bottom + (int) @dimen/l1}"
|
||||
android:layout_marginBottom="@dimen/l1"
|
||||
app:backgroundTint="?colorSurfaceSurfaceVariant"
|
||||
app:layout_fitsSystemWindowsInsets="bottom"
|
||||
app:srcCompat="@drawable/ic_search_md2"
|
||||
app:tint="?colorPrimary"
|
||||
tools:layout_marginBottom="64dp" />
|
||||
|
@@ -22,8 +22,9 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:fillViewport="true"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
|
||||
android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l3}"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
android:paddingBottom="@dimen/l3"
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
tools:layout_marginTop="24dp">
|
||||
|
||||
<LinearLayout
|
||||
|
@@ -20,8 +20,9 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:fillViewport="true"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
|
||||
android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l2}"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
android:paddingBottom="@dimen/l2"
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
tools:paddingTop="24dp">
|
||||
|
||||
<FrameLayout
|
||||
@@ -34,7 +35,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/l1">
|
||||
android:paddingTop="@dimen/l_50">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card"
|
||||
|
@@ -29,7 +29,8 @@
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@{viewModel.insets.bottom + (int) @dimen/l1}"
|
||||
android:layout_marginBottom="@dimen/l1"
|
||||
app:layout_fitsSystemWindowsInsets="bottom"
|
||||
app:backgroundTint="?colorSurfaceSurfaceVariant"
|
||||
app:srcCompat="@drawable/ic_folder_list"
|
||||
app:tint="?colorPrimary"
|
||||
|
@@ -31,8 +31,6 @@
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/module_list"
|
||||
adapter="@{viewModel.adapter}"
|
||||
dividerHorizontal="@{@drawable/divider_l1}"
|
||||
dividerVertical="@{@drawable/divider_l_50}"
|
||||
gone="@{viewModel.loading && viewModel.items.empty}"
|
||||
itemBinding="@{viewModel.itemBinding}"
|
||||
items="@{viewModel.items}"
|
||||
@@ -40,10 +38,8 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/l1"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size + (int) @dimen/l1}"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/internal_action_bar_size + (int) @dimen/l1}"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_module_md2" />
|
||||
|
||||
@@ -54,7 +50,8 @@
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@{viewModel.insets.bottom + (int) @dimen/l1}"
|
||||
android:layout_marginBottom="@dimen/l1"
|
||||
app:layout_fitsSystemWindowsInsets="bottom"
|
||||
app:backgroundTint="?colorSurfaceSurfaceVariant"
|
||||
app:srcCompat="@drawable/ic_search_md2"
|
||||
app:tint="?colorPrimary"
|
||||
|
@@ -22,8 +22,8 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:fillViewport="true"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
|
||||
android:paddingBottom="@{viewModel.insets.bottom}"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
tools:paddingBottom="48dp"
|
||||
tools:paddingTop="24dp">
|
||||
|
||||
|
@@ -15,8 +15,6 @@
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
adapter="@{viewModel.adapter}"
|
||||
dividerHorizontal="@{@drawable/divider_l_50}"
|
||||
dividerVertical="@{@drawable/divider_l_50}"
|
||||
itemBinding="@{viewModel.itemBinding}"
|
||||
items="@{viewModel.items}"
|
||||
android:id="@+id/settings_list"
|
||||
@@ -25,10 +23,8 @@
|
||||
android:focusableInTouchMode="false"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/l1"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
|
||||
android:paddingEnd="@dimen/l_50"
|
||||
android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l1}"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:layout_marginTop="24dp"
|
||||
tools:listitem="@layout/item_settings"
|
||||
|
@@ -20,8 +20,6 @@
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/superuser_list"
|
||||
adapter="@{viewModel.adapter}"
|
||||
dividerHorizontal="@{@drawable/divider_l1}"
|
||||
dividerVertical="@{@drawable/divider_l_50}"
|
||||
goneUnless="@{viewModel.loaded || !viewModel.items.empty}"
|
||||
itemBinding="@{viewModel.itemBinding}"
|
||||
items="@{viewModel.items}"
|
||||
@@ -29,9 +27,8 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/l1"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size + (int) @dimen/l1}"
|
||||
android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l2}"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_policy_md2" />
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
|
||||
@@ -17,15 +18,17 @@
|
||||
android:clipToPadding="false"
|
||||
android:fillViewport="true"
|
||||
android:paddingStart="@dimen/l1"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size + (int) @dimen/l1}"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
android:paddingEnd="@dimen/l1"
|
||||
android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l1}">
|
||||
android:paddingBottom="@dimen/l1"
|
||||
app:fitsSystemWindowsInsets="top|bottom">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/theme_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/l1"
|
||||
android:useDefaultMargins="true">
|
||||
|
||||
<include
|
||||
|
@@ -21,7 +21,8 @@
|
||||
android:paddingStart="@dimen/l1"
|
||||
android:paddingTop="@dimen/l1"
|
||||
android:paddingEnd="@dimen/l1"
|
||||
android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l1}"
|
||||
android:paddingBottom="@dimen/l1"
|
||||
app:fitsSystemWindowsInsets="bottom"
|
||||
tools:layout_gravity="bottom"
|
||||
tools:paddingBottom="64dp">
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
@@ -32,8 +33,8 @@
|
||||
precomputedText="@{viewModel.consoleText}"
|
||||
android:textAppearance="@style/AppearanceFoundation.Caption"
|
||||
android:textSize="10sp"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
|
||||
android:paddingBottom="@{viewModel.insets.bottom}"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
app:layout_fitsSystemWindowsInsets="top|bottom"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
@@ -16,16 +16,15 @@
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/log_superuser"
|
||||
itemBinding="@{viewModel.itemBinding}"
|
||||
items="@{viewModel.items}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/l1"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
|
||||
android:paddingEnd="@dimen/l1"
|
||||
android:paddingBottom="@{viewModel.insets.bottom}"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
app:fitsSystemWindowsInsets="top|bottom"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_log_access_md2"
|
||||
tools:paddingTop="24dp" />
|
||||
|
@@ -18,14 +18,13 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l1}"
|
||||
android:paddingBottom="@dimen/l1"
|
||||
app:fitsSystemWindowsInsets="bottom"
|
||||
tools:layout_gravity="bottom"
|
||||
tools:paddingBottom="64dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/module_filter_list"
|
||||
dividerHorizontal="@{@drawable/divider_l1}"
|
||||
dividerVertical="@{@drawable/divider_l_50}"
|
||||
itemBinding="@{viewModel.itemSearchBinding}"
|
||||
items="@{viewModel.itemsSearch}"
|
||||
android:layout_width="match_parent"
|
||||
@@ -33,8 +32,8 @@
|
||||
android:layout_marginBottom="@dimen/l1"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/l1"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size + (int) @dimen/l1}"
|
||||
android:paddingTop="@dimen/internal_action_bar_size"
|
||||
app:fitsSystemWindowsInsets="top"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/module_filter_title_search"
|
||||
|
@@ -1,3 +1,8 @@
|
||||
## v8.0.4
|
||||
|
||||
- A lot of stability changes and minor bug fixes
|
||||
- Collect device properties, app logcat, and Magisk logs when saving logs in the logs menu
|
||||
|
||||
## v8.0.3
|
||||
|
||||
- Switch to the new Magisk Module Repo setup in preparation to allow 3rd party repos
|
||||
|
@@ -30,10 +30,16 @@ direct_install() {
|
||||
rm -f $MAGISKBIN/new-boot.img
|
||||
echo "- Flashing new boot image"
|
||||
flash_image $1/new-boot.img $2
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "! Insufficient partition size"
|
||||
return 1
|
||||
fi
|
||||
case $? in
|
||||
1)
|
||||
echo "! Insufficient partition size"
|
||||
return 1
|
||||
;;
|
||||
2)
|
||||
echo "! $2 is read only"
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
rm -rf $1
|
||||
return 0
|
||||
}
|
||||
|
@@ -6,8 +6,8 @@
|
||||
<string name="logs">Журналы</string>
|
||||
<string name="settings">Налады</string>
|
||||
<string name="refresh">Абнавіць даныя</string>
|
||||
<string name="install">Усталяваць</string>
|
||||
<string name="section_home">На хатнюю старонку</string>
|
||||
<string name="install">Усталёўка</string>
|
||||
<string name="section_home">Хатняя старонка</string>
|
||||
<string name="section_theme">Тэмы</string>
|
||||
<string name="safetynet">SafetyNet</string>
|
||||
<!--Home-->
|
||||
@@ -16,7 +16,7 @@
|
||||
<string name="manager">Кіраўнік</string>
|
||||
<string name="loading">Загрузка…</string>
|
||||
<string name="update">Абнавіць</string>
|
||||
<string name="not_available">Н/Д</string>
|
||||
<string name="not_available">Не</string>
|
||||
<string name="hide">Схаваць</string>
|
||||
<string name="status">Статус</string>
|
||||
<string name="home_package">Пакунак</string>
|
||||
@@ -26,7 +26,7 @@
|
||||
<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_installed_version">Усталявана</string>
|
||||
<string name="home_latest_version">Апошні</string>
|
||||
<string name="invalid_update_channel">Хібны канал абнаўлення</string>
|
||||
<string name="uninstall_magisk_title">Выдаліць Magisk</string>
|
||||
|
@@ -170,6 +170,8 @@
|
||||
<string name="superuser_notification">Notificación de superusuario</string>
|
||||
<string name="settings_su_reauth_title">Re-autenticación</string>
|
||||
<string name="settings_su_reauth_summary">Pedir permisos de superusuario nuevamente si una aplicación es actualizada o reinstalada</string>
|
||||
<string name="settings_su_tapjack_title">Habilitar Protección contra Tapjacking</string>
|
||||
<string name="settings_su_tapjack_summary">El cuadro de solicitud de superusuario no responderá mientras esté oculto por cualquier otra ventana o superposición</string>
|
||||
<string name="settings_su_biometric_title">Habilitar autenticación biométrica</string>
|
||||
<string name="settings_su_biometric_summary">Usar autenticación biométrica para permitir solicitudes de superusuario</string>
|
||||
<string name="no_biometric">Dispositivo no compatible o las configuraciones biométricas no están habilitadas</string>
|
||||
|
@@ -22,7 +22,7 @@
|
||||
<string name="status">Status</string>
|
||||
<string name="home_package">Paket</string>
|
||||
|
||||
<string name="home_notice_content">Pastikan selalu menggunakan Magisk Manager dari sumber terbuka. Manager dari sumber yang tidak dikenal dapat melakukan tindakan jahat.</string>
|
||||
<string name="home_notice_content">Pastikan selalu menggunakan Magisk Manager dari sumber terbuka. Manager dari sumber yang tidak dikenal dapat melakukan tindakan jahat!</string>
|
||||
<string name="home_support_title">Dukung kami</string>
|
||||
<string name="home_item_source">Sumber</string>
|
||||
<string name="home_support_content">Magisk gratis dan bersumber terbuka, dan akan selalu seperti itu. Bagaimanapun juga Anda dapat menunjukan kepedulian Anda kepada kami dengan mengirimkan sedikit donasi.</string>
|
||||
@@ -85,11 +85,11 @@
|
||||
<string name="superuser_policy_none">Belum ada aplikasi yang meminta izin superuser.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Log Anda masih kosong, coba gunakan lebih banyak aplikasi yang membutuhkan SU Anda.</string>
|
||||
<string name="log_data_magisk_none">Log Magisk kosong, ini aneh.</string>
|
||||
<string name="log_data_none">Log Anda masih kosong, coba gunakan lebih banyak aplikasi yang membutuhkan SU Anda</string>
|
||||
<string name="log_data_magisk_none">Log Magisk kosong, ini aneh</string>
|
||||
<string name="menuSaveLog">Simpan log</string>
|
||||
<string name="menuClearLog">Hapus log sekarang</string>
|
||||
<string name="logs_cleared">Log berhasil dihapus.</string>
|
||||
<string name="logs_cleared">Log berhasil dihapus</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Target UID: %1$d</string>
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
<string name="safetynet_attest_loading">Tunggu sebentar…</string>
|
||||
<string name="safetynet_attest_restart">Coba lagi</string>
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Tampilkan aplikasi sistem</string>
|
||||
<string name="show_os_app">Tampilkan aplikasi OS</string>
|
||||
<string name="hide_filter_hint">Filter menurut nama</string>
|
||||
@@ -109,7 +109,7 @@
|
||||
<string name="hide_filters">Filter</string>
|
||||
<string name="hide_search">Telusuri</string>
|
||||
|
||||
<!--Module -->
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Info tidak tersedia)</string>
|
||||
<string name="reboot_userspace">Nyalakan ulang secara halus</string>
|
||||
<string name="reboot_recovery">Nyalakan ke mode Recovery</string>
|
||||
@@ -127,7 +127,7 @@
|
||||
<string name="module_section_online">Online</string>
|
||||
<string name="sorting_order">Urutan sortir</string>
|
||||
|
||||
<!--Settings -->
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Mode tema</string>
|
||||
<string name="settings_dark_mode_message">Pilih mode yang paling cocok dengan gaya Anda!</string>
|
||||
<string name="settings_dark_mode_light">Selalu terang</string>
|
||||
@@ -220,7 +220,7 @@
|
||||
<string name="repo_cache_cleared">Cache repo dihapus</string>
|
||||
<string name="flashing">Memasang…</string>
|
||||
<string name="done">Selesai!</string>
|
||||
<string name="failure">Gagal</string>
|
||||
<string name="failure">Gagal!</string>
|
||||
<string name="hide_manager_title">Menyembunyikan Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">Gagal menyembunyikan Magisk Manager.</string>
|
||||
<string name="restore_manager_fail_toast">Gagal memulihkan Magisk Manager</string>
|
||||
|
@@ -55,6 +55,7 @@
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Richiesta di accesso root</string>
|
||||
<string name="touch_filtered_warning">Magisk non può verificare la tua risposta perché un\'app sta oscurando una richiesta di accesso root</string>
|
||||
<string name="deny">Nega</string>
|
||||
<string name="prompt">Chiedi</string>
|
||||
<string name="grant">Consenti</string>
|
||||
@@ -101,6 +102,7 @@
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">Mostra app di sistema</string>
|
||||
<string name="show_os_app">Mostra app del sistema operativo</string>
|
||||
<string name="hide_filter_hint">Filtra per nome</string>
|
||||
<string name="hide_scroll_up">Scorri verso l\'alto</string>
|
||||
<string name="hide_filters">Filtri</string>
|
||||
@@ -108,6 +110,7 @@
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Nessuna informazione)</string>
|
||||
<string name="reboot_userspace">Riavvio veloce</string>
|
||||
<string name="reboot_recovery">Riavvia in recovery</string>
|
||||
<string name="reboot_bootloader">Riavvia in bootloader</string>
|
||||
<string name="reboot_download">Riavvia in modalità download</string>
|
||||
@@ -169,6 +172,8 @@
|
||||
<string name="superuser_notification">Notifica di accesso root</string>
|
||||
<string name="settings_su_reauth_title">Riautentica dopo aggiornamento</string>
|
||||
<string name="settings_su_reauth_summary">Richiedi nuovamente i permessi di root dopo l\'aggiornamento di un\'app</string>
|
||||
<string name="settings_su_tapjack_title">Abilita protezione anti-tapjacking</string>
|
||||
<string name="settings_su_tapjack_summary">La schermata di richiesta dei permessi di root non risponderà al tocco mentre è oscurata da altre finestre o elementi in sovraimpressione</string>
|
||||
<string name="settings_su_biometric_title">Abilita autenticazione biometrica</string>
|
||||
<string name="settings_su_biometric_summary">Utilizza l\'autenticazione biometrica per accettare le richieste di accesso root</string>
|
||||
<string name="no_biometric">Il dispositivo non è supportato o le impostazioni biometriche sono disattivate</string>
|
||||
@@ -217,6 +222,7 @@
|
||||
<string name="failure">Fallito</string>
|
||||
<string name="hide_manager_title">Nascondendo Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">Non è stato possibile nascondere Magisk Manager.</string>
|
||||
<string name="restore_manager_fail_toast">Non è stato possibile ripristinare Magisk Manager.</string>
|
||||
<string name="open_link_failed_toast">Nessuna app disponibile per aprire il link</string>
|
||||
<string name="complete_uninstall">Disinstalla completamente</string>
|
||||
<string name="restore_img">Ripristina immagini</string>
|
||||
|
@@ -15,7 +15,7 @@
|
||||
<string name="no_connection">אין חיבור זמין</string>
|
||||
<string name="app_changelog">רשימת שינויים</string>
|
||||
<string name="manager">מנהל</string>
|
||||
<string name="loading">…טוען</string>
|
||||
<string name="loading">טוען…</string>
|
||||
<string name="update">עדכון</string>
|
||||
<string name="not_available">ל/ז</string>
|
||||
<string name="hide">הסתרה</string>
|
||||
@@ -33,6 +33,7 @@
|
||||
<string name="invalid_update_channel">ערוץ עדכון לא חוקי</string>
|
||||
<string name="uninstall_magisk_title">הסרת Magisk</string>
|
||||
<string name="uninstall_magisk_msg">כל המודולים יושבתו/יוסרו!\nגישת שורש תושבת!\nהנתונים שלך עשויים להיות מוצפנים אם לא הוצפנו כבר!</string>
|
||||
<string name="home_check_safetynet">בדיקת SafetyNet</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">שמירה על הצפנה בכח</string>
|
||||
@@ -41,7 +42,7 @@
|
||||
<string name="install_options_title">אפשרויות</string>
|
||||
<string name="install_method_title">שיטה</string>
|
||||
<string name="install_next">הבא</string>
|
||||
<string name="install_start">מצא לדרך</string>
|
||||
<string name="install_start">צא לדרך</string>
|
||||
<string name="manager_download_install">לחץ להורדה והתקנה</string>
|
||||
<string name="download_zip_only">הורד ZIP בלבד</string>
|
||||
<string name="direct_install">התקנה ישירה (מומלץ)</string>
|
||||
@@ -55,7 +56,8 @@
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">בקשות משתמש על</string>
|
||||
<string name="deny">דחה</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>
|
||||
@@ -93,7 +95,7 @@
|
||||
|
||||
<!--SafetyNet-->
|
||||
<string name="safetynet_api_error">SafetyNet שגיאת API</string>
|
||||
<string name="safetynet_res_invalid">התגובה אינה חוקים</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>
|
||||
@@ -101,6 +103,7 @@
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">הצגת יישומי מערכת</string>
|
||||
<string name="show_os_app">הצגת יישומי מערכת הפעלה</string>
|
||||
<string name="hide_filter_hint">סינון לפי שם</string>
|
||||
<string name="hide_scroll_up">גלול למעלה</string>
|
||||
<string name="hide_filters">מסננים</string>
|
||||
@@ -108,6 +111,7 @@
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(לא סופק מידע)</string>
|
||||
<string name="reboot_userspace">אתחול מהיר</string>
|
||||
<string name="reboot_recovery">אתחול למצב שחזור</string>
|
||||
<string name="reboot_bootloader">אתחול מצב מנהל האתחול</string>
|
||||
<string name="reboot_download">אתחול מצב הורדה</string>
|
||||
@@ -120,8 +124,8 @@
|
||||
<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">מקוון</string>
|
||||
<string name="sorting_order">סדר מיון</string>
|
||||
<string name="external_rw_permission_denied">הענק הרשאה לאחסון כדי לאפשר פונקציונליות זו</string>
|
||||
|
||||
<!--Settings -->
|
||||
<string name="settings_dark_mode_title">מצב עיצוב</string>
|
||||
@@ -168,12 +172,15 @@
|
||||
<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_biometric_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 מורעל במדינות מסוימות</string>
|
||||
<string name="multiuser_mode">מצב מרובה משתמשים</string>
|
||||
<string name="settings_owner_only">בעל ההתקן בלבד</string>
|
||||
<string name="settings_owner_manage">אחראי ניהול ההתקן</string>
|
||||
@@ -214,8 +221,9 @@
|
||||
<string name="failure">נכשל</string>
|
||||
<string name="hide_manager_title">מסתיר את מנהל Magisk…</string>
|
||||
<string name="hide_manager_fail_toast">הסתרת מנהל Magisk כשלה.</string>
|
||||
<string name="open_link_failed_toast">לא נמצאו יישומים לפתיחת קישור זה</string>
|
||||
<string name="complete_uninstall">הוסר סהצלחה</string>
|
||||
<string name="restore_manager_fail_toast">שחזור מנהל Magisk נכשל</string>
|
||||
<string name="open_link_failed_toast">לא נמצאו יישומים לפתיחת קישור זה</string>
|
||||
<string name="complete_uninstall">הסרה מלאה</string>
|
||||
<string name="restore_img">שיחזור תמונות</string>
|
||||
<string name="restore_img_msg">משחזר…</string>
|
||||
<string name="restore_done">השיחזור בוצע!</string>
|
||||
@@ -229,5 +237,8 @@
|
||||
<string name="authenticate">אימות</string>
|
||||
<string name="unsupport_magisk_title">גרסת Magisk אינה נתמכת</string>
|
||||
<string name="unsupport_magisk_msg">גרסה זו של מנהל Magisk אינה תומכת בגרסת 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>
|
||||
|
8
app/src/main/res/values-night/styles_md2.xml
Normal file
8
app/src/main/res/values-night/styles_md2.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="SplashTheme" parent="Theme.Splash" />
|
||||
|
||||
<style name="Foundation" parent="Theme.Foundation" />
|
||||
|
||||
</resources>
|
244
app/src/main/res/values-pa/strings.xml
Normal file
244
app/src/main/res/values-pa/strings.xml
Normal file
@@ -0,0 +1,244 @@
|
||||
<resources>
|
||||
|
||||
<!--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">ਸੇਫਟੀਨੇਟ</string>
|
||||
|
||||
<!--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">N/A</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">ਸਟੱਬ</string>
|
||||
<string name="home_installed_version">ਇੰਸਟਾਲਡ</string>
|
||||
<string name="home_latest_version">ਨਵੀਨਤਮ</string>
|
||||
<string name="invalid_update_channel">ਅਵੈਧ ਅਪਡੇਟ ਚੈਨਲ</string>
|
||||
<string name="uninstall_magisk_title">ਮੈਜਿਸਕ ਨੂੰ ਅਣਇੰਸਟੌਲ ਕਰੋ</string>
|
||||
<string name="uninstall_magisk_msg">ਸਾਰੇ ਮੋਡੀਊਲ ਅਯੋਗ/ਮਿਟਾ ਦਿੱਤੇ ਜਾਣਗੇ!\n ਰੂਟ ਨੂੰ ਹਟਾਇਆ ਜਾਏਗਾ!\n ਤੁਹਾਡਾ ਡਾਟਾ ਸੰਭਾਵਤ ਤੌਰ ਤੇ ਐਨਕ੍ਰਿਪਟ ਕੀਤਾ ਗਿਆ ਹੈ ਜੇ ਪਹਿਲਾਂ ਹੀ ਨਹੀਂ!</string>
|
||||
<string name="home_check_safetynet">ਸੇਫਟੀਨੇਟ ਦੀ ਜਾਂਚ ਕਰੋ</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">ਫੋਰਸ ਇਨਕ੍ਰਿਪਸ਼ਨ ਨੂੰ ਸੁਰੱਖਿਅਤ ਰੱਖੋ</string>
|
||||
<string name="keep_dm_verity">AVB 2.0/dm-verity ਨੂੰ ਸੁਰੱਖਿਅਤ ਰੱਖੋ</string>
|
||||
<string name="recovery_mode">ਰਿਕਵਰੀ ਮੋਡ</string>
|
||||
<string name="install_options_title">ਚੋਣ</string>
|
||||
<string name="install_method_title">ਤਰੀਕਾ</string>
|
||||
<string name="install_next">ਅੱਗੇ</string>
|
||||
<string name="install_start">ਤਾਂ ਆਓ ਸ਼ੁਰੂ ਕਰੀਏ!</string>
|
||||
<string name="manager_download_install">ਡਾਊਨਲੋਡ ਅਤੇ ਇੰਸਟੌਲ ਕਰਨ ਲਈ ਦਬਾਓ</string>
|
||||
<string name="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>
|
||||
<string name="setup_title">ਅਤਿਰਿਕਤ ਸੈਟਅਪ</string>
|
||||
<string name="select_patch_file">ਇੱਕ ਫਾਈਲ ਚੁਣੋ ਅਤੇ ਪੈਚ ਕਰੋ</string>
|
||||
<string name="patch_file_msg">ਇੱਕ ਰੋ ਛੱਞੀ ਨੂੰ (* .img) ਜਾਂ ਇੱਕ Odin tarfile (* .tar) ਦੀ ਚੋਣ ਕਰੋ</string>
|
||||
<string name="reboot_delay_toast">5 ਸਕਿੰਟਾਂ ਵਿੱਚ ਰੀਬੂਟ ਹੋ ਰਿਹਾ ਹੈ …</string>
|
||||
<string name="flash_screen_title">ਇੰਸਟਾਲੇਸ਼ਨ</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">ਸੁਪਰਯੂਜ਼ਰ ਮੰਗ</string>
|
||||
<string name="deny">ਇਨਕਾਰ ਕਰੋ</string>
|
||||
<string name="prompt">ਸੰਕੇਤ ਦਿਖਾਓ</string>
|
||||
<string name="grant">ਆਗਿਆ ਦਿਓ</string>
|
||||
<string name="su_warning">ਇਹ ਤੁਹਾਡੀ ਡਿਵਾਈਸ ਤੇ ਪੂਰੀ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦੇਵੇਗਾ,\n ਜੇ ਤੁਹਾਨੂੰ ਯਕੀਨ ਨਹੀਂ ਹੈ ਤਾਂ ਇਨਕਾਰ ਕਰੋ!</string>
|
||||
<string name="forever">ਹਮੇਸ਼ਾ</string>
|
||||
<string name="once">ਇਕ ਵਾਰ</string>
|
||||
<string name="tenmin">10 ਮਿੰਟ</string>
|
||||
<string name="twentymin">20 ਮਿੰਟ</string>
|
||||
<string name="thirtymin">30 ਮਿੰਟ</string>
|
||||
<string name="sixtymin">60 ਮਿੰਟ</string>
|
||||
<string name="su_allow_toast"> %1$s ਨੂੰ ਸੁਪਰਯੂਜ਼ਰ ਅਧਿਕਾਰ ਦਿੱਤੇ ਗਏ ਸਨ</string>
|
||||
<string name="su_deny_toast"> %1$s ਸੁਪਰਯੂਜ਼ਰ ਅਧਿਕਾਰਾਂ ਤੋਂ ਇਨਕਾਰ ਕੀਤਾ ਗਿਆ ਸੀ</string>
|
||||
<string name="su_snack_grant"> %1$s ਨੂੰ ਸੁਪਰਯੂਜ਼ਰ ਅਧਿਕਾਰ ਦਿੱਤੇ ਗਏ ਸਨ</string>
|
||||
<string name="su_snack_deny"> %1$s ਸੁਪਰ ਯੂਜ਼ਰ ਅਧਿਕਾਰਾਂ ਤੋਂ ਇਨਕਾਰ ਕੀਤਾ ਗਿਆ ਹੈ</string>
|
||||
<string name="su_snack_notif_on"> %1$s ਦੀਆਂ ਸੂਚਨਾਵਾਂ ਯੋਗ ਹਨ</string>
|
||||
<string name="su_snack_notif_off"> %1$s ਦੀਆਂ ਸੂਚਨਾਵਾਂ ਅਯੋਗ ਹਨ</string>
|
||||
<string name="su_snack_log_on"> %1$s ਦੀਆਂ ਲੌਗਿੰਗ ਯੋਗ ਹਨ</string>
|
||||
<string name="su_snack_log_off"> %1$s ਦੀਆਂ ਲੌਗਿੰਗ ਅਯੋਗ ਹਨ</string>
|
||||
<string name="su_revoke_title">ਅਧਿਕਾਰ ਵਾਪਸ ਲਓ?</string>
|
||||
<string name="su_revoke_msg"> %1$s ਦੇ ਅਧਿਕਾਰਾਂ ਨੂੰ ਰੱਦ ਕਰਨ ਦੀ ਪੁਸ਼ਟੀ ਕਰਦੇ ਹੋ?</string>
|
||||
<string name="toast">ਪੌਪ-ਅੱਪ ਸੂਚਨਾ</string>
|
||||
<string name="none">ਕੋਈ ਨਹੀਂ</string>
|
||||
|
||||
<string name="superuser_toggle_notification">ਸੂਚਨਾ</string>
|
||||
<string name="superuser_toggle_revoke">ਅਧਿਕਾਰ ਵਾਪਸ ਲਓ</string>
|
||||
<string name="superuser_policy_none">ਅਜੇ ਤੱਕ ਕਿਸੇ ਵੀ ਐਪ ਨੇ ਸੁਪਰਯੂਜ਼ਰ ਲਈ ਆਗਿਆ ਨਹੀਂ ਮੰਗੀ ਹੈ।</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">ਕੋਈ ਲੌਗਸ ਨਹੀਂ, ਆਪਣੀ ਸੁਪਰਯੂਜ਼ਰ ਸਮਰਥਿਤ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਹੋਰ ਵਰਤਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।</string>
|
||||
<string name="log_data_magisk_none">ਮੈਜਿਸਕ ਲੌਗਸ ਖਾਲੀ ਹਨ, ਇਹ ਅਜੀਬ ਹੈ।</string>
|
||||
<string name="menuSaveLog">ਲੌਗ ਸੇਵ ਕਰੋ</string>
|
||||
<string name="menuClearLog">ਲੌਗ ਸਾਫ ਕਰੋ</string>
|
||||
<string name="logs_cleared">ਲੌਗ ਸਫਲਤਾਪੂਰਕ ਸਾਫ ਹੋ ਗਏ ਹਨ।</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">ਟੀਚਾ UID: %1$d</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
<string name="safetynet_api_error">ਸੇਫਟੀਨੇਟ ਏਪੀਆਈ ਗਲਤੀ</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">ਸਾਫਟ ਰੀਬੂਟ</string>
|
||||
<string name="reboot_recovery">ਰਿਕਵਰੀ ਰੀਬੂਟ</string>
|
||||
<string name="reboot_bootloader">ਬੂਟਲੋਡਰ ਰੀਬੂਟ</string>
|
||||
<string name="reboot_download">ਡਾਊਨਲੋਡ ਮੋਡ ਰੀਬੂਟ</string>
|
||||
<string name="reboot_edl">EDL ਰੀਬੂਟ</string>
|
||||
<string name="module_version_author"> %2$s ਦੇ ਦੁਆਰਾ %1$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>
|
||||
<string name="module_section_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">ਹਮੇਸ਼ਾਂ ਚਿੱਟੇ ਰੰਗ ਦੀ</string>
|
||||
<string name="settings_dark_mode_system">ਸਿਸਟਮ ਦੇ ਅਨੁਸਾਰ</string>
|
||||
<string name="settings_dark_mode_dark">ਹਮੇਸ਼ਾ ਗਹਿਰੇ ਰੰਗ ਦੀ</string>
|
||||
<string name="settings_download_path_title">ਡਾਊਨਲੋਡ ਮਾਰਗ</string>
|
||||
<string name="settings_download_path_message">ਫਾਈਲਾਂ ਨੂੰ %1$s ਵਿੱਚ ਸੇਵ ਕੀਤਾ ਜਾਏਗਾ</string>
|
||||
<string name="settings_clear_cache_title">ਰੈਪੋ ਕੈਚੇ ਸਾਫ਼ ਕਰੋ</string>
|
||||
<string name="settings_clear_cache_summary">ਆਨਲਾਈਨਨ ਰਿਪੋਜ਼ ਲਈ ਕੈਸ਼ ਕੀਤੀ ਜਾਣਕਾਰੀ ਸਾਫ ਕਰੋ। ਇਹ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਆਨਲਾਈਨ ਤਾਜ਼ਾ ਕਰਨ ਲਈ ਮਜਬੂਰ ਕਰਦਾ ਹੈ।</string>
|
||||
<string name="settings_hide_manager_title">ਮੈਜਿਸਕ ਮੈਨੇਜਰ ਓਹਲੇ ਕਰੋ</string>
|
||||
<string name="settings_hide_manager_summary">ਬੇਤਰਤੀਬੇ ਪੈਕੇਜ ਅਤੇ ਐਪ ਦੇ ਨਾਮਾਂ ਨਾਲ ਮੈਜਿਸਕ ਮੈਨੇਜਰ ਨੂੰ ਰਿ੫ੇਕੇਜ ਕਰੋ</string>
|
||||
<string name="settings_restore_manager_title">ਮੈਜਿਸਕ ਮੈਨੇਜਰ ਨੂੰ ਰੀਸਟੋਰ ਕਰੋ</string>
|
||||
<string name="settings_restore_manager_summary">ਅਸਲੀ ਪੈਕੇਜ ਅਤੇ ਐਪਲੀਕੇਸ਼ ਦੇ ਨਾਮ ਨਾਲ ਮੈਜਿਸਕ ਮੈਨੇਜਰ ਨੂੰ ਰੀਸਟੋਰ ਕਰੋ</string>
|
||||
<string name="language">ਭਾਸ਼ਾ</string>
|
||||
<string name="system_default">(ਸਿਸਟਮ ਡਿਫੌਲਟ)</string>
|
||||
<string name="settings_check_update_title">ਚੈੱਕ ਅੱਪਡੇਟ</string>
|
||||
<string name="settings_check_update_summary">ਸਮੇਂ-ਸਮੇਂ \'ਤੇ ਪਿਛੋਕੜ ਦੇ ਅਪਡੇਟਾਂ ਦੀ ਜਾਂਚ ਕਰੋ</string>
|
||||
<string name="settings_update_channel_title">ਅਪਡੇਟ ਚੈਨਲ</string>
|
||||
<string name="settings_update_stable">ਸਥਿਰ</string>
|
||||
<string name="settings_update_beta">ਬੀਟਾ</string>
|
||||
<string name="settings_update_custom">ਕਸਟਮ ਚੈਨਲ</string>
|
||||
<string name="settings_update_custom_msg">ਇੱਕ ਕਸਟਮ URL ਦਾਖਲ ਕਰੋ</string>
|
||||
<string name="settings_magiskhide_summary">ਵੱਖ ਵੱਖ ਕਿਸਮਾਂ ਦੀਆਂ ਖੋਜਾਂ ਤੋਂ ਬਚਣ ਲਈ ਮੈਜਿਸਕ ਨੂੰ ਓਹਲੇ ਕਰੋ</string>
|
||||
<string name="settings_hosts_title">Systemless ਹੋਸਟ</string>
|
||||
<string name="settings_hosts_summary">Adblock ਐਪਸ ਲਈ Systemless ਹੋਸਟ ਦੀ ਸਹਿਯੋਗ</string>
|
||||
<string name="settings_hosts_toast">Systemless ਹੋਸਟ ਮੋਡੀਊਲ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ</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">ਐਪਸ ਅਤੇ ਐਡਬੀ</string>
|
||||
<string name="settings_su_app">ਸਿਰਫ ਐਪਸ</string>
|
||||
<string name="settings_su_adb">ਸਿਰਫ ਐਡਬੀ</string>
|
||||
<string name="settings_su_disable">ਬੰਦ ਹੈ</string>
|
||||
<string name="settings_su_request_10">10 ਸਕਿੰਟ</string>
|
||||
<string name="settings_su_request_15">15 ਸਕਿੰਟ</string>
|
||||
<string name="settings_su_request_20">20 ਸਕਿੰਟ</string>
|
||||
<string name="settings_su_request_30">30 ਸਕਿੰਟ</string>
|
||||
<string name="settings_su_request_45">45 ਸਕਿੰਟ</string>
|
||||
<string name="settings_su_request_60">60 ਸਕਿੰਟ</string>
|
||||
<string name="superuser_access">ਸੁਪਰਯੂਜ਼ਰ ਐਕਸੈਸ</string>
|
||||
<string name="auto_response">ਆਟੋਮੈਟਿਕ ਜਵਾਬ</string>
|
||||
<string name="request_timeout">ਬੇਨਤੀ ਦਾ ਸਮਾਂ ਸਮਾਪਤ</string>
|
||||
<string name="superuser_notification">ਸੁਪਰਯੂਜ਼ਰ ਸੂਚਨਾ</string>
|
||||
<string name="settings_su_reauth_title">ਅਪਗ੍ਰੇਡ ਹੋਣ ਤੋਂ ਬਾਅਦ ਪ੍ਰਮਾਣਿਕਤਾ</string>
|
||||
<string name="settings_su_reauth_summary">ਇੱਕ ਐਪ ਦੇ ਅਪਡੇਟ ਹੋਣ ਤੋਂ ਬਾਅਦ ਸੁਪਰਯੂਜ਼ਰ ਆਗਿਆ ਪ੍ਰਮਾਣਿਤ ਕਰੋ</string>
|
||||
<string name="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 ਵਿਸ਼ਾਕਤਤਾ ਦਾ ਹੱਲ</string>
|
||||
|
||||
<string name="multiuser_mode">ਮਲਟੀ ਯੂਜ਼ਰ ਮੋਡ</string>
|
||||
<string name="settings_owner_only">ਸਿਰਫ ਡਿਵਾਈਸ ਮਾਲਕ</string>
|
||||
<string name="settings_owner_manage">ਡਿਵਾਈਸ ਮਾਲਕ ਦੁਆਰਾ ਪ੍ਰਬੰਧਿਤ</string>
|
||||
<string name="settings_user_independent">ਸੁਤੰਤਰ ਉਪਭੋਗਤਾ</string>
|
||||
<string name="owner_only_summary">ਸਿਰਫ ਮਾਲਕ ਕੋਲ ਰੂਟ ਐਕਸੈਸ ਹੈ</string>
|
||||
<string name="owner_manage_summary">ਸਿਰਫ ਮਾਲਕ ਰੂਟ ਐਕਸੈਸ ਦਾ ਪ੍ਰਬੰਧ ਕਰ ਸਕਦੇ ਹਨ ਅਤੇ ਬੇਨਤੀ ਸਿਗਨਲ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੇ ਹਨ</string>
|
||||
<string name="user_indepenent_summary">ਹਰੇਕ ਉਪਭੋਗਤਾ ਦਾ ਆਪਣਾ ਵੱਖਰਾ ਰੂਟ ਨਿਯਮ ਹੁੰਦਾ ਹੈ</string>
|
||||
|
||||
<string name="mount_namespace_mode">ਮਾਉਂਟ ਨੇਮਸਪੇਸ ਮੋਡ</string>
|
||||
<string name="settings_ns_global">ਗਲੋਬਲ ਨੇਮਸਪੇਸ</string>
|
||||
<string name="settings_ns_requester">ਇਨਹੇਰਟ ਨੇਮਸਪੇਸ</string>
|
||||
<string name="settings_ns_isolate">ਆਈਸੋਲੇਟਿਡ ਨੇਮਸਪੇਸ</string>
|
||||
<string name="global_summary">ਸਾਰੇ ਰੂਟ ਸੈਸ਼ਨ ਗਲੋਬਲ ਨੇਮਸਪੇਸ ਦੀ ਵਰਤੋਂ ਕਰਦੇ ਹਨ</string>
|
||||
<string name="requester_summary">ਰੂਟ ਸੈਸ਼ਨ ਨਾਮਸਪੇਸ ਬੇਨਤੀਆਂ ਨੂੰ ਪ੍ਰਾਪਤ ਕਰਨਗੇ</string>
|
||||
<string name="isolate_summary">ਹਰੇਕ ਰੂਟ ਸੈਸ਼ਨ ਦਾ ਆਪਣਾ ਵੱਖਰਾ ਨਾਮਸਪੇਸ ਹੋਵੇਗਾ</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">ਮੈਜਿਸਕ ਅਪਡੇਟ</string>
|
||||
<string name="progress_channel">ਤਰੱਕੀ ਦੀਆਂ ਸੂਚਨਾਵਾਂ</string>
|
||||
<string name="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">ਮੈਜਿਸਕ ਮੈਨੇਜਰ ਲਈ ਅਪਡੇਟ ਉਪਲੱਬਧ ਹੈ!</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">ਰਿਪੋ ਕੈਚੇ ਸਾਫ਼ ਕੀਤਾ</string>
|
||||
<string name="flashing">ਫਲੈਸ਼ਿੰਗ …</string>
|
||||
<string name="done">ਸਫਲ ਹੋਇਆ!</string>
|
||||
<string name="failure">ਅਸਫਲ ਹੋਇਆ</string>
|
||||
<string name="hide_manager_title">ਮੈਜਿਸਕ ਮੈਨੇਜਰ ਨੂੰ ਲੁਕਾਇਆ ਜਾ ਰਿਹਾ ਹੈ …</string>
|
||||
<string name="hide_manager_fail_toast">ਮੈਜਿਸਕ ਮੈਨੇਜਰ ਓਹਲੇ ਕਰਨ ਵਿੱਚ ਅਸਫਲ</string>
|
||||
<string name="restore_manager_fail_toast">ਮੈਜਿਸਕ ਮੈਨੇਜਰ ਨੂੰ ਰਿਸਟੋਰ ਕਰਨਾ ਵਿਫਲ ਹੋਇਆ</string>
|
||||
<string name="open_link_failed_toast">ਲਿੰਕ ਖੋਲ੍ਹਣ ਲਈ ਕੋਈ ਐਪਲੀਕੇਸ਼ਨ ਨਹੀਂ ਮਿਲੀ</string>
|
||||
<string name="complete_uninstall">ਪੂਰੀ ਤਰ੍ਹਾਂ ਅਣਇੰਸਟੌਲ ਕਰੋ</string>
|
||||
<string name="restore_img">ਇਮੇਜ ਰਿਸਟੋਰ ਕਰੋ</string>
|
||||
<string name="restore_img_msg">ਰਿਸਟੋਰ ਹੋ ਰਿਹਾ ਹੈ …</string>
|
||||
<string name="restore_done">रिस्टोर ਸਫਲ ਹੋਇਆ!</string>
|
||||
<string name="restore_fail">ਸਟਾਕ ਬੈਕਅਪ ਮੌਜੂਦ ਨਹੀਂ ਹੈ!</string>
|
||||
<string name="proprietary_title">ਮਲਕੀਅਤ ਕੋਡ ਡਾਉਨਲੋਡ ਕਰੋ</string>
|
||||
<string name="proprietary_notice">ਮੈਜਿਸਕ ਮੈਨੇਜਰ ਇੱਕ ਮੁਫਤ ਅਤੇ ਓਪਨ ਸੋਰਸ ਐਪ ਹੈ ਜੋ ਗੂਗਲ ਦੇ ਮਲਕੀਅਤ ਸੁਰੱਖਿਆ ਕੋਡ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕਰਦਾ। ਕੀ ਤੁਸੀਂ ਸੁਰੱਖਿਆ ਨੂੰ ਵੇਖਣ ਲਈ ਮੈਸਿਕ ਮੈਨੇਜਰ ਨੂੰ ਇੱਕ ਐਕਸਟੈਂਸ਼ਨ ਡਾ ਡਾਊਨਲੋਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣਾ ਚਾਹੁੰਦੇ ਹੋ?</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>
|
||||
<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>
|
@@ -22,7 +22,7 @@
|
||||
<string name="status">Stare</string>
|
||||
<string name="home_package">Pachet</string>
|
||||
|
||||
<string name="home_notice_content">Asigură-te întotdeauna că folosești aplicația open-source Magisk Manager. Managerul din surse necunoscute poate efectua acțiuni rău-intenționate.</string>
|
||||
<string name="home_notice_content">Asigură-te întotdeauna că folosești aplicația open-source Magisk Manager. Managerul din surse necunoscute poate efectua acțiuni rău-intenționate!</string>
|
||||
<string name="home_support_title">Sprijină-ne</string>
|
||||
<string name="home_item_source">Sursă</string>
|
||||
<string name="home_support_content">Magisk este și va fi întotdeauna gratuit și open-source. Cu toate acestea, ne poți arăta că îți pasă trimițând o mică donație.</string>
|
||||
@@ -55,7 +55,8 @@
|
||||
<string name="flash_screen_title">Instalare</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Solicitare de superutilizator</string>
|
||||
<string name="su_request_title">Cerere de superutilizator</string>
|
||||
<string name="touch_filtered_warning">Deoarece o aplicație ascunde o cerere de superutilizator, Magisk nu îți poate verifica răspunsul</string>
|
||||
<string name="deny">Refuză</string>
|
||||
<string name="prompt">Solicită</string>
|
||||
<string name="grant">Acordă</string>
|
||||
@@ -81,14 +82,14 @@
|
||||
|
||||
<string name="superuser_toggle_notification">Notificări</string>
|
||||
<string name="superuser_toggle_revoke">Revocă</string>
|
||||
<string name="superuser_policy_none">Nicio aplicație nu a solicitat încă permisiuni de superutilizator.</string>
|
||||
<string name="superuser_policy_none">Nicio aplicație nu a cerut încă permisiuni de superutilizator.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Nu există jurnale, încearcă să folosești ceva mai mult aplicațiile cu acces de superutilizator.</string>
|
||||
<string name="log_data_magisk_none">Jurnalele Magisk sunt goale, asta-i ciudat.</string>
|
||||
<string name="log_data_none">Nu există jurnale, încearcă să folosești ceva mai mult aplicațiile cu acces de superutilizator</string>
|
||||
<string name="log_data_magisk_none">Jurnalele Magisk sunt goale, asta-i ciudat</string>
|
||||
<string name="menuSaveLog">Salvează jurnalul</string>
|
||||
<string name="menuClearLog">Golește jurnalul acum</string>
|
||||
<string name="logs_cleared">Jurnal golit cu succes.</string>
|
||||
<string name="logs_cleared">Jurnal golit cu succes</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">UID țintă: %1$d</string>
|
||||
|
||||
@@ -100,15 +101,17 @@
|
||||
<string name="safetynet_attest_loading">Așteaptă o clipă…</string>
|
||||
<string name="safetynet_attest_restart">Încearcă din nou</string>
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Afișează aplicațiile de sistem</string>
|
||||
<string name="show_os_app">Afișează aplicațiile SO-ului</string>
|
||||
<string name="hide_filter_hint">Filtrează după nume</string>
|
||||
<string name="hide_scroll_up">Derulează în sus</string>
|
||||
<string name="hide_filters">Filtre</string>
|
||||
<string name="hide_search">Căutare</string>
|
||||
|
||||
<!--Module -->
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Nu sunt furnizate informații)</string>
|
||||
<string name="reboot_userspace">Repornire software</string>
|
||||
<string name="reboot_recovery">Repornește în modul recovery</string>
|
||||
<string name="reboot_bootloader">Repornește în modul bootloader</string>
|
||||
<string name="reboot_download">Repornește în modul download</string>
|
||||
@@ -124,7 +127,7 @@
|
||||
<string name="module_section_online">Online</string>
|
||||
<string name="sorting_order">Ordine de sortare</string>
|
||||
|
||||
<!--Settings -->
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Mod pentru temă</string>
|
||||
<string name="settings_dark_mode_message">Selectează modul care ți se pliază cel mai bine stilului!</string>
|
||||
<string name="settings_dark_mode_light">Mereu deschis</string>
|
||||
@@ -133,7 +136,7 @@
|
||||
<string name="settings_download_path_title">Cale de descărcare</string>
|
||||
<string name="settings_download_path_message">Fișierele vor fi salvate în %1$s</string>
|
||||
<string name="settings_clear_cache_title">Golește cache-ul depozitelor</string>
|
||||
<string name="settings_clear_cache_summary">Golește informațiile memorate în cache pentru depozitele online. Acest lucru forțează actualizarea aplicației online.</string>
|
||||
<string name="settings_clear_cache_summary">Golește informațiile memorate în cache pentru depozitele online. Acest lucru forțează actualizarea aplicației online</string>
|
||||
<string name="settings_hide_manager_title">Ascunde Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Reîmpachetează Magisk Manager cu numele aleatorii pentru pachet și aplicație</string>
|
||||
<string name="settings_restore_manager_title">Restaurează Magisk Manager</string>
|
||||
@@ -166,10 +169,12 @@
|
||||
<string name="settings_su_request_60">60 de secunde</string>
|
||||
<string name="superuser_access">Acces pentru superutilizator</string>
|
||||
<string name="auto_response">Răspuns automat</string>
|
||||
<string name="request_timeout">Expirare pentru solicitare</string>
|
||||
<string name="request_timeout">Expirare pentru cerere</string>
|
||||
<string name="superuser_notification">Notificare de superutilizator</string>
|
||||
<string name="settings_su_reauth_title">Reautentificare după actualizare</string>
|
||||
<string name="settings_su_reauth_summary">Reautentifică permisiunile pentru superutilizator după o actualizare a aplicației</string>
|
||||
<string name="settings_su_tapjack_title">Activează protecția față de tapjacking</string>
|
||||
<string name="settings_su_tapjack_summary">Caseta de dialog pentru solicitarea drepturilor de superutilizator nu va răspunde la input cât timp este ascunsă de orice altă fereastră sau suprapunere</string>
|
||||
<string name="settings_su_biometric_title">Activează autentificarea biometrică</string>
|
||||
<string name="settings_su_biometric_summary">Folosește autentificarea biometrică pentru a permite cereri de superutilizator</string>
|
||||
<string name="no_biometric">Dispozitiv nesuportat sau nu sunt activate setări biometrice</string>
|
||||
@@ -183,7 +188,7 @@
|
||||
<string name="settings_owner_manage">Gestionat de proprietarul dispozitivului</string>
|
||||
<string name="settings_user_independent">Utilizator independent</string>
|
||||
<string name="owner_only_summary">Numai proprietarul are acces la root</string>
|
||||
<string name="owner_manage_summary">Numai proprietarul poate să gestioneze accesul la root și să primească solicitări</string>
|
||||
<string name="owner_manage_summary">Numai proprietarul poate să gestioneze accesul la root și să primească cereri</string>
|
||||
<string name="user_indepenent_summary">Fiecare utilizator are propriile sale reguli separate pentru root</string>
|
||||
|
||||
<string name="mount_namespace_mode">Modul spațiului de nume pentru montare</string>
|
||||
@@ -215,9 +220,10 @@
|
||||
<string name="repo_cache_cleared">Cache-ul depozitelor golit</string>
|
||||
<string name="flashing">Se scrie în memoria flash…</string>
|
||||
<string name="done">Terminat!</string>
|
||||
<string name="failure">Eșec</string>
|
||||
<string name="failure">Eșec!</string>
|
||||
<string name="hide_manager_title">Se ascunde Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">Ascunderea Magisk Manager a eșuat.</string>
|
||||
<string name="hide_manager_fail_toast">Ascunderea Magisk Manager a eșuat</string>
|
||||
<string name="restore_manager_fail_toast">Restaurarea Magisk Manager a eșuat</string>
|
||||
<string name="open_link_failed_toast">Nu a fost găsită nicio aplicație pentru a deschide linkul</string>
|
||||
<string name="complete_uninstall">Finalizează dezinstalarea</string>
|
||||
<string name="restore_img">Restaurează imagini</string>
|
||||
|
@@ -22,7 +22,7 @@
|
||||
<string name="status">Stav</string>
|
||||
<string name="home_package">Balíček</string>
|
||||
|
||||
<string name="home_notice_content">Vždy sa uistite, že používate Magisk Manager s otvoreným kódom. Správca z neznámeho zdroja môže vykonávať škodlivé akcie.</string>
|
||||
<string name="home_notice_content">Vždy sa uistite, že používate Magisk Manager s otvoreným kódom. Správca z neznámeho zdroja môže vykonávať škodlivé akcie!</string>
|
||||
<string name="home_support_title">Podporte nás</string>
|
||||
<string name="home_item_source">Zdroj</string>
|
||||
<string name="home_support_content">Magisk je a vždy bude slobodný a s otvoreným kódom. Môžete nám však ukázať, že vám na tom záleží zaslaním malého daru.</string>
|
||||
@@ -85,11 +85,11 @@
|
||||
<string name="superuser_policy_none">Žiadna aplikácia zatiaľ nepožiadala o povolenie Superužívateľa.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Nie sú žiadne záznamy, skúste nejakú appku vyžadujúcu práva Superužívateľa.</string>
|
||||
<string name="log_data_magisk_none">Záznamy Magisku sú prázdne, to je divné.</string>
|
||||
<string name="log_data_none">Nie sú žiadne záznamy, skúste nejakú appku vyžadujúcu práva Superužívateľa</string>
|
||||
<string name="log_data_magisk_none">Záznamy Magisku sú prázdne, to je divné</string>
|
||||
<string name="menuSaveLog">Uložiť záznam</string>
|
||||
<string name="menuClearLog">Odstrániť záznam</string>
|
||||
<string name="logs_cleared">Záznam úspešne odstránený.</string>
|
||||
<string name="logs_cleared">Záznam úspešne odstránený</string>
|
||||
<string name="pid">PID:%1$d</string>
|
||||
<string name="target_uid">Cieľový UID: %1$d</string>
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
<string name="safetynet_attest_loading">Momentík ...</string>
|
||||
<string name="safetynet_attest_restart">Skúste to znova</string>
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Zobraziť systémové aplikácie</string>
|
||||
<string name="show_os_app">Zobraziť aplikácie OS</string>
|
||||
<string name="hide_filter_hint">Filtrovať podľa názvu</string>
|
||||
@@ -109,7 +109,7 @@
|
||||
<string name="hide_filters">Filtre</string>
|
||||
<string name="hide_search">Vyhľadávanie</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Nie sú k dispozícii žiadne informácie)</string>
|
||||
<string name="reboot_userspace">Softvérový reštart</string>
|
||||
<string name="reboot_recovery">Reštartovať do Recovery</string>
|
||||
@@ -129,7 +129,7 @@
|
||||
|
||||
|
||||
|
||||
<!--Settings -->
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Režim motívu</string>
|
||||
<string name="settings_dark_mode_message">Vyberte režim, ktorý najlepšie vyhovuje vášmu štýlu!</string>
|
||||
<string name="settings_dark_mode_light">Vždy svetlý</string>
|
||||
@@ -222,7 +222,7 @@
|
||||
<string name="repo_cache_cleared">Cache repo odstránená</string>
|
||||
<string name="flashing">Flashovanie...</string>
|
||||
<string name="done">Hotovo!</string>
|
||||
<string name="failure">Zlyhalo</string>
|
||||
<string name="failure">Zlyhalo!</string>
|
||||
<string name="hide_manager_title">Skrývanie Magisk Managera…</string>
|
||||
<string name="hide_manager_fail_toast">Skrytie Magisk Managera zlyhalo</string>
|
||||
<string name="restore_manager_fail_toast">Obnovenie Magisk Managera zlyhalo</string>
|
||||
|
231
app/src/main/res/values-sq/strings.xml
Normal file
231
app/src/main/res/values-sq/strings.xml
Normal file
@@ -0,0 +1,231 @@
|
||||
<resources>
|
||||
|
||||
<!--Sections-->
|
||||
<string name="modules">Modulet</string>
|
||||
<string name="superuser">Superpërdoruesi</string>
|
||||
<string name="logs">Regjistrat</string>
|
||||
<string name="settings">Cilësimet</string>
|
||||
<string name="refresh">Rifresko të Dhënat Lokale</string>
|
||||
<string name="install">Instalo</string>
|
||||
<string name="section_home">Shtëpia</string>
|
||||
<string name="section_theme">Temat</string>
|
||||
<string name="safetynet">Rrjeti i Sigurisë</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">Nuk ka lidhje interneti</string>
|
||||
<string name="app_changelog">Ndryshimet</string>
|
||||
<string name="manager">Menaxheri</string>
|
||||
<string name="loading">Po ngarkon…</string>
|
||||
<string name="update">Përditëso</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Fshih</string>
|
||||
<string name="status">Statusi</string>
|
||||
<string name="home_package">Paketa</string>
|
||||
|
||||
<string name="home_notice_content">Gjithmonë sigurohuni që jeni duke përdorur Magisk Manager me burim të hapur. Magisk Manager i një burimi të panjohur mund të kryejë veprime të dëmshme!</string>
|
||||
<string name="home_support_title">Na ndihmoni</string>
|
||||
<string name="home_item_source">Burimi</string>
|
||||
<string name="home_support_content">Magisk është, dhe gjithmonë do të jetë, falas dhe me burim të hapur. Megjithatë mund të na tregoni se ju interesoni duke dërguar një donacion të vogël.</string>
|
||||
<string name="home_status_normal">Normale</string>
|
||||
<string name="home_status_stub">Stub</string>
|
||||
<string name="home_installed_version">Instaluar</string>
|
||||
<string name="home_latest_version">Më të fundit</string>
|
||||
<string name="invalid_update_channel">Kanal i pavlefshëm i azhurnimit</string>
|
||||
<string name="uninstall_magisk_title">Çinstalo Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Të gjitha modulet do të çaktivizohen/hiqen! \n Rrënja do të hiqet! \n Të dhënat tuaja potencialisht të koduara nëse jo tashmë!</string>
|
||||
<string name="home_check_safetynet">Kontrolloni Rrjetin e Sigurisë</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Ruaj kriptimin me forcë</string>
|
||||
<string name="keep_dm_verity">Ruaj AVB 2.0 /dm-vërtetësi</string>
|
||||
<string name="recovery_mode">Modaliteti i rikuperimit</string>
|
||||
<string name="install_options_title">Opsionet</string>
|
||||
<string name="install_method_title">Metoda</string>
|
||||
<string name="install_next">Tjetra</string>
|
||||
<string name="install_start">Le të shkojmë</string>
|
||||
<string name="manager_download_install">Shtypni për të shkarkuar dhe instaluar</string>
|
||||
<string name="download_zip_only">Shkarkoni vetëm Zip</string>
|
||||
<string name="direct_install">Instalimi i drejtpërdrejtë (rekomandohet)</string>
|
||||
<string name="install_inactive_slot">Instalo në Slot Inaktive (Pas OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Pajisja juaj do të detyrohet të nisë në vendndodhjen aktuale joaktive pas një rindezjeje! \n Të përdorni vetëm këtë mundësi pasi të keni mbaruar OTA. \n Vazhdoni? </string>
|
||||
<string name="setup_title">Konfigurimi shtesë</string>
|
||||
<string name="select_patch_file">Zgjidhni dhe Patch një Skedar</string>
|
||||
<string name="patch_file_msg">Zgjidhni një imazh të papërpunuar (* .img) ose një tarifë ODIN (* .tar)</string>
|
||||
<string name="reboot_delay_toast">Rinisje pas 5 sekonda…</string>
|
||||
<string name="flash_screen_title">Instalimi</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="touch_filtered_warning">Meqenëse një aplikacion po errëson një kërkesë të superpërdoruesit, Magisk nuk mund të verifikojë përgjigjen tuaj</string>
|
||||
<string name="deny">Moho</string>
|
||||
<string name="prompt">Pyet</string>
|
||||
<string name="grant">Lejo</string>
|
||||
<string name="su_warning">Jep akses të plotë në pajisjen tuaj. \n Moho nëse nuk je i sigurt!</string>
|
||||
<string name="forever">Përgjithmonë</string>
|
||||
<string name="once">Një herë</string>
|
||||
<string name="tenmin">10 minuta</string>
|
||||
<string name="twentymin">20 minuta</string>
|
||||
<string name="thirtymin">30 minuta</string>
|
||||
<string name="sixtymin">60 minuta</string>
|
||||
<string name="su_allow_toast">%1$s iu dha e drejta e superpërdoruesit</string>
|
||||
<string name="su_deny_toast">%1$s iu refuzuan të drejtat e Superpërdoruesit</string>
|
||||
<string name="su_snack_grant">Jep të drejtën e superpërdoruesit prej %1$s</string>
|
||||
<string name="su_snack_deny">Të drejtat e superpërdoruesit prej %1$s mohohen</string>
|
||||
<string name="su_snack_notif_on">Njoftimet e %1$s janë aktivizuar</string>
|
||||
<string name="su_snack_notif_off">Njoftimet e %1$s janë çaktivizuar</string>
|
||||
<string name="su_snack_log_on">Regjistrimi i %1$s është aktivizuar</string>
|
||||
<string name="su_snack_log_off">Regjistrimi i %1$s është çaktivizuar</string>
|
||||
<string name="su_revoke_title">Anulohet?</string>
|
||||
<string name="su_revoke_msg">Konfirmo të heqësh të drejtat e %1$s?</string>
|
||||
<string name="toast">dolli</string>
|
||||
<string name="superuser_toggle_notification">Njoftimet</string>
|
||||
<string name="superuser_toggle_revoke">Anulo</string>
|
||||
<string name="superuser_policy_none">Asnjë aplikacion nuk ka kërkuar ende leje për superpërdoruesin.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Ju jeni pa regjistrime, provoni të përdorni më shumë aplikacionet tuaja të aktivizuara SU</string>
|
||||
<string name="log_data_magisk_none">Regjistrat e Magisk janë bosh, kjo është e çuditshme</string>
|
||||
<string name="menuSaveLog">Ruaj regjistrin</string>
|
||||
<string name="menuClearLog">Pastro regjistrin tani</string>
|
||||
<string name="logs_cleared">Regjistri u pastrua me sukses</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">UID i synuar: %1$d</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
<string name="safetynet_api_error">Gabim API i SafetyNet</string>
|
||||
<string name="safetynet_res_invalid">Përgjigjja është e pavlefshme </string>
|
||||
<string name="safetynet_attest_success">Suksese!</string>
|
||||
<string name="safetynet_attest_failure">Vërtetimi dështoi!</string>
|
||||
<string name="safetynet_attest_loading">Thjesht një sekondë…</string>
|
||||
<string name="safetynet_attest_restart">Provo përsëri</string>
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Shfaq aplikacionet e sistemit</string>
|
||||
<string name="show_os_app">Shfaq aplikacionet e sistemit operativ</string>
|
||||
<string name="hide_filter_hint">Filtro me emër</string>
|
||||
<string name="hide_scroll_up">Lëviz lart</string>
|
||||
<string name="hide_filters">Filtrat</string>
|
||||
<string name="hide_search">Kërko</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Nuk ofrohet informacion)</string>
|
||||
<string name="reboot_userspace">Ristartim normal</string>
|
||||
<string name="reboot_recovery">Ristartoni në recovery</string>
|
||||
<string name="reboot_bootloader">Ristartoni në bootloader</string>
|
||||
<string name="reboot_download">Ristartoni për në download</string>
|
||||
<string name="reboot_edl">Ristartoni në EDL</string>
|
||||
<string name="module_version_author">%1$s nga %2$s</string>
|
||||
<string name="module_state_remove">Hiq</string>
|
||||
<string name="module_state_restore">Rivendos</string>
|
||||
<string name="module_action_install_external">Instaloni nga hapësira ruajtëse</string>
|
||||
<string name="update_available">Përditësimi i disponueshëm</string>
|
||||
<string name="module_installed">@string/home_installed_version</string>
|
||||
<string name="module_section_online">Në internet</string>
|
||||
<string name="sorting_order">Renditja e Renditjes</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Modaliteti i temës</string>
|
||||
<string name="settings_dark_mode_message">Zgjidhni mënyrën e cila i përshtatet më mirë stilit tuaj!</string>
|
||||
<string name="settings_dark_mode_light">Gjithmonë e lehtë</string>
|
||||
<string name="settings_dark_mode_system">Ndiqni sistemin</string>
|
||||
<string name="settings_dark_mode_dark">Gjithmonë e errët</string>
|
||||
<string name="settings_download_path_title">Shtegu i shkarkimit</string>
|
||||
<string name="settings_download_path_message">Skedarët do të ruhen në %1$s</string>
|
||||
<string name="settings_clear_cache_title">Pastro Cache të Repos</string>
|
||||
<string name="settings_clear_cache_summary">Pastroni informacionin e memorizuar për repot në internet. Kjo e detyron aplikacionin të rifreskohet në internet</string>
|
||||
<string name="settings_hide_manager_title">Fshih menaxherin e Magisk</string>
|
||||
<string name="settings_hide_manager_summary">Ripaketimi Magisk Manager me paketa të rastësishme dhe emra të aplikacioneve</string>
|
||||
<string name="settings_restore_manager_title">Rivendos menaxherin e Magisk</string>
|
||||
<string name="settings_restore_manager_summary">Rivendos Magisk Manager me paketën origjinale dhe emrat e aplikacioneve</string>
|
||||
<string name="language">Gjuha</string>
|
||||
<string name="system_default">(Parazgjedhja e Sistemit)</string>
|
||||
<string name="settings_check_update_title">Kontrolloni azhurnimet</string>
|
||||
<string name="settings_check_update_summary">Kontrolloni periodikisht për azhurnime në sfond</string>
|
||||
<string name="settings_update_channel_title">Azhurnoni Kanalin</string>
|
||||
<string name="settings_update_stable">Qëndrueshëm</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_custom">Kanali i personalizuar</string>
|
||||
<string name="settings_update_custom_msg">Vendos një URL të personalizuar</string>
|
||||
<string name="settings_magiskhide_summary">Fshih Magisk nga forma të ndryshme të zbulimit</string>
|
||||
<string name="settings_hosts_title">Pritës pa sistem</string>
|
||||
<string name="settings_hosts_summary">Mbështetja e hostëve pa sistem për aplikacionet Adblock</string>
|
||||
<string name="settings_hosts_toast">Moduli i pritur i sistemit pa sistem</string>
|
||||
<string name="settings_app_name_hint">Emër i ri</string>
|
||||
<string name="settings_app_name_helper">Aplikacioni do të ripaketohet në këtë emër</string>
|
||||
<string name="settings_app_name_error">Formati i pavlefshëm</string>
|
||||
<string name="settings_su_app_adb">Aplikacionet dhe ADB</string>
|
||||
<string name="settings_su_app">Vetëm aplikacionet</string>
|
||||
<string name="settings_su_adb">Vetëm ADB</string>
|
||||
<string name="settings_su_disable">Çaktivizuar</string>
|
||||
<string name="superuser_access">Hyrja në superpërdorues</string>
|
||||
<string name="auto_response">Përgjigje Automatike</string>
|
||||
<string name="request_timeout">Koha e kërkesës</string>
|
||||
<string name="superuser_notification">Njoftimi i superpërdoruesit</string>
|
||||
<string name="settings_su_reauth_title">Ridentifikoni pas azhurnimit</string>
|
||||
<string name="settings_su_reauth_summary">Ridentifikoni lejet e superpërdoruesit pas azhurnimeve të një aplikacioni</string>
|
||||
<string name="settings_su_tapjack_title">Aktivizo Mbrojtjen nga Tapjacking</string>
|
||||
<string name="settings_su_tapjack_summary">Dialogu i menjëhershëm i superpërdoruesit nuk do ti përgjigjet hyrjes ndërsa errësohet nga ndonjë dritare tjetër ose mbivendosja</string>
|
||||
<string name="settings_su_biometric_title">Aktivizo vërtetimin biometrik</string>
|
||||
<string name="settings_su_biometric_summary">Përdorni vërtetimin biometrik për të lejuar kërkesat e superpërdoruesit</string>
|
||||
<string name="no_biometric">Pajisja e pambështetur ose nuk janë aktivizuar cilësime biometrike</string>
|
||||
<string name="settings_customization">Përshtatje</string>
|
||||
<string name="setting_add_shortcut_summary">"Shtoni një shkurtore të bukur në ekranin kryesor në rast se emri dhe ikona janë të vështira për t'u njohur pasi të keni fshehur aplikacionin"</string>
|
||||
<string name="settings_doh_title">DNS mbi HTTPS</string>
|
||||
<string name="settings_doh_description">Helmimi i paqartë nga DNS në disa kombe</string>
|
||||
<string name="multiuser_mode">Modaliteti i shumë përdoruesit</string>
|
||||
<string name="settings_owner_only">Vetëm zotëruesi i pajisjes</string>
|
||||
<string name="settings_owner_manage">Pronari i Pajisjes i Menaxhuar</string>
|
||||
<string name="settings_user_independent">Përdorues i Pavarur</string>
|
||||
<string name="user_indepenent_summary">Çdo përdorues ka rregullat e tij/saj të veçanta rrënjësore</string>
|
||||
<string name="mount_namespace_mode">Modaliteti i hapësirës së emrave të malit</string>
|
||||
<string name="settings_ns_global">Hapësira Globale e Emrave</string>
|
||||
<string name="settings_ns_requester">Trashëgoni Hapësirën e Emrave</string>
|
||||
<string name="settings_ns_isolate">Hapësira e izoluar e emrave</string>
|
||||
<string name="global_summary">Të gjitha sesionet rrënjësore përdorin hapësirën globale të emrave të montimit</string>
|
||||
<string name="requester_summary">Seancat rrënjësore do të trashëgojnë hapësirën e emrave të kërkuesit të tyre</string>
|
||||
<string name="isolate_summary">Çdo sesion rrënjë do të ketë hapësirën e vet të izoluar të tij</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Përditësimet e Magisk</string>
|
||||
<string name="progress_channel">Njoftimet e Progresit</string>
|
||||
<string name="download_complete">Shkarkimi përfundoi</string>
|
||||
<string name="download_file_error">Gabim në shkarkimin e skedarit</string>
|
||||
<string name="download_open_parent">Shfaq në dosjen mëmë</string>
|
||||
<string name="magisk_update_title">Mundësohet azhurnimi i Magisk!</string>
|
||||
<string name="manager_update_title">Ka perditësime të ri të Magisk Manager!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Po</string>
|
||||
<string name="no">Jo</string>
|
||||
<string name="repo_install_title">Instalo %1$s</string>
|
||||
<string name="repo_install_msg">Dëshiron të instalosh %1$s tani?</string>
|
||||
<string name="download">Shkarkim</string>
|
||||
<string name="reboot">Rinis</string>
|
||||
<string name="release_notes">Lëshoni shënime</string>
|
||||
<string name="repo_cache_cleared">Depoja e repos u pastrua</string>
|
||||
<string name="flashing">Ndezje…</string>
|
||||
<string name="done">U krye!</string>
|
||||
<string name="failure">Dështoi!</string>
|
||||
<string name="hide_manager_title">Fshehja e Magisk manager…</string>
|
||||
<string name="hide_manager_fail_toast">Fsheh Menaxherin e Magisk dështoi</string>
|
||||
<string name="restore_manager_fail_toast">Rikthe menaxherin e Magisk dështoi</string>
|
||||
<string name="open_link_failed_toast">Asnjë aplikacion nuk u gjet për të hapur lidhjen</string>
|
||||
<string name="complete_uninstall">Çinstalimi i plotë</string>
|
||||
<string name="restore_img">Rikthe Imazhet</string>
|
||||
<string name="restore_img_msg">Rivendosja…</string>
|
||||
<string name="restore_done">Restaurimi u krye!</string>
|
||||
<string name="restore_fail">Rezervimi i aksioneve nuk ekziston!</string>
|
||||
<string name="proprietary_title">Shkarkoni Kodin e Pronarit</string>
|
||||
<string name="proprietary_notice">Magisk Manager është FOSS dhe nuk përmban kodin e pronarit të Google SecurityNet API. \n\n A do të lejoni që Magisk Manager të shkarkojë një shtesë (përmban GoogleApiClient) për kontrollet e SafetyNet?</string>
|
||||
<string name="setup_fail">Konfigurimi dështoi</string>
|
||||
<string name="env_fix_title">Kërkon Konfigurim Shtesë</string>
|
||||
<string name="env_fix_msg">Pajisja juaj ka nevojë për konfigurim shtesë që Magisk të funksionojë siç duhet. Do të shkarkojë zip-in e konfigurimit të Magisk, doni të vazhdoni tani?</string>
|
||||
<string name="setup_msg">Konfigurimi i mjedisit drejtues…</string>
|
||||
<string name="authenticate">Vërteto</string>
|
||||
<string name="unsupport_magisk_title">Versioni i pambështetur i Magisk</string>
|
||||
<string name="unsupport_magisk_msg">Ky version i Magisk Manager nuk e mbështet versionin Magisk më të ulët se %1$s. \n\n Aplikimi do të sillet sikur nuk është instaluar asnjë Magisk, ju lutemi azhurnoni Magisk sa më shpejt të jetë e mundur.</string>
|
||||
<string name="external_rw_permission_denied">Jep leje për ruajtje për të mundësuar këtë funksionalitet</string>
|
||||
<string name="add_shortcut_title">Shto shkurtore në ekranin kryesor</string>
|
||||
<string name="add_shortcut_msg">Pasi të keni fshehur Magisk Manager, emri dhe ikona e tij mund të bëhen të vështira për t\'u njohur. \n\n Dëshironi të shtoni një shkurtore të bukur në ekranin kryesor?</string>
|
||||
<string name="app_not_found">Asnjë aplikacion nuk u gjet për të trajtuar këtë veprim</string>
|
||||
|
||||
</resources>
|
@@ -1,11 +1,11 @@
|
||||
<resources>
|
||||
|
||||
<!--Welcome Activity-->
|
||||
<!--Sections-->
|
||||
<string name="modules">Modüller</string>
|
||||
<string name="superuser">Yetkili Kullanıcı</string>
|
||||
<string name="logs">Günlük</string>
|
||||
<string name="settings">Ayarlar</string>
|
||||
<string name="refresh">Yerel Verileri Yenile</string>
|
||||
<string name="refresh">Yerel verileri yenile</string>
|
||||
<string name="install">Yükle</string>
|
||||
<string name="section_home">Ana Ekran</string>
|
||||
<string name="section_theme">Temalar</string>
|
||||
@@ -14,7 +14,7 @@
|
||||
<!--Home-->
|
||||
<string name="no_connection">Bağlantı yok</string>
|
||||
<string name="app_changelog">Değişiklikler</string>
|
||||
<string name="manager">Yönetici</string>
|
||||
<string name="manager">Manager</string>
|
||||
<string name="loading">Yükleniyor…</string>
|
||||
<string name="update">Güncelle</string>
|
||||
<string name="not_available">N/A</string>
|
||||
@@ -22,13 +22,13 @@
|
||||
<string name="status">Durum</string>
|
||||
<string name="home_package">Paket</string>
|
||||
|
||||
<string name="home_notice_content">Her zaman açık kaynaklı Magisk Manager kullandığınızdan emin olun. Kaynağı bilinmeyen yönetici uygulamaları kötü amaçlı eylemler gerçekleştirebilir.</string>
|
||||
<string name="home_notice_content">Her zaman açık kaynaklı Magisk Manager kullandığınızdan emin olun. Kaynağı bilinmeyen Manager(yönetici) uygulamaları kötü amaçlı eylemler gerçekleştirebilirler!</string>
|
||||
<string name="home_support_title">Bizi Destekleyin</string>
|
||||
<string name="home_item_source">Kaynak</string>
|
||||
<string name="home_support_content">Magisk özgür ve açık kaynaklıdır ve her zaman da öyle kalacak. Ancak küçük bir bağış göndererek bize önem verdiğinizi gösterebilirsiniz.</string>
|
||||
<string name="home_support_content">Magisk özgür ve açık kaynaklıdır ve her zaman da öyle kalacaktır. Ancak küçük bir bağış göndererek bize önem verdiğinizi gösterebilirsiniz.</string>
|
||||
<string name="home_status_normal">Normal</string>
|
||||
<string name="home_status_stub">Stub</string>
|
||||
<string name="home_installed_version">Yüklendi</string>
|
||||
<string name="home_installed_version">Yüklü</string>
|
||||
<string name="home_latest_version">Güncel</string>
|
||||
<string name="invalid_update_channel">Geçersiz Güncelleme Kanalı</string>
|
||||
<string name="uninstall_magisk_title">Magisk\'i kaldır</string>
|
||||
@@ -36,7 +36,7 @@
|
||||
<string name="home_check_safetynet">SafetyNet\'i kontrol et</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Şifrelemeyi zorlamayı sürdür</string>
|
||||
<string name="keep_force_encryption">Şifrelemeye zorlamayı sürdür</string>
|
||||
<string name="keep_dm_verity">AVB 2.0/dm-verity\'i koru</string>
|
||||
<string name="recovery_mode">Kurtarma Modu</string>
|
||||
<string name="install_options_title">Seçenekler</string>
|
||||
@@ -47,7 +47,7 @@
|
||||
<string name="download_zip_only">Sadece Zip\'i indir</string>
|
||||
<string name="direct_install">Doğrudan kurulum (Önerilen)</string>
|
||||
<string name="install_inactive_slot">Aktif olmayan yuvaya yükle (OTA\'dan sonra)</string>
|
||||
<string name="install_inactive_slot_msg">Cihazınız, yeniden başlatmanın ardından geçerli etkin olmayan yuvaya önyüklemeye ZORLANACAK!\nBu seçeneği yalnızca OTA bittikten sonra kullanın.\nDevam edilsin mi?</string>
|
||||
<string name="install_inactive_slot_msg">Cihazınız, yeniden başlatmanın ardından geçerli etkin olmayan yuvaya ön yüklemeye ZORLANACAK!\nBu seçeneği yalnızca OTA bittikten sonra kullanın.\nDevam edilsin mi?</string>
|
||||
<string name="setup_title">Ek kurulum</string>
|
||||
<string name="select_patch_file">Dosya seçin ve yamalayın</string>
|
||||
<string name="patch_file_msg">Bir raw imaj (*.img) veya ODIN tar dosyası (*.tar) seçin</string>
|
||||
@@ -56,12 +56,13 @@
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Yetkili Kullanıcı İsteği</string>
|
||||
<string name="touch_filtered_warning">Çünkü bir uygulama yetkili kullanıcı isteğini engelliyor, Magisk senin yanıtını doğrulayamaz</string>
|
||||
<string name="deny">Reddet</string>
|
||||
<string name="prompt">Sor</string>
|
||||
<string name="grant">İzin ver</string>
|
||||
<string name="su_warning">Cihazınıza tam erişim izni verir.\nEmin değilseniz reddedin!</string>
|
||||
<string name="forever">Daima</string>
|
||||
<string name="once">Bir kere</string>
|
||||
<string name="once">Bir kereliğine</string>
|
||||
<string name="tenmin">10 dakika</string>
|
||||
<string name="twentymin">20 dakika</string>
|
||||
<string name="thirtymin">30 dakika</string>
|
||||
@@ -81,14 +82,14 @@
|
||||
|
||||
<string name="superuser_toggle_notification">Bildirimler</string>
|
||||
<string name="superuser_toggle_revoke">İptal et</string>
|
||||
<string name="superuser_policy_none">Henüz hiçbir uygulama süper kullanıcı izni istemedi.</string>
|
||||
<string name="superuser_policy_none">Henüz hiçbir uygulama yetkili kullanıcı izni istemedi.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Günlük boş, daha fazla uygulama için SU etkinleştirmeyi deneyin.</string>
|
||||
<string name="log_data_magisk_none">Magisk günlükleri boş, bu garip.</string>
|
||||
<string name="log_data_none">Günlüksüzsün, daha fazla Yetkili Kullanıcı İzni isteyen uygulama kullanmayı dene</string>
|
||||
<string name="log_data_magisk_none">Magisk günlükleri boş, bu garip</string>
|
||||
<string name="menuSaveLog">Günlüğü kaydet</string>
|
||||
<string name="menuClearLog">Günlüğü temizle</string>
|
||||
<string name="logs_cleared">Günlük başarıyla temizlendi.</string>
|
||||
<string name="logs_cleared">Günlük başarıyla temizlendi</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Hedef UID: %1$d</string>
|
||||
|
||||
@@ -96,41 +97,43 @@
|
||||
<string name="safetynet_api_error">SafetyNet API Hatası</string>
|
||||
<string name="safetynet_res_invalid">Yanıt geçersiz</string>
|
||||
<string name="safetynet_attest_success">Başarılı!</string>
|
||||
<string name="safetynet_attest_failure">Onaylama başarısız oldu!</string>
|
||||
<string name="safetynet_attest_failure">Onaylama başarısız!</string>
|
||||
<string name="safetynet_attest_loading">Sadece bir saniye…</string>
|
||||
<string name="safetynet_attest_restart">Tekrar deneyiniz</string>
|
||||
<string name="safetynet_attest_restart">Tekrar dene</string>
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">Sistem uygulamalarını göster</string>
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Sistem uyg. göster</string>
|
||||
<string name="show_os_app">İşletim Sistemi uyg. göster</string>
|
||||
<string name="hide_filter_hint">İsme göre filtrele</string>
|
||||
<string name="hide_scroll_up">Yukarı kaydır</string>
|
||||
<string name="hide_filters">Filtreler</string>
|
||||
<string name="hide_search">Ara</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Hiçbir açıklama sağlanmadı)</string>
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Hiçbir bilgi sağlanmadı)</string>
|
||||
<string name="reboot_userspace">Hızlı yeniden başlat</string>
|
||||
<string name="reboot_recovery">Kurtarma moduna yeniden başlat</string>
|
||||
<string name="reboot_bootloader">Ön yükleyici moduna yeniden başlat</string>
|
||||
<string name="reboot_download">Yükleme moduna yeniden başlat</string>
|
||||
<string name="reboot_download">İndirme moduna yeniden başlat</string>
|
||||
<string name="reboot_edl">EDL moduna yeniden başlat</string>
|
||||
<string name="module_version_author">%1$s - %2$s</string>
|
||||
<string name="module_section_pending">Güncellemeler</string>
|
||||
<string name="module_section_pending_action">Tümünü güncelle</string>
|
||||
<string name="module_state_remove">Kaldır</string>
|
||||
<string name="module_state_restore">Geri yükle</string>
|
||||
<string name="module_action_install_external">Depolama alanından yükle</string>
|
||||
<string name="module_action_install_external">Dahili depolamadan yükle</string>
|
||||
<string name="update_available">Güncelleme mevcut</string>
|
||||
<string name="module_installed">@string/home_installed_version</string>
|
||||
<string name="module_section_online">Çevrim içi</string>
|
||||
<string name="module_section_online">Çevrim İçi</string>
|
||||
<string name="sorting_order">Sıralama Düzeni</string>
|
||||
|
||||
<!--Settings -->
|
||||
<string name="settings_dark_mode_title">Tema Modu</string>
|
||||
<string name="settings_dark_mode_message">Stilinize en uygun modu seçin!</string>
|
||||
<string name="settings_dark_mode_light">Her zaman aydınlık</string>
|
||||
<string name="settings_dark_mode_system">Sistemi takip et</string>
|
||||
<string name="settings_dark_mode_dark">Her zaman karanlık</string>
|
||||
<string name="settings_download_path_title">İndirme Yolu</string>
|
||||
<string name="settings_dark_mode_message">Stilinize en iyi uyan modu seçin!</string>
|
||||
<string name="settings_dark_mode_light">Her Zaman Aydınlık</string>
|
||||
<string name="settings_dark_mode_system">Sistemi Takip Et</string>
|
||||
<string name="settings_dark_mode_dark">Her Zmana Karanlık</string>
|
||||
<string name="settings_download_path_title">İndirme dizini</string>
|
||||
<string name="settings_download_path_message">Dosyalar %1$s konumuna kaydedilecek</string>
|
||||
<string name="settings_clear_cache_title">Depo Önbelleğini Temizle</string>
|
||||
<string name="settings_clear_cache_summary">Çevrim içi depolar için önbellek bilgilerini temizle, uygulamayı çevrim içi yenilemeye zorla</string>
|
||||
@@ -143,9 +146,9 @@
|
||||
<string name="settings_check_update_title">Güncellemeleri Denetle</string>
|
||||
<string name="settings_check_update_summary">Düzenli aralıklarla arka planda güncellemeleri denetle</string>
|
||||
<string name="settings_update_channel_title">Güncelleme Kanalı</string>
|
||||
<string name="settings_update_stable">Stabil(kararlı)</string>
|
||||
<string name="settings_update_stable">Stabil</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_custom">Özel</string>
|
||||
<string name="settings_update_custom">Özel Kanal</string>
|
||||
<string name="settings_update_custom_msg">Özel bir URL ekleyin</string>
|
||||
<string name="settings_magiskhide_summary">Magisk\'i çeşitli algılamalardan gizle</string>
|
||||
<string name="settings_hosts_title">Sistemsiz host</string>
|
||||
@@ -157,7 +160,7 @@
|
||||
<string name="settings_su_app_adb">Uygulamalar ve ADB</string>
|
||||
<string name="settings_su_app">Sadece uygulamalar</string>
|
||||
<string name="settings_su_adb">Sadece ADB</string>
|
||||
<string name="settings_su_disable">Devre dışı</string>
|
||||
<string name="settings_su_disable">Devre Dışı</string>
|
||||
<string name="settings_su_request_10">10 saniye</string>
|
||||
<string name="settings_su_request_15">15 saniye</string>
|
||||
<string name="settings_su_request_20">20 saniye</string>
|
||||
@@ -170,13 +173,15 @@
|
||||
<string name="superuser_notification">Yetkili Kullanıcı Bildirimi</string>
|
||||
<string name="settings_su_reauth_title">Yükseltmeden sonra yeniden kimlik doğrula</string>
|
||||
<string name="settings_su_reauth_summary">Uygulama yükseltmeleri sonrasında yetkili kullanıcı izinlerini yeniden doğrula</string>
|
||||
<string name="settings_su_tapjack_title">Tapjacking Korumasını Aktifleştir</string>
|
||||
<string name="settings_su_tapjack_summary">Yetkili Kullanıcı izin penceresi başka bir pencere veya arayüz tarafından engellenirken verilen yanıtlara tepki vermeyecektir</string>
|
||||
<string name="settings_su_biometric_title">Biyometrik Kimlik Doğrulamayı Etkinleştir</string>
|
||||
<string name="settings_su_biometric_summary">Superuser isteklerine izin vermek için biyometrik kimlik doğrulamayı kullanın</string>
|
||||
<string name="no_biometric">Desteklenmeyen cihaz veya biyometrik ayar etkinleştirilmemiş</string>
|
||||
<string name="settings_customization">Özelleştirme</string>
|
||||
<string name="setting_add_shortcut_summary">Uygulamayı gizledikten sonra ismini ve simgesini tanımakta zorlanırsan ana ekrana güzel bir kısayol ekleyebilirsin</string>
|
||||
<string name="settings_doh_title">HTTPS üzerinden DNS</string>
|
||||
<string name="settings_doh_description">Bazı ülkelerdeki DNS zehirlenmesini çözmeye çalışır</string>
|
||||
<string name="settings_doh_description">Bazı ülkelerdeki DNS zehirlenmesini çözmeye çalış</string>
|
||||
|
||||
<string name="multiuser_mode">Çoklu Kullanıcı Modu</string>
|
||||
<string name="settings_owner_only">Yalnızca Cihaz Sahibi</string>
|
||||
@@ -186,8 +191,8 @@
|
||||
<string name="owner_manage_summary">Yalnızca cihaz sahibi kök erişimini yönetebilir ve izin isteklerini alabilir</string>
|
||||
<string name="user_indepenent_summary">Her kullanıcının kendi ayrı kök erişimi kuralları vardır</string>
|
||||
|
||||
<string name="mount_namespace_mode">Ad Alanı Modunu Doldur</string>
|
||||
<string name="settings_ns_global">Evrensel Ad Alanı</string>
|
||||
<string name="mount_namespace_mode">Ad Alanı Modunu Bağla</string>
|
||||
<string name="settings_ns_global">Global Ad Alanı</string>
|
||||
<string name="settings_ns_requester">Devralınan Ad Alanı</string>
|
||||
<string name="settings_ns_isolate">Ayrılmış Ad Alanı</string>
|
||||
<string name="global_summary">Tüm kök oturumları genel bağlama ad alanını kullanır</string>
|
||||
@@ -201,8 +206,8 @@
|
||||
<string name="download_file_error">Dosya indirme hatası</string>
|
||||
<string name="download_open_parent">Üst klasörde göster</string>
|
||||
<string name="download_open_self">Dosyayı göster</string>
|
||||
<string name="magisk_update_title">Yeni Magisk Güncellemesi Mevcut!</string>
|
||||
<string name="manager_update_title">Yeni Magisk Manager Güncellemesi Mevcut!</string>
|
||||
<string name="magisk_update_title">Magisk Güncellemesi Mevcut!</string>
|
||||
<string name="manager_update_title">Magisk Manager Güncellemesi Mevcut!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Evet</string>
|
||||
@@ -217,25 +222,26 @@
|
||||
<string name="done">Tamamlandı!</string>
|
||||
<string name="failure">Başarısız</string>
|
||||
<string name="hide_manager_title">Magisk Manager Gizleniyor…</string>
|
||||
<string name="hide_manager_fail_toast">Magisk Manager\'ı Gizleme başarısız oldu.</string>
|
||||
<string name="open_link_failed_toast">Bağlantıyı açabilecek uygulama bulunamadı.</string>
|
||||
<string name="hide_manager_fail_toast">Magisk Manager\'ı gizleme başarısız</string>
|
||||
<string name="restore_manager_fail_toast">Magisk Manager\'ı geri yükleme başarısız</string>
|
||||
<string name="open_link_failed_toast">Bağlantıyı açabilecek bir uygulama bulunamadı</string>
|
||||
<string name="complete_uninstall">Tamamen Kaldır</string>
|
||||
<string name="restore_img">Önyükleme İmajını Geri Yükle</string>
|
||||
<string name="restore_img">İmajları Geri Yükle</string>
|
||||
<string name="restore_img_msg">Geri Yükleniyor…</string>
|
||||
<string name="restore_done">Yenileme tamamlandı!</string>
|
||||
<string name="restore_fail">Stok önyükleme yedeği yok!</string>
|
||||
<string name="restore_done">Geri yükleme tamamlandı!</string>
|
||||
<string name="restore_fail">Stock ön yükleme yedeği yok!</string>
|
||||
<string name="proprietary_title">Sahipli Kodu İndirin</string>
|
||||
<string name="proprietary_notice">Magisk Yöneticisi, FOSS(açık kaynaklı) olduğundan gerekli olan Google\'ın sahipli(kapalı kaynaklı) SafetyNet API kodunu içermez.\n\nMagisk Manager\'ın SafetyNet kontrolü için (GoogleApiClient içeren) bir uzantıyı indirmesine izin veriyor musunuz?</string>
|
||||
<string name="proprietary_notice">Magisk Manager, FOSS(açık kaynaklı) olduğundan gerekli olan Google\'ın sahipli(kapalı kaynaklı) SafetyNet API kodunu içermez.\n\nMagisk Manager\'ın SafetyNet kontrolü için (GoogleApiClient içeren) bir uzantıyı indirmesine izin veriyor musunuz?</string>
|
||||
<string name="setup_fail">Kurulum başarısız</string>
|
||||
<string name="env_fix_title">Ek Kurulum Gerekli</string>
|
||||
<string name="env_fix_msg">Cihazınızın Magisk\'in düzgün çalışması için ek kuruluma ihtiyacı var. Bu Magisk kurulum zip dosyasını indirecektir, şimdi devam etmek istiyor musunuz?</string>
|
||||
<string name="setup_msg">Ortam kurulumu çalışıyor…</string>
|
||||
<string name="authenticate">Kimlik doğrulaması</string>
|
||||
<string name="setup_msg">Ortam kurulumu çalıştırılıyor…</string>
|
||||
<string name="authenticate">Kimlik Doğrulaması</string>
|
||||
<string name="unsupport_magisk_title">Desteklenmeyen Magisk Sürümü</string>
|
||||
<string name="unsupport_magisk_msg">Magisk Manager\'ın bu sürümü, %1$s daha düşük Magisk versiyonlarını desteklememektedir.\n\nUygulama hiçbir Magisk kurulu değil gibi davranacak, lütfen en kısa zamanda Magisk\'i yükseltin.</string>
|
||||
<string name="external_rw_permission_denied">Bu işlevi etkinleştirmek için depolama izni verin</string>
|
||||
<string name="add_shortcut_title">Ana ekrana kısayol ekle</string>
|
||||
<string name="add_shortcut_msg">Magisk Manager\'ı gizledikten sonra, ismini ve simgesini tanıması güç olabilir. Ana ekrana güzel bir kısayol eklemek ister misin??</string>
|
||||
<string name="add_shortcut_msg">Magisk Manager\'ı gizledikten sonra, ismini ve simgesini tanıması zor olabilir. Ana ekrana güzel bir kısayol eklemek ister misin?</string>
|
||||
<string name="app_not_found">Bu işlemi yapabilecek bir uygulama bulunamadı</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="SplashTheme" parent="SplashThemeBase">
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
<item name="android:windowTranslucentNavigation">true</item>
|
||||
</style>
|
||||
</resources>
|
18
app/src/main/res/values-v19/themes.xml
Normal file
18
app/src/main/res/values-v19/themes.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Base.V19.Theme.Splash.Light" parent="Base.V17.Theme.Splash.Light">
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
<item name="android:windowTranslucentNavigation">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Base.V19.Theme.Splash" parent="Base.V17.Theme.Splash">
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
<item name="android:windowTranslucentNavigation">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Splash.Light" parent="Base.V19.Theme.Splash.Light" />
|
||||
|
||||
<style name="Theme.Splash" parent="Base.V19.Theme.Splash" />
|
||||
|
||||
</resources>
|
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Foundation.Compat">
|
||||
<item name="android:windowTranslucentNavigation">true</item>
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
</style>
|
||||
</resources>
|
38
app/src/main/res/values-v21/themes.xml
Normal file
38
app/src/main/res/values-v21/themes.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Base.V21.Theme.Foundation.Light" parent="Base.V17.Theme.Foundation.Light">
|
||||
<item name="android:statusBarColor">#38000000</item>
|
||||
<item name="android:navigationBarColor">#38000000</item>
|
||||
</style>
|
||||
|
||||
<style name="Base.V21.Theme.Foundation" parent="Base.V17.Theme.Foundation">
|
||||
<item name="android:statusBarColor">#60000000</item>
|
||||
<item name="android:navigationBarColor">#60000000</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Foundation.Light" parent="Base.V21.Theme.Foundation.Light" />
|
||||
|
||||
<style name="Theme.Foundation" parent="Base.V21.Theme.Foundation" />
|
||||
|
||||
<style name="Base.V21.Theme.Splash.Light" parent="Base.V19.Theme.Splash.Light">
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
<item name="android:windowTranslucentNavigation">false</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Base.V21.Theme.Splash" parent="Base.V19.Theme.Splash">
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
<item name="android:windowTranslucentNavigation">false</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Splash.Light" parent="Base.V21.Theme.Splash.Light" />
|
||||
|
||||
<style name="Theme.Splash" parent="Base.V21.Theme.Splash" />
|
||||
|
||||
</resources>
|
17
app/src/main/res/values-v23/themes.xml
Normal file
17
app/src/main/res/values-v23/themes.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Base.V23.Theme.Foundation.Light" parent="Base.V21.Theme.Foundation.Light">
|
||||
<item name="android:statusBarColor">#40bdbdbd</item>
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Foundation.Light" parent="Base.V23.Theme.Foundation.Light" />
|
||||
|
||||
<style name="Base.V23.Theme.Splash.Light" parent="Base.V21.Theme.Splash.Light">
|
||||
<item name="android:windowLightStatusBar">false</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Splash.Light" parent="Base.V23.Theme.Splash.Light" />
|
||||
|
||||
</resources>
|
24
app/src/main/res/values-v27/themes.xml
Normal file
24
app/src/main/res/values-v27/themes.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Base.V27.Theme.Foundation.Light" parent="Base.V23.Theme.Foundation.Light">
|
||||
<item name="android:navigationBarColor">#e0fafafa</item>
|
||||
<item name="android:navigationBarDividerColor">#1f000000</item>
|
||||
<item name="android:windowLightNavigationBar">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Base.V27.Theme.Foundation" parent="Base.V21.Theme.Foundation">
|
||||
<item name="android:navigationBarDividerColor">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Foundation.Light" parent="Base.V27.Theme.Foundation.Light" />
|
||||
|
||||
<style name="Theme.Foundation" parent="Base.V27.Theme.Foundation" />
|
||||
|
||||
<style name="Base.V27.Theme.Splash.Light" parent="Base.V23.Theme.Splash.Light">
|
||||
<item name="android:windowLightNavigationBar">false</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Splash.Light" parent="Base.V27.Theme.Splash.Light" />
|
||||
|
||||
</resources>
|
@@ -16,14 +16,6 @@
|
||||
<item>@string/settings_su_app_adb</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="value_array">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="request_timeout">
|
||||
<item>@string/settings_su_request_10</item>
|
||||
<item>@string/settings_su_request_15</item>
|
||||
|
@@ -19,4 +19,32 @@
|
||||
|
||||
<!--endregion-->
|
||||
|
||||
<declare-styleable name="WindowInsetsHelper">
|
||||
<attr name="edgeToEdge" format="boolean"/>
|
||||
<attr name="fitsSystemWindowsInsets" format="flags">
|
||||
<flag name="top" value="0x30" />
|
||||
<flag name="bottom" value="0x50" />
|
||||
<flag name="left" value="0x03" />
|
||||
<flag name="right" value="0x05" />
|
||||
<flag name="start" value="0x00800003" />
|
||||
<flag name="end" value="0x00800005" />
|
||||
</attr>
|
||||
<attr name="consumeSystemWindowsInsets" format="flags">
|
||||
<flag name="top" value="0x30" />
|
||||
<flag name="bottom" value="0x50" />
|
||||
<flag name="left" value="0x03" />
|
||||
<flag name="right" value="0x05" />
|
||||
<flag name="start" value="0x00800003" />
|
||||
<flag name="end" value="0x00800005" />
|
||||
</attr>
|
||||
<attr name="layout_fitsSystemWindowsInsets" format="flags">
|
||||
<flag name="top" value="0x30" />
|
||||
<flag name="bottom" value="0x50" />
|
||||
<flag name="left" value="0x03" />
|
||||
<flag name="right" value="0x05" />
|
||||
<flag name="start" value="0x00800003" />
|
||||
<flag name="end" value="0x00800005" />
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
|
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<item name="recyclerScrollListener" type="id" />
|
||||
<item name="coroutineScope" type="id"/>
|
||||
<item name="coroutineScope" type="id" />
|
||||
|
||||
<item name="tag_rikka_material_WindowInsetsHelper" type="id"/>
|
||||
<item name="tag_rikka_recyclerView_OverScrollIfContentScrollsListener" type="id"/>
|
||||
</resources>
|
||||
|
@@ -1,20 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="SplashThemeBase" parent="android:Theme.DeviceDefault.NoActionBar">
|
||||
<item name="android:windowBackground">@drawable/ic_splash_activity</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
</style>
|
||||
<style name="SplashTheme" parent="Theme.Splash.Light" />
|
||||
|
||||
<style name="SplashTheme" parent="SplashThemeBase" />
|
||||
|
||||
<style name="Foundation" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||
<item name="android:windowBackground">?colorSurface</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
</style>
|
||||
|
||||
<!--This should be overridden in v21 for transparency, etc-->
|
||||
<style name="Foundation.Compat" />
|
||||
<style name="Foundation" parent="Theme.Foundation.Light" />
|
||||
|
||||
<!--region Do not remove-->
|
||||
<style name="Empty" />
|
||||
@@ -28,7 +17,7 @@
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="Foundation.Default" parent="Foundation.Compat">
|
||||
<style name="Foundation.Default">
|
||||
<item name="android:includeFontPadding">false</item>
|
||||
<item name="actionBarSize">@dimen/internal_action_bar_size</item>
|
||||
<item name="popupMenuStyle">@style/Foundation.PopupMenu</item>
|
||||
|
41
app/src/main/res/values/themes.xml
Normal file
41
app/src/main/res/values/themes.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Base.V17.Theme.Foundation.Light" parent="Theme.MaterialComponents.Light.NoActionBar">
|
||||
<item name="android:windowBackground">?colorSurface</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="dialogTheme">@style/ThemeOverlay.Foundation.Dialog</item>
|
||||
</style>
|
||||
|
||||
<style name="Base.V17.Theme.Foundation" parent="Theme.MaterialComponents.NoActionBar">
|
||||
<item name="android:windowBackground">?colorSurface</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="dialogTheme">@style/ThemeOverlay.Foundation.Dialog</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Foundation.Light" parent="Base.V17.Theme.Foundation.Light" />
|
||||
|
||||
<style name="Theme.Foundation" parent="Base.V17.Theme.Foundation" />
|
||||
|
||||
<style name="Base.V17.Theme.Splash.Light" parent="android:Theme.DeviceDefault.Light.NoActionBar">
|
||||
<item name="android:windowBackground">@drawable/ic_splash_activity</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
</style>
|
||||
|
||||
<style name="Base.V17.Theme.Splash" parent="android:Theme.DeviceDefault.NoActionBar">
|
||||
<item name="android:windowBackground">@drawable/ic_splash_activity</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Splash.Light" parent="Base.V17.Theme.Splash.Light" />
|
||||
|
||||
<style name="Theme.Splash" parent="Base.V17.Theme.Splash" />
|
||||
|
||||
<style name="Base.V17.ThemeOverlay.Foundation.Dialog" parent="ThemeOverlay.MaterialComponents.Dialog">
|
||||
<item name="android:windowMinWidthMajor">@dimen/abc_dialog_min_width_major</item>
|
||||
<item name="android:windowMinWidthMinor">@dimen/abc_dialog_min_width_minor</item>
|
||||
</style>
|
||||
|
||||
<style name="ThemeOverlay.Foundation.Dialog" parent="Base.V17.ThemeOverlay.Foundation.Dialog" />
|
||||
|
||||
</resources>
|
@@ -14,12 +14,12 @@ buildscript {
|
||||
maven { url = uri("https://kotlin.bintray.com/kotlinx") }
|
||||
}
|
||||
|
||||
val vNav = "2.3.1"
|
||||
val vNav = "2.3.2"
|
||||
extra["vNav"] = vNav
|
||||
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:4.1.1")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.21")
|
||||
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:${vNav}")
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
@@ -27,7 +27,7 @@ buildscript {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("clean",Delete::class){
|
||||
tasks.register("clean", Delete::class) {
|
||||
delete(rootProject.buildDir)
|
||||
}
|
||||
|
||||
@@ -110,12 +110,12 @@ subprojects {
|
||||
buildTypes {
|
||||
signingConfigs.getByName("config").also {
|
||||
getByName("debug") {
|
||||
// If keystore exists, sign the APK with custom signature
|
||||
if (it.storeFile?.exists() == true)
|
||||
signingConfig = it
|
||||
signingConfig = if (it.storeFile?.exists() == true) it
|
||||
else signingConfigs.getByName("debug")
|
||||
}
|
||||
getByName("release") {
|
||||
signingConfig = it
|
||||
signingConfig = if (it.storeFile?.exists() == true) it
|
||||
else signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
222
build.py
222
build.py
@@ -2,20 +2,32 @@
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
is_windows = os.name == 'nt'
|
||||
if is_windows:
|
||||
import colorama
|
||||
colorama.init()
|
||||
import argparse
|
||||
import multiprocessing
|
||||
import zipfile
|
||||
import datetime
|
||||
import errno
|
||||
import shutil
|
||||
import lzma
|
||||
import platform
|
||||
import urllib.request
|
||||
import os.path as op
|
||||
from distutils.dir_util import copy_tree
|
||||
|
||||
|
||||
def error(str):
|
||||
print('\n' + '\033[41m' + str + '\033[0m' + '\n')
|
||||
if is_ci:
|
||||
print(f'\n ! {str}\n')
|
||||
else:
|
||||
print(f'\n\033[41m{str}\033[0m\n')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def header(str):
|
||||
print('\n' + '\033[44m' + str + '\033[0m' + '\n')
|
||||
if is_ci:
|
||||
print(f'\n{str}\n')
|
||||
else:
|
||||
print(f'\n\033[44m{str}\033[0m\n')
|
||||
|
||||
|
||||
def vprint(str):
|
||||
@@ -23,6 +35,13 @@ def vprint(str):
|
||||
print(str)
|
||||
|
||||
|
||||
is_windows = os.name == 'nt'
|
||||
is_ci = 'CI' in os.environ and os.environ['CI'] == 'true'
|
||||
|
||||
if not is_ci and is_windows:
|
||||
import colorama
|
||||
colorama.init()
|
||||
|
||||
# Environment checks
|
||||
if not sys.version_info >= (3, 6):
|
||||
error('Requires Python 3.6+')
|
||||
@@ -36,28 +55,12 @@ try:
|
||||
except FileNotFoundError:
|
||||
error('Please install JDK and make sure \'javac\' is available in PATH')
|
||||
|
||||
import argparse
|
||||
import multiprocessing
|
||||
import zipfile
|
||||
import datetime
|
||||
import errno
|
||||
import shutil
|
||||
import lzma
|
||||
import tempfile
|
||||
import platform
|
||||
import urllib.request
|
||||
import os.path as op
|
||||
from distutils.dir_util import copy_tree
|
||||
|
||||
cpu_count = multiprocessing.cpu_count()
|
||||
archs = ['armeabi-v7a', 'x86']
|
||||
arch64 = ['arm64-v8a', 'x86_64']
|
||||
support_targets = ['magisk', 'magiskinit', 'magiskboot', 'magiskpolicy', 'resetprop', 'busybox', 'test']
|
||||
default_targets = ['magisk', 'magiskinit', 'magiskboot', 'busybox']
|
||||
|
||||
ndk_ver = '21d'
|
||||
ndk_ver_full = '21.3.6528147'
|
||||
|
||||
ndk_root = op.join(os.environ['ANDROID_SDK_ROOT'], 'ndk')
|
||||
ndk_path = op.join(ndk_root, 'magisk')
|
||||
ndk_build = op.join(ndk_path, 'ndk-build')
|
||||
@@ -66,6 +69,8 @@ gradlew = op.join('.', 'gradlew' + ('.bat' if is_windows else ''))
|
||||
# Global vars
|
||||
config = {}
|
||||
STDOUT = None
|
||||
build_tools = None
|
||||
|
||||
|
||||
def mv(source, target):
|
||||
try:
|
||||
@@ -116,6 +121,10 @@ def system(cmd):
|
||||
return subprocess.run(cmd, shell=True, stdout=STDOUT)
|
||||
|
||||
|
||||
def cmd_out(cmd):
|
||||
return subprocess.check_output(cmd).strip().decode('utf-8')
|
||||
|
||||
|
||||
def xz(data):
|
||||
return lzma.compress(data, preset=9, check=lzma.CHECK_NONE)
|
||||
|
||||
@@ -129,37 +138,36 @@ def parse_props(file):
|
||||
prop = line.split('=')
|
||||
if len(prop) != 2:
|
||||
continue
|
||||
props[prop[0].strip(' \t\r\n')] = prop[1].strip(' \t\r\n')
|
||||
value = prop[1].strip(' \t\r\n')
|
||||
if len(value) == 0:
|
||||
continue
|
||||
props[prop[0].strip(' \t\r\n')] = value
|
||||
return props
|
||||
|
||||
|
||||
def load_config(args):
|
||||
# Load prop file
|
||||
if not op.exists(args.config):
|
||||
error(f'Please make sure {args.config} exists')
|
||||
commit_hash = cmd_out(['git', 'rev-parse', '--short=8', 'HEAD'])
|
||||
|
||||
# Some default values
|
||||
# Default values
|
||||
config['version'] = commit_hash
|
||||
config['outdir'] = 'out'
|
||||
config['prettyName'] = 'false'
|
||||
config['keyStore'] = 'release-key.jks'
|
||||
|
||||
config.update(parse_props(args.config))
|
||||
# Load prop files
|
||||
if op.exists(args.config):
|
||||
config.update(parse_props(args.config))
|
||||
|
||||
# Sanitize configs
|
||||
for key, value in parse_props('gradle.properties').items():
|
||||
if key.startswith('magisk.'):
|
||||
config[key[7:]] = value
|
||||
|
||||
config['prettyName'] = config['prettyName'].lower() == 'true'
|
||||
|
||||
if 'version' not in config or 'versionCode' not in config:
|
||||
error('Config error: "version" and "versionCode" is required')
|
||||
|
||||
try:
|
||||
config['versionCode'] = int(config['versionCode'])
|
||||
except ValueError:
|
||||
error('Config error: "versionCode" is required to be an integer')
|
||||
|
||||
if args.release and not op.exists(config['keyStore']):
|
||||
error(f'Config error: assign "keyStore" to a java keystore')
|
||||
|
||||
mkdir_p(config['outdir'])
|
||||
global STDOUT
|
||||
STDOUT = None if args.verbose else subprocess.DEVNULL
|
||||
@@ -188,33 +196,52 @@ def clean_elf():
|
||||
elf_cleaner = op.join('native', 'out', 'elf-cleaner')
|
||||
if not op.exists(elf_cleaner):
|
||||
execv(['g++', '-std=c++11', 'tools/termux-elf-cleaner/termux-elf-cleaner.cpp',
|
||||
'-o', elf_cleaner])
|
||||
'-o', elf_cleaner])
|
||||
args = [elf_cleaner]
|
||||
args.extend(op.join('native', 'out', arch, 'magisk') for arch in archs + arch64)
|
||||
args.extend(op.join('native', 'out', arch, 'magisk')
|
||||
for arch in archs + arch64)
|
||||
execv(args)
|
||||
|
||||
|
||||
def sign_zip(unsigned, output, release):
|
||||
if not release:
|
||||
mv(unsigned, output)
|
||||
def find_build_tools():
|
||||
global build_tools
|
||||
if build_tools:
|
||||
return build_tools
|
||||
build_tools_root = op.join(os.environ['ANDROID_SDK_ROOT'], 'build-tools')
|
||||
ls = os.listdir(build_tools_root)
|
||||
# Use the latest build tools available
|
||||
ls.sort()
|
||||
build_tools = op.join(build_tools_root, ls[-1])
|
||||
return build_tools
|
||||
|
||||
|
||||
def sign_zip(unsigned):
|
||||
if 'keyStore' not in config:
|
||||
return
|
||||
|
||||
signer_name = 'zipsigner-4.0.jar'
|
||||
zipsigner = op.join('app', 'signing', 'build', 'libs', signer_name)
|
||||
msg = '* Signing APK'
|
||||
apksigner = op.join(find_build_tools(), 'apksigner')
|
||||
|
||||
if not op.exists(zipsigner):
|
||||
header('* Building ' + signer_name)
|
||||
proc = execv([gradlew, 'app:signing:shadowJar'])
|
||||
if proc.returncode != 0:
|
||||
error(f'Build {signer_name} failed!')
|
||||
execArgs = [apksigner, 'sign',
|
||||
'--ks', config['keyStore'],
|
||||
'--ks-pass', f'pass:{config["keyStorePass"]}',
|
||||
'--ks-key-alias', config['keyAlias'],
|
||||
'--key-pass', f'pass:{config["keyPass"]}',
|
||||
'--v1-signer-name', 'CERT',
|
||||
'--v4-signing-enabled', 'false']
|
||||
|
||||
header('* Signing Zip')
|
||||
if unsigned.endswith('.zip'):
|
||||
msg = '* Signing zip'
|
||||
execArgs.extend(['--min-sdk-version', '17',
|
||||
'--v2-signing-enabled', 'false',
|
||||
'--v3-signing-enabled', 'false'])
|
||||
|
||||
proc = execv(['java', '-jar', zipsigner, config['keyStore'], config['keyStorePass'],
|
||||
config['keyAlias'], config['keyPass'], unsigned, output])
|
||||
execArgs.append(unsigned)
|
||||
|
||||
header(msg)
|
||||
proc = execv(execArgs)
|
||||
if proc.returncode != 0:
|
||||
error('Signing zip failed!')
|
||||
error('Signing failed!')
|
||||
|
||||
|
||||
def binary_dump(src, out, var_name):
|
||||
@@ -274,9 +301,7 @@ def dump_bin_headers():
|
||||
binary_dump(src, out, 'magisk_xz')
|
||||
stub = op.join(config['outdir'], 'stub-release.apk')
|
||||
if not op.exists(stub):
|
||||
stub = op.join(config['outdir'], 'stub-debug.apk')
|
||||
if not op.exists(stub):
|
||||
error('Build stub APK before building "magiskinit"')
|
||||
error('Build stub APK before building "magiskinit"')
|
||||
with open(op.join('native', 'out', 'binaries.h'), 'w') as out:
|
||||
with open(stub, 'rb') as src:
|
||||
binary_dump(src, out, 'manager_xz')
|
||||
@@ -285,7 +310,7 @@ def dump_bin_headers():
|
||||
def build_binary(args):
|
||||
# Verify NDK install
|
||||
props = parse_props(op.join(ndk_path, 'source.properties'))
|
||||
if props['Pkg.Revision'] != ndk_ver_full:
|
||||
if props['Pkg.Revision'] != config['fullNdkVersion']:
|
||||
error('Incorrect NDK. Please install/upgrade NDK with "build.py ndk"')
|
||||
|
||||
if args.target:
|
||||
@@ -297,7 +322,20 @@ def build_binary(args):
|
||||
|
||||
header('* Building binaries: ' + ' '.join(args.target))
|
||||
|
||||
os.utime(op.join('native', 'jni', 'include', 'flags.hpp'))
|
||||
update_flags = False
|
||||
flags = op.join('native', 'jni', 'include', 'flags.hpp')
|
||||
flags_stat = os.stat(flags)
|
||||
|
||||
if op.exists(args.config):
|
||||
if os.stat(args.config).st_mtime_ns > flags_stat.st_mtime_ns:
|
||||
update_flags = True
|
||||
|
||||
last_commit = int(cmd_out(['git', 'log', '-1', r'--format=%at', 'HEAD']))
|
||||
if last_commit > flags_stat.st_mtime:
|
||||
update_flags = True
|
||||
|
||||
if update_flags:
|
||||
os.utime(flags)
|
||||
|
||||
# Basic flags
|
||||
global base_flags
|
||||
@@ -331,10 +369,10 @@ def build_binary(args):
|
||||
|
||||
|
||||
def build_apk(args, module):
|
||||
build_type = 'Release' if args.release else 'Debug'
|
||||
build_type = 'Release' if args.release or module == 'stub' else 'Debug'
|
||||
|
||||
proc = execv([gradlew, f'{module}:assemble{build_type}',
|
||||
'-PconfigPath=' + op.abspath(args.config)])
|
||||
'-PconfigPath=' + op.abspath(args.config)])
|
||||
if proc.returncode != 0:
|
||||
error(f'Build {module} failed!')
|
||||
|
||||
@@ -379,10 +417,16 @@ def build_snet(args):
|
||||
def zip_main(args):
|
||||
header('* Packing Flashable Zip')
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||
unsigned = f.name
|
||||
if config['prettyName']:
|
||||
name = f'Magisk-v{config["version"]}.zip'
|
||||
elif args.release:
|
||||
name = 'magisk-release.zip'
|
||||
else:
|
||||
name = 'magisk-debug.zip'
|
||||
|
||||
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
|
||||
output = op.join(config['outdir'], name)
|
||||
|
||||
with zipfile.ZipFile(output, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
|
||||
# update-binary
|
||||
target = op.join('META-INF', 'com', 'google',
|
||||
'android', 'update-binary')
|
||||
@@ -429,26 +473,27 @@ def zip_main(args):
|
||||
|
||||
# chromeos
|
||||
for tool in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
||||
source = op.join('tools', tool)
|
||||
if tool == 'futility':
|
||||
source = op.join('tools', tool)
|
||||
else:
|
||||
source = op.join('tools', 'keys', tool)
|
||||
target = op.join('chromeos', tool)
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# End of zipping
|
||||
|
||||
output = op.join(config['outdir'], f'Magisk-v{config["version"]}.zip' if config['prettyName'] else
|
||||
'magisk-release.zip' if args.release else 'magisk-debug.zip')
|
||||
sign_zip(unsigned, output, args.release)
|
||||
rm(unsigned)
|
||||
sign_zip(output)
|
||||
header('Output: ' + output)
|
||||
|
||||
|
||||
def zip_uninstaller(args):
|
||||
header('* Packing Uninstaller Zip')
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||
unsigned = f.name
|
||||
datestr = datetime.datetime.now().strftime("%Y%m%d")
|
||||
name = f'Magisk-uninstaller-{datestr}.zip' if config['prettyName'] else 'magisk-uninstaller.zip'
|
||||
output = op.join(config['outdir'], name)
|
||||
|
||||
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
|
||||
with zipfile.ZipFile(output, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
|
||||
# update-binary
|
||||
target = op.join('META-INF', 'com', 'google',
|
||||
'android', 'update-binary')
|
||||
@@ -475,17 +520,16 @@ def zip_uninstaller(args):
|
||||
|
||||
# chromeos
|
||||
for tool in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
|
||||
source = op.join('tools', tool)
|
||||
if tool == 'futility':
|
||||
source = op.join('tools', tool)
|
||||
else:
|
||||
source = op.join('tools', 'keys', tool)
|
||||
target = op.join('chromeos', tool)
|
||||
zip_with_msg(zipf, source, target)
|
||||
|
||||
# End of zipping
|
||||
|
||||
datestr = datetime.datetime.now().strftime("%Y%m%d")
|
||||
output = op.join(config['outdir'], f'Magisk-uninstaller-{datestr}.zip'
|
||||
if config['prettyName'] else 'magisk-uninstaller.zip')
|
||||
sign_zip(unsigned, output, args.release)
|
||||
rm(unsigned)
|
||||
sign_zip(output)
|
||||
header('Output: ' + output)
|
||||
|
||||
|
||||
@@ -510,6 +554,7 @@ def cleanup(args):
|
||||
|
||||
def setup_ndk(args):
|
||||
os_name = platform.system().lower()
|
||||
ndk_ver = config['ndkVersion']
|
||||
url = f'https://dl.google.com/android/repository/android-ndk-r{ndk_ver}-{os_name}-x86_64.zip'
|
||||
ndk_zip = url.split('/')[-1]
|
||||
|
||||
@@ -523,7 +568,7 @@ def setup_ndk(args):
|
||||
for info in zf.infolist():
|
||||
extracted_path = zf.extract(info, ndk_root)
|
||||
vprint(f'Extracting {info.filename}')
|
||||
if info.create_system == 3: # ZIP_UNIX_SYSTEM = 3
|
||||
if info.create_system == 3: # ZIP_UNIX_SYSTEM = 3
|
||||
unix_attributes = info.external_attr >> 16
|
||||
if unix_attributes:
|
||||
os.chmod(extracted_path, unix_attributes)
|
||||
@@ -540,15 +585,15 @@ def setup_ndk(args):
|
||||
|
||||
header('* Replacing API-16 static libs')
|
||||
for target in ['arm-linux-androideabi', 'i686-linux-android']:
|
||||
arch = target.split('-')[0]
|
||||
lib_dir = op.join(
|
||||
ndk_path, 'toolchains', 'llvm', 'prebuilt', f'{os_name}-x86_64',
|
||||
'sysroot', 'usr', 'lib', f'{target}', '16')
|
||||
src_dir = op.join('tools', 'ndk-bins', arch)
|
||||
# Remove stupid macOS crap
|
||||
rm(op.join(src_dir, '.DS_Store'))
|
||||
for path in copy_tree(src_dir, lib_dir):
|
||||
vprint(f'Replaced {path}')
|
||||
arch = target.split('-')[0]
|
||||
lib_dir = op.join(
|
||||
ndk_path, 'toolchains', 'llvm', 'prebuilt', f'{os_name}-x86_64',
|
||||
'sysroot', 'usr', 'lib', f'{target}', '16')
|
||||
src_dir = op.join('tools', 'ndk-bins', arch)
|
||||
# Remove stupid macOS crap
|
||||
rm(op.join(src_dir, '.DS_Store'))
|
||||
for path in copy_tree(src_dir, lib_dir):
|
||||
vprint(f'Replaced {path}')
|
||||
|
||||
|
||||
def build_all(args):
|
||||
@@ -561,12 +606,13 @@ def build_all(args):
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description='Magisk build script')
|
||||
parser.set_defaults(func=lambda x: None)
|
||||
parser.add_argument('-r', '--release', action='store_true',
|
||||
help='compile in release mode')
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='verbose output')
|
||||
parser.add_argument('-c', '--config', default='config.prop',
|
||||
help='override config file (default: config.prop)')
|
||||
help='custom config file (default: config.prop)')
|
||||
subparsers = parser.add_subparsers(title='actions')
|
||||
|
||||
all_parser = subparsers.add_parser(
|
||||
|
@@ -13,3 +13,7 @@ gradlePlugin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.eclipse.jgit:org.eclipse.jgit:5.10.0.202012080955-r")
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.internal.storage.file.FileRepository
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
@@ -7,19 +8,30 @@ import java.io.File
|
||||
import java.util.*
|
||||
|
||||
private val props = Properties()
|
||||
private lateinit var commitHash: String
|
||||
private var commitCount = 0
|
||||
|
||||
object Config {
|
||||
operator fun get(key: String) = props[key] as? String
|
||||
fun contains(key: String) = props.containsKey(key)
|
||||
operator fun get(key: String) : String? {
|
||||
val v = props[key] as? String ?: return null
|
||||
return if (v.isBlank()) null else v
|
||||
}
|
||||
fun contains(key: String) = get(key) != null
|
||||
|
||||
val appVersion: String get() = get("appVersion") ?: commitHash
|
||||
val appVersionCode: Int get() = commitCount
|
||||
}
|
||||
|
||||
class MagiskPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
val configPath: String? by project
|
||||
val file = configPath?.let { File(it) } ?: project.file("config.prop")
|
||||
if (!file.exists())
|
||||
throw GradleException("Please setup config.prop")
|
||||
val config = configPath?.let { File(it) } ?: project.rootProject.file("config.prop")
|
||||
if (config.exists())
|
||||
config.inputStream().use { props.load(it) }
|
||||
|
||||
file.inputStream().use { props.load(it) }
|
||||
val repo = FileRepository(project.rootProject.file(".git"))
|
||||
val refId = repo.refDatabase.exactRef("HEAD").objectId
|
||||
commitHash = repo.newObjectReader().abbreviate(refId, 8).name()
|
||||
commitCount = Git(repo).log().add(refId).call().count()
|
||||
}
|
||||
}
|
||||
|
@@ -1,21 +1,36 @@
|
||||
# The version name and version code of Magisk
|
||||
version=
|
||||
versionCode=
|
||||
##########################################################
|
||||
# All variables in config.prop are optional
|
||||
# Removing or leaving them blank will keep default values
|
||||
##########################################################
|
||||
|
||||
# The version name and version code of Magisk Manager
|
||||
appVersion=
|
||||
appVersionCode=
|
||||
# The version name of Magisk. Default: git HEAD short SHA1
|
||||
version=string
|
||||
|
||||
outdir=out
|
||||
# The version name of Magisk Manager. Default: git HEAD short SHA1
|
||||
appVersion=string
|
||||
|
||||
# Whether use pretty names for zips, e.g. Magisk-v${version}.zip, Magisk-uninstaller-${date}.zip
|
||||
# The default output names are magisk-${release/debug/uninstaller}.zip
|
||||
prettyName=false
|
||||
# Output path. Default: out
|
||||
outdir=string
|
||||
|
||||
# Only used when building with release flag
|
||||
# These passwords are used along with keyStore to sign APKs and zips
|
||||
# keyPass is the pwd for the specified keyAlias
|
||||
keyStore=release-key.jks
|
||||
keyStorePass=
|
||||
keyAlias=
|
||||
keyPass=
|
||||
################################################################
|
||||
# Whether to use pretty names for zips
|
||||
# e.g. Magisk-v${version}.zip, Magisk-uninstaller-${date}.zip
|
||||
# Default names are magisk-${release/debug/uninstaller}.zip
|
||||
################################################################
|
||||
|
||||
# Default: false
|
||||
prettyName=bool
|
||||
|
||||
#####################################################
|
||||
# Signing configs for signing zips and APKs
|
||||
# These 4 variables has to be either all set or not
|
||||
#####################################################
|
||||
|
||||
# Path to keystore file
|
||||
keyStore=string
|
||||
# Keystore password
|
||||
keyStorePass=string
|
||||
# The desired key alias in the keystore
|
||||
keyAlias=string
|
||||
# Password of specified key alias
|
||||
keyPass=string
|
||||
|
@@ -1,5 +1,10 @@
|
||||
# Magisk Manager Changelog
|
||||
|
||||
### v8.0.4
|
||||
|
||||
- A lot of stability changes and minor bug fixes
|
||||
- Collect device properties, app logcat, and Magisk logs when saving logs in the logs menu
|
||||
|
||||
### v8.0.3
|
||||
|
||||
- Switch to the new Magisk Module Repo setup in preparation to allow 3rd party repos
|
||||
|
@@ -1,5 +1,14 @@
|
||||
# Magisk Changelog
|
||||
|
||||
### v21.2
|
||||
|
||||
- [MagiskInit] Detect 2SI after mounting `system_root` on legacy SAR devices
|
||||
- [General] Make sure `post-fs-data` scripts cannot block more than 35 seconds
|
||||
- [General] Fix the `magisk --install-module` command
|
||||
- [General] Trim Windows newline when reading files
|
||||
- [General] Directly log to file to prevent `logcat` weirdness
|
||||
- [MagiskBoot] Fix header dump/load for header v3 images
|
||||
|
||||
### v21.1
|
||||
|
||||
- [MagiskBoot] Support boot header v3 (Pixel 5 and 4a 5G)
|
||||
|
@@ -10,7 +10,7 @@ For those who want to use this "Standalone Mode" feature outside of Magisk, ther
|
||||
1. Set environment variable `ASH_STANDALONE` to `1`<br>Example: `ASH_STANDALONE=1 /data/adb/magisk/busybox sh <script>`
|
||||
2. Toggle with command-line options:<br>`/data/adb/magisk/busybox sh -o standalone <script>`
|
||||
|
||||
To make sure all subsequent `sh` shell executed also runs in standalone mode, option 1 is the preferred method (and this is what Magisk and Magisk Manager internally uses) as environment variables are inherited down to child processes.
|
||||
To make sure all subsequent `sh` shell executed also runs in standalone mode, option 1 is the preferred method (and this is what Magisk and Magisk Manager internally use) as environment variables are inherited down to child processes.
|
||||
|
||||
|
||||
## Magisk Modules
|
||||
@@ -29,7 +29,7 @@ A Magisk module is a folder placed in `/data/adb/modules` with the structure bel
|
||||
│ │
|
||||
│ │ *** Main Contents ***
|
||||
│ │
|
||||
│ ├── system <--- This folder will be mounted if skip_mount does not exists
|
||||
│ ├── system <--- This folder will be mounted if skip_mount does not exist
|
||||
│ │ ├── ...
|
||||
│ │ ├── ...
|
||||
│ │ └── ...
|
||||
@@ -96,14 +96,14 @@ This file follows the same format as `build.prop`. Each line comprises of `[key]
|
||||
#### sepolicy.rule
|
||||
If your module requires some additional sepolicy patches, please add those rules into this file. The module installer script and Magisk's daemon will make sure this file is copied to somewhere `magiskinit` can read pre-init to ensure these rules are injected properly.
|
||||
|
||||
Each line in this file will be treated as a policy statement. For more details how a policy statement is formatted, please check [magiskpolicy](tools.md#magiskpolicy)'s documentation.
|
||||
Each line in this file will be treated as a policy statement. For more details about how a policy statement is formatted, please check [magiskpolicy](tools.md#magiskpolicy)'s documentation.
|
||||
|
||||
#### The `system` folder
|
||||
All files you want Magisk to replace/inject for you should be placed in this folder. Please read through the [Magic Mount](details.md#magic-mount) section to understand how Magisk mount your files.
|
||||
|
||||
## Magisk Module Installer
|
||||
|
||||
A Magisk Module Installer is a Magisk Module packaged in a zip file that can be flashed in Magisk Manager or custom recoveries such as TWRP. An installer have the same file structure as a Magisk module (please check the previous section for more info). The simplest Magisk Module Installer is just a Magisk Module packed in a zip file, with addition to the following files:
|
||||
A Magisk Module Installer is a Magisk Module packaged in a zip file that can be flashed in Magisk Manager or custom recoveries such as TWRP. An installer has the same file structure as a Magisk module (please check the previous section for more info). The simplest Magisk Module Installer is just a Magisk Module packed in a zip file, with addition to the following files:
|
||||
|
||||
- `update-binary`: Download the latest [module_installer.sh](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) and rename/copy that script as `update-binary`
|
||||
- `updater-script`: This file should only contain the string `#MAGISK`
|
||||
@@ -156,7 +156,7 @@ ui_print <msg>
|
||||
Avoid using 'echo' as it will not display in custom recovery's console
|
||||
|
||||
abort <msg>
|
||||
print error message <msg> to console and terminate installation
|
||||
print error message <msg> to console and terminate the installation
|
||||
Avoid using 'exit' as it will skip the termination cleanup steps
|
||||
|
||||
set_perm <target> <owner> <group> <permission> [context]
|
||||
@@ -214,7 +214,7 @@ Same as mentioned above, actually making the folder *disappear* is not worth the
|
||||
In Magisk, you can run boot scripts in 2 different modes: **post-fs-data** and **late_start service** mode.
|
||||
|
||||
- post-fs-data mode
|
||||
- This stage is BLOCKING. Boot process is paused before execution is done, or 10 seconds has passed.
|
||||
- This stage is BLOCKING. The boot process is paused before execution is done, or 10 seconds have passed.
|
||||
- Scripts run before any modules are mounted. This allows a module developer to dynamically adjust their modules before it gets mounted.
|
||||
- This stage happens before Zygote is started, which pretty much means everything in Android
|
||||
- **Run scripts in this mode only if necessary!**
|
||||
@@ -247,7 +247,7 @@ Overlay files shall be placed in the `overlay.d` folder in boot image ramdisk, a
|
||||
2. Existing files can be replaced by files located at the same relative path
|
||||
3. Files that correspond to a non-existing file will be ignored
|
||||
|
||||
In order to have additional files which you want to reference in your custom `*.rc` scripts, add them in `overlay.d/sbin`. The 3 rules above does not apply to everything in this specific folder, as they will directly be copied to Magisk's internal `tmpfs` directory (which used to always be located at `/sbin`).
|
||||
In order to have additional files that you want to reference in your custom `*.rc` scripts, add them in `overlay.d/sbin`. The 3 rules above does not apply to everything in this specific folder, as they will directly be copied to Magisk's internal `tmpfs` directory (which used to always be located at `/sbin`).
|
||||
|
||||
Due to changes in Android 11, the `/sbin` folder is no longer guaranteed to exist. In that case, Magisk randomly generates the `tmpfs` folder. Every occurrence of the pattern `${MAGISKTMP}` in your `*.rc` scripts will be replaced with the Magisk `tmpfs` folder when `magiskinit` injects it into `init.rc`. This also works on pre Android 11 devices as `${MAGISKTMP}` will simply be replaced with `/sbin` in this case, so the best practice is to **NEVER** hardcode `/sbin` in your `*.rc` scripts when referencing additional files.
|
||||
|
||||
|
@@ -89,7 +89,7 @@ Each device has its own key combo to boot into recovery, as an example for Galax
|
||||
Unlocking BL on modern Samsung devices have some caveats, so I figure this would be helpful.
|
||||
|
||||
- Allow bootloader unlocking in **Developer options → OEM unlocking**
|
||||
- Reboot to download mode: reboot and press the download mode key combo for your device.
|
||||
- Reboot to download mode: power off your device and press the download mode key combo for your device (usually Power + Vol Down + Bixby).
|
||||
- Long press volume up to unlock the bootloader. **This will wipe your data and automatically reboot.**
|
||||
|
||||
If you think the bootloader is fully unlocked, it is actually not! Samsung introduced `VaultKeeper`, meaning the bootloader will still reject any unofficial partitions before `VaultKeeper` explicitly allows it.
|
||||
@@ -109,8 +109,9 @@ If you think the bootloader is fully unlocked, it is actually not! Samsung intro
|
||||
- Copy the patched tar file to your PC with ADB:<br>
|
||||
`adb pull /sdcard/Download/magisk_patched.tar`<br>
|
||||
Do **NOT** use MTP as it is reported to corrupt files.
|
||||
- Reboot to download mode. Open Odin on your PC, and flash `magisk_patched.tar` as `AP`, together with `BL`, `CP`, and `CSC` (**NOT** `HOME_CSC` because we want to **wipe data**) from the original firmware.
|
||||
- After Odin is done, your device should reboot. You may continue with standard initial setup.
|
||||
- Reboot to download mode. Open Odin on your PC, and flash `magisk_patched.tar` as `AP`, together with `BL`, `CP`, and `CSC` (**NOT** `HOME_CSC` because we want to **wipe data**) from the original firmware. This may take some time (>10 mins).
|
||||
- After Odin is done, your device should reboot. You may continue with standard initial setup.<br>
|
||||
If you are stuck in a bootloop, agree to do a factory reset if promted.
|
||||
- If your device does **NOT** have boot ramdisk, reboot to recovery now to boot Android with Magisk (reason stated in [Magisk in Recovery](#magisk-in-recovery)).
|
||||
- Although Magisk is installed, it still need some additional setup. Please connect to the Internet.
|
||||
- Install the latest Magisk Manager and open the app. It should show a dialog asking for additional setups. Let it do its job and the app will automatically reboot your device.
|
||||
|
14
docs/releases/21200.md
Normal file
14
docs/releases/21200.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## 2020.12.28 Magisk v21.2
|
||||
|
||||
v21.2 is a maintenance update, mostly addressing bugs, and expanding device compatibility. Checkout the full [v21.0 release notes](https://topjohnwu.github.io/Magisk/releases/21000.html) if coming from older releases.
|
||||
|
||||
### v21.2
|
||||
|
||||
- [MagiskInit] Detect 2SI after mounting `system_root` on legacy SAR devices
|
||||
- [General] Make sure `post-fs-data` scripts cannot block more than 35 seconds
|
||||
- [General] Fix the `magisk --install-module` command
|
||||
- [General] Trim Windows newline when reading files
|
||||
- [General] Directly log to file to prevent `logcat` weirdness
|
||||
- [MagiskBoot] Fix header dump/load for header v3 images
|
||||
|
||||
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)
|
@@ -17,11 +17,7 @@ org.gradle.jvmargs=-Xmx2560m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
org.gradle.parallel=true
|
||||
|
||||
# When set to true the Gradle daemon is used to run the build. For local developer builds this is our favorite property.
|
||||
# The developer environment is optimized for speed and feedback so we nearly always run Gradle jobs with the daemon.
|
||||
org.gradle.daemon=true
|
||||
|
||||
#AndroidX
|
||||
# Android
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=false
|
||||
android.enableR8.fullMode=true
|
||||
@@ -29,3 +25,8 @@ android.databinding.incremental=true
|
||||
|
||||
android.injected.testOnly=false
|
||||
kapt.incremental.apt=true
|
||||
|
||||
# Magisk
|
||||
magisk.versionCode=21200
|
||||
magisk.ndkVersion=21d
|
||||
magisk.fullNdkVersion=21.3.6528147
|
||||
|
@@ -15,7 +15,6 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
static bool pfs_done = false;
|
||||
static bool safe_mode = false;
|
||||
|
||||
/*********
|
||||
@@ -61,7 +60,7 @@ static void mount_mirrors() {
|
||||
char buf1[4096];
|
||||
char buf2[4096];
|
||||
|
||||
LOGI("* Mounting mirrors");
|
||||
LOGI("* Mounting mirrors\n");
|
||||
|
||||
parse_mnt("/proc/mounts", [&](mntent *me) {
|
||||
struct stat st;
|
||||
@@ -222,39 +221,6 @@ void unlock_blocks() {
|
||||
}
|
||||
}
|
||||
|
||||
static void collect_logs(bool reset) {
|
||||
static bool running = false;
|
||||
static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
{
|
||||
mutex_guard lock(log_lock);
|
||||
if (running)
|
||||
return;
|
||||
int test = exec_command_sync("/system/bin/logcat", "-d", "-f", "/dev/null");
|
||||
chmod("/dev/null", 0666);
|
||||
if (test != 0)
|
||||
return;
|
||||
running = true;
|
||||
}
|
||||
if (reset)
|
||||
rename(LOGFILE, LOGFILE ".bak");
|
||||
// Start a daemon thread and wait indefinitely
|
||||
new_daemon_thread([]{
|
||||
int fd = xopen(LOGFILE, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
|
||||
exec_t exec {
|
||||
.fd = fd,
|
||||
.fork = fork_no_zombie
|
||||
};
|
||||
int pid = exec_command(exec, "/system/bin/logcat", "-s", "Magisk");
|
||||
close(fd);
|
||||
if (pid < 0) {
|
||||
mutex_guard lock(log_lock);
|
||||
running = false;
|
||||
} else {
|
||||
waitpid(pid, nullptr, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8)))
|
||||
|
||||
static bool check_key_combo() {
|
||||
@@ -302,22 +268,27 @@ static bool check_key_combo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/****************
|
||||
* Entry points *
|
||||
****************/
|
||||
/***********************
|
||||
* Boot Stage Handlers *
|
||||
***********************/
|
||||
|
||||
static pthread_mutex_t stage_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void post_fs_data(int client) {
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
|
||||
mutex_guard lock(stage_lock);
|
||||
|
||||
if (getenv("REMOUNT_ROOT"))
|
||||
xmount(nullptr, "/", nullptr, MS_REMOUNT | MS_RDONLY, nullptr);
|
||||
|
||||
if (!check_data())
|
||||
goto unblock_init;
|
||||
|
||||
collect_logs(true);
|
||||
DAEMON_STATE = STATE_POST_FS_DATA;
|
||||
setup_logfile(true);
|
||||
|
||||
LOGI("** post-fs-data mode running\n");
|
||||
|
||||
@@ -353,25 +324,27 @@ void post_fs_data(int client) {
|
||||
handle_modules();
|
||||
}
|
||||
|
||||
pfs_done = true;
|
||||
|
||||
early_abort:
|
||||
// We still do magic mount because root itself might need it
|
||||
magic_mount();
|
||||
DAEMON_STATE = STATE_POST_FS_DATA_DONE;
|
||||
|
||||
unblock_init:
|
||||
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT, 0));
|
||||
}
|
||||
|
||||
void late_start(int client) {
|
||||
LOGI("** late_start service mode running\n");
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
|
||||
collect_logs(false);
|
||||
mutex_guard lock(stage_lock);
|
||||
run_finally fin([]{ DAEMON_STATE = STATE_LATE_START_DONE; });
|
||||
setup_logfile(false);
|
||||
|
||||
if (!pfs_done || safe_mode)
|
||||
LOGI("** late_start service mode running\n");
|
||||
|
||||
if (DAEMON_STATE < STATE_POST_FS_DATA_DONE || safe_mode)
|
||||
return;
|
||||
|
||||
exec_common_scripts("service");
|
||||
@@ -379,12 +352,15 @@ void late_start(int client) {
|
||||
}
|
||||
|
||||
void boot_complete(int client) {
|
||||
LOGI("** boot_complete triggered\n");
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
|
||||
collect_logs(false);
|
||||
mutex_guard lock(stage_lock);
|
||||
DAEMON_STATE = STATE_BOOT_COMPLETE;
|
||||
setup_logfile(false);
|
||||
|
||||
LOGI("** boot_complete triggered\n");
|
||||
|
||||
if (safe_mode)
|
||||
return;
|
||||
@@ -393,20 +369,20 @@ void boot_complete(int client) {
|
||||
if (access(SECURE_DIR, F_OK) != 0)
|
||||
xmkdir(SECURE_DIR, 0700);
|
||||
|
||||
if (pfs_done)
|
||||
auto_start_magiskhide();
|
||||
auto_start_magiskhide();
|
||||
|
||||
if (access(MANAGERAPK, F_OK) == 0) {
|
||||
// Install Magisk Manager if exists
|
||||
rename(MANAGERAPK, "/data/magisk.apk");
|
||||
install_apk("/data/magisk.apk");
|
||||
} else {
|
||||
// Check whether we have manager installed
|
||||
if (!check_manager()) {
|
||||
if (!check_manager()) {
|
||||
if (access(MANAGERAPK, F_OK) == 0) {
|
||||
// Only try to install APK when no manager is installed
|
||||
// Magisk Manager should be upgraded by itself, not through recovery installs
|
||||
rename(MANAGERAPK, "/data/magisk.apk");
|
||||
install_apk("/data/magisk.apk");
|
||||
} else {
|
||||
// Install stub
|
||||
auto init = MAGISKTMP + "/magiskinit";
|
||||
exec_command_sync(init.data(), "-x", "manager", "/data/magisk.apk");
|
||||
install_apk("/data/magisk.apk");
|
||||
}
|
||||
}
|
||||
unlink(MANAGERAPK);
|
||||
}
|
||||
|
@@ -14,17 +14,18 @@
|
||||
#include <db.hpp>
|
||||
#include <resetprop.hpp>
|
||||
#include <flags.hpp>
|
||||
#include <stream.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int SDK_INT = -1;
|
||||
bool RECOVERY_MODE = false;
|
||||
string MAGISKTMP;
|
||||
int DAEMON_STATE = STATE_UNKNOWN;
|
||||
int DAEMON_STATE = STATE_NONE;
|
||||
|
||||
static struct stat self_st;
|
||||
|
||||
static bool verify_client(int client, pid_t pid) {
|
||||
static bool verify_client(pid_t pid) {
|
||||
// Verify caller is the same as server
|
||||
char path[32];
|
||||
sprintf(path, "/proc/%d/exe", pid);
|
||||
@@ -70,11 +71,14 @@ static void handle_request(int client) {
|
||||
// Verify client credentials
|
||||
ucred cred;
|
||||
get_client_cred(client, &cred);
|
||||
if (cred.uid != 0 && !verify_client(client, cred.pid))
|
||||
if (cred.uid != 0 && !verify_client(cred.pid))
|
||||
goto shortcut;
|
||||
|
||||
req_code = read_int(client);
|
||||
if (req_code < 0 || req_code >= DAEMON_CODE_END)
|
||||
goto shortcut;
|
||||
|
||||
// Check client permissions
|
||||
req_code = read_int(client);
|
||||
switch (req_code) {
|
||||
case MAGISKHIDE:
|
||||
case POST_FS_DATA:
|
||||
@@ -95,19 +99,8 @@ static void handle_request(int client) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Simple requests
|
||||
switch (req_code) {
|
||||
// In case of init trigger launches, set the corresponding states
|
||||
case POST_FS_DATA:
|
||||
DAEMON_STATE = STATE_POST_FS_DATA;
|
||||
break;
|
||||
case LATE_START:
|
||||
DAEMON_STATE = STATE_LATE_START;
|
||||
break;
|
||||
case BOOT_COMPLETE:
|
||||
DAEMON_STATE = STATE_BOOT_COMPLETE;
|
||||
break;
|
||||
|
||||
// Simple requests to query daemon info
|
||||
case CHECK_VERSION:
|
||||
write_string(client, MAGISK_VERSION ":MAGISK");
|
||||
goto shortcut;
|
||||
@@ -117,26 +110,95 @@ static void handle_request(int client) {
|
||||
case GET_PATH:
|
||||
write_string(client, MAGISKTMP.data());
|
||||
goto shortcut;
|
||||
case DO_NOTHING:
|
||||
case START_DAEMON:
|
||||
setup_logfile(true);
|
||||
goto shortcut;
|
||||
}
|
||||
|
||||
// Create new thread to handle complex requests
|
||||
new_daemon_thread(std::bind(&request_handler, client, req_code, cred));
|
||||
new_daemon_thread([=] { return request_handler(client, req_code, cred); });
|
||||
return;
|
||||
|
||||
shortcut:
|
||||
close(client);
|
||||
}
|
||||
|
||||
#define vlog __android_log_vprint
|
||||
static shared_ptr<FILE> log_file;
|
||||
|
||||
atomic_flag file_backed = ATOMIC_FLAG_INIT;
|
||||
static char *log_buf;
|
||||
static size_t log_buf_len;
|
||||
|
||||
void setup_logfile(bool reset) {
|
||||
if (file_backed.test_and_set(memory_order_relaxed))
|
||||
return;
|
||||
if (reset)
|
||||
rename(LOGFILE, LOGFILE ".bak");
|
||||
|
||||
int fd = xopen(LOGFILE, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
|
||||
if (fd < 0) {
|
||||
log_file.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Dump all logs in memory (if exists)
|
||||
if (log_buf)
|
||||
write(fd, log_buf, log_buf_len);
|
||||
|
||||
if (FILE *fp = fdopen(fd, "a")) {
|
||||
setbuf(fp, nullptr);
|
||||
log_file.reset(fp, &fclose);
|
||||
}
|
||||
}
|
||||
|
||||
static int magisk_log(int prio, const char *fmt, va_list ap) {
|
||||
va_list args;
|
||||
va_copy(args, ap);
|
||||
|
||||
// Log to logcat
|
||||
__android_log_vprint(prio, "Magisk", fmt, ap);
|
||||
|
||||
auto local_log_file = log_file;
|
||||
if (!local_log_file)
|
||||
return 0;
|
||||
|
||||
char buf[4096];
|
||||
timeval tv;
|
||||
tm tm;
|
||||
char type;
|
||||
switch (prio) {
|
||||
case ANDROID_LOG_DEBUG:
|
||||
type = 'D';
|
||||
break;
|
||||
case ANDROID_LOG_INFO:
|
||||
type = 'I';
|
||||
break;
|
||||
case ANDROID_LOG_WARN:
|
||||
type = 'W';
|
||||
break;
|
||||
default:
|
||||
type = 'E';
|
||||
break;
|
||||
}
|
||||
gettimeofday(&tv, nullptr);
|
||||
localtime_r(&tv.tv_sec, &tm);
|
||||
size_t len = strftime(buf, sizeof(buf), "%m-%d %T", &tm);
|
||||
int ms = tv.tv_usec / 1000;
|
||||
len += sprintf(buf + len, ".%03d %c : ", ms, type);
|
||||
strcpy(buf + len, fmt);
|
||||
return vfprintf(local_log_file.get(), buf, args);
|
||||
}
|
||||
|
||||
static void android_logging() {
|
||||
static constexpr char TAG[] = "Magisk";
|
||||
log_cb.d = [](auto fmt, auto ap){ return vlog(ANDROID_LOG_DEBUG, TAG, fmt, ap); };
|
||||
log_cb.i = [](auto fmt, auto ap){ return vlog(ANDROID_LOG_INFO, TAG, fmt, ap); };
|
||||
log_cb.w = [](auto fmt, auto ap){ return vlog(ANDROID_LOG_WARN, TAG, fmt, ap); };
|
||||
log_cb.e = [](auto fmt, auto ap){ return vlog(ANDROID_LOG_ERROR, TAG, fmt, ap); };
|
||||
auto in_mem_file = make_stream_fp<byte_stream>(log_buf, log_buf_len);
|
||||
log_file.reset(in_mem_file.release(), [](FILE *) {
|
||||
free(log_buf);
|
||||
log_buf = nullptr;
|
||||
});
|
||||
log_cb.d = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_DEBUG, fmt, ap); };
|
||||
log_cb.i = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_INFO, fmt, ap); };
|
||||
log_cb.w = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_WARN, fmt, ap); };
|
||||
log_cb.e = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_ERROR, fmt, ap); };
|
||||
log_cb.ex = nop_ex;
|
||||
}
|
||||
|
||||
|
@@ -359,7 +359,7 @@ bool validate_manager(string &pkg, int userid, struct stat *st) {
|
||||
// Check the official package name
|
||||
sprintf(app_path, "%s/%d/" JAVA_PACKAGE_NAME, APP_DATA_DIR, userid);
|
||||
if (stat(app_path, st)) {
|
||||
LOGE("su: cannot find manager");
|
||||
LOGE("su: cannot find manager\n");
|
||||
memset(st, 0, sizeof(*st));
|
||||
pkg.clear();
|
||||
return false;
|
||||
|
@@ -81,7 +81,7 @@ int magisk_main(int argc, char *argv[]) {
|
||||
return 0;
|
||||
} else if (argv[1] == "--daemon"sv) {
|
||||
int fd = connect_daemon(true);
|
||||
write_int(fd, DO_NOTHING);
|
||||
write_int(fd, START_DAEMON);
|
||||
return 0;
|
||||
} else if (argv[1] == "--post-fs-data"sv) {
|
||||
int fd = connect_daemon(true);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user