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