try to fix a few bugs

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/285/head
androidacy-user 1 year ago
parent cc12fe60fa
commit 2dd1257f37

@ -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

@ -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);

@ -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(() -> {

@ -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();

@ -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<ModuleListCache> 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<LocalModuleInfo> 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();

@ -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);

@ -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;

@ -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=<some 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<RepoModule> 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/<some number>"
// 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;
}

@ -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);
}

@ -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;

@ -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())

@ -37,35 +37,16 @@
</FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<eightbitlab.com.blurview.BlurView
android:id="@+id/action_bar_blur"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/action_bar_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</eightbitlab.com.blurview.BlurView>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:scaleY="2"
app:showAnimationBehavior="outward"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/action_bar_blur" />
<!--<TextView
android:id="@+id/noodle_debug"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/action_bar_blur" />-->
app:layout_constraintTop_toTopOf="@+id/swipe_refresh" />
<LinearLayout
android:id="@+id/search_container"
@ -73,8 +54,8 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginVertical="8dp"
android:gravity="right"
android:filterTouchesWhenObscured="true"
android:gravity="bottom|end"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_fitsSystemWindowsInsets="bottom"
@ -91,6 +72,7 @@
android:layout_marginHorizontal="8dp"
android:background="@color/transparent"
android:visibility="gone" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/search_card"
android:layout_width="wrap_content"
@ -101,15 +83,16 @@
app:cardCornerRadius="@dimen/card_corner_radius"
app:cardElevation="0dp"
app:cardPreventCornerOverlap="true"
android:visibility="gone"
app:strokeColor="@android:color/transparent"
app:strokeWidth="0dp">
<androidx.appcompat.widget.SearchView
android:id="@+id/search_bar"
android:layout_width="match_parent"
app:useDrawerArrowDrawable="true"
android:layout_height="wrap_content"
android:background="@null" />
android:background="@null"
app:useDrawerArrowDrawable="true" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<!-- bottom md3 navigation bar -->

@ -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

Loading…
Cancel
Save