mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-23 15:07:31 +00:00
Move :stub to :app:stub
This commit is contained in:
3
app/stub/.gitignore
vendored
Normal file
3
app/stub/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/build
|
||||
/src/release/AndroidManifest.xml
|
||||
/src/debug/AndroidManifest.xml
|
45
app/stub/build.gradle.kts
Normal file
45
app/stub/build.gradle.kts
Normal file
@@ -0,0 +1,45 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.lsposed.lsparanoid")
|
||||
}
|
||||
|
||||
lsparanoid {
|
||||
seed = if (RAND_SEED != 0) RAND_SEED else null
|
||||
includeDependencies = true
|
||||
global = true
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.topjohnwu.magisk"
|
||||
|
||||
val canary = !Config.version.contains(".")
|
||||
|
||||
val url = if (canary) null
|
||||
else "https://cdn.jsdelivr.net/gh/topjohnwu/magisk-files@${Config.version}/app-release.apk"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.topjohnwu.magisk"
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
buildConfigField("String", "APK_URL", url?.let { "\"$it\"" } ?: "null" )
|
||||
buildConfigField("int", "STUB_VERSION", Config.stubVersion)
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = false
|
||||
proguardFiles("proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
}
|
||||
|
||||
setupStub()
|
||||
|
||||
dependencies {
|
||||
implementation(project(":app:shared"))
|
||||
}
|
32
app/stub/proguard-rules.pro
vendored
Normal file
32
app/stub/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-obfuscationdictionary ../dict.txt
|
||||
-classobfuscationdictionary ../dict.txt
|
||||
-packageobfuscationdictionary ../dict.txt
|
||||
|
||||
# Excessive obfuscation
|
||||
-repackageclasses
|
||||
-allowaccessmodification
|
||||
-keepclassmembers class com.topjohnwu.magisk.dummy.* { <init>(); }
|
||||
-keepclassmembers class com.topjohnwu.magisk.DownloadActivity { <init>(); }
|
||||
-keepclassmembers class com.topjohnwu.magisk.DelegateRootService { <init>(); }
|
13
app/stub/src/main/AndroidManifest.xml
Normal file
13
app/stub/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission
|
||||
android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
|
||||
android:maxSdkVersion="25" />
|
||||
|
||||
<application tools:ignore="MissingApplicationIcon">
|
||||
</application>
|
||||
|
||||
</manifest>
|
137
app/stub/src/main/java/com/topjohnwu/magisk/ClassLoaders.java
Normal file
137
app/stub/src/main/java/com/topjohnwu/magisk/ClassLoaders.java
Normal file
@@ -0,0 +1,137 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.job.JobService;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
|
||||
import com.topjohnwu.magisk.dummy.DummyProvider;
|
||||
import com.topjohnwu.magisk.dummy.DummyReceiver;
|
||||
import com.topjohnwu.magisk.dummy.DummyService;
|
||||
import com.topjohnwu.magisk.utils.DynamicClassLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
// Wrap the actual classloader as we only want to resolve classname
|
||||
// mapping when loading from platform (via LoadedApk.mClassLoader)
|
||||
class AppClassLoader extends ClassLoader {
|
||||
final Map<String, String> mapping = new HashMap<>();
|
||||
|
||||
AppClassLoader(File apk) {
|
||||
super(new DynamicClassLoader(apk));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
String clz = mapping.get(name);
|
||||
name = clz != null ? clz : name;
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
|
||||
void updateComponentMap(PackageInfo stub, PackageInfo app) {
|
||||
{
|
||||
var src = stub.activities;
|
||||
var dest = app.activities;
|
||||
|
||||
final ActivityInfo sa;
|
||||
final ActivityInfo da;
|
||||
final ActivityInfo sb;
|
||||
final ActivityInfo db;
|
||||
if (src[0].exported) {
|
||||
sa = src[0];
|
||||
sb = src[1];
|
||||
} else {
|
||||
sa = src[1];
|
||||
sb = src[0];
|
||||
}
|
||||
if (dest[0].exported) {
|
||||
da = dest[0];
|
||||
db = dest[1];
|
||||
} else {
|
||||
da = dest[1];
|
||||
db = dest[0];
|
||||
}
|
||||
mapping.put(sa.name, da.name);
|
||||
mapping.put(sb.name, db.name);
|
||||
}
|
||||
|
||||
{
|
||||
var src = stub.services;
|
||||
var dest = app.services;
|
||||
|
||||
final ServiceInfo sa;
|
||||
final ServiceInfo da;
|
||||
final ServiceInfo sb;
|
||||
final ServiceInfo db;
|
||||
if (JobService.PERMISSION_BIND.equals(src[0].permission)) {
|
||||
sa = src[0];
|
||||
sb = src[1];
|
||||
} else {
|
||||
sa = src[1];
|
||||
sb = src[0];
|
||||
}
|
||||
if (JobService.PERMISSION_BIND.equals(dest[0].permission)) {
|
||||
da = dest[0];
|
||||
db = dest[1];
|
||||
} else {
|
||||
da = dest[1];
|
||||
db = dest[0];
|
||||
}
|
||||
mapping.put(sa.name, da.name);
|
||||
mapping.put(sb.name, db.name);
|
||||
}
|
||||
|
||||
{
|
||||
var src = stub.receivers;
|
||||
var dest = app.receivers;
|
||||
mapping.put(src[0].name, dest[0].name);
|
||||
}
|
||||
|
||||
{
|
||||
var src = stub.providers;
|
||||
var dest = app.providers;
|
||||
mapping.put(src[0].name, dest[0].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StubClassLoader extends ClassLoader {
|
||||
|
||||
private final Map<String, Class<?>> mapping = new HashMap<>();
|
||||
|
||||
StubClassLoader(PackageInfo info) {
|
||||
super(StubClassLoader.class.getClassLoader());
|
||||
for (var c : info.activities) {
|
||||
mapping.put(c.name, DownloadActivity.class);
|
||||
}
|
||||
for (var c : info.services) {
|
||||
mapping.put(c.name, DummyService.class);
|
||||
}
|
||||
for (var c : info.providers) {
|
||||
mapping.put(c.name, DummyProvider.class);
|
||||
}
|
||||
for (var c : info.receivers) {
|
||||
mapping.put(c.name, DummyReceiver.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
Class<?> clz = mapping.get(name);
|
||||
return clz == null ? super.loadClass(name, resolve) : clz;
|
||||
}
|
||||
}
|
||||
|
||||
class DelegateClassLoader extends ClassLoader {
|
||||
|
||||
DelegateClassLoader() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
return DynLoad.activeClassLoader.loadClass(name);
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
|
||||
public class DelegateApplication extends Application {
|
||||
|
||||
private Application receiver;
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
receiver = DynLoad.createAndSetupApp(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
if (receiver != null)
|
||||
receiver.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (receiver != null)
|
||||
receiver.onConfigurationChanged(newConfig);
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AppComponentFactory;
|
||||
import android.app.Application;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
|
||||
import com.topjohnwu.magisk.dummy.DummyProvider;
|
||||
import com.topjohnwu.magisk.dummy.DummyReceiver;
|
||||
import com.topjohnwu.magisk.dummy.DummyService;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public class DelegateComponentFactory extends AppComponentFactory {
|
||||
|
||||
AppComponentFactory receiver;
|
||||
|
||||
public DelegateComponentFactory() {
|
||||
DynLoad.componentFactory = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader instantiateClassLoader(ClassLoader cl, ApplicationInfo info) {
|
||||
return new DelegateClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Application instantiateApplication(ClassLoader cl, String className) {
|
||||
return new DelegateApplication();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Activity instantiateActivity(ClassLoader cl, String className, Intent intent)
|
||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||
if (receiver != null)
|
||||
return receiver.instantiateActivity(DynLoad.activeClassLoader, className, intent);
|
||||
return create(className, DownloadActivity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, Intent intent)
|
||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||
if (receiver != null)
|
||||
return receiver.instantiateReceiver(DynLoad.activeClassLoader, className, intent);
|
||||
return create(className, DummyReceiver.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Service instantiateService(ClassLoader cl, String className, Intent intent)
|
||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||
if (receiver != null)
|
||||
return receiver.instantiateService(DynLoad.activeClassLoader, className, intent);
|
||||
return create(className, DummyService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentProvider instantiateProvider(ClassLoader cl, String className)
|
||||
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||
if (receiver != null)
|
||||
return receiver.instantiateProvider(DynLoad.activeClassLoader, className);
|
||||
return create(className, DummyProvider.class);
|
||||
}
|
||||
|
||||
private <T> T create(String name, Class<T> fallback)
|
||||
throws IllegalAccessException, InstantiationException {
|
||||
try {
|
||||
// noinspection unchecked
|
||||
return (T) DynLoad.activeClassLoader.loadClass(name).newInstance();
|
||||
} catch (ClassNotFoundException e) {
|
||||
return fallback.newInstance();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
public class DelegateRootService extends ContextWrapper {
|
||||
|
||||
public DelegateRootService() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
ClassLoader loader = DynLoad.loadApk(base);
|
||||
if (loader == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
// Create application to get the real root service class
|
||||
var data = DynLoad.createApkData();
|
||||
File apk = StubApk.current(base);
|
||||
PackageManager pm = base.getPackageManager();
|
||||
PackageInfo pkgInfo = pm.getPackageArchiveInfo(apk.getPath(), 0);
|
||||
loader.loadClass(pkgInfo.applicationInfo.className)
|
||||
.getConstructor(Object.class)
|
||||
.newInstance(data.getObject());
|
||||
|
||||
// Create the actual RootService and call its attachBaseContext
|
||||
Constructor<?> ctor = data.getRootService().getConstructor(Object.class);
|
||||
ctor.setAccessible(true);
|
||||
Object service = ctor.newInstance(this);
|
||||
DynLoad.attachContext(service, base);
|
||||
} catch (Exception e) {
|
||||
Log.e(DelegateRootService.class.getSimpleName(), "", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,201 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import static android.R.string.no;
|
||||
import static android.R.string.ok;
|
||||
import static android.R.string.yes;
|
||||
import static com.topjohnwu.magisk.R.string.dling;
|
||||
import static com.topjohnwu.magisk.R.string.no_internet_msg;
|
||||
import static com.topjohnwu.magisk.R.string.upgrade_msg;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.loader.ResourcesLoader;
|
||||
import android.content.res.loader.ResourcesProvider;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
import android.util.Log;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
||||
import com.topjohnwu.magisk.net.Networking;
|
||||
import com.topjohnwu.magisk.net.Request;
|
||||
import com.topjohnwu.magisk.utils.APKInstall;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class DownloadActivity extends Activity {
|
||||
|
||||
private static final String APP_NAME = "Magisk";
|
||||
private static final String JSON_URL = BuildConfig.DEBUG ?
|
||||
"https://topjohnwu.github.io/magisk-files/debug.json" :
|
||||
"https://topjohnwu.github.io/magisk-files/canary.json";
|
||||
|
||||
private String apkLink = BuildConfig.APK_URL;
|
||||
private Context themed;
|
||||
private ProgressDialog dialog;
|
||||
private boolean dynLoad;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (DynLoad.activeClassLoader instanceof AppClassLoader) {
|
||||
// For some reason activity is created before Application.attach(),
|
||||
// relaunch the activity using the same intent
|
||||
finishAffinity();
|
||||
startActivity(getIntent());
|
||||
return;
|
||||
}
|
||||
|
||||
themed = new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault);
|
||||
|
||||
// Only download and dynamic load full APK if hidden
|
||||
dynLoad = !getPackageName().equals(BuildConfig.APPLICATION_ID);
|
||||
|
||||
// Inject resources
|
||||
try {
|
||||
loadResources();
|
||||
} catch (Exception e) {
|
||||
error(e);
|
||||
}
|
||||
|
||||
ProviderInstaller.install(this);
|
||||
|
||||
if (Networking.checkNetworkStatus(this)) {
|
||||
if (BuildConfig.APK_URL == null) {
|
||||
fetchCanary();
|
||||
} else {
|
||||
showDialog();
|
||||
}
|
||||
} else {
|
||||
new AlertDialog.Builder(themed)
|
||||
.setCancelable(false)
|
||||
.setTitle(APP_NAME)
|
||||
.setMessage(getString(no_internet_msg))
|
||||
.setNegativeButton(ok, (d, w) -> finish())
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
super.finish();
|
||||
Runtime.getRuntime().exit(0);
|
||||
}
|
||||
|
||||
private void error(Throwable e) {
|
||||
Log.e(getClass().getSimpleName(), Log.getStackTraceString(e));
|
||||
finish();
|
||||
}
|
||||
|
||||
private Request request(String url) {
|
||||
return Networking.get(url).setErrorHandler((conn, e) -> error(e));
|
||||
}
|
||||
|
||||
private void showDialog() {
|
||||
new AlertDialog.Builder(themed)
|
||||
.setCancelable(false)
|
||||
.setTitle(APP_NAME)
|
||||
.setMessage(getString(upgrade_msg))
|
||||
.setPositiveButton(yes, (d, w) -> dlAPK())
|
||||
.setNegativeButton(no, (d, w) -> finish())
|
||||
.show();
|
||||
}
|
||||
|
||||
private void fetchCanary() {
|
||||
dialog = ProgressDialog.show(themed, "", "", true);
|
||||
request(JSON_URL).getAsJSONObject(json -> {
|
||||
dialog.dismiss();
|
||||
try {
|
||||
apkLink = json.getJSONObject("magisk").getString("link");
|
||||
showDialog();
|
||||
} catch (JSONException e) {
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void dlAPK() {
|
||||
dialog = ProgressDialog.show(themed, getString(dling), getString(dling) + " " + APP_NAME, true);
|
||||
// Download and upgrade the app
|
||||
var request = request(apkLink).setExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
if (dynLoad) {
|
||||
request.getAsFile(StubApk.current(this), file -> StubApk.restartProcess(this));
|
||||
} else {
|
||||
request.getAsInputStream(input -> {
|
||||
var session = APKInstall.startSession(this);
|
||||
try (input; var out = session.openStream(this)) {
|
||||
if (out != null)
|
||||
APKInstall.transfer(input, out);
|
||||
} catch (IOException e) {
|
||||
error(e);
|
||||
}
|
||||
Intent intent = session.waitIntent();
|
||||
if (intent != null)
|
||||
startActivity(intent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void decryptResources(OutputStream out) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
SecretKey key = new SecretKeySpec(Bytes.key(), "AES");
|
||||
IvParameterSpec iv = new IvParameterSpec(Bytes.iv());
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, iv);
|
||||
var is = new InflaterInputStream(new CipherInputStream(
|
||||
new ByteArrayInputStream(Bytes.res()), cipher));
|
||||
try (is; out) {
|
||||
APKInstall.transfer(is, out);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadResources() throws Exception {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
var fd = Os.memfd_create("res", 0);
|
||||
try {
|
||||
decryptResources(new FileOutputStream(fd));
|
||||
Os.lseek(fd, 0, OsConstants.SEEK_SET);
|
||||
var loader = new ResourcesLoader();
|
||||
try (var pfd = ParcelFileDescriptor.dup(fd)) {
|
||||
loader.addProvider(ResourcesProvider.loadFromTable(pfd, null));
|
||||
getResources().addLoaders(loader);
|
||||
}
|
||||
} finally {
|
||||
Os.close(fd);
|
||||
}
|
||||
} else {
|
||||
File res = new File(getCodeCacheDir(), "res.apk");
|
||||
try (var out = new ZipOutputStream(new FileOutputStream(res))) {
|
||||
// AndroidManifest.xml is reuqired on Android 6-, and directory support is broken on Android 9-10
|
||||
out.putNextEntry(new ZipEntry("AndroidManifest.xml"));
|
||||
try (var stubApk = new ZipFile(getPackageCodePath())) {
|
||||
APKInstall.transfer(stubApk.getInputStream(stubApk.getEntry("AndroidManifest.xml")), out);
|
||||
}
|
||||
out.putNextEntry(new ZipEntry("resources.arsc"));
|
||||
decryptResources(out);
|
||||
}
|
||||
StubApk.addAssetPath(getResources(), res.getPath());
|
||||
}
|
||||
}
|
||||
}
|
201
app/stub/src/main/java/com/topjohnwu/magisk/DynLoad.java
Normal file
201
app/stub/src/main/java/com/topjohnwu/magisk/DynLoad.java
Normal file
@@ -0,0 +1,201 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import static com.topjohnwu.magisk.BuildConfig.APPLICATION_ID;
|
||||
|
||||
import android.app.AppComponentFactory;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.topjohnwu.magisk.utils.APKInstall;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public class DynLoad {
|
||||
|
||||
static Object componentFactory;
|
||||
static ClassLoader activeClassLoader = DynLoad.class.getClassLoader();
|
||||
|
||||
static StubApk.Data createApkData() {
|
||||
var data = new StubApk.Data();
|
||||
data.setVersion(BuildConfig.STUB_VERSION);
|
||||
data.setClassToComponent(new HashMap<>());
|
||||
data.setRootService(DelegateRootService.class);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void attachContext(Object o, Context context) {
|
||||
if (!(o instanceof ContextWrapper))
|
||||
return;
|
||||
try {
|
||||
Method m = ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
|
||||
m.setAccessible(true);
|
||||
m.invoke(o, context);
|
||||
} catch (Exception ignored) { /* Impossible */ }
|
||||
}
|
||||
|
||||
// Dynamically load APK from internal, external storage, or previous app
|
||||
static AppClassLoader loadApk(Context context) {
|
||||
File apk = StubApk.current(context);
|
||||
File update = StubApk.update(context);
|
||||
|
||||
if (update.exists()) {
|
||||
// Rename from update
|
||||
update.renameTo(apk);
|
||||
}
|
||||
|
||||
// Copy from external for easier development
|
||||
if (BuildConfig.DEBUG) {
|
||||
try {
|
||||
File external = new File(context.getExternalFilesDir(null), "magisk.apk");
|
||||
if (external.exists()) {
|
||||
apk.delete();
|
||||
try {
|
||||
var in = new FileInputStream(external);
|
||||
var out = new FileOutputStream(apk);
|
||||
apk.setReadOnly();
|
||||
try (in; out) {
|
||||
APKInstall.transfer(in, out);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||
apk.delete();
|
||||
} finally {
|
||||
external.delete();
|
||||
}
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
// Do not crash in root service
|
||||
}
|
||||
}
|
||||
|
||||
if (apk.exists()) {
|
||||
apk.setReadOnly();
|
||||
return new AppClassLoader(apk);
|
||||
}
|
||||
|
||||
// If no APK is loaded, attempt to copy from previous app
|
||||
if (!context.getPackageName().equals(APPLICATION_ID)) {
|
||||
try {
|
||||
var info = context.getPackageManager().getApplicationInfo(APPLICATION_ID, 0);
|
||||
apk.delete();
|
||||
var src = new FileInputStream(info.sourceDir);
|
||||
var out = new FileOutputStream(apk);
|
||||
apk.setReadOnly();
|
||||
try (src; out) {
|
||||
APKInstall.transfer(src, out);
|
||||
}
|
||||
return new AppClassLoader(apk);
|
||||
} catch (PackageManager.NameNotFoundException ignored) {
|
||||
} catch (IOException e) {
|
||||
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||
apk.delete();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Dynamically load APK and create the Application instance from the loaded APK
|
||||
static Application createAndSetupApp(Application context) {
|
||||
// On API >= 29, AppComponentFactory will replace the ClassLoader for us
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
|
||||
replaceClassLoader(context);
|
||||
|
||||
// noinspection InlinedApi
|
||||
int flags = PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES
|
||||
| PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS
|
||||
| PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DISABLED_COMPONENTS
|
||||
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
|
||||
var pm = context.getPackageManager();
|
||||
|
||||
final PackageInfo info;
|
||||
try {
|
||||
// noinspection WrongConstant
|
||||
info = pm.getPackageInfo(context.getPackageName(), flags);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Impossible
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
File apk = StubApk.current(context);
|
||||
|
||||
final var cl = loadApk(context);
|
||||
if (cl != null) try {
|
||||
// noinspection WrongConstant
|
||||
var pkgInfo = pm.getPackageArchiveInfo(apk.getPath(), flags);
|
||||
cl.updateComponentMap(info, pkgInfo);
|
||||
|
||||
var appInfo = pkgInfo.applicationInfo;
|
||||
|
||||
var data = createApkData();
|
||||
var map = data.getClassToComponent();
|
||||
// Create the inverse mapping (class to component name)
|
||||
for (var e : cl.mapping.entrySet()) {
|
||||
map.put(e.getValue(), e.getKey());
|
||||
}
|
||||
|
||||
// Create the receiver Application
|
||||
var app = (Application) cl.loadClass(appInfo.className)
|
||||
.getConstructor(Object.class)
|
||||
.newInstance(data.getObject());
|
||||
|
||||
// Create the receiver component factory
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && componentFactory != null) {
|
||||
var delegate = (DelegateComponentFactory) componentFactory;
|
||||
if (appInfo.appComponentFactory == null) {
|
||||
delegate.receiver = new AppComponentFactory();
|
||||
} else {
|
||||
Object factory = cl.loadClass(appInfo.appComponentFactory).newInstance();
|
||||
delegate.receiver = (AppComponentFactory) factory;
|
||||
}
|
||||
}
|
||||
|
||||
activeClassLoader = cl;
|
||||
|
||||
// Send real application to attachBaseContext
|
||||
attachContext(app, context);
|
||||
|
||||
return app;
|
||||
} catch (Exception e) {
|
||||
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||
apk.delete();
|
||||
}
|
||||
|
||||
activeClassLoader = new StubClassLoader(info);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Replace LoadedApk mClassLoader
|
||||
private static void replaceClassLoader(Context context) {
|
||||
// Get ContextImpl
|
||||
while (context instanceof ContextWrapper) {
|
||||
context = ((ContextWrapper) context).getBaseContext();
|
||||
}
|
||||
|
||||
try {
|
||||
Field mInfo = context.getClass().getDeclaredField("mPackageInfo");
|
||||
mInfo.setAccessible(true);
|
||||
Object loadedApk = mInfo.get(context);
|
||||
assert loadedApk != null;
|
||||
Field mcl = loadedApk.getClass().getDeclaredField("mClassLoader");
|
||||
mcl.setAccessible(true);
|
||||
mcl.set(loadedApk, new DelegateClassLoader());
|
||||
} catch (Exception e) {
|
||||
// Actually impossible as this method is only called on API < 29,
|
||||
// and API 23 - 28 do not restrict access to these fields.
|
||||
Log.e(DynLoad.class.getSimpleName(), "", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package com.topjohnwu.magisk.dummy;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
|
||||
public class DummyProvider extends ContentProvider {
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package com.topjohnwu.magisk.dummy;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class DummyReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.topjohnwu.magisk.dummy;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
public class DummyService extends Service {
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
package com.topjohnwu.magisk.net;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
class BadRequest extends Request {
|
||||
|
||||
private final IOException ex;
|
||||
|
||||
BadRequest(IOException e) { super(null); ex = e; }
|
||||
|
||||
@Override
|
||||
public Request addHeaders(String key, String value) { return this; }
|
||||
|
||||
@Override
|
||||
public Result<InputStream> execForInputStream() { fail(); return new Result<>(); }
|
||||
|
||||
@Override
|
||||
public void getAsFile(File out, ResponseListener<File> rs) { fail(); }
|
||||
|
||||
@Override
|
||||
public void execForFile(File out) { fail(); }
|
||||
|
||||
@Override
|
||||
public void getAsString(ResponseListener<String> rs) { fail(); }
|
||||
|
||||
@Override
|
||||
public Result<String> execForString() { fail(); return new Result<>(); }
|
||||
|
||||
@Override
|
||||
public void getAsJSONObject(ResponseListener<JSONObject> rs) { fail(); }
|
||||
|
||||
@Override
|
||||
public Result<JSONObject> execForJSONObject() { fail(); return new Result<>(); }
|
||||
|
||||
@Override
|
||||
public void getAsJSONArray(ResponseListener<JSONArray> rs) { fail(); }
|
||||
|
||||
@Override
|
||||
public Result<JSONArray> execForJSONArray() { fail(); return new Result<>(); }
|
||||
|
||||
private void fail() {
|
||||
if (err != null)
|
||||
err.onError(null, ex);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package com.topjohnwu.magisk.net;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
public interface ErrorHandler {
|
||||
void onError(HttpURLConnection conn, Exception e);
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package com.topjohnwu.magisk.net;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class Networking {
|
||||
|
||||
private static final int READ_TIMEOUT = 15000;
|
||||
private static final int CONNECT_TIMEOUT = 15000;
|
||||
static Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
private static Request request(String url, String method) {
|
||||
try {
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setRequestMethod(method);
|
||||
conn.setReadTimeout(READ_TIMEOUT);
|
||||
conn.setConnectTimeout(CONNECT_TIMEOUT);
|
||||
return new Request(conn);
|
||||
} catch (IOException e) {
|
||||
return new BadRequest(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Request get(String url) {
|
||||
return request(url, "GET");
|
||||
}
|
||||
|
||||
public static boolean checkNetworkStatus(Context context) {
|
||||
ConnectivityManager manager = (ConnectivityManager)
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
|
||||
return networkInfo != null && networkInfo.isConnected();
|
||||
}
|
||||
}
|
209
app/stub/src/main/java/com/topjohnwu/magisk/net/Request.java
Normal file
209
app/stub/src/main/java/com/topjohnwu/magisk/net/Request.java
Normal file
@@ -0,0 +1,209 @@
|
||||
package com.topjohnwu.magisk.net;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.topjohnwu.magisk.utils.APKInstall;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class Request {
|
||||
private final HttpURLConnection conn;
|
||||
private Executor executor = null;
|
||||
private int code = -1;
|
||||
|
||||
ErrorHandler err = null;
|
||||
|
||||
private interface Requestor<T> {
|
||||
T request() throws Exception;
|
||||
}
|
||||
|
||||
public class Result<T> {
|
||||
T result;
|
||||
|
||||
public T getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return code >= 200 && code <= 299;
|
||||
}
|
||||
|
||||
public HttpURLConnection getConnection() {
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
|
||||
Request(HttpURLConnection c) {
|
||||
conn = c;
|
||||
}
|
||||
|
||||
public Request addHeaders(String key, String value) {
|
||||
conn.setRequestProperty(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Request setErrorHandler(ErrorHandler handler) {
|
||||
err = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Request setExecutor(Executor e) {
|
||||
executor = e;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Result<Void> connect() {
|
||||
try {
|
||||
connect0();
|
||||
} catch (IOException e) {
|
||||
if (err != null)
|
||||
err.onError(conn, e);
|
||||
}
|
||||
return new Result<>();
|
||||
}
|
||||
|
||||
public Result<InputStream> execForInputStream() {
|
||||
return exec(this::getInputStream);
|
||||
}
|
||||
|
||||
public void getAsInputStream(ResponseListener<InputStream> rs) {
|
||||
submit(this::getInputStream, rs);
|
||||
}
|
||||
|
||||
public void getAsFile(File out, ResponseListener<File> rs) {
|
||||
submit(() -> dlFile(out), rs);
|
||||
}
|
||||
|
||||
public void execForFile(File out) {
|
||||
exec(() -> dlFile(out));
|
||||
}
|
||||
|
||||
public void getAsBytes(ResponseListener<byte[]> rs) {
|
||||
submit(this::dlBytes, rs);
|
||||
}
|
||||
|
||||
public Result<byte[]> execForBytes() {
|
||||
return exec(this::dlBytes);
|
||||
}
|
||||
|
||||
public void getAsString(ResponseListener<String> rs) {
|
||||
submit(this::dlString, rs);
|
||||
}
|
||||
|
||||
public Result<String> execForString() {
|
||||
return exec(this::dlString);
|
||||
}
|
||||
|
||||
public void getAsJSONObject(ResponseListener<JSONObject> rs) {
|
||||
submit(this::dlJSONObject, rs);
|
||||
}
|
||||
|
||||
public Result<JSONObject> execForJSONObject() {
|
||||
return exec(this::dlJSONObject);
|
||||
}
|
||||
|
||||
public void getAsJSONArray(ResponseListener<JSONArray> rs) {
|
||||
submit(this::dlJSONArray, rs);
|
||||
}
|
||||
|
||||
public Result<JSONArray> execForJSONArray() {
|
||||
return exec(this::dlJSONArray);
|
||||
}
|
||||
|
||||
private void connect0() throws IOException {
|
||||
conn.connect();
|
||||
code = conn.getResponseCode();
|
||||
}
|
||||
|
||||
private <T> Result<T> exec(Requestor<T> req) {
|
||||
Result<T> res = new Result<>();
|
||||
try {
|
||||
res.result = req.request();
|
||||
} catch (Exception e) {
|
||||
if (err != null)
|
||||
err.onError(conn, e);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private <T> void submit(Requestor<T> req, ResponseListener<T> rs) {
|
||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||
try {
|
||||
T t = req.request();
|
||||
Runnable cb = () -> rs.onResponse(t);
|
||||
if (executor == null)
|
||||
Networking.mainHandler.post(cb);
|
||||
else
|
||||
executor.execute(cb);
|
||||
} catch (Exception e) {
|
||||
if (err != null)
|
||||
err.onError(conn, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private BufferedInputStream getInputStream() throws IOException {
|
||||
connect0();
|
||||
InputStream in = new FilterInputStream(conn.getInputStream()) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
conn.disconnect();
|
||||
}
|
||||
};
|
||||
return new BufferedInputStream(in);
|
||||
}
|
||||
|
||||
private String dlString() throws IOException {
|
||||
try (Scanner s = new Scanner(getInputStream(), "UTF-8")) {
|
||||
s.useDelimiter("\\A");
|
||||
return s.next();
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject dlJSONObject() throws IOException, JSONException {
|
||||
return new JSONObject(dlString());
|
||||
}
|
||||
|
||||
private JSONArray dlJSONArray() throws IOException, JSONException {
|
||||
return new JSONArray(dlString());
|
||||
}
|
||||
|
||||
private File dlFile(File f) throws IOException {
|
||||
try (InputStream in = getInputStream();
|
||||
OutputStream out = new BufferedOutputStream(new FileOutputStream(f))) {
|
||||
APKInstall.transfer(in, out);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
private byte[] dlBytes() throws IOException {
|
||||
int len = conn.getContentLength();
|
||||
len = len > 0 ? len : 32;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(len);
|
||||
try (InputStream in = getInputStream()) {
|
||||
APKInstall.transfer(in, out);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package com.topjohnwu.magisk.net;
|
||||
|
||||
public interface ResponseListener<T> {
|
||||
void onResponse(T response);
|
||||
}
|
6
app/stub/src/main/res/values-ar/strings.xml
Normal file
6
app/stub/src/main/res/values-ar/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">عليك الترقية ماجـيسك Manager لإكمال تهيئة التطبيق.هل اكمل؟</string>
|
||||
<string name="no_internet_msg">يرجى الاتصال بالانترنيت! ترقية ماجـيسك مطلوب...</string>
|
||||
<string name="dling">جارٍ التنزيل</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-ast/strings.xml
Normal file
7
app/stub/src/main/res/values-ast/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Instala la versión completa de Magisk pa finar la configuración. ¿Quies facelo agora?</string>
|
||||
<string name="no_internet_msg">¡Conéctate a internet! Tienes d\'instalar la versión completa de Magisk.</string>
|
||||
<string name="dling">Baxando</string>
|
||||
<string name="relaunch_app">Volvi llanzar l\'aplicación manualmente</string>
|
||||
</resources>
|
4
app/stub/src/main/res/values-az/strings.xml
Normal file
4
app/stub/src/main/res/values-az/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">Qurmanı sonlandırmaq üçün full Magisk Manager`ə yüksəldin. Yüklənib qurulsun?</string>
|
||||
<string name="no_internet_msg">Lütfən internetə qoşulun! Full Magisk Manager\'ə yüksəltmə lazımidir.</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-be/strings.xml
Normal file
7
app/stub/src/main/res/values-be/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Абнавіце Magisk Manager для завяршэння ўсталёўкі. Спампаваць і ўсталяваць?</string>
|
||||
<string name="no_internet_msg">Калі ласка, падлучыцеся да інтэрнэту! Патрабуецца абнаўленне Magisk Manager.</string>
|
||||
<string name="dling">Спампоўванне</string>
|
||||
<string name="relaunch_app">Калі ласка, уласнаручна перазапусціце праграму</string>
|
||||
</resources>
|
4
app/stub/src/main/res/values-bg/strings.xml
Normal file
4
app/stub/src/main/res/values-bg/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">Надградете до пълната версия на Magisk Manager, за да довършите първоначалната настройка. Изтегляне и инсталиране сега?</string>
|
||||
<string name="no_internet_msg">Моля да се свържете към работеща интернет мрежа, защото надграждането до пълната версия на Magisk Manager е задължително.</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-ca/strings.xml
Normal file
7
app/stub/src/main/res/values-ca/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Fes una actualització total de Magisk Manager per finalitzar l\'instal·lació. Descarregar i instal·lar?</string>
|
||||
<string name="no_internet_msg">Si us plau, connecta\'t a internet! Es necessari fer una actualització total de Magisk Manager.</string>
|
||||
<string name="dling">Baixant</string>
|
||||
<string name="relaunch_app">Torni a obrir l\'aplicació manualment, si us plau</string>
|
||||
</resources>
|
1
app/stub/src/main/res/values-cs/strings.xml
Normal file
1
app/stub/src/main/res/values-cs/strings.xml
Normal file
@@ -0,0 +1 @@
|
||||
<resources></resources>
|
6
app/stub/src/main/res/values-de/strings.xml
Normal file
6
app/stub/src/main/res/values-de/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">Upgrade zum vollständigen Magisk Manager, um das Setup abzuschließen. Herunterladen und installieren?</string>
|
||||
<string name="no_internet_msg">Bitte Internetverbindung herstellen! Das Upgrade zum vollständigen Magisk Manager ist erforderlich.</string>
|
||||
<string name="dling">Herunterladen...</string>
|
||||
<string name="relaunch_app">Bitte starte die App manuell neu!</string>
|
||||
</resources>
|
1
app/stub/src/main/res/values-el/strings.xml
Normal file
1
app/stub/src/main/res/values-el/strings.xml
Normal file
@@ -0,0 +1 @@
|
||||
<resources></resources>
|
6
app/stub/src/main/res/values-es/strings.xml
Normal file
6
app/stub/src/main/res/values-es/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">Actualiza a la versión completa de Magisk para finalizar la instalación. ¿Descargar e instalar?</string>
|
||||
<string name="no_internet_msg">Sin conexión disponible</string>
|
||||
<string name="dling">Descargando...</string>
|
||||
<string name="relaunch_app">Por favor, relanza la app</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-et/strings.xml
Normal file
7
app/stub/src/main/res/values-et/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Täienda seadistuse lõpetamiseks Magiski täisversioonile. Kas laadid alla ja installid?</string>
|
||||
<string name="no_internet_msg">Palun ühendu Internetti! Nõutud on Magiski täisversioonile täiendamine.</string>
|
||||
<string name="dling">Allalaadimine</string>
|
||||
<string name="relaunch_app">Palun käivita rakendus käsitsi uuesti</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-fa/strings.xml
Normal file
7
app/stub/src/main/res/values-fa/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">برای به پایان رساندن تنظیمات ، به نسخه کامل Magisk Manager ارتقا دهید. دانلود و نصب بشه؟</string>
|
||||
<string name="no_internet_msg">لطفاً به اینترنت متصل شوید! برای ارتقا به نسخه کامل Magisk Manager لازم است.</string>
|
||||
<string name="dling">درحال دانلود</string>
|
||||
<string name="relaunch_app">لطفاً به صورت دستی برنامه را دوباره راه اندازی کنید</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-fr/strings.xml
Normal file
7
app/stub/src/main/res/values-fr/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Une mise à niveau de Magisk Manager en version complète est nécessaire afin de terminer l’installation. Souhaitez‑vous procéder à son téléchargement et son installation ?</string>
|
||||
<string name="no_internet_msg">Veuillez vous connecter à Internet ! Une mise à niveau complète de Magisk Manager est requise.</string>
|
||||
<string name="dling">Téléchargement en cours</string>
|
||||
<string name="relaunch_app">Veuillez relancer manuellement l’application</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-hi/strings.xml
Normal file
7
app/stub/src/main/res/values-hi/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">सेटअप को पूरा करने के लिए पूर्ण मैजिस्क मैनेजर में अपग्रेड करें. डाउनलोड करके इंस्टॉल करें?</string>
|
||||
<string name="no_internet_msg">कृपया इन्टरनेट से जुड़िये! पूर्ण मैजिस्क मैनेजर में अपग्रेड की आवश्यकता है।</string>
|
||||
<string name="dling">डाउनलोड हो रहा है</string>
|
||||
<string name="relaunch_app">कृपया ऐप को फिर से शुरू करें</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-hr/strings.xml
Normal file
7
app/stub/src/main/res/values-hr/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Nadogradite na full Magisk Manager da biste dovršili postavljanje. Preuzeti i instalirati?</string>
|
||||
<string name="no_internet_msg">Povežite se na Internet! Potrebna je nadogradnja na full Magisk Manager.</string>
|
||||
<string name="dling">Preuzimanje</string>
|
||||
<string name="relaunch_app">Ručno ponovno pokrenite aplikaciju</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-hu/strings.xml
Normal file
7
app/stub/src/main/res/values-hu/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">A telepítés befejezéséhez frissíts a teljes Magiskre. Letöltés és telepítés?</string>
|
||||
<string name="no_internet_msg">Csatlakozz az internethez! Frissíteni kell a teljes Magiskre.</string>
|
||||
<string name="dling">Letöltés</string>
|
||||
<string name="relaunch_app">Kérjük manuálisan indítsd újra az alkalmazást</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-in/strings.xml
Normal file
7
app/stub/src/main/res/values-in/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Tingkatkan Magisk Manager ke versi utuh untuk menyelesaikan penyiapan. Download dan instal?</string>
|
||||
<string name="no_internet_msg">Harap sambungkan ke Internet! Peningkatan Magisk Manager versi utuh diperlukan.</string>
|
||||
<string name="dling">Mendownload</string>
|
||||
<string name="relaunch_app">Buka kembali aplikasi secara manual</string>
|
||||
</resources>
|
6
app/stub/src/main/res/values-it/strings.xml
Normal file
6
app/stub/src/main/res/values-it/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">Aggiorna alla versione completa di Magisk per completare l\'installazione. Vuoi procedere con il download e l\'installazione?</string>
|
||||
<string name="no_internet_msg">Controlla la connessione a Internet! È necessaria per l\'aggiornamento alla versione completa di Magisk.</string>
|
||||
<string name="dling">Download in corso</string>
|
||||
<string name="relaunch_app">Riavvia manualmente l\'app</string>
|
||||
</resources>
|
6
app/stub/src/main/res/values-iw/strings.xml
Normal file
6
app/stub/src/main/res/values-iw/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">יש לעדכן לגירסה מלאה של מנהל Magisk בכדי לסיים את העדכון. להוריד ולהתקין?</string>
|
||||
<string name="no_internet_msg">נא להתחבר לאינטרנט! עדכון לגירסה מלאה של מנהל Magisk נדרש.</string>
|
||||
<string name="dling">מוריד</string>
|
||||
</resources>
|
5
app/stub/src/main/res/values-ja/strings.xml
Normal file
5
app/stub/src/main/res/values-ja/strings.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">Magisk Manager のフルバージョンにアップグレードしてセットアップを完了します。ダウンロードしてインストールしますか?</string>
|
||||
<string name="dling">ダウンロード中</string>
|
||||
<string name="no_internet_msg">インターネットに接続してください!フルバージョンの Magisk Manager が必要です。</string>
|
||||
</resources>
|
6
app/stub/src/main/res/values-ka/strings.xml
Normal file
6
app/stub/src/main/res/values-ka/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">განაახლეთ სრულ Magisk მენჯერის ვერსიამდე ინსტალაციის დასასრულებლად. გსურთ გადმოწერა და ინსტალირება?</string>
|
||||
<string name="no_internet_msg">გთხოვთ დაუკავშირდით ინტერნეტს! საჭიროა Magisk მენეჯერის სრულ ვერსიამდე განახლება.</string>
|
||||
<string name="dling">მიმდინარეობს გადმოწერა</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-kk/strings.xml
Normal file
7
app/stub/src/main/res/values-kk/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Орнатуды аяқтау үшін Magisk қолданбасының толық нұсқасын жүктеп алу керек. Жалғастырасыз ба?</string>
|
||||
<string name="no_internet_msg">Интернетке қосылыңызшы! Magisk қолданбасын жаңарту керек.</string>
|
||||
<string name="dling">Жүктеп алуда</string>
|
||||
<string name="relaunch_app">Қолданбаны қайта қосыңыз</string>
|
||||
</resources>
|
6
app/stub/src/main/res/values-ko/strings.xml
Normal file
6
app/stub/src/main/res/values-ko/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">완전한 Magisk Manager로 업데이트하여 설치를 마치십시오. 다운로드하고 설치하시겠습니까?</string>
|
||||
<string name="no_internet_msg">인터넷에 연결해 주시기 바랍니다! 완전한 Magisk Manager로 업데이트 해야 합니다.</string>
|
||||
<string name="dling">다운로드중</string>
|
||||
<string name="relaunch_app">앱을 수동으로 재시작 하세요</string>
|
||||
</resources>
|
4
app/stub/src/main/res/values-lt/strings.xml
Normal file
4
app/stub/src/main/res/values-lt/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">Atsinaujinkite į pilną Magisk Manager versiją, kad baigtumėte pasiruošimą. Atsisiųsti ir instaliuoti?</string>
|
||||
<string name="no_internet_msg">Prašome prisijungti prie interneto! Atsinaujinimas į pilną Magisk Manager versiją yra privalomas.</string>
|
||||
</resources>
|
4
app/stub/src/main/res/values-mk/strings.xml
Normal file
4
app/stub/src/main/res/values-mk/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">Надградете до целосната верзија на Magisk Manager за да го завршите поставувањето. Преземете и инсталирајте?</string>
|
||||
<string name="no_internet_msg">Ве молиме поврзете се на интернет бидејќи е потребна надградба на целосната верзија на Magisk Manager.</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-ml/strings.xml
Normal file
7
app/stub/src/main/res/values-ml/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">സജ്ജീകരണം പൂർത്തിയാക്കാൻ പൂർണ്ണ മജിസ്കിലേക്ക് അപ്ഗ്രേഡ് ചെയ്യുക. ഡൗൺലോഡ് ചെയ്ത് ഇൻസ്റ്റാൾ ചെയ്യണോ?</string>
|
||||
<string name="no_internet_msg">ദയവായി ഇന്റർനെറ്റിലേക്ക് കണക്റ്റുചെയ്യുക! പൂർണ്ണ മജിസ്കിലേക്ക് അപ്ഗ്രേഡ് ചെയ്യേണ്ടതുണ്ട്.</string>
|
||||
<string name="dling">ഡൗൺലോഡ് ചെയ്യുന്നു</string>
|
||||
<string name="relaunch_app">ആപ്പ് സ്വമേധയാ വീണ്ടും തുറക്കുക</string>
|
||||
</resources>
|
5
app/stub/src/main/res/values-nb/strings.xml
Normal file
5
app/stub/src/main/res/values-nb/strings.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Oppgrader til den komplette versjonen av Magisk Manager for å fullføre oppsettet. Vil du laste ned og installere?</string>
|
||||
<string name="no_internet_msg">Vennligst koble deg på internettet! Å oppgradere til den komplette versjonen av Magisk Manager er påkrevd.</string>
|
||||
</resources>
|
6
app/stub/src/main/res/values-nl/strings.xml
Normal file
6
app/stub/src/main/res/values-nl/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Installeer de volledige Magisk Manager om de installatie af te ronden. Wil je dit nu doen?</string>
|
||||
<string name="no_internet_msg">Maak verbinding met het internet! Het installeren van de volledige Magisk Manager is vereist.</string>
|
||||
<string name="dling">Bezig met downloaden...</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-pa/strings.xml
Normal file
7
app/stub/src/main/res/values-pa/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">ਸੈਟਅਪ ਨੂੰ ਪੂਰਾ ਕਰਨ ਲਈ ਪੂਰੇ ਮੈਜਿਕਸ ਮੈਨੇਜਰ ਵਿਚ ਅਪਗ੍ਰੇਡ ਕਰੋ. ਡਾਉਨਲੋਡ ਅਤੇ ਇੰਸਟੌਲ ਕਰੋ?</string>
|
||||
<string name="no_internet_msg">ਕਿਰਪਾ ਕਰਕੇ ਇੰਟਰਨੈਟ ਨਾਲ ਜੁੜੋ! ਪੂਰਾ ਮੈਜਿਕਸ ਮੈਨੇਜਰ ਅਪਗ੍ਰੇਡ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।</string>
|
||||
<string name="dling">ਡਾਊਨਲੋਡ ਹੋ ਰਿਹਾ ਹੈ</string>
|
||||
<string name="relaunch_app">ਕਿਰਪਾ ਕਰਕੇ ਐਪ ਨੂੰ ਮੁੜ ਚਾਲੂ ਕਰੋ</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-pl/strings.xml
Normal file
7
app/stub/src/main/res/values-pl/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Zaktualizuj Magisk do pełnej wersji aby ukończyć instalację. Pobrać i zainstalować?</string>
|
||||
<string name="no_internet_msg">Połącz się z Internetem! Wymagane jest uaktualnienie Magisk do pełnej wersji.</string>
|
||||
<string name="dling">Pobieranie</string>
|
||||
<string name="relaunch_app">Proszę ręcznie uruchomić aplikację ponownie.</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-pt-rBR/strings.xml
Normal file
7
app/stub/src/main/res/values-pt-rBR/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Atualize para o Magisk completo para finalizar a configuração. Deseja baixar e instalar?</string>
|
||||
<string name="no_internet_msg">Por favor, conecte-se à internet! É necessário atualizar para o Magisk completo.</string>
|
||||
<string name="dling">Baixando</string>
|
||||
<string name="relaunch_app">Por favor, reinicie o app manualmente.</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-pt-rPT/strings.xml
Normal file
7
app/stub/src/main/res/values-pt-rPT/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Atualize para o Magisk completo para finalizar a configuração. Deseja baixar e instalar?</string>
|
||||
<string name="no_internet_msg">Por favor, conecte-se à internet! É necessário atualizar para o Magisk completo.</string>
|
||||
<string name="dling">Baixando</string>
|
||||
<string name="relaunch_app">Por favor, reinicie o app manualmente.</string>
|
||||
</resources>
|
6
app/stub/src/main/res/values-ro/strings.xml
Normal file
6
app/stub/src/main/res/values-ro/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">Treci la versiunea completă Magisk pentru a finaliza configurarea. Descarci și instalezi?</string>
|
||||
<string name="no_internet_msg">Te rugăm să te conectezi la internet! Este necesară actualizarea la versiunea completă Magisk.</string>
|
||||
<string name="dling">Se descarcă</string>
|
||||
<string name="relaunch_app">Te rugăm să relansezi manual aplicația</string>
|
||||
</resources>
|
6
app/stub/src/main/res/values-ru/strings.xml
Normal file
6
app/stub/src/main/res/values-ru/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">Обновите Magisk для завершения установки. Продолжить?</string>
|
||||
<string name="no_internet_msg">Пожалуйста, подключитесь к Интернету! Требуется обновление Magisk.</string>
|
||||
<string name="dling">Загрузка</string>
|
||||
<string name="relaunch_app">Пожалуйста, перезапустите приложение</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-sk/strings.xml
Normal file
7
app/stub/src/main/res/values-sk/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Pre dokončenie inštalácie sa vyžaduje upgrade Magisk Managera. Stiahnuť a nainštalovať?</string>
|
||||
<string name="no_internet_msg">Pripojte sa na internet! Upgrade Magisk Managera je potrebný.</string>
|
||||
<string name="dling">Sťahuje sa</string>
|
||||
<string name="relaunch_app">Zavrite a spustite apku manuálne</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-sq/strings.xml
Normal file
7
app/stub/src/main/res/values-sq/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Përditësoni Magisk në versionin e plotë për të përfunduar konfigurimin. Shkarkoni dhe instaloni?</string>
|
||||
<string name="no_internet_msg">Ju lutemi lidhuni me internetin! Kërkohet internet për të shkarkuar Magisk në versionin e plotë.</string>
|
||||
<string name="dling">Duke shkarkuar</string>
|
||||
<string name="relaunch_app">Ju lutemi, ri-hapni aplikacionin manualisht</string>
|
||||
</resources>
|
1
app/stub/src/main/res/values-sr/strings.xml
Normal file
1
app/stub/src/main/res/values-sr/strings.xml
Normal file
@@ -0,0 +1 @@
|
||||
<resources></resources>
|
1
app/stub/src/main/res/values-sv/strings.xml
Normal file
1
app/stub/src/main/res/values-sv/strings.xml
Normal file
@@ -0,0 +1 @@
|
||||
<resources></resources>
|
7
app/stub/src/main/res/values-sw/strings.xml
Normal file
7
app/stub/src/main/res/values-sw/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Pata toleo jipya la Magisk kamili ili kumaliza usanidi. Pakua na usakinishe?</string>
|
||||
<string name="no_internet_msg">Tafadhali unganisha kwenye Mtandao! Kusasisha hadi Magisk kamili inahitajika.</string>
|
||||
<string name="dling">Inapakua</string>
|
||||
<string name="relaunch_app">Tafadhali zindua upya programu wewe mwenyewe</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values-ta/strings.xml
Normal file
7
app/stub/src/main/res/values-ta/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">அமைப்பை முடிக்க முழு மேகிஸ்க்கு மேம்படுத்தவும். பதிவிறக்கி நிறுவவா?</string>
|
||||
<string name="no_internet_msg">இணையத்துடன் இணைக்கவும்! முழு மேகிஸ்க்கு மேம்படுத்தல் தேவை.</string>
|
||||
<string name="dling">பதிவிறக்குகிறது</string>
|
||||
<string name="relaunch_app">பயன்பாட்டை கைமுறையாக மீண்டும் தொடங்கவும்</string>
|
||||
</resources>
|
2
app/stub/src/main/res/values-th/strings.xml
Normal file
2
app/stub/src/main/res/values-th/strings.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
8
app/stub/src/main/res/values-tr/strings.xml
Normal file
8
app/stub/src/main/res/values-tr/strings.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Kurulumu tamamlamak için tam Magisk Manager\'a yükseltin. İndirip yüklensin mi?</string>
|
||||
<string name="no_internet_msg">Lütfen internete bağlanın! Magisk Manager\'ın tam sürümüne yükseltmek gerekiyor.</string>
|
||||
<string name="dling">İndiriliyor</string>
|
||||
<string name="relaunch_app">Lütfen uygulamayı el ile yeniden açınız.</string>
|
||||
</resources>
|
||||
|
6
app/stub/src/main/res/values-uk/strings.xml
Normal file
6
app/stub/src/main/res/values-uk/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Оновіть додаток Magisk для завершення встановлення. Завантажити і встановити?</string>
|
||||
<string name="no_internet_msg">Будь ласка, підключіться до Інтернету! Потрібно оновити додаток Magisk.</string>
|
||||
<string name="dling">Завантаження</string>
|
||||
</resources>
|
1
app/stub/src/main/res/values-vi/strings.xml
Normal file
1
app/stub/src/main/res/values-vi/strings.xml
Normal file
@@ -0,0 +1 @@
|
||||
<resources></resources>
|
7
app/stub/src/main/res/values-zh-rCN/strings.xml
Normal file
7
app/stub/src/main/res/values-zh-rCN/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">需要下载完整版 Magisk 才能正常运行。开始下载?</string>
|
||||
<string name="no_internet_msg">下载需要网络,请检查网络连接。</string>
|
||||
<string name="dling">正在下载</string>
|
||||
<string name="relaunch_app">请重新打开本应用</string>
|
||||
</resources>
|
5
app/stub/src/main/res/values-zh-rTW/strings.xml
Normal file
5
app/stub/src/main/res/values-zh-rTW/strings.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">需要升級到完整版 Magisk Manager。是否下載並安裝?</string>
|
||||
<string name="no_internet_msg">請連上網路!升級到完整版 Magisk Manager 是必須的。</string>
|
||||
<string name="dling">正在下載</string>
|
||||
</resources>
|
7
app/stub/src/main/res/values/strings.xml
Normal file
7
app/stub/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Upgrade to full Magisk to finish the setup. Download and install?</string>
|
||||
<string name="no_internet_msg">Please connect to the Internet! Upgrading to full Magisk is required.</string>
|
||||
<string name="dling">Downloading</string>
|
||||
<string name="relaunch_app">Please manually re-launch the app</string>
|
||||
</resources>
|
Reference in New Issue
Block a user