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

Commit efa6c14

Browse files
committed
Improve/rework Androidacy compatibility
1 parent fd4e683 commit efa6c14

File tree

12 files changed

+215
-25
lines changed

12 files changed

+215
-25
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,16 @@
111111
android:name="androidx.work.WorkManagerInitializer"
112112
tools:node="remove" />
113113
</provider>
114+
115+
<provider
116+
android:authorities="${applicationId}.file-provider"
117+
android:name="androidx.core.content.FileProvider"
118+
android:exported="false"
119+
android:grantUriPermissions="true">
120+
121+
<meta-data
122+
android:name="android.support.FILE_PROVIDER_PATHS"
123+
android:resource="@xml/shared_file_paths" />
124+
</provider>
114125
</application>
115126
</manifest>

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.fox2code.mmm;
22

3+
import android.Manifest;
4+
import android.content.pm.PackageManager;
35
import android.content.res.Configuration;
46
import android.content.res.Resources;
57
import android.graphics.Color;
@@ -16,6 +18,7 @@
1618
import androidx.annotation.NonNull;
1719
import androidx.appcompat.widget.SearchView;
1820
import androidx.cardview.widget.CardView;
21+
import androidx.core.content.ContextCompat;
1922
import androidx.core.graphics.ColorUtils;
2023
import androidx.recyclerview.widget.LinearLayoutManager;
2124
import androidx.recyclerview.widget.RecyclerView;
@@ -36,6 +39,7 @@
3639
import com.fox2code.mmm.utils.IntentHelper;
3740
import com.fox2code.mmm.utils.NoodleDebug;
3841
import com.google.android.material.progressindicator.LinearProgressIndicator;
42+
import com.topjohnwu.superuser.Shell;
3943

4044
import eightbitlab.com.blurview.BlurView;
4145
import eightbitlab.com.blurview.RenderScriptBlur;
@@ -159,6 +163,9 @@ public void onPathReceived(String path) {
159163
moduleViewListBuilder.addNotification(NotificationType.INSTALL_FROM_STORAGE);
160164
noodleDebug.setEnabled(noodleDebugState);
161165
noodleDebug.bind();
166+
noodleDebug.push("Ensure Permissions");
167+
ensurePermissions();
168+
noodleDebug.pop();
162169
ModuleManager.getINSTANCE().scan();
163170
ModuleManager.getINSTANCE().runAfterScan(
164171
moduleViewListBuilder::appendInstalledModules);
@@ -516,4 +523,15 @@ public int getOverScrollInsetTop() {
516523
public int getOverScrollInsetBottom() {
517524
return this.overScrollInsetBottom;
518525
}
526+
527+
private void ensurePermissions() {
528+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
529+
ContextCompat.checkSelfPermission(this,
530+
Manifest.permission.POST_NOTIFICATIONS) !=
531+
PackageManager.PERMISSION_GRANTED) {
532+
// TODO Use standard Android API to ask for permissions
533+
Shell.cmd("pm grant " + this.getPackageName() + " " +
534+
Manifest.permission.POST_NOTIFICATIONS);
535+
}
536+
}
519537
}

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

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import androidx.annotation.NonNull;
2323
import androidx.annotation.Nullable;
24+
import androidx.core.content.FileProvider;
2425
import androidx.webkit.WebResourceErrorCompat;
2526
import androidx.webkit.WebSettingsCompat;
2627
import androidx.webkit.WebViewClientCompat;
@@ -34,11 +35,14 @@
3435
import com.fox2code.mmm.XHooks;
3536
import com.fox2code.mmm.utils.Http;
3637
import com.fox2code.mmm.utils.IntentHelper;
38+
import com.google.android.material.progressindicator.LinearProgressIndicator;
3739

3840
import org.json.JSONException;
3941
import org.json.JSONObject;
4042

4143
import java.io.ByteArrayInputStream;
44+
import java.io.File;
45+
import java.io.FileOutputStream;
4246
import java.io.IOException;
4347
import java.util.HashMap;
4448

@@ -54,15 +58,19 @@ public final class AndroidacyActivity extends FoxActivity {
5458
}
5559
}
5660

61+
File moduleFile;
5762
WebView webView;
5863
TextView webViewNote;
5964
AndroidacyWebAPI androidacyWebAPI;
65+
LinearProgressIndicator progressIndicator;
6066
boolean backOnResume;
67+
boolean downloadMode;
6168

6269
@SuppressWarnings("deprecation")
6370
@Override
6471
@SuppressLint({"SetJavaScriptEnabled", "JavascriptInterface", "RestrictedApi"})
6572
protected void onCreate(@Nullable Bundle savedInstanceState) {
73+
this.moduleFile = new File(this.getCacheDir(), "module.zip");
6674
super.onCreate(savedInstanceState);
6775
Intent intent = this.getIntent();
6876
Uri uri;
@@ -119,6 +127,8 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
119127
}
120128
}
121129
}
130+
this.progressIndicator = this.findViewById(R.id.progress_bar);
131+
this.progressIndicator.setMax(100);
122132
this.webView = this.findViewById(R.id.webView);
123133
this.webViewNote = this.findViewById(R.id.webViewNote);
124134
WebSettings webSettings = this.webView.getSettings();
@@ -156,6 +166,7 @@ public boolean shouldOverrideUrlLoading(
156166
// Don't open non Androidacy urls inside WebView
157167
if (request.isForMainFrame() &&
158168
!AndroidacyUtil.isAndroidacyLink(request.getUrl())) {
169+
if (downloadMode || backOnResume) return true;
159170
Log.i(TAG, "Exiting WebView " + // hideToken in case isAndroidacyLink fail.
160171
AndroidacyUtil.hideToken(request.getUrl().toString()));
161172
IntentHelper.openUri(view.getContext(), request.getUrl().toString());
@@ -185,6 +196,8 @@ public void onPageStarted(WebView view, String url, Bitmap favicon) {
185196
@Override
186197
public void onPageFinished(WebView view, String url) {
187198
webViewNote.setVisibility(View.GONE);
199+
progressIndicator.setVisibility(View.INVISIBLE);
200+
progressIndicator.setProgressCompat(0, false);
188201
}
189202

190203
private void onReceivedError(String url, int errorCode) {
@@ -247,9 +260,22 @@ public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
247260
}
248261
return super.onConsoleMessage(consoleMessage);
249262
}
263+
264+
@Override
265+
public void onProgressChanged(WebView view, int newProgress) {
266+
if (downloadMode) return;
267+
if (newProgress != 100 && // Show progress bar
268+
progressIndicator.getVisibility() != View.VISIBLE)
269+
progressIndicator.setVisibility(View.VISIBLE);
270+
progressIndicator.setProgressCompat(newProgress, true);
271+
if (newProgress == 100 && // Hide progress bar
272+
progressIndicator.getVisibility() != View.INVISIBLE)
273+
progressIndicator.setVisibility(View.INVISIBLE);
274+
}
250275
});
251276
this.webView.setDownloadListener((
252277
downloadUrl, userAgent, contentDisposition, mimetype, contentLength) -> {
278+
if (this.downloadMode || this.isDownloadUrl(downloadUrl)) return;
253279
if (AndroidacyUtil.isAndroidacyLink(downloadUrl) && !this.backOnResume) {
254280
AndroidacyWebAPI androidacyWebAPI = this.androidacyWebAPI;
255281
if (androidacyWebAPI != null) {
@@ -259,7 +285,7 @@ public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
259285
return;
260286
// Workaround Androidacy bug
261287
final String moduleId = moduleIdOfUrl(downloadUrl);
262-
if (moduleId != null) {
288+
if (moduleId != null && !this.isFileUrl(downloadUrl)) {
263289
webView.evaluateJavascript("document.querySelector(" +
264290
"\"#download-form input[name=_token]\").value",
265291
result -> new Thread("Androidacy popup workaround thread") {
@@ -354,10 +380,22 @@ private String moduleIdOfUrl(String url) {
354380
if (i == -1) i = url.length();
355381
if (url.startsWith(prefix)) return url.substring(prefix.length(), i);
356382
}
383+
if (this.isFileUrl(url)) {
384+
int i = url.indexOf("&module=");
385+
if (i != -1) {
386+
int j = url.indexOf('&', i + 1);
387+
if (j == -1) {
388+
return url.substring(i + 8);
389+
} else {
390+
return url.substring(i + 8, j);
391+
}
392+
}
393+
}
357394
return null;
358395
}
359396

360397
private boolean isFileUrl(String url) {
398+
if (url == null) return false;
361399
for (String prefix : new String[]{
362400
"https://production-api.androidacy.com/magisk/file/",
363401
"https://staging-api.androidacy.com/magisk/file/"
@@ -367,18 +405,57 @@ private boolean isFileUrl(String url) {
367405
return false;
368406
}
369407

408+
private boolean isDownloadUrl(String url) {
409+
for (String prefix : new String[]{
410+
"https://production-api.androidacy.com/magisk/download/",
411+
"https://staging-api.androidacy.com/magisk/download/"
412+
}) { // Make both staging and non staging act the same
413+
if (url.startsWith(prefix)) return true;
414+
}
415+
return false;
416+
}
417+
370418
private boolean megaIntercept(String pageUrl, String fileUrl) {
371419
if (pageUrl == null || fileUrl == null) return false;
372420
if (this.isFileUrl(fileUrl)) {
373421
Log.d(TAG, "megaIntercept(" +
374422
AndroidacyUtil.hideToken(pageUrl) + ", " +
375423
AndroidacyUtil.hideToken(fileUrl) + ")");
376-
}
424+
} else return false;
377425
final AndroidacyWebAPI androidacyWebAPI = this.androidacyWebAPI;
378-
final String moduleId = this.moduleIdOfUrl(pageUrl);
379-
if (moduleId == null || !this.isFileUrl(fileUrl)) return false;
426+
String moduleId = this.moduleIdOfUrl(fileUrl);
427+
if (moduleId == null) moduleId = this.moduleIdOfUrl(pageUrl);
428+
if (moduleId == null) {
429+
Log.d(TAG, "No module id?");
430+
return false;
431+
}
380432
androidacyWebAPI.openNativeModuleDialogRaw(fileUrl,
381433
moduleId, "", androidacyWebAPI.canInstall());
382434
return true;
383435
}
436+
437+
Uri downloadFileAsync(String url) throws IOException {
438+
this.downloadMode = true;
439+
this.runOnUiThread(() -> {
440+
progressIndicator.setIndeterminate(false);
441+
progressIndicator.setVisibility(View.VISIBLE);
442+
});
443+
byte[] module;
444+
try {
445+
module = Http.doHttpGet(url, (downloaded, total, done) ->
446+
progressIndicator.setProgressCompat((downloaded * 100) / total, true));
447+
try (FileOutputStream fileOutputStream = new FileOutputStream(this.moduleFile)) {
448+
fileOutputStream.write(module);
449+
}
450+
} finally {
451+
module = null;
452+
this.runOnUiThread(() ->
453+
progressIndicator.setVisibility(View.INVISIBLE));
454+
}
455+
this.backOnResume = true;
456+
this.downloadMode = false;
457+
return FileProvider.getUriForFile(this,
458+
this.getPackageName() + ".file-provider",
459+
this.moduleFile);
460+
}
384461
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ static boolean isAndroidacyLink(@NonNull String url,@NonNull Uri uri) {
2424
uri.getHost().endsWith(".androidacy.com");
2525
}
2626

27+
public static boolean isAndroidacyFileUrl(@Nullable String url) {
28+
if (url == null) return false;
29+
for (String prefix : new String[]{
30+
"https://production-api.androidacy.com/magisk/file/",
31+
"https://staging-api.androidacy.com/magisk/file/"
32+
}) { // Make both staging and non staging act the same
33+
if (url.startsWith(prefix)) return true;
34+
}
35+
return false;
36+
}
37+
2738
// Avoid logging token
2839
public static String hideToken(@NonNull String url) {
2940
int i = url.lastIndexOf("token=");

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ void forceQuitRaw(String error) {
6464

6565
void openNativeModuleDialogRaw(String moduleUrl, String installTitle,
6666
String checksum, boolean canInstall) {
67+
Log.d(TAG, "ModuleDialog, downloadUrl: " + AndroidacyUtil.hideToken(moduleUrl));
6768
this.downloadMode = false;
6869
RepoModule repoModule = AndroidacyRepoData
6970
.getInstance().moduleHashMap.get(installTitle);
@@ -92,7 +93,7 @@ void openNativeModuleDialogRaw(String moduleUrl, String installTitle,
9293
.setIcon(R.drawable.ic_baseline_extension_24);
9394
builder.setNegativeButton(R.string.download_module, (x, y) -> {
9495
this.downloadMode = true;
95-
this.activity.webView.loadUrl(moduleUrl);
96+
IntentHelper.openCustomTab(this.activity, moduleUrl);
9697
});
9798
if (canInstall) {
9899
boolean hasUpdate = false;
@@ -115,8 +116,18 @@ void openNativeModuleDialogRaw(String moduleUrl, String installTitle,
115116
if (!this.activity.backOnResume)
116117
this.consumedAction = false;
117118
});
118-
ExternalHelper.INSTANCE.injectButton(builder,
119-
Uri.parse(moduleUrl), "androidacy_repo");
119+
ExternalHelper.INSTANCE.injectButton(builder, () -> {
120+
this.downloadMode = true;
121+
try {
122+
return this.activity.downloadFileAsync(moduleUrl);
123+
} catch (IOException e) {
124+
Log.e(TAG, "Failed to download module", e);
125+
AndroidacyWebAPI.this.activity.runOnUiThread(() ->
126+
Toast.makeText(AndroidacyWebAPI.this.activity,
127+
R.string.failed_download, Toast.LENGTH_SHORT).show());
128+
return null;
129+
}
130+
}, "androidacy_repo");
120131
final int dim5dp = FoxDisplay.dpToPixel(5);
121132
builder.setBackgroundInsetStart(dim5dp).setBackgroundInsetEnd(dim5dp);
122133
this.activity.runOnUiThread(() -> {

app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.fox2code.mmm.MainApplication;
2424
import com.fox2code.mmm.R;
2525
import com.fox2code.mmm.XHooks;
26+
import com.fox2code.mmm.androidacy.AndroidacyUtil;
2627
import com.fox2code.mmm.module.ActionButtonType;
2728
import com.fox2code.mmm.sentry.SentryBreadcrumb;
2829
import com.fox2code.mmm.sentry.SentryMain;
@@ -156,6 +157,7 @@ protected void onCreate(Bundle savedInstanceState) {
156157
Log.e(TAG, "Failed to delete module cache");
157158
String errMessage = "Failed to download module zip";
158159
byte[] rawModule;
160+
boolean androidacyBlame = false; // In case Androidacy mess-up again...
159161
try {
160162
Log.i(TAG, (urlMode ? "Downloading: " : "Loading: ") + target);
161163
rawModule = urlMode ? Http.doHttpGet(target, (progress, max, done) -> {
@@ -172,6 +174,7 @@ protected void onCreate(Bundle savedInstanceState) {
172174
this.progressIndicator.setIndeterminate(true);
173175
});
174176
if (this.canceled) return;
177+
androidacyBlame = urlMode && AndroidacyUtil.isAndroidacyFileUrl(target);
175178
if (checksum != null && !checksum.isEmpty()) {
176179
Log.d(TAG, "Checking for checksum: " + checksum);
177180
this.runOnUiThread(() -> this.installerTerminal.addLine("- Checking file integrity"));
@@ -208,10 +211,15 @@ protected void onCreate(Bundle savedInstanceState) {
208211
}
209212
}
210213
if (!isModule && !isAnyKernel3) {
214+
if (androidacyBlame) {
215+
this.installerTerminal.addLine(
216+
"! Note: The following error is probably an Androidacy backend error");
217+
}
211218
this.setInstallStateFinished(false,
212219
"! File is not a valid Magisk module or AnyKernel3 zip", "");
213220
return;
214221
}
222+
androidacyBlame = false;
215223
if (noPatch) {
216224
if (urlMode) {
217225
errMessage = "Failed to save module zip";
@@ -237,6 +245,10 @@ protected void onCreate(Bundle savedInstanceState) {
237245
this.doInstall(moduleCache, noExtensions, rootless);
238246
} catch (IOException e) {
239247
Log.e(TAG, errMessage, e);
248+
if (androidacyBlame) {
249+
this.installerTerminal.addLine(
250+
"! Note: The following error is probably an Androidacy backend error");
251+
}
240252
this.setInstallStateFinished(false,
241253
"! " + errMessage, "");
242254
} catch (OutOfMemoryError e) {

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,9 @@ public void doAction(Chip button, ModuleHolder moduleHolder) {
113113
builder.setMessage(desc);
114114
}
115115
Log.d("Test", "URL: " + updateZipUrl);
116-
builder.setNegativeButton(R.string.download_module, (x, y) ->
117-
IntentHelper.openCustomTab(button.getContext(), updateZipUrl));
116+
builder.setNegativeButton(R.string.download_module, (x, y) -> {
117+
IntentHelper.openCustomTab(button.getContext(), updateZipUrl);
118+
});
118119
if (hasRoot) {
119120
builder.setPositiveButton(moduleHolder.hasUpdate() ?
120121
R.string.update_module : R.string.install_module, (x, y) -> {
@@ -125,7 +126,7 @@ public void doAction(Chip button, ModuleHolder moduleHolder) {
125126
});
126127
}
127128
ExternalHelper.INSTANCE.injectButton(builder,
128-
Uri.parse(updateZipUrl), moduleHolder.getUpdateZipRepo());
129+
() -> Uri.parse(updateZipUrl), moduleHolder.getUpdateZipRepo());
129130
int dim5dp = FoxDisplay.dpToPixel(5);
130131
builder.setBackgroundInsetStart(dim5dp).setBackgroundInsetEnd(dim5dp);
131132
AlertDialog alertDialog = builder.show();

0 commit comments

Comments
 (0)