diff --git a/app/build.gradle b/app/build.gradle
index 1661b02..0f58843 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,8 +10,8 @@ android {
applicationId "com.fox2code.mmm"
minSdk 21
targetSdk 32
- versionCode 30
- versionName "0.3.2"
+ versionCode 32
+ versionName "0.4.0-rc1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -64,7 +64,7 @@ dependencies {
// Utils
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.3'
implementation 'com.squareup.okhttp3:okhttp-brotli:4.9.3'
- implementation 'com.github.topjohnwu.libsu:io:3.2.1'
+ implementation 'com.github.topjohnwu.libsu:io:4.0.0'
// Markdown
implementation "io.noties.markwon:core:4.6.2"
@@ -74,6 +74,9 @@ dependencies {
annotationProcessor "io.noties:prism4j-bundler:2.0.0"
implementation "com.caverock:androidsvg:1.4"
+ // Utils for compat
+ compileOnly "org.robolectric:android-all:11-robolectric-6757853"
+
// Test
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
diff --git a/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java b/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java
index 1050811..9ce29e7 100644
--- a/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java
+++ b/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java
@@ -2,25 +2,42 @@ package com.fox2code.mmm;
import android.util.Log;
+import com.fox2code.mmm.utils.Files;
import com.fox2code.mmm.utils.Http;
import org.json.JSONArray;
import org.json.JSONObject;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
// See https://docs.github.com/en/rest/reference/repos#releases
public class AppUpdateManager {
+ public static int FLAG_COMPAT_LOW_QUALITY = 0x01;
+ public static int FLAG_COMPAT_NO_EXT = 0x02;
+ public static int FLAG_COMPAT_MAGISK_CMD = 0x04;
+ public static int FLAG_COMPAT_NEED_32BIT = 0x08;
private static final String TAG = "AppUpdateManager";
private static final AppUpdateManager INSTANCE = new AppUpdateManager();
private static final String RELEASES_API_URL =
"https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/releases";
+ private static final String COMPAT_API_URL =
+ "https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/releases";
public static AppUpdateManager getAppUpdateManager() {
return INSTANCE;
}
+ private final HashMap compatDataId = new HashMap<>();
private final Object updateLock = new Object();
+ private final File compatFile;
private String latestRelease;
private String latestPreRelease;
private long lastChecked;
@@ -28,12 +45,20 @@ public class AppUpdateManager {
private boolean lastCheckSuccess;
private AppUpdateManager() {
+ this.compatFile = new File(MainApplication.getINSTANCE().getFilesDir(), "compat.txt");
this.latestRelease = MainApplication.getBootSharedPreferences()
.getString("updater_latest_release", BuildConfig.VERSION_NAME);
this.latestPreRelease = MainApplication.getBootSharedPreferences()
.getString("updater_latest_pre_release", BuildConfig.VERSION_NAME);
this.lastChecked = 0;
this.preReleaseNewer = true;
+ if (this.compatFile.isFile()) {
+ try {
+ this.parseCompatibilityFlags(new FileInputStream(this.compatFile));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
}
// Return true if should show a notification
@@ -95,6 +120,31 @@ public class AppUpdateManager {
return this.peekShouldUpdate();
}
+ public void checkUpdateCompat() {
+ if (this.compatFile.exists()) {
+ long lastUpdate = this.compatFile.lastModified();
+ if (lastUpdate <= System.currentTimeMillis() &&
+ lastUpdate + 600_000L > System.currentTimeMillis()) {
+ return; // Skip update
+ }
+ }
+ try {
+ JSONObject object = new JSONObject(new String(Http.doHttpGet(
+ COMPAT_API_URL, false), StandardCharsets.UTF_8));
+ if (object.isNull("body")) {
+ compatDataId.clear();
+ Files.write(compatFile, new byte[0]);
+ return;
+ }
+ byte[] rawData = object.getString("body")
+ .getBytes(StandardCharsets.UTF_8);
+ this.parseCompatibilityFlags(new ByteArrayInputStream(rawData));
+ Files.write(compatFile, rawData);
+ } catch (Exception e) {
+ Log.e("AppUpdateManager", "Failed to update compat list", e);
+ }
+ }
+
public boolean peekShouldUpdate() {
return !(BuildConfig.VERSION_NAME.equals(this.latestRelease) ||
(this.preReleaseNewer &&
@@ -109,4 +159,46 @@ public class AppUpdateManager {
public boolean isLastCheckSuccess() {
return lastCheckSuccess;
}
+
+ private void parseCompatibilityFlags(InputStream inputStream) throws IOException {
+ compatDataId.clear();
+ BufferedReader bufferedReader = new BufferedReader(
+ new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ line = line.trim();
+ if (line.isEmpty() || line.startsWith("#")) continue;
+ int i = line.indexOf('/');
+ if (i == -1) continue;
+ int value = 0;
+ for (String arg : line.substring(i + 1).split(",")) {
+ switch (arg) {
+ default:
+ break;
+ case "lowQuality":
+ value |= FLAG_COMPAT_LOW_QUALITY;
+ break;
+ case "noExt":
+ value |= FLAG_COMPAT_NO_EXT;
+ break;
+ case "magiskCmd":
+ value |= FLAG_COMPAT_MAGISK_CMD;
+ break;
+ case "need32bit":
+ value |= FLAG_COMPAT_NEED_32BIT;
+ break;
+ }
+ }
+ compatDataId.put(line.substring(0, i), value);
+ }
+ }
+
+ public int getCompatibilityFlags(String moduleId) {
+ Integer compatFlags = compatDataId.get(moduleId);
+ return compatFlags == null ? 0 : compatFlags;
+ }
+
+ public static int getFlagsForModule(String moduleId) {
+ return INSTANCE.getCompatibilityFlags(moduleId);
+ }
}
diff --git a/app/src/main/java/com/fox2code/mmm/MainActivity.java b/app/src/main/java/com/fox2code/mmm/MainActivity.java
index 2bd3ce3..563b4fc 100644
--- a/app/src/main/java/com/fox2code/mmm/MainActivity.java
+++ b/app/src/main/java/com/fox2code/mmm/MainActivity.java
@@ -117,6 +117,7 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
MainActivity.this.searchView.clearFocus();
}
});
+ this.searchCard.setRadius(this.searchCard.getHeight() / 2F);
this.searchView.setMinimumHeight(CompatDisplay.dpToPixel(16));
this.searchView.setImeOptions(EditorInfo.IME_ACTION_SEARCH |
EditorInfo.IME_FLAG_NO_FULLSCREEN);
@@ -179,6 +180,8 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
} else {
if (AppUpdateManager.getAppUpdateManager().checkUpdate(true))
moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE);
+ if (AppUpdateManager.getAppUpdateManager().isLastCheckSuccess())
+ AppUpdateManager.getAppUpdateManager().checkUpdateCompat();
if (max != 0) {
int current = 0;
for (LocalModuleInfo localModuleInfo :
@@ -241,9 +244,10 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
swipeRefreshLayoutOrigStartOffset + combinedBarsHeight,
swipeRefreshLayoutOrigEndOffset + combinedBarsHeight);
this.moduleViewListBuilder.setHeaderPx(
- actionBarHeight + CompatDisplay.dpToPixel(4));
+ actionBarHeight + CompatDisplay.dpToPixel(8));
this.moduleViewListBuilder.setFooterPx(
bottomInset + this.searchCard.getHeight());
+ this.searchCard.setRadius(this.searchCard.getHeight() / 2F);
this.moduleViewListBuilder.updateInsets();
this.actionBarBlur.invalidate();
this.overScrollInsetTop = combinedBarsHeight;
diff --git a/app/src/main/java/com/fox2code/mmm/ModuleViewAdapter.java b/app/src/main/java/com/fox2code/mmm/ModuleViewAdapter.java
index 0b44297..accb39c 100644
--- a/app/src/main/java/com/fox2code/mmm/ModuleViewAdapter.java
+++ b/app/src/main/java/com/fox2code/mmm/ModuleViewAdapter.java
@@ -104,6 +104,7 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter {
if (this.initState) return; // Skip if non user
diff --git a/app/src/main/java/com/fox2code/mmm/ModuleViewListBuilder.java b/app/src/main/java/com/fox2code/mmm/ModuleViewListBuilder.java
index ec074a2..02417ad 100644
--- a/app/src/main/java/com/fox2code/mmm/ModuleViewListBuilder.java
+++ b/app/src/main/java/com/fox2code/mmm/ModuleViewListBuilder.java
@@ -76,6 +76,7 @@ public class ModuleViewListBuilder {
RepoManager repoManager = RepoManager.getINSTANCE();
repoManager.runAfterUpdate(() -> {
Log.i(TAG, "A2: " + repoManager.getModules().size());
+ boolean no32bitSupport = Build.SUPPORTED_32_BIT_ABIS.length == 0;
for (RepoModule repoModule : repoManager.getModules().values()) {
if (!repoModule.repoData.isEnabled()) continue;
ModuleInfo moduleInfo = repoModule.moduleInfo;
@@ -84,9 +85,11 @@ public class ModuleViewListBuilder {
// Only check Magisk compatibility if root is present
(InstallerInitializer.peekMagiskPath() != null &&
repoModule.moduleInfo.minMagisk >
- InstallerInitializer.peekMagiskVersion()
- )))
- continue; // Skip adding incompatible modules
+ InstallerInitializer.peekMagiskVersion())) ||
+ // If 64bit only system, skip 32bit only modules
+ (no32bitSupport && (AppUpdateManager.getFlagsForModule(repoModule.id)
+ & AppUpdateManager.FLAG_COMPAT_NEED_32BIT) != 0)
+ ) continue; // Skip adding incompatible modules
ModuleHolder moduleHolder = this.mappedModuleHolders.get(repoModule.id);
if (moduleHolder == null) {
this.mappedModuleHolders.put(repoModule.id,
diff --git a/app/src/main/java/com/fox2code/mmm/NotificationType.java b/app/src/main/java/com/fox2code/mmm/NotificationType.java
index 71fc825..06b4330 100644
--- a/app/src/main/java/com/fox2code/mmm/NotificationType.java
+++ b/app/src/main/java/com/fox2code/mmm/NotificationType.java
@@ -31,13 +31,17 @@ public enum NotificationType implements NotificationTypeCst {
return !MainApplication.isShowcaseMode();
}
},
- NO_ROOT(R.string.fail_root_magisk, R.drawable.ic_baseline_numbers_24) {
+ NO_ROOT(R.string.fail_root_magisk, R.drawable.ic_baseline_numbers_24, v -> {
+ IntentHelper.openUrl(v.getContext(), "https://github.com/topjohnwu/Magisk");
+ }) {
@Override
public boolean shouldRemove() {
return InstallerInitializer.peekMagiskPath() != null;
}
},
- MAGISK_OUTDATED(R.string.magisk_outdated, R.drawable.ic_baseline_update_24) {
+ MAGISK_OUTDATED(R.string.magisk_outdated, R.drawable.ic_baseline_update_24, v -> {
+ IntentHelper.openUrl(v.getContext(), "https://github.com/topjohnwu/Magisk");
+ }) {
@Override
public boolean shouldRemove() {
return InstallerInitializer.peekMagiskPath() == null ||
@@ -128,7 +132,11 @@ public enum NotificationType implements NotificationTypeCst {
public final boolean special;
NotificationType(@StringRes int textId, int iconId) {
- this(textId, iconId, R.attr.colorError, R.attr.colorOnPrimary); //R.attr.colorOnError);
+ this(textId, iconId, R.attr.colorError, R.attr.colorOnPrimary);
+ }
+
+ NotificationType(@StringRes int textId, int iconId, View.OnClickListener onClickListener) {
+ this(textId, iconId, R.attr.colorError, R.attr.colorOnPrimary, onClickListener);
}
NotificationType(@StringRes int textId, int iconId, int backgroundAttr, int foregroundAttr) {
diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java
index c7d905d..8b10024 100644
--- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java
+++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java
@@ -81,9 +81,9 @@ public class AndroidacyActivity extends CompatActivity {
setActionBarBackground(null);
this.setDisplayHomeAsUpEnabled(true);
if (title == null || title.isEmpty()) {
- this.setTitle(title);
- } else {
this.setTitle("Androidacy");
+ } else {
+ this.setTitle(title);
}
if (allowInstall || title == null || title.isEmpty()) {
this.hideActionBar();
@@ -120,9 +120,9 @@ public class AndroidacyActivity extends CompatActivity {
public boolean shouldOverrideUrlLoading(
@NonNull WebView view, @NonNull WebResourceRequest request) {
// Don't open non Androidacy urls inside WebView
- if (request.isForMainFrame() && !(request.getUrl().getScheme().equals("intent") ||
- AndroidacyUtil.isAndroidacyLink(request.getUrl()))) {
- IntentHelper.openUrl(view.getContext(), request.getUrl().toString());
+ if (request.isForMainFrame() &&
+ !AndroidacyUtil.isAndroidacyLink(request.getUrl())) {
+ IntentHelper.openUri(view.getContext(), request.getUrl().toString());
return true;
}
return false;
diff --git a/app/src/main/java/com/fox2code/mmm/compat/CompatActivity.java b/app/src/main/java/com/fox2code/mmm/compat/CompatActivity.java
index a7031b8..0df73eb 100644
--- a/app/src/main/java/com/fox2code/mmm/compat/CompatActivity.java
+++ b/app/src/main/java/com/fox2code/mmm/compat/CompatActivity.java
@@ -10,6 +10,7 @@ import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
+import android.os.SystemProperties;
import android.util.Log;
import android.util.TypedValue;
import android.view.KeyCharacterMap;
@@ -88,8 +89,7 @@ public class CompatActivity extends AppCompatActivity {
if (!this.onCreateCalled) {
this.getLayoutInflater().setFactory2(new LayoutInflaterFactory(this.getDelegate())
.addOnViewCreatedListener(WindowInsetsHelper.Companion.getLISTENER()));
- this.hasHardwareNavBar = ViewConfiguration.get(this).hasPermanentMenuKey() ||
- KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
+ this.hasHardwareNavBar = this.hasHardwareNavBar0();
this.onCreateCalledOnce = true;
}
Application application = this.getApplication();
@@ -104,6 +104,7 @@ public class CompatActivity extends AppCompatActivity {
@Override
protected void onResume() {
+ this.hasHardwareNavBar = this.hasHardwareNavBar0();
super.onResume();
this.refreshUI();
}
@@ -287,9 +288,13 @@ public class CompatActivity extends AppCompatActivity {
public boolean hasHardwareNavBar() {
// If onCreate has not been called yet, cached value is not valid
- return this.onCreateCalledOnce ? this.hasHardwareNavBar :
- ViewConfiguration.get(this).hasPermanentMenuKey() ||
- KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
+ return this.onCreateCalledOnce ? this.hasHardwareNavBar : this.hasHardwareNavBar0();
+ }
+
+ private boolean hasHardwareNavBar0() {
+ return (ViewConfiguration.get(this).hasPermanentMenuKey() ||
+ KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK)) &&
+ !"0".equals(SystemProperties.get("qemu.hw.mainkeys"));
}
public void setActionBarExtraMenuButton(@DrawableRes int drawableResId,
diff --git a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java
index 448c55f..cc21da1 100644
--- a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java
+++ b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java
@@ -16,6 +16,7 @@ import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;
import com.fox2code.mmm.ActionButtonType;
+import com.fox2code.mmm.AppUpdateManager;
import com.fox2code.mmm.BuildConfig;
import com.fox2code.mmm.Constants;
import com.fox2code.mmm.MainApplication;
@@ -26,6 +27,7 @@ import com.fox2code.mmm.utils.Files;
import com.fox2code.mmm.utils.Hashes;
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.IntentHelper;
+import com.fox2code.mmm.utils.PropUtils;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.topjohnwu.superuser.CallbackList;
import com.topjohnwu.superuser.Shell;
@@ -261,23 +263,39 @@ public class InstallerActivity extends CompatActivity {
.to(installerController, installerMonitor);
} else {
String arch32 = "true"; // Do nothing by default
+ boolean needs32bit = false;
+ String moduleId = null;
+ try (ZipFile zipFile = new ZipFile(file)) {
+ if (zipFile.getEntry( // Check if module hard require 32bit support
+ "common/addon/Volume-Key-Selector/tools/arm64/keycheck") == null &&
+ zipFile.getEntry(
+ "common/addon/Volume-Key-Selector/install.sh") != null) {
+ needs32bit = true;
+ }
+ moduleId = PropUtils.readModuleId(zipFile
+ .getInputStream(zipFile.getEntry("module.prop")));
+ } catch (IOException ignored) {}
+ int compatFlags = AppUpdateManager.getFlagsForModule(moduleId);
+ if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NEED_32BIT) != 0)
+ needs32bit = true;
+ if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NO_EXT) != 0)
+ noExtensions = true;
+ if (moduleId != null && (moduleId.isEmpty() ||
+ moduleId.contains("/") || moduleId.contains("\0") ||
+ (moduleId.startsWith(".") && moduleId.endsWith(".")))) {
+ this.setInstallStateFinished(false,
+ "! This module contain a dangerous moduleId",
+ null);
+ return;
+ }
if (Build.SUPPORTED_32_BIT_ABIS.length == 0) {
- boolean needs32bit = false;
- try (ZipFile zipFile = new ZipFile(file)) {
- if (zipFile.getEntry( // Check if module hard require 32bit support
- "common/addon/Volume-Key-Selector/tools/arm64/keycheck") == null &&
- zipFile.getEntry(
- "common/addon/Volume-Key-Selector/install.sh") != null) {
- needs32bit = true;
- }
- } catch (IOException ignored) {}
if (needs32bit) {
this.setInstallStateFinished(false,
"! This module can't be installed on a 64bit only system",
null);
return;
}
- } else {
+ } else if (needs32bit || (compatFlags & AppUpdateManager.FLAG_COMPAT_NO_EXT) == 0) {
// Restore Magisk legacy stuff for retro compatibility
if (Build.SUPPORTED_32_BIT_ABIS[0].contains("arm"))
arch32 = "export ARCH32=arm";
@@ -288,7 +306,8 @@ public class InstallerActivity extends CompatActivity {
File installExecutable;
if (InstallerInitializer.peekMagiskVersion() >=
Constants.MAGISK_VER_CODE_INSTALL_COMMAND &&
- (noExtensions || MainApplication.isUsingMagiskCommand())) {
+ ((compatFlags & AppUpdateManager.FLAG_COMPAT_MAGISK_CMD) != 0 ||
+ noExtensions || MainApplication.isUsingMagiskCommand())) {
installCommand = "magisk --install-module \"" + file.getAbsolutePath() + "\"";
installExecutable = new File(InstallerInitializer.peekMagiskPath()
.equals("/sbin") ? "/sbin/magisk" : "/system/bin/magisk");
@@ -296,13 +315,14 @@ public class InstallerActivity extends CompatActivity {
installExecutable = this.extractInstallScript("module_installer_compat.sh");
if (installExecutable == null) {
this.setInstallStateFinished(false,
- "! Failed to extract module install script", "");
+ "! Failed to extract module install script", null);
return;
}
installCommand = "sh \"" + installExecutable.getAbsolutePath() + "\"" +
" /dev/null 1 \"" + file.getAbsolutePath() + "\"";
}
installerMonitor = new InstallerMonitor(installExecutable);
+ if (moduleId != null) installerMonitor.setForCleanUp(moduleId);
if (noExtensions) {
installJob = Shell.su(arch32, // No Extensions
"cd \"" + this.moduleCache.getAbsolutePath() + "\"",
@@ -462,6 +482,7 @@ public class InstallerActivity extends CompatActivity {
private static final String DEFAULT_ERR = "! Install failed";
private final String installScriptErr;
public String lastCommand = "";
+ public String forCleanUp;
public InstallerMonitor(File installScript) {
super(Runnable::run);
@@ -476,6 +497,10 @@ public class InstallerActivity extends CompatActivity {
this.lastCommand = s;
}
+ public void setForCleanUp(String forCleanUp) {
+ this.forCleanUp = forCleanUp;
+ }
+
private String doCleanUp() {
String installScriptErr = this.installScriptErr;
// This block is mainly to help fixing customize.sh syntax errors
@@ -490,6 +515,10 @@ public class InstallerActivity extends CompatActivity {
Log.e(TAG, "Failed to delete failed update");
return "Error: " + installScriptErr.substring(i + 1);
}
+ } else if (this.forCleanUp != null) {
+ SuFile moduleUpdate = new SuFile("/data/adb/modules_update/" + this.forCleanUp);
+ if (moduleUpdate.exists() && !moduleUpdate.deleteRecursive())
+ Log.e(TAG, "Failed to delete failed update");
}
return DEFAULT_ERR;
}
diff --git a/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java b/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java
index c10d62e..a022b1a 100644
--- a/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java
+++ b/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java
@@ -30,8 +30,21 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.URISyntaxException;
public class IntentHelper {
+ private static final String TAG = "IntentHelper";
+
+ public static void openUri(Context context, String uri) {
+ if (uri.startsWith("intent://")) {
+ try {
+ startActivity(context, Intent.parseUri(uri, Intent.URI_INTENT_SCHEME), false);
+ } catch (URISyntaxException | ActivityNotFoundException e) {
+ Log.e(TAG, "Failed launch of " + uri, e);
+ }
+ } else openUrl(context, uri);
+ }
+
public static void openUrl(Context context, String url) {
openUrl(context, url, false);
}
@@ -227,7 +240,7 @@ public class IntentHelper {
callback.onReceived(destination, null, RESPONSE_ERROR);
return;
}
- Log.d("IntentHelper", "FilePicker returned " + uri);
+ Log.d(TAG, "FilePicker returned " + uri);
if ("http".equals(uri.getScheme()) ||
"https".equals(uri.getScheme())) {
callback.onReceived(destination, uri, RESPONSE_URL);
@@ -259,7 +272,7 @@ public class IntentHelper {
Files.copy(inputStream, outputStream);
success = true;
} catch (Exception e) {
- Log.e("IntentHelper", "failed copy of " + uri, e);
+ Log.e(TAG, "failed copy of " + uri, e);
Toast.makeText(compatActivity,
R.string.file_picker_failure,
Toast.LENGTH_SHORT).show();
@@ -267,7 +280,7 @@ public class IntentHelper {
Files.closeSilently(inputStream);
Files.closeSilently(outputStream);
if (!success && destination.exists() && !destination.delete())
- Log.e("IntentHelper", "Failed to delete artefact!");
+ Log.e(TAG, "Failed to delete artefact!");
}
callback.onReceived(destination, uri, success ? RESPONSE_FILE : RESPONSE_ERROR);
});
diff --git a/app/src/main/java/com/fox2code/mmm/utils/PropUtils.java b/app/src/main/java/com/fox2code/mmm/utils/PropUtils.java
index 5a9deed..8b78628 100644
--- a/app/src/main/java/com/fox2code/mmm/utils/PropUtils.java
+++ b/app/src/main/java/com/fox2code/mmm/utils/PropUtils.java
@@ -1,7 +1,11 @@
package com.fox2code.mmm.utils;
+import static com.fox2code.mmm.AppUpdateManager.FLAG_COMPAT_LOW_QUALITY;
+import static com.fox2code.mmm.AppUpdateManager.getFlagsForModule;
+
import android.os.Build;
import android.text.TextUtils;
+import android.util.Log;
import com.fox2code.mmm.manager.ModuleInfo;
import com.topjohnwu.superuser.io.SuFileInputStream;
@@ -274,6 +278,22 @@ public class PropUtils {
}
}
+ public static String readModuleId(InputStream inputStream) {
+ String moduleId = null;
+ try (BufferedReader bufferedReader = new BufferedReader(
+ new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ if (line.startsWith("id=")) {
+ moduleId = line.substring(3).trim();
+ }
+ }
+ } catch (IOException e) {
+ Log.d("PropUtils", "Failed to get moduleId", e);
+ }
+ return moduleId;
+ }
+
public static void applyFallbacks(ModuleInfo moduleInfo) {
if (moduleInfo.support == null || moduleInfo.support.isEmpty()) {
moduleInfo.support = moduleSupportsFallbacks.get(moduleInfo.id);
@@ -299,7 +319,8 @@ public class PropUtils {
|| moduleInfo.author == null || !TextUtils.isGraphic(moduleInfo.author)
|| (description = moduleInfo.description) == null || !TextUtils.isGraphic(description)
|| description.toLowerCase(Locale.ROOT).equals(moduleInfo.name.toLowerCase(Locale.ROOT))
- || description.length() < Math.min(Math.max(moduleInfo.name.length() + 4, 16), 24);
+ || description.length() < Math.min(Math.max(moduleInfo.name.length() + 4, 16), 24)
+ || (getFlagsForModule(moduleInfo.id) & FLAG_COMPAT_LOW_QUALITY) != 0;
}
private static boolean isInvalidValue(String name) {
diff --git a/app/src/main/res/values-id/arrays.xml b/app/src/main/res/values-id/arrays.xml
new file mode 100644
index 0000000..d1e9488
--- /dev/null
+++ b/app/src/main/res/values-id/arrays.xml
@@ -0,0 +1,7 @@
+
+
+ - Sistem
+ - Gelap
+ - Terang
+
+
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
new file mode 100644
index 0000000..3815c05
--- /dev/null
+++ b/app/src/main/res/values-id/strings.xml
@@ -0,0 +1,96 @@
+
+ Fox\'s Magisk Module Manager
+ Fox\'s Mmm
+ Tidak dapat mengakses root atau Magisk
+ Memuat…
+ Dapat diperbarui
+ Terpasang
+ Repo Online
+ Aplikasi berada dalam mode lockdown
+ Tidak dapat mengunduh file.
+ Modul membutuhkan waktu yang terlalu lama untuk melakukan boot, pertimbangkanglah untuk mematikan beberapa modul
+ Tidak dapat terhubung dengan internet
+ SettingsActivity
+ Tersedia pembaruan aplikasi
+ Perbarui
+ Tidak dapat menemukan deskripsi.
+ Unduh modul
+ Instal modul
+ Perbarui modul
+ Changelog
+ Situs
+ Dukungan
+ Donasi
+ Kirim sebuah modul
+ Membutuhkan Android 6.0+
+
+
+ Pembaruan terakhir:
+ Repo:
+ oleh
+ Diunduh:
+ Bintang:
+
+
+
+ Kelola repo
+ Mode lockdown
+ Mode lockdown mencegah manager dari melakukan tindakan terhadap modul
+ Peraturan
+ Info
+ Perlihatkan lisensi
+ Lisensi
+ Tampilkan modul tidak kompatibel
+ Tampilkan modul yang tidak kompatibel dengan perangkat anda berdasarkan metadata mereka
+ Versi Magisk usang!
+ Repos
+ Repository yang menampung modul Magisk
+ Sebuah alternatif Magisk-Modules-Repo dengan restriksi lebih ringan.
+ Hapus file modul?
+ Simpan file
+ Hapus file
+ Tidak dapat menghapus file modul
+ Tema
+ Mode tema
+ Id modul:
+ Instal modul dari penyimpanan
+ Modul yang diseleksi mempunyai format yang tidak valid
+ Instal lokal
+ Kode sumber
+ Modul bawaan Magisk
+ Modul bawaan Substratum
+ Paksa mode gelap pada terminal
+ Pemilih file anda sekarang gagal memberikan akses kepada file.
+ Instal jarak jauh
+ Pemilih file anda mengembalikan tanggapan yang tidak standar.
+ Gunakan perintah instal modul magisk
+
+ Waktu diuji ini menyebabkan masalah kepada alat diagnosis kesalahan instal modul,
+ jadi saya menyembunyikan opsi ini dibelakan mode pengembang, aktifkan pada risiko anda sendiri!
+
+ Mode pengembang aktif
+ Paksa Bahasa Inggris
+ Nonaktifkan filter modul qualitas rendah
+
+ Beberapa modul tidak menyatakan metadata mereka dengan benar,menyebabkan glitch visual,
+ dan/atau mengindikasikan modul qualitas rendah, nonaktifkan pada risiko anda sendiri!
+
+ DNS melalui HTTPS
+
+ Mungkin dapat memperbaiki masalah koneksi didalam beberapa kasus.
+ (Tidak berlaku untuk WebView)
+
+ Nonaktifkan ekstensi
+
+ Nonaktifkan ekstensi Fox\'s Mmm, ini mencegah modul dari menggunakan
+ ekstensi terminal, berguna jika sebuah modul menyalahgunakan ekstensi Fox\'s Mmm.
+
+ Wrap teks
+
+ Wrap teks ke sebuah garis baru daripada menaruh
+ semua teks pada garis yang sama pada saat menginstal sebuah modul
+
+ Aktifkan blur
+ Repo diaktifkan
+ Repo dinonaktifkan
+
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index bf920b3..239ff79 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -22,6 +22,7 @@
支持
捐赠
提交一个模块
+ 需要 Android 6.0+
最后更新:
@@ -50,6 +51,7 @@
删除文件
无法删除模块文件
主题
+ 主题模式
模块ID:
从存储空间安装模块
所选模块的格式无效
@@ -88,6 +90,7 @@
将文本换行至新行,
而不是将所有文本放在同一行
+ 启用模糊
启用仓库
禁用仓库