diff --git a/app/build.gradle b/app/build.gradle index 2dca1f1..6d5b5bf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,7 +25,7 @@ android { defaultConfig { applicationId "com.fox2code.mmm" - minSdk 21 + minSdk 23 targetSdk 33 versionCode 61 versionName "1.0.1" diff --git a/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java b/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java index d29f460..b02c5af 100644 --- a/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java +++ b/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java @@ -111,9 +111,9 @@ public class AppUpdateManager { this.latestPreRelease = ""; this.preReleaseNewer = false; } - Log.d(TAG, "Latest release: " + latestRelease); - Log.d(TAG, "Latest pre-release: " + latestPreRelease); - Log.d(TAG, "Latest pre-release newer: " + preReleaseNewer); + if (BuildConfig.DEBUG) Log.d(TAG, "Latest release: " + latestRelease); + if (BuildConfig.DEBUG) Log.d(TAG, "Latest pre-release: " + latestPreRelease); + if (BuildConfig.DEBUG) Log.d(TAG, "Latest pre-release newer: " + preReleaseNewer); this.lastChecked = System.currentTimeMillis(); this.lastCheckSuccess = true; } catch (Exception ioe) { diff --git a/app/src/main/java/com/fox2code/mmm/MainActivity.java b/app/src/main/java/com/fox2code/mmm/MainActivity.java index 65fccd1..069073b 100644 --- a/app/src/main/java/com/fox2code/mmm/MainActivity.java +++ b/app/src/main/java/com/fox2code/mmm/MainActivity.java @@ -26,10 +26,10 @@ import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SearchView; import androidx.cardview.widget.CardView; +import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; @@ -87,6 +87,8 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe private CardView searchCard; private SearchView searchView; private boolean initMode; + private boolean doSetupNowRunning; + private boolean urlFactoryInstalled = false; public MainActivity() { this.moduleViewListBuilder = new ModuleViewListBuilder(this); @@ -104,6 +106,16 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe this.initMode = true; // Ensure HTTP Cache directories are created Http.ensureCacheDirs(this); + if (!urlFactoryInstalled) { + try { + ExperimentalCronetEngine cronetEngine = new ExperimentalCronetEngine.Builder(this).build(); + CronetURLStreamHandlerFactory cronetURLStreamHandlerFactory = new CronetURLStreamHandlerFactory(cronetEngine); + URL.setURLStreamHandlerFactory(cronetURLStreamHandlerFactory); + urlFactoryInstalled = true; + } catch (Throwable t) { + Log.e(TAG, "Failed to install CronetURLStreamHandlerFactory", t); + } + } BackgroundUpdateChecker.onMainActivityCreate(this); super.onCreate(savedInstanceState); this.setActionBarExtraMenuButton(R.drawable.ic_baseline_settings_24, v -> { @@ -185,6 +197,21 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe } public void commonNext() { + doSetupNowRunning = true; + doSetupNow(); + + // Wait for doSetupNow to finish + while (doSetupNowRunning) { + try { + //noinspection BusyWait + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + } + /*if (BuildConfig.DEBUG) { + SharedPreferences prefs = MainApplication.getSharedPreferences(); + if (BuildConfig.DEBUG) Log.d("MainActivity", String.format("Background update check: %s, Crash reporting: %s, Androidacy repo: %s, Magisk alt repo: %s", prefs.getBoolean("pref_background_update_check", false), prefs.getBoolean("pref_crash_reporting", false), prefs.getBoolean("pref_androidacy_repo_enabled", false), prefs.getBoolean("pref_magisk_alt_repo_enabled", false))); + }*/ swipeRefreshBlocker = System.currentTimeMillis() + 5_000L; updateScreenInsets(); // Fix an edge case if (MainApplication.isShowcaseMode()) @@ -199,21 +226,12 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe updateScreenInsets(getResources().getConfiguration()); }); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - showSetupBox(); - - // Wait for pref_first_launch to be false - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this); - while (prefs.getBoolean("pref_first_launch", true)) { - try { - //noinspection BusyWait - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + // On every preferences change, log the change if debug is enabled + if (BuildConfig.DEBUG) { + Log.i("PrefsListener", "onCreate: Preferences: " + MainApplication.getSharedPreferences().getAll()); + // Log all preferences changes + MainApplication.getSharedPreferences().registerOnSharedPreferenceChangeListener((prefs, key) -> Log.i("PrefsListener", "onSharedPreferenceChanged: " + key + " = " + prefs.getAll().get(key))); } - ensurePermissions(); Log.i(TAG, "Scanning for modules!"); if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Initialize Update"); final int max = ModuleManager.getINSTANCE().getUpdatableModuleCount(); @@ -222,9 +240,18 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe } if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Check Update Compat"); AppUpdateManager.getAppUpdateManager().checkUpdateCompat(); + /*if (BuildConfig.DEBUG) { + SharedPreferences prefs = MainApplication.getSharedPreferences(); + Log.d("MainActivity", String.format("Background update check: %s, Crash reporting: %s, Androidacy repo: %s, Magisk alt repo: %s", prefs.getBoolean("pref_background_update_check", false), prefs.getBoolean("pref_crash_reporting", false), prefs.getBoolean("pref_androidacy_repo_enabled", false), prefs.getBoolean("pref_magisk_alt_repo_enabled", false))); + }*/ if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Check Update"); RepoManager.getINSTANCE().update(value -> runOnUiThread(max == 0 ? () -> progressIndicator.setProgressCompat((int) (value * PRECISION), true) : () -> progressIndicator.setProgressCompat((int) (value * PRECISION * 0.75F), true))); NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder); + + /*if (BuildConfig.DEBUG) { + SharedPreferences prefs = MainApplication.getSharedPreferences(); + Log.d("MainActivity", String.format("Background update check: %s, Crash reporting: %s, Androidacy repo: %s, Magisk alt repo: %s", prefs.getBoolean("pref_background_update_check", false), prefs.getBoolean("pref_crash_reporting", false), prefs.getBoolean("pref_androidacy_repo_enabled", false), prefs.getBoolean("pref_magisk_alt_repo_enabled", false))); + }*/ if (!NotificationType.NO_INTERNET.shouldRemove()) { moduleViewListBuilder.addNotification(NotificationType.NO_INTERNET); } else if (!NotificationType.REPO_UPDATE_FAILED.shouldRemove()) { @@ -237,6 +264,11 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE); if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Check Json Update"); if (max != 0) { + + /*if (BuildConfig.DEBUG) { + SharedPreferences prefs = MainApplication.getSharedPreferences(); + Log.d("MainActivity", String.format("Background update check: %s, Crash reporting: %s, Androidacy repo: %s, Magisk alt repo: %s", prefs.getBoolean("pref_background_update_check", false), prefs.getBoolean("pref_crash_reporting", false), prefs.getBoolean("pref_androidacy_repo_enabled", false), prefs.getBoolean("pref_magisk_alt_repo_enabled", false))); + }*/ int current = 0; // noodleDebug.push(""); for (LocalModuleInfo localModuleInfo : ModuleManager.getINSTANCE().getModules().values()) { @@ -263,6 +295,11 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe }); if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Apply"); RepoManager.getINSTANCE().runAfterUpdate(moduleViewListBuilder::appendRemoteModules); + + /*if (BuildConfig.DEBUG) { + SharedPreferences prefs = MainApplication.getSharedPreferences(); + Log.d("MainActivity", String.format("Background update check: %s, Crash reporting: %s, Androidacy repo: %s, Magisk alt repo: %s", prefs.getBoolean("pref_background_update_check", false), prefs.getBoolean("pref_crash_reporting", false), prefs.getBoolean("pref_androidacy_repo_enabled", false), prefs.getBoolean("pref_magisk_alt_repo_enabled", false))); + }*/ moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter); Log.i(TAG, "Finished app opening state!"); // noodleDebug.unbind(); @@ -279,16 +316,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe String lastEventId = preferences.getString("lastEventId", ""); if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Last Event ID: " + lastEventId); if (!lastEventId.equals("")) { - try { - ExperimentalCronetEngine cronetEngine = new ExperimentalCronetEngine.Builder(this).build(); - CronetURLStreamHandlerFactory cronetURLStreamHandlerFactory = new CronetURLStreamHandlerFactory(cronetEngine); - URL.setURLStreamHandlerFactory(cronetURLStreamHandlerFactory); - } catch (Exception e) { - if (BuildConfig.DEBUG) { - Log.w(TAG, "Failed to setup cronet HTTPURLConnection factory", e); - Log.w(TAG, "This might mean the factory is already set"); - } - } // Three edit texts for the user to enter their email, name and a description of the issue EditText email = new EditText(this); email.setHint(R.string.email); @@ -319,10 +346,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe // Prevent strict mode violation new Thread(() -> { try { - // Use HTTPConnectionURL to send a post request to the sentry server - ExperimentalCronetEngine cronetEngine = new ExperimentalCronetEngine.Builder(this).build(); - CronetURLStreamHandlerFactory cronetURLStreamHandlerFactory = new CronetURLStreamHandlerFactory(cronetEngine); - URL.setURLStreamHandlerFactory(cronetURLStreamHandlerFactory); HttpURLConnection connection = (HttpURLConnection) new URL("https" + "://sentry.io/api/0/projects/androidacy-i6/foxmmm/user-feedback/").openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/json"); @@ -588,33 +611,50 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe @SuppressLint("RestrictedApi") private void ensurePermissions() { + if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Ensure Permissions"); // First, check if user has said don't ask again by checking if pref_dont_ask_again_notification_permission is true if (!PreferenceManager.getDefaultSharedPreferences(this).getBoolean("pref_dont_ask_again_notification_permission", false)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { - // Show a dialog explaining why we need this permission, which is to show - // notifications for updates - runOnUiThread(() -> { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); - builder.setTitle(R.string.permission_notification_title); - builder.setMessage(R.string.permission_notification_message); - // Don't ask again checkbox - View view = getLayoutInflater().inflate(R.layout.dialog_checkbox, null); - CheckBox checkBox = view.findViewById(R.id.checkbox); - checkBox.setText(R.string.dont_ask_again); - checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("pref_dont_ask_again_notification_permission", isChecked).apply()); - builder.setView(view); - builder.setPositiveButton(R.string.permission_notification_grant, (dialog, which) -> { - // Request the permission - this.requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 0); - }); - builder.setNegativeButton(R.string.cancel, (dialog, which) -> { - // Set pref_background_update_check to false and dismiss dialog - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - prefs.edit().putBoolean("pref_background_update_check", false).apply(); - dialog.dismiss(); + if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Request Notification Permission"); + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.POST_NOTIFICATIONS)) { + // Show a dialog explaining why we need this permission, which is to show + // notifications for updates + runOnUiThread(() -> { + if (BuildConfig.DEBUG) + Log.d("NoodleDebug", "Show Notification Permission Dialog"); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); + builder.setTitle(R.string.permission_notification_title); + builder.setMessage(R.string.permission_notification_message); + // Don't ask again checkbox + View view = getLayoutInflater().inflate(R.layout.dialog_checkbox, null); + CheckBox checkBox = view.findViewById(R.id.checkbox); + checkBox.setText(R.string.dont_ask_again); + checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("pref_dont_ask_again_notification_permission", isChecked).apply()); + builder.setView(view); + builder.setPositiveButton(R.string.permission_notification_grant, (dialog, which) -> { + // Request the permission + this.requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 0); + }); + builder.setNegativeButton(R.string.cancel, (dialog, which) -> { + // Set pref_background_update_check to false and dismiss dialog + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + prefs.edit().putBoolean("pref_background_update_check", false).apply(); + dialog.dismiss(); + }); + builder.show(); + if (BuildConfig.DEBUG) + Log.d("NoodleDebug", "Show Notification Permission Dialog Done"); }); - builder.show(); - }); + } else { + // Request the permission + if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Request Notification Permission"); + this.requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 0); + if (BuildConfig.DEBUG) { + // Log if granted via onRequestPermissionsResult + Log.d("NoodleDebug", "Request Notification Permission Done. Result: " + (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED)); + } + doSetupNowRunning = false; + } // Next branch is for < android 13 and user has blocked notifications } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && !NotificationManagerCompat.from(this).areNotificationsEnabled()) { runOnUiThread(() -> { @@ -634,65 +674,77 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivity(intent); + doSetupNowRunning = false; }); builder.setNegativeButton(R.string.cancel, (dialog, which) -> { // Set pref_background_update_check to false and dismiss dialog SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); prefs.edit().putBoolean("pref_background_update_check", false).apply(); dialog.dismiss(); + doSetupNowRunning = false; }); builder.show(); }); + } else { + doSetupNowRunning = false; } + } else { + if (BuildConfig.DEBUG) + Log.d("NoodleDebug", "Notification Permission Already Granted or Don't Ask Again"); + doSetupNowRunning = false; } } // Method to show a setup box on first launch - @RequiresApi(api = Build.VERSION_CODES.M) @SuppressLint({"InflateParams", "RestrictedApi", "UnspecifiedImmutableFlag", "ApplySharedPref"}) - private void showSetupBox() { + private void doSetupNow() { + if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Do setup now"); // Check if this is the first launch - if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("pref_first_launch", true)) { - MainApplication.getBootSharedPreferences().edit().putBoolean("mm_first_scan", false).commit(); + SharedPreferences prefs = MainApplication.getSharedPreferences(); + boolean firstLaunch = MainApplication.getBootSharedPreferences().getBoolean("first_launch", true); + if (BuildConfig.DEBUG) Log.d("Noodle", "First launch: " + firstLaunch); + if (firstLaunch) { // Show setup box runOnUiThread(() -> { MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); builder.setCancelable(false); builder.setTitle(R.string.setup_title); - builder.setView(getLayoutInflater().inflate(R.layout.setup_box, null)); + View view = getLayoutInflater().inflate(R.layout.setup_box, null); + builder.setView(view); // For now, we'll just have the positive button save the preferences and dismiss the dialog builder.setPositiveButton(R.string.setup_button, (dialog, which) -> { // Set the preferences - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - prefs.edit().putBoolean("pref_background_update_check", - ((MaterialSwitch) Objects.requireNonNull(((AlertDialog) dialog).findViewById(R.id.setup_background_update_check))).isChecked()).commit(); + prefs.edit().putBoolean("pref_background_update_check", ((MaterialSwitch) Objects.requireNonNull(((AlertDialog) dialog).findViewById(R.id.setup_background_update_check))).isChecked()).commit(); prefs.edit().putBoolean("pref_crash_reporting", ((MaterialSwitch) Objects.requireNonNull(((AlertDialog) dialog).findViewById(R.id.setup_crash_reporting))).isChecked()).commit(); prefs.edit().putBoolean("pref_androidacy_repo_enabled", ((MaterialSwitch) Objects.requireNonNull(((AlertDialog) dialog).findViewById(R.id.setup_androidacy_repo))).isChecked()).commit(); prefs.edit().putBoolean("pref_magisk_alt_repo_enabled", ((MaterialSwitch) Objects.requireNonNull(((AlertDialog) dialog).findViewById(R.id.setup_magisk_alt_repo))).isChecked()).commit(); if (BuildConfig.DEBUG) { - Log.d("MainActivity", String.format("Background update check: %s, Crash reporting: %s, Androidacy repo: %s, Magisk alt repo: %s", - prefs.getBoolean("pref_background_update_check", false), - prefs.getBoolean("pref_crash_reporting", false), - prefs.getBoolean("pref_androidacy_repo_enabled", false), - prefs.getBoolean("pref_magisk_alt_repo_enabled", false))); + Log.d("MainActivity", String.format("Background update check: %s, Crash reporting: %s, Androidacy repo: %s, Magisk alt repo: %s", prefs.getBoolean("pref_background_update_check", false), prefs.getBoolean("pref_crash_reporting", false), prefs.getBoolean("pref_androidacy_repo_enabled", false), prefs.getBoolean("pref_magisk_alt_repo_enabled", false))); } // Set pref_first_launch to false - PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("pref_first_launch", - false).commit(); + MainApplication.getBootSharedPreferences().edit().putBoolean("first_launch", false).commit(); // Restart the app Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); finish(); + startActivity(intent); + ensurePermissions(); }); builder.setNegativeButton(R.string.setup_button_skip, (dialog, which) -> { - // Set pref_first_launch to false - PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("pref_first_launch", - false).commit(); + MainApplication.getBootSharedPreferences().edit().putBoolean("first_launch", false).commit(); dialog.dismiss(); + ensurePermissions(); }); builder.show(); + // Set the switches appropriately + ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).setChecked(BuildConfig.ENABLE_AUTO_UPDATER); + ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).setChecked(BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING); + // Repos are a little harder, as the enabled_repos build config is an arraylist + ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_androidacy_repo))).setChecked(BuildConfig.ENABLED_REPOS.contains("androidacy_repo")); + ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_magisk_alt_repo))).setChecked(BuildConfig.ENABLED_REPOS.contains("magisk_alt_repo")); }); + } else { + ensurePermissions(); } } } \ No newline at end of file diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java index f14de34..b9258ae 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java @@ -108,7 +108,7 @@ public final class AndroidacyRepoData extends RepoData { deviceId = output; } } - // Now, get device model, manufacturer, and Android version + // Now, get device model, manufacturer, and Android version originally from String deviceModel = android.os.Build.MODEL; String deviceManufacturer = android.os.Build.MANUFACTURER; String androidVersion = android.os.Build.VERSION.RELEASE; 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 b2cfb06..13ca53b 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java @@ -63,7 +63,7 @@ public class AndroidacyWebAPI { void openNativeModuleDialogRaw(String moduleUrl, String moduleId, String installTitle, String checksum, boolean canInstall) { - Log.d(TAG, "ModuleDialog, downloadUrl: " + AndroidacyUtil.hideToken(moduleUrl) + + if (BuildConfig.DEBUG) Log.d(TAG, "ModuleDialog, downloadUrl: " + AndroidacyUtil.hideToken(moduleUrl) + ", moduleId: " + moduleId + ", installTitle: " + installTitle + ", checksum: " + checksum + ", canInstall: " + canInstall); this.downloadMode = false; @@ -145,7 +145,7 @@ public class AndroidacyWebAPI { void notifyCompatModeRaw(int value) { if (this.consumedAction) return; - Log.d(TAG, "Androidacy Compat mode: " + value); + if (BuildConfig.DEBUG) Log.d(TAG, "Androidacy Compat mode: " + value); this.notifiedCompatMode = value; if (value < 0) { value = 0; @@ -180,7 +180,7 @@ public class AndroidacyWebAPI { if (this.consumedAction) return; this.consumedAction = true; this.downloadMode = false; - Log.d(TAG, "Received openUrl request: " + url); + if (BuildConfig.DEBUG) Log.d(TAG, "Received openUrl request: " + url); if (Uri.parse(url).getScheme().equals("https")) { IntentHelper.openUrl(this.activity, url); } @@ -194,7 +194,7 @@ public class AndroidacyWebAPI { if (this.consumedAction) return; this.consumedAction = true; this.downloadMode = false; - Log.d(TAG, "Received openCustomTab request: " + url); + if (BuildConfig.DEBUG) Log.d(TAG, "Received openCustomTab request: " + url); if (Uri.parse(url).getScheme().equals("https")) { IntentHelper.openCustomTab(this.activity, url); } @@ -238,7 +238,7 @@ public class AndroidacyWebAPI { } this.consumedAction = true; this.downloadMode = false; - Log.d(TAG, "Received install request: " + + if (BuildConfig.DEBUG) Log.d(TAG, "Received install request: " + moduleUrl + " " + installTitle + " " + checksum); if (!AndroidacyUtil.isAndroidacyLink(moduleUrl)) { this.forceQuitRaw("Non Androidacy module link used on Androidacy"); @@ -375,6 +375,7 @@ public class AndroidacyWebAPI { /** * Return true if the module is an Andoridacy module. */ + @SuppressWarnings("BooleanMethodIsAlwaysInverted") @JavascriptInterface public boolean isAndroidacyModule(String moduleId) { LocalModuleInfo localModuleInfo = ModuleManager.getINSTANCE().getModules().get(moduleId); @@ -447,7 +448,7 @@ public class AndroidacyWebAPI { /** * Return current android sdk-int version code, see: - * https://source.android.com/setup/start/build-numbers + * right here */ @JavascriptInterface public int getAndroidVersionCode() { diff --git a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java index df4f53e..3d8888a 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java +++ b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java @@ -113,7 +113,7 @@ public final class ModuleManager extends SyncManager { PropUtils.readProperties(moduleInfo, "/data/adb/modules/" + module + "/module.prop", true); } catch (Exception e) { - Log.d(TAG, "Failed to parse metadata!", e); + if (BuildConfig.DEBUG) Log.d(TAG, "Failed to parse metadata!", e); moduleInfo.flags |= FLAG_MM_INVALID; } } @@ -136,7 +136,7 @@ public final class ModuleManager extends SyncManager { PropUtils.readProperties(moduleInfo, "/data/adb/modules_update/" + module + "/module.prop", true); } catch (Exception e) { - Log.d(TAG, "Failed to parse metadata!", e); + if (BuildConfig.DEBUG) Log.d(TAG, "Failed to parse metadata!", e); moduleInfo.flags |= FLAG_MM_INVALID; } } diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java index 26aa6c6..02712b4 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java @@ -42,7 +42,7 @@ public class RepoData extends XRepo { public String name, website, support, donate, submitModule; private boolean forceHide, enabled; // Cache for speed - protected RepoData(String url, File cacheRoot, SharedPreferences cachedPreferences) { + public RepoData(String url, File cacheRoot, SharedPreferences cachedPreferences) { this.url = url; this.id = RepoManager.internalIdOfUrl(url); this.cacheRoot = cacheRoot; diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java index 9e8dc38..659d24d 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java @@ -75,12 +75,12 @@ public final class RepoManager extends SyncManager { this.repoData = new LinkedHashMap<>(); this.modules = new HashMap<>(); // We do not have repo list config yet. + this.androidacyRepoData = this.addAndroidacyRepoData(); RepoData altRepo = this.addRepoData( MAGISK_ALT_REPO, "Magisk Modules Alt Repo"); altRepo.defaultWebsite = RepoManager.MAGISK_ALT_REPO_HOMEPAGE; altRepo.defaultSubmitModule = "https://github.com/Magisk-Modules-Alt-Repo/submission/issues"; - this.androidacyRepoData = this.addAndroidacyRepoData(); this.customRepoManager = new CustomRepoManager(mainApplication, this); XHooks.onRepoManagerInitialize(); // Populate default cache @@ -145,10 +145,10 @@ public final class RepoManager extends SyncManager { static boolean isBuiltInRepo(String repo) { switch (repo) { - case RepoManager.MAGISK_ALT_REPO: - case RepoManager.MAGISK_ALT_REPO_JSDELIVR: case RepoManager.ANDROIDACY_MAGISK_REPO_ENDPOINT: case RepoManager.ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT: + case RepoManager.MAGISK_ALT_REPO: + case RepoManager.MAGISK_ALT_REPO_JSDELIVR: return true; } return false; 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 58ba64d..907e635 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -1,6 +1,9 @@ package com.fox2code.mmm.settings; +import static java.lang.Integer.parseInt; + import android.annotation.SuppressLint; +import android.app.ActivityManager; import android.app.AlarmManager; import android.app.Application; import android.app.PendingIntent; @@ -70,8 +73,10 @@ import com.topjohnwu.superuser.internal.UiThreadHandler; import org.json.JSONException; import java.io.IOException; +import java.io.RandomAccessFile; import java.security.NoSuchAlgorithmException; import java.util.HashSet; +import java.util.Locale; import java.util.Objects; import java.util.Random; @@ -81,6 +86,50 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { private static boolean devModeStepFirstBootIgnore = MainApplication.isDeveloper(); private static int devModeStep = 0; + // Shamelessly adapted from https://github.com/DrKLO/Telegram/blob/2c71f6c92b45386f0c2b25f1442596462404bb39/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java#L1254 + public final static int PERFORMANCE_CLASS_LOW = 0; + public final static int PERFORMANCE_CLASS_AVERAGE = 1; + public final static int PERFORMANCE_CLASS_HIGH = 2; + + @PerformanceClass + public static int getDevicePerformanceClass() { + int devicePerformanceClass; + int androidVersion = Build.VERSION.SDK_INT; + int cpuCount = Runtime.getRuntime().availableProcessors(); + int memoryClass = + ((ActivityManager) MainApplication.getINSTANCE().getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); + int totalCpuFreq = 0; + int freqResolved = 0; + for (int i = 0; i < cpuCount; i++) { + try { + RandomAccessFile reader = new RandomAccessFile(String.format(Locale.ENGLISH, "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i), "r"); + String line = reader.readLine(); + if (line != null) { + totalCpuFreq += parseInt(line) / 1000; + freqResolved++; + } + reader.close(); + } catch (Throwable ignore) {} + } + int maxCpuFreq = freqResolved == 0 ? -1 : (int) Math.ceil(totalCpuFreq / (float) freqResolved); + + if (androidVersion < 21 || cpuCount <= 2 || memoryClass <= 100 || cpuCount <= 4 && maxCpuFreq != -1 && maxCpuFreq <= 1250 || cpuCount <= 4 && maxCpuFreq <= 1600 && memoryClass <= 128 && androidVersion <= 21 || cpuCount <= 4 && maxCpuFreq <= 1300 && memoryClass <= 128 && androidVersion <= 24) { + devicePerformanceClass = PERFORMANCE_CLASS_LOW; + } else if (cpuCount < 8 || memoryClass <= 160 || maxCpuFreq != -1 && maxCpuFreq <= 2050 || maxCpuFreq == -1 && cpuCount == 8 && androidVersion <= 23) { + devicePerformanceClass = PERFORMANCE_CLASS_AVERAGE; + } else { + devicePerformanceClass = PERFORMANCE_CLASS_HIGH; + } + + if (BuildConfig.DEBUG) { + Log.d(TAG, "getDevicePerformanceClass: androidVersion=" + androidVersion + " cpuCount=" + cpuCount + " memoryClass=" + memoryClass + " maxCpuFreq=" + maxCpuFreq + " devicePerformanceClass=" + devicePerformanceClass); + } + + return devicePerformanceClass; + } + + public @interface PerformanceClass {} + @Override protected void onCreate(Bundle savedInstanceState) { devModeStep = 0; @@ -232,13 +281,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { 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); - } + mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, + mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); if (BuildConfig.DEBUG) { @@ -252,9 +296,36 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { return true; }); Preference enableBlur = findPreference("pref_enable_blur"); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - enableBlur.setSummary(R.string.require_android_6); - enableBlur.setEnabled(false); + // Disable blur on low performance devices + if (getDevicePerformanceClass() < PERFORMANCE_CLASS_AVERAGE) { + // Show a warning + enableBlur.setOnPreferenceChangeListener((preference, newValue) -> { + if (newValue.equals(true)) { + new MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.low_performance_device_dialogue_title) + .setMessage(R.string.low_performance_device_dialogue_message) + .setPositiveButton(R.string.ok, (dialog, which) -> { + // Toggle blur on + ((TwoStatePreference) findPreference("pref_enable_blur")).setChecked(true); + SharedPreferences.Editor editor = + getPreferenceManager().getSharedPreferences().edit(); + editor.putBoolean("pref_enable_blur", true).apply(); + // Set summary + findPreference("pref_enable_blur").setSummary(R.string.blur_disabled_summary); + }) + .setNegativeButton(R.string.cancel, (dialog, which) -> { + // Revert to blur on + ((TwoStatePreference) findPreference("pref_enable_blur")).setChecked(false); + SharedPreferences.Editor editor = + getPreferenceManager().getSharedPreferences().edit(); + editor.putBoolean("pref_enable_blur", false).apply(); + // Set summary + findPreference("pref_enable_blur").setSummary(null); + }) + .show(); + } + return true; + }); } Preference disableMonet = findPreference("pref_enable_monet"); @@ -566,13 +637,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { 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); - } + mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, + mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); if (BuildConfig.DEBUG) { @@ -603,13 +669,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { 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); - } + mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, + mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); if (BuildConfig.DEBUG) { @@ -694,13 +755,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { 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); - } + mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, + mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); if (BuildConfig.DEBUG) { @@ -752,13 +808,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { 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); - } + mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, + mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); if (BuildConfig.DEBUG) { diff --git a/app/src/main/res/layout/setup_box.xml b/app/src/main/res/layout/setup_box.xml index c362f2c..994d6bb 100644 --- a/app/src/main/res/layout/setup_box.xml +++ b/app/src/main/res/layout/setup_box.xml @@ -42,7 +42,7 @@ android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="4dp" - android:checked="true" + android:checked="false" android:key="pref_androidacy_repo_enabled" android:text="@string/setup_androidacy_repo" android:textSize="14sp" /> @@ -65,7 +65,7 @@ android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="2dp" - android:checked="true" + android:checked="false" android:text="@string/setup_custom_repos" android:textSize="12sp" /> @@ -86,7 +86,7 @@ android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="4dp" - android:checked="true" + android:checked="false" android:key="pref_crash_reporting_enabled" android:text="@string/setup_crash_reporting" android:textSize="14sp" /> @@ -98,7 +98,7 @@ android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="4dp" - android:checked="true" + android:checked="false" android:key="pref_background_update_check" android:text="@string/setup_background_update_check" android:textSize="14sp" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0e85cc0..bd522d7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -222,15 +222,20 @@ Official Unofficial First time setup - Looks like this is your first time opening the app.\nPick - the basic options you desire below. You can change them later in settings. + Welcome! This app will help you install and manage modules + for Magisk. To get started, please select the options below. Everything can be + configured from settings later. Finish setup Allow us to check for updates in the background. May use more battery. Enable the Androidacy repo, which features user reviews, automatic virus scans, fast updates, a wide selection, and is backed by Androidacy. - Enable the Magisk Alternative Repo. Less rules and reviewing than the original. Fully hosted on GitHub. + + Enable the Magisk Alternative Repo. Significantly more lax + than the original, but at the cost of some safety and privacy. Fully hosted on GitHub + . Enable automatic crash reporting and performance monitoring. Crash reports are anonymized with all personal info removed, and are onlly accessible to the developers. Uses sentry.io. You can add custom repos later in settings. Repos Misc settings - Skip + SkipEnabling blur on lower-end deviceYou are trying to enable blur on a device that may not perform well with it.\nYou may enable it, but this may lead to a poor user experience and we recommend you don\'t. diff --git a/app/src/main/res/xml/repo_preferences.xml b/app/src/main/res/xml/repo_preferences.xml index d85af87..d8b0e2a 100644 --- a/app/src/main/res/xml/repo_preferences.xml +++ b/app/src/main/res/xml/repo_preferences.xml @@ -1,35 +1,5 @@ - - - - - - - @@ -87,6 +57,36 @@ app:singleLineTitle="false" app:summary="@string/androidacy_repo_info" /> + + + + + + +