From a54ecbc6d8f56523cc3c152c4142750d9a1159dc Mon Sep 17 00:00:00 2001 From: androidacy-user Date: Mon, 23 Jan 2023 11:44:03 -0500 Subject: [PATCH] more work on realm Signed-off-by: androidacy-user --- app/build.gradle | 10 +- .../com/fox2code/mmm/AppUpdateManager.java | 13 +- .../java/com/fox2code/mmm/CrashHandler.java | 78 +++++-- .../com/fox2code/mmm/MainApplication.java | 37 +-- .../java/com/fox2code/mmm/SetupActivity.java | 21 +- .../mmm/androidacy/AndroidacyRepoData.java | 5 - .../com/fox2code/mmm/repo/RepoManager.java | 4 +- .../com/fox2code/mmm/repo/RepoUpdater.java | 217 ++++++++++-------- .../java/com/fox2code/mmm/utils/io/Http.java | 5 + .../fox2code/mmm/utils/sentry/SentryMain.java | 13 +- app/src/main/res/values/strings.xml | 2 +- build.gradle | 9 +- settings.gradle | 3 + 13 files changed, 256 insertions(+), 161 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 42d5938..83b9b51 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,6 +12,7 @@ apply plugin: "realm-android" android { namespace "com.fox2code.mmm" compileSdk 33 + ndkVersion "25.1.8937393" signingConfigs { release { // Everything comes from local.properties @@ -288,7 +289,12 @@ dependencies { implementation 'com.github.topjohnwu.libsu:io:5.0.1' implementation 'com.github.Fox2Code:RosettaX:1.0.9' implementation 'com.github.Fox2Code:AndroidANSI:1.0.1' + + // sentry implementation "io.sentry:sentry-android:$sentry_version" + implementation "io.sentry:sentry-android-timber:$sentry_version" + implementation "io.sentry:sentry-android-fragment:$sentry_version" + implementation "io.sentry:sentry-android-okhttp:$sentry_version" // Markdown implementation "io.noties.markwon:core:4.6.2" @@ -307,7 +313,9 @@ dependencies { // timber implementation 'com.jakewharton.timber:timber:5.0.1' - implementation "io.sentry:sentry-android-timber:$sentry_version" + + // ksp + implementation "com.google.devtools.ksp:symbol-processing-api:1.8.0-1.0.8" } if (hasSentryConfig) { diff --git a/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java b/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java index ec90d5d..34b6815 100644 --- a/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java +++ b/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java @@ -150,12 +150,19 @@ public class AppUpdateManager { // Convert both BuildConfig.VERSION_NAME and latestRelease to int int currentVersion = 0, latestVersion = 0; try { - currentVersion = Integer.parseInt(BuildConfig.VERSION_NAME.replace(".", "")); - latestVersion = Integer.parseInt(this.latestRelease.replace(".", "")); + currentVersion = Integer.parseInt(BuildConfig.VERSION_NAME.replaceAll("\\D", "")); + latestVersion = Integer.parseInt(this.latestRelease.replace("v", "").replaceAll("\\D", "")); } catch ( NumberFormatException ignored) { } - return currentVersion < latestVersion || (this.preReleaseNewer && currentVersion < Integer.parseInt(this.latestPreRelease.replace(".", ""))); + int latestPreReleaseVersion = 0; + // replace all non-numeric characters with empty string + try { + latestPreReleaseVersion = Integer.parseInt(this.latestPreRelease.replaceAll("\\D", "")); + } catch ( + NumberFormatException ignored) { + } + return currentVersion < latestVersion || (this.preReleaseNewer && currentVersion < latestPreReleaseVersion); } public boolean peekHasUpdate() { diff --git a/app/src/main/java/com/fox2code/mmm/CrashHandler.java b/app/src/main/java/com/fox2code/mmm/CrashHandler.java index 7db8852..9f97de8 100644 --- a/app/src/main/java/com/fox2code/mmm/CrashHandler.java +++ b/app/src/main/java/com/fox2code/mmm/CrashHandler.java @@ -11,17 +11,28 @@ import android.widget.Toast; import com.fox2code.foxcompat.app.FoxActivity; import com.google.android.material.textview.MaterialTextView; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.URL; -import io.sentry.Sentry; -import io.sentry.UserFeedback; -import io.sentry.protocol.SentryId; +import timber.log.Timber; public class CrashHandler extends FoxActivity { @SuppressLint("RestrictedApi") @Override protected void onCreate(Bundle savedInstanceState) { + Timber.i("CrashHandler.onCreate(%s)", savedInstanceState); + // log intent with extras + Timber.d("CrashHandler.onCreate: intent=%s", getIntent()); + // get exception, stacktrace, and lastEventId from intent and log them + Timber.d("CrashHandler.onCreate: exception=%s", getIntent().getSerializableExtra("exception")); + Timber.d("CrashHandler.onCreate: stacktrace=%s", getIntent().getSerializableExtra("stacktrace")); + Timber.d("CrashHandler.onCreate: lastEventId=%s", getIntent().getStringExtra("lastEventId")); super.onCreate(savedInstanceState); setContentView(R.layout.activity_crash_handler); // set crash_details MaterialTextView to the exception passed in the intent or unknown if null @@ -43,11 +54,12 @@ public class CrashHandler extends FoxActivity { stacktrace = stacktrace.replace(",", "\n "); crashDetails.setText(getString(R.string.crash_full_stacktrace, stacktrace)); } + SharedPreferences preferences = getSharedPreferences("sentry", MODE_PRIVATE); + // get lastEventId from intent + String lastEventId = getIntent().getStringExtra("lastEventId"); // disable feedback if sentry is disabled - if (MainApplication.isCrashReportingEnabled()) { - SharedPreferences preferences = getSharedPreferences("sentry", MODE_PRIVATE); - // get lastEventId from intent - SentryId lastEventId = Sentry.captureException((Throwable) getIntent().getSerializableExtra("exception")); + //noinspection ConstantConditions + if (MainApplication.isCrashReportingEnabled() && !BuildConfig.SENTRY_TOKEN.equals("") && lastEventId != null) { // get name, email, and message fields EditText name = findViewById(R.id.feedback_name); EditText email = findViewById(R.id.feedback_email); @@ -60,17 +72,48 @@ public class CrashHandler extends FoxActivity { return; } // if email or name is empty, use "Anonymous" - String nameString = name.getText().toString().equals("") ? "Anonymous" : name.getText().toString(); - String emailString = email.getText().toString().equals("") ? "Anonymous" : email.getText().toString(); + final String[] nameString = {name.getText().toString().equals("") ? "Anonymous" : name.getText().toString()}; + final String[] emailString = {email.getText().toString().equals("") ? "Anonymous" : email.getText().toString()}; // Prevent strict mode violation + // create sentry userFeedback request new Thread(() -> { - // create sentry userFeedback request - UserFeedback userFeedback = new UserFeedback(lastEventId); - userFeedback.setName(nameString); - userFeedback.setEmail(emailString); - userFeedback.setComments(description.getText().toString()); - // send the request - Sentry.captureUserFeedback(userFeedback); + try { + HttpURLConnection connection = (HttpURLConnection) new URL("https" + "://sentry.io/api/0/projects/androidacy-i6/foxmmm/user-feedback/").openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("Authorization", "Bearer " + BuildConfig.SENTRY_TOKEN); + // Setups the JSON body + if (nameString[0].equals("")) + nameString[0] = "Anonymous"; + if (emailString[0].equals("")) + emailString[0] = "Anonymous"; + JSONObject body = new JSONObject(); + body.put("event_id", lastEventId); + body.put("name", nameString[0]); + body.put("email", emailString[0]); + body.put("comments", description.getText().toString()); + // Send the request + connection.setDoOutput(true); + connection.getOutputStream().write(body.toString().getBytes()); + connection.connect(); + // For debug builds, log the response code and response body + if (BuildConfig.DEBUG) { + Timber.d("Response Code: %s", connection.getResponseCode()); + } + // Check if the request was successful + if (connection.getResponseCode() == 200) { + runOnUiThread(() -> Toast.makeText(this, R.string.sentry_dialogue_success, Toast.LENGTH_LONG).show()); + } else { + runOnUiThread(() -> Toast.makeText(this, R.string.sentry_dialogue_failed_toast, Toast.LENGTH_LONG).show()); + } + // close and disconnect the connection + connection.disconnect(); + } catch ( + JSONException | + IOException ignored) { + // Show a toast if the user feedback could not be submitted + runOnUiThread(() -> Toast.makeText(this, R.string.sentry_dialogue_failed_toast, Toast.LENGTH_LONG).show()); + } }).start(); // Close the activity finish(); @@ -123,7 +166,8 @@ public class CrashHandler extends FoxActivity { new Thread(() -> { try { Thread.sleep(1000); - } catch (InterruptedException e) { + } catch ( + InterruptedException e) { e.printStackTrace(); } runOnUiThread(() -> view.setBackgroundResource(R.drawable.baseline_copy_all_24)); diff --git a/app/src/main/java/com/fox2code/mmm/MainApplication.java b/app/src/main/java/com/fox2code/mmm/MainApplication.java index 3890df5..9a9486a 100644 --- a/app/src/main/java/com/fox2code/mmm/MainApplication.java +++ b/app/src/main/java/com/fox2code/mmm/MainApplication.java @@ -47,11 +47,16 @@ import io.noties.markwon.html.HtmlPlugin; import io.noties.markwon.image.ImagesPlugin; import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler; import io.realm.Realm; +import io.sentry.IHub; import io.sentry.Sentry; import io.sentry.SentryLevel; +import io.sentry.android.timber.SentryTimberTree; import timber.log.Timber; public class MainApplication extends FoxApplication implements androidx.work.Configuration.Provider { + // Warning! Locales that are't exist will crash the app + // Anything that is commented out is supported but the translation is not complete to at least 60% + public static final HashSet supportedLocales = new HashSet<>(); private static final String timeFormatString = "dd MMM yyyy"; // Example: 13 july 2001 private static final Shell.Builder shellBuilder; private static final long secret; @@ -59,9 +64,6 @@ public class MainApplication extends FoxApplication implements androidx.work.Con // Use FoxProcess wrapper helper. private static final boolean wrapped = !FoxProcessExt.isRootLoader(); public static boolean isOfficial = false; - // Warning! Locales that are't exist will crash the app - // Anything that is commented out is supported but the translation is not complete to at least 60% - public static final HashSet supportedLocales = new HashSet<>(); private static Locale timeFormatLocale = Resources.getSystem().getConfiguration().getLocales().get(0); private static SimpleDateFormat timeFormat = new SimpleDateFormat(timeFormatString, timeFormatLocale); private static SharedPreferences bootSharedPreferences; @@ -282,8 +284,16 @@ public class MainApplication extends FoxApplication implements androidx.work.Con @Override public void onCreate() { // init timber - if (BuildConfig.DEBUG) Timber.plant(new Timber.DebugTree()); - else Timber.plant(new ReleaseTree()); + if (BuildConfig.DEBUG) { + Timber.plant(new Timber.DebugTree()); + } else { + if (isCrashReportingEnabled()) { + @SuppressWarnings("UnstableApiUsage") IHub hub = Sentry.getCurrentHub(); + Timber.plant(new SentryTimberTree(hub, SentryLevel.ERROR, SentryLevel.ERROR)); + } else { + Timber.plant(new ReleaseTree()); + } + } // supportedLocales.add("ar"); // supportedLocales.add("ar_SA"); supportedLocales.add("cs"); @@ -479,17 +489,16 @@ public class MainApplication extends FoxApplication implements androidx.work.Con } private static class ReleaseTree extends Timber.Tree { - @SuppressWarnings("StatementWithEmptyBody") @Override protected void log(int priority, String tag, @NonNull String message, Throwable t) { - if (priority == Log.VERBOSE || priority == Log.DEBUG) { - // silently ignore - } else if (priority == Log.INFO) { - Sentry.captureMessage(message, SentryLevel.INFO); - } else if (priority == Log.WARN) { - Sentry.captureMessage(message, SentryLevel.WARNING); - } else if (priority == Log.ERROR) { - Sentry.captureException(t); + // basically silently drop all logs below error, and write the rest to logcat + if (priority >= Log.ERROR) { + if (t != null) { + Log.println(priority, tag, message); + t.printStackTrace(); + } else { + Log.println(priority, tag, message); + } } } } diff --git a/app/src/main/java/com/fox2code/mmm/SetupActivity.java b/app/src/main/java/com/fox2code/mmm/SetupActivity.java index dd05fd1..7a58a8e 100644 --- a/app/src/main/java/com/fox2code/mmm/SetupActivity.java +++ b/app/src/main/java/com/fox2code/mmm/SetupActivity.java @@ -18,7 +18,6 @@ import com.fox2code.foxcompat.app.FoxActivity; import com.fox2code.mmm.androidacy.AndroidacyRepoData; import com.fox2code.mmm.databinding.ActivitySetupBinding; import com.fox2code.mmm.repo.RepoManager; -import com.fox2code.mmm.utils.realm.ModuleListCache; import com.fox2code.mmm.utils.realm.ReposList; import com.fox2code.rosettax.LanguageActivity; import com.fox2code.rosettax.LanguageSwitcher; @@ -275,17 +274,6 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { // creates the realm database private void createRealmDatabase() { Timber.d("Creating Realm databases"); - // create the realm database for ModuleListCache - RealmConfiguration config = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).build(); - // do a dummy write to create the database - Realm.getInstanceAsync(config, new Realm.Callback() { - @Override - public void onSuccess(@NonNull Realm realm) { - realm.executeTransactionAsync(realm1 -> { - // do nothing - }); - } - }); // create the realm database for ReposList // next, create the realm database for ReposList RealmConfiguration config2 = new RealmConfiguration.Builder().name("ReposList.realm").directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); @@ -332,16 +320,9 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { realm1.close(); if (BuildConfig.DEBUG) { Timber.d("Realm databases created"); - // log each database - Realm realm2 = Realm.getInstance(config); - RealmResults moduleListCaches = realm2.where(ModuleListCache.class).findAll(); - Timber.d("ModuleListCache.realm"); - for (ModuleListCache moduleListCache : moduleListCaches) { - Timber.d(moduleListCache.toString()); - } - realm2.close(); Realm realm3 = Realm.getInstance(config2); RealmResults reposLists = realm3.where(ReposList.class).findAll(); + assert reposLists != null; Timber.d("ReposList.realm"); for (ReposList reposList : reposLists) { Timber.d(reposList.toString()); diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java index ef08a95..5e26e90 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java @@ -36,8 +36,6 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; -import io.realm.Realm; -import io.realm.RealmConfiguration; import okhttp3.HttpUrl; import timber.log.Timber; @@ -64,9 +62,6 @@ public final class AndroidacyRepoData extends RepoData { public AndroidacyRepoData(File cacheRoot, SharedPreferences cachedPreferences, boolean testMode) { super(testMode ? RepoManager.ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT : RepoManager.ANDROIDACY_MAGISK_REPO_ENDPOINT, cacheRoot, cachedPreferences); - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").allowWritesOnUiThread(true).allowWritesOnUiThread(true).directory(cacheRoot).build(); - Realm.setDefaultConfiguration(realmConfiguration); - Realm.getInstance(realmConfiguration); this.defaultName = "Androidacy Modules Repo"; this.defaultWebsite = RepoManager.ANDROIDACY_MAGISK_REPO_HOMEPAGE; this.defaultSupport = "https://t.me/androidacy_discussions"; 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 82fafa6..8ab4c88 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java @@ -383,7 +383,7 @@ public final class RepoManager extends SyncManager { private RepoData addRepoData(String url, String fallBackName) { String id = internalIdOfUrl(url); - File cacheRoot = new File(this.mainApplication.getDataDir(), id); + File cacheRoot = new File(this.mainApplication.getDataDir(), "repos/" + id); SharedPreferences sharedPreferences = this.mainApplication.getSharedPreferences("mmm_" + id, Context.MODE_PRIVATE); RepoData repoData = id.startsWith("repo_") ? new CustomRepoData(url, cacheRoot, sharedPreferences) : new RepoData(url, cacheRoot, sharedPreferences); if (fallBackName != null && !fallBackName.isEmpty()) { @@ -409,7 +409,7 @@ public final class RepoManager extends SyncManager { private AndroidacyRepoData addAndroidacyRepoData() { // cache dir is actually under app data - File cacheRoot = new File(this.mainApplication.getDataDir(), "androidacy_repo"); + File cacheRoot = new File(this.mainApplication.getDataDir(), "repos/androidacy_repo"); SharedPreferences sharedPreferences = this.mainApplication.getSharedPreferences("mmm_androidacy_repo", Context.MODE_PRIVATE); AndroidacyRepoData repoData = new AndroidacyRepoData(cacheRoot, sharedPreferences, MainApplication.isAndroidacyTestMode()); this.repoData.put(ANDROIDACY_MAGISK_REPO_ENDPOINT, repoData); 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 71d7cd0..7179e17 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java @@ -1,13 +1,12 @@ package com.fox2code.mmm.repo; -import androidx.annotation.NonNull; - import com.fox2code.mmm.utils.io.Http; import com.fox2code.mmm.utils.realm.ModuleListCache; -import org.json.JSONException; +import org.json.JSONArray; import org.json.JSONObject; +import java.io.File; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Collections; @@ -84,8 +83,8 @@ public class RepoUpdater { // iterate over modules, using this.supportedProperties as a template to attempt to get each property from the module. everything that is not null is added to the module // use realm to insert to // props avail: - // - RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).deleteRealmIfMigrationNeeded().build(); + File cacheRoot = this.repoData.cacheRoot; + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).deleteRealmIfMigrationNeeded().allowWritesOnUiThread(true).allowQueriesOnUiThread(true).directory(cacheRoot).build(); // array with module info default values // supported properties for a module //id= @@ -109,32 +108,21 @@ public class RepoUpdater { //installedVersionCode= (only if installed) // // all except first six can be null - // this.indexRaw is the raw index file (json) and the modules can be either under the "modules" key or the "data" key - // both are arrays of modules - // try to get modules from "modules" key + // this.indexRaw is the raw index file (json) JSONObject modules = new JSONObject(new String(this.indexRaw, StandardCharsets.UTF_8)); - // check if modules has key "modules" or "data" - try { - if (modules.has("modules")) { - // get modules from "modules" key - modules = modules.getJSONObject("modules"); - } else if (modules.has("data")) { - // get modules from "data" key - modules = modules.getJSONObject("data"); - } - } catch (JSONException e) { - // there's a possibility that the modules key is an array, so we need to convert it to a json object - // get modules array - JSONObject[] modulesArray = new JSONObject[]{modules}; - // create new json object - modules = new JSONObject(); - // iterate over modules array - for (int i = 0; i < modulesArray.length; i++) { - // put module in json object - modules.put(String.valueOf(i), modulesArray[i]); - } + JSONArray modulesArray; + // androidacy repo uses "data" key, others should use "modules" key. Both are JSONArrays + if (this.repoData.getName().equals("Androidacy Modules Repo")) { + // get modules from "data" key. This is a JSONArray so we need to convert it to a JSONObject + modulesArray = modules.getJSONArray("data"); + } else { + // get modules from "modules" key. This is a JSONArray so we need to convert it to a JSONObject + modulesArray = modules.getJSONArray("modules"); } - for (JSONObject module : new JSONObject[]{modules}) { + // iterate over modules. pls dont hate me for this, its ugly but it works + for (int n = 0; n < modulesArray.length(); n++) { + // get module + JSONObject module = modulesArray.getJSONObject(n); try { // get module id String id = module.getString("id"); @@ -149,23 +137,74 @@ public class RepoUpdater { // get module description String description = module.getString("description"); // get module min api - int minApi = module.getInt("minApi"); - // get module max api - int maxApi = module.getInt("maxApi"); + String minApi; + if (module.has("minApi") && !module.getString("minApi").equals("")) { + minApi = module.getString("minApi"); + } else { + minApi = "0"; + } + // coerce min api to int + int minApiInt = Integer.parseInt(minApi); + // get module max api and set to 0 if it's "" or null + String maxApi; + if (module.has("maxApi") && !module.getString("maxApi").equals("")) { + maxApi = module.getString("maxApi"); + } else { + maxApi = "0"; + } + // coerce max api to int + int maxApiInt = Integer.parseInt(maxApi); // get module min magisk - int minMagisk = module.getInt("minMagisk"); + String minMagisk; + if (module.has("minMagisk") && !module.getString("minMagisk").equals("")) { + minMagisk = module.getString("minMagisk"); + } else { + minMagisk = "0"; + } + // coerce min magisk to int + int minMagiskInt = Integer.parseInt(minMagisk); // get module need ramdisk - boolean needRamdisk = module.getBoolean("needRamdisk"); + boolean needRamdisk; + if (module.has("needRamdisk")) { + needRamdisk = module.getBoolean("needRamdisk"); + } else { + needRamdisk = false; + } // get module support - String support = module.getString("support"); + String support; + if (module.has("support")) { + support = module.getString("support"); + } else { + support = ""; + } // get module donate - String donate = module.getString("donate"); + String donate; + if (module.has("donate")) { + donate = module.getString("donate"); + } else { + donate = ""; + } // get module config - String config = module.getString("config"); + String config; + if (module.has("config")) { + config = module.getString("config"); + } else { + config = ""; + } // get module change boot - boolean changeBoot = module.getBoolean("changeBoot"); + boolean changeBoot; + if (module.has("changeBoot")) { + changeBoot = module.getBoolean("changeBoot"); + } else { + changeBoot = false; + } // get module mmt reborn - boolean mmtReborn = module.getBoolean("mmtReborn"); + boolean mmtReborn; + if (module.has("mmtReborn")) { + mmtReborn = module.getBoolean("mmtReborn"); + } else { + mmtReborn = false; + } // get module repo id String repoId = this.repoData.id; // get module installed @@ -177,63 +216,53 @@ public class RepoUpdater { // then insert to realm // then commit // then close - Realm.getInstanceAsync(realmConfiguration, new Realm.Callback() { - @Override - public void onSuccess(@NonNull Realm realm) { - realm.executeTransactionAsync(r -> { - // create a new module - ModuleListCache moduleListCache = r.createObject(ModuleListCache.class); - // set module id - moduleListCache.setId(id); - // set module name - moduleListCache.setName(name); - // set module version - moduleListCache.setVersion(version); - // set module version code - moduleListCache.setVersionCode(versionCode); - // set module author - moduleListCache.setAuthor(author); - // set module description - moduleListCache.setDescription(description); - // set module min api - moduleListCache.setMinApi(minApi); - // set module max api - moduleListCache.setMaxApi(maxApi); - // set module min magisk - moduleListCache.setMinMagisk(minMagisk); - // set module need ramdisk - moduleListCache.setNeedRamdisk(needRamdisk); - // set module support - moduleListCache.setSupport(support); - // set module donate - moduleListCache.setDonate(donate); - // set module config - moduleListCache.setConfig(config); - // set module change boot - moduleListCache.setChangeBoot(changeBoot); - // set module mmt reborn - moduleListCache.setMmtReborn(mmtReborn); - // set module repo id - moduleListCache.setRepoId(repoId); - // set module installed - moduleListCache.setInstalled(installed); - // set module installed version code - moduleListCache.setInstalledVersionCode(installedVersionCode); - }, () -> { - // Transaction was a success. - Timber.d("onSuccess: Transaction was a success."); - // close realm - realm.close(); - }, error -> { - // Transaction failed and was automatically canceled. - Timber.e(error); - // close realm - realm.close(); - }); - } + Realm realm = Realm.getInstance(realmConfiguration); + if (realm.isInTransaction()) { + realm.cancelTransaction(); + } + realm.executeTransaction(r -> { + // create the object + // if it already exists, it will be updated + // create a new module + ModuleListCache moduleListCache = r.createObject(ModuleListCache.class, id); + // set module name + moduleListCache.setName(name); + // set module version + moduleListCache.setVersion(version); + // set module version code + moduleListCache.setVersionCode(versionCode); + // set module author + moduleListCache.setAuthor(author); + // set module description + moduleListCache.setDescription(description); + // set module min api + moduleListCache.setMinApi(minApiInt); + // set module max api + moduleListCache.setMaxApi(maxApiInt); + // set module min magisk + moduleListCache.setMinMagisk(minMagiskInt); + // set module need ramdisk + moduleListCache.setNeedRamdisk(needRamdisk); + // set module support + moduleListCache.setSupport(support); + // set module donate + moduleListCache.setDonate(donate); + // set module config + moduleListCache.setConfig(config); + // set module change boot + moduleListCache.setChangeBoot(changeBoot); + // set module mmt reborn + moduleListCache.setMmtReborn(mmtReborn); + // set module repo id + moduleListCache.setRepoId(repoId); + // set module installed + moduleListCache.setInstalled(installed); + // set module installed version code + moduleListCache.setInstalledVersionCode(installedVersionCode); }); + realm.close(); } catch ( - JSONException e) { + Exception e) { e.printStackTrace(); Timber.w("Failed to get module info from module " + module + " in repo " + this.repoData.id + " with error " + e.getMessage()); } diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/Http.java b/app/src/main/java/com/fox2code/mmm/utils/io/Http.java index c4bd7e4..3739894 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/Http.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/Http.java @@ -40,6 +40,7 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; +import io.sentry.android.okhttp.SentryOkHttpInterceptor; import okhttp3.Cache; import okhttp3.Cookie; import okhttp3.CookieJar; @@ -174,6 +175,10 @@ public class Http { Timber.e(e, "Failed to init cronet"); // Gracefully fallback to okhttp } + // add sentry interceptor + if (MainApplication.isCrashReportingEnabled()) { + httpclientBuilder.addInterceptor(new SentryOkHttpInterceptor()); + } // Fallback DNS cache responses in case request fail but already succeeded once in the past fallbackDNS = new FallBackDNS(mainApplication, dns, "github.com", "api.github.com", "raw.githubusercontent.com", "camo.githubusercontent.com", "user-images.githubusercontent.com", "cdn.jsdelivr.net", "img.shields.io", "magisk-modules-repo.github.io", "www.androidacy.com", "api.androidacy.com", "production-api.androidacy.com"); httpclientBuilder.cookieJar(new CDNCookieJar(cookieManager)); diff --git a/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java b/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java index f04631b..1d1d7c3 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java +++ b/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.java @@ -15,6 +15,7 @@ import java.util.Objects; import io.sentry.Sentry; import io.sentry.android.core.SentryAndroid; import io.sentry.android.fragment.FragmentLifecycleIntegration; +import io.sentry.android.timber.SentryTimberIntegration; public class SentryMain { public static final boolean IS_SENTRY_INSTALLED = true; @@ -43,6 +44,8 @@ public class SentryMain { intent.putExtra("exception", throwable); // add stacktrace as string intent.putExtra("stacktrace", throwable.getStackTrace()); + // put lastEventId in intent (get from preferences) + intent.putExtra("lastEventId", String.valueOf(Sentry.getLastEventId())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); mainApplication.startActivity(intent); android.os.Process.killProcess(android.os.Process.myPid()); @@ -55,6 +58,10 @@ public class SentryMain { } else { sentryEnabled = true; // Set sentry state to enabled options.addIntegration(new FragmentLifecycleIntegration(mainApplication, true, true)); + // Enable automatic activity lifecycle breadcrumbs + options.setEnableActivityLifecycleBreadcrumbs(true); + // Enable automatic fragment lifecycle breadcrumbs + options.addIntegration(new SentryTimberIntegration()); options.setCollectAdditionalContext(true); options.setAttachThreads(true); options.setAttachStacktrace(true); @@ -84,8 +91,10 @@ public class SentryMain { // Filter breadrcrumb content from crash report. options.setBeforeBreadcrumb((breadcrumb, hint) -> { String url = (String) breadcrumb.getData("url"); - if (url == null || url.isEmpty()) return breadcrumb; - if ("cloudflare-dns.com".equals(Uri.parse(url).getHost())) return null; + if (url == null || url.isEmpty()) + return breadcrumb; + if ("cloudflare-dns.com".equals(Uri.parse(url).getHost())) + return null; if (AndroidacyUtil.isAndroidacyLink(url)) { breadcrumb.setData("url", AndroidacyUtil.hideToken(url)); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2e72c91..644c4f0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -287,7 +287,7 @@ Choose language Setup Wizard Settings - The following repos have failed to update:\\n\\n%s + The following repos have failed to update:\n\n%s Reset API keys Upgrade to premium Upgrading to premium will remove ads, captchas, and downloads for the Androidacy Repository, and support Androidacy and the module authors. diff --git a/build.gradle b/build.gradle index 5c7893d..a057724 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,12 @@ buildscript { google() mavenCentral() gradlePluginPortal() - maven { url 'https://jitpack.io' } + maven { + url 'https://jitpack.io' + } + maven { + url 'https://oss.sonatype.org/content/repositories/snapshots/' + } } project.ext.latestAboutLibsRelease = "10.5.2" project.ext.sentryConfigFile = new File(rootDir, "sentry.properties").getAbsoluteFile() @@ -22,7 +27,7 @@ buildscript { // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files - classpath "io.realm:realm-gradle-plugin:10.11.1" + classpath "io.realm:realm-gradle-plugin:10.13.1-transformer-api" } } diff --git a/settings.gradle b/settings.gradle index 865356f..8af392b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,6 +6,9 @@ dependencyResolutionManagement { maven { url 'https://jitpack.io' } + maven { + url 'https://oss.sonatype.org/content/repositories/snapshots/' + } } } rootProject.name = "MagiskModuleManager"