From f66503063ef9276bfd4bd3e2990b421744e47b06 Mon Sep 17 00:00:00 2001 From: Fox2Code Date: Mon, 31 Jan 2022 22:59:47 +0100 Subject: [PATCH] Implement downloading files without installing them. (Implement #3) --- DEVELOPERS.md | 13 +++- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 2 + .../com/fox2code/mmm/ActionButtonType.java | 61 +++++++++++++++++-- .../java/com/fox2code/mmm/ModuleHolder.java | 5 +- .../mmm/androidacy/AndroidacyWebAPI.java | 25 ++++++-- .../fox2code/mmm/compat/CompatDisplay.java | 21 +++++++ .../fox2code/mmm/manager/LocalModuleInfo.java | 6 +- .../fox2code/mmm/manager/ModuleManager.java | 2 +- .../com/fox2code/mmm/utils/IntentHelper.java | 26 +++++--- app/src/main/res/values/themes.xml | 6 +- 11 files changed, 144 insertions(+), 25 deletions(-) create mode 100644 app/src/main/java/com/fox2code/mmm/compat/CompatDisplay.java diff --git a/DEVELOPERS.md b/DEVELOPERS.md index b914807..cc16f5a 100644 --- a/DEVELOPERS.md +++ b/DEVELOPERS.md @@ -6,10 +6,21 @@ Note: This doc assume you already read the Note: official repo do not accept new modules anymore, submit [here](https://github.com/Magisk-Modules-Alt-Repo/submission) instead. -Index: +Index: +- [Special notes](DEVELOPERS.md#special-notes) - [Properties](DEVELOPERS.md#properties) - [Installer commands](DEVELOPERS.md#installer-commands) +## Special notes + +MitM: Certificate pinning is only available since Android 7.0, +any issue regarding MitM that can only be performed of +Android versions that doesn't support this feature will be ignored. + +App hiding: I don't intent on hiding the app, the package names should always be +`com.fox2code.mmm` or starts with `com.fox2code.mmm.`, however I notice the presence of +my app is causing issues due to it existing, I may add an hiding feature to the app. + ## Properties In addition to the following required magisk properties diff --git a/app/build.gradle b/app/build.gradle index 23877f0..86b0323 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -51,7 +51,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.emoji2:emoji2:1.0.1' implementation 'androidx.emoji2:emoji2-views-helper:1.0.1' - implementation 'androidx.preference:preference:1.1.1' + implementation 'androidx.preference:preference:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d143063..04c600b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,6 +16,8 @@ + + IntentHelper.openUrl(button.getContext(), updateZipUrl, true)); + if (hasRoot) { + builder.setPositiveButton(moduleHolder.hasUpdate() ? + R.string.update_module : R.string.install_module, (x, y) -> { + String updateZipChecksum = moduleHolder.getUpdateZipChecksum(); + IntentHelper.openInstaller(button.getContext(), updateZipUrl, + moduleInfo.name, moduleInfo.config, updateZipChecksum); + }); + } + int dim5dp = CompatDisplay.dpToPixel(5); + builder.setBackgroundInsetStart(dim5dp).setBackgroundInsetEnd(dim5dp); + AlertDialog alertDialog = builder.show(); + for (int i = -3; i < 0; i++) { + Button alertButton = alertDialog.getButton(i); + if (alertButton != null && alertButton.getPaddingStart() > dim5dp) { + alertButton.setPadding(dim5dp, dim5dp, dim5dp, dim5dp); + } + } } }, UNINSTALL() { diff --git a/app/src/main/java/com/fox2code/mmm/ModuleHolder.java b/app/src/main/java/com/fox2code/mmm/ModuleHolder.java index a164b8d..a56e908 100644 --- a/app/src/main/java/com/fox2code/mmm/ModuleHolder.java +++ b/app/src/main/java/com/fox2code/mmm/ModuleHolder.java @@ -7,7 +7,6 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.StringRes; -import com.fox2code.mmm.installer.InstallerInitializer; import com.fox2code.mmm.manager.LocalModuleInfo; import com.fox2code.mmm.manager.ModuleInfo; import com.fox2code.mmm.repo.RepoModule; @@ -103,6 +102,7 @@ public final class ModuleHolder implements Comparable { return timeStamp <= 0 ? "" : MainApplication.formatTime(timeStamp); } + public String getRepoName() { if (this.repoModule == null) return ""; return this.repoModule.repoName; @@ -157,8 +157,7 @@ public final class ModuleHolder implements Comparable { buttonTypeList.add(ActionButtonType.INFO); } if ((this.repoModule != null || (this.moduleInfo != null && - this.moduleInfo.updateZipUrl != null)) && !showcaseMode && - InstallerInitializer.peekMagiskPath() != null) { + this.moduleInfo.updateZipUrl != null))) { buttonTypeList.add(ActionButtonType.UPDATE_INSTALL); } String config = this.getMainModuleConfig(); diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java index 03f63be..d875031 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java @@ -68,6 +68,7 @@ public class AndroidacyWebAPI { */ @JavascriptInterface public boolean canInstall() { + // With lockdown mode enabled or lack of root, install should not have any effect return this.allowInstall && this.hasRoot() && !MainApplication.isShowcaseMode(); } @@ -77,9 +78,7 @@ public class AndroidacyWebAPI { */ @JavascriptInterface public void install(String moduleUrl, String installTitle,String checksum) { - if (!this.allowInstall || !this.hasRoot() || - MainApplication.isShowcaseMode()) { - // With lockdown mode enabled or lack of root, install should not have any effect + if (!this.canInstall()) { return; } Log.d(TAG, "Received install request: " + @@ -154,7 +153,7 @@ public class AndroidacyWebAPI { */ @JavascriptInterface public String getAndroidacyModuleFile(String moduleId, String moduleFile) { - if (!this.isAndroidacyModule(moduleId)) return ""; + if (moduleFile == null || !this.isAndroidacyModule(moduleId)) return ""; File moduleFolder = new File("/data/adb/modules/" + moduleId); File absModuleFile = new File(moduleFolder, moduleFile).getAbsoluteFile(); if (!absModuleFile.getPath().startsWith(moduleFolder.getPath())) return ""; @@ -165,4 +164,22 @@ public class AndroidacyWebAPI { return ""; } } + + /** + * Create an ".androidacy" file with {@param content} as content + * Return true if action succeeded + */ + @JavascriptInterface + public boolean setAndroidacyModuleMeta(String moduleId, String content) { + if (content == null || !this.isAndroidacyModule(moduleId)) return false; + File androidacyMetaFile = new File( + "/data/adb/modules/" + moduleId + "/.andoridacy"); + try { + Files.writeSU(androidacyMetaFile, + content.getBytes(StandardCharsets.UTF_8)); + return true; + } catch (IOException e) { + return false; + } + } } diff --git a/app/src/main/java/com/fox2code/mmm/compat/CompatDisplay.java b/app/src/main/java/com/fox2code/mmm/compat/CompatDisplay.java new file mode 100644 index 0000000..f0ff3eb --- /dev/null +++ b/app/src/main/java/com/fox2code/mmm/compat/CompatDisplay.java @@ -0,0 +1,21 @@ +package com.fox2code.mmm.compat; + +import android.content.res.Resources; +import android.util.DisplayMetrics; + +import androidx.annotation.Dimension; +import androidx.annotation.Px; + +public class CompatDisplay { + @Dimension @Px + public static int dpToPixel(@Dimension(unit = Dimension.DP) int dp){ + return (int) (dp * ((float) Resources.getSystem().getDisplayMetrics() + .densityDpi / DisplayMetrics.DENSITY_DEFAULT)); + } + + @Dimension(unit = Dimension.DP) + public static int pixelsToDp(@Dimension @Px int px){ + return (int) (px / ((float) Resources.getSystem().getDisplayMetrics() + .densityDpi / DisplayMetrics.DENSITY_DEFAULT)); + } +} diff --git a/app/src/main/java/com/fox2code/mmm/manager/LocalModuleInfo.java b/app/src/main/java/com/fox2code/mmm/manager/LocalModuleInfo.java index b29c6a0..507161a 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/LocalModuleInfo.java +++ b/app/src/main/java/com/fox2code/mmm/manager/LocalModuleInfo.java @@ -14,7 +14,7 @@ public class LocalModuleInfo extends ModuleInfo { public String updateVersion; public long updateVersionCode = Long.MIN_VALUE; public String updateZipUrl; - public String updateChangeLog; + public String updateChangeLog = ""; public String updateChecksum; public LocalModuleInfo(String id) { @@ -34,11 +34,13 @@ public class LocalModuleInfo extends ModuleInfo { if (this.updateZipUrl.isEmpty()) throw FastException.INSTANCE; this.updateVersion = PropUtils.shortenVersionName( this.updateVersion.trim(), this.updateVersionCode); + if (this.updateChangeLog.length() > 1000) + this.updateChangeLog = this.updateChangeLog.substring(0, 1000); } catch (Exception e) { this.updateVersion = null; this.updateVersionCode = Long.MIN_VALUE; this.updateZipUrl = null; - this.updateChangeLog = null; + this.updateChangeLog = ""; this.updateChecksum = null; Log.w("LocalModuleInfo", "Failed update checking for module: " + this.id, e); diff --git a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java index 3eb723c..0c52ab7 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java +++ b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java @@ -158,7 +158,7 @@ public final class ModuleManager { moduleInfo.updateVersion = null; moduleInfo.updateVersionCode = Long.MIN_VALUE; moduleInfo.updateZipUrl = null; - moduleInfo.updateChangeLog = null; + moduleInfo.updateChangeLog = ""; } if (moduleInfo.name == null || (moduleInfo.name.equals(moduleInfo.id))) { moduleInfo.name = Character.toUpperCase(moduleInfo.id.charAt(0)) + 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 730f27e..dd4bb87 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java +++ b/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java @@ -33,9 +33,17 @@ import java.io.OutputStream; public class IntentHelper { public static void openUrl(Context context, String url) { + openUrl(context, url, false); + } + + public static void openUrl(Context context, String url, boolean forceBrowser) { try { Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - context.startActivity(myIntent); + myIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (forceBrowser) { + myIntent.addCategory(Intent.CATEGORY_BROWSABLE); + } + startActivity(context, myIntent, false); } catch (ActivityNotFoundException e) { Toast.makeText(context, "No application can handle this request." + " Please install a web-browser", Toast.LENGTH_SHORT).show(); @@ -60,7 +68,7 @@ public class IntentHelper { if (config != null) myIntent.putExtra(Constants.EXTRA_ANDROIDACY_ACTIONBAR_CONFIG, config); MainApplication.addSecret(myIntent); - context.startActivity(myIntent); + startActivity(context, myIntent, true); } catch (ActivityNotFoundException e) { Toast.makeText(context, "No application can handle this request." + " Please install a web-browser", Toast.LENGTH_SHORT).show(); @@ -155,13 +163,15 @@ public class IntentHelper { public static void startActivity(Context context, Intent intent,boolean sameApp) throws ActivityNotFoundException { - int flags = intent.getFlags(); - if (sameApp) { - flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK; - // flags |= Intent.FLAG_ACTIVITY_REORDER_TO_FRONT; - } else { + int flags = intent.getFlags() & + ~(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + if (!sameApp) { flags &= ~Intent.FLAG_ACTIVITY_MULTIPLE_TASK; - flags |= Intent.FLAG_ACTIVITY_NEW_TASK; + if (intent.getData() == null) { + flags |= Intent.FLAG_ACTIVITY_NEW_TASK; + } else { + flags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT; + } } intent.setFlags(flags); Activity activity = getActivity(context); diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index b0e999a..da01a99 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,5 +1,5 @@ - @@ -46,6 +48,8 @@ @*android:anim/slide_out_right --> @android:anim/fade_in @android:anim/fade_out + 6dp + 6dp