From 2dd1257f3745c8c0237ae1079f619f7db835832d Mon Sep 17 00:00:00 2001 From: androidacy-user Date: Tue, 21 Feb 2023 21:35:50 -0500 Subject: [PATCH] try to fix a few bugs Signed-off-by: androidacy-user --- app/build.gradle | 25 +++-- .../java/com/fox2code/mmm/CrashHandler.java | 18 +++- .../java/com/fox2code/mmm/MainActivity.java | 50 ++++------ .../com/fox2code/mmm/MainApplication.java | 23 ++--- .../fox2code/mmm/manager/ModuleManager.java | 95 ++++++++++++------ .../mmm/module/ModuleViewListBuilder.java | 2 + .../java/com/fox2code/mmm/repo/RepoData.java | 6 +- .../com/fox2code/mmm/repo/RepoManager.java | 96 +++++++------------ .../com/fox2code/mmm/repo/RepoUpdater.java | 9 +- .../java/com/fox2code/mmm/utils/io/Http.java | 4 +- .../fox2code/mmm/utils/sentry/SentryMain.java | 3 +- app/src/main/res/layout/activity_main.xml | 33 ++----- build.gradle | 3 +- 13 files changed, 192 insertions(+), 175 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ca8023d..53a6f5b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,3 +1,4 @@ +import io.sentry.android.gradle.extensions.InstrumentationFeature plugins { // Gradle doesn't allow conditionally enabling/disabling plugins id "io.sentry.android.gradle" version "3.4.1" @@ -30,6 +31,8 @@ android { } enableV3Signing = true enableV4Signing = true + enableV1Signing = true + enableV2Signing = true } debug { // Everything comes from local.properties @@ -47,6 +50,8 @@ android { } enableV3Signing = true enableV4Signing = true + enableV1Signing = true + enableV2Signing = true } } @@ -265,6 +270,7 @@ sentry { // Default is enabled. tracingInstrumentation { enabled = hasSentryConfig + features = EnumSet.allOf(InstrumentationFeature) } // Enable auto-installation of Sentry components (sentry-android SDK and okhttp, timber and fragment integrations). @@ -299,7 +305,7 @@ dependencies { implementation 'com.github.KieronQuinn:MonetCompat:0.4.1' implementation 'com.github.Fox2Code:FoxCompat:0.2.0' // Update the version code in the root build.gradle - implementation "com.mikepenz:aboutlibraries:${latestAboutLibsRelease}" + implementation "com.mikepenz:aboutlibraries:10.6.0" // Utils implementation 'androidx.work:work-runtime:2.8.0' @@ -314,12 +320,13 @@ dependencies { 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" - implementation "io.sentry:sentry-kotlin-extensions:$sentry_version" - implementation "io.sentry:sentry-android-ndk:$sentry_version" + implementation platform('io.sentry:sentry-bom:6.14.0') + implementation "io.sentry:sentry-android" + implementation "io.sentry:sentry-android-timber" + implementation "io.sentry:sentry-android-fragment" + implementation "io.sentry:sentry-android-okhttp" + implementation "io.sentry:sentry-kotlin-extensions" + implementation "io.sentry:sentry-android-ndk" // Markdown implementation "io.noties.markwon:core:4.6.2" @@ -360,8 +367,8 @@ if (hasSentryConfig) { android { ndkVersion '25.1.8937393' dependenciesInfo { - includeInApk true - includeInBundle true + includeInApk false + includeInBundle false } buildFeatures { viewBinding true diff --git a/app/src/main/java/com/fox2code/mmm/CrashHandler.java b/app/src/main/java/com/fox2code/mmm/CrashHandler.java index aa06ff7..4ea63b6 100644 --- a/app/src/main/java/com/fox2code/mmm/CrashHandler.java +++ b/app/src/main/java/com/fox2code/mmm/CrashHandler.java @@ -40,6 +40,8 @@ public class CrashHandler extends FoxActivity { crashDetails.setText(""); // get the exception from the intent Throwable exception = (Throwable) getIntent().getSerializableExtra("exception"); + // get the crashReportingEnabled from the intent + boolean crashReportingEnabled = getIntent().getBooleanExtra("crashReportingEnabled", false); // if the exception is null, set the crash details to "Unknown" if (exception == null) { crashDetails.setText(R.string.crash_details); @@ -55,9 +57,23 @@ public class CrashHandler extends FoxActivity { SharedPreferences preferences = getSharedPreferences("sentry", MODE_PRIVATE); // get lastEventId from intent String lastEventId = getIntent().getStringExtra("lastEventId"); + Timber.d("CrashHandler.onCreate: lastEventId=%s, crashReportingEnabled=%s", lastEventId, crashReportingEnabled); + if (lastEventId == null && crashReportingEnabled) { + // if lastEventId is null, hide the feedback button + findViewById(R.id.feedback).setVisibility(View.GONE); + Timber.d("CrashHandler.onCreate: lastEventId is null but crash reporting is enabled. This may indicate a bug in the crash reporting system."); + } else { + // if lastEventId is not null, show the feedback button + findViewById(R.id.feedback).setVisibility(View.VISIBLE); + // set the name and email fields to the saved values + EditText name = findViewById(R.id.feedback_name); + EditText email = findViewById(R.id.feedback_email); + name.setText(preferences.getString("name", "")); + email.setText(preferences.getString("email", "")); + } // disable feedback if sentry is disabled //noinspection ConstantConditions - if (MainApplication.isCrashReportingEnabled() && !BuildConfig.SENTRY_TOKEN.equals("") && lastEventId != null) { + if (crashReportingEnabled && !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); diff --git a/app/src/main/java/com/fox2code/mmm/MainActivity.java b/app/src/main/java/com/fox2code/mmm/MainActivity.java index e7e9b83..519fbea 100644 --- a/app/src/main/java/com/fox2code/mmm/MainActivity.java +++ b/app/src/main/java/com/fox2code/mmm/MainActivity.java @@ -22,13 +22,11 @@ import android.view.View; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.widget.CheckBox; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.widget.SearchView; import androidx.cardview.widget.CardView; -import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; @@ -47,7 +45,6 @@ import com.fox2code.mmm.module.ModuleViewAdapter; import com.fox2code.mmm.module.ModuleViewListBuilder; import com.fox2code.mmm.repo.RepoManager; import com.fox2code.mmm.settings.SettingsActivity; -import com.fox2code.mmm.utils.BlurUtils; import com.fox2code.mmm.utils.ExternalHelper; import com.fox2code.mmm.utils.io.Http; import com.google.android.material.bottomnavigation.BottomNavigationView; @@ -59,7 +56,6 @@ import org.chromium.net.urlconnection.CronetURLStreamHandlerFactory; import java.net.URL; -import eightbitlab.com.blurview.BlurView; import timber.log.Timber; public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRefreshListener, SearchView.OnQueryTextListener, SearchView.OnCloseListener, OverScrollManager.OverScrollHelper { @@ -77,8 +73,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe private long swipeRefreshBlocker = 0; private int overScrollInsetTop; private int overScrollInsetBottom; - private TextView actionBarPadding; - private BlurView actionBarBlur; private ColorDrawable actionBarBackground; private RecyclerView moduleList; private RecyclerView moduleListOnline; @@ -141,8 +135,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; this.getWindow().setAttributes(layoutParams); } - this.actionBarPadding = findViewById(R.id.action_bar_padding); - this.actionBarBlur = findViewById(R.id.action_bar_blur); this.actionBarBackground = new ColorDrawable(Color.TRANSPARENT); this.progressIndicator = findViewById(R.id.progress_bar); this.swipeRefreshLayout = findViewById(R.id.swipe_refresh); @@ -158,12 +150,12 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe this.moduleList.setAdapter(this.moduleViewAdapter); this.moduleListOnline.setAdapter(this.moduleViewAdapterOnline); this.moduleList.setLayoutManager(new LinearLayoutManager(this)); + // set top padding to 0 + this.moduleList.setPadding(0, 0, 0, 0); this.moduleListOnline.setLayoutManager(new LinearLayoutManager(this)); this.moduleList.setItemViewCacheSize(4); // Default is 2 this.swipeRefreshLayout.setOnRefreshListener(this); - this.actionBarBlur.setBackground(this.actionBarBackground); hideActionBar(); - BlurUtils.setupBlur(this.actionBarBlur, this, R.id.blur_frame); this.updateBlurState(); checkShowInitialSetup(); this.moduleList.addOnScrollListener(new RecyclerView.OnScrollListener() { @@ -194,7 +186,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe // on the bottom nav, there's a settings item. open the settings activity when it's clicked. BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation); // set the bottom padding of the main layout to the height of the bottom nav - findViewById(R.id.root_container).setPadding(0, 0, 0, bottomNavigationView.getHeight()); + findViewById(R.id.root_container).setPadding(0, 0, 0, (bottomNavigationView.getHeight() > 0) ? bottomNavigationView.getHeight() : FoxDisplay.dpToPixel(56)); bottomNavigationView.setOnItemSelectedListener(item -> { if (item.getItemId() == R.id.settings_menu_item) { startActivity(new Intent(MainActivity.this, SettingsActivity.class)); @@ -250,6 +242,15 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe Timber.d("Common next"); moduleViewListBuilder.addNotification(NotificationType.DEBUG); } + NotificationType.NO_INTERNET.autoAdd(moduleViewListBuilderOnline); + // hide progress bar is repo-manager says we have no internet + if (!RepoManager.getINSTANCE().hasConnectivity()) { + runOnUiThread(() -> { + progressIndicator.setVisibility(View.GONE); + progressIndicator.setIndeterminate(false); + progressIndicator.setMax(PRECISION); + }); + } updateScreenInsets(); // Fix an edge case if (waitInitialSetupFinished()) { Timber.d("waiting..."); @@ -294,13 +295,10 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe RepoManager.getINSTANCE().update(value -> runOnUiThread(max == 0 ? () -> progressIndicator.setProgressCompat((int) (value * PRECISION), true) : () -> progressIndicator.setProgressCompat((int) (value * PRECISION * 0.75F), true))); // various notifications NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder); - // Add debug notification for debug builds - if (!NotificationType.DEBUG.shouldRemove()) { - moduleViewListBuilder.addNotification(NotificationType.DEBUG); - } - if (!NotificationType.NO_INTERNET.shouldRemove()) { - moduleViewListBuilder.addNotification(NotificationType.NO_INTERNET); - } else if (!NotificationType.REPO_UPDATE_FAILED.shouldRemove()) { + NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilderOnline); + NotificationType.DEBUG.autoAdd(moduleViewListBuilder); + NotificationType.DEBUG.autoAdd(moduleViewListBuilderOnline); + if (!NotificationType.REPO_UPDATE_FAILED.shouldRemove()) { moduleViewListBuilder.addNotification(NotificationType.REPO_UPDATE_FAILED); } else { // Compatibility data still needs to be updated @@ -312,7 +310,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe if (BuildConfig.DEBUG) Timber.i("Check Json Update"); if (max != 0) { - int current = 0; for (LocalModuleInfo localModuleInfo : ModuleManager.getINSTANCE().getModules().values()) { if (localModuleInfo.updateJson != null) { @@ -371,7 +368,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe int statusBarHeight = getStatusBarHeight(); int actionBarHeight = getActionBarHeight(); int combinedBarsHeight = statusBarHeight + actionBarHeight; - this.actionBarPadding.setMinHeight(combinedBarsHeight); this.swipeRefreshLayout.setProgressViewOffset(false, swipeRefreshLayoutOrigStartOffset + combinedBarsHeight, swipeRefreshLayoutOrigEndOffset + combinedBarsHeight); this.moduleViewListBuilder.setHeaderPx(Math.max(statusBarHeight, combinedBarsHeight - FoxDisplay.dpToPixel(4))); this.moduleViewListBuilder.setFooterPx(FoxDisplay.dpToPixel(4) + bottomInset + this.searchCard.getHeight()); @@ -393,12 +389,9 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe colorBackground = this.getColorCompat(isLightMode ? R.color.white : R.color.black); } if (MainApplication.isBlurEnabled()) { - this.actionBarBlur.setBlurEnabled(true); this.actionBarBackground.setColor(ColorUtils.setAlphaComponent(colorBackground, 0x02)); this.actionBarBackground.setColor(Color.TRANSPARENT); } else { - this.actionBarBlur.setBlurEnabled(false); - this.actionBarBlur.setOverlayColor(Color.TRANSPARENT); this.actionBarBackground.setColor(colorBackground); } } @@ -452,10 +445,9 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe Timber.i("Common Before"); if (MainApplication.isShowcaseMode()) moduleViewListBuilder.addNotification(NotificationType.SHOWCASE_MODE); - NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder); - if (!NotificationType.NO_INTERNET.shouldRemove()) - moduleViewListBuilder.addNotification(NotificationType.NO_INTERNET); - else if (AppUpdateManager.getAppUpdateManager().checkUpdate(false)) + NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilderOnline); + NotificationType.NO_INTERNET.autoAdd(moduleViewListBuilderOnline); + if (AppUpdateManager.getAppUpdateManager().checkUpdate(false)) moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE); RepoManager.getINSTANCE().updateEnabledStates(); if (RepoManager.getINSTANCE().getCustomRepoManager().needUpdate()) { @@ -505,7 +497,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe RepoManager.getINSTANCE().update(value -> runOnUiThread(max == 0 ? () -> progressIndicator.setProgressCompat((int) (value * PRECISION), true) : () -> progressIndicator.setProgressCompat((int) (value * PRECISION * 0.75F), true))); NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder); if (!NotificationType.NO_INTERNET.shouldRemove()) { - moduleViewListBuilder.addNotification(NotificationType.NO_INTERNET); + moduleViewListBuilderOnline.addNotification(NotificationType.NO_INTERNET); } else if (!NotificationType.REPO_UPDATE_FAILED.shouldRemove()) { moduleViewListBuilder.addNotification(NotificationType.REPO_UPDATE_FAILED); } else { @@ -611,7 +603,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { if (BuildConfig.DEBUG) Timber.i("Request Notification Permission"); - if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.POST_NOTIFICATIONS)) { + if (FoxActivity.getFoxActivity(this).shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { // Show a dialog explaining why we need this permission, which is to show // notifications for updates runOnUiThread(() -> { diff --git a/app/src/main/java/com/fox2code/mmm/MainApplication.java b/app/src/main/java/com/fox2code/mmm/MainApplication.java index a184b19..8cb5e5b 100644 --- a/app/src/main/java/com/fox2code/mmm/MainApplication.java +++ b/app/src/main/java/com/fox2code/mmm/MainApplication.java @@ -265,21 +265,18 @@ public class MainApplication extends FoxApplication implements androidx.work.Con @SuppressLint("NonConstantResourceId") public boolean isLightTheme() { - switch (this.managerThemeResId) { - case R.style.Theme_MagiskModuleManager: - case R.style.Theme_MagiskModuleManager_Monet: - return (this.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) != Configuration.UI_MODE_NIGHT_YES; - case R.style.Theme_MagiskModuleManager_Monet_Light: - case R.style.Theme_MagiskModuleManager_Light: - return true; - case R.style.Theme_MagiskModuleManager_Monet_Dark: - case R.style.Theme_MagiskModuleManager_Dark: - return false; - default: - return super.isLightTheme(); - } + return switch (this.managerThemeResId) { + case R.style.Theme_MagiskModuleManager, R.style.Theme_MagiskModuleManager_Monet -> + (this.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) != Configuration.UI_MODE_NIGHT_YES; + case R.style.Theme_MagiskModuleManager_Monet_Light, R.style.Theme_MagiskModuleManager_Light -> + true; + case R.style.Theme_MagiskModuleManager_Monet_Dark, R.style.Theme_MagiskModuleManager_Dark -> + false; + default -> super.isLightTheme(); + }; } + @SuppressWarnings("unused") @SuppressLint("NonConstantResourceId") public boolean isDarkTheme() { return !this.isLightTheme(); 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 c475bab..1e55103 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java +++ b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java @@ -9,17 +9,22 @@ import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.installer.InstallerInitializer; import com.fox2code.mmm.utils.SyncManager; import com.fox2code.mmm.utils.io.PropUtils; +import com.fox2code.mmm.utils.realm.ModuleListCache; import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.io.SuFile; import com.topjohnwu.superuser.io.SuFileInputStream; import java.io.BufferedReader; +import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Iterator; +import java.util.Objects; +import io.realm.Realm; +import io.realm.RealmConfiguration; import timber.log.Timber; public final class ModuleManager extends SyncManager { @@ -68,15 +73,61 @@ public final class ModuleManager extends SyncManager { if (!FORCE_NEED_FALLBACK && needFallback) { Timber.e("using fallback instead."); } - if (BuildConfig.DEBUG) - Timber.d("Scan"); + if (BuildConfig.DEBUG) Timber.d("Scan"); if (modules != null) { for (String module : modules) { if (!new SuFile("/data/adb/modules/" + module).isDirectory()) continue; // Ignore non directory files inside modules folder - if (BuildConfig.DEBUG) - Timber.d(module); + if (BuildConfig.DEBUG) Timber.d(module); LocalModuleInfo moduleInfo = moduleInfos.get(module); + // next, merge the module info with a record from ModuleListCache if it exists + RealmConfiguration realmConfiguration; + // get all dirs under the realms/repos/ dir under app's data dir + File cacheRoot = new File(MainApplication.getINSTANCE().getDataDirWithPath("realms/repos/").toURI()); + ModuleListCache moduleListCache; + for (File dir : Objects.requireNonNull(cacheRoot.listFiles())) { + if (dir.isDirectory()) { + // 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(); + 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(); + if (moduleListCache != null) { + Timber.d("Found cache for %s", module); + moduleInfo = new LocalModuleInfo(module); + assert moduleListCache.getAuthor() != null; + assert moduleListCache.getDescription() != null; + assert moduleListCache.getSupport() != null; + assert moduleListCache.getConfig() != null; + assert moduleListCache.getName() != null; + moduleInfo.author = moduleListCache.getAuthor(); + moduleInfo.description = moduleListCache.getDescription() + " (from cache)"; + moduleInfo.support = moduleListCache.getSupport(); + moduleInfo.config = moduleListCache.getConfig(); + moduleInfo.name = moduleListCache.getName(); + moduleInfo.minApi = moduleListCache.getMinApi(); + moduleInfo.maxApi = moduleListCache.getMaxApi(); + moduleInfo.minMagisk = moduleListCache.getMinMagisk(); + moduleInfo.safe = moduleListCache.isSafe(); + moduleInfos.put(module, moduleInfo); + // This should not really happen, but let's handles theses cases anyway + moduleInfo.flags |= ModuleInfo.FLAG_MODULE_UPDATING_ONLY; + break; + } else { + Timber.d("No cache for %s", module); + // just for shits n giggles, log the codename of all the modules in the cache + Iterator iterator = realm.where(ModuleListCache.class).findAll().iterator(); + StringBuilder sb = new StringBuilder(); + while (iterator.hasNext()) { + sb.append(iterator.next().getCodename()).append(", "); + } + Timber.d("Cache contains: %s", sb.toString()); + } + } + } + if (moduleInfo == null) { moduleInfo = new LocalModuleInfo(module); moduleInfos.put(module, moduleInfo); @@ -106,23 +157,19 @@ public final class ModuleManager extends SyncManager { } try { PropUtils.readProperties(moduleInfo, "/data/adb/modules/" + module + "/module.prop", true); - } catch ( - Exception e) { - if (BuildConfig.DEBUG) - Timber.d(e); + } catch (Exception e) { + if (BuildConfig.DEBUG) Timber.d(e); moduleInfo.flags |= FLAG_MM_INVALID; } } } - if (BuildConfig.DEBUG) - Timber.d("Scan update"); + if (BuildConfig.DEBUG) Timber.d("Scan update"); String[] modules_update = new SuFile("/data/adb/modules_update").list(); if (modules_update != null) { for (String module : modules_update) { if (!new SuFile("/data/adb/modules_update/" + module).isDirectory()) continue; // Ignore non directory files inside modules folder - if (BuildConfig.DEBUG) - Timber.d(module); + if (BuildConfig.DEBUG) Timber.d(module); LocalModuleInfo moduleInfo = moduleInfos.get(module); if (moduleInfo == null) { moduleInfo = new LocalModuleInfo(module); @@ -132,22 +179,18 @@ public final class ModuleManager extends SyncManager { moduleInfo.flags |= ModuleInfo.FLAG_MODULE_UPDATING; try { PropUtils.readProperties(moduleInfo, "/data/adb/modules_update/" + module + "/module.prop", true); - } catch ( - Exception e) { - if (BuildConfig.DEBUG) - Timber.d(e); + } catch (Exception e) { + if (BuildConfig.DEBUG) Timber.d(e); moduleInfo.flags |= FLAG_MM_INVALID; } } } - if (BuildConfig.DEBUG) - Timber.d("Finalize scan"); + if (BuildConfig.DEBUG) Timber.d("Finalize scan"); this.updatableModuleCount = 0; Iterator moduleInfoIterator = this.moduleInfos.values().iterator(); while (moduleInfoIterator.hasNext()) { LocalModuleInfo moduleInfo = moduleInfoIterator.next(); - if (BuildConfig.DEBUG) - Timber.d(moduleInfo.id); + if (BuildConfig.DEBUG) Timber.d(moduleInfo.id); if ((moduleInfo.flags & FLAG_MM_UNPROCESSED) != 0) { moduleInfoIterator.remove(); continue; // Don't process fallbacks if unreferenced @@ -185,8 +228,7 @@ public final class ModuleManager extends SyncManager { } public boolean setEnabledState(ModuleInfo moduleInfo, boolean checked) { - if (moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING) && !checked) - return false; + if (moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING) && !checked) return false; SuFile disable = new SuFile("/data/adb/modules/" + moduleInfo.id + "/disable"); if (checked) { if (disable.exists() && !disable.delete()) { @@ -204,8 +246,7 @@ public final class ModuleManager extends SyncManager { } public boolean setUninstallState(ModuleInfo moduleInfo, boolean checked) { - if (checked && moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING)) - return false; + if (checked && moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING)) return false; SuFile disable = new SuFile("/data/adb/modules/" + moduleInfo.id + "/remove"); if (checked) { if (!disable.exists() && !disable.createNewFile()) { @@ -223,8 +264,7 @@ public final class ModuleManager extends SyncManager { } public boolean masterClear(ModuleInfo moduleInfo) { - if (moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_HAS_ACTIVE_MOUNT)) - return false; + if (moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_HAS_ACTIVE_MOUNT)) return false; String escapedId = moduleInfo.id.replace("\\", "\\\\").replace("\"", "\\\"").replace(" ", "\\ "); try { // Check for module that declare having file outside their own folder. try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(SuFileInputStream.open("/data/adb/modules/." + moduleInfo.id + "-files"), StandardCharsets.UTF_8))) { @@ -237,8 +277,7 @@ public final class ModuleManager extends SyncManager { Shell.cmd("rm -rf \"" + line + "\"").exec(); } } - } catch ( - IOException ignored) { + } catch (IOException ignored) { } Shell.cmd("rm -rf /data/adb/modules/" + escapedId + "/").exec(); Shell.cmd("rm -f /data/adb/modules/." + escapedId + "-files").exec(); diff --git a/app/src/main/java/com/fox2code/mmm/module/ModuleViewListBuilder.java b/app/src/main/java/com/fox2code/mmm/module/ModuleViewListBuilder.java index fc7038a..4abf527 100644 --- a/app/src/main/java/com/fox2code/mmm/module/ModuleViewListBuilder.java +++ b/app/src/main/java/com/fox2code/mmm/module/ModuleViewListBuilder.java @@ -67,6 +67,8 @@ public class ModuleViewListBuilder { if (notificationType == null) { Timber.w("addNotification(null) called!"); return; + } else { + Timber.i("addNotification(%s) called", notificationType); } synchronized (this.updateLock) { this.notifications.add(notificationType); 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 d99b6ef..8b38224 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java @@ -315,8 +315,8 @@ public class RepoData extends XRepo { boolean dbEnabled; try { dbEnabled = Objects.requireNonNull(realm2.where(ReposList.class).equalTo("id", this.id).findFirst()).isEnabled(); - } catch (NullPointerException e) { - Timber.e(e, "Error while updating enabled state"); + } catch (Exception e) { + Timber.e(e, "Error while updating enabled state for repo %s", this.id); // for now, throw an exception throw e; } @@ -384,7 +384,7 @@ public class RepoData extends XRepo { long diff = currentTime - lastUpdate; long diffMinutes = diff / (60 * 1000) % 60; Timber.d("Repo " + this.id + " updated: " + diffMinutes + " minutes ago"); - return diffMinutes > 15; + return diffMinutes > (BuildConfig.DEBUG ? 5 : 20); } else { Timber.d("Repo " + this.id + " should update could not find repo in database"); return true; diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java index 5f0075d..fb2c207 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java @@ -25,20 +25,13 @@ import com.fox2code.mmm.utils.io.Http; import com.fox2code.mmm.utils.io.PropUtils; import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; import timber.log.Timber; @@ -84,8 +77,7 @@ public final class RepoManager extends SyncManager { boolean x = false; for (RepoData repoData : this.repoData.values()) { if (repoData == this.androidacyRepoData) { - if (x) - return; + if (x) return; x = true; } this.populateDefaultCache(repoData); @@ -174,8 +166,7 @@ public final class RepoManager extends SyncManager { } public RepoData get(String url) { - if (url == null) - return null; + if (url == null) return null; if (MAGISK_ALT_REPO_JSDELIVR.equals(url)) { url = MAGISK_ALT_REPO; } @@ -187,8 +178,7 @@ public final class RepoManager extends SyncManager { } public RepoData addOrGet(String url, String fallBackName) { - if (MAGISK_ALT_REPO_JSDELIVR.equals(url)) - url = MAGISK_ALT_REPO; + if (MAGISK_ALT_REPO_JSDELIVR.equals(url)) url = MAGISK_ALT_REPO; RepoData repoData; synchronized (this.syncLock) { repoData = this.repoData.get(url); @@ -221,52 +211,17 @@ public final class RepoManager extends SyncManager { RepoData[] repoDatas = new LinkedHashSet<>(this.repoData.values()).toArray(new RepoData[0]); RepoUpdater[] repoUpdaters = new RepoUpdater[repoDatas.length]; int moduleToUpdate = 0; - this.hasInternet = false; - // Check if we have internet connection - // Attempt to contact connectivitycheck.gstatic.com/generate_204 - // If we can't, we don't have internet connection - HttpURLConnection urlConnection = null; - try { - Timber.d("Checking internet connection..."); - // this url is actually hosted by Cloudflare and is not dependent on Androidacy servers being up - urlConnection = (HttpURLConnection) new URL("https://production-api.androidacy.com/cdn-cgi/trace").openConnection(); - urlConnection.setRequestMethod("GET"); - urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Linux; Android 10; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Mobile Safari/537.36"); - urlConnection.setRequestProperty("Accept", "*/*"); - urlConnection.setRequestProperty("Accept-Language", "en-US,en;q=0.5"); - Timber.d("Opened connection to %s", String.valueOf(urlConnection.getURL())); - urlConnection.setInstanceFollowRedirects(false); - urlConnection.setReadTimeout(1000); - urlConnection.setUseCaches(false); - urlConnection.getInputStream().close(); - // should return a 200 and the content should contain "visit_scheme=https" and ip= - Timber.d("Response code: %s", urlConnection.getResponseCode()); - // get the response body - BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); - String responseBody = reader.lines().collect(Collectors.joining("\n")); - reader.close(); - // check if the response body contains the expected content - if (urlConnection.getResponseCode() == 200 && responseBody.contains("visit_scheme=https") && responseBody.contains("ip=")) { - this.hasInternet = true; - } else { - Timber.e("Failed to check internet connection"); - } - // close output stream - Timber.d("Closed connection to %s", String.valueOf(urlConnection.getURL())); - } catch ( - IOException e) { - Timber.e(e); - } finally { - Objects.requireNonNull(urlConnection).disconnect(); + this.checkConnection(); + if (!this.hasConnectivity()) { + updateListener.update(STEP3); + return; } for (int i = 0; i < repoDatas.length; i++) { - if (BuildConfig.DEBUG) - Timber.d("Preparing to fetch: %s", repoDatas[i].getName()); + if (BuildConfig.DEBUG) Timber.d("Preparing to fetch: %s", repoDatas[i].getName()); moduleToUpdate += (repoUpdaters[i] = new RepoUpdater(repoDatas[i])).fetchIndex(); updateListener.update(STEP1 / repoDatas.length * (i + 1)); } - if (BuildConfig.DEBUG) - Timber.d("Updating meta-data"); + if (BuildConfig.DEBUG) Timber.d("Updating meta-data"); int updatedModules = 0; boolean allowLowQualityModules = MainApplication.isDisableLowQualityModuleFilter(); for (int i = 0; i < repoUpdaters.length; i++) { @@ -278,8 +233,7 @@ public final class RepoManager extends SyncManager { } List repoModules = repoUpdaters[i].toUpdate(); RepoData repoData = repoDatas[i]; - if (BuildConfig.DEBUG) - Timber.d("Registering %s", repoData.getName()); + if (BuildConfig.DEBUG) Timber.d("Registering %s", repoData.getName()); for (RepoModule repoModule : repoModules) { try { if (repoModule.propUrl != null && !repoModule.propUrl.isEmpty()) { @@ -301,8 +255,7 @@ public final class RepoManager extends SyncManager { } else { repoModule.moduleInfo.flags |= ModuleInfo.FLAG_METADATA_INVALID; } - } catch ( - Exception e) { + } catch (Exception e) { Timber.e(e); } updatedModules++; @@ -323,9 +276,8 @@ public final class RepoManager extends SyncManager { } } } - if (BuildConfig.DEBUG) - Timber.d("Finishing update"); - if (hasInternet) { + if (BuildConfig.DEBUG) Timber.d("Finishing update"); + if (hasConnectivity()) { for (int i = 0; i < repoDatas.length; i++) { // If repo is not enabled, skip if (!repoDatas[i].isEnabled()) { @@ -368,6 +320,27 @@ public final class RepoManager extends SyncManager { updateListener.update(1D); } + private void checkConnection() { + this.hasInternet = false; + // Check if we have internet connection + // Attempt to contact connectivitycheck.gstatic.com/generate_204 + // If we can't, we don't have internet connection + Timber.d("Checking internet connection..."); + // this url is actually hosted by Cloudflare and is not dependent on Androidacy servers being up + byte[] resp = new byte[0]; + try { + resp = Http.doHttpGet("https://production-api.androidacy.com/cdn-cgi/trace", false); + } catch (Exception e) { + Timber.e("Failed to check internet connection. Assuming no internet connection."); + } + // get the response body + String response = new String(resp, StandardCharsets.UTF_8); + // check if the response body contains "visit_scheme=https" and "http/" + // if it does, we have internet connection + this.hasInternet = response.contains("visit_scheme=https") && response.contains("http/"); + Timber.d("Internet connection: %s", this.hasInternet); + } + public void updateEnabledStates() { for (RepoData repoData : this.repoData.values()) { boolean wasEnabled = repoData.isEnabled(); @@ -384,6 +357,7 @@ public final class RepoManager extends SyncManager { } public boolean hasConnectivity() { + Timber.d("Has connectivity: %s", this.hasInternet); return this.hasInternet; } 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 ef24065..f9c2048 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java @@ -14,6 +14,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import io.realm.Realm; @@ -161,7 +162,7 @@ public class RepoUpdater { realm.beginTransaction(); realm.where(ModuleListCache.class).equalTo("repoId", this.repoData.id).findAll().deleteAllFromRealm(); realm.commitTransaction(); - // iterate over modules. pls dont hate me for this, its ugly but it works + // iterate over modules. pls don't hate me for this, its ugly but it works for (int n = 0; n < modulesArray.length(); n++) { // get module JSONObject module = modulesArray.getJSONObject(n); @@ -294,6 +295,7 @@ public class RepoUpdater { if (realm.isInTransaction()) { realm.cancelTransaction(); } + Timber.d("Inserting module %s to realm", id); // create a realm object and insert or update it // add everything to the realm object realm.beginTransaction(); @@ -320,6 +322,7 @@ public class RepoUpdater { moduleListCache.setStats(downloads); realm.copyToRealmOrUpdate(moduleListCache); realm.commitTransaction(); + Timber.d("Inserted module %s to realm. New record is %s", id, Objects.requireNonNull(realm.where(ModuleListCache.class).equalTo("codename", id).findFirst()).toString()); } catch ( Exception e) { Timber.w("Failed to get module info from module " + module + " in repo " + this.repoData.id + " with error " + e.getMessage()); @@ -341,7 +344,9 @@ public class RepoUpdater { ReposList repoListCache = r.where(ReposList.class).equalTo("id", this.repoData.id).findFirst(); if (repoListCache != null) { success.set(true); - repoListCache.setLastUpdate((int) System.currentTimeMillis()); + // get unix timestamp of current time + int currentTime = (int) (System.currentTimeMillis() / 1000); + repoListCache.setLastUpdate(currentTime); } else { Timber.w("Failed to update lastUpdate for repo %s", this.repoData.id); } diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/Http.java b/app/src/main/java/com/fox2code/mmm/utils/io/Http.java index 95786f3..24aba21 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 @@ -97,8 +97,8 @@ public enum Http { hasWebView = cookieManager != null; OkHttpClient.Builder httpclientBuilder = new OkHttpClient.Builder(); // Default is 10, extend it a bit for slow mobile connections. - httpclientBuilder.connectTimeout(15, TimeUnit.SECONDS); - httpclientBuilder.writeTimeout(15, TimeUnit.SECONDS); + httpclientBuilder.connectTimeout(5, TimeUnit.SECONDS); + httpclientBuilder.writeTimeout(10, TimeUnit.SECONDS); httpclientBuilder.readTimeout(15, TimeUnit.SECONDS); httpclientBuilder.proxy(Proxy.NO_PROXY); // Do not use system proxy Dns dns = Dns.SYSTEM; 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 0aeed73..8a59514 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 @@ -45,6 +45,7 @@ public class SentryMain { intent.putExtra("stacktrace", throwable.getStackTrace()); // put lastEventId in intent (get from preferences) intent.putExtra("lastEventId", String.valueOf(Sentry.getLastEventId())); + intent.putExtra("crashReportingEnabled", isSentryEnabled()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); mainApplication.startActivity(intent); android.os.Process.killProcess(android.os.Process.myPid()); @@ -87,7 +88,7 @@ public class SentryMain { editor.apply(); return event; }); - // Filter breadrcrumb content from crash report. + // Filter breadcrumb content from crash report. options.setBeforeBreadcrumb((breadcrumb, hint) -> { String url = (String) breadcrumb.getData("url"); if (url == null || url.isEmpty()) diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ea69e84..b0c124a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -37,35 +37,16 @@ - - - - - - - + app:layout_constraintTop_toTopOf="@+id/swipe_refresh" /> + + android:background="@null" + app:useDrawerArrowDrawable="true" /> diff --git a/build.gradle b/build.gradle index 5476492..122063d 100644 --- a/build.gradle +++ b/build.gradle @@ -19,11 +19,12 @@ buildscript { flavorAware: true ] project.ext.kotlin_version = "1.8.0" - project.ext.sentry_version = "6.13.0" + project.ext.sentry_version = "6.14.0" dependencies { classpath 'com.android.tools.build:gradle:7.4.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${latestAboutLibsRelease}" + classpath 'io.sentry:sentry-android:6.14.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files