From 92a0d504433a30c88beedaf3d4be338d93c3e3b7 Mon Sep 17 00:00:00 2001 From: androidacy-user Date: Tue, 28 Mar 2023 21:41:07 -0400 Subject: [PATCH] rework a bunch fof stuff work around alt repo crap (incomplete) update workflow - fixes #296 etc etc Signed-off-by: androidacy-user --- .github/workflows/build-debug.yml | 55 ++--- app/build.gradle | 11 +- app/src/main/AndroidManifest.xml | 2 + .../com/fox2code/mmm/MainApplication.java | 190 ++++++++++++++++++ .../java/com/fox2code/mmm/SetupActivity.java | 172 ++++++++++------ .../mmm/installer/InstallerActivity.java | 46 +++-- .../mmm/installer/InstallerInitializer.java | 2 +- .../fox2code/mmm/manager/ModuleManager.java | 2 +- .../fox2code/mmm/repo/CustomRepoManager.java | 61 +++--- .../java/com/fox2code/mmm/repo/RepoData.java | 10 +- .../com/fox2code/mmm/repo/RepoManager.java | 8 +- .../com/fox2code/mmm/repo/RepoUpdater.java | 14 +- .../mmm/settings/SettingsActivity.java | 46 ++++- .../java/com/fox2code/mmm/utils/io/Files.java | 56 ++++++ app/src/main/res/menu/setup_bottom_nav.xml | 2 + app/src/main/res/values/strings.xml | 10 +- app/src/main/res/xml/root_preferences.xml | 7 + build.gradle | 2 +- gradle.properties | 3 + 19 files changed, 517 insertions(+), 182 deletions(-) diff --git a/.github/workflows/build-debug.yml b/.github/workflows/build-debug.yml index e4ebb29..9822491 100644 --- a/.github/workflows/build-debug.yml +++ b/.github/workflows/build-debug.yml @@ -12,7 +12,7 @@ on: paths-ignore: - '**.md' workflow_dispatch: - + jobs: build: @@ -33,16 +33,20 @@ jobs: java-version: 17 distribution: 'adopt' cache: gradle - + + - name: Setup Android SDK + uses: android-actions/setup-android@v2 + - name: Change wrapper permissions run: chmod +x ./gradlew - + - name: Run tests run: ./gradlew test - name: Build apk debug run: ./gradlew app:assembleDefaultDebug + # will not upload, just build to check if it builds - name: Build apk fdroid-debug run: ./gradlew app:assembleFdroidDebug @@ -54,58 +58,27 @@ jobs: with: name: FoxMMM-default-arm64-v8a-debug path: app/build/outputs/apk/default/debug/FoxMMM-v*-default-arm64-v8a-debug.apk - + - name: Upload FoxMMM-default-armeabi-v7a-debug uses: actions/upload-artifact@v3 with: name: FoxMMM-default-armeabi-v7a-debug path: app/build/outputs/apk/default/debug/FoxMMM-v*-default-armeabi-v7a-debug.apk - + - name: Upload FoxMMM-default-universal-debug uses: actions/upload-artifact@v3 with: name: FoxMMM-default-universal-debug path: app/build/outputs/apk/default/debug/FoxMMM-v*-default-universal-debug.apk - + - name: Upload FoxMMM-default-x86-debug uses: actions/upload-artifact@v3 with: name: FoxMMM-default-x86-debug - path: app/build/outputs/apk/default/debug/FoxMMM-v*-default-x86-debug.apk - - - name: Upload FoxMMM-default-x86_64-debug - uses: actions/upload-artifact@v3 - with: - name: FoxMMM-default-x86_64-debug - path: app/build/outputs/apk/default/debug/FoxMMM-v*-default-x86_64-debug.apk - - # FoxMMM-fdroid-debug - - name: Upload FoxMMM-fdroid-arm64-v8a-debug - uses: actions/upload-artifact@v3 - with: - name: FoxMMM-fdroid-arm64-v8a-debug - path: app/build/outputs/apk/fdroid/debug/FoxMMM-v*-fdroid-arm64-v8a-debug.apk - - - name: Upload FoxMMM-fdroid-armeabi-v7a-debug - uses: actions/upload-artifact@v3 - with: - name: FoxMMM-fdroid-armeabi-v7a-debug - path: app/build/outputs/apk/fdroid/debug/FoxMMM-v*-fdroid-armeabi-v7a-debug.apk - - - name: Upload FoxMMM-fdroid-universal-debug - uses: actions/upload-artifact@v3 - with: - name: FoxMMM-fdroid-universal-debug - path: app/build/outputs/apk/fdroid/debug/FoxMMM-v*-fdroid-universal-debug.apk - - - name: Upload FoxMMM-fdroid-x86-debug - uses: actions/upload-artifact@v3 - with: - name: FoxMMM-fdroid-x86-debug - path: app/build/outputs/apk/fdroid/debug/FoxMMM-v*-fdroid-x86-debug.apk + path: app/build/outputs/apk/default/debug/FoxMMM-v*-default-x86-debug.apk - - name: Upload FoxMMM-fdroid-x86_64-debug + - name: Upload FoxMMM-default-x86_64-debug uses: actions/upload-artifact@v3 with: - name: FoxMMM-fdroid-x86_64-debug - path: app/build/outputs/apk/fdroid/debug/FoxMMM-v*-fdroid-x86_64-debug.apk + name: FoxMMM-default-x86_64-debug + path: app/build/outputs/apk/default/debug/FoxMMM-v*-default-x86_64-debug.apk \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index e1a4557..2a071bf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -295,13 +295,13 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' //noinspection GradleDependency implementation "androidx.activity:activity-ktx:1.7.0-beta01" - implementation 'androidx.emoji2:emoji2:1.2.0' - implementation 'androidx.emoji2:emoji2-views-helper:1.2.0' + implementation 'androidx.emoji2:emoji2:1.3.0' + implementation 'androidx.emoji2:emoji2-views-helper:1.3.0' implementation 'androidx.preference:preference-ktx:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.recyclerview:recyclerview:1.3.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' - implementation 'androidx.webkit:webkit:1.6.0' + implementation 'androidx.webkit:webkit:1.6.1' implementation 'com.google.android.material:material:1.8.0' implementation 'dev.rikka.rikkax.layoutinflater:layoutinflater:1.3.0' implementation "dev.rikka.rikkax.insets:insets:1.3.0" @@ -312,7 +312,7 @@ dependencies { implementation 'com.mikepenz:aboutlibraries:10.6.1' // Utils - implementation 'androidx.work:work-runtime:2.8.0' + implementation 'androidx.work:work-runtime:2.8.1' implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10' implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:5.0.0-alpha.10' // logging interceptor @@ -357,6 +357,9 @@ dependencies { implementation "com.google.devtools.ksp:symbol-processing-api:1.8.10-1.0.9" implementation "androidx.security:security-crypto:1.1.0-alpha05" + + // some utils + implementation 'commons-io:commons-io:2.11.0' } if (hasSentryConfig) { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bcb0ab4..ce8c5c1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -44,6 +44,8 @@ + + { Timber.i("Setup button clicked"); // get instance of editor + Timber.d("Saving preferences"); SharedPreferences.Editor editor = prefs.edit(); + Timber.d("Got editor: %s", editor); // 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()); + Timber.d("Saving preferences"); // 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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).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 realm = Realm.getInstance(realmConfig); - realm.beginTransaction(); - 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(); + Timber.d("Realm instance: %s", realm); + if (realm.isInTransaction()) { + realm.commitTransaction(); + Timber.d("Committed last unfinished transaction"); + } + // check if instance has been closed + if (realm.isClosed()) { + Timber.d("Realm instance was closed, reopening"); + realm = Realm.getInstance(realmConfig); + } + realm.executeTransactionAsync(r -> { + Timber.d("Realm transaction started"); + Objects.requireNonNull(r.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst()).setEnabled(androidacyRepo); + Objects.requireNonNull(r.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst()).setEnabled(magiskAltRepo); + Timber.d("Realm transaction committing"); + // commit the changes + r.commitTransaction(); + r.close(); + Timber.d("Realm transaction committed"); + }); editor.putString("last_shown_setup", "v1"); // Commit the changes editor.commit(); + // sleep to allow the realm transaction to finish + try { + Thread.sleep(250); + } catch (InterruptedException e) { + e.printStackTrace(); + } // Log the changes Timber.d("Setup finished. Preferences: %s", prefs.getAll()); Timber.d("Androidacy repo: %s", androidacyRepo); @@ -181,23 +206,19 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { Timber.d("Last shown setup: %s", prefs.getString("last_shown_setup", "v0")); // Restart the activity MainActivity.doSetupRestarting = true; - Intent intent = new Intent(this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra("doSetupRestarting", true); - startActivity(intent); - finish(); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_IMMUTABLE); + try { + pendingIntent.send(); + } catch (PendingIntent.CanceledException e) { + e.printStackTrace(); + } + android.os.Process.killProcess(android.os.Process.myPid()); }); // Cancel button BottomNavigationItemView cancelButton = view.findViewById(R.id.cancel_setup); cancelButton.setOnClickListener(v -> { Timber.i("Cancel button clicked"); - // Set first launch to false and restart the activity - prefs.edit().putString("last_shown_setup", "v1").commit(); - MainActivity.doSetupRestarting = true; - Intent intent = new Intent(this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra("doSetupRestarting", true); - startActivity(intent); + // close the app finish(); }); } @@ -252,55 +273,78 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { // creates the realm database private void createRealmDatabase() { + if (realmDatabasesCreated) { + Timber.d("Realm databases already created"); + return; + } Timber.d("Creating Realm databases"); long startTime = System.currentTimeMillis(); // create encryption key - // Timber.d("Creating encryption key"); + Timber.d("Creating encryption key"); + byte[] key = MainApplication.getINSTANCE().getNewKey(); // create the realm database for ReposList - // next, create the realm database for ReposList - new Thread(() -> { - // create the realm database for ReposList - // create the realm configuration - RealmConfiguration config2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); - // get the instance - Realm realm1 = Realm.getInstance(config2); - // create androidacy_repo and magisk_alt_repo if they don't exist under ReposList - // each has id, name, donate, website, support, enabled, and lastUpdate and name - // create androidacy_repo - realm1.beginTransaction(); - if (realm1.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst() == null) { - // cant use createObject because it crashes because reasons. use copyToRealm instead - ReposList androidacy_repo = realm1.createObject(ReposList.class, "androidacy_repo"); - androidacy_repo.setName("Androidacy Repo"); - androidacy_repo.setDonate(AndroidacyRepoData.getInstance().getDonate()); - androidacy_repo.setSupport(AndroidacyRepoData.getInstance().getSupport()); - androidacy_repo.setSubmitModule(AndroidacyRepoData.getInstance().getSubmitModule()); - androidacy_repo.setUrl(RepoManager.ANDROIDACY_MAGISK_REPO_ENDPOINT); - androidacy_repo.setEnabled(true); - androidacy_repo.setLastUpdate(0); - androidacy_repo.setWebsite(RepoManager.ANDROIDACY_MAGISK_REPO_HOMEPAGE); - // now copy the data from the data class to the realm object using copyToRealmOrUpdate - realm1.insertOrUpdate(androidacy_repo); + // create the realm configuration + RealmConfiguration config = new RealmConfiguration.Builder().name("ReposList.realm").directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).encryptionKey(key).build(); + // get the instance + Realm.getInstanceAsync(config, new Realm.Callback() { + @Override + public void onSuccess(@NonNull Realm realm) { + Timber.d("Realm instance: %s", realm); + realm.beginTransaction(); + // create the ReposList realm database + Timber.d("Creating ReposList realm database"); + if (realm.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst() == null) { + Timber.d("Creating androidacy_repo"); + // create the androidacy_repo row + // cant use createObject because it crashes because reasons. use copyToRealm instead + ReposList androidacy_repo = realm.createObject(ReposList.class, "androidacy_repo"); + Timber.d("Created androidacy_repo object"); + androidacy_repo.setName("Androidacy Repo"); + Timber.d("Set androidacy_repo name"); + androidacy_repo.setDonate("https://www.androidacy.com/membership-account/membership-join/?utm_source=fox-app&utm_medium=app&utm_campaign=app"); + Timber.d("Set androidacy_repo donate"); + androidacy_repo.setSupport("https://t.me/androidacy_discussions"); + Timber.d("Set androidacy_repo support"); + androidacy_repo.setSubmitModule("https://www.androidacy.com/module-repository-applications/?utm_source=fox-app&utm_medium=app&utm_campaign=app"); + Timber.d("Set androidacy_repo submit module"); + androidacy_repo.setUrl(RepoManager.ANDROIDACY_MAGISK_REPO_ENDPOINT); + Timber.d("Set androidacy_repo url"); + androidacy_repo.setEnabled(true); + Timber.d("Set androidacy_repo enabled"); + androidacy_repo.setLastUpdate(0); + Timber.d("Set androidacy_repo last update"); + androidacy_repo.setWebsite(RepoManager.ANDROIDACY_MAGISK_REPO_HOMEPAGE); + Timber.d("Set androidacy_repo website"); + // now copy the data from the data class to the realm object using copyToRealmOrUpdate + Timber.d("Copying data to realm object"); + realm.copyToRealmOrUpdate(androidacy_repo); + Timber.d("Created androidacy_repo"); + } + // create magisk_alt_repo + if (realm.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst() == null) { + Timber.d("Creating magisk_alt_repo"); + ReposList magisk_alt_repo = realm.createObject(ReposList.class, "magisk_alt_repo"); + Timber.d("Created magisk_alt_repo object"); + magisk_alt_repo.setName("Magisk Alt Repo"); + magisk_alt_repo.setDonate(null); + magisk_alt_repo.setWebsite(RepoManager.MAGISK_ALT_REPO_HOMEPAGE); + magisk_alt_repo.setSupport(null); + magisk_alt_repo.setEnabled(true); + magisk_alt_repo.setUrl(RepoManager.MAGISK_ALT_REPO); + magisk_alt_repo.setSubmitModule(RepoManager.MAGISK_ALT_REPO_HOMEPAGE + "/submission"); + magisk_alt_repo.setLastUpdate(0); + // commit the changes + Timber.d("Copying data to realm object"); + realm.copyToRealmOrUpdate(magisk_alt_repo); + Timber.d("Created magisk_alt_repo"); + } + realm.commitTransaction(); + realmDatabasesCreated = true; + Timber.d("Realm transaction finished"); + long endTime = System.currentTimeMillis(); + Timber.d("Realm databases created in %d ms", endTime - startTime); } - // create magisk_alt_repo - if (realm1.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst() == null) { - ReposList magisk_alt_repo = realm1.createObject(ReposList.class, "magisk_alt_repo"); - magisk_alt_repo.setName("Magisk Alt Repo"); - magisk_alt_repo.setDonate(null); - magisk_alt_repo.setWebsite(RepoManager.MAGISK_ALT_REPO_HOMEPAGE); - magisk_alt_repo.setSupport(null); - magisk_alt_repo.setEnabled(true); - magisk_alt_repo.setUrl(RepoManager.MAGISK_ALT_REPO); - magisk_alt_repo.setSubmitModule(RepoManager.MAGISK_ALT_REPO_HOMEPAGE + "/submission"); - magisk_alt_repo.setLastUpdate(0); - // commit the changes - realm1.insertOrUpdate(magisk_alt_repo); - } - realm1.commitTransaction(); - realm1.close(); - long endTime = System.currentTimeMillis(); - Timber.d("Realm databases created in %d ms", endTime - startTime); - }).start(); + }); } public void createFiles() { diff --git a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java index 5b6b623..80849bf 100644 --- a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java +++ b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java @@ -1,12 +1,14 @@ package com.fox2code.mmm.installer; import android.annotation.SuppressLint; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Bundle; +import android.os.PowerManager; import android.view.KeyEvent; import android.view.View; import android.view.WindowManager; @@ -31,8 +33,8 @@ import com.fox2code.mmm.utils.FastException; import com.fox2code.mmm.utils.IntentHelper; import com.fox2code.mmm.utils.io.Files; import com.fox2code.mmm.utils.io.Hashes; -import com.fox2code.mmm.utils.io.net.Http; import com.fox2code.mmm.utils.io.PropUtils; +import com.fox2code.mmm.utils.io.net.Http; import com.fox2code.mmm.utils.sentry.SentryBreadcrumb; import com.fox2code.mmm.utils.sentry.SentryMain; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -58,6 +60,7 @@ import java.util.zip.ZipInputStream; import timber.log.Timber; public class InstallerActivity extends FoxActivity { + private static final HashSet extracted = new HashSet<>(); public LinearProgressIndicator progressIndicator; public ExtendedFloatingActionButton rebootFloatingButton; public InstallerTerminal installerTerminal; @@ -66,8 +69,7 @@ public class InstallerActivity extends FoxActivity { private boolean textWrap; private boolean canceled; private boolean warnReboot; - - private static final HashSet extracted = new HashSet<>(); + private PowerManager.WakeLock wakeLock; @Override protected void onCreate(Bundle savedInstanceState) { @@ -153,9 +155,12 @@ public class InstallerActivity extends FoxActivity { installTerminal.setItemAnimator(null); this.progressIndicator.setVisibility(View.GONE); this.progressIndicator.setIndeterminate(true); - this.getWindow().setFlags( // Note: Doesn't require WAKELOCK permission + this.getWindow().setFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + // acquire wakelock + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Fox:Installer"); this.progressIndicator.setVisibility(View.VISIBLE); if (urlMode) this.installerTerminal.addLine("- Downloading " + name); String finalTarget = target; @@ -201,6 +206,8 @@ public class InstallerActivity extends FoxActivity { } if (this.canceled) return; Files.fixJavaZipHax(rawModule); + // checks to make sure zip is not a source archive, and if it is, unzips the folder within, switches to it, and zips up the contents of it + Files.fixSourceArchiveShit(rawModule); boolean noPatch = false; boolean isModule = false; boolean isAnyKernel3 = false; @@ -219,7 +226,8 @@ public class InstallerActivity extends FoxActivity { noPatch = true; isModule = true; break; - } if (entryName.equals("META-INF/com/google/android/magisk/module.prop")) { + } + if (entryName.equals("META-INF/com/google/android/magisk/module.prop")) { noPatch = true; isInstallZipModule = true; break; @@ -318,14 +326,14 @@ public class InstallerActivity extends FoxActivity { } installerMonitor = new InstallerMonitor(installScript); installJob = Shell.cmd("export MMM_EXT_SUPPORT=1", - "export MMM_USER_LANGUAGE=" + this.getResources() - .getConfiguration().getLocales().get(0).toLanguageTag(), - "export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME, - "export MMM_TEXT_WRAP=" + (this.textWrap ? "1" : "0"), - AnsiConstants.ANSI_CMD_SUPPORT, - "cd \"" + this.moduleCache.getAbsolutePath() + "\"", - "sh \"" + installScript.getAbsolutePath() + "\"" + - " 3 0 \"" + file.getAbsolutePath() + "\"") + "export MMM_USER_LANGUAGE=" + this.getResources() + .getConfiguration().getLocales().get(0).toLanguageTag(), + "export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME, + "export MMM_TEXT_WRAP=" + (this.textWrap ? "1" : "0"), + AnsiConstants.ANSI_CMD_SUPPORT, + "cd \"" + this.moduleCache.getAbsolutePath() + "\"", + "sh \"" + installScript.getAbsolutePath() + "\"" + + " 3 0 \"" + file.getAbsolutePath() + "\"") .to(installerController, installerMonitor); } else { String arch32 = "true"; // Do nothing by default @@ -508,7 +516,8 @@ public class InstallerActivity extends FoxActivity { } boolean success = installJob.exec().isSuccess(); // Wait one UI cycle before disabling controller or processing results - UiThreadHandler.runAndWait(() -> {}); // to avoid race conditions + UiThreadHandler.runAndWait(() -> { + }); // to avoid race conditions installerController.disable(); String message = "- Install successful"; if (!success) { @@ -561,6 +570,11 @@ public class InstallerActivity extends FoxActivity { } else toDelete = null; this.runOnUiThread(() -> { this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, 0); + // release wakelock + if (wakeLock != null && wakeLock.isHeld()) { + wakeLock.release(); + wakeLock = null; + } // Set the back press to finish the activity and return to the main activity this.setOnBackPressedCallback(a -> { this.finishAndRemoveTask(); @@ -589,7 +603,6 @@ public class InstallerActivity extends FoxActivity { } }); this.rebootFloatingButton.setVisibility(View.VISIBLE); - if (message != null && !message.isEmpty()) this.installerTerminal.addLine(message); if (optionalLink != null && !optionalLink.isEmpty()) { @@ -670,7 +683,8 @@ public class InstallerActivity extends FoxActivity { this.processCommand("showLoading 256"); this.processCommand("setLoading " + progressInt); this.isRecoveryBar = true; - } catch (Exception ignored) {} + } catch (Exception ignored) { + } } else { this.terminal.addLine(s.replace( this.moduleFile.getAbsolutePath(), diff --git a/app/src/main/java/com/fox2code/mmm/installer/InstallerInitializer.java b/app/src/main/java/com/fox2code/mmm/installer/InstallerInitializer.java index 41b3412..457baca 100644 --- a/app/src/main/java/com/fox2code/mmm/installer/InstallerInitializer.java +++ b/app/src/main/java/com/fox2code/mmm/installer/InstallerInitializer.java @@ -159,7 +159,7 @@ public class InstallerInitializer extends Shell.Initializer { public boolean onInit(@NonNull Context context, @NonNull Shell shell) { if (!shell.isRoot()) return true; - // switch to global namespace using the setns syscall + // switch to global namespace return shell.newJob().add("export ASH_STANDALONE=1; nsenter -t 1 -m -u /data/adb/magisk/busybox ash").exec().isSuccess(); } } 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 066a6cf..f823bdd 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java +++ b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java @@ -94,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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).schemaVersion(1).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/CustomRepoManager.java b/app/src/main/java/com/fox2code/mmm/repo/CustomRepoManager.java index d087e6e..30d4943 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/CustomRepoManager.java +++ b/app/src/main/java/com/fox2code/mmm/repo/CustomRepoManager.java @@ -8,7 +8,6 @@ import com.fox2code.mmm.utils.realm.ReposList; import io.realm.Realm; import io.realm.RealmConfiguration; -import timber.log.Timber; public class CustomRepoManager { public static final int MAX_CUSTOM_REPOS = 5; @@ -23,16 +22,19 @@ public class CustomRepoManager { this.repoManager = repoManager; this.customRepos = new String[MAX_CUSTOM_REPOS]; this.customReposCount = 0; + // refuse to load if setup is not complete + if (MainApplication.getPreferences("mmm").getString("last_shown_setup", "").equals("")) { + return; + } SharedPreferences sharedPreferences = this.getSharedPreferences(); int lastFilled = 0; for (int i = 0; i < MAX_CUSTOM_REPOS; i++) { - 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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); Realm realm = Realm.getInstance(realmConfiguration); - try { - realm.beginTransaction(); - } catch (IllegalStateException e) { - Timber.w(e, "Failed to begin transaction"); + if (realm.isInTransaction()) { + realm.commitTransaction(); } + realm.beginTransaction(); // find the matching entry for repo_0, repo_1, etc. ReposList reposList = realm.where(ReposList.class).equalTo("id", "repo_" + i).findFirst(); if (reposList == null) { @@ -41,22 +43,19 @@ public class CustomRepoManager { String repo = reposList.getUrl(); if (!PropUtils.isNullString(repo) && !RepoManager.isBuiltInRepo(repo)) { lastFilled = i; - int index = AUTO_RECOMPILE ? - this.customReposCount : i; + int index = AUTO_RECOMPILE ? this.customReposCount : i; this.customRepos[index] = repo; this.customReposCount++; - ((CustomRepoData) this.repoManager.addOrGet(repo)) - .override = "custom_repo_" + index; + ((CustomRepoData) this.repoManager.addOrGet(repo)).override = "custom_repo_" + index; } } if (AUTO_RECOMPILE && (lastFilled + 1) != this.customReposCount) { - 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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); Realm realm = Realm.getInstance(realmConfiguration); - try { - realm.beginTransaction(); - } catch (IllegalStateException e) { - Timber.w(e, "Failed to begin transaction"); + if (realm.isInTransaction()) { + realm.commitTransaction(); } + realm.beginTransaction(); for (int i = 0; i < MAX_CUSTOM_REPOS; i++) { if (this.customRepos[i] != null) { // find the matching entry for repo_0, repo_1, etc. @@ -71,22 +70,23 @@ public class CustomRepoManager { } private SharedPreferences getSharedPreferences() { - return MainApplication.getPreferences( - "mmm_custom_repos"); + return MainApplication.getPreferences("mmm_custom_repos"); } public CustomRepoData addRepo(String repo) { if (RepoManager.isBuiltInRepo(repo)) throw new IllegalArgumentException("Can't add built-in repo to custom repos"); for (String repoEntry : this.customRepos) { - if (repo.equals(repoEntry)) - return (CustomRepoData) this.repoManager.get(repoEntry); + if (repo.equals(repoEntry)) return (CustomRepoData) this.repoManager.get(repoEntry); } int i = 0; while (customRepos[i] != null) i++; customRepos[i] = repo; - 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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); Realm realm = Realm.getInstance(realmConfiguration); + if (realm.isInTransaction()) { + realm.commitTransaction(); + } realm.beginTransaction(); // find the matching entry for repo_0, repo_1, etc. ReposList reposList = realm.where(ReposList.class).equalTo("id", "repo_" + i).findFirst(); @@ -97,8 +97,7 @@ public class CustomRepoManager { realm.commitTransaction(); customReposCount++; this.dirty = true; - CustomRepoData customRepoData = (CustomRepoData) - this.repoManager.addOrGet(repo); + CustomRepoData customRepoData = (CustomRepoData) this.repoManager.addOrGet(repo); customRepoData.override = "custom_repo_" + i; // Set the enabled state to true customRepoData.setEnabled(true); @@ -109,8 +108,7 @@ public class CustomRepoManager { public CustomRepoData getRepo(int index) { if (index >= MAX_CUSTOM_REPOS) return null; String repo = customRepos[index]; - return repo == null ? null : - (CustomRepoData) this.repoManager.get(repo); + return repo == null ? null : (CustomRepoData) this.repoManager.get(repo); } public void removeRepo(int index) { @@ -118,22 +116,19 @@ public class CustomRepoManager { if (oldRepo != null) { customRepos[index] = null; customReposCount--; - CustomRepoData customRepoData = - (CustomRepoData) this.repoManager.get(oldRepo); + CustomRepoData customRepoData = (CustomRepoData) this.repoManager.get(oldRepo); if (customRepoData != null) { customRepoData.setEnabled(false); customRepoData.override = null; } - this.getSharedPreferences().edit() - .remove("repo_" + index).apply(); + this.getSharedPreferences().edit().remove("repo_" + index).apply(); this.dirty = true; } } public boolean hasRepo(String repo) { for (String repoEntry : this.customRepos) { - if (repo.equals(repoEntry)) - return true; + if (repo.equals(repoEntry)) return true; } return false; } @@ -143,11 +138,9 @@ public class CustomRepoManager { } public boolean canAddRepo(String repo) { - if (RepoManager.isBuiltInRepo(repo) || - this.hasRepo(repo) || !this.canAddRepo()) + if (RepoManager.isBuiltInRepo(repo) || this.hasRepo(repo) || !this.canAddRepo()) return false; - return repo.startsWith("https://") && - repo.indexOf('/', 9) != -1; + return repo.startsWith("https://") && repo.indexOf('/', 9) != -1; } public int getRepoCount() { 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 e7a5892..94ba4b5 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java @@ -105,7 +105,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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); Realm realm = Realm.getInstance(realmConfiguration); ReposList reposList = realm.where(ReposList.class).equalTo("id", this.id).findFirst(); if (BuildConfig.DEBUG) { @@ -291,7 +291,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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); realm2.executeTransaction(realm -> { ReposList reposList = realm.where(ReposList.class).equalTo("id", this.id).findFirst(); @@ -313,7 +313,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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); Realm realm2 = Realm.getInstance(realmConfiguration2); boolean dbEnabled; try { @@ -372,12 +372,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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).schemaVersion(1).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 9707e68..1aa3b2f 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java @@ -52,8 +52,8 @@ public final class RepoManager extends SyncManager { private final MainApplication mainApplication; private final LinkedHashMap repoData; private final HashMap modules; - private final AndroidacyRepoData androidacyRepoData; - private final CustomRepoManager customRepoManager; + private AndroidacyRepoData androidacyRepoData; + private CustomRepoManager customRepoManager; public String repoLastErrorName = null; private boolean hasInternet; private boolean initialized; @@ -65,6 +65,10 @@ public final class RepoManager extends SyncManager { this.mainApplication = mainApplication; this.repoData = new LinkedHashMap<>(); this.modules = new HashMap<>(); + // refuse to load if setup is not complete + if (MainApplication.getPreferences("mmm").getString("last_shown_setup", "").equals("")) { + return; + } // We do not have repo list config yet. this.androidacyRepoData = this.addAndroidacyRepoData(); RepoData altRepo = this.addRepoData(MAGISK_ALT_REPO, "Magisk Modules Alt Repo"); 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 41bb362..8ec84d3 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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).schemaVersion(1).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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); // array with module info default values // supported properties for a module //id= @@ -159,6 +159,9 @@ public class RepoUpdater { } Realm realm = Realm.getInstance(realmConfiguration); // drop old data + if (realm.isInTransaction()) { + realm.commitTransaction(); + } realm.beginTransaction(); realm.where(ModuleListCache.class).equalTo("repoId", this.repoData.id).findAll().deleteAllFromRealm(); realm.commitTransaction(); @@ -297,6 +300,9 @@ public class RepoUpdater { } // create a realm object and insert or update it // add everything to the realm object + if (realm.isInTransaction()) { + realm.commitTransaction(); + } realm.beginTransaction(); ModuleListCache moduleListCache = realm.createObject(ModuleListCache.class, id); moduleListCache.setName(name); @@ -333,7 +339,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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).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 a83af8b..716ee63 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -393,13 +393,42 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { if (!MainApplication.isDeveloper()) { findPreference("pref_disable_low_quality_module_filter").setVisible(false); + // Find pref_clear_data and set it invisible + Objects.requireNonNull((Preference) findPreference("pref_clear_data")).setVisible(false); } + // hande clear cache + findPreference("pref_clear_cache").setOnPreferenceClickListener(preference -> { + // Clear cache + new MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.clear_cache_dialogue_title).setMessage(R.string.clear_cache_dialogue_message).setPositiveButton(R.string.yes, (dialog, which) -> { + // Clear app cache + try { + File cacheDir = requireContext().getCacheDir(); + for (File file : cacheDir.listFiles()) { + if (file.isDirectory()) { + for (File file2 : file.listFiles()) { + if (!file2.delete()) { + Timber.e("Failed to delete %s", file2.getAbsolutePath()); + } + } + } + if (!file.delete()) { + Timber.e("Failed to delete %s", file.getAbsolutePath()); + } + } + Toast.makeText(requireContext(), R.string.cache_cleared, Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + Timber.e(e); + Toast.makeText(requireContext(), R.string.cache_clear_failed, Toast.LENGTH_SHORT).show(); + } + }).setNegativeButton(R.string.no, (dialog, which) -> { + // Do nothing + }).show(); + return true; + }); if (!SentryMain.IS_SENTRY_INSTALLED || !BuildConfig.DEBUG || InstallerInitializer.peekMagiskPath() == null) { // Hide the pref_crash option if not in debug mode - stop users from purposely crashing the app Timber.i(InstallerInitializer.peekMagiskPath()); Objects.requireNonNull((Preference) findPreference("pref_test_crash")).setVisible(false); - // Find pref_clear_data and set it invisible - Objects.requireNonNull((Preference) findPreference("pref_clear_data")).setVisible(false); } else { if (findPreference("pref_test_crash") != null && findPreference("pref_clear_data") != null) { findPreference("pref_test_crash").setOnPreferenceClickListener(preference -> { @@ -850,7 +879,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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); Realm realm1 = Realm.getInstance(realmConfig); ReposList reposList = realm1.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst(); if (reposList != null) { @@ -885,7 +914,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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); Realm realm = Realm.getInstance(realmConfiguration); realm.executeTransaction(realm2 -> { ReposList repoRealmResults = realm2.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); @@ -898,7 +927,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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); Realm realm = Realm.getInstance(realmConfiguration); ReposList repoRealmResults = realm.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); if (repoRealmResults == null) { @@ -1055,7 +1084,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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); Realm realm = Realm.getInstance(realmConfiguration); // get all repos that are not built-in int CUSTOM_REPO_ENTRIES = 0; @@ -1074,6 +1103,9 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { if (preference == null) continue; final int index = i; preference.setOnPreferenceClickListener(preference1 -> { + if (realm.isInTransaction()) { + realm.commitTransaction(); + } realm.beginTransaction(); Objects.requireNonNull(realm.where(ReposList.class).equalTo("id", repoData.id).findFirst()).deleteFromRealm(); realm.commitTransaction(); @@ -1175,7 +1207,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { if (preference == null) 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").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).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/io/Files.java b/app/src/main/java/com/fox2code/mmm/utils/io/Files.java index 5193d8d..7d628cd 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/Files.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/Files.java @@ -10,10 +10,13 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.fox2code.mmm.MainApplication; import com.topjohnwu.superuser.io.SuFile; import com.topjohnwu.superuser.io.SuFileInputStream; import com.topjohnwu.superuser.io.SuFileOutputStream; +import org.apache.commons.io.FileUtils; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; @@ -23,6 +26,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; @@ -188,4 +193,55 @@ public enum Files { zipOutputStream.close(); zipInputStream.close(); } + + public static void fixSourceArchiveShit(byte[] rawModule) { + // unzip the module, check if it has just one folder within. if so, switch to the folder and zip up contents, and replace the original file with that + try { + File tempDir = new File(MainApplication.getINSTANCE().getCacheDir(), "temp"); + if (tempDir.exists()) { + FileUtils.deleteDirectory(tempDir); + } + tempDir.mkdirs(); + File tempFile = new File(tempDir, "module.zip"); + Files.write(tempFile, rawModule); + File tempUnzipDir = new File(tempDir, "unzip"); + tempUnzipDir.mkdirs(); + // unzip + try (ZipInputStream zipInputStream = new ZipInputStream( + new ByteArrayInputStream(rawModule))) { + ZipEntry zipEntry; + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + String name = zipEntry.getName(); + File file = new File(tempUnzipDir, name); + if (zipEntry.isDirectory()) { + file.mkdirs(); + } else { + file.getParentFile().mkdirs(); + try (FileOutputStream fileOutputStream = new FileOutputStream(file)) { + int nRead; + byte[] data = new byte[16384]; + while ((nRead = zipInputStream.read(data, 0, data.length)) != -1) { + fileOutputStream.write(data, 0, nRead); + } + fileOutputStream.flush(); + } + } + } + } catch (Exception e) { + Timber.e(Log.getStackTraceString(e)); + } + File[] files = tempUnzipDir.listFiles(); + if (files != null && files.length == 1 && files[0].isDirectory()) { + File[] files2 = files[0].listFiles(); + if (files2 != null && files2.length > 0) { + File tempZipFile = new File(tempDir, "module2.zip"); + // TODO: zip the contents of the folder + Files.write(tempFile, Files.read(tempZipFile)); + } + } + rawModule = Files.read(tempFile); + } catch (Exception e) { + Timber.e(Log.getStackTraceString(e)); + } + } } diff --git a/app/src/main/res/menu/setup_bottom_nav.xml b/app/src/main/res/menu/setup_bottom_nav.xml index 4ff2f5d..6bdd1af 100644 --- a/app/src/main/res/menu/setup_bottom_nav.xml +++ b/app/src/main/res/menu/setup_bottom_nav.xml @@ -4,6 +4,7 @@ @@ -11,6 +12,7 @@ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a9c20fd..4968643 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -330,7 +330,7 @@ In-app Updater Update app Please wait while we check for and install updates to FoxMMM. This may take a few minutes - Please wait... + Please wait… ERROR: Invalid data received on launch You appear to be running a debug build. Debug builds must be updated manually, and do not support in-app updates ERROR: Invalid action specified. Refusing to continue.Update found @@ -350,7 +350,7 @@ Repos must be served over HTTPS, and must follow the spec outlined in the documentation. The following modules can be updated: %1$s to version %2$s - Checking for updates... + Checking for updates… FoxMMM is checking for updates in the background. Background update status Shows a notification while checking for updates so the system doesn\'t kill it @@ -380,4 +380,10 @@ Require wifi for update checks Require wifi or an otherwise unmetered connection to do update checks Allow app analytics + Clear app cache + This shouldn\'t be necessary normally but may help fix some issues. + Successfully cleared cache + Failed to clear cache + Clear app cache? + This will clear app cache. Your preferences will be saved, but the app may take longer to do some operations temporarily. diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index d9cca61..d7a13c5 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -190,6 +190,13 @@ app:key="pref_clear_data" app:singleLineTitle="false" app:title="@string/clear_app_data" /> + + diff --git a/build.gradle b/build.gradle index 42d8137..6c778e6 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ buildscript { project.ext.sentry_version = "6.16.0" dependencies { //noinspection AndroidGradlePluginVersion - classpath 'com.android.tools.build:gradle:7.4.2' + classpath 'com.android.tools.build:gradle:8.0.0-rc01' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10" classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${latestAboutLibsRelease}" diff --git a/gradle.properties b/gradle.properties index 5e3f284..1f83605 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,3 +22,6 @@ android.enableJetifier=true org.gradle.parallel=true android.enableR8.fullMode=true org.gradle.unsafe.configuration-cache=true +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false