Skip to content
This repository was archived by the owner on May 4, 2023. It is now read-only.

Commit ebfa3e2

Browse files
committed
Major AndroidacyAPI rework for download button.
1 parent ee81ba4 commit ebfa3e2

File tree

10 files changed

+323
-31
lines changed

10 files changed

+323
-31
lines changed

app/src/main/java/com/fox2code/mmm/ActionButtonType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public void doAction(ImageButton button, ModuleHolder moduleHolder) {
9292
}
9393
Log.d("Test", "URL: " + updateZipUrl);
9494
builder.setNegativeButton(R.string.download_module, (x, y) ->
95-
IntentHelper.openUrl(button.getContext(), updateZipUrl, true));
95+
IntentHelper.openCustomTab(button.getContext(), updateZipUrl));
9696
if (hasRoot) {
9797
builder.setPositiveButton(moduleHolder.hasUpdate() ?
9898
R.string.update_module : R.string.install_module, (x, y) -> {

app/src/main/java/com/fox2code/mmm/AppUpdateManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class AppUpdateManager {
2929
private static final String RELEASES_API_URL =
3030
"https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/releases";
3131
private static final String COMPAT_API_URL =
32-
"https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/releases";
32+
"https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/issues/4";
3333

3434
public static AppUpdateManager getAppUpdateManager() {
3535
return INSTANCE;

app/src/main/java/com/fox2code/mmm/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class Constants {
1616
public static final String EXTRA_INSTALL_CHECKSUM = "extra_install_checksum";
1717
public static final String EXTRA_INSTALL_NO_EXTENSIONS = "extra_install_no_extensions";
1818
public static final String EXTRA_INSTALL_TEST_ROOTLESS = "extra_install_test_rootless";
19+
public static final String EXTRA_ANDROIDACY_COMPAT_LEVEL = "extra_androidacy_compat_level";
1920
public static final String EXTRA_ANDROIDACY_ALLOW_INSTALL = "extra_androidacy_allow_install";
2021
public static final String EXTRA_ANDROIDACY_ACTIONBAR_TITLE = "extra_androidacy_actionbar_title";
2122
public static final String EXTRA_ANDROIDACY_ACTIONBAR_CONFIG = "extra_androidacy_actionbar_config";

app/src/main/java/com/fox2code/mmm/MainActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newStat
134134
});
135135
this.searchView.setEnabled(false); // Enabled later
136136
this.cardIconifyUpdate();
137-
this.updateScreenInsets();
137+
this.updateScreenInsets(this.getResources().getConfiguration());
138138
InstallerInitializer.tryGetMagiskPathAsync(new InstallerInitializer.Callback() {
139139
@Override
140140
public void onPathReceived(String path) {

app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
7777
Constants.EXTRA_ANDROIDACY_ALLOW_INSTALL, false);
7878
String title = intent.getStringExtra(Constants.EXTRA_ANDROIDACY_ACTIONBAR_TITLE);
7979
String config = intent.getStringExtra(Constants.EXTRA_ANDROIDACY_ACTIONBAR_CONFIG);
80+
int compatLevel = intent.getIntExtra(Constants.EXTRA_ANDROIDACY_COMPAT_LEVEL, 0);
8081
this.setContentView(R.layout.webview);
8182
setActionBarBackground(null);
8283
this.setDisplayHomeAsUpEnabled(true);
@@ -166,8 +167,24 @@ public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathC
166167
return true;
167168
}
168169
});
170+
this.webView.setDownloadListener((
171+
downloadUrl, userAgent, contentDisposition, mimetype, contentLength) -> {
172+
if (AndroidacyUtil.isAndroidacyLink(downloadUrl)) {
173+
AndroidacyWebAPI androidacyWebAPI = this.androidacyWebAPI;
174+
if (androidacyWebAPI != null) {
175+
if (androidacyWebAPI.consumedAction && !androidacyWebAPI.downloadMode) {
176+
return; // Native module popup may cause download after consumed action
177+
}
178+
androidacyWebAPI.consumedAction = true;
179+
androidacyWebAPI.downloadMode = false;
180+
} else if (this.backOnResume) return;
181+
this.backOnResume = true;
182+
IntentHelper.openCustomTab(this, downloadUrl);
183+
}
184+
});
169185
this.webView.addJavascriptInterface(androidacyWebAPI =
170186
new AndroidacyWebAPI(this, allowInstall), "mmm");
187+
if (compatLevel != 0) androidacyWebAPI.notifyCompatModeRaw(compatLevel);
171188
this.webView.loadUrl(url);
172189
}
173190

app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java

Lines changed: 181 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,27 @@
44
import android.os.Build;
55
import android.util.Log;
66
import android.webkit.JavascriptInterface;
7+
import android.widget.Button;
78
import android.widget.Toast;
89

910
import androidx.annotation.Keep;
11+
import androidx.appcompat.app.AlertDialog;
1012

1113
import com.fox2code.mmm.BuildConfig;
1214
import com.fox2code.mmm.MainApplication;
15+
import com.fox2code.mmm.R;
16+
import com.fox2code.mmm.compat.CompatDisplay;
1317
import com.fox2code.mmm.installer.InstallerInitializer;
1418
import com.fox2code.mmm.manager.LocalModuleInfo;
1519
import com.fox2code.mmm.manager.ModuleInfo;
1620
import com.fox2code.mmm.manager.ModuleManager;
21+
import com.fox2code.mmm.repo.RepoManager;
22+
import com.fox2code.mmm.repo.RepoModule;
1723
import com.fox2code.mmm.utils.Files;
1824
import com.fox2code.mmm.utils.Hashes;
1925
import com.fox2code.mmm.utils.IntentHelper;
26+
import com.fox2code.mmm.utils.PropUtils;
27+
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
2028

2129
import java.io.File;
2230
import java.io.IOException;
@@ -25,31 +33,115 @@
2533
@Keep
2634
public class AndroidacyWebAPI {
2735
private static final String TAG = "AndroidacyWebAPI";
36+
private static final int MAX_COMPAT_MODE = 1;
2837
private final AndroidacyActivity activity;
2938
private final boolean allowInstall;
3039
boolean consumedAction;
40+
boolean downloadMode;
41+
int effectiveCompatMode;
42+
int notifiedCompatMode;
3143

3244
public AndroidacyWebAPI(AndroidacyActivity activity, boolean allowInstall) {
3345
this.activity = activity;
3446
this.allowInstall = allowInstall;
3547
}
3648

37-
public void forceQuitRaw(String error) {
49+
void forceQuitRaw(String error) {
3850
Toast.makeText(this.activity, error, Toast.LENGTH_LONG).show();
3951
this.activity.runOnUiThread(this.activity::forceBackPressed);
4052
this.activity.backOnResume = true; // Set backOnResume just in case
53+
this.downloadMode = false;
54+
}
55+
56+
void openNativeModuleDialogRaw(String moduleUrl, String installTitle,
57+
String checksum, boolean canInstall) {
58+
this.downloadMode = false;
59+
RepoModule repoModule = RepoManager.getINSTANCE()
60+
.getAndroidacyRepoData().moduleHashMap.get(installTitle);
61+
String title, description;
62+
if (repoModule != null) {
63+
title = repoModule.moduleInfo.name;
64+
description = repoModule.moduleInfo.description;
65+
if (description == null || description.length() == 0) {
66+
description = this.activity.getString(R.string.no_desc_found);
67+
}
68+
} else {
69+
title = PropUtils.makeNameFromId(installTitle);
70+
String checkSumType = Hashes.checkSumName(checksum);
71+
if (checkSumType == null) {
72+
description = "Checksum: " + ((
73+
checksum == null || checksum.isEmpty()) ? "null" : checksum);
74+
} else {
75+
description = checkSumType + ": " + checksum;
76+
}
77+
}
78+
final MaterialAlertDialogBuilder builder =
79+
new MaterialAlertDialogBuilder(this.activity);
80+
builder.setTitle(title).setMessage(description).setCancelable(true)
81+
.setIcon(R.drawable.ic_baseline_extension_24);
82+
builder.setNegativeButton(R.string.download_module, (x, y) -> {
83+
this.downloadMode = true;
84+
this.activity.webView.loadUrl(moduleUrl);
85+
});
86+
if (canInstall) {
87+
boolean hasUpdate = false;
88+
String config = null;
89+
if (repoModule != null) {
90+
config = repoModule.moduleInfo.config;
91+
LocalModuleInfo localModuleInfo =
92+
ModuleManager.getINSTANCE().getModules().get(repoModule.id);
93+
hasUpdate = localModuleInfo != null &&
94+
repoModule.moduleInfo.versionCode > localModuleInfo.versionCode;
95+
}
96+
final String fModuleUrl = moduleUrl, fTitle = title,
97+
fConfig = config, fChecksum = checksum;
98+
builder.setPositiveButton(hasUpdate ?
99+
R.string.update_module : R.string.install_module, (x, y) -> {
100+
IntentHelper.openInstaller(this.activity,
101+
fModuleUrl, fTitle, fConfig, fChecksum);
102+
});
103+
}
104+
builder.setOnCancelListener(dialogInterface -> {
105+
if (!this.activity.backOnResume)
106+
this.consumedAction = false;
107+
});
108+
final int dim5dp = CompatDisplay.dpToPixel(5);
109+
builder.setBackgroundInsetStart(dim5dp).setBackgroundInsetEnd(dim5dp);
110+
this.activity.runOnUiThread(() -> {
111+
AlertDialog alertDialog = builder.show();
112+
for (int i = -3; i < 0; i++) {
113+
Button alertButton = alertDialog.getButton(i);
114+
if (alertButton != null && alertButton.getPaddingStart() > dim5dp) {
115+
alertButton.setPadding(dim5dp, dim5dp, dim5dp, dim5dp);
116+
}
117+
}
118+
});
119+
}
120+
121+
void notifyCompatModeRaw(int value) {
122+
if (this.consumedAction) return;
123+
Log.d(TAG, "Androidacy Compat mode: " + value);
124+
this.notifiedCompatMode = value;
125+
if (value < 0) {
126+
value = 0;
127+
} else if (value > MAX_COMPAT_MODE) {
128+
value = MAX_COMPAT_MODE;
129+
}
130+
this.effectiveCompatMode = value;
41131
}
42132

43133
@JavascriptInterface
44134
public void forceQuit(String error) {
45-
if (this.consumedAction) return;
135+
// Allow forceQuit and cancel in downloadMode
136+
if (this.consumedAction && !this.downloadMode) return;
46137
this.consumedAction = true;
47138
this.forceQuitRaw(error);
48139
}
49140

50141
@JavascriptInterface
51142
public void cancel() {
52-
if (this.consumedAction) return;
143+
// Allow forceQuit and cancel in downloadMode
144+
if (this.consumedAction && !this.downloadMode) return;
53145
this.consumedAction = true;
54146
this.activity.runOnUiThread(
55147
this.activity::forceBackPressed);
@@ -62,12 +154,30 @@ public void cancel() {
62154
public void openUrl(String url) {
63155
if (this.consumedAction) return;
64156
this.consumedAction = true;
157+
this.downloadMode = false;
65158
Log.d(TAG, "Received openUrl request: " + url);
66159
if (Uri.parse(url).getScheme().equals("https")) {
67160
IntentHelper.openUrl(this.activity, url);
68161
}
69162
}
70163

164+
/**
165+
* Open an url in a custom tab if possible.
166+
*/
167+
@JavascriptInterface
168+
public void openCustomTab(String url) {
169+
if (this.consumedAction) return;
170+
this.consumedAction = true;
171+
this.downloadMode = false;
172+
Log.d(TAG, "Received openCustomTab request: " + url);
173+
if (Uri.parse(url).getScheme().equals("https")) {
174+
IntentHelper.openCustomTab(this.activity, url);
175+
}
176+
}
177+
178+
/**
179+
* Return if current theme is a light theme.
180+
*/
71181
@JavascriptInterface
72182
public boolean isLightTheme() {
73183
return MainApplication.getINSTANCE().isLightTheme();
@@ -97,14 +207,57 @@ public boolean canInstall() {
97207
*/
98208
@JavascriptInterface
99209
public void install(String moduleUrl, String installTitle,String checksum) {
100-
if (this.consumedAction || !this.canInstall()) {
210+
// If compat mode is 0, this means Androidacy didn't implemented a download mode yet
211+
if (this.consumedAction || (this.effectiveCompatMode >= 1 && !this.canInstall())) {
101212
return;
102213
}
103214
this.consumedAction = true;
215+
this.downloadMode = false;
104216
Log.d(TAG, "Received install request: " +
105217
moduleUrl + " " + installTitle + " " + checksum);
106-
Uri uri = Uri.parse(moduleUrl);
107-
if (!AndroidacyUtil.isAndroidacyLink(moduleUrl, uri)) {
218+
if (!AndroidacyUtil.isAndroidacyLink(moduleUrl)) {
219+
this.forceQuitRaw("Non Androidacy module link used on Androidacy");
220+
return;
221+
}
222+
if (checksum != null) checksum = checksum.trim();
223+
if (checksum == null || checksum.isEmpty()) {
224+
Log.w(TAG, "Androidacy WebView didn't provided a checksum!");
225+
} else if (!Hashes.checkSumValid(checksum)) {
226+
this.forceQuitRaw("Androidacy didn't provided a valid checksum");
227+
return;
228+
}
229+
// Let's handle download mode ourself if not implemented
230+
if (this.effectiveCompatMode < 1) {
231+
if (!this.canInstall()) {
232+
this.downloadMode = true;
233+
this.activity.runOnUiThread(() ->
234+
this.activity.webView.loadUrl(moduleUrl));
235+
} else {
236+
this.openNativeModuleDialogRaw(moduleUrl, installTitle, checksum, true);
237+
}
238+
} else {
239+
RepoModule repoModule = RepoManager.getINSTANCE()
240+
.getAndroidacyRepoData().moduleHashMap.get(installTitle);
241+
String config = null;
242+
if (repoModule != null && repoModule.moduleInfo.name.length() >= 3) {
243+
installTitle = repoModule.moduleInfo.name; // Set title to module name
244+
config = repoModule.moduleInfo.config;
245+
}
246+
this.activity.backOnResume = true;
247+
IntentHelper.openInstaller(this.activity,
248+
moduleUrl, installTitle, config, checksum);
249+
}
250+
}
251+
252+
/**
253+
* install a module via url, with the file checked with the md5 checksum value.
254+
*/
255+
@JavascriptInterface
256+
public void openNativeModuleDialog(String moduleUrl, String moduleId, String checksum) {
257+
if (this.consumedAction) return;
258+
this.consumedAction = true;
259+
this.downloadMode = false;
260+
if (!AndroidacyUtil.isAndroidacyLink(moduleUrl)) {
108261
this.forceQuitRaw("Non Androidacy module link used on Androidacy");
109262
return;
110263
}
@@ -115,9 +268,7 @@ public void install(String moduleUrl, String installTitle,String checksum) {
115268
this.forceQuitRaw("Androidacy didn't provided a valid checksum");
116269
return;
117270
}
118-
this.activity.backOnResume = true;
119-
IntentHelper.openInstaller(this.activity,
120-
moduleUrl, installTitle, null, checksum);
271+
this.openNativeModuleDialogRaw(moduleUrl, moduleId, checksum, this.canInstall());
121272
}
122273

123274
/**
@@ -274,4 +425,25 @@ public int getAndroidVersionCode() {
274425
public int getNavigationBarHeight() {
275426
return this.activity.getNavigationBarHeight();
276427
}
428+
429+
/**
430+
* Allow Androidacy backend to notify compat mode
431+
* return current effective compat mode
432+
*/
433+
@JavascriptInterface
434+
public int getEffectiveCompatMode() {
435+
return this.effectiveCompatMode;
436+
}
437+
438+
// Androidacy feature level declaration method
439+
440+
@JavascriptInterface
441+
public void notifyCompatUnsupported() {
442+
this.notifyCompatModeRaw(0);
443+
}
444+
445+
@JavascriptInterface
446+
public void notifyCompatDownloadButton() {
447+
this.notifyCompatModeRaw(1);
448+
}
277449
}

app/src/main/java/com/fox2code/mmm/repo/RepoManager.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,16 @@ public static RepoManager getINSTANCE() {
5454
private final MainApplication mainApplication;
5555
private final LinkedHashMap<String, RepoData> repoData;
5656
private final HashMap<String, RepoModule> modules;
57+
private final AndroidacyRepoData androidacyRepoData;
5758

5859
private RepoManager(MainApplication mainApplication) {
5960
this.mainApplication = mainApplication;
6061
this.repoData = new LinkedHashMap<>();
6162
this.modules = new HashMap<>();
6263
// We do not have repo list config yet.
6364
this.addRepoData(MAGISK_ALT_REPO);
64-
this.addAndroidacyRepoData();
65+
this.androidacyRepoData =
66+
this.addAndroidacyRepoData();
6567
// Populate default cache
6668
for (RepoData repoData:this.repoData.values()) {
6769
for (RepoModule repoModule:repoData.moduleHashMap.values()) {
@@ -251,13 +253,17 @@ private RepoData addRepoData(String url) {
251253
return repoData;
252254
}
253255

254-
private RepoData addAndroidacyRepoData() {
256+
private AndroidacyRepoData addAndroidacyRepoData() {
255257
File cacheRoot = new File(this.mainApplication.getCacheDir(), "androidacy_repo");
256258
SharedPreferences sharedPreferences = this.mainApplication
257259
.getSharedPreferences("mmm_androidacy_repo", Context.MODE_PRIVATE);
258-
RepoData repoData = new AndroidacyRepoData(
260+
AndroidacyRepoData repoData = new AndroidacyRepoData(
259261
ANDROIDACY_MAGISK_REPO_ENDPOINT, cacheRoot, sharedPreferences);
260262
this.repoData.put(ANDROIDACY_MAGISK_REPO_ENDPOINT, repoData);
261263
return repoData;
262264
}
265+
266+
public AndroidacyRepoData getAndroidacyRepoData() {
267+
return this.androidacyRepoData;
268+
}
263269
}

0 commit comments

Comments
 (0)