From 150b9f77e0b4bbd6c4fb06f43ae479d26a96c195 Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 23 Feb 2023 09:31:24 +0000 Subject: [PATCH 1/4] Update multiple translations Co-authored-by: Suu --- app/src/main/res/values-ja/strings.xml | 60 +++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 563ea75..1a2bc01 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -9,7 +9,7 @@ ファイルのダウンロードに失敗したよ インターネットに接続できないよ アプリの新しいバージョンがあるよ - アップデート + アプリをアップデート 最終更新: リポジトリ: @@ -161,7 +161,7 @@ 非公式 初回セットアップ 設定を完了 - バックグラウンドでアップデートを確認 + 自動でアップデートを確認 Sentry を有効化 リポジトリ コミット %1$s @ %2$s @@ -171,7 +171,7 @@ API キーが変更されたよ。アプリを再起動して変更を適用してね。 入力された API キーはすでに使われているものと同じだよ。 通知を許可しますか? - アプリとモジュールのアップデート通知を受け取るには通知権限が必要だよ。この権限がなければバックグラウンドでのアップデートチェックは行われないよ。 + アプリとモジュールのアップデート通知を受け取るには通知権限が必要だよ。この権限がなければ自動でアップデートチェックは行われないよ。 権限を付与 このオプションを有効化するには通知を有効化してね。 再度質問しない @@ -190,10 +190,10 @@ その操作をするにはインターネットに接続されている必要があるよ Androidacy テストモード Androidacy のリリースエンドポイントの代わりにステージングエンドポイントを使うよ。(再起動が必要だよ) - %i 個のモジュールアップデートが見つかったよ + %1$d 個のモジュールアップデートが見つかったよ %i 個のモジュールアップデートを見つけたよ クリックしてアプリを開く - バックグラウンドでモジュールのアップデートを確認 + 自動でモジュールのアップデートを確認 バッテリーの消耗が大きくなることがあるよ 通知をテスト いくつかの言語の翻訳は最新ではないことがあるよ。GitHub で翻訳に協力してね @@ -295,4 +295,54 @@ 設定 クラッシュレポートは無効化されてるよ。有効化してフィードバックを送ってね。 スタックトレースをクリップボードにコピーしたよ! + アップデートのダウンロードメカニズムをテスト + アップデートを確認するには Wi-Fi または容量無制限のネットワークを必要とするよ。モバイルデータを使ってるならオンのままにしておくことをおすすめするよ。 + このモジュールには無効または品質が悪いと思われるメタデータが含まれているよ。アンインストールすることをおすすめするよ。 + 自動アップデートチェックから除外するモジュール + アプリ内アップデータ + アプリをアップデート + FoxMMM のアップデートをインストールしてるよ。数分かかることがあるよ + お待ちください... + エラー: 起動時に無効なデータを受け取ったよ + デバッグビルドで実行してるよ。自動でアプリが更新されないので手動で更新してね + エラー: 無効なアクションが指定されたよ。無視して続けるね。 + アップデートが見つかったよ + アップデートを確認しています… + 最新版だよ! + アップデートをダウンロード + アップデート情報のダウンロード中にエラーが発生したよ。 + エラー: アップデート情報を取得できなかったよ + アップデートをダウンロードしています... %1$d%% + アップデートをインストールしています… + エラー: アップデートパッケージが見つからなかったよ。 + アプリのアップデートを確認 + モジュールのリストはアップデートチェックから除外されてるよ + モジュールを除外 + アップデート + 無効な入力 + 入力したモジュールのリストは無効だよ。コンマで区切られた ID リストを入力してね + まだ変更点がないよ! + アップデートをキャンセル + 入力したリポジトリのURLは無効だよ + リポジトリは HTTPS 対応 とドキュメントに書かれているスペックを満たしている必要があるよ。 + 以下のモジュールがアップデートされるよ: + %1$s を %2$s へ + アップデートを確認しています... + FoxMMM がバックグラウンドでアップデートを確認してるよ。 + バックグラウンドアップデートステータス + 通知を表示してアップデートの確認中にキルされないようにする + WiFi 接続時にのみ確認 + このデバイスにはモジュールがインストールされていないよ + モジュールのアップデートが見つかったら通知するよ + アップデート + オンライン + 安全 + 安全なモジュールとして検証済み + このモジュールは安全なリポジトリから提供されて検証されているよ。品質、安全対策、ウイルスチェックがされているよ。 + アプリのアップデートを自動で確認するよ。ダウンロードは自動で行われないよ。 + アプリのアップデートを確認 + アプリアップデート + アプリのアップデートがあるときに通知するよ + アプリのアップデートがあるよ! + FoxMMM のアップデートがあるよ。ここをタップしてアップデートできるよ。 \ No newline at end of file From 17cf8baee93a0883773ce4ce66278a4e05ffe499 Mon Sep 17 00:00:00 2001 From: Weblate Date: Fri, 24 Feb 2023 15:19:48 +0000 Subject: [PATCH 2/4] Update multiple translations Co-authored-by: CONSTANT --- app/src/main/res/values-de/strings.xml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index da6ab2c..b25f1aa 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -13,7 +13,7 @@ Verbindung mit dem Internet fehlgeschlagen Aktivitätseinstellungen Anwendungsaktualisierung verfügbar - Update + Update app Letzte Aktualisierung: Repo: @@ -50,7 +50,9 @@ Entwicklermodus aktiviert Englische Sprache erzwingen Modulfilter niedriger Qualität deaktivieren - Einige Module deklarieren ihre Metadaten nicht richtig, was zu visuellen Störungen führt, und/oder auf eine schlechte Modulqualität hinweisen. + Some modules do not declare their metadata properly, causing visual glitches, and/or indicating poor module quality. +\nTurn this on at your own risk! +\nTurn this on at your own risk! \nDeaktivieren auf eigene Gefahr! System WebView konnte nicht geöffnet werden Keine Beschreibung gefunden. @@ -121,10 +123,10 @@ Dieses Repository könnte Anzeigen anzeigen. Unterstützt Bewertungen, automatische Virus-Scans und weitere Features. Androidacy Testmodus Androidacy Beta aktivieren (wird App neu starten) - %i Updates gefunden + Modulaktualisierungen gefunden: %1$d %i Updates geschnüffelt Tippen, um die App zu öffnen - Aktualisierungsbenachrichtigung + Automatische Suche nach Modulaktualisierungen Könnte Batterie-Nutzung erhöhen Test-Benachrichtigung Einige Übersetzungen für die aktuelle Sprache sind nicht aktuell, bitte überlege, auf GitHub beizutragen @@ -185,7 +187,7 @@ Willkommen Willkommen! Diese App hilft dir, Magisk-Module zu installieren und einzurichten. Bitte stelle die folgenden Optionen ein um anzufangen. Diese und weiter können später in den Einstellungen umgestellt werden. Weiter - Updatesuche im Hintergrund + Automatische Suche nach Updates Aktiviere Androidacy-Repo Aktiviere Magisk Alt Repo Aktiviere Sentry @@ -194,7 +196,7 @@ API-Schlüssel wurde geändert. Starte die App neu, um Änderungen anzuwenden. Der eingegebene API-Schlüssel ist bereits eingestellt. Nichts hat sich geändert. Erlaube Benachrichtigungen\? - Wir brauchen die Benachrichtigungsberechtigung, um dich bei App- und Modulupdates zu informieren. Ohne diese Berechtigung wird nicht automatisch nach Updates gesucht. + Wir müssen dieBenachrichtigung senden, um Sie über App-und modulsupdates zu benachrichtigen. Automatische Updates werden nicht ausgeführt, wenn Sie diese Erlaubnis nicht bereitstellen. Erlauben Bitte aktiviere Benachrichtigungen, um diese Einstellung zu aktivieren. Nicht nochmal fragen @@ -259,4 +261,12 @@ In Teilen von Androidacy entwickelt Danke Androidacy für eure Integration und Beiträge zur App. Dunkel (transparent) + Durch das Upgrade auf Premium werden Anzeigen, Captchas und Downloads für das Androidacy-Repository entfernt und Androidacy und die Modulautoren unterstützt. + Autoren + Fox2Code ist der ursprüngliche Autor der App. Ohne ihn wäre das nicht möglich gewesen. + Erstellt von Fox2Code + Log-Log im Tresor speichern und freigeben + Die folgenden Repos konnten nicht aktualisiert werden: %s + API-Schlüssel zurücksetzen + Upgrade auf Premium \ No newline at end of file From 5029486d4f3c726c422698aaa1feaa370ffa7a09 Mon Sep 17 00:00:00 2001 From: androidacy-user Date: Fri, 24 Feb 2023 21:32:37 -0500 Subject: [PATCH 3/4] 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 --- app/build.gradle | 12 +- app/src/main/AndroidManifest.xml | 1 + .../java/com/fox2code/mmm/MainActivity.java | 28 +++-- .../com/fox2code/mmm/MainApplication.java | 75 ++++++++---- .../java/com/fox2code/mmm/SetupActivity.java | 67 ++++------- .../mmm/androidacy/AndroidacyActivity.java | 20 +--- .../mmm/androidacy/AndroidacyRepoData.java | 13 +-- .../background/BackgroundUpdateChecker.java | 16 +-- .../com/fox2code/mmm/manager/ModuleInfo.java | 2 +- .../fox2code/mmm/manager/ModuleManager.java | 36 ++---- .../mmm/module/ModuleViewListBuilder.java | 15 +-- .../java/com/fox2code/mmm/repo/RepoData.java | 2 +- .../com/fox2code/mmm/repo/RepoUpdater.java | 2 +- .../EncryptedPreferenceDataStore.java | 108 ++++++++++++++++++ .../mmm/settings/SettingsActivity.java | 46 +++----- .../com/fox2code/mmm/utils/io/PropUtils.java | 11 +- .../fox2code/mmm/utils/sentry/SentryMain.java | 4 +- app/src/main/res/layout/activity_main.xml | 14 ++- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 20 files changed, 275 insertions(+), 201 deletions(-) create mode 100644 app/src/main/java/com/fox2code/mmm/settings/EncryptedPreferenceDataStore.java 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 From dac0c5dddec283820ca3b6bce0e2fda65c27c8c8 Mon Sep 17 00:00:00 2001 From: androidacy-user Date: Sat, 25 Feb 2023 21:29:49 -0500 Subject: [PATCH 4/4] encrypt realms still a lil crashy but works Signed-off-by: androidacy-user --- .../java/com/fox2code/mmm/MainActivity.java | 2 +- .../com/fox2code/mmm/MainApplication.java | 71 +++++++++ .../java/com/fox2code/mmm/SetupActivity.java | 138 ++++++++++++++---- .../background/BackgroundUpdateChecker.java | 4 +- .../fox2code/mmm/manager/ModuleManager.java | 6 +- .../java/com/fox2code/mmm/repo/RepoData.java | 24 +-- .../com/fox2code/mmm/repo/RepoManager.java | 4 + .../com/fox2code/mmm/repo/RepoUpdater.java | 8 +- .../mmm/settings/SettingsActivity.java | 10 +- .../fox2code/mmm/utils/sentry/SentryMain.java | 2 +- 10 files changed, 213 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/com/fox2code/mmm/MainActivity.java b/app/src/main/java/com/fox2code/mmm/MainActivity.java index 92b3377..46270bc 100644 --- a/app/src/main/java/com/fox2code/mmm/MainActivity.java +++ b/app/src/main/java/com/fox2code/mmm/MainActivity.java @@ -697,7 +697,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe Timber.i("Checking if we need to run setup"); // Check if this is the first launch SharedPreferences prefs = MainApplication.getSharedPreferences("mmm"); - boolean firstLaunch = prefs.getBoolean("first_time_setup_done", true); + boolean firstLaunch = prefs.getString("last_shown_setup", null) == null; if (BuildConfig.DEBUG) Timber.i("First launch: %s", firstLaunch); if (firstLaunch) { diff --git a/app/src/main/java/com/fox2code/mmm/MainApplication.java b/app/src/main/java/com/fox2code/mmm/MainApplication.java index 59243f7..851be53 100644 --- a/app/src/main/java/com/fox2code/mmm/MainApplication.java +++ b/app/src/main/java/com/fox2code/mmm/MainApplication.java @@ -12,6 +12,8 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.os.SystemClock; +import android.security.keystore.KeyProperties; +import android.util.Base64; import android.util.Log; import androidx.annotation.NonNull; @@ -37,7 +39,16 @@ import com.topjohnwu.superuser.Shell; import java.io.File; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; @@ -47,6 +58,13 @@ import java.util.Locale; import java.util.Objects; import java.util.Random; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + import io.noties.markwon.Markwon; import io.noties.markwon.html.HtmlPlugin; import io.noties.markwon.image.ImagesPlugin; @@ -513,4 +531,57 @@ public class MainApplication extends FoxApplication implements androidx.work.Con } } } + + // Access the encrypted key in the keystore, decrypt it with the secret, + // and use it to open and read from the realm again + public byte[] getExistingKey() { + // open a connection to the android keystore + KeyStore keyStore; + try { + keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + } catch (KeyStoreException | NoSuchAlgorithmException + | CertificateException | IOException e) { + throw new RuntimeException(e); + } + // access the encrypted key that's stored in shared preferences + byte[] initializationVectorAndEncryptedKey = Base64.decode(getSharedPreferences("realm_key") + .getString("iv_and_encrypted_key", null), Base64.DEFAULT); + ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey); + buffer.order(ByteOrder.BIG_ENDIAN); + // extract the length of the initialization vector from the buffer + int initializationVectorLength = buffer.getInt(); + // extract the initialization vector based on that length + byte[] initializationVector = new byte[initializationVectorLength]; + buffer.get(initializationVector); + // extract the encrypted key + byte[] encryptedKey = new byte[initializationVectorAndEncryptedKey.length + - Integer.BYTES + - initializationVectorLength]; + buffer.get(encryptedKey); + // create a cipher that uses AES encryption to decrypt our key + Cipher cipher; + try { + cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + + "/" + KeyProperties.BLOCK_MODE_CBC + + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new RuntimeException(e); + } + // decrypt the encrypted key with the secret key stored in the keystore + byte[] decryptedKey; + try { + final SecretKey secretKey = + (SecretKey) keyStore.getKey("realm_key", null); + final IvParameterSpec initializationVectorSpec = + new IvParameterSpec(initializationVector); + cipher.init(Cipher.DECRYPT_MODE, secretKey, initializationVectorSpec); + decryptedKey = cipher.doFinal(encryptedKey); + } catch (InvalidKeyException | UnrecoverableKeyException | NoSuchAlgorithmException | + BadPaddingException | KeyStoreException | IllegalBlockSizeException | + InvalidAlgorithmParameterException e) { + throw new RuntimeException(e); + } + return decryptedKey; // pass to a realm configuration via encryptionKey() + } } diff --git a/app/src/main/java/com/fox2code/mmm/SetupActivity.java b/app/src/main/java/com/fox2code/mmm/SetupActivity.java index 4ae7f24..77a3c7e 100644 --- a/app/src/main/java/com/fox2code/mmm/SetupActivity.java +++ b/app/src/main/java/com/fox2code/mmm/SetupActivity.java @@ -10,6 +10,9 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Bundle; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.util.Base64; import android.view.View; import android.view.WindowManager; @@ -36,15 +39,35 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.util.Objects; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; + import io.realm.Realm; import io.realm.RealmConfiguration; import timber.log.Timber; public class SetupActivity extends FoxActivity implements LanguageActivity { + MasterKey mainKeyAlias; + @SuppressLint({"ApplySharedPref", "RestrictedApi"}) @Override protected void onCreate(Bundle savedInstanceState) { @@ -61,7 +84,6 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { actionBar.show(); } this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, 0); - createRealmDatabase(); createFiles(); disableUpdateActivityForFdroidFlavor(); // Set theme @@ -165,42 +187,27 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { // Set first launch to false // get instance of editor SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean("first_time_setup_done", false); + editor.putString("last_shown_setup", "v1"); // Set the Automatic update check pref editor.putBoolean("pref_background_update_check", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).isChecked()); // Set the crash reporting pref editor.putBoolean("pref_crash_reporting", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).isChecked()); // Set the repos in the ReposList realm db - RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).build(); + RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).build(); boolean androidacyRepo = ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_androidacy_repo))).isChecked(); boolean magiskAltRepo = ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_magisk_alt_repo))).isChecked(); - Realm.getInstanceAsync(realmConfig, new Realm.Callback() { - @Override - public void onSuccess(@NonNull Realm realm) { - realm.executeTransaction(realm1 -> { - ReposList androidacyRepoDB = realm1.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); - if (androidacyRepoDB != null) { - androidacyRepoDB.setEnabled(androidacyRepo); - } - ReposList magiskAltRepoDB = realm1.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst(); - if (magiskAltRepoDB != null) { - magiskAltRepoDB.setEnabled(magiskAltRepo); - } - // commit the changes - realm1.commitTransaction(); - realm1.close(); - }); - realm.commitTransaction(); - realm.close(); - } - }); + Realm realm = Realm.getInstance(realmConfig); + Objects.requireNonNull(realm.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst()).setEnabled(androidacyRepo); + Objects.requireNonNull(realm.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst()).setEnabled(magiskAltRepo); + // commit the changes + realm.commitTransaction(); + realm.close(); // Commit the changes editor.commit(); // Sleep for 1 second to allow the user to see the changes try { Thread.sleep(500); - } catch ( - InterruptedException e) { + } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // Log the changes if debug @@ -219,7 +226,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { cancelButton.setText(R.string.cancel); cancelButton.setOnClickListener(v -> { // Set first launch to false and restart the activity - prefs.edit().putBoolean("first_time_setup_done", false).commit(); + prefs.edit().putString("last_shown_setup", "v1").commit(); MainActivity.doSetupRestarting = true; Intent intent = new Intent(this, MainActivity.class); startActivity(intent); @@ -258,9 +265,13 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { // creates the realm database private void createRealmDatabase() { Timber.d("Creating Realm databases"); + // create encryption key + Timber.d("Creating encryption key"); + // generate the encryption key and store it in the prefs + byte[] encryptionKey = getNewKey(); // create the realm database for ReposList // next, create the realm database for ReposList - RealmConfiguration config2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration config2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).encryptionKey(encryptionKey).build(); // get the instance Realm.getInstanceAsync(config2, new Realm.Callback() { @Override @@ -308,14 +319,12 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { // initial set of cookies, only really used to create the keypair and encrypted file String initialCookie = "is_foxmmm=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=production-api.androidacy.com; SameSite=None; Secure;|foxmmm_version=" + BuildConfig.VERSION_CODE + "; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=production-api.androidacy.com; SameSite=None; Secure;"; Context context = getApplicationContext(); - MasterKey mainKeyAlias; mainKeyAlias = new MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(); EncryptedFile encryptedFile = new EncryptedFile.Builder(context, new File(MainApplication.getINSTANCE().getFilesDir(), cookieFileName), mainKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build(); InputStream inputStream; try { inputStream = encryptedFile.openFileInput(); - } catch ( - FileNotFoundException e) { + } catch (FileNotFoundException e) { Timber.d("Cookie file not found, creating new file"); OutputStream outputStream = encryptedFile.openFileOutput(); outputStream.write(initialCookie.getBytes()); @@ -337,8 +346,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { outputStream.close(); outputStream.flush(); } - } catch (GeneralSecurityException | - IOException e) { + } catch (GeneralSecurityException | IOException e) { Timber.e(e); } // we literally only use these to create the http cache folders @@ -354,6 +362,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { Timber.d("Created http cache dir"); } } + createRealmDatabase(); } @SuppressWarnings("ConstantConditions") @@ -366,4 +375,69 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { pm.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } } + + @SuppressLint("NewApi") + public byte[] getNewKey() { + if (MainApplication.getSharedPreferences("mmm").getBoolean("keygen", false)) { + Timber.d("Key already generated, returning"); + return MainApplication.getINSTANCE().getExistingKey(); + } + // open a connection to the android keystore + KeyStore keyStore; + try { + keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + } catch (java.security.KeyStoreException | NoSuchAlgorithmException | CertificateException | + IOException e) { + throw new RuntimeException(e); + } + // create a securely generated random asymmetric RSA key + byte[] realmKey = new byte[Realm.ENCRYPTION_KEY_LENGTH]; + new SecureRandom().nextBytes(realmKey); + // create a cipher that uses AES encryption -- we'll use this to encrypt our key + Cipher cipher; + try { + cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new RuntimeException(e); + } + // generate secret key + KeyGenerator keyGenerator; + try { + keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); + } catch (NoSuchAlgorithmException | NoSuchProviderException e) { + throw new RuntimeException(e); + } + KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder("realm_key", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT).setBlockModes(KeyProperties.BLOCK_MODE_CBC).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7).setUserAuthenticationRequired(false).build(); + try { + keyGenerator.init(keySpec); + } catch (InvalidAlgorithmParameterException e) { + throw new RuntimeException(e); + } + keyGenerator.generateKey(); + // access the generated key in the android keystore, then + // use the cipher to create an encrypted version of the key + byte[] initializationVector; + byte[] encryptedKeyForRealm; + try { + SecretKey secretKey = (SecretKey) keyStore.getKey("realm_key", null); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + encryptedKeyForRealm = cipher.doFinal(realmKey); + initializationVector = cipher.getIV(); + } catch (InvalidKeyException | UnrecoverableKeyException | NoSuchAlgorithmException | + KeyStoreException | BadPaddingException | IllegalBlockSizeException e) { + throw new RuntimeException(e); + } + // keep the encrypted key in shared preferences + // to persist it across application runs + byte[] initializationVectorAndEncryptedKey = new byte[Integer.BYTES + initializationVector.length + encryptedKeyForRealm.length]; + ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey); + buffer.order(ByteOrder.BIG_ENDIAN); + buffer.putInt(initializationVector.length); + buffer.put(initializationVector); + buffer.put(encryptedKeyForRealm); + MainApplication.getSharedPreferences("realm_key").edit().putString("iv_and_encrypted_key", Base64.encodeToString(initializationVectorAndEncryptedKey, Base64.NO_WRAP)).apply(); + MainApplication.getSharedPreferences("mmm").edit().putBoolean("keygen", true).apply(); + return realmKey; // pass to a realm configuration via encryptionKey() + } } \ No newline at end of file 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 91871f6..cf24466 100644 --- a/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java +++ b/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java @@ -208,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("mmm").getBoolean("first_time_setup_done", true)) + if (!Objects.equals(MainApplication.getSharedPreferences("mmm").getString("last_shown_setup", null), "v1")) return; // create notification channel group if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -219,7 +219,7 @@ public class BackgroundUpdateChecker extends Worker { NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context); notificationManagerCompat.createNotificationChannel(new NotificationChannelCompat.Builder(NOTIFICATION_CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_HIGH).setShowBadge(true).setName(context.getString(R.string.notification_update_pref)).setDescription(context.getString(R.string.auto_updates_notifs)).setGroup(NOTFIICATION_GROUP).build()); notificationManagerCompat.cancel(BackgroundUpdateChecker.NOTIFICATION_ID); - WorkManager.getInstance(context).enqueueUniquePeriodicWork("background_checker", ExistingPeriodicWorkPolicy.REPLACE, new PeriodicWorkRequest.Builder(BackgroundUpdateChecker.class, 6, TimeUnit.HOURS).setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build()).build()); + WorkManager.getInstance(context).enqueueUniquePeriodicWork("background_checker", ExistingPeriodicWorkPolicy.UPDATE, new PeriodicWorkRequest.Builder(BackgroundUpdateChecker.class, 6, TimeUnit.HOURS).setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build()).build()); } public static void onMainActivityResume(Context context) { 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 976e989..0da695a 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java +++ b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java @@ -54,6 +54,10 @@ public final class ModuleManager extends SyncManager { } protected void scanInternal(@NonNull UpdateListener updateListener) { + // if last_shown_setup is not "v1", them=n refuse to continue + if (!MainApplication.getSharedPreferences("mmm").getString("last_shown_setup", "").equals("v1")) { + return; + } boolean firstScan = this.bootPrefs.getBoolean("mm_first_scan", true); SharedPreferences.Editor editor = firstScan ? this.bootPrefs.edit() : null; for (ModuleInfo v : this.moduleInfos.values()) { @@ -90,7 +94,7 @@ public final class ModuleManager extends SyncManager { // if the dir name matches the module name, use it as the cache dir File tempCacheRoot = new File(dir.toString()); Timber.d("Looking for cache in %s", tempCacheRoot); - realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(tempCacheRoot).build(); + realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(tempCacheRoot).build(); Realm realm = Realm.getInstance(realmConfiguration); 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(); 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 431b15d..dabea3d 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java @@ -36,11 +36,11 @@ import io.realm.RealmResults; import timber.log.Timber; public class RepoData extends XRepo { - public final String url; - public final String id; - public final File cacheRoot; - public final SharedPreferences cachedPreferences; - public final HashMap moduleHashMap; + public String url; + public String id; + public File cacheRoot; + public SharedPreferences cachedPreferences; + public HashMap moduleHashMap; public final JSONObject supportedProperties = new JSONObject(); private final Object populateLock = new Object(); public JSONObject metaDataCache; @@ -72,6 +72,10 @@ public class RepoData extends XRepo { private boolean forceHide, enabled; // Cache for speed public RepoData(String url, File cacheRoot, SharedPreferences cachedPreferences) { + // if last_shown_setup is not "v1", them=n refuse to continue + if (!cachedPreferences.getString("last_shown_setup", "").equals("v1")) { + return; + } // setup supportedProperties try { supportedProperties.put("id", ""); @@ -106,7 +110,7 @@ public class RepoData extends XRepo { this.defaultName = url; // Set url as default name this.forceHide = AppUpdateManager.shouldForceHide(this.id); // this.enable is set from the database - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm = Realm.getInstance(realmConfiguration); ReposList reposList = realm.where(ReposList.class).equalTo("id", this.id).findFirst(); if (BuildConfig.DEBUG) { @@ -292,7 +296,7 @@ public class RepoData extends XRepo { public void setEnabled(boolean enabled) { this.enabled = enabled && !this.forceHide; // reposlist realm - RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); realm2.executeTransaction(realm -> { ReposList reposList = realm.where(ReposList.class).equalTo("id", this.id).findFirst(); @@ -310,7 +314,7 @@ public class RepoData extends XRepo { } this.forceHide = AppUpdateManager.shouldForceHide(this.id); // reposlist realm - RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); boolean dbEnabled; try { @@ -369,12 +373,12 @@ public class RepoData extends XRepo { // should update (lastUpdate > 15 minutes) public boolean shouldUpdate() { Timber.d("Repo " + this.id + " should update check called"); - RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); ReposList repo = realm2.where(ReposList.class).equalTo("id", this.id).findFirst(); // Make sure ModuleListCache for repoId is not null File cacheRoot = MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + this.id); - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); Realm realm = Realm.getInstance(realmConfiguration); RealmResults moduleListCache = realm.where(ModuleListCache.class).equalTo("repoId", this.id).findAll(); if (repo != null) { diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java index fb2c207..4027604 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java @@ -147,6 +147,10 @@ public final class RepoManager extends SyncManager { @SuppressWarnings("StatementWithEmptyBody") private void populateDefaultCache(RepoData repoData) { + // if last_shown_setup is not "v1", them=n refuse to continue + if (!MainApplication.getSharedPreferences("mmm").getString("last_shown_setup", "").equals("v1")) { + return; + } for (RepoModule repoModule : repoData.moduleHashMap.values()) { if (!repoModule.moduleInfo.hasFlag(ModuleInfo.FLAG_METADATA_INVALID)) { RepoModule registeredRepoModule = this.modules.get(repoModule.id); 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 877fb77..36ba289 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java @@ -49,11 +49,11 @@ public class RepoUpdater { if (!this.repoData.shouldUpdate()) { Timber.d("Fetching index from cache for %s", this.repoData.id); File cacheRoot = MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + this.repoData.id); - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); Realm realm = Realm.getInstance(realmConfiguration); RealmResults results = realm.where(ModuleListCache.class).equalTo("repoId", this.repoData.id).findAll(); // reposlist realm - RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); this.toUpdate = Collections.emptyList(); this.toApply = new HashSet<>(); @@ -122,7 +122,7 @@ public class RepoUpdater { // use realm to insert to // props avail: File cacheRoot = MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/" + this.repoData.id); - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); // array with module info default values // supported properties for a module //id= @@ -333,7 +333,7 @@ public class RepoUpdater { Timber.w("Failed to get module info from %s with error %s", this.repoData.id, e.getMessage()); } this.indexRaw = null; - RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); if (realm2.isInTransaction()) { realm2.cancelTransaction(); 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 e02d636..967b619 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -850,7 +850,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { }); } // Get magisk_alt_repo enabled state from realm db - RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm1 = Realm.getInstance(realmConfig); ReposList reposList = realm1.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst(); if (reposList != null) { @@ -885,7 +885,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { SwitchPreferenceCompat switchPreferenceCompat = (SwitchPreferenceCompat) androidacyRepoEnabled; switchPreferenceCompat.setChecked(false); // Disable in realm db - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm = Realm.getInstance(realmConfiguration); realm.executeTransaction(realm2 -> { ReposList repoRealmResults = realm2.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); @@ -898,7 +898,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { }); } // get if androidacy repo is enabled from realm db - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm = Realm.getInstance(realmConfiguration); ReposList repoRealmResults = realm.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); if (repoRealmResults == null) { @@ -1058,7 +1058,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { @SuppressLint("RestrictedApi") public void updateCustomRepoList(boolean initial) { - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm = Realm.getInstance(realmConfiguration); // get all repos that are not built-in int CUSTOM_REPO_ENTRIES = 0; @@ -1184,7 +1184,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { return; if (!preferenceName.contains("androidacy") && !preferenceName.contains("magisk_alt_repo")) { if (repoData != null) { - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(MainApplication.getINSTANCE().getExistingKey()).build(); Realm realm = Realm.getInstance(realmConfiguration); RealmResults repoDataRealmResults = realm.where(ReposList.class).equalTo("id", repoData.id).findAll(); Timber.d("Setting preference " + preferenceName + " because it is not the Androidacy repo or the Magisk Alt Repo"); 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 1c37b45..f8afe9c 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 @@ -29,7 +29,7 @@ public class SentryMain { public static void initialize(final MainApplication mainApplication) { // If first_launch pref is not false, refuse to initialize Sentry SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm"); - if (sharedPreferences.getBoolean("first_time_setup_done", true)) { + if (!Objects.equals(sharedPreferences.getString("last_shown_setup", null), "v1")) { return; } Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {