Merge branch 'master' into f2c-merge-patch

pull/27/head
Der_Googler 3 years ago committed by GitHub
commit 73c8baa662
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,9 @@
# Contributor Covenant Code of Conduct # Contributor Covenant Code of Conduct
Note: The code of conduct are the rules that applies to everyone interacting
with the repo, regardless of acceptance, moderators of the repo are not forced
to apply it strictly, so be nice!
## Our Pledge ## Our Pledge
We as members, contributors, and leaders pledge to make participation in our We as members, contributors, and leaders pledge to make participation in our
@ -24,6 +28,8 @@ community include:
and learning from the experience and learning from the experience
* Focusing on what is best not just for us as individuals, but for the * Focusing on what is best not just for us as individuals, but for the
overall community overall community
* Respecting mistakes as something happening to everyone that is useful as
learning material
Examples of unacceptable behavior include: Examples of unacceptable behavior include:
@ -31,8 +37,8 @@ Examples of unacceptable behavior include:
advances of any kind advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks * Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment * Public or private harassment
* Publishing others' private information, such as a physical or email * Publishing others' private information, such as a physical or email address,
address, without their explicit permission with or without their explicit permission unless widely publicly available
* Other conduct which could reasonably be considered inappropriate in a * Other conduct which could reasonably be considered inappropriate in a
professional setting professional setting
@ -73,16 +79,16 @@ the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction ### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed **Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community. harmful or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing **Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested. behavior was inappropriate.
### 2. Warning ### 2. Warning
**Community Impact**: A violation through a single incident or series **Community Impact**: A violation through a single incident or series
of actions. of actions, or a behaviour that induce hatred in the community.
**Consequence**: A warning with consequences for continued behavior. No **Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with interaction with the people involved, including unsolicited interaction with

@ -10,8 +10,8 @@ android {
applicationId "com.fox2code.mmm" applicationId "com.fox2code.mmm"
minSdk 21 minSdk 21
targetSdk 32 targetSdk 32
versionCode 39 versionCode 40
versionName "0.4.4" versionName "0.4.5"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }

@ -83,14 +83,16 @@ public enum NotificationType implements NotificationTypeCst {
try { try {
boolean needPatch; boolean needPatch;
try (ZipFile zipFile = new ZipFile(d)) { try (ZipFile zipFile = new ZipFile(d)) {
needPatch = zipFile.getEntry("module.prop") == null; needPatch = zipFile.getEntry("module.prop") == null &&
zipFile.getEntry("anykernel.sh") == null;
} }
if (needPatch) { if (needPatch) {
Files.patchModuleSimple(Files.read(d), Files.patchModuleSimple(Files.read(d),
new FileOutputStream(d)); new FileOutputStream(d));
} }
try (ZipFile zipFile = new ZipFile(d)) { try (ZipFile zipFile = new ZipFile(d)) {
needPatch = zipFile.getEntry("module.prop") == null; needPatch = zipFile.getEntry("module.prop") == null &&
zipFile.getEntry("anykernel.sh") == null;
} }
if (needPatch) { if (needPatch) {
if (d.exists() && !d.delete()) if (d.exists() && !d.delete())

@ -37,11 +37,14 @@ import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.internal.UiThreadHandler; import com.topjohnwu.superuser.internal.UiThreadHandler;
import com.topjohnwu.superuser.io.SuFile; import com.topjohnwu.superuser.io.SuFile;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
@ -123,19 +126,18 @@ public class InstallerActivity extends CompatActivity {
this.getWindow().setFlags( // Note: Doesn't require WAKELOCK permission this.getWindow().setFlags( // Note: Doesn't require WAKELOCK permission
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (urlMode) {
this.progressIndicator.setVisibility(View.VISIBLE); this.progressIndicator.setVisibility(View.VISIBLE);
this.installerTerminal.addLine("- Downloading " + name); if (urlMode) this.installerTerminal.addLine("- Downloading " + name);
new Thread(() -> { new Thread(() -> {
File moduleCache = this.toDelete = File moduleCache = this.toDelete = urlMode ?
new File(this.moduleCache, "module.zip"); new File(this.moduleCache, "module.zip") : new File(target);
if (moduleCache.exists() && !moduleCache.delete() && if (urlMode && moduleCache.exists() && !moduleCache.delete() &&
!new SuFile(moduleCache.getAbsolutePath()).delete()) !new SuFile(moduleCache.getAbsolutePath()).delete())
Log.e(TAG, "Failed to delete module cache"); Log.e(TAG, "Failed to delete module cache");
String errMessage = "Failed to download module zip"; String errMessage = "Failed to download module zip";
try { try {
Log.i(TAG, "Downloading: " + target); Log.i(TAG, (urlMode ? "Downloading: " : "Loading: ") + target);
byte[] rawModule = Http.doHttpGet(target, (progress, max, done) -> { byte[] rawModule = urlMode ? Http.doHttpGet(target, (progress, max, done) -> {
if (max <= 0 && this.progressIndicator.isIndeterminate()) if (max <= 0 && this.progressIndicator.isIndeterminate())
return; return;
this.runOnUiThread(() -> { this.runOnUiThread(() -> {
@ -143,7 +145,7 @@ public class InstallerActivity extends CompatActivity {
this.progressIndicator.setMax(max); this.progressIndicator.setMax(max);
this.progressIndicator.setProgressCompat(progress, true); this.progressIndicator.setProgressCompat(progress, true);
}); });
}); }) : Files.readSU(moduleCache);
this.runOnUiThread(() -> { this.runOnUiThread(() -> {
this.progressIndicator.setVisibility(View.GONE); this.progressIndicator.setVisibility(View.GONE);
this.progressIndicator.setIndeterminate(true); this.progressIndicator.setIndeterminate(true);
@ -172,29 +174,33 @@ public class InstallerActivity extends CompatActivity {
while ((zipEntry = zipInputStream.getNextEntry()) != null) { while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String entryName = zipEntry.getName(); String entryName = zipEntry.getName();
if (entryName.equals("anykernel.sh")) { if (entryName.equals("anykernel.sh")) {
noPatch = true;
isAnyKernel = true; isAnyKernel = true;
break; break;
} else if (entryName.equals("module.prop")) { } else if (entryName.equals("module.prop")) {
noPatch = true; noPatch = true;
isModule = true; isModule = true;
break; break;
} else if (entryName.endsWith("/anykernel.sh")) {
isAnyKernel = true;
} else if (entryName.endsWith("/module.prop")) { } else if (entryName.endsWith("/module.prop")) {
isModule = true; isModule = true;
} }
} }
} }
if (!isModule) { if (!isModule && !isAnyKernel) {
this.setInstallStateFinished(false, isAnyKernel ? this.setInstallStateFinished(false,
"! AnyKernel modules can only be installed on recovery" :
"! File is not a valid magisk module", ""); "! File is not a valid magisk module", "");
return; return;
} }
if (noPatch) { if (noPatch) {
if (urlMode) {
errMessage = "Failed to save module zip"; errMessage = "Failed to save module zip";
try (OutputStream outputStream = new FileOutputStream(moduleCache)) { try (OutputStream outputStream = new FileOutputStream(moduleCache)) {
outputStream.write(rawModule); outputStream.write(rawModule);
outputStream.flush(); outputStream.flush();
} }
}
} else { } else {
errMessage = "Failed to patch module zip"; errMessage = "Failed to patch module zip";
this.runOnUiThread(() -> { this.runOnUiThread(() -> {
@ -206,9 +212,9 @@ public class InstallerActivity extends CompatActivity {
outputStream.flush(); outputStream.flush();
} }
} }
if (this.canceled) return;
//noinspection UnusedAssignment (Important to avoid OutOfMemoryError) //noinspection UnusedAssignment (Important to avoid OutOfMemoryError)
rawModule = null; // Because reference is kept when calling doInstall rawModule = null; // Because reference is kept when calling doInstall
if (this.canceled) return;
this.runOnUiThread(() -> { this.runOnUiThread(() -> {
this.installerTerminal.addLine("- Installing " + name); this.installerTerminal.addLine("- Installing " + name);
}); });
@ -219,31 +225,7 @@ public class InstallerActivity extends CompatActivity {
this.setInstallStateFinished(false, this.setInstallStateFinished(false,
"! " + errMessage, ""); "! " + errMessage, "");
} }
}, "Module download Thread").start(); }, "Module install Thread").start();
} else {
final File moduleFile = new File(target);
if (checksum != null && !checksum.isEmpty()) {
Log.d(TAG, "Checking for checksum: " + checksum);
this.installerTerminal.addLine("- Checking file integrity");
try {
if (!Hashes.checkSumMatch(Files.readSU(moduleFile), checksum)) {
this.setInstallStateFinished(false,
"! File integrity check failed", "");
return;
}
} catch (IOException e) {
Log.e(TAG, "Failed to read file for checksum check", e);
this.setInstallStateFinished(false,
"! File integrity check failed", "");
return;
}
if (this.canceled) return;
}
this.installerTerminal.addLine("- Installing " + name);
new Thread(() -> this.doInstall(
this.toDelete = moduleFile, noExtensions, rootless),
"Install Thread").start();
}
} }
@ -276,17 +258,67 @@ public class InstallerActivity extends CompatActivity {
String arch32 = "true"; // Do nothing by default String arch32 = "true"; // Do nothing by default
boolean needs32bit = false; boolean needs32bit = false;
String moduleId = null; String moduleId = null;
boolean anyKernel = false;
boolean magiskModule = false;
boolean anyKernelSystemLess = false;
File anyKernelInstallScript = new File(this.moduleCache, "update-binary");
try (ZipFile zipFile = new ZipFile(file)) { try (ZipFile zipFile = new ZipFile(file)) {
ZipEntry anyKernelSh = zipFile.getEntry("anykernel.sh");
if (anyKernelSh != null) { // Check if module is AnyKernel module
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(zipFile.getInputStream(anyKernelSh)));
String line;
// Check if AnyKernel module support system-less
while ((line = bufferedReader.readLine()) != null) {
String trimmedLine = line.trim();
if (trimmedLine.equals("do.modules=1"))
anyKernel = true;
if (trimmedLine.equals("do.systemless=1"))
anyKernelSystemLess = true;
}
bufferedReader.close();
if (anyKernelSystemLess && anyKernel) {
anyKernelSystemLess = false;
ZipEntry updateBinary = zipFile.getEntry(
"META-INF/com/google/android/update-binary");
if (updateBinary != null) {
bufferedReader = new BufferedReader(
new InputStreamReader(zipFile.getInputStream(updateBinary)));
PrintStream printStream = new PrintStream(
new FileOutputStream(anyKernelInstallScript));
while ((line = bufferedReader.readLine()) != null) {
String trimmedLine = line.trim();
if (trimmedLine.equals("mount_all;") ||
trimmedLine.equals("umount_all;"))
continue; // Do not mount anything
line = line.replace("/sbin/sh", "/system/bin/sh");
int prePatch = line.length();
line = line.replace("/data/adb/modules/ak3-helper",
"/data/adb/modules-update/ak3-helper");
if (prePatch != line.length()) anyKernelSystemLess = true;
printStream.println(line);
}
printStream.close();
bufferedReader.close();
if (!anyKernelSystemLess) anyKernelInstallScript.delete();
}
}
anyKernel = true;
}
if (zipFile.getEntry( // Check if module hard require 32bit support if (zipFile.getEntry( // Check if module hard require 32bit support
"common/addon/Volume-Key-Selector/tools/arm64/keycheck") == null && "common/addon/Volume-Key-Selector/tools/arm64/keycheck") == null &&
zipFile.getEntry( zipFile.getEntry(
"common/addon/Volume-Key-Selector/install.sh") != null) { "common/addon/Volume-Key-Selector/install.sh") != null) {
needs32bit = true; needs32bit = true;
} }
ZipEntry moduleProp = zipFile.getEntry("module.prop");
magiskModule = moduleProp != null;
moduleId = PropUtils.readModuleId(zipFile moduleId = PropUtils.readModuleId(zipFile
.getInputStream(zipFile.getEntry("module.prop"))); .getInputStream(zipFile.getEntry("module.prop")));
} catch (IOException ignored) { } catch (IOException ignored) {
} }
.getInputStream(moduleProp));
} catch (IOException ignored) {}
int compatFlags = AppUpdateManager.getFlagsForModule(moduleId); int compatFlags = AppUpdateManager.getFlagsForModule(moduleId);
if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NEED_32BIT) != 0) if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NEED_32BIT) != 0)
needs32bit = true; needs32bit = true;
@ -300,6 +332,12 @@ public class InstallerActivity extends CompatActivity {
null); null);
return; return;
} }
if (magiskModule && moduleId == null && !anyKernel) {
// Modules without module Ids are module installed by 3rd party software
this.setInstallStateFinished(false,
"! Magisk modules require a moduleId", null);
return;
}
if (Build.SUPPORTED_32_BIT_ABIS.length == 0) { if (Build.SUPPORTED_32_BIT_ABIS.length == 0) {
if (needs32bit) { if (needs32bit) {
this.setInstallStateFinished(false, this.setInstallStateFinished(false,
@ -316,14 +354,23 @@ public class InstallerActivity extends CompatActivity {
} }
String installCommand; String installCommand;
File installExecutable; File installExecutable;
if (InstallerInitializer.peekMagiskVersion() >= if (anyKernel && moduleId == null) { // AnyKernel modules don't have a moduleId
if (!anyKernelSystemLess) {
this.setInstallStateFinished(false,
"! This AnyKernel module only support recovery install", null);
return;
}
installExecutable = anyKernelInstallScript;
installCommand = "sh \"" + installExecutable.getAbsolutePath() + "\"" +
" /dev/null 0 \"" + file.getAbsolutePath() + "\"";
} else if (InstallerInitializer.peekMagiskVersion() >=
Constants.MAGISK_VER_CODE_INSTALL_COMMAND && Constants.MAGISK_VER_CODE_INSTALL_COMMAND &&
((compatFlags & AppUpdateManager.FLAG_COMPAT_MAGISK_CMD) != 0 || ((compatFlags & AppUpdateManager.FLAG_COMPAT_MAGISK_CMD) != 0 ||
noExtensions || MainApplication.isUsingMagiskCommand())) { noExtensions || MainApplication.isUsingMagiskCommand())) {
installCommand = "magisk --install-module \"" + file.getAbsolutePath() + "\""; installCommand = "magisk --install-module \"" + file.getAbsolutePath() + "\"";
installExecutable = new File(InstallerInitializer.peekMagiskPath() installExecutable = new File(InstallerInitializer.peekMagiskPath()
.equals("/sbin") ? "/sbin/magisk" : "/system/bin/magisk"); .equals("/sbin") ? "/sbin/magisk" : "/system/bin/magisk");
} else { } else if (moduleId != null) {
installExecutable = this.extractInstallScript("module_installer_compat.sh"); installExecutable = this.extractInstallScript("module_installer_compat.sh");
if (installExecutable == null) { if (installExecutable == null) {
this.setInstallStateFinished(false, this.setInstallStateFinished(false,
@ -331,7 +378,11 @@ public class InstallerActivity extends CompatActivity {
return; return;
} }
installCommand = "sh \"" + installExecutable.getAbsolutePath() + "\"" + installCommand = "sh \"" + installExecutable.getAbsolutePath() + "\"" +
" /dev/null 1 \"" + file.getAbsolutePath() + "\""; " /dev/null 0 \"" + file.getAbsolutePath() + "\"";
} else {
this.setInstallStateFinished(false,
"! Zip file is not a valid Magisk or a AnyKernel module!", null);
return;
} }
installerMonitor = new InstallerMonitor(installExecutable); installerMonitor = new InstallerMonitor(installExecutable);
if (moduleId != null) installerMonitor.setForCleanUp(moduleId); if (moduleId != null) installerMonitor.setForCleanUp(moduleId);

@ -1,6 +1,7 @@
package com.fox2code.mmm.utils; package com.fox2code.mmm.utils;
import android.os.Build; import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -45,6 +46,11 @@ public class Files {
} }
public static byte[] readSU(File file) throws IOException { public static byte[] readSU(File file) throws IOException {
if (file.isFile() && file.canRead()) {
try { // Read as app if su not required
return read(file);
} catch (IOException ignored) {}
}
try (InputStream inputStream = SuFileInputStream.open(file)) { try (InputStream inputStream = SuFileInputStream.open(file)) {
return readAllBytes(inputStream); return readAllBytes(inputStream);
} }

@ -371,6 +371,7 @@ public class IntentHelper {
} }
outputStream = new FileOutputStream(destination); outputStream = new FileOutputStream(destination);
Files.copy(inputStream, outputStream); Files.copy(inputStream, outputStream);
Log.d(TAG, "File saved at " + destination);
success = true; success = true;
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "failed copy of " + uri, e); Log.e(TAG, "failed copy of " + uri, e);

@ -303,6 +303,7 @@ public class PropUtils {
} }
public static String readModuleId(InputStream inputStream) { public static String readModuleId(InputStream inputStream) {
if (inputStream == null) return null;
String moduleId = null; String moduleId = null;
try (BufferedReader bufferedReader = new BufferedReader( try (BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {

@ -1,23 +1,23 @@
<resources> <resources>
<string name="app_name">Fox\'s Magisk Module Manager</string> <string name="app_name">Fox\'s Magisk Module Manager</string>
<string name="app_name_short">Fox\'s Mmm</string> <string name="app_name_short">Fox\'s Mmm</string>
<string name="fail_root_magisk">Failed to get access to Root or Magisk</string> <string name="fail_root_magisk">Could not access either Root or Magisk</string>
<string name="loading">Loading…</string> <string name="loading">Loading…</string>
<string name="updatable">Updatable</string> <string name="updatable">Upgradable</string>
<string name="installed">Installed</string> <string name="installed">Installed</string>
<string name="online_repo">Online Repo</string> <string name="online_repo">Online Repo</string>
<string name="showcase_mode">The application is in lockdown mode</string> <string name="showcase_mode">The app is in lockdown mode</string>
<string name="failed_download">Failed to download file.</string> <string name="failed_download">Could not download the file</string>
<string name="slow_modules">Modules took too long to boot, consider disabling some modules</string> <string name="slow_modules">Modules took too long to boot, consider disabling some modules</string>
<string name="fail_internet">Failed to connect to the internet</string> <string name="fail_internet">Could not connect to the Internet</string>
<string name="no_web_view">Failed to get system WebView</string> <string name="no_web_view">Could not open system WebView</string>
<string name="title_activity_settings">SettingsActivity</string> <string name="title_activity_settings">SettingsActivity</string>
<string name="app_update_available">Application update available</string> <string name="app_update_available">A new version of the app is available</string>
<string name="app_update">Update</string> <string name="app_update">Upgrade</string>
<string name="no_desc_found">No description found.</string> <string name="no_desc_found">No description found.</string>
<string name="download_module">Download module</string> <string name="download_module">Download module</string>
<string name="install_module">Install module</string> <string name="install_module">Install module</string>
<string name="update_module">Update module</string> <string name="update_module">Upgrade module</string>
<string name="changelog">Changelog</string> <string name="changelog">Changelog</string>
<string name="website">Website</string> <string name="website">Website</string>
<string name="support">Support</string> <string name="support">Support</string>
@ -29,7 +29,7 @@
<string name="no">No</string> <string name="no">No</string>
<!-- Module section translation --> <!-- Module section translation -->
<string name="module_last_update">Last update:</string> <string name="module_last_update">Last version:</string>
<string name="module_repo">Repo:</string> <string name="module_repo">Repo:</string>
<string name="module_by">by</string> <string name="module_by">by</string>
<string name="module_downloads">Downloads:</string> <string name="module_downloads">Downloads:</string>
@ -42,65 +42,68 @@
<string name="showcase_mode_desc">Lockdown mode prevent manager to do action on modules</string> <string name="showcase_mode_desc">Lockdown mode prevent manager to do action on modules</string>
<string name="prevent_reboot_pref">Prevent reboot</string> <string name="prevent_reboot_pref">Prevent reboot</string>
<string name="prevent_reboot_desc">Prevents unexpected reboots</string> <string name="prevent_reboot_desc">Prevents unexpected reboots</string>
<string name="showcase_mode_desc">Lockdown mode prevents the manager from carrying out actions on modules</string>
<string name="pref_category_settings">Settings</string> <string name="pref_category_settings">Settings</string>
<string name="pref_category_info">Info</string> <string name="pref_category_info">Info</string>
<string name="show_licenses">Show licenses</string> <string name="show_licenses">Show licenses</string>
<string name="licenses">Licences</string> <string name="licenses">Licences</string>
<string name="show_incompatible_pref">Show incompatible modules</string> <string name="show_incompatible_pref">Show incompatible modules</string>
<string name="show_incompatible_desc">Show modules that are incompatible with your device based on their metadata</string> <string name="show_incompatible_desc">Show modules unlikely to work on your device based on their metadata</string>
<string name="magisk_outdated">Magisk is outdated!</string> <string name="magisk_outdated">There is a new version of Magisk to install!</string>
<string name="pref_category_repos">Repos</string> <string name="pref_category_repos">Repos</string>
<string name="pref_category_security">Security</string> <string name="pref_category_security">Security</string>
<string name="pref_category_appearance">Appearance</string> <string name="pref_category_appearance">Appearance</string>
<string name="pref_category_general">General</string> <string name="pref_category_general">General</string>
<string name="repo_main_desc">The repository hosting Magisk Modules</string> <string name="repo_main_desc">The repository hosting Magisk Modules</string>
<string name="repo_main_alt">An alternative to Magisk-Modules-Repo with fewer restrictions.</string> <string name="repo_main_alt">An alternative to Magisk-Modules-Repo with fewer restrictions.</string>
<string name="repo_main_desc">The repository hosting Magisk modules</string>
<string name="repo_main_alt">An alternative to the Magisk-Modules-Repo with fewer restrictions.</string>
<string name="master_delete">Delete the module files?</string> <string name="master_delete">Delete the module files?</string>
<string name="master_delete_no">Keep files</string> <string name="master_delete_no">Keep</string>
<string name="master_delete_yes">Delete files</string> <string name="master_delete_yes">Delete</string>
<string name="master_delete_fail">Failed to delete the module files</string> <string name="master_delete_fail">Could not delete the module files</string>
<string name="theme_pref">Theme</string> <string name="theme_pref">Theme</string>
<string name="theme_mode_pref">Theme mode</string> <string name="theme_mode_pref">Theme mode</string>
<string name="module_id_prefix">Module id: </string> <string name="module_id_prefix">Module ID: </string>
<string name="install_from_storage">Install module from storage</string> <string name="install_from_storage">Install module from storage</string>
<string name="invalid_format">The selected module is in an invalid format</string> <string name="invalid_format">The selected module has an invalid format</string>
<string name="local_install_title">Local install</string> <string name="local_install_title">Local install</string>
<string name="source_code">Source code</string> <string name="source_code">Source code</string>
<string name="magisk_builtin_module">Magisk builtin module</string> <string name="magisk_builtin_module">Magisk built-in module</string>
<string name="substratum_builtin_module">Substratum builtin module</string> <string name="substratum_builtin_module">Substratum built-in module</string>
<string name="force_dark_terminal_title">Force dark mode terminal</string> <string name="force_dark_terminal_title">Dark terminal</string>
<string name="file_picker_failure">Your current file picker failed to give access to the file.</string> <string name="file_picker_failure">Your current file picker could not access the file.</string>
<string name="remote_install_title">Remote install</string> <string name="remote_install_title">Remote install</string>
<string name="file_picker_wierd">Your file picker returned a non standard response.</string> <string name="file_picker_wierd">Your file picker returned a non-standard response.</string>
<string name="use_magisk_install_command_pref">Use magisk module install command</string> <string name="use_magisk_install_command_pref">Use the \"magisk --install-module\" command</string>
<string name="use_magisk_install_command_desc"> <string name="use_magisk_install_command_desc">
During test it caused problems to the module install error diagnosis tool, During testing it caused problems to the module install error diagnosis tool,
so I hid this option behind developer mode, enable this at your own risk! so this option behind is hidden behind developer mode.\nTurn this on at your own risk!
</string> </string>
<string name="dev_mode_enabled">Developer mode enabled</string> <string name="dev_mode_enabled">Developer mode on</string>
<string name="force_english_pref">Force English language</string> <string name="force_english_pref">English app language</string>
<string name="disable_low_quality_module_filter_pref">Disable low quality module filter</string> <string name="disable_low_quality_module_filter_pref">Show low-quality modules</string>
<string name="disable_low_quality_module_filter_desc"> <string name="disable_low_quality_module_filter_desc">
Some modules do not declare their metadata properly,causing visual glitches, Some modules do not declare their metadata properly, causing visual glitches,
and/or indicating poor module quality, disable at your own risk! and/or indicating poor module quality.\nTurn this off at your own risk!
</string> </string>
<string name="dns_over_https_pref">Dns over https</string> <string name="dns_over_https_pref">DNS over HTTPS</string>
<string name="dns_over_https_desc"> <string name="dns_over_https_desc">
May fix connections issues in some cases. May fix connections issues in some cases.
(Does not apply to WebView) (Does not apply to WebView.)
</string> </string>
<string name="disable_extensions_pref">Disable extensions</string> <string name="disable_extensions_pref">No Mmm</string>
<string name="disable_extensions_desc"> <string name="disable_extensions_desc">
Disable Fox\'s Mmm extensions, this prevent modules from using Turn off Fox\'s Mmm extensions, preventing modules from using
terminal extensions, useful if a module misuse Fox\'s Mmm extensions. terminal extensions.\nUseful if a module misuses Fox\'s Mmm extensions.
</string> </string>
<string name="wrap_text_pref">Wrap text</string> <string name="wrap_text_pref">Text wrapping</string>
<string name="wrap_text_desc"> <string name="wrap_text_desc">
Wrap text to a new line instead of putting Show text on multiple lines instead of putting
all text on the same line when installing a module all text on the same line when installing a module.
</string> </string>
<string name="enable_blur_pref">Enable blur</string> <string name="enable_blur_pref">Blur</string>
<string name="repo_enabled">Repo enabled</string> <string name="repo_enabled">Repo on</string>
<string name="repo_disabled">Repo disabled</string> <string name="repo_disabled">Repo off</string>
<string name="androidacy_repo_info">Androidacy repo uses ads and trackers.</string> <string name="androidacy_repo_info">The Androidacy repo has ads and trackers.</string>
</resources> </resources>

Loading…
Cancel
Save