diff --git a/app/build.gradle b/app/build.gradle index 9090b9b..aadd35e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -313,9 +313,8 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10' implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:5.0.0-alpha.10' // Chromium cronet from androidacy - implementation 'com.androidacy:cronet-common:109.0.5414.75' - implementation 'com.androidacy:cronet-native:109.0.5414.75' - // Force prefer our own version of Cronet + implementation 'org.chromium.net:cronet-embedded:108.5359.79' + implementation 'com.github.topjohnwu.libsu:io:5.0.1' implementation 'com.github.Fox2Code:RosettaX:1.0.9' implementation 'com.github.Fox2Code:AndroidANSI:1.0.1' @@ -342,12 +341,11 @@ dependencies { implementation 'androidx.core:core-ktx:1.9.0' - // timber implementation 'com.jakewharton.timber:timber:5.0.1' // ksp - implementation "com.google.devtools.ksp:symbol-processing-api:1.8.0-1.0.9" + implementation "com.google.devtools.ksp:symbol-processing-api:1.8.10-1.0.9" implementation "androidx.security:security-crypto:1.1.0-alpha04" } @@ -366,7 +364,7 @@ if (hasSentryConfig) { } android { - ndkVersion '25.1.8937393' + ndkVersion '25.2.9519653' dependenciesInfo { includeInApk false includeInBundle false @@ -379,7 +377,7 @@ android { jvmTarget = JavaVersion.VERSION_17 } //noinspection GrDeprecatedAPIUsage - buildToolsVersion '33.0.1' + buildToolsVersion '33.0.2' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c36bb9c..ba23fd9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -56,6 +56,7 @@ android:testOnly="false" android:theme="@style/Theme.MagiskModuleManager" android:usesCleartextTraffic="false" + android:extractNativeLibs="true" tools:ignore="ManifestResource" tools:replace="android:supportsRtl" tools:targetApi="tiramisu"> diff --git a/app/src/main/java/com/fox2code/mmm/MainActivity.java b/app/src/main/java/com/fox2code/mmm/MainActivity.java index 519fbea..92b3377 100644 --- a/app/src/main/java/com/fox2code/mmm/MainActivity.java +++ b/app/src/main/java/com/fox2code/mmm/MainActivity.java @@ -14,6 +14,7 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.net.Uri; +import android.net.http.HttpResponseCache; import android.os.Build; import android.os.Bundle; import android.provider.Settings; @@ -51,9 +52,9 @@ import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.progressindicator.LinearProgressIndicator; -import org.chromium.net.ExperimentalCronetEngine; -import org.chromium.net.urlconnection.CronetURLStreamHandlerFactory; +import org.chromium.net.CronetEngine; +import java.io.File; import java.net.URL; import timber.log.Timber; @@ -101,10 +102,17 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe Http.ensureCacheDirs(this); if (!urlFactoryInstalled) { try { - ExperimentalCronetEngine cronetEngine = new ExperimentalCronetEngine.Builder(this).build(); - CronetURLStreamHandlerFactory cronetURLStreamHandlerFactory = new CronetURLStreamHandlerFactory(cronetEngine); + HttpResponseCache cache = HttpResponseCache.getInstalled(); + if (cache == null) { + File cacheDir = new File(getCacheDir(), "http"); + //noinspection ResultOfMethodCallIgnored + cacheDir.mkdirs(); + long cacheSize = 10 * 1024 * 1024; // 10 MiB + HttpResponseCache.install(cacheDir, cacheSize); + } + CronetEngine cronetEngine = new CronetEngine.Builder(this).build(); try { - URL.setURLStreamHandlerFactory(cronetURLStreamHandlerFactory); + URL.setURLStreamHandlerFactory(cronetEngine.createURLStreamHandlerFactory()); } catch ( Error e) { Timber.e("Failed to install Cronet URLStreamHandlerFactory"); @@ -150,8 +158,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe this.moduleList.setAdapter(this.moduleViewAdapter); this.moduleListOnline.setAdapter(this.moduleViewAdapterOnline); this.moduleList.setLayoutManager(new LinearLayoutManager(this)); - // set top padding to 0 - this.moduleList.setPadding(0, 0, 0, 0); this.moduleListOnline.setLayoutManager(new LinearLayoutManager(this)); this.moduleList.setItemViewCacheSize(4); // Default is 2 this.swipeRefreshLayout.setOnRefreshListener(this); @@ -185,8 +191,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe // on the bottom nav, there's a settings item. open the settings activity when it's clicked. BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation); - // set the bottom padding of the main layout to the height of the bottom nav - findViewById(R.id.root_container).setPadding(0, 0, 0, (bottomNavigationView.getHeight() > 0) ? bottomNavigationView.getHeight() : FoxDisplay.dpToPixel(56)); bottomNavigationView.setOnItemSelectedListener(item -> { if (item.getItemId() == R.id.settings_menu_item) { startActivity(new Intent(MainActivity.this, SettingsActivity.class)); @@ -271,9 +275,9 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe // On every preferences change, log the change if debug is enabled if (BuildConfig.DEBUG) { - Timber.d("onCreate: Preferences: %s", MainApplication.getSharedPreferences().getAll()); + Timber.d("onCreate: Preferences: %s", MainApplication.getSharedPreferences("mmm").getAll()); // Log all preferences changes - MainApplication.getSharedPreferences().registerOnSharedPreferenceChangeListener((prefs, key) -> Timber.i("onSharedPreferenceChanged: " + key + " = " + prefs.getAll().get(key))); + MainApplication.getSharedPreferences("mmm").registerOnSharedPreferenceChangeListener((prefs, key) -> Timber.i("onSharedPreferenceChanged: " + key + " = " + prefs.getAll().get(key))); } Timber.i("Scanning for modules!"); @@ -692,7 +696,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe if (BuildConfig.DEBUG) Timber.i("Checking if we need to run setup"); // Check if this is the first launch - SharedPreferences prefs = MainApplication.getSharedPreferences(); + SharedPreferences prefs = MainApplication.getSharedPreferences("mmm"); boolean firstLaunch = prefs.getBoolean("first_time_setup_done", true); if (BuildConfig.DEBUG) Timber.i("First launch: %s", firstLaunch); diff --git a/app/src/main/java/com/fox2code/mmm/MainApplication.java b/app/src/main/java/com/fox2code/mmm/MainApplication.java index 8cb5e5b..59243f7 100644 --- a/app/src/main/java/com/fox2code/mmm/MainApplication.java +++ b/app/src/main/java/com/fox2code/mmm/MainApplication.java @@ -20,6 +20,8 @@ import androidx.core.app.NotificationManagerCompat; import androidx.emoji2.text.DefaultEmojiCompatConfig; import androidx.emoji2.text.EmojiCompat; import androidx.emoji2.text.FontRequestEmojiCompatConfig; +import androidx.security.crypto.EncryptedSharedPreferences; +import androidx.security.crypto.MasterKey; import com.fox2code.foxcompat.app.FoxActivity; import com.fox2code.foxcompat.app.FoxApplication; @@ -34,6 +36,8 @@ import com.google.common.hash.Hashing; import com.topjohnwu.superuser.Shell; import java.io.File; +import java.io.IOException; +import java.security.GeneralSecurityException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; @@ -55,16 +59,16 @@ import timber.log.Timber; @SuppressWarnings("CommentedOutCode") public class MainApplication extends FoxApplication implements androidx.work.Configuration.Provider { - // Warning! Locales that are't exist will crash the app + // Warning! Locales that don't exist will crash the app // Anything that is commented out is supported but the translation is not complete to at least 60% public static final HashSet supportedLocales = new HashSet<>(); private static final String timeFormatString = "dd MMM yyyy"; // Example: 13 july 2001 private static final Shell.Builder shellBuilder; - private static long secret; @SuppressLint("RestrictedApi") // Use FoxProcess wrapper helper. private static final boolean wrapped = !FoxProcessExt.isRootLoader(); public static boolean isOfficial = false; + private static long secret; private static Locale timeFormatLocale = Resources.getSystem().getConfiguration().getLocales().get(0); private static SimpleDateFormat timeFormat = new SimpleDateFormat(timeFormatString, timeFormatLocale); private static SharedPreferences bootSharedPreferences; @@ -116,62 +120,72 @@ public class MainApplication extends FoxApplication implements androidx.work.Con return intent != null && intent.getLongExtra("secret", ~secret) == secret; } - public static SharedPreferences getSharedPreferences() { - return INSTANCE.getSharedPreferences("mmm", MODE_PRIVATE); + public static SharedPreferences getSharedPreferences(String store) { + MasterKey mainKeyAlias; + try { + mainKeyAlias = new MasterKey.Builder(INSTANCE.getApplicationContext()).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(); + } catch (GeneralSecurityException | IOException e) { + throw new RuntimeException(e); + } + try { + return EncryptedSharedPreferences.create(INSTANCE.getApplicationContext(), store, mainKeyAlias, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM); + } catch (GeneralSecurityException | IOException e) { + throw new RuntimeException(e); + } } public static boolean isShowcaseMode() { - return getSharedPreferences().getBoolean("pref_showcase_mode", false); + return getSharedPreferences("mmm").getBoolean("pref_showcase_mode", false); } public static boolean shouldPreventReboot() { - return getSharedPreferences().getBoolean("pref_prevent_reboot", true); + return getSharedPreferences("mmm").getBoolean("pref_prevent_reboot", true); } public static boolean isShowIncompatibleModules() { - return getSharedPreferences().getBoolean("pref_show_incompatible", false); + return getSharedPreferences("mmm").getBoolean("pref_show_incompatible", false); } public static boolean isForceDarkTerminal() { - return getSharedPreferences().getBoolean("pref_force_dark_terminal", false); + return getSharedPreferences("mmm").getBoolean("pref_force_dark_terminal", false); } public static boolean isTextWrapEnabled() { - return getSharedPreferences().getBoolean("pref_wrap_text", false); + return getSharedPreferences("mmm").getBoolean("pref_wrap_text", false); } public static boolean isDohEnabled() { - return getSharedPreferences().getBoolean("pref_dns_over_https", true); + return getSharedPreferences("mmm").getBoolean("pref_dns_over_https", true); } public static boolean isMonetEnabled() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && getSharedPreferences().getBoolean("pref_enable_monet", true); + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && getSharedPreferences("mmm").getBoolean("pref_enable_monet", true); } public static boolean isBlurEnabled() { - return getSharedPreferences().getBoolean("pref_enable_blur", false); + return getSharedPreferences("mmm").getBoolean("pref_enable_blur", false); } public static boolean isDeveloper() { if (BuildConfig.DEBUG) return true; - return getSharedPreferences().getBoolean("developer", false); + return getSharedPreferences("mmm").getBoolean("developer", false); } public static boolean isDisableLowQualityModuleFilter() { - return getSharedPreferences().getBoolean("pref_disable_low_quality_module_filter", false) && isDeveloper(); + return getSharedPreferences("mmm").getBoolean("pref_disable_low_quality_module_filter", false) && isDeveloper(); } public static boolean isUsingMagiskCommand() { - return InstallerInitializer.peekMagiskVersion() >= Constants.MAGISK_VER_CODE_INSTALL_COMMAND && getSharedPreferences().getBoolean("pref_use_magisk_install_command", false) && isDeveloper(); + return InstallerInitializer.peekMagiskVersion() >= Constants.MAGISK_VER_CODE_INSTALL_COMMAND && getSharedPreferences("mmm").getBoolean("pref_use_magisk_install_command", false) && isDeveloper(); } public static boolean isBackgroundUpdateCheckEnabled() { - return !wrapped && getSharedPreferences().getBoolean("pref_background_update_check", true); + return !wrapped && getSharedPreferences("mmm").getBoolean("pref_background_update_check", true); } public static boolean isAndroidacyTestMode() { - return isDeveloper() && getSharedPreferences().getBoolean("pref_androidacy_test_mode", false); + return isDeveloper() && getSharedPreferences("mmm").getBoolean("pref_androidacy_test_mode", false); } public static boolean isFirstBoot() { @@ -179,11 +193,11 @@ public class MainApplication extends FoxApplication implements androidx.work.Con } public static void setHasGottenRootAccess(boolean bool) { - getSharedPreferences().edit().putBoolean("has_root_access", bool).apply(); + getSharedPreferences("mmm").edit().putBoolean("has_root_access", bool).apply(); } public static boolean isCrashReportingEnabled() { - return SentryMain.IS_SENTRY_INSTALLED && getSharedPreferences().getBoolean("pref_crash_reporting", BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING); + return SentryMain.IS_SENTRY_INSTALLED && getSharedPreferences("mmm").getBoolean("pref_crash_reporting", BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING); } public static SharedPreferences getBootSharedPreferences() { @@ -224,7 +238,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con @StyleRes int themeResId; String theme; boolean monet = isMonetEnabled(); - switch (theme = getSharedPreferences().getString("pref_theme", "system")) { + switch (theme = getSharedPreferences("mmm").getString("pref_theme", "system")) { default: Timber.w("Unknown theme id: %s", theme); case "system": @@ -336,15 +350,15 @@ public class MainApplication extends FoxApplication implements androidx.work.Con try { // Get the signature of the key used to sign the app @SuppressLint("PackageManagerGetSignatures") Signature[] signatures = this.getPackageManager().getPackageInfo(this.getPackageName(), PackageManager.GET_SIGNATURES).signatures; - String[] officialSignatureHashArray = new String[]{"7bec7c4462f4aac616612d9f56a023ee3046e83afa956463b5fab547fd0a0be6", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}; + @SuppressWarnings("SpellCheckingInspection") String[] officialSignatureHashArray = new String[]{"7bec7c4462f4aac616612d9f56a023ee3046e83afa956463b5fab547fd0a0be6", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}; String ourSignatureHash = Hashing.sha256().hashBytes(signatures[0].toByteArray()).toString(); isOfficial = Arrays.asList(officialSignatureHashArray).contains(ourSignatureHash); } catch ( PackageManager.NameNotFoundException ignored) { } - SharedPreferences sharedPreferences = MainApplication.getSharedPreferences(); + SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm"); // We are only one process so it's ok to do this - SharedPreferences bootPrefs = MainApplication.bootSharedPreferences = this.getSharedPreferences("mmm_boot", MODE_PRIVATE); + SharedPreferences bootPrefs = MainApplication.bootSharedPreferences = MainApplication.getSharedPreferences("mmm_boot"); long lastBoot = System.currentTimeMillis() - SystemClock.elapsedRealtime(); long lastBootPrefs = bootPrefs.getLong("last_boot", 0); if (lastBootPrefs == 0 || Math.abs(lastBoot - lastBootPrefs) > 100) { @@ -450,7 +464,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con @SuppressLint("RestrictedApi") // view is nullable because it's called from xml public void resetApp() { - // cant show a dialog because android is throwing a fit so heres hoping anybody who calls this method is otherwise confirming that the user wants to reset the app + // cant show a dialog because android is throwing a fit so here's hoping anybody who calls this method is otherwise confirming that the user wants to reset the app Timber.w("Resetting app..."); // recursively delete the app's data ((ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE)).clearApplicationUserData(); @@ -461,10 +475,12 @@ public class MainApplication extends FoxApplication implements androidx.work.Con ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE); List appProcesses = activityManager.getRunningAppProcesses(); if (appProcesses == null) { + Timber.d("appProcesses is null"); return false; } final String packageName = this.getPackageName(); for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { + Timber.d("Process: %s, Importance: %d", appProcess.processName, appProcess.importance); if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) { return true; } @@ -472,6 +488,17 @@ public class MainApplication extends FoxApplication implements androidx.work.Con return false; } + // returns if background execution is restricted + @SuppressWarnings("unused") + public boolean isBackgroundRestricted() { + ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + return am.isBackgroundRestricted(); + } else { + return false; + } + } + private static class ReleaseTree extends Timber.Tree { @Override protected void log(int priority, String tag, @NonNull String message, Throwable t) { diff --git a/app/src/main/java/com/fox2code/mmm/SetupActivity.java b/app/src/main/java/com/fox2code/mmm/SetupActivity.java index 1d751b1..4ae7f24 100644 --- a/app/src/main/java/com/fox2code/mmm/SetupActivity.java +++ b/app/src/main/java/com/fox2code/mmm/SetupActivity.java @@ -65,23 +65,14 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { createFiles(); disableUpdateActivityForFdroidFlavor(); // Set theme - SharedPreferences prefs = MainApplication.getSharedPreferences(); + SharedPreferences prefs = MainApplication.getSharedPreferences("mmm"); switch (prefs.getString("theme", "system")) { - case "light": - setTheme(R.style.Theme_MagiskModuleManager_Monet_Light); - break; - case "dark": - setTheme(R.style.Theme_MagiskModuleManager_Monet_Dark); - break; - case "system": - setTheme(R.style.Theme_MagiskModuleManager_Monet); - break; - case "black": - setTheme(R.style.Theme_MagiskModuleManager_Monet_Black); - break; - case "transparent_light": - setTheme(R.style.Theme_MagiskModuleManager_Transparent_Light); - break; + case "light" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Light); + case "dark" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Dark); + case "system" -> setTheme(R.style.Theme_MagiskModuleManager_Monet); + case "black" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Black); + case "transparent_light" -> + setTheme(R.style.Theme_MagiskModuleManager_Transparent_Light); } ActivitySetupBinding binding = ActivitySetupBinding.inflate(getLayoutInflater()); @@ -143,21 +134,12 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { // Set the theme UiThreadHandler.handler.postDelayed(() -> { switch (prefs.getString("pref_theme", "system")) { - case "light": - setTheme(R.style.Theme_MagiskModuleManager_Monet_Light); - break; - case "dark": - setTheme(R.style.Theme_MagiskModuleManager_Monet_Dark); - break; - case "system": - setTheme(R.style.Theme_MagiskModuleManager_Monet); - break; - case "black": - setTheme(R.style.Theme_MagiskModuleManager_Monet_Black); - break; - case "transparent_light": - setTheme(R.style.Theme_MagiskModuleManager_Transparent_Light); - break; + case "light" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Light); + case "dark" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Dark); + case "system" -> setTheme(R.style.Theme_MagiskModuleManager_Monet); + case "black" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Black); + case "transparent_light" -> + setTheme(R.style.Theme_MagiskModuleManager_Transparent_Light); } // restart the activity because switching to transparent pisses the rendering engine off Intent intent = new Intent(this, SetupActivity.class); @@ -249,23 +231,14 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { public Resources.Theme getTheme() { Resources.Theme theme = super.getTheme(); // Set the theme - SharedPreferences prefs = MainApplication.getSharedPreferences(); + SharedPreferences prefs = MainApplication.getSharedPreferences("mmm"); switch (prefs.getString("pref_theme", "system")) { - case "light": - theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet_Light, true); - break; - case "dark": - theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet_Dark, true); - break; - case "system": - theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet, true); - break; - case "black": - theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet_Black, true); - break; - case "transparent_light": - theme.applyStyle(R.style.Theme_MagiskModuleManager_Transparent_Light, true); - break; + case "light" -> theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet_Light, true); + case "dark" -> theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet_Dark, true); + case "system" -> theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet, true); + case "black" -> theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet_Black, true); + case "transparent_light" -> + theme.applyStyle(R.style.Theme_MagiskModuleManager_Transparent_Light, true); } return theme; } 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 dc6e885..61102fc 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java @@ -237,21 +237,11 @@ public final class AndroidacyActivity extends FoxActivity { public boolean onConsoleMessage(ConsoleMessage consoleMessage) { if (BuildConfig.DEBUG) { switch (consoleMessage.messageLevel()) { - case TIP: - Timber.v(consoleMessage.message()); - break; - case LOG: - Timber.i(consoleMessage.message()); - break; - case WARNING: - Timber.w(consoleMessage.message()); - break; - case ERROR: - Timber.e(consoleMessage.message()); - break; - case DEBUG: - Timber.d(consoleMessage.message()); - break; + case TIP -> Timber.v(consoleMessage.message()); + case LOG -> Timber.i(consoleMessage.message()); + case WARNING -> Timber.w(consoleMessage.message()); + case ERROR -> Timber.e(consoleMessage.message()); + case DEBUG -> Timber.d(consoleMessage.message()); } } return true; 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 e4a66a5..a514f4f 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java @@ -43,7 +43,7 @@ import timber.log.Timber; @SuppressWarnings("KotlinInternalInJava") public final class AndroidacyRepoData extends RepoData { - public static String token = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", null); + public static String token = MainApplication.getSharedPreferences("androidacy").getString("pref_androidacy_api_token", null); static { HttpUrl.Builder OK_HTTP_URL_BUILDER = new HttpUrl.Builder().scheme("https"); @@ -54,7 +54,7 @@ public final class AndroidacyRepoData extends RepoData { @SuppressWarnings("unused") public final String ClientID = BuildConfig.ANDROIDACY_CLIENT_ID; - public final SharedPreferences cachedPreferences = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0); + public final SharedPreferences cachedPreferences = MainApplication.getSharedPreferences("androidacy"); private final boolean testMode; private final String host; public String[][] userInfo = new String[][]{{"role", null}, {"permissions", null}}; @@ -88,12 +88,11 @@ public final class AndroidacyRepoData extends RepoData { // limiting and fraud detection. public static String generateDeviceId() throws NoSuchAlgorithmException { // Try to get the device ID from the shared preferences - SharedPreferences sharedPreferences = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0); + SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("androidacy"); String deviceIdPref = sharedPreferences.getString("device_id", null); if (deviceIdPref != null) { return deviceIdPref; } else { - // AAAA we're fingerprintiiiiing // Really not that scary - just hashes some device info. We can't even get the info // we originally hashed, so it's not like we can use it to track you. String deviceId = null; @@ -176,7 +175,7 @@ public final class AndroidacyRepoData extends RepoData { protected boolean prepare() throws NoSuchAlgorithmException { // If ANDROIDACY_CLIENT_ID is not set or is empty, disable this repo and return if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) { - SharedPreferences.Editor editor = MainApplication.getSharedPreferences().edit(); + SharedPreferences.Editor editor = MainApplication.getSharedPreferences("mmm").edit(); editor.putBoolean("pref_androidacy_repo_enabled", false); editor.apply(); return false; @@ -213,7 +212,7 @@ public final class AndroidacyRepoData extends RepoData { long time = System.currentTimeMillis(); if (this.androidacyBlockade > time) return true; // fake it till you make it. Basically, - // don't fail just becaue we're rate limited. API and web rate limits are different. + // don't fail just because we're rate limited. API and web rate limits are different. this.androidacyBlockade = time + 30_000L; try { if (token == null) { @@ -267,7 +266,7 @@ public final class AndroidacyRepoData extends RepoData { return false; } // Save token to shared preference - SharedPreferences.Editor editor = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit(); + SharedPreferences.Editor editor = MainApplication.getSharedPreferences("androidacy").edit(); editor.putString("pref_androidacy_api_token", token); editor.apply(); } catch ( 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 c164cbb..91871f6 100644 --- a/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java +++ b/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java @@ -59,11 +59,15 @@ public class BackgroundUpdateChecker extends Worker { static void doCheck(Context context) { // first, check if the user has enabled background update checking - if (!MainApplication.getSharedPreferences().getBoolean("pref_background_update_check", false)) { + if (!MainApplication.getSharedPreferences("mmm").getBoolean("pref_background_update_check", false)) { + return; + } + if (MainApplication.getINSTANCE().isInForeground()) { + // don't check if app is in foreground, this is a background check return; } // next, check if user requires wifi - if (MainApplication.getSharedPreferences().getBoolean("pref_background_update_check_wifi", true)) { + if (MainApplication.getSharedPreferences("mmm").getBoolean("pref_background_update_check_wifi", true)) { // check if wifi is connected ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); Network networkInfo = connectivityManager.getActiveNetwork(); @@ -74,7 +78,6 @@ public class BackgroundUpdateChecker extends Worker { } // post checking notification if notofiications are enabled if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { - if (!MainApplication.getINSTANCE().isInForeground()) { NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); notificationManager.createNotificationChannel(new NotificationChannelCompat.Builder(NOTIFICATION_CHANNEL_ID_ONGOING, NotificationManagerCompat.IMPORTANCE_LOW).setName(context.getString(R.string.notification_channel_category_background_update)).setDescription(context.getString(R.string.notification_channel_category_background_update_description)).setGroup(NOTFIICATION_GROUP).build()); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID); @@ -89,7 +92,6 @@ public class BackgroundUpdateChecker extends Worker { builder.setContentTitle(context.getString(R.string.notification_channel_background_update)); builder.setContentText(context.getString(R.string.notification_channel_background_update_description)); notificationManager.notify(NOTIFICATION_ID_ONGOING, builder.build()); - } } Thread.currentThread().setPriority(2); ModuleManager.getINSTANCE().scanAsync(); @@ -104,7 +106,7 @@ public class BackgroundUpdateChecker extends Worker { continue; // exclude all modules with id's stored in the pref pref_background_update_check_excludes try { - if (MainApplication.getSharedPreferences().getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id)) + if (MainApplication.getSharedPreferences("mmm").getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id)) continue; } catch ( Exception ignored) { @@ -124,7 +126,7 @@ public class BackgroundUpdateChecker extends Worker { } }); // check for app updates - if (MainApplication.getSharedPreferences().getBoolean("pref_background_update_check_app", false)) { + if (MainApplication.getSharedPreferences("mmm").getBoolean("pref_background_update_check_app", false)) { try { boolean shouldUpdate = AppUpdateManager.getAppUpdateManager().checkUpdate(true); if (shouldUpdate) { @@ -206,7 +208,7 @@ public class BackgroundUpdateChecker extends Worker { public static void onMainActivityCreate(Context context) { // Refuse to run if first_launch pref is not false - if (MainApplication.getSharedPreferences().getBoolean("first_time_setup_done", true)) + if (MainApplication.getSharedPreferences("mmm").getBoolean("first_time_setup_done", true)) return; // create notification channel group if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { diff --git a/app/src/main/java/com/fox2code/mmm/manager/ModuleInfo.java b/app/src/main/java/com/fox2code/mmm/manager/ModuleInfo.java index d4790d6..97674c7 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/ModuleInfo.java +++ b/app/src/main/java/com/fox2code/mmm/manager/ModuleInfo.java @@ -25,7 +25,7 @@ public class ModuleInfo { private static final int FLAG_FENCE = 0x10000000; // Should never be set // Magisk standard - public final String id; + public String id; public String name; public String version; public long versionCode; 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 1e55103..976e989 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java +++ b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java @@ -78,13 +78,13 @@ public final class ModuleManager extends SyncManager { for (String module : modules) { if (!new SuFile("/data/adb/modules/" + module).isDirectory()) continue; // Ignore non directory files inside modules folder - if (BuildConfig.DEBUG) Timber.d(module); LocalModuleInfo moduleInfo = moduleInfos.get(module); // next, merge the module info with a record from ModuleListCache if it exists RealmConfiguration realmConfiguration; // get all dirs under the realms/repos/ dir under app's data dir File cacheRoot = new File(MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/").toURI()); ModuleListCache moduleListCache; + boolean foundCache = false; for (File dir : Objects.requireNonNull(cacheRoot.listFiles())) { if (dir.isDirectory()) { // if the dir name matches the module name, use it as the cache dir @@ -95,35 +95,21 @@ public final class ModuleManager extends SyncManager { Timber.d("Looking for cache for %s out of %d", module, realm.where(ModuleListCache.class).count()); moduleListCache = realm.where(ModuleListCache.class).equalTo("codename", module).findFirst(); if (moduleListCache != null) { + foundCache = true; Timber.d("Found cache for %s", module); - moduleInfo = new LocalModuleInfo(module); - assert moduleListCache.getAuthor() != null; - assert moduleListCache.getDescription() != null; - assert moduleListCache.getSupport() != null; - assert moduleListCache.getConfig() != null; - assert moduleListCache.getName() != null; - moduleInfo.author = moduleListCache.getAuthor(); - moduleInfo.description = moduleListCache.getDescription() + " (from cache)"; - moduleInfo.support = moduleListCache.getSupport(); - moduleInfo.config = moduleListCache.getConfig(); + // get module info from cache + if (moduleInfo == null) { + moduleInfo = new LocalModuleInfo(module); + } moduleInfo.name = moduleListCache.getName(); - moduleInfo.minApi = moduleListCache.getMinApi(); - moduleInfo.maxApi = moduleListCache.getMaxApi(); - moduleInfo.minMagisk = moduleListCache.getMinMagisk(); + moduleInfo.description = moduleListCache.getDescription() + " (cached)"; + moduleInfo.author = moduleListCache.getAuthor(); moduleInfo.safe = moduleListCache.isSafe(); + moduleInfo.support = moduleListCache.getSupport(); + moduleInfo.donate = moduleListCache.getDonate(); moduleInfos.put(module, moduleInfo); - // This should not really happen, but let's handles theses cases anyway - moduleInfo.flags |= ModuleInfo.FLAG_MODULE_UPDATING_ONLY; + realm.close(); break; - } else { - Timber.d("No cache for %s", module); - // just for shits n giggles, log the codename of all the modules in the cache - Iterator iterator = realm.where(ModuleListCache.class).findAll().iterator(); - StringBuilder sb = new StringBuilder(); - while (iterator.hasNext()) { - sb.append(iterator.next().getCodename()).append(", "); - } - Timber.d("Cache contains: %s", sb.toString()); } } } diff --git a/app/src/main/java/com/fox2code/mmm/module/ModuleViewListBuilder.java b/app/src/main/java/com/fox2code/mmm/module/ModuleViewListBuilder.java index 4abf527..3b86af3 100644 --- a/app/src/main/java/com/fox2code/mmm/module/ModuleViewListBuilder.java +++ b/app/src/main/java/com/fox2code/mmm/module/ModuleViewListBuilder.java @@ -183,7 +183,6 @@ public class ModuleViewListBuilder { final ArrayList moduleHolders; final int newNotificationsLen; final boolean first; - final ModuleHolder[] headerFooter = new ModuleHolder[2]; try { synchronized (this.updateLock) { // Build start @@ -236,11 +235,11 @@ public class ModuleViewListBuilder { } moduleHolders.sort(this.moduleSorter); // Header is always first - moduleHolders.add(0, headerFooter[0] = - new ModuleHolder(this.headerPx, true)); + //moduleHolders.add(0, headerFooter[0] = + // new ModuleHolder(this.headerPx / 2, true)); // Footer is always last - moduleHolders.add(headerFooter[1] = - new ModuleHolder(this.footerPx, false)); + //moduleHolders.add(headerFooter[1] = + // new ModuleHolder(this.footerPx * 2, false)); Timber.i("Got " + moduleHolders.size() + " entries!"); // Build end } @@ -281,10 +280,6 @@ public class ModuleViewListBuilder { int oldLen = moduleViewAdapter.moduleHolders.size(); moduleViewAdapter.moduleHolders.clear(); moduleViewAdapter.moduleHolders.addAll(moduleHolders); - synchronized (this.updateLock) { - headerFooter[0].footerPx = this.headerPx; - headerFooter[1].footerPx = this.footerPx; - } if (oldNotificationsLen != newNotificationsLen || !oldNotifications.equals(this.notifications)) { notifySizeChanged(moduleViewAdapter, 0, @@ -306,8 +301,6 @@ public class ModuleViewListBuilder { if (isTop) moduleList.scrollToPosition(0); if (isBottom) moduleList.scrollToPosition(newLen); this.updateInsets = () -> { - headerFooter[0].footerPx = this.headerPx; - headerFooter[1].footerPx = this.footerPx; notifySizeChanged(moduleViewAdapter, 0, 1, 1); notifySizeChanged(moduleViewAdapter, moduleHolders.size(), 1, 1); 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 8b38224..431b15d 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java @@ -384,7 +384,7 @@ public class RepoData extends XRepo { long diff = currentTime - lastUpdate; long diffMinutes = diff / (60 * 1000) % 60; Timber.d("Repo " + this.id + " updated: " + diffMinutes + " minutes ago"); - return diffMinutes > (BuildConfig.DEBUG ? 5 : 20); + return diffMinutes > (BuildConfig.DEBUG ? 15 : 20); } else { Timber.d("Repo " + this.id + " should update could not find repo in database"); return true; diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java b/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java index f9c2048..877fb77 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java @@ -295,7 +295,6 @@ public class RepoUpdater { if (realm.isInTransaction()) { realm.cancelTransaction(); } - Timber.d("Inserting module %s to realm", id); // create a realm object and insert or update it // add everything to the realm object realm.beginTransaction(); @@ -346,6 +345,7 @@ public class RepoUpdater { success.set(true); // get unix timestamp of current time int currentTime = (int) (System.currentTimeMillis() / 1000); + Timber.d("Updating lastUpdate for repo %s to %s which is %s seconds ago", this.repoData.id, currentTime, (currentTime - repoListCache.getLastUpdate())); repoListCache.setLastUpdate(currentTime); } else { Timber.w("Failed to update lastUpdate for repo %s", this.repoData.id); diff --git a/app/src/main/java/com/fox2code/mmm/settings/EncryptedPreferenceDataStore.java b/app/src/main/java/com/fox2code/mmm/settings/EncryptedPreferenceDataStore.java new file mode 100644 index 0000000..33c1f6e --- /dev/null +++ b/app/src/main/java/com/fox2code/mmm/settings/EncryptedPreferenceDataStore.java @@ -0,0 +1,108 @@ +package com.fox2code.mmm.settings; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.annotation.Nullable; +import androidx.preference.PreferenceDataStore; +import androidx.security.crypto.EncryptedSharedPreferences; +import androidx.security.crypto.MasterKey; + +import com.fox2code.mmm.MainApplication; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Set; + +public class EncryptedPreferenceDataStore extends PreferenceDataStore { + + private static final String CONFIG_FILE_NAME = "mmm"; + @SuppressLint("StaticFieldLeak") + private static EncryptedPreferenceDataStore mInstance; + private SharedPreferences mSharedPreferences; + + private EncryptedPreferenceDataStore(Context context) { + try { + MasterKey mainKeyAlias; + try { + mainKeyAlias = new MasterKey.Builder(MainApplication.getINSTANCE().getApplicationContext()).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(); + } catch (GeneralSecurityException | IOException e) { + throw new RuntimeException(e); + } + mSharedPreferences = EncryptedSharedPreferences.create(MainApplication.getINSTANCE().getApplicationContext(), "mmm", mainKeyAlias, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM); + } catch (Exception e) { + // Fallback + mSharedPreferences = context.getSharedPreferences(CONFIG_FILE_NAME, Context.MODE_PRIVATE); + } + } + + public static PreferenceDataStore getInstance() { + if (mInstance == null) { + mInstance = new EncryptedPreferenceDataStore(MainApplication.getINSTANCE().getApplicationContext()); + } + return mInstance; + } + + @Override + public void putString(String key, @Nullable String value) { + mSharedPreferences.edit().putString(key, value).apply(); + } + + @Override + public void putStringSet(String key, @Nullable Set values) { + mSharedPreferences.edit().putStringSet(key, values).apply(); + } + + @Override + public void putInt(String key, int value) { + mSharedPreferences.edit().putInt(key, value).apply(); + } + + @Override + public void putLong(String key, long value) { + mSharedPreferences.edit().putLong(key, value).apply(); + } + + @Override + public void putFloat(String key, float value) { + mSharedPreferences.edit().putFloat(key, value).apply(); + } + + @Override + public void putBoolean(String key, boolean value) { + mSharedPreferences.edit().putBoolean(key, value).apply(); + } + + @Nullable + @Override + public String getString(String key, @Nullable String defValue) { + return mSharedPreferences.getString(key, defValue); + } + + @Nullable + @Override + public Set getStringSet(String key, @Nullable Set defValues) { + return mSharedPreferences.getStringSet(key, defValues); + } + + @Override + public int getInt(String key, int defValue) { + return mSharedPreferences.getInt(key, defValue); + } + + @Override + public long getLong(String key, long defValue) { + return mSharedPreferences.getLong(key, defValue); + } + + @Override + public float getFloat(String key, float defValue) { + return mSharedPreferences.getFloat(key, defValue); + } + + @Override + public boolean getBoolean(String key, boolean defValue) { + return mSharedPreferences.getBoolean(key, defValue); + } +} 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 5691099..e02d636 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -6,7 +6,6 @@ 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; import android.content.ClipData; import android.content.ClipboardManager; @@ -40,11 +39,11 @@ import androidx.preference.ListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceManager; import androidx.preference.SwitchPreferenceCompat; import androidx.preference.TwoStatePreference; import com.fox2code.foxcompat.app.FoxActivity; -import com.fox2code.foxcompat.app.internal.FoxProcessExt; import com.fox2code.foxcompat.view.FoxDisplay; import com.fox2code.foxcompat.view.FoxViewCompat; import com.fox2code.mmm.AppUpdateManager; @@ -206,7 +205,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { @Override @SuppressWarnings("ConstantConditions") public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - getPreferenceManager().setSharedPreferencesName("mmm"); + PreferenceManager preferenceManager = getPreferenceManager(); + preferenceManager.setPreferenceDataStore(EncryptedPreferenceDataStore.getInstance()); setPreferencesFromResource(R.xml.root_preferences, rootKey); applyMaterial3(getPreferenceScreen()); // add bottom navigation bar to the settings @@ -475,7 +475,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { // set the box to unchecked ((SwitchPreferenceCompat) backgroundUpdateCheck).setChecked(false); // ensure that the preference is false - MainApplication.getSharedPreferences().edit().putBoolean("pref_background_update_check", false).apply(); + MainApplication.getSharedPreferences("mmm").edit().putBoolean("pref_background_update_check", false).apply(); new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.permission_notification_title).setMessage(R.string.permission_notification_message).setPositiveButton(R.string.ok, (dialog, which) -> { // Open the app settings Intent intent = new Intent(); @@ -512,7 +512,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { int i = 0; for (LocalModuleInfo localModuleInfo : localModuleInfos) { moduleNames[i] = localModuleInfo.name; - SharedPreferences sharedPreferences = MainApplication.getSharedPreferences(); + SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm"); // 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 @@ -522,7 +522,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { } 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(); + SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm"); Set stringSet = new HashSet<>(sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>())); // get id from name String id; @@ -610,11 +610,11 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { if (devModeStep == 2) { devModeStep = 0; if (MainApplication.isDeveloper() && !BuildConfig.DEBUG) { - MainApplication.getSharedPreferences().edit().putBoolean("developer", false).apply(); + MainApplication.getSharedPreferences("mmm").edit().putBoolean("developer", false).apply(); Toast.makeText(getContext(), // Tell the user something changed R.string.dev_mode_disabled, Toast.LENGTH_SHORT).show(); } else { - MainApplication.getSharedPreferences().edit().putBoolean("developer", true).apply(); + MainApplication.getSharedPreferences("mmm").edit().putBoolean("developer", true).apply(); Toast.makeText(getContext(), // Tell the user something changed R.string.dev_mode_enabled, Toast.LENGTH_SHORT).show(); } @@ -693,7 +693,9 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { } // Share logs Intent shareIntent = new Intent(); + // create a new intent and grantUriPermission to the file provider shareIntent.setAction(Intent.ACTION_SEND); + shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); shareIntent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(requireContext(), BuildConfig.APPLICATION_ID + ".file-provider", logsFile)); shareIntent.setType("text/plain"); startActivity(Intent.createChooser(shareIntent, getString(R.string.share_logs))); @@ -749,25 +751,6 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { findPreference("pref_pkg_info").setSummary(pkgInfo); } - @SuppressLint("RestrictedApi") - private String getRepackageState() { - Application initialApplication = null; - try { - initialApplication = FoxProcessExt.getInitialApplication(); - } catch ( - Exception ignored) { - } - String realPackageName; - if (initialApplication != null) { - realPackageName = initialApplication.getPackageName(); - } else { - realPackageName = this.requireContext().getPackageName(); - } - if (BuildConfig.APPLICATION_ID.equals(realPackageName)) - return ""; - return "\n" + this.getString(FoxProcessExt.isRootLoader() ? R.string.repackaged_as : R.string.wrapped_from) + realPackageName; - } - private void openFragment(Fragment fragment, @StringRes int title) { FoxActivity compatActivity = getFoxActivity(this); compatActivity.setOnBackPressedCallback(this); @@ -802,8 +785,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { * namely, from neo wellbeing */ public static void applyMaterial3(Preference p) { - if (p instanceof PreferenceGroup) { - PreferenceGroup pg = (PreferenceGroup) p; + if (p instanceof PreferenceGroup pg) { for (int i = 0; i < pg.getPreferenceCount(); i++) { applyMaterial3(pg.getPreference(i)); } @@ -827,7 +809,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { // Use MaterialAlertDialogBuilder new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.warning).setCancelable(false).setMessage(R.string.androidacy_test_mode_warning).setPositiveButton(android.R.string.ok, (dialog, which) -> { // User clicked OK button - MainApplication.getSharedPreferences().edit().putBoolean("androidacy_test_mode", true).apply(); + MainApplication.getSharedPreferences("mmm").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); @@ -845,10 +827,10 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { 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(); + MainApplication.getSharedPreferences("mmm").edit().putBoolean("androidacy_test_mode", false).apply(); }).show(); } else { - MainApplication.getSharedPreferences().edit().putBoolean("androidacy_test_mode", false).apply(); + MainApplication.getSharedPreferences("mmm").edit().putBoolean("androidacy_test_mode", false).apply(); // Show dialog to restart app with ok button new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.warning).setCancelable(false).setMessage(R.string.androidacy_test_mode_disable_warning).setNeutralButton(android.R.string.ok, (dialog, which) -> { // User clicked OK button 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 0473e3e..660b3f2 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,6 +22,7 @@ import java.util.Locale; import timber.log.Timber; +@SuppressWarnings("SpellCheckingInspection") public enum PropUtils { ; private static final HashMap moduleSupportsFallbacks = new HashMap<>(); @@ -122,6 +123,10 @@ public enum PropUtils { continue; String key = line.substring(0, index); String value = line.substring(index + 1).trim(); + // check if field is defined on the moduleInfo object we are reading + if (moduleInfo.toString().contains(key)) { + continue; + } // name and id have their own implementation if (isInvalidValue(key)) { if (local) { @@ -144,7 +149,8 @@ public enum PropUtils { if (local) { invalid = true; break; - } throw new IOException("Invalid module id!"); + } + throw new IOException("Invalid module id!"); } readId = true; if (!moduleInfo.id.equals(value)) { @@ -167,7 +173,8 @@ public enum PropUtils { if (local) { invalid = true; break; - } throw new IOException("Invalid module name!"); + } + throw new IOException("Invalid module name!"); } readName = true; moduleInfo.name = value; 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 8a59514..1c37b45 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 @@ -28,12 +28,12 @@ public class SentryMain { @SuppressLint({"RestrictedApi", "UnspecifiedImmutableFlag"}) public static void initialize(final MainApplication mainApplication) { // If first_launch pref is not false, refuse to initialize Sentry - SharedPreferences sharedPreferences = MainApplication.getSharedPreferences(); + SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm"); if (sharedPreferences.getBoolean("first_time_setup_done", true)) { return; } Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { - SharedPreferences.Editor editor = mainApplication.getSharedPreferences("sentry", Context.MODE_PRIVATE).edit(); + SharedPreferences.Editor editor = MainApplication.getSharedPreferences("sentry").edit(); editor.putString("lastExitReason", "crash"); editor.putLong("lastExitTime", System.currentTimeMillis()); editor.apply(); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index b0c124a..065aa7c 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -11,10 +11,10 @@ @@ -45,6 +47,7 @@ android:scaleY="2" app:showAnimationBehavior="outward" app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/swipe_refresh" /> @@ -56,7 +59,7 @@ android:layout_marginVertical="8dp" android:filterTouchesWhenObscured="true" android:gravity="bottom|end" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toTopOf="@id/bottom_navigation" app:layout_constraintRight_toRightOf="parent" app:layout_fitsSystemWindowsInsets="bottom" tools:ignore="RtlHardcoded"> @@ -105,6 +108,7 @@ android:visibility="visible" app:compatShadowEnabled="true" app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@+id/swipe_refresh" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:menu="@menu/bottom_nav_menu" /> diff --git a/build.gradle b/build.gradle index 122063d..d555aba 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ buildscript { project.ext.sentry_version = "6.14.0" dependencies { classpath 'com.android.tools.build:gradle:7.4.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10" classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${latestAboutLibsRelease}" classpath 'io.sentry:sentry-android:6.14.0' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 074a5bf..2e5f7c7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sun Jun 05 10:40:53 EDT 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-rc-1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME \ No newline at end of file