diff --git a/app/build.gradle b/app/build.gradle index ecdbf45..4be636e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -57,6 +57,9 @@ android { versionCode 64 versionName "2.0.0-beta.1" archivesBaseName = "FoxMMM-v$versionName" + vectorDrawables { + useSupportLibrary true + } } splits { @@ -281,15 +284,15 @@ configurations { dependencies { // UI implementation 'androidx.appcompat:appcompat:1.6.0' - implementation "androidx.activity:activity-ktx:1.7.0-alpha03" + implementation "androidx.activity:activity-ktx:1.7.0-alpha04" implementation 'androidx.emoji2:emoji2:1.2.0' implementation 'androidx.emoji2:emoji2-views-helper:1.2.0' implementation 'androidx.preference:preference-ktx:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' - implementation 'androidx.webkit:webkit:1.5.0' - implementation 'com.google.android.material:material:1.7.0' + implementation 'androidx.webkit:webkit:1.6.0' + implementation 'com.google.android.material:material:1.8.0' implementation "dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0" implementation "dev.rikka.rikkax.insets:insets:1.3.0" implementation 'com.github.Dimezis:BlurView:version-2.0.2' @@ -338,6 +341,17 @@ dependencies { implementation "com.google.devtools.ksp:symbol-processing-api:1.8.0-1.0.8" implementation "androidx.security:security-crypto:1.1.0-alpha04" + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' + implementation 'androidx.activity:activity-compose:1.6.1' + implementation platform('androidx.compose:compose-bom:2022.10.00') + implementation 'androidx.compose.ui:ui' + implementation 'androidx.compose.ui:ui-graphics' + implementation 'androidx.compose.ui:ui-tooling-preview' + implementation 'androidx.compose.material3:material3' + androidTestImplementation platform('androidx.compose:compose-bom:2022.10.00') + androidTestImplementation 'androidx.compose.ui:ui-test-junit4' + debugImplementation 'androidx.compose.ui:ui-tooling' + debugImplementation 'androidx.compose.ui:ui-test-manifest' } if (hasSentryConfig) { @@ -361,13 +375,22 @@ android { } buildFeatures { viewBinding true + compose true } kotlinOptions { - jvmTarget=JavaVersion.VERSION_11 + jvmTarget= '1.8' } //noinspection GrDeprecatedAPIUsage buildToolsVersion '33.0.1' + composeOptions { + kotlinCompilerExtensionVersion '1.3.2' + } + packagingOptions { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 104360f..bf551f5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,9 +23,9 @@ + allowList = new HashSet<>(); + allowList.add("https://*.androidacy.com"); + WebSettingsCompat.setRequestedWithHeaderOriginAllowList(webSettings, allowList); } this.webView.setWebViewClient(new WebViewClientCompat() { 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 7723415..cb4780e 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java @@ -56,7 +56,7 @@ public final class AndroidacyRepoData extends RepoData { public final String ClientID = BuildConfig.ANDROIDACY_CLIENT_ID; private final boolean testMode; private final String host; - public SharedPreferences cachedPreferences = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0); + public final SharedPreferences cachedPreferences = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0); public String memberLevel; // Avoid spamming requests to Androidacy private long androidacyBlockade = 0; diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyUtil.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyUtil.java index e95f9e3..99d6dd8 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyUtil.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyUtil.java @@ -7,7 +7,8 @@ import androidx.annotation.Nullable; import com.fox2code.mmm.BuildConfig; -public class AndroidacyUtil { +public enum AndroidacyUtil { + ; public static final String REFERRER = "utm_source=FoxMMM&utm_medium=app"; public static boolean isAndroidacyLink(@Nullable Uri uri) { 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 d8f2ce9..1495f30 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java @@ -36,6 +36,7 @@ import java.nio.charset.StandardCharsets; import timber.log.Timber; +@SuppressWarnings("SameReturnValue") @Keep public class AndroidacyWebAPI { public static final int COMPAT_UNSUPPORTED = 0; diff --git a/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java b/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java index b0c6cd1..a7821ef 100644 --- a/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java +++ b/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java @@ -7,10 +7,10 @@ import android.content.Intent; import android.content.pm.PackageManager; import androidx.annotation.NonNull; -import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationChannelCompat; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; +import androidx.core.content.ContextCompat; import androidx.work.Constraints; import androidx.work.ExistingPeriodicWorkPolicy; import androidx.work.NetworkType; @@ -52,6 +52,9 @@ public class BackgroundUpdateChecker extends Worker { for (LocalModuleInfo localModuleInfo : ModuleManager.getINSTANCE().getModules().values()) { if ("twrp-keep".equals(localModuleInfo.id)) continue; + // exclude all modules with id's stored in the pref pref_background_update_check_excludes + if (MainApplication.getSharedPreferences().getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id)) + continue; RepoModule repoModule = repoModules.get(localModuleInfo.id); localModuleInfo.checkModuleUpdate(); if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode && !PropUtils.isNullString(localModuleInfo.updateVersion)) { @@ -61,20 +64,20 @@ public class BackgroundUpdateChecker extends Worker { } } if (moduleUpdateCount != 0) { - postNotification(context, moduleUpdateCount); + postNotification(context, moduleUpdateCount, false); } }); } - public static void postNotification(Context context, int updateCount) { + public static void postNotification(Context context, int updateCount, boolean test) { if (!easterEggActive) easterEggActive = new Random().nextInt(100) <= updateCount; NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID).setContentTitle(context.getString(easterEggActive ? R.string.notification_update_title_easter_egg : R.string.notification_update_title).replace("%i", String.valueOf(updateCount))).setContentText(context.getString(R.string.notification_update_subtitle)).setSmallIcon(R.drawable.ic_baseline_extension_24).setPriority(NotificationCompat.PRIORITY_HIGH).setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), PendingIntent.FLAG_IMMUTABLE)).setAutoCancel(true); - if (ActivityCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { + if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { return; } // check if app is in foreground. if so, don't show notification - if (MainApplication.getINSTANCE().isInForeground()) + if (MainApplication.getINSTANCE().isInForeground() && !test) return; NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, builder.build()); } 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 3821d12..3288bdb 100644 --- a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java +++ b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java @@ -57,7 +57,6 @@ import java.util.zip.ZipInputStream; import timber.log.Timber; -@SuppressWarnings("IOStreamConstructor") public class InstallerActivity extends FoxActivity { public LinearProgressIndicator progressIndicator; public ExtendedFloatingActionButton rebootFloatingButton; diff --git a/app/src/main/java/com/fox2code/mmm/markdown/MarkdownUrlLinker.java b/app/src/main/java/com/fox2code/mmm/markdown/MarkdownUrlLinker.java index 9f21459..1385cc7 100644 --- a/app/src/main/java/com/fox2code/mmm/markdown/MarkdownUrlLinker.java +++ b/app/src/main/java/com/fox2code/mmm/markdown/MarkdownUrlLinker.java @@ -4,7 +4,8 @@ import java.util.ArrayList; import timber.log.Timber; -public class MarkdownUrlLinker { +public enum MarkdownUrlLinker { + ; public static String urlLinkify(String url) { int index = url.indexOf("https://"); 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 e022222..0abc1ec 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java @@ -123,6 +123,8 @@ public class RepoData extends XRepo { if (this.enabled) { try { this.metaDataCache = ModuleListCache.getRepoModulesAsJson(this.id); + // log count of modules in the database + Timber.d("RepoData: " + this.id + ". modules in database: " + this.metaDataCache.length()); // load repo metadata from ReposList unless it's a built-in repo if (RepoManager.isBuiltInRepo(this.id)) { this.name = this.defaultName; 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 e5055bc..52c1d6e 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -11,6 +11,7 @@ import android.app.PendingIntent; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; @@ -51,6 +52,8 @@ import com.fox2code.mmm.R; import com.fox2code.mmm.androidacy.AndroidacyRepoData; import com.fox2code.mmm.background.BackgroundUpdateChecker; import com.fox2code.mmm.installer.InstallerInitializer; +import com.fox2code.mmm.manager.LocalModuleInfo; +import com.fox2code.mmm.manager.ModuleManager; import com.fox2code.mmm.module.ActionButtonType; import com.fox2code.mmm.repo.CustomRepoData; import com.fox2code.mmm.repo.CustomRepoManager; @@ -66,6 +69,7 @@ import com.fox2code.rosettax.LanguageActivity; import com.fox2code.rosettax.LanguageSwitcher; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.internal.TextWatcherAdapter; +import com.google.android.material.snackbar.BaseTransientBottomBar; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.textfield.MaterialAutoCompleteTextView; import com.mikepenz.aboutlibraries.LibsBuilder; @@ -78,9 +82,12 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.RandomAccessFile; import java.security.NoSuchAlgorithmException; +import java.util.Collection; +import java.util.HashSet; import java.util.Locale; import java.util.Objects; import java.util.Random; +import java.util.Set; import io.realm.Realm; import io.realm.RealmConfiguration; @@ -395,11 +402,11 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { findPreference("pref_use_magisk_install_command").setVisible(false); } Preference debugNotification = findPreference("pref_background_update_check_debug"); + Preference updateCheckExcludes = findPreference("pref_background_update_check_excludes"); debugNotification.setEnabled(MainApplication.isBackgroundUpdateCheckEnabled()); - debugNotification.setVisible(MainApplication.isDeveloper() && !MainApplication.isWrapped()); - debugNotification.setVisible(MainApplication.isDeveloper() && !MainApplication.isWrapped()); + debugNotification.setVisible(MainApplication.isDeveloper() && !MainApplication.isWrapped() && MainApplication.isBackgroundUpdateCheckEnabled()); debugNotification.setOnPreferenceClickListener(preference -> { - BackgroundUpdateChecker.postNotification(this.requireContext(), new Random().nextInt(4) + 2); + BackgroundUpdateChecker.postNotification(this.requireContext(), new Random().nextInt(4) + 2, true); return true; }); Preference backgroundUpdateCheck = findPreference("pref_background_update_check"); @@ -426,29 +433,57 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { }); backgroundUpdateCheck.setSummary(R.string.background_update_check_permission_required); } - - EditTextPreference updateCheckExcludes = findPreference("pref_background_update_check_excludes"); + updateCheckExcludes.setVisible(MainApplication.isBackgroundUpdateCheckEnabled() && !MainApplication.isWrapped()); backgroundUpdateCheck.setOnPreferenceChangeListener((preference, newValue) -> { boolean enabled = Boolean.parseBoolean(String.valueOf(newValue)); debugNotification.setEnabled(enabled); + debugNotification.setVisible(MainApplication.isDeveloper() && !MainApplication.isWrapped() && enabled); updateCheckExcludes.setEnabled(enabled); + updateCheckExcludes.setVisible(enabled && !MainApplication.isWrapped()); if (!enabled) { BackgroundUpdateChecker.onMainActivityResume(this.requireContext()); } return true; }); - // updateCheckExcludes is an EditTextPreference. on change, validate it contains only alphanumerical and , - _ characters - updateCheckExcludes.setOnPreferenceChangeListener((preference, newValue) -> { - String value = String.valueOf(newValue); - // strip whitespace - value = value.replaceAll("\\s", ""); - if (value.matches("^[a-zA-Z0-9,\\-_]*$")) { - return true; - } else { - new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.invalid_excludes).setMessage(R.string.invalid_characters_message).setPositiveButton(R.string.ok, (dialog, which) -> { - }).show(); - return false; + // updateCheckExcludes saves to pref_background_update_check_excludes as a stringset. On clicking, it should open a dialog with a list of all installed modules + updateCheckExcludes.setOnPreferenceClickListener(preference -> { + Collection localModuleInfos = ModuleManager.getINSTANCE().getModules().values(); + String[] moduleNames = new String[localModuleInfos.size()]; + boolean[] checkedItems = new boolean[localModuleInfos.size()]; + int i = 0; + for (LocalModuleInfo localModuleInfo : localModuleInfos) { + moduleNames[i] = localModuleInfo.name; + SharedPreferences sharedPreferences = MainApplication.getSharedPreferences(); + // get the stringset pref_background_update_check_excludes + Set stringSet = sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>()); + // Stringset uses id, we show name + checkedItems[i] = stringSet.contains(localModuleInfo.id); + Timber.d("name: %s, checked: %s", moduleNames[i], checkedItems[i]); + i++; } + new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.background_update_check_excludes).setMultiChoiceItems(moduleNames, checkedItems, (dialog, which, isChecked) -> { + // get the stringset pref_background_update_check_excludes + SharedPreferences sharedPreferences = MainApplication.getSharedPreferences(); + Set stringSet = new HashSet<>(sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>())); + // get id from name + String id; + if (localModuleInfos.stream().anyMatch(localModuleInfo -> localModuleInfo.name.equals(moduleNames[which]))) { + //noinspection OptionalGetWithoutIsPresent + id = localModuleInfos.stream().filter(localModuleInfo -> localModuleInfo.name.equals(moduleNames[which])).findFirst().get().id; + } else { + id = ""; + } + if (!id.isEmpty()) { + if (isChecked) { + stringSet.add(id); + } else { + stringSet.remove(id); + } + } + sharedPreferences.edit().putStringSet("pref_background_update_check_excludes", stringSet).apply(); + }).setPositiveButton(R.string.ok, (dialog, which) -> { + }).show(); + return true; }); final LibsBuilder libsBuilder = new LibsBuilder().withShowLoadingProgress(false).withLicenseShown(true).withAboutMinimalDesign(false); ClipboardManager clipboard = (ClipboardManager) requireContext().getSystemService(Context.CLIPBOARD_SERVICE); @@ -851,7 +886,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { // validate the api key client side first. should be 64 characters long, and only allow alphanumeric characters if (!newValue.toString().matches("[a-zA-Z0-9]{64}")) { // Show snack bar with error - Snackbar.make(requireView(), R.string.api_key_mismatch, Snackbar.LENGTH_LONG).show(); + Snackbar.make(requireView(), R.string.api_key_mismatch, BaseTransientBottomBar.LENGTH_LONG).show(); // Restore the original api key prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]); prefAndroidacyRepoApiKey.performClick(); @@ -863,7 +898,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { // get original api key String apiKey = String.valueOf(newValue); // Show snack bar with indeterminate progress - Snackbar.make(requireView(), R.string.checking_api_key, Snackbar.LENGTH_INDEFINITE).setAction(R.string.cancel, v -> { + Snackbar.make(requireView(), R.string.checking_api_key, BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(R.string.cancel, v -> { // Restore the original api key prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]); }).show(); @@ -873,7 +908,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { if (apiKey.isEmpty()) { MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().remove("pref_androidacy_api_token").apply(); new Handler(Looper.getMainLooper()).post(() -> { - Snackbar.make(requireView(), R.string.api_key_removed, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(requireView(), R.string.api_key_removed, BaseTransientBottomBar.LENGTH_SHORT).show(); // Show dialog to restart app with ok button new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.restart).setCancelable(false).setMessage(R.string.api_key_restart).setNeutralButton(android.R.string.ok, (dialog, which) -> { // User clicked OK button @@ -893,7 +928,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { // If key < 64 chars, it's not valid if (apiKey.length() < 64) { new Handler(Looper.getMainLooper()).post(() -> { - Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(requireView(), R.string.api_key_invalid, BaseTransientBottomBar.LENGTH_SHORT).show(); // Save the original key MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().putString("pref_androidacy_api_token", originalApiKeyRef[0]).apply(); // Re-show the dialog with an error @@ -904,7 +939,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { } else { // If the key is the same as the original, just show a snack bar if (apiKey.equals(originalApiKeyRef[0])) { - new Handler(Looper.getMainLooper()).post(() -> Snackbar.make(requireView(), R.string.api_key_unchanged, Snackbar.LENGTH_SHORT).show()); + new Handler(Looper.getMainLooper()).post(() -> Snackbar.make(requireView(), R.string.api_key_unchanged, BaseTransientBottomBar.LENGTH_SHORT).show()); return; } boolean valid = false; @@ -921,7 +956,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().putString("pref_androidacy_api_token", apiKey).apply(); // Snackbar with success and restart button new Handler(Looper.getMainLooper()).post(() -> { - Snackbar.make(requireView(), R.string.api_key_valid, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(requireView(), R.string.api_key_valid, BaseTransientBottomBar.LENGTH_SHORT).show(); // Show dialog to restart app with ok button new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.restart).setCancelable(false).setMessage(R.string.api_key_restart).setNeutralButton(android.R.string.ok, (dialog, which) -> { // User clicked OK button @@ -939,7 +974,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { }); } else { new Handler(Looper.getMainLooper()).post(() -> { - Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(requireView(), R.string.api_key_invalid, BaseTransientBottomBar.LENGTH_SHORT).show(); // Save the original key MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().putString("pref_androidacy_api_token", originalApiKeyRef[0]).apply(); // Re-show the dialog with an error @@ -1027,7 +1062,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { }); builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); AlertDialog alertDialog = builder.show(); - final Button positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); + final Button positiveButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); input.setValidator(new AutoCompleteTextView.Validator() { @Override public boolean isValid(CharSequence charSequence) { @@ -1086,7 +1121,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { preference.setOnPreferenceChangeListener((p, newValue) -> { p.setTitle(((Boolean) newValue) ? R.string.repo_enabled : R.string.repo_disabled); // Show snackbar telling the user to refresh the modules list or restart the app - Snackbar.make(requireView(), R.string.repo_enabled_changed, Snackbar.LENGTH_LONG).show(); + Snackbar.make(requireView(), R.string.repo_enabled_changed, BaseTransientBottomBar.LENGTH_LONG).show(); return true; }); } diff --git a/app/src/main/java/com/fox2code/mmm/utils/BlurUtils.java b/app/src/main/java/com/fox2code/mmm/utils/BlurUtils.java index 811cd6a..60957bd 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/BlurUtils.java +++ b/app/src/main/java/com/fox2code/mmm/utils/BlurUtils.java @@ -14,7 +14,9 @@ import eightbitlab.com.blurview.BlurView; import eightbitlab.com.blurview.RenderEffectBlur; import eightbitlab.com.blurview.RenderScriptBlur; -public class BlurUtils { +public enum BlurUtils { + ; + public static void setupBlur(BlurView blurView, Activity activity, @IdRes int viewId) { setupBlur(blurView, activity, activity.findViewById(viewId)); } diff --git a/app/src/main/java/com/fox2code/mmm/utils/BudgetProgressDialog.java b/app/src/main/java/com/fox2code/mmm/utils/BudgetProgressDialog.java index 95c5d05..e702a60 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/BudgetProgressDialog.java +++ b/app/src/main/java/com/fox2code/mmm/utils/BudgetProgressDialog.java @@ -14,8 +14,10 @@ import androidx.appcompat.widget.LinearLayoutCompat; import com.google.android.material.dialog.MaterialAlertDialogBuilder; // ProgressDialog is deprecated because it's an bad UX pattern, but sometimes we have no other choice... -public class BudgetProgressDialog { - public static AlertDialog build(Context context, String title, String message) { +public enum BudgetProgressDialog { + ; + + public static AlertDialog build(Context context, String title, String message) { Resources r = context.getResources(); int padding = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, r.getDisplayMetrics())); LinearLayoutCompat v = new LinearLayoutCompat(context); @@ -39,10 +41,6 @@ public class BudgetProgressDialog { return build(context, context.getString(title), message); } - public static AlertDialog build(Context context, String title, int message) { - return build(context, title, context.getString(message)); - } - public static AlertDialog build(Context context, int title, int message) { return build(context, title, context.getString(message)); } diff --git a/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java b/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java index 93f7ea8..1dfee4e 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java +++ b/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java @@ -39,7 +39,8 @@ import java.net.URISyntaxException; import timber.log.Timber; -public class IntentHelper { +public enum IntentHelper { + ; private static final String EXTRA_TAB_SESSION = "android.support.customtabs.extra.SESSION"; private static final String EXTRA_TAB_COLOR_SCHEME = diff --git a/app/src/main/java/com/fox2code/mmm/utils/ProcessHelper.java b/app/src/main/java/com/fox2code/mmm/utils/ProcessHelper.java index ede43a4..177e162 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/ProcessHelper.java +++ b/app/src/main/java/com/fox2code/mmm/utils/ProcessHelper.java @@ -9,7 +9,8 @@ import com.fox2code.mmm.MainActivity; import java.util.concurrent.ThreadLocalRandom; -public class ProcessHelper { +public enum ProcessHelper { + ; private static final int sPendingIntentId = ThreadLocalRandom.current().nextInt(100, 1000000 + 1); public static void restartApplicationProcess(Context context) { diff --git a/app/src/main/java/com/fox2code/mmm/utils/SyncManager.java b/app/src/main/java/com/fox2code/mmm/utils/SyncManager.java index 863cc27..0e016ff 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/SyncManager.java +++ b/app/src/main/java/com/fox2code/mmm/utils/SyncManager.java @@ -3,8 +3,6 @@ package com.fox2code.mmm.utils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.fox2code.mmm.repo.RepoManager; - /** * Manager that want both to be thread safe and not to worry about thread safety * {@link #scan()} and {@link #update(UpdateListener)} can be called from multiple diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/AddCookiesInterceptor.java b/app/src/main/java/com/fox2code/mmm/utils/io/AddCookiesInterceptor.java index fa224f7..d7ae8e7 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/AddCookiesInterceptor.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/AddCookiesInterceptor.java @@ -62,14 +62,20 @@ public class AddCookiesInterceptor implements Interceptor { // ensure the cookie applies to the current domain if (cookie.contains("domain=")) { // match from the start of the string to the first semicolon - Pattern pattern = Pattern.compile("domain=([^;]+)"); - String domain = pattern.matcher(cookie).group(1); - if (domain != null && !chain.request().url().host().contains(domain)) { - //noinspection UnnecessaryContinue - continue; - } else { - builder.addHeader("Cookie", cookie); + try { + Pattern pattern = Pattern.compile("domain=([^;]+)"); + String domain = pattern.matcher(cookie).group(1); + if (domain != null && !chain.request().url().host().contains(domain)) { + //noinspection UnnecessaryContinue + continue; + } else { + builder.addHeader("Cookie", cookie); + } + } catch ( + Exception ignored) { } + } else { + builder.addHeader("Cookie", cookie); } } diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/Files.java b/app/src/main/java/com/fox2code/mmm/utils/io/Files.java index 00b1511..5193d8d 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/Files.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/Files.java @@ -29,7 +29,8 @@ import java.util.zip.ZipOutputStream; import timber.log.Timber; -public class Files { +public enum Files { + ; private static final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0; // stolen from https://stackoverflow.com/a/25005243 diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/GMSProviderInstaller.java b/app/src/main/java/com/fox2code/mmm/utils/io/GMSProviderInstaller.java index e7ad262..92811b3 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/GMSProviderInstaller.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/GMSProviderInstaller.java @@ -35,7 +35,8 @@ import timber.log.Timber; */ // Note: This code is MIT because I took it from another unpublished project I had // I might upstream this to MicroG at some point -public class GMSProviderInstaller { +public enum GMSProviderInstaller { + ; private static boolean called = false; public static void installIfNeeded(final Context context) { diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/Hashes.java b/app/src/main/java/com/fox2code/mmm/utils/io/Hashes.java index e8c7f80..b1bec18 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/Hashes.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/Hashes.java @@ -7,7 +7,8 @@ import java.util.regex.Pattern; import timber.log.Timber; -public class Hashes { +public enum Hashes { + ; private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); private static final Pattern nonAlphaNum = Pattern.compile("[^a-zA-Z0-9]"); diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/Http.java b/app/src/main/java/com/fox2code/mmm/utils/io/Http.java index 5e2306c..585b7e0 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/Http.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/Http.java @@ -56,7 +56,8 @@ import okhttp3.dnsoverhttps.DnsOverHttps; import okio.BufferedSink; import timber.log.Timber; -public class Http { +public enum Http { + ; private static final OkHttpClient httpClient; private static final OkHttpClient httpClientDoH; private static final OkHttpClient httpClientWithCache; diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/PropUtils.java b/app/src/main/java/com/fox2code/mmm/utils/io/PropUtils.java index b72ba3c..33a1643 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/PropUtils.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/PropUtils.java @@ -22,11 +22,13 @@ import java.util.Locale; import timber.log.Timber; -public class PropUtils { +public enum PropUtils { + ; private static final HashMap moduleSupportsFallbacks = new HashMap<>(); private static final HashMap moduleConfigsFallbacks = new HashMap<>(); private static final HashMap moduleMinApiFallbacks = new HashMap<>(); private static final HashMap moduleUpdateJsonFallbacks = new HashMap<>(); + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private static final HashSet moduleMTTRebornFallback = new HashSet<>(); private static final HashSet moduleImportantProp = new HashSet<>(Arrays.asList( "id", "name", "version", "versionCode" diff --git a/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java b/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java index 0aeed73..5ac6f20 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java +++ b/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java @@ -17,7 +17,8 @@ import io.sentry.android.core.SentryAndroid; import io.sentry.android.fragment.FragmentLifecycleIntegration; import io.sentry.android.timber.SentryTimberIntegration; -public class SentryMain { +public enum SentryMain { + ; public static final boolean IS_SENTRY_INSTALLED = true; private static boolean sentryEnabled = false; diff --git a/app/src/main/res/drawable/baseline_block_24.xml b/app/src/main/res/drawable/baseline_block_24.xml new file mode 100644 index 0000000..14567ea --- /dev/null +++ b/app/src/main/res/drawable/baseline_block_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_update.xml b/app/src/main/res/layout/activity_update.xml new file mode 100644 index 0000000..aec5912 --- /dev/null +++ b/app/src/main/res/layout/activity_update.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/no_magisk.xml b/app/src/main/res/layout/no_magisk.xml index 3660afc..d13bf96 100644 --- a/app/src/main/res/layout/no_magisk.xml +++ b/app/src/main/res/layout/no_magisk.xml @@ -1,9 +1,8 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> +