Implement downloading files without installing them. (Implement #3)

pull/55/head
Fox2Code 2 years ago
parent ef00997ea4
commit f66503063e

@ -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

@ -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'

@ -16,6 +16,8 @@
<!-- Supposed to fix bugs with old firmware, only requested on pre Marshmallow -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="22" />
<application
android:name=".MainApplication"

@ -1,17 +1,22 @@
package com.fox2code.mmm;
import android.app.AlertDialog;
import android.content.Context;
import android.util.Log;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.appcompat.app.AlertDialog;
import com.fox2code.mmm.compat.CompatActivity;
import com.fox2code.mmm.compat.CompatDisplay;
import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.manager.LocalModuleInfo;
import com.fox2code.mmm.manager.ModuleInfo;
import com.fox2code.mmm.manager.ModuleManager;
import com.fox2code.mmm.utils.IntentHelper;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
public enum ActionButtonType {
INFO(R.drawable.ic_baseline_info_24) {
@ -53,15 +58,63 @@ public enum ActionButtonType {
if (moduleInfo == null) return;
String updateZipUrl = moduleHolder.getUpdateZipUrl();
if (updateZipUrl == null) return;
// Androidacy manage the selection between download and install
if (updateZipUrl.startsWith("https://www.androidacy.com/")) {
IntentHelper.openUrlAndroidacy(
button.getContext(), updateZipUrl, true,
moduleInfo.name, moduleInfo.config);
return;
}
String updateZipChecksum = moduleHolder.getUpdateZipChecksum();
IntentHelper.openInstaller(button.getContext(), updateZipUrl,
moduleInfo.name, moduleInfo.config, updateZipChecksum);
boolean hasRoot = InstallerInitializer.peekMagiskPath() != null
&& !MainApplication.isShowcaseMode();
MaterialAlertDialogBuilder builder =
new MaterialAlertDialogBuilder(button.getContext());
builder.setTitle(moduleInfo.name).setCancelable(true)
.setIcon(R.drawable.ic_baseline_extension_24);
String desc;
if (moduleInfo instanceof LocalModuleInfo) {
LocalModuleInfo localModuleInfo = (LocalModuleInfo) moduleInfo;
desc = localModuleInfo.updateChangeLog.isEmpty() ?
moduleInfo.description : localModuleInfo.updateChangeLog;
} else {
desc = moduleInfo.description;
}
if (desc == null || desc.isEmpty()) {
builder.setMessage(R.string.no_desc_found);
} else {
if (desc.length() == 1000) {
int lastDot = desc.lastIndexOf('.');
if (lastDot == -1) {
int lastSpace = desc.lastIndexOf(' ');
if (lastSpace == -1)
desc = desc.substring(0, lastSpace);
} else {
desc = desc.substring(0, lastDot + 1);
}
}
builder.setMessage(desc);
}
Log.d("Test", "URL: " + updateZipUrl);
builder.setNegativeButton(R.string.download_module, (x, y) ->
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() {

@ -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<ModuleHolder> {
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<ModuleHolder> {
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();

@ -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;
}
}
}

@ -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));
}
}

@ -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);

@ -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)) +

@ -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);

@ -1,5 +1,5 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.MagiskModuleManager.Light" parent="Theme.MaterialComponents.Light.DarkActionBar">
<style name="Theme.MagiskModuleManager.Light" parent="Theme.MaterialComponents.Light">
<item name="android:isLightTheme"
tools:targetApi="q">true</item>
<item name="isLightTheme">true</item>
@ -21,6 +21,8 @@
<item name="android:activityCloseExitAnimation">@*android:anim/slide_out_right</item> -->
<item name="android:windowEnterAnimation">@android:anim/fade_in</item>
<item name="android:windowExitAnimation">@android:anim/fade_out</item>
<item name="android:dialogCornerRadius" tools:targetApi="p">8dp</item>
<item name="dialogCornerRadius">8dp</item>
</style>
<!-- Base application theme. -->
@ -46,6 +48,8 @@
<item name="android:activityCloseExitAnimation">@*android:anim/slide_out_right</item> -->
<item name="android:windowEnterAnimation">@android:anim/fade_in</item>
<item name="android:windowExitAnimation">@android:anim/fade_out</item>
<item name="android:dialogCornerRadius" tools:targetApi="p">6dp</item>
<item name="dialogCornerRadius">6dp</item>
</style>
<!-- Base application theme. -->

Loading…
Cancel
Save