Merge branch 'Fox2Code:master' into master

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

@ -92,7 +92,7 @@ public enum ActionButtonType {
}
Log.d("Test", "URL: " + updateZipUrl);
builder.setNegativeButton(R.string.download_module, (x, y) ->
IntentHelper.openUrl(button.getContext(), updateZipUrl, true));
IntentHelper.openCustomTab(button.getContext(), updateZipUrl));
if (hasRoot) {
builder.setPositiveButton(moduleHolder.hasUpdate() ?
R.string.update_module : R.string.install_module, (x, y) -> {

@ -29,7 +29,7 @@ public class 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";
"https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/issues/4";
public static AppUpdateManager getAppUpdateManager() {
return INSTANCE;

@ -16,6 +16,7 @@ public class Constants {
public static final String EXTRA_INSTALL_CHECKSUM = "extra_install_checksum";
public static final String EXTRA_INSTALL_NO_EXTENSIONS = "extra_install_no_extensions";
public static final String EXTRA_INSTALL_TEST_ROOTLESS = "extra_install_test_rootless";
public static final String EXTRA_ANDROIDACY_COMPAT_LEVEL = "extra_androidacy_compat_level";
public static final String EXTRA_ANDROIDACY_ALLOW_INSTALL = "extra_androidacy_allow_install";
public static final String EXTRA_ANDROIDACY_ACTIONBAR_TITLE = "extra_androidacy_actionbar_title";
public static final String EXTRA_ANDROIDACY_ACTIONBAR_CONFIG = "extra_androidacy_actionbar_config";

@ -134,7 +134,7 @@ public class MainActivity extends CompatActivity implements SwipeRefreshLayout.O
});
this.searchView.setEnabled(false); // Enabled later
this.cardIconifyUpdate();
this.updateScreenInsets();
this.updateScreenInsets(this.getResources().getConfiguration());
InstallerInitializer.tryGetMagiskPathAsync(new InstallerInitializer.Callback() {
@Override
public void onPathReceived(String path) {

@ -77,6 +77,7 @@ public class AndroidacyActivity extends CompatActivity {
Constants.EXTRA_ANDROIDACY_ALLOW_INSTALL, false);
String title = intent.getStringExtra(Constants.EXTRA_ANDROIDACY_ACTIONBAR_TITLE);
String config = intent.getStringExtra(Constants.EXTRA_ANDROIDACY_ACTIONBAR_CONFIG);
int compatLevel = intent.getIntExtra(Constants.EXTRA_ANDROIDACY_COMPAT_LEVEL, 0);
this.setContentView(R.layout.webview);
setActionBarBackground(null);
this.setDisplayHomeAsUpEnabled(true);
@ -166,8 +167,24 @@ public class AndroidacyActivity extends CompatActivity {
return true;
}
});
this.webView.setDownloadListener((
downloadUrl, userAgent, contentDisposition, mimetype, contentLength) -> {
if (AndroidacyUtil.isAndroidacyLink(downloadUrl)) {
AndroidacyWebAPI androidacyWebAPI = this.androidacyWebAPI;
if (androidacyWebAPI != null) {
if (androidacyWebAPI.consumedAction && !androidacyWebAPI.downloadMode) {
return; // Native module popup may cause download after consumed action
}
androidacyWebAPI.consumedAction = true;
androidacyWebAPI.downloadMode = false;
} else if (this.backOnResume) return;
this.backOnResume = true;
IntentHelper.openCustomTab(this, downloadUrl);
}
});
this.webView.addJavascriptInterface(androidacyWebAPI =
new AndroidacyWebAPI(this, allowInstall), "mmm");
if (compatLevel != 0) androidacyWebAPI.notifyCompatModeRaw(compatLevel);
this.webView.loadUrl(url);
}

@ -4,19 +4,27 @@ import android.net.Uri;
import android.os.Build;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.Keep;
import androidx.appcompat.app.AlertDialog;
import com.fox2code.mmm.BuildConfig;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.R;
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.repo.RepoManager;
import com.fox2code.mmm.repo.RepoModule;
import com.fox2code.mmm.utils.Files;
import com.fox2code.mmm.utils.Hashes;
import com.fox2code.mmm.utils.IntentHelper;
import com.fox2code.mmm.utils.PropUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.io.File;
import java.io.IOException;
@ -25,31 +33,115 @@ import java.nio.charset.StandardCharsets;
@Keep
public class AndroidacyWebAPI {
private static final String TAG = "AndroidacyWebAPI";
private static final int MAX_COMPAT_MODE = 1;
private final AndroidacyActivity activity;
private final boolean allowInstall;
boolean consumedAction;
boolean downloadMode;
int effectiveCompatMode;
int notifiedCompatMode;
public AndroidacyWebAPI(AndroidacyActivity activity, boolean allowInstall) {
this.activity = activity;
this.allowInstall = allowInstall;
}
public void forceQuitRaw(String error) {
void forceQuitRaw(String error) {
Toast.makeText(this.activity, error, Toast.LENGTH_LONG).show();
this.activity.runOnUiThread(this.activity::forceBackPressed);
this.activity.backOnResume = true; // Set backOnResume just in case
this.downloadMode = false;
}
void openNativeModuleDialogRaw(String moduleUrl, String installTitle,
String checksum, boolean canInstall) {
this.downloadMode = false;
RepoModule repoModule = RepoManager.getINSTANCE()
.getAndroidacyRepoData().moduleHashMap.get(installTitle);
String title, description;
if (repoModule != null) {
title = repoModule.moduleInfo.name;
description = repoModule.moduleInfo.description;
if (description == null || description.length() == 0) {
description = this.activity.getString(R.string.no_desc_found);
}
} else {
title = PropUtils.makeNameFromId(installTitle);
String checkSumType = Hashes.checkSumName(checksum);
if (checkSumType == null) {
description = "Checksum: " + ((
checksum == null || checksum.isEmpty()) ? "null" : checksum);
} else {
description = checkSumType + ": " + checksum;
}
}
final MaterialAlertDialogBuilder builder =
new MaterialAlertDialogBuilder(this.activity);
builder.setTitle(title).setMessage(description).setCancelable(true)
.setIcon(R.drawable.ic_baseline_extension_24);
builder.setNegativeButton(R.string.download_module, (x, y) -> {
this.downloadMode = true;
this.activity.webView.loadUrl(moduleUrl);
});
if (canInstall) {
boolean hasUpdate = false;
String config = null;
if (repoModule != null) {
config = repoModule.moduleInfo.config;
LocalModuleInfo localModuleInfo =
ModuleManager.getINSTANCE().getModules().get(repoModule.id);
hasUpdate = localModuleInfo != null &&
repoModule.moduleInfo.versionCode > localModuleInfo.versionCode;
}
final String fModuleUrl = moduleUrl, fTitle = title,
fConfig = config, fChecksum = checksum;
builder.setPositiveButton(hasUpdate ?
R.string.update_module : R.string.install_module, (x, y) -> {
IntentHelper.openInstaller(this.activity,
fModuleUrl, fTitle, fConfig, fChecksum);
});
}
builder.setOnCancelListener(dialogInterface -> {
if (!this.activity.backOnResume)
this.consumedAction = false;
});
final int dim5dp = CompatDisplay.dpToPixel(5);
builder.setBackgroundInsetStart(dim5dp).setBackgroundInsetEnd(dim5dp);
this.activity.runOnUiThread(() -> {
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);
}
}
});
}
void notifyCompatModeRaw(int value) {
if (this.consumedAction) return;
Log.d(TAG, "Androidacy Compat mode: " + value);
this.notifiedCompatMode = value;
if (value < 0) {
value = 0;
} else if (value > MAX_COMPAT_MODE) {
value = MAX_COMPAT_MODE;
}
this.effectiveCompatMode = value;
}
@JavascriptInterface
public void forceQuit(String error) {
if (this.consumedAction) return;
// Allow forceQuit and cancel in downloadMode
if (this.consumedAction && !this.downloadMode) return;
this.consumedAction = true;
this.forceQuitRaw(error);
}
@JavascriptInterface
public void cancel() {
if (this.consumedAction) return;
// Allow forceQuit and cancel in downloadMode
if (this.consumedAction && !this.downloadMode) return;
this.consumedAction = true;
this.activity.runOnUiThread(
this.activity::forceBackPressed);
@ -62,12 +154,30 @@ public class AndroidacyWebAPI {
public void openUrl(String url) {
if (this.consumedAction) return;
this.consumedAction = true;
this.downloadMode = false;
Log.d(TAG, "Received openUrl request: " + url);
if (Uri.parse(url).getScheme().equals("https")) {
IntentHelper.openUrl(this.activity, url);
}
}
/**
* Open an url in a custom tab if possible.
*/
@JavascriptInterface
public void openCustomTab(String url) {
if (this.consumedAction) return;
this.consumedAction = true;
this.downloadMode = false;
Log.d(TAG, "Received openCustomTab request: " + url);
if (Uri.parse(url).getScheme().equals("https")) {
IntentHelper.openCustomTab(this.activity, url);
}
}
/**
* Return if current theme is a light theme.
*/
@JavascriptInterface
public boolean isLightTheme() {
return MainApplication.getINSTANCE().isLightTheme();
@ -97,14 +207,57 @@ public class AndroidacyWebAPI {
*/
@JavascriptInterface
public void install(String moduleUrl, String installTitle,String checksum) {
if (this.consumedAction || !this.canInstall()) {
// If compat mode is 0, this means Androidacy didn't implemented a download mode yet
if (this.consumedAction || (this.effectiveCompatMode >= 1 && !this.canInstall())) {
return;
}
this.consumedAction = true;
this.downloadMode = false;
Log.d(TAG, "Received install request: " +
moduleUrl + " " + installTitle + " " + checksum);
Uri uri = Uri.parse(moduleUrl);
if (!AndroidacyUtil.isAndroidacyLink(moduleUrl, uri)) {
if (!AndroidacyUtil.isAndroidacyLink(moduleUrl)) {
this.forceQuitRaw("Non Androidacy module link used on Androidacy");
return;
}
if (checksum != null) checksum = checksum.trim();
if (checksum == null || checksum.isEmpty()) {
Log.w(TAG, "Androidacy WebView didn't provided a checksum!");
} else if (!Hashes.checkSumValid(checksum)) {
this.forceQuitRaw("Androidacy didn't provided a valid checksum");
return;
}
// Let's handle download mode ourself if not implemented
if (this.effectiveCompatMode < 1) {
if (!this.canInstall()) {
this.downloadMode = true;
this.activity.runOnUiThread(() ->
this.activity.webView.loadUrl(moduleUrl));
} else {
this.openNativeModuleDialogRaw(moduleUrl, installTitle, checksum, true);
}
} else {
RepoModule repoModule = RepoManager.getINSTANCE()
.getAndroidacyRepoData().moduleHashMap.get(installTitle);
String config = null;
if (repoModule != null && repoModule.moduleInfo.name.length() >= 3) {
installTitle = repoModule.moduleInfo.name; // Set title to module name
config = repoModule.moduleInfo.config;
}
this.activity.backOnResume = true;
IntentHelper.openInstaller(this.activity,
moduleUrl, installTitle, config, checksum);
}
}
/**
* install a module via url, with the file checked with the md5 checksum value.
*/
@JavascriptInterface
public void openNativeModuleDialog(String moduleUrl, String moduleId, String checksum) {
if (this.consumedAction) return;
this.consumedAction = true;
this.downloadMode = false;
if (!AndroidacyUtil.isAndroidacyLink(moduleUrl)) {
this.forceQuitRaw("Non Androidacy module link used on Androidacy");
return;
}
@ -115,9 +268,7 @@ public class AndroidacyWebAPI {
this.forceQuitRaw("Androidacy didn't provided a valid checksum");
return;
}
this.activity.backOnResume = true;
IntentHelper.openInstaller(this.activity,
moduleUrl, installTitle, null, checksum);
this.openNativeModuleDialogRaw(moduleUrl, moduleId, checksum, this.canInstall());
}
/**
@ -274,4 +425,25 @@ public class AndroidacyWebAPI {
public int getNavigationBarHeight() {
return this.activity.getNavigationBarHeight();
}
/**
* Allow Androidacy backend to notify compat mode
* return current effective compat mode
*/
@JavascriptInterface
public int getEffectiveCompatMode() {
return this.effectiveCompatMode;
}
// Androidacy feature level declaration method
@JavascriptInterface
public void notifyCompatUnsupported() {
this.notifyCompatModeRaw(0);
}
@JavascriptInterface
public void notifyCompatDownloadButton() {
this.notifyCompatModeRaw(1);
}
}

@ -54,6 +54,7 @@ public final class RepoManager {
private final MainApplication mainApplication;
private final LinkedHashMap<String, RepoData> repoData;
private final HashMap<String, RepoModule> modules;
private final AndroidacyRepoData androidacyRepoData;
private RepoManager(MainApplication mainApplication) {
this.mainApplication = mainApplication;
@ -61,7 +62,8 @@ public final class RepoManager {
this.modules = new HashMap<>();
// We do not have repo list config yet.
this.addRepoData(MAGISK_ALT_REPO);
this.addAndroidacyRepoData();
this.androidacyRepoData =
this.addAndroidacyRepoData();
// Populate default cache
for (RepoData repoData:this.repoData.values()) {
for (RepoModule repoModule:repoData.moduleHashMap.values()) {
@ -251,13 +253,17 @@ public final class RepoManager {
return repoData;
}
private RepoData addAndroidacyRepoData() {
private AndroidacyRepoData addAndroidacyRepoData() {
File cacheRoot = new File(this.mainApplication.getCacheDir(), "androidacy_repo");
SharedPreferences sharedPreferences = this.mainApplication
.getSharedPreferences("mmm_androidacy_repo", Context.MODE_PRIVATE);
RepoData repoData = new AndroidacyRepoData(
AndroidacyRepoData repoData = new AndroidacyRepoData(
ANDROIDACY_MAGISK_REPO_ENDPOINT, cacheRoot, sharedPreferences);
this.repoData.put(ANDROIDACY_MAGISK_REPO_ENDPOINT, repoData);
return repoData;
}
public AndroidacyRepoData getAndroidacyRepoData() {
return this.androidacyRepoData;
}
}

@ -105,4 +105,21 @@ public class Hashes {
return true;
}
}
public static String checkSumName(String checksum) {
if (checksum == null) return null;
switch (checksum.length()) {
case 0:
default:
return null;
case 32:
return "MD5";
case 40:
return "SHA-1";
case 64:
return "SHA-256";
case 128:
return "SHA-512";
}
}
}

@ -12,9 +12,11 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.util.TypedValue;
import android.widget.Toast;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.app.BundleCompat;
import com.fox2code.mmm.BuildConfig;
import com.fox2code.mmm.Constants;
@ -34,6 +36,16 @@ import java.net.URISyntaxException;
public class IntentHelper {
private static final String TAG = "IntentHelper";
private static final String EXTRA_TAB_SESSION =
"android.support.customtabs.extra.SESSION";
private static final String EXTRA_TAB_COLOR_SCHEME =
"androidx.browser.customtabs.extra.COLOR_SCHEME";
private static final int EXTRA_TAB_COLOR_SCHEME_DARK = 2;
private static final int EXTRA_TAB_COLOR_SCHEME_LIGHT = 1;
private static final String EXTRA_TAB_TOOLBAR_COLOR =
"android.support.customtabs.extra.TOOLBAR_COLOR";
private static final String EXTRA_TAB_EXIT_ANIMATION_BUNDLE =
"android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
public static void openUri(Context context, String uri) {
if (uri.startsWith("intent://")) {
@ -58,8 +70,22 @@ public class IntentHelper {
}
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();
Toast.makeText(context, "No application can handle this request.\n"
+ " Please install a web-browser", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
public static void openCustomTab(Context context, String url) {
try {
Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
viewIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Intent tabIntent = new Intent(viewIntent);
tabIntent.addCategory(Intent.CATEGORY_BROWSABLE);
startActivityEx(context, tabIntent, viewIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, "No application can handle this request.\n"
+ " Please install a web-browser", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
@ -174,6 +200,72 @@ public class IntentHelper {
public static void startActivity(Context context, Intent intent,boolean sameApp)
throws ActivityNotFoundException {
if (sameApp) {
startActivityEx(context, intent, null);
} else {
startActivityEx(context, null, intent);
}
}
public static void startActivityEx(Context context, Intent intent1,Intent intent2)
throws ActivityNotFoundException {
if (intent1 == null && intent2 == null)
throw new NullPointerException("No intent defined for activity!");
changeFlags(intent1, true);
changeFlags(intent2, false);
Activity activity = getActivity(context);
Bundle param = ActivityOptionsCompat.makeCustomAnimation(context,
android.R.anim.fade_in, android.R.anim.fade_out).toBundle();
if (activity == null) {
if (intent1 != null) {
try {
context.startActivity(intent1, param);
return;
} catch (ActivityNotFoundException e) {
if (intent2 == null) throw e;
}
}
context.startActivity(intent2, param);
} else {
if (intent1 != null) {
// Support Custom Tabs as sameApp intent
if (intent1.hasCategory(Intent.CATEGORY_BROWSABLE)) {
if (!intent1.hasExtra(EXTRA_TAB_SESSION)) {
Bundle bundle = new Bundle();
BundleCompat.putBinder(bundle, EXTRA_TAB_SESSION, null);
intent1.putExtras(bundle);
}
intent1.putExtra(IntentHelper.EXTRA_TAB_EXIT_ANIMATION_BUNDLE, param);
if (activity instanceof CompatActivity) {
TypedValue typedValue = new TypedValue();
activity.getTheme().resolveAttribute(
android.R.attr.background, typedValue, true);
if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
intent1.putExtra(IntentHelper.EXTRA_TAB_TOOLBAR_COLOR, typedValue.data);
intent1.putExtra(IntentHelper.EXTRA_TAB_COLOR_SCHEME,
((CompatActivity) activity).isLightTheme() ?
IntentHelper.EXTRA_TAB_COLOR_SCHEME_LIGHT :
IntentHelper.EXTRA_TAB_COLOR_SCHEME_DARK);
}
}
}
try {
intent1.putExtra(Constants.EXTRA_FADE_OUT, true);
activity.overridePendingTransition(
android.R.anim.fade_in, android.R.anim.fade_out);
activity.startActivity(intent1, param);
return;
} catch (ActivityNotFoundException e) {
if (intent2 == null) throw e;
}
}
activity.startActivity(intent2, param);
}
}
private static void changeFlags(Intent intent,boolean sameApp) {
if (intent == null) return;
int flags = intent.getFlags() &
~(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
if (!sameApp) {
@ -185,19 +277,6 @@ public class IntentHelper {
}
}
intent.setFlags(flags);
Activity activity = getActivity(context);
Bundle param = ActivityOptionsCompat.makeCustomAnimation(context,
android.R.anim.fade_in, android.R.anim.fade_out).toBundle();
if (activity == null) {
context.startActivity(intent, param);
} else {
if (sameApp) {
intent.putExtra(Constants.EXTRA_FADE_OUT, true);
activity.overridePendingTransition(
android.R.anim.fade_in, android.R.anim.fade_out);
}
activity.startActivity(intent, param);
}
}
public static Activity getActivity(Context context) {

@ -353,7 +353,7 @@ public class PropUtils {
|| url.length() <= 12 || url.indexOf('\0') != -1;
}
private static String makeNameFromId(String moduleId) {
public static String makeNameFromId(String moduleId) {
return moduleId.substring(0, 1).toUpperCase(Locale.ROOT) +
moduleId.substring(1).replace('_', ' ');
}

Loading…
Cancel
Save