diff --git a/app/build.gradle b/app/build.gradle
index e14fa9a..0b9fec8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -13,8 +13,7 @@ android {
minSdk 21
targetSdk 33
versionCode 57
- versionName "0.6.6"
-
+ versionName "0.6.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9df61ff..2c17148 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -113,10 +113,10 @@
android:name="androidx.work.WorkManagerInitializer"
tools:node="remove" />
-
+
+ android:value="https://cdcdb0efca4a42a28df90e4b7f087347@sentry.androidacy.com/2" />
{
- options.addIntegration(new FragmentLifecycleIntegration(this, true, false));
- // Note: Sentry library only take a screenshot of Fox Magisk Module Manager.
- // The screen shot doesn't and cannot contain other applications (if in multi windows)
- // status bar and notifications (even if notification shade is pulled down)
-
- // In the possibility you find this library sending anything listed above,
- // it's a serious bug and a security issue you should report to Google
- // Google bug bounties on Android are huge, so you can also get rich by doing that.
- options.setAttachScreenshot(true);
- // User interaction tracing is not needed to get context of crash
- options.setEnableUserInteractionTracing(false);
- // Send client reports has nothing to do with error reporting
- options.setSendClientReports(false);
- // Auto session tracking has nothing to do with error reporting
- options.setEnableAutoSessionTracking(false);
- // Add a callback that will be used before the event is sent to Sentry.
- // With this callback, you can modify the event or, when returning null, also discard the event.
- options.setBeforeSend((event, hint) -> {
- if (BuildConfig.DEBUG) { // Debug sentry events for debug.
- StringBuilder stringBuilder = new StringBuilder("Sentry report debug: ");
- try {
- event.serialize(new JsonObjectWriter(new Writer() {
- @Override
- public void write(char[] cbuf) {
- stringBuilder.append(cbuf);
- }
-
- @Override
- public void write(String str) {
- stringBuilder.append(str);
- }
-
- @Override
- public void write(char[] chars, int i, int i1) {
- stringBuilder.append(chars, i, i1);
- }
-
- @Override
- public void write(String str, int off, int len) {
- stringBuilder.append(str, off, len);
- }
-
- @Override
- public void flush() {}
-
- @Override
- public void close() {}
- }, 4), NoOpLogger.getInstance());
- } catch (IOException ignored) {}
- Log.i(TAG, stringBuilder.toString());
- }
- // Check saved preferences to see if the user has opted out of crash reporting.
- // If the user has opted out, return null.
- if (isCrashReportingEnabled()) {
- Log.i(TAG, "Relayed sentry report according to user preference");
+ SentryAndroid.init(this, options -> {
+ // If crash reporting is disabled, stop here.
+ if (!sharedPreferences.getBoolean("pref_crash_reporting", true)) {
+ options.setDsn("");
+ } else {
+ options.addIntegration(new FragmentLifecycleIntegration(this, true, true));
+ // Sentry sends ABSOLUTELY NO Personally Identifiable Information (PII) by default.
+ // A screenshot of the app itself is only sent if the app crashes, and it only shows the last activity
+ // In addition, sentry is configured with a trusted third party other than sentry.io, and only trusted people have access to the sentry instance
+ // Add a callback that will be used before the event is sent to Sentry.
+ // With this callback, you can modify the event or, when returning null, also discard the event.
+ options.setBeforeSend((event, hint) -> {
+ if (BuildConfig.DEBUG) { // Debug sentry events for debug.
+ StringBuilder stringBuilder = new StringBuilder("Sentry report debug: ");
+ try {
+ event.serialize(new JsonObjectWriter(new Writer() {
+ @Override
+ public void write(char[] cbuf) {
+ stringBuilder.append(cbuf);
+ }
+
+ @Override
+ public void write(String str) {
+ stringBuilder.append(str);
+ }
+
+ @Override
+ public void write(char[] chars, int i, int i1) {
+ stringBuilder.append(chars, i, i1);
+ }
+
+ @Override
+ public void write(String str, int off, int len) {
+ stringBuilder.append(str, off, len);
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void close() {
+ }
+ }, 4), NoOpLogger.getInstance());
+ } catch (IOException ignored) {
+ }
+ Log.i(TAG, stringBuilder.toString());
+ }
+ // We already know that the user has opted in to crash reporting, so we don't need to ask again.
return event;
- } else {
- Log.i(TAG, "Blocked sentry report according to user preference");
- // We need to do this to avoid crash delay on crash when the event is dropped
- DiskFlushNotification diskFlushNotification = hint.getAs(
- TypeCheckHint.SENTRY_TYPE_CHECK_HINT, DiskFlushNotification.class);
- if (diskFlushNotification != null) diskFlushNotification.markFlushed();
- return null;
- }
- });
- });*/
+ });
+ }
+
+ });
}
@Override
diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java
index fd8a54a..3928991 100644
--- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java
+++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java
@@ -32,7 +32,6 @@ import com.fox2code.mmm.Constants;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.R;
import com.fox2code.mmm.XHooks;
-import com.fox2code.mmm.repo.RepoManager;
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.IntentHelper;
@@ -60,6 +59,7 @@ public final class AndroidacyActivity extends FoxActivity {
AndroidacyWebAPI androidacyWebAPI;
boolean backOnResume;
+ @SuppressWarnings("deprecation")
@Override
@SuppressLint({"SetJavaScriptEnabled", "JavascriptInterface"})
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -115,7 +115,8 @@ public final class AndroidacyActivity extends FoxActivity {
IntentHelper.openConfig(this, config);
return true;
});
- } catch (PackageManager.NameNotFoundException ignored) {}
+ } catch (PackageManager.NameNotFoundException ignored) {
+ }
}
}
this.webView = this.findViewById(R.id.webView);
@@ -125,12 +126,21 @@ public final class AndroidacyActivity extends FoxActivity {
webSettings.setDomStorageEnabled(true);
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccess(false);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Make website follow app theme
- webSettings.setForceDark(MainApplication.getINSTANCE().isLightTheme() ?
- WebSettings.FORCE_DARK_OFF : WebSettings.FORCE_DARK_ON);
- } else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
- WebSettingsCompat.setForceDark(webSettings, MainApplication.getINSTANCE().isLightTheme() ?
- WebSettingsCompat.FORCE_DARK_OFF : WebSettingsCompat.FORCE_DARK_ON);
+ // If API level is .= 33, allow setAlgorithmicDarkeningAllowed
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.TIRAMISU) {
+ try {
+ webSettings.setAlgorithmicDarkeningAllowed(true);
+ } catch (NoSuchMethodError ignored) {
+ }
+ } else {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Make website follow app theme
+ webSettings.setForceDark(MainApplication.getINSTANCE().isLightTheme() ?
+ WebSettings.FORCE_DARK_OFF : WebSettings.FORCE_DARK_ON);
+ } else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ // If api level is < 32, use force dark
+ WebSettingsCompat.setForceDark(webSettings, MainApplication.getINSTANCE().isLightTheme() ?
+ WebSettingsCompat.FORCE_DARK_OFF : WebSettingsCompat.FORCE_DARK_ON);
+ }
}
this.webView.setWebViewClient(new WebViewClientCompat() {
private String pageUrl;
@@ -173,7 +183,7 @@ public final class AndroidacyActivity extends FoxActivity {
}
private void onReceivedError(String url, int errorCode) {
- if ((url.startsWith("https://api.androidacy.com/magisk/") ||
+ if ((url.startsWith("https://production-api.androidacy.com/magisk/") ||
url.startsWith("https://staging-api.androidacy.com/magisk/") ||
url.equals(pageUrl)) && (errorCode == 419 || errorCode == 429 || errorCode == 503)) {
Toast.makeText(AndroidacyActivity.this,
@@ -247,33 +257,33 @@ public final class AndroidacyActivity extends FoxActivity {
if (moduleId != null) {
webView.evaluateJavascript("document.querySelector(" +
"\"#download-form input[name=_token]\").value",
- result -> new Thread("Androidacy popup workaround thread") {
- @Override
- public void run() {
- if (androidacyWebAPI.consumedAction) return;
- try {
- JSONObject jsonObject = new JSONObject();
- jsonObject.put("moduleId", moduleId);
- jsonObject.put("token", AndroidacyRepoData
- .getInstance().getToken());
- jsonObject.put("_token", result);
- String realUrl = Http.doHttpPostRedirect(downloadUrl,
- jsonObject.toString(), true);
- if (downloadUrl.equals(realUrl)) {
- Log.e(TAG, "Failed to resolve URL from " +
- downloadUrl);
- AndroidacyActivity.this.megaIntercept(
- webView.getUrl(), downloadUrl);
- return;
+ result -> new Thread("Androidacy popup workaround thread") {
+ @Override
+ public void run() {
+ if (androidacyWebAPI.consumedAction) return;
+ try {
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("moduleId", moduleId);
+ jsonObject.put("token", AndroidacyRepoData
+ .getInstance().getToken());
+ jsonObject.put("_token", result);
+ String realUrl = Http.doHttpPostRedirect(downloadUrl,
+ jsonObject.toString(), true);
+ if (downloadUrl.equals(realUrl)) {
+ Log.e(TAG, "Failed to resolve URL from " +
+ downloadUrl);
+ AndroidacyActivity.this.megaIntercept(
+ webView.getUrl(), downloadUrl);
+ return;
+ }
+ Log.i(TAG, "Got url: " + realUrl);
+ androidacyWebAPI.openNativeModuleDialogRaw(realUrl,
+ moduleId, "", androidacyWebAPI.canInstall());
+ } catch (IOException | JSONException e) {
+ Log.e(TAG, "Failed redirect intercept", e);
}
- Log.i(TAG, "Got url: " + realUrl);
- androidacyWebAPI.openNativeModuleDialogRaw(realUrl,
- moduleId, "", androidacyWebAPI.canInstall());
- } catch (IOException | JSONException e) {
- Log.e(TAG, "Failed redirect intercept", e);
}
- }
- }.start());
+ }.start());
return;
} else if (this.megaIntercept(webView.getUrl(), downloadUrl))
return;
@@ -285,7 +295,7 @@ public final class AndroidacyActivity extends FoxActivity {
Log.i(TAG, "Exiting WebView " +
AndroidacyUtil.hideToken(downloadUrl));
for (String prefix : new String[]{
- "https://api.androidacy.com/magisk/download/",
+ "https://production-api.androidacy.com/magisk/download/",
"https://staging-api.androidacy.com/magisk/download/"
}) {
if (downloadUrl.startsWith(prefix)) {
@@ -328,14 +338,15 @@ public final class AndroidacyActivity extends FoxActivity {
private String moduleIdOfUrl(String url) {
for (String prefix : new String[]{
- "https://api.androidacy.com/magisk/download/",
+ "https://production-api.androidacy.com/magisk/download/",
"https://staging-api.androidacy.com/magisk/download/",
- "https://api.androidacy.com/magisk/readme/",
+ "https://production-api.androidacy.com/magisk/readme/",
"https://staging-api.androidacy.com/magisk/readme/",
- "https://api.androidacy.com/magisk/info/",
+ "https://prodiuction-api.androidacy.com/magisk/info/",
"https://staging-api.androidacy.com/magisk/info/"
}) { // Make both staging and non staging act the same
- int i = url.indexOf('?', prefix.length()); if (i == -1) i = url.length();
+ int i = url.indexOf('?', prefix.length());
+ if (i == -1) i = url.length();
if (url.startsWith(prefix)) return url.substring(prefix.length(), i);
}
return null;
@@ -343,7 +354,7 @@ public final class AndroidacyActivity extends FoxActivity {
private boolean isFileUrl(String url) {
for (String prefix : new String[]{
- "https://api.androidacy.com/magisk/file/",
+ "https://production-api.androidacy.com/magisk/file/",
"https://staging-api.androidacy.com/magisk/file/"
}) { // Make both staging and non staging act the same
if (url.startsWith(prefix)) return true;
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 302efaa..2d320c3 100644
--- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java
+++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java
@@ -1,5 +1,6 @@
package com.fox2code.mmm.androidacy;
+import android.annotation.SuppressLint;
import android.content.res.Resources;
import android.graphics.Color;
import android.net.Uri;
@@ -107,10 +108,8 @@ public class AndroidacyWebAPI {
fConfig = config, fChecksum = checksum;
final boolean fMMTReborn = mmtReborn;
builder.setPositiveButton(hasUpdate ?
- R.string.update_module : R.string.install_module, (x, y) -> {
- IntentHelper.openInstaller(this.activity,
- fModuleUrl, fTitle, fConfig, fChecksum, fMMTReborn);
- });
+ R.string.update_module : R.string.install_module, (x, y) -> IntentHelper.openInstaller(this.activity,
+ fModuleUrl, fTitle, fConfig, fChecksum, fMMTReborn));
}
builder.setOnCancelListener(dialogInterface -> {
if (!this.activity.backOnResume)
@@ -500,7 +499,7 @@ public class AndroidacyWebAPI {
*/
@JavascriptInterface
public String getMonetColor(String id) {
- int nameResourceID = this.activity.getResources().getIdentifier("@android:color/" + id,
+ @SuppressLint("DiscouragedApi") int nameResourceID = this.activity.getResources().getIdentifier("@android:color/" + id,
"color", this.activity.getApplicationInfo().packageName);
if (nameResourceID == 0) {
throw new IllegalArgumentException(
diff --git a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java
index 658d594..c35a30c 100644
--- a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java
+++ b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java
@@ -50,6 +50,10 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
+import io.sentry.Breadcrumb;
+import io.sentry.Sentry;
+import io.sentry.SentryLevel;
+
public class InstallerActivity extends FoxActivity {
private static final String TAG = "InstallerActivity";
public LinearProgressIndicator progressIndicator;
@@ -104,7 +108,7 @@ public class InstallerActivity extends FoxActivity {
}
Log.i(TAG, "Install link: " + target);
// Note: Sentry only send this info on crash.
- /*if (MainApplication.isCrashReportingEnabled()) {
+ if (MainApplication.isCrashReportingEnabled()) {
Breadcrumb breadcrumb = new Breadcrumb();
breadcrumb.setType("install");
breadcrumb.setData("target", target);
@@ -113,7 +117,7 @@ public class InstallerActivity extends FoxActivity {
breadcrumb.setCategory("app.action.preinstall");
breadcrumb.setLevel(SentryLevel.INFO);
Sentry.addBreadcrumb(breadcrumb);
- }*/
+ }
boolean urlMode = target.startsWith("http://") || target.startsWith("https://");
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setTitle(name);
@@ -173,9 +177,7 @@ public class InstallerActivity extends FoxActivity {
if (this.canceled) return;
if (checksum != null && !checksum.isEmpty()) {
Log.d(TAG, "Checking for checksum: " + checksum);
- this.runOnUiThread(() -> {
- this.installerTerminal.addLine("- Checking file integrity");
- });
+ this.runOnUiThread(() -> this.installerTerminal.addLine("- Checking file integrity"));
if (!Hashes.checkSumMatch(rawModule, checksum)) {
this.setInstallStateFinished(false,
"! File integrity check failed", "");
@@ -223,9 +225,7 @@ public class InstallerActivity extends FoxActivity {
}
} else {
errMessage = "Failed to patch module zip";
- this.runOnUiThread(() -> {
- this.installerTerminal.addLine("- Patching " + name);
- });
+ this.runOnUiThread(() -> this.installerTerminal.addLine("- Patching " + name));
Log.i(TAG, "Patching: " + moduleCache.getName());
try (OutputStream outputStream = new FileOutputStream(moduleCache)) {
Files.patchModuleSimple(rawModule, outputStream);
@@ -235,9 +235,7 @@ public class InstallerActivity extends FoxActivity {
//noinspection UnusedAssignment (Important to avoid OutOfMemoryError)
rawModule = null; // Because reference is kept when calling doInstall
if (this.canceled) return;
- this.runOnUiThread(() -> {
- this.installerTerminal.addLine("- Installing " + name);
- });
+ this.runOnUiThread(() -> this.installerTerminal.addLine("- Installing " + name));
errMessage = "Failed to install module zip";
this.doInstall(moduleCache, noExtensions, rootless);
} catch (IOException e) {
@@ -451,7 +449,7 @@ public class InstallerActivity extends FoxActivity {
installCommand).to(installerController, installerMonitor);
}
// Note: Sentry only send this info on crash.
- /*if (MainApplication.isCrashReportingEnabled()) {
+ if (MainApplication.isCrashReportingEnabled()) {
Breadcrumb breadcrumb = new Breadcrumb();
breadcrumb.setType("install");
breadcrumb.setData("moduleId", moduleId == null ? "" : moduleId);
@@ -464,7 +462,7 @@ public class InstallerActivity extends FoxActivity {
breadcrumb.setCategory("app.action.install");
breadcrumb.setLevel(SentryLevel.INFO);
Sentry.addBreadcrumb(breadcrumb);
- }*/
+ }
if (mmtReborn && magiskCmdLine) {
Log.w(TAG, "mmtReborn and magiskCmdLine may not work well together");
}
@@ -738,12 +736,8 @@ public class InstallerActivity extends FoxActivity {
.setTitle(R.string.install_terminal_reboot_now)
.setCancelable(false)
.setIcon(R.drawable.ic_reboot_24)
- .setPositiveButton(R.string.yes, (x, y) -> {
- Shell.cmd(reboot_cmd).submit();
- })
- .setNegativeButton(R.string.no, (x, y) -> {
- x.dismiss();
- }).show();
+ .setPositiveButton(R.string.yes, (x, y) -> Shell.cmd(reboot_cmd).submit())
+ .setNegativeButton(R.string.no, (x, y) -> x.dismiss()).show();
} else {
Shell.cmd(reboot_cmd).submit();
}
diff --git a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java
index 96f77bc..ffe42a5 100644
--- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java
+++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java
@@ -21,7 +21,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.EditTextPreference;
import androidx.preference.ListPreference;
@@ -106,6 +105,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
}
public static class SettingsFragment extends PreferenceFragmentCompat implements FoxActivity.OnBackPressedCallback {
+ @SuppressLint("UnspecifiedImmutableFlag")
@Override
@SuppressWarnings("ConstantConditions")
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -133,13 +133,47 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
return true;
});
// Crash reporting
- /*TwoStatePreference crashReportingPreference = findPreference("pref_crash_reporting");
+ TwoStatePreference crashReportingPreference = findPreference("pref_crash_reporting");
crashReportingPreference.setChecked(MainApplication.isCrashReportingEnabled());
crashReportingPreference.setOnPreferenceChangeListener((preference, newValue) -> {
devModeStepFirstBootIgnore = true;
devModeStep = 0;
+ // Save the new value and restart the app
+ MainApplication.getSharedPreferences().edit()
+ .putBoolean("crash_reporting", (boolean) newValue).apply();
+ // Show a dialog to restart the app
+ MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(requireContext());
+ materialAlertDialogBuilder.setTitle(R.string.crash_reporting_restart_title);
+ materialAlertDialogBuilder.setMessage(R.string.crash_reporting_restart_message);
+ materialAlertDialogBuilder.setPositiveButton(R.string.restart, (dialog, which) -> {
+ Intent mStartActivity = new Intent(requireContext(), MainActivity.class);
+ mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+ int mPendingIntentId = 123456;
+ // If < 23, FLAG_IMMUTABLE is not available
+ PendingIntent mPendingIntent;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId,
+ mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ } else {
+ mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId,
+ mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+ AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE);
+ mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
+ if (BuildConfig.DEBUG) {
+ Log.d(TAG, "Restarting app to save crash reporting preference: " + newValue);
+ }
+ System.exit(0); // Exit app process
+ });
+ // Reverse the change if the user cancels the dialog
+ materialAlertDialogBuilder.setNegativeButton(R.string.cancel, (dialog, which) -> {
+ crashReportingPreference.setChecked(!crashReportingPreference.isChecked());
+ MainApplication.getSharedPreferences().edit()
+ .putBoolean("crash_reporting", crashReportingPreference.isChecked()).apply();
+ });
+ materialAlertDialogBuilder.show();
return true;
- });*/
+ });
Preference enableBlur = findPreference("pref_enable_blur");
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
enableBlur.setSummary(R.string.require_android_6);
@@ -289,7 +323,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
return true;
});
findPreference("pref_pkg_info").setSummary(BuildConfig.APPLICATION_ID +
- " v" + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")" +
+ " v" + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")" +
getRepackageState()); // State may not be "I am just running from myself as myself"
}
@@ -298,7 +332,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
Application initialApplication = null;
try {
initialApplication = FoxProcessExt.getInitialApplication();
- } catch (Throwable ignored) {}
+ } catch (Throwable ignored) {
+ }
String realPackageName;
if (initialApplication != null) {
realPackageName = initialApplication.getPackageName();
@@ -350,7 +385,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
onCreatePreferencesAndroidacy();
}
- @SuppressLint("RestrictedApi")
+ @SuppressLint({"RestrictedApi", "UnspecifiedImmutableFlag"})
public void onCreatePreferencesAndroidacy() {
Preference androidacyTestMode = Objects.requireNonNull(findPreference("pref_androidacy_test_mode"));
if (!MainApplication.isDeveloper()) {
@@ -359,12 +394,70 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// Show a warning if user tries to enable test mode
androidacyTestMode.setOnPreferenceChangeListener((preference, newValue) -> {
if (Boolean.parseBoolean(String.valueOf(newValue))) {
- new AlertDialog.Builder(this.requireContext())
+ // Use MaterialAlertDialogBuilder
+ new MaterialAlertDialogBuilder(this.requireContext())
.setTitle(R.string.warning)
.setMessage(R.string.androidacy_test_mode_warning)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
- // Do nothing
- }).show();
+ // User clicked OK button
+ MainApplication.getSharedPreferences().edit().putBoolean("androidacy_test_mode", true).apply();
+ // Check the switch
+ Intent mStartActivity = new Intent(requireContext(), MainActivity.class);
+ mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+ int mPendingIntentId = 123456;
+ // If < 23, FLAG_IMMUTABLE is not available
+ PendingIntent mPendingIntent;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId,
+ mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ } else {
+ mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId,
+ mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+ AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE);
+ mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
+ if (BuildConfig.DEBUG) {
+ Log.d(TAG, "Restarting app to save staging endpoint preference: " + newValue);
+ }
+ System.exit(0); // Exit app process
+ })
+ .setNegativeButton(android.R.string.cancel, (dialog, which) -> {
+ // User cancelled the dialog
+ // Uncheck the switch
+ SwitchPreferenceCompat switchPreferenceCompat = (SwitchPreferenceCompat) androidacyTestMode;
+ switchPreferenceCompat.setChecked(false);
+ // There's probably a better way to do this than duplicate code but I'm too lazy to figure it out
+ MainApplication.getSharedPreferences().edit().putBoolean("androidacy_test_mode", false).apply();
+ })
+ .show();
+ } else {
+ MainApplication.getSharedPreferences().edit().putBoolean("androidacy_test_mode", false).apply();
+ // Show dialog to restart app with ok button
+ new MaterialAlertDialogBuilder(this.requireContext())
+ .setTitle(R.string.warning)
+ .setMessage(R.string.androidacy_test_mode_disable_warning)
+ .setNeutralButton(android.R.string.ok, (dialog, which) -> {
+ // User clicked OK button
+ Intent mStartActivity = new Intent(requireContext(), MainActivity.class);
+ mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+ int mPendingIntentId = 123456;
+ // If < 23, FLAG_IMMUTABLE is not available
+ PendingIntent mPendingIntent;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId,
+ mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ } else {
+ mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId,
+ mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+ AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE);
+ mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
+ if (BuildConfig.DEBUG) {
+ Log.d(TAG, "Restarting app to save staging endpoint preference: " + newValue);
+ }
+ System.exit(0); // Exit app process
+ })
+ .show();
}
return true;
});
diff --git a/app/src/main/res/raw/androidacy_root_ca b/app/src/main/res/raw/androidacy_root_ca
new file mode 100644
index 0000000..da96dbb
Binary files /dev/null and b/app/src/main/res/raw/androidacy_root_ca differ
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 229a7f4..f93ff6f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -136,7 +136,7 @@
Restore modules
This operation require an internet connection
Androidacy test mode
- Use staging Androidacy endpoint instead of release endpoint. (Require app process restart)
+ Use staging Androidacy endpoint instead of release endpoint. (Will restart app)
Found %i module updates
@@ -168,8 +168,12 @@
Successfully reset API key
Validate
Warning!
- You are setting the app to use a non-production endpoint for Androidacy. This may result in app instability and failure to load the online repo. Do NOT report bugs if you have this switch on. Change will take effect on app restart.
+ You are setting the app to use a non-production endpoint for Androidacy. This may result in app instability and failure to load the online repo. Do NOT report bugs if you have this switch on. App will be restarted to reload repos.
Crash the app for testing
Repackaged as:
Wrapped from:
+ Restart app to apply changes?
+ The app needs to restart to apply this setting
+ Restart
+ App will be restarted to disable staging endpoint
diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml
index 4271b6d..f1391e8 100644
--- a/app/src/main/res/xml/network_security_config.xml
+++ b/app/src/main/res/xml/network_security_config.xml
@@ -1,7 +1,10 @@
-
+
androidacy.com
+
+
+
6x/7mHVkS/6XLcenTc5gxonnGPTB1MD5mPQFqHTbfa4=
Y9mvm0exBk1JoQ57f9Vm28jKo5lFm/woKcVxrYxu80o=
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
index 8e05b29..3678524 100644
--- a/app/src/main/res/xml/root_preferences.xml
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -116,13 +116,13 @@
app:summary="@string/prevent_reboot_desc"
app:title="@string/prevent_reboot_pref" />
-
+ app:title="@string/crash_reporting" />