encryption for sensitive data

everything we can reasonably encrypt is now encrypted, since the app is a rather attractive attack vector for attackers. this continues the work started in v1.1 to secure the app.

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/287/head
androidacy-user 1 year ago
parent 17cf8baee9
commit 5029486d4f

@ -313,9 +313,8 @@ dependencies {
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10' implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10'
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:5.0.0-alpha.10' implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:5.0.0-alpha.10'
// Chromium cronet from androidacy // Chromium cronet from androidacy
implementation 'com.androidacy:cronet-common:109.0.5414.75' implementation 'org.chromium.net:cronet-embedded:108.5359.79'
implementation 'com.androidacy:cronet-native:109.0.5414.75'
// Force prefer our own version of Cronet
implementation 'com.github.topjohnwu.libsu:io:5.0.1' implementation 'com.github.topjohnwu.libsu:io:5.0.1'
implementation 'com.github.Fox2Code:RosettaX:1.0.9' implementation 'com.github.Fox2Code:RosettaX:1.0.9'
implementation 'com.github.Fox2Code:AndroidANSI:1.0.1' implementation 'com.github.Fox2Code:AndroidANSI:1.0.1'
@ -342,12 +341,11 @@ dependencies {
implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.core:core-ktx:1.9.0'
// timber // timber
implementation 'com.jakewharton.timber:timber:5.0.1' implementation 'com.jakewharton.timber:timber:5.0.1'
// ksp // 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" implementation "androidx.security:security-crypto:1.1.0-alpha04"
} }
@ -366,7 +364,7 @@ if (hasSentryConfig) {
} }
android { android {
ndkVersion '25.1.8937393' ndkVersion '25.2.9519653'
dependenciesInfo { dependenciesInfo {
includeInApk false includeInApk false
includeInBundle false includeInBundle false
@ -379,7 +377,7 @@ android {
jvmTarget = JavaVersion.VERSION_17 jvmTarget = JavaVersion.VERSION_17
} }
//noinspection GrDeprecatedAPIUsage //noinspection GrDeprecatedAPIUsage
buildToolsVersion '33.0.1' buildToolsVersion '33.0.2'
} }

@ -56,6 +56,7 @@
android:testOnly="false" android:testOnly="false"
android:theme="@style/Theme.MagiskModuleManager" android:theme="@style/Theme.MagiskModuleManager"
android:usesCleartextTraffic="false" android:usesCleartextTraffic="false"
android:extractNativeLibs="true"
tools:ignore="ManifestResource" tools:ignore="ManifestResource"
tools:replace="android:supportsRtl" tools:replace="android:supportsRtl"
tools:targetApi="tiramisu"> tools:targetApi="tiramisu">

@ -14,6 +14,7 @@ import android.content.res.Resources;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.net.http.HttpResponseCache;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; 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.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.progressindicator.LinearProgressIndicator; import com.google.android.material.progressindicator.LinearProgressIndicator;
import org.chromium.net.ExperimentalCronetEngine; import org.chromium.net.CronetEngine;
import org.chromium.net.urlconnection.CronetURLStreamHandlerFactory;
import java.io.File;
import java.net.URL; import java.net.URL;
import timber.log.Timber; import timber.log.Timber;
@ -101,10 +102,17 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
Http.ensureCacheDirs(this); Http.ensureCacheDirs(this);
if (!urlFactoryInstalled) { if (!urlFactoryInstalled) {
try { try {
ExperimentalCronetEngine cronetEngine = new ExperimentalCronetEngine.Builder(this).build(); HttpResponseCache cache = HttpResponseCache.getInstalled();
CronetURLStreamHandlerFactory cronetURLStreamHandlerFactory = new CronetURLStreamHandlerFactory(cronetEngine); 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 { try {
URL.setURLStreamHandlerFactory(cronetURLStreamHandlerFactory); URL.setURLStreamHandlerFactory(cronetEngine.createURLStreamHandlerFactory());
} catch ( } catch (
Error e) { Error e) {
Timber.e("Failed to install Cronet URLStreamHandlerFactory"); 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.moduleList.setAdapter(this.moduleViewAdapter);
this.moduleListOnline.setAdapter(this.moduleViewAdapterOnline); this.moduleListOnline.setAdapter(this.moduleViewAdapterOnline);
this.moduleList.setLayoutManager(new LinearLayoutManager(this)); 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.moduleListOnline.setLayoutManager(new LinearLayoutManager(this));
this.moduleList.setItemViewCacheSize(4); // Default is 2 this.moduleList.setItemViewCacheSize(4); // Default is 2
this.swipeRefreshLayout.setOnRefreshListener(this); 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. // on the bottom nav, there's a settings item. open the settings activity when it's clicked.
BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation); 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 -> { bottomNavigationView.setOnItemSelectedListener(item -> {
if (item.getItemId() == R.id.settings_menu_item) { if (item.getItemId() == R.id.settings_menu_item) {
startActivity(new Intent(MainActivity.this, SettingsActivity.class)); 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 // On every preferences change, log the change if debug is enabled
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Timber.d("onCreate: Preferences: %s", MainApplication.getSharedPreferences().getAll()); Timber.d("onCreate: Preferences: %s", MainApplication.getSharedPreferences("mmm").getAll());
// Log all preferences changes // 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!"); Timber.i("Scanning for modules!");
@ -692,7 +696,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)
Timber.i("Checking if we need to run setup"); Timber.i("Checking if we need to run setup");
// Check if this is the first launch // 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); boolean firstLaunch = prefs.getBoolean("first_time_setup_done", true);
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)
Timber.i("First launch: %s", firstLaunch); Timber.i("First launch: %s", firstLaunch);

@ -20,6 +20,8 @@ import androidx.core.app.NotificationManagerCompat;
import androidx.emoji2.text.DefaultEmojiCompatConfig; import androidx.emoji2.text.DefaultEmojiCompatConfig;
import androidx.emoji2.text.EmojiCompat; import androidx.emoji2.text.EmojiCompat;
import androidx.emoji2.text.FontRequestEmojiCompatConfig; 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.FoxActivity;
import com.fox2code.foxcompat.app.FoxApplication; import com.fox2code.foxcompat.app.FoxApplication;
@ -34,6 +36,8 @@ import com.google.common.hash.Hashing;
import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.Shell;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
@ -55,16 +59,16 @@ import timber.log.Timber;
@SuppressWarnings("CommentedOutCode") @SuppressWarnings("CommentedOutCode")
public class MainApplication extends FoxApplication implements androidx.work.Configuration.Provider { 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% // Anything that is commented out is supported but the translation is not complete to at least 60%
public static final HashSet<String> supportedLocales = new HashSet<>(); public static final HashSet<String> supportedLocales = new HashSet<>();
private static final String timeFormatString = "dd MMM yyyy"; // Example: 13 july 2001 private static final String timeFormatString = "dd MMM yyyy"; // Example: 13 july 2001
private static final Shell.Builder shellBuilder; private static final Shell.Builder shellBuilder;
private static long secret;
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
// Use FoxProcess wrapper helper. // Use FoxProcess wrapper helper.
private static final boolean wrapped = !FoxProcessExt.isRootLoader(); private static final boolean wrapped = !FoxProcessExt.isRootLoader();
public static boolean isOfficial = false; public static boolean isOfficial = false;
private static long secret;
private static Locale timeFormatLocale = Resources.getSystem().getConfiguration().getLocales().get(0); private static Locale timeFormatLocale = Resources.getSystem().getConfiguration().getLocales().get(0);
private static SimpleDateFormat timeFormat = new SimpleDateFormat(timeFormatString, timeFormatLocale); private static SimpleDateFormat timeFormat = new SimpleDateFormat(timeFormatString, timeFormatLocale);
private static SharedPreferences bootSharedPreferences; 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; return intent != null && intent.getLongExtra("secret", ~secret) == secret;
} }
public static SharedPreferences getSharedPreferences() { public static SharedPreferences getSharedPreferences(String store) {
return INSTANCE.getSharedPreferences("mmm", MODE_PRIVATE); 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() { public static boolean isShowcaseMode() {
return getSharedPreferences().getBoolean("pref_showcase_mode", false); return getSharedPreferences("mmm").getBoolean("pref_showcase_mode", false);
} }
public static boolean shouldPreventReboot() { public static boolean shouldPreventReboot() {
return getSharedPreferences().getBoolean("pref_prevent_reboot", true); return getSharedPreferences("mmm").getBoolean("pref_prevent_reboot", true);
} }
public static boolean isShowIncompatibleModules() { public static boolean isShowIncompatibleModules() {
return getSharedPreferences().getBoolean("pref_show_incompatible", false); return getSharedPreferences("mmm").getBoolean("pref_show_incompatible", false);
} }
public static boolean isForceDarkTerminal() { 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() { public static boolean isTextWrapEnabled() {
return getSharedPreferences().getBoolean("pref_wrap_text", false); return getSharedPreferences("mmm").getBoolean("pref_wrap_text", false);
} }
public static boolean isDohEnabled() { 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() { 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() { public static boolean isBlurEnabled() {
return getSharedPreferences().getBoolean("pref_enable_blur", false); return getSharedPreferences("mmm").getBoolean("pref_enable_blur", false);
} }
public static boolean isDeveloper() { public static boolean isDeveloper() {
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)
return true; return true;
return getSharedPreferences().getBoolean("developer", false); return getSharedPreferences("mmm").getBoolean("developer", false);
} }
public static boolean isDisableLowQualityModuleFilter() { 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() { 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() { 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() { 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() { public static boolean isFirstBoot() {
@ -179,11 +193,11 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
} }
public static void setHasGottenRootAccess(boolean bool) { 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() { 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() { public static SharedPreferences getBootSharedPreferences() {
@ -224,7 +238,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
@StyleRes int themeResId; @StyleRes int themeResId;
String theme; String theme;
boolean monet = isMonetEnabled(); boolean monet = isMonetEnabled();
switch (theme = getSharedPreferences().getString("pref_theme", "system")) { switch (theme = getSharedPreferences("mmm").getString("pref_theme", "system")) {
default: default:
Timber.w("Unknown theme id: %s", theme); Timber.w("Unknown theme id: %s", theme);
case "system": case "system":
@ -336,15 +350,15 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
try { try {
// Get the signature of the key used to sign the app // Get the signature of the key used to sign the app
@SuppressLint("PackageManagerGetSignatures") Signature[] signatures = this.getPackageManager().getPackageInfo(this.getPackageName(), PackageManager.GET_SIGNATURES).signatures; @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(); String ourSignatureHash = Hashing.sha256().hashBytes(signatures[0].toByteArray()).toString();
isOfficial = Arrays.asList(officialSignatureHashArray).contains(ourSignatureHash); isOfficial = Arrays.asList(officialSignatureHashArray).contains(ourSignatureHash);
} catch ( } catch (
PackageManager.NameNotFoundException ignored) { PackageManager.NameNotFoundException ignored) {
} }
SharedPreferences sharedPreferences = MainApplication.getSharedPreferences(); SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm");
// We are only one process so it's ok to do this // 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 lastBoot = System.currentTimeMillis() - SystemClock.elapsedRealtime();
long lastBootPrefs = bootPrefs.getLong("last_boot", 0); long lastBootPrefs = bootPrefs.getLong("last_boot", 0);
if (lastBootPrefs == 0 || Math.abs(lastBoot - lastBootPrefs) > 100) { if (lastBootPrefs == 0 || Math.abs(lastBoot - lastBootPrefs) > 100) {
@ -450,7 +464,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
// view is nullable because it's called from xml // view is nullable because it's called from xml
public void resetApp() { 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..."); Timber.w("Resetting app...");
// recursively delete the app's data // recursively delete the app's data
((ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE)).clearApplicationUserData(); ((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); ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses(); List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcesses == null) { if (appProcesses == null) {
Timber.d("appProcesses is null");
return false; return false;
} }
final String packageName = this.getPackageName(); final String packageName = this.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { 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)) { if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
return true; return true;
} }
@ -472,6 +488,17 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
return false; 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 { private static class ReleaseTree extends Timber.Tree {
@Override @Override
protected void log(int priority, String tag, @NonNull String message, Throwable t) { protected void log(int priority, String tag, @NonNull String message, Throwable t) {

@ -65,23 +65,14 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
createFiles(); createFiles();
disableUpdateActivityForFdroidFlavor(); disableUpdateActivityForFdroidFlavor();
// Set theme // Set theme
SharedPreferences prefs = MainApplication.getSharedPreferences(); SharedPreferences prefs = MainApplication.getSharedPreferences("mmm");
switch (prefs.getString("theme", "system")) { switch (prefs.getString("theme", "system")) {
case "light": case "light" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Light);
setTheme(R.style.Theme_MagiskModuleManager_Monet_Light); case "dark" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Dark);
break; case "system" -> setTheme(R.style.Theme_MagiskModuleManager_Monet);
case "dark": case "black" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Black);
setTheme(R.style.Theme_MagiskModuleManager_Monet_Dark); case "transparent_light" ->
break; setTheme(R.style.Theme_MagiskModuleManager_Transparent_Light);
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;
} }
ActivitySetupBinding binding = ActivitySetupBinding.inflate(getLayoutInflater()); ActivitySetupBinding binding = ActivitySetupBinding.inflate(getLayoutInflater());
@ -143,21 +134,12 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
// Set the theme // Set the theme
UiThreadHandler.handler.postDelayed(() -> { UiThreadHandler.handler.postDelayed(() -> {
switch (prefs.getString("pref_theme", "system")) { switch (prefs.getString("pref_theme", "system")) {
case "light": case "light" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Light);
setTheme(R.style.Theme_MagiskModuleManager_Monet_Light); case "dark" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Dark);
break; case "system" -> setTheme(R.style.Theme_MagiskModuleManager_Monet);
case "dark": case "black" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Black);
setTheme(R.style.Theme_MagiskModuleManager_Monet_Dark); case "transparent_light" ->
break; setTheme(R.style.Theme_MagiskModuleManager_Transparent_Light);
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;
} }
// restart the activity because switching to transparent pisses the rendering engine off // restart the activity because switching to transparent pisses the rendering engine off
Intent intent = new Intent(this, SetupActivity.class); Intent intent = new Intent(this, SetupActivity.class);
@ -249,23 +231,14 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
public Resources.Theme getTheme() { public Resources.Theme getTheme() {
Resources.Theme theme = super.getTheme(); Resources.Theme theme = super.getTheme();
// Set the theme // Set the theme
SharedPreferences prefs = MainApplication.getSharedPreferences(); SharedPreferences prefs = MainApplication.getSharedPreferences("mmm");
switch (prefs.getString("pref_theme", "system")) { switch (prefs.getString("pref_theme", "system")) {
case "light": case "light" -> theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet_Light, true);
theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet_Light, true); case "dark" -> theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet_Dark, true);
break; case "system" -> theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet, true);
case "dark": case "black" -> theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet_Black, true);
theme.applyStyle(R.style.Theme_MagiskModuleManager_Monet_Dark, true); case "transparent_light" ->
break; theme.applyStyle(R.style.Theme_MagiskModuleManager_Transparent_Light, true);
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;
} }
return theme; return theme;
} }

@ -237,21 +237,11 @@ public final class AndroidacyActivity extends FoxActivity {
public boolean onConsoleMessage(ConsoleMessage consoleMessage) { public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
switch (consoleMessage.messageLevel()) { switch (consoleMessage.messageLevel()) {
case TIP: case TIP -> Timber.v(consoleMessage.message());
Timber.v(consoleMessage.message()); case LOG -> Timber.i(consoleMessage.message());
break; case WARNING -> Timber.w(consoleMessage.message());
case LOG: case ERROR -> Timber.e(consoleMessage.message());
Timber.i(consoleMessage.message()); case DEBUG -> Timber.d(consoleMessage.message());
break;
case WARNING:
Timber.w(consoleMessage.message());
break;
case ERROR:
Timber.e(consoleMessage.message());
break;
case DEBUG:
Timber.d(consoleMessage.message());
break;
} }
} }
return true; return true;

@ -43,7 +43,7 @@ import timber.log.Timber;
@SuppressWarnings("KotlinInternalInJava") @SuppressWarnings("KotlinInternalInJava")
public final class AndroidacyRepoData extends RepoData { 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 { static {
HttpUrl.Builder OK_HTTP_URL_BUILDER = new HttpUrl.Builder().scheme("https"); HttpUrl.Builder OK_HTTP_URL_BUILDER = new HttpUrl.Builder().scheme("https");
@ -54,7 +54,7 @@ public final class AndroidacyRepoData extends RepoData {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final String ClientID = BuildConfig.ANDROIDACY_CLIENT_ID; 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 boolean testMode;
private final String host; private final String host;
public String[][] userInfo = new String[][]{{"role", null}, {"permissions", null}}; public String[][] userInfo = new String[][]{{"role", null}, {"permissions", null}};
@ -88,12 +88,11 @@ public final class AndroidacyRepoData extends RepoData {
// limiting and fraud detection. // limiting and fraud detection.
public static String generateDeviceId() throws NoSuchAlgorithmException { public static String generateDeviceId() throws NoSuchAlgorithmException {
// Try to get the device ID from the shared preferences // 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); String deviceIdPref = sharedPreferences.getString("device_id", null);
if (deviceIdPref != null) { if (deviceIdPref != null) {
return deviceIdPref; return deviceIdPref;
} else { } else {
// AAAA we're fingerprintiiiiing
// Really not that scary - just hashes some device info. We can't even get the info // 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. // we originally hashed, so it's not like we can use it to track you.
String deviceId = null; String deviceId = null;
@ -176,7 +175,7 @@ public final class AndroidacyRepoData extends RepoData {
protected boolean prepare() throws NoSuchAlgorithmException { protected boolean prepare() throws NoSuchAlgorithmException {
// If ANDROIDACY_CLIENT_ID is not set or is empty, disable this repo and return // If ANDROIDACY_CLIENT_ID is not set or is empty, disable this repo and return
if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) { 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.putBoolean("pref_androidacy_repo_enabled", false);
editor.apply(); editor.apply();
return false; return false;
@ -213,7 +212,7 @@ public final class AndroidacyRepoData extends RepoData {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
if (this.androidacyBlockade > time) if (this.androidacyBlockade > time)
return true; // fake it till you make it. Basically, 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; this.androidacyBlockade = time + 30_000L;
try { try {
if (token == null) { if (token == null) {
@ -267,7 +266,7 @@ public final class AndroidacyRepoData extends RepoData {
return false; return false;
} }
// Save token to shared preference // 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.putString("pref_androidacy_api_token", token);
editor.apply(); editor.apply();
} catch ( } catch (

@ -59,11 +59,15 @@ public class BackgroundUpdateChecker extends Worker {
static void doCheck(Context context) { static void doCheck(Context context) {
// first, check if the user has enabled background update checking // 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; return;
} }
// next, check if user requires wifi // 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 // check if wifi is connected
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
Network networkInfo = connectivityManager.getActiveNetwork(); Network networkInfo = connectivityManager.getActiveNetwork();
@ -74,7 +78,6 @@ public class BackgroundUpdateChecker extends Worker {
} }
// post checking notification if notofiications are enabled // post checking notification if notofiications are enabled
if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
if (!MainApplication.getINSTANCE().isInForeground()) {
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); 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()); 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); 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.setContentTitle(context.getString(R.string.notification_channel_background_update));
builder.setContentText(context.getString(R.string.notification_channel_background_update_description)); builder.setContentText(context.getString(R.string.notification_channel_background_update_description));
notificationManager.notify(NOTIFICATION_ID_ONGOING, builder.build()); notificationManager.notify(NOTIFICATION_ID_ONGOING, builder.build());
}
} }
Thread.currentThread().setPriority(2); Thread.currentThread().setPriority(2);
ModuleManager.getINSTANCE().scanAsync(); ModuleManager.getINSTANCE().scanAsync();
@ -104,7 +106,7 @@ public class BackgroundUpdateChecker extends Worker {
continue; continue;
// exclude all modules with id's stored in the pref pref_background_update_check_excludes // exclude all modules with id's stored in the pref pref_background_update_check_excludes
try { 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; continue;
} catch ( } catch (
Exception ignored) { Exception ignored) {
@ -124,7 +126,7 @@ public class BackgroundUpdateChecker extends Worker {
} }
}); });
// check for app updates // 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 { try {
boolean shouldUpdate = AppUpdateManager.getAppUpdateManager().checkUpdate(true); boolean shouldUpdate = AppUpdateManager.getAppUpdateManager().checkUpdate(true);
if (shouldUpdate) { if (shouldUpdate) {
@ -206,7 +208,7 @@ public class BackgroundUpdateChecker extends Worker {
public static void onMainActivityCreate(Context context) { public static void onMainActivityCreate(Context context) {
// Refuse to run if first_launch pref is not false // 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; return;
// create notification channel group // create notification channel group
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

@ -25,7 +25,7 @@ public class ModuleInfo {
private static final int FLAG_FENCE = 0x10000000; // Should never be set private static final int FLAG_FENCE = 0x10000000; // Should never be set
// Magisk standard // Magisk standard
public final String id; public String id;
public String name; public String name;
public String version; public String version;
public long versionCode; public long versionCode;

@ -78,13 +78,13 @@ public final class ModuleManager extends SyncManager {
for (String module : modules) { for (String module : modules) {
if (!new SuFile("/data/adb/modules/" + module).isDirectory()) if (!new SuFile("/data/adb/modules/" + module).isDirectory())
continue; // Ignore non directory files inside modules folder continue; // Ignore non directory files inside modules folder
if (BuildConfig.DEBUG) Timber.d(module);
LocalModuleInfo moduleInfo = moduleInfos.get(module); LocalModuleInfo moduleInfo = moduleInfos.get(module);
// next, merge the module info with a record from ModuleListCache if it exists // next, merge the module info with a record from ModuleListCache if it exists
RealmConfiguration realmConfiguration; RealmConfiguration realmConfiguration;
// get all dirs under the realms/repos/ dir under app's data dir // get all dirs under the realms/repos/ dir under app's data dir
File cacheRoot = new File(MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/").toURI()); File cacheRoot = new File(MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/").toURI());
ModuleListCache moduleListCache; ModuleListCache moduleListCache;
boolean foundCache = false;
for (File dir : Objects.requireNonNull(cacheRoot.listFiles())) { for (File dir : Objects.requireNonNull(cacheRoot.listFiles())) {
if (dir.isDirectory()) { if (dir.isDirectory()) {
// if the dir name matches the module name, use it as the cache dir // 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()); 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(); moduleListCache = realm.where(ModuleListCache.class).equalTo("codename", module).findFirst();
if (moduleListCache != null) { if (moduleListCache != null) {
foundCache = true;
Timber.d("Found cache for %s", module); Timber.d("Found cache for %s", module);
moduleInfo = new LocalModuleInfo(module); // get module info from cache
assert moduleListCache.getAuthor() != null; if (moduleInfo == null) {
assert moduleListCache.getDescription() != null; moduleInfo = new LocalModuleInfo(module);
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();
moduleInfo.name = moduleListCache.getName(); moduleInfo.name = moduleListCache.getName();
moduleInfo.minApi = moduleListCache.getMinApi(); moduleInfo.description = moduleListCache.getDescription() + " (cached)";
moduleInfo.maxApi = moduleListCache.getMaxApi(); moduleInfo.author = moduleListCache.getAuthor();
moduleInfo.minMagisk = moduleListCache.getMinMagisk();
moduleInfo.safe = moduleListCache.isSafe(); moduleInfo.safe = moduleListCache.isSafe();
moduleInfo.support = moduleListCache.getSupport();
moduleInfo.donate = moduleListCache.getDonate();
moduleInfos.put(module, moduleInfo); moduleInfos.put(module, moduleInfo);
// This should not really happen, but let's handles theses cases anyway realm.close();
moduleInfo.flags |= ModuleInfo.FLAG_MODULE_UPDATING_ONLY;
break; 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<ModuleListCache> 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());
} }
} }
} }

@ -183,7 +183,6 @@ public class ModuleViewListBuilder {
final ArrayList<ModuleHolder> moduleHolders; final ArrayList<ModuleHolder> moduleHolders;
final int newNotificationsLen; final int newNotificationsLen;
final boolean first; final boolean first;
final ModuleHolder[] headerFooter = new ModuleHolder[2];
try { try {
synchronized (this.updateLock) { synchronized (this.updateLock) {
// Build start // Build start
@ -236,11 +235,11 @@ public class ModuleViewListBuilder {
} }
moduleHolders.sort(this.moduleSorter); moduleHolders.sort(this.moduleSorter);
// Header is always first // Header is always first
moduleHolders.add(0, headerFooter[0] = //moduleHolders.add(0, headerFooter[0] =
new ModuleHolder(this.headerPx, true)); // new ModuleHolder(this.headerPx / 2, true));
// Footer is always last // Footer is always last
moduleHolders.add(headerFooter[1] = //moduleHolders.add(headerFooter[1] =
new ModuleHolder(this.footerPx, false)); // new ModuleHolder(this.footerPx * 2, false));
Timber.i("Got " + moduleHolders.size() + " entries!"); Timber.i("Got " + moduleHolders.size() + " entries!");
// Build end // Build end
} }
@ -281,10 +280,6 @@ public class ModuleViewListBuilder {
int oldLen = moduleViewAdapter.moduleHolders.size(); int oldLen = moduleViewAdapter.moduleHolders.size();
moduleViewAdapter.moduleHolders.clear(); moduleViewAdapter.moduleHolders.clear();
moduleViewAdapter.moduleHolders.addAll(moduleHolders); moduleViewAdapter.moduleHolders.addAll(moduleHolders);
synchronized (this.updateLock) {
headerFooter[0].footerPx = this.headerPx;
headerFooter[1].footerPx = this.footerPx;
}
if (oldNotificationsLen != newNotificationsLen || if (oldNotificationsLen != newNotificationsLen ||
!oldNotifications.equals(this.notifications)) { !oldNotifications.equals(this.notifications)) {
notifySizeChanged(moduleViewAdapter, 0, notifySizeChanged(moduleViewAdapter, 0,
@ -306,8 +301,6 @@ public class ModuleViewListBuilder {
if (isTop) moduleList.scrollToPosition(0); if (isTop) moduleList.scrollToPosition(0);
if (isBottom) moduleList.scrollToPosition(newLen); if (isBottom) moduleList.scrollToPosition(newLen);
this.updateInsets = () -> { this.updateInsets = () -> {
headerFooter[0].footerPx = this.headerPx;
headerFooter[1].footerPx = this.footerPx;
notifySizeChanged(moduleViewAdapter, 0, 1, 1); notifySizeChanged(moduleViewAdapter, 0, 1, 1);
notifySizeChanged(moduleViewAdapter, notifySizeChanged(moduleViewAdapter,
moduleHolders.size(), 1, 1); moduleHolders.size(), 1, 1);

@ -384,7 +384,7 @@ public class RepoData extends XRepo {
long diff = currentTime - lastUpdate; long diff = currentTime - lastUpdate;
long diffMinutes = diff / (60 * 1000) % 60; long diffMinutes = diff / (60 * 1000) % 60;
Timber.d("Repo " + this.id + " updated: " + diffMinutes + " minutes ago"); Timber.d("Repo " + this.id + " updated: " + diffMinutes + " minutes ago");
return diffMinutes > (BuildConfig.DEBUG ? 5 : 20); return diffMinutes > (BuildConfig.DEBUG ? 15 : 20);
} else { } else {
Timber.d("Repo " + this.id + " should update could not find repo in database"); Timber.d("Repo " + this.id + " should update could not find repo in database");
return true; return true;

@ -295,7 +295,6 @@ public class RepoUpdater {
if (realm.isInTransaction()) { if (realm.isInTransaction()) {
realm.cancelTransaction(); realm.cancelTransaction();
} }
Timber.d("Inserting module %s to realm", id);
// create a realm object and insert or update it // create a realm object and insert or update it
// add everything to the realm object // add everything to the realm object
realm.beginTransaction(); realm.beginTransaction();
@ -346,6 +345,7 @@ public class RepoUpdater {
success.set(true); success.set(true);
// get unix timestamp of current time // get unix timestamp of current time
int currentTime = (int) (System.currentTimeMillis() / 1000); 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); repoListCache.setLastUpdate(currentTime);
} else { } else {
Timber.w("Failed to update lastUpdate for repo %s", this.repoData.id); Timber.w("Failed to update lastUpdate for repo %s", this.repoData.id);

@ -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<String> 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<String> getStringSet(String key, @Nullable Set<String> 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);
}
}

@ -6,7 +6,6 @@ import static java.lang.Integer.parseInt;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.Application;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
@ -40,11 +39,11 @@ import androidx.preference.ListPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import androidx.preference.SwitchPreferenceCompat; import androidx.preference.SwitchPreferenceCompat;
import androidx.preference.TwoStatePreference; import androidx.preference.TwoStatePreference;
import com.fox2code.foxcompat.app.FoxActivity; import com.fox2code.foxcompat.app.FoxActivity;
import com.fox2code.foxcompat.app.internal.FoxProcessExt;
import com.fox2code.foxcompat.view.FoxDisplay; import com.fox2code.foxcompat.view.FoxDisplay;
import com.fox2code.foxcompat.view.FoxViewCompat; import com.fox2code.foxcompat.view.FoxViewCompat;
import com.fox2code.mmm.AppUpdateManager; import com.fox2code.mmm.AppUpdateManager;
@ -206,7 +205,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
@Override @Override
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
getPreferenceManager().setSharedPreferencesName("mmm"); PreferenceManager preferenceManager = getPreferenceManager();
preferenceManager.setPreferenceDataStore(EncryptedPreferenceDataStore.getInstance());
setPreferencesFromResource(R.xml.root_preferences, rootKey); setPreferencesFromResource(R.xml.root_preferences, rootKey);
applyMaterial3(getPreferenceScreen()); applyMaterial3(getPreferenceScreen());
// add bottom navigation bar to the settings // add bottom navigation bar to the settings
@ -475,7 +475,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// set the box to unchecked // set the box to unchecked
((SwitchPreferenceCompat) backgroundUpdateCheck).setChecked(false); ((SwitchPreferenceCompat) backgroundUpdateCheck).setChecked(false);
// ensure that the preference is 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) -> { 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 // Open the app settings
Intent intent = new Intent(); Intent intent = new Intent();
@ -512,7 +512,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
int i = 0; int i = 0;
for (LocalModuleInfo localModuleInfo : localModuleInfos) { for (LocalModuleInfo localModuleInfo : localModuleInfos) {
moduleNames[i] = localModuleInfo.name; moduleNames[i] = localModuleInfo.name;
SharedPreferences sharedPreferences = MainApplication.getSharedPreferences(); SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm");
// get the stringset pref_background_update_check_excludes // get the stringset pref_background_update_check_excludes
Set<String> stringSet = sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>()); Set<String> stringSet = sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>());
// Stringset uses id, we show name // 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) -> { 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 // get the stringset pref_background_update_check_excludes
SharedPreferences sharedPreferences = MainApplication.getSharedPreferences(); SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm");
Set<String> stringSet = new HashSet<>(sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>())); Set<String> stringSet = new HashSet<>(sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>()));
// get id from name // get id from name
String id; String id;
@ -610,11 +610,11 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
if (devModeStep == 2) { if (devModeStep == 2) {
devModeStep = 0; devModeStep = 0;
if (MainApplication.isDeveloper() && !BuildConfig.DEBUG) { 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 Toast.makeText(getContext(), // Tell the user something changed
R.string.dev_mode_disabled, Toast.LENGTH_SHORT).show(); R.string.dev_mode_disabled, Toast.LENGTH_SHORT).show();
} else { } else {
MainApplication.getSharedPreferences().edit().putBoolean("developer", true).apply(); MainApplication.getSharedPreferences("mmm").edit().putBoolean("developer", true).apply();
Toast.makeText(getContext(), // Tell the user something changed Toast.makeText(getContext(), // Tell the user something changed
R.string.dev_mode_enabled, Toast.LENGTH_SHORT).show(); R.string.dev_mode_enabled, Toast.LENGTH_SHORT).show();
} }
@ -693,7 +693,9 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
} }
// Share logs // Share logs
Intent shareIntent = new Intent(); Intent shareIntent = new Intent();
// create a new intent and grantUriPermission to the file provider
shareIntent.setAction(Intent.ACTION_SEND); 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.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(requireContext(), BuildConfig.APPLICATION_ID + ".file-provider", logsFile));
shareIntent.setType("text/plain"); shareIntent.setType("text/plain");
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_logs))); 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); 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) { private void openFragment(Fragment fragment, @StringRes int title) {
FoxActivity compatActivity = getFoxActivity(this); FoxActivity compatActivity = getFoxActivity(this);
compatActivity.setOnBackPressedCallback(this); compatActivity.setOnBackPressedCallback(this);
@ -802,8 +785,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
* namely, from <a href="https://github.com/NeoApplications/Neo-Wellbeing/blob/9fca4136263780c022f9ec6433c0b43d159166db/app/src/main/java/org/eu/droid_ng/wellbeing/prefs/SettingsActivity.java#L101">neo wellbeing</a> * namely, from <a href="https://github.com/NeoApplications/Neo-Wellbeing/blob/9fca4136263780c022f9ec6433c0b43d159166db/app/src/main/java/org/eu/droid_ng/wellbeing/prefs/SettingsActivity.java#L101">neo wellbeing</a>
*/ */
public static void applyMaterial3(Preference p) { public static void applyMaterial3(Preference p) {
if (p instanceof PreferenceGroup) { if (p instanceof PreferenceGroup pg) {
PreferenceGroup pg = (PreferenceGroup) p;
for (int i = 0; i < pg.getPreferenceCount(); i++) { for (int i = 0; i < pg.getPreferenceCount(); i++) {
applyMaterial3(pg.getPreference(i)); applyMaterial3(pg.getPreference(i));
} }
@ -827,7 +809,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// Use MaterialAlertDialogBuilder // 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) -> { 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 // 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 // Check the switch
Intent mStartActivity = new Intent(requireContext(), MainActivity.class); Intent mStartActivity = new Intent(requireContext(), MainActivity.class);
mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); 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 switchPreferenceCompat = (SwitchPreferenceCompat) androidacyTestMode;
switchPreferenceCompat.setChecked(false); switchPreferenceCompat.setChecked(false);
// There's probably a better way to do this than duplicate code but I'm too lazy to figure it out // 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(); }).show();
} else { } 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 // 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) -> { 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 // User clicked OK button

@ -22,6 +22,7 @@ import java.util.Locale;
import timber.log.Timber; import timber.log.Timber;
@SuppressWarnings("SpellCheckingInspection")
public enum PropUtils { public enum PropUtils {
; ;
private static final HashMap<String, String> moduleSupportsFallbacks = new HashMap<>(); private static final HashMap<String, String> moduleSupportsFallbacks = new HashMap<>();
@ -122,6 +123,10 @@ public enum PropUtils {
continue; continue;
String key = line.substring(0, index); String key = line.substring(0, index);
String value = line.substring(index + 1).trim(); 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 // name and id have their own implementation
if (isInvalidValue(key)) { if (isInvalidValue(key)) {
if (local) { if (local) {
@ -144,7 +149,8 @@ public enum PropUtils {
if (local) { if (local) {
invalid = true; invalid = true;
break; break;
} throw new IOException("Invalid module id!"); }
throw new IOException("Invalid module id!");
} }
readId = true; readId = true;
if (!moduleInfo.id.equals(value)) { if (!moduleInfo.id.equals(value)) {
@ -167,7 +173,8 @@ public enum PropUtils {
if (local) { if (local) {
invalid = true; invalid = true;
break; break;
} throw new IOException("Invalid module name!"); }
throw new IOException("Invalid module name!");
} }
readName = true; readName = true;
moduleInfo.name = value; moduleInfo.name = value;

@ -28,12 +28,12 @@ public class SentryMain {
@SuppressLint({"RestrictedApi", "UnspecifiedImmutableFlag"}) @SuppressLint({"RestrictedApi", "UnspecifiedImmutableFlag"})
public static void initialize(final MainApplication mainApplication) { public static void initialize(final MainApplication mainApplication) {
// If first_launch pref is not false, refuse to initialize Sentry // 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)) { if (sharedPreferences.getBoolean("first_time_setup_done", true)) {
return; return;
} }
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { 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.putString("lastExitReason", "crash");
editor.putLong("lastExitTime", System.currentTimeMillis()); editor.putLong("lastExitTime", System.currentTimeMillis());
editor.apply(); editor.apply();

@ -11,10 +11,10 @@
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh" android:id="@+id/swipe_refresh"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottom_navigation"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<!-- FrameLayout is the best way to fix blurring --> <!-- FrameLayout is the best way to fix blurring -->
<FrameLayout <FrameLayout
@ -24,14 +24,16 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/module_list" android:id="@+id/module_list"
android:paddingVertical="8dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
app:edgeToEdge="true" /> app:edgeToEdge="true" />
<!-- online modules --> <!-- online modules -->
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/module_list_online" android:id="@+id/module_list_online"
android:paddingVertical="8dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:visibility="gone" android:visibility="gone"
app:edgeToEdge="true" /> app:edgeToEdge="true" />
</FrameLayout> </FrameLayout>
@ -45,6 +47,7 @@
android:scaleY="2" android:scaleY="2"
app:showAnimationBehavior="outward" app:showAnimationBehavior="outward"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/swipe_refresh" /> app:layout_constraintTop_toTopOf="@+id/swipe_refresh" />
@ -56,7 +59,7 @@
android:layout_marginVertical="8dp" android:layout_marginVertical="8dp"
android:filterTouchesWhenObscured="true" android:filterTouchesWhenObscured="true"
android:gravity="bottom|end" android:gravity="bottom|end"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toTopOf="@id/bottom_navigation"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_fitsSystemWindowsInsets="bottom" app:layout_fitsSystemWindowsInsets="bottom"
tools:ignore="RtlHardcoded"> tools:ignore="RtlHardcoded">
@ -105,6 +108,7 @@
android:visibility="visible" android:visibility="visible"
app:compatShadowEnabled="true" app:compatShadowEnabled="true"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/swipe_refresh"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_nav_menu" /> app:menu="@menu/bottom_nav_menu" />

@ -22,7 +22,7 @@ buildscript {
project.ext.sentry_version = "6.14.0" project.ext.sentry_version = "6.14.0"
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.4.1' 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 "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${latestAboutLibsRelease}"
classpath 'io.sentry:sentry-android:6.14.0' classpath 'io.sentry:sentry-android:6.14.0'

@ -1,6 +1,6 @@
#Sun Jun 05 10:40:53 EDT 2022 #Sun Jun 05 10:40:53 EDT 2022
distributionBase=GRADLE_USER_HOME 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 distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
Loading…
Cancel
Save