diff --git a/README.md b/README.md index f8439ea..1b5ea4e 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,13 @@ If your language is right to left don't forget to set `lang_support_rtl` to `tru Translators are not expected to have any previous coding experience. +## License +See [LICENSE](LICENCE). Library licenses can be found in the licenses section of the app. + +Cronet is licensed under the Apache License, Version 2.0. Static libraries are licensed under +the BSD license. See [LICENSE](https://chromium.googlesource.com/chromium/src/+/master/LICENSE) +for more information. Libraries were built using the microg build script which can be found [here](https://github.com/microg/cronet-build). + ## I want to add my own repo To add you own repo to Fox's mmm it need to follow theses conditions: diff --git a/app/build.gradle b/app/build.gradle index c121ede..8cb190c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,6 +9,17 @@ android { namespace "com.fox2code.mmm" compileSdk 33 buildToolsVersion '30.0.3' + signingConfigs { + release { + // Everything comes from local.properties + Properties properties = new Properties() + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + storeFile file(properties.getProperty('keystore.file')) + storePassword properties.getProperty('keystore.password') + keyAlias 'key0' + keyPassword properties.getProperty('keystore.password') + } + } defaultConfig { applicationId "com.fox2code.mmm" @@ -17,13 +28,15 @@ android { versionCode 60 versionName "0.6.8" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + signingConfig signingConfigs.release } buildTypes { release { minifyEnabled true shrinkResources true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), +'proguard-rules.pro' } debug { applicationIdSuffix '.debug' @@ -42,9 +55,6 @@ android { dimension "type" buildConfigField "boolean", "ENABLE_AUTO_UPDATER", "true" buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "true" - buildConfigField("java.util.List", - "ENABLED_REPOS", - "java.util.Arrays.asList(\"magisk_alt_repo\", \"androidacy_repo\")",) // Get the androidacy client ID from the androidacy.properties Properties properties = new Properties() // If androidacy.properties doesn't exist, use the default client ID (an empty string @@ -52,9 +62,19 @@ android { if (project.rootProject.file('androidacy.properties').exists()) { properties.load(project.rootProject.file('androidacy.properties').newDataInputStream()) } else { - properties.setProperty('client_id', '""') + properties.setProperty('client_id', '') } buildConfigField("String", "ANDROIDACY_CLIENT_ID", properties.getProperty('client_id')) + // If client ID is empty, disable androidacy + if (properties.getProperty('client_id').isEmpty()) { + buildConfigField("java.util.List", + "ENABLED_REPOS", "java.util.Arrays.asList(\"magisk_alt_repo\")") + } else { + buildConfigField("java.util.List", + "ENABLED_REPOS", + "java.util.Arrays.asList(\"magisk_alt_repo\", \"androidacy_repo\")",) + } + } fdroid { @@ -82,7 +102,7 @@ android { if (project.rootProject.file('androidacy.properties').exists()) { properties.load(project.rootProject.file('androidacy.properties').newDataInputStream()) } else { - properties.setProperty('client_id', '""') + properties.setProperty('client_id', '') } buildConfigField("String", "ANDROIDACY_CLIENT_ID", properties.getProperty('client_id')) } @@ -160,7 +180,7 @@ sentry { // as Gradle will resolve it to the latest version. // // Defaults to the latest published sentry version. - sentryVersion = '6.8.0' + sentryVersion = '6.9.2' } } @@ -198,18 +218,20 @@ dependencies { implementation 'androidx.work:work-runtime:2.7.1' implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:5.0.0-alpha.10' implementation 'com.squareup.okhttp3:okhttp-brotli:5.0.0-alpha.10' - implementation 'com.google.net.cronet:cronet-okhttp:0.1.0' + // Chromium cronet from microG + implementation fileTree(dir: 'libs', include: '*.jar') + // Force prefer our own version of Cronet 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' if (hasSentryConfig) { // Error reporting - defaultImplementation 'io.sentry:sentry-android:6.8.0' - defaultImplementation 'io.sentry:sentry-android-fragment:6.8.0' - defaultImplementation 'io.sentry:sentry-android-okhttp:6.8.0' - defaultImplementation 'io.sentry:sentry-android-core:6.8.0' - defaultImplementation 'io.sentry:sentry-android-ndk:6.8.0' + defaultImplementation 'io.sentry:sentry-android:6.9.2' + defaultImplementation 'io.sentry:sentry-android-fragment:6.9.2' + defaultImplementation 'io.sentry:sentry-android-okhttp:6.9.2' + defaultImplementation 'io.sentry:sentry-android-core:6.9.2' + defaultImplementation 'io.sentry:sentry-android-ndk:6.9.2' } // Markdown @@ -217,7 +239,8 @@ dependencies { implementation "io.noties.markwon:html:4.6.2" implementation "io.noties.markwon:image:4.6.2" implementation "io.noties.markwon:syntax-highlight:4.6.2" - implementation 'com.google.android.gms:play-services-cronet:18.0.1' + implementation 'com.google.net.cronet:cronet-okhttp:0.1.0' + // Ignore all org.chromium.net dependencies annotationProcessor "io.noties:prism4j-bundler:2.0.0" implementation "com.caverock:androidsvg:1.4" diff --git a/app/libs/arm64-v8a.jar b/app/libs/arm64-v8a.jar new file mode 100644 index 0000000..315c2b8 Binary files /dev/null and b/app/libs/arm64-v8a.jar differ diff --git a/app/libs/armeabi-v7a.jar b/app/libs/armeabi-v7a.jar new file mode 100644 index 0000000..c3c8f84 Binary files /dev/null and b/app/libs/armeabi-v7a.jar differ diff --git a/app/libs/cronet_impl_common_java.jar b/app/libs/cronet_impl_common_java.jar new file mode 100644 index 0000000..cc492a6 Binary files /dev/null and b/app/libs/cronet_impl_common_java.jar differ diff --git a/app/libs/cronet_impl_native_java.jar b/app/libs/cronet_impl_native_java.jar new file mode 100644 index 0000000..ecb9d8a Binary files /dev/null and b/app/libs/cronet_impl_native_java.jar differ diff --git a/app/libs/x86.jar b/app/libs/x86.jar new file mode 100644 index 0000000..8a1556c Binary files /dev/null and b/app/libs/x86.jar differ diff --git a/app/libs/x86_64.jar b/app/libs/x86_64.jar new file mode 100644 index 0000000..6d45fad Binary files /dev/null and b/app/libs/x86_64.jar differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 3f7467f..a70b781 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -187,6 +187,11 @@ android.graphics.Insets getWaterfallInsets(); } +# Keep all of Cronet API and google's internal classes +-keep class org.chromium.net.** { *; } +-keep class org.chromium.** { *; } +-keep class com.google.** { *; } + # Silence some warnings -dontwarn android.os.SystemProperties -dontwarn android.view.ThreadedRenderer @@ -196,4 +201,22 @@ -dontwarn me.weishu.reflection.Reflection -dontwarn org.lsposed.hiddenapibypass.HiddenApiBypass -dontwarn rikka.core.res.ResourcesCompatLayoutInflaterListener --dontwarn rikka.core.util.ResourceUtils \ No newline at end of file +-dontwarn rikka.core.util.ResourceUtils +-dontwarn com.afollestad.materialdialogs.MaterialDialog +-dontwarn com.afollestad.materialdialogs.WhichButton +-dontwarn com.afollestad.materialdialogs.actions.DialogActionExtKt +-dontwarn com.afollestad.materialdialogs.callbacks.DialogCallbackExtKt +-dontwarn com.afollestad.materialdialogs.internal.button.DialogActionButton +-dontwarn com.afollestad.materialdialogs.internal.button.DialogActionButtonLayout +-dontwarn com.afollestad.materialdialogs.internal.main.DialogLayout +-dontwarn com.afollestad.materialdialogs.internal.main.DialogTitleLayout +-dontwarn com.afollestad.materialdialogs.internal.message.DialogContentLayout +-dontwarn com.oracle.svm.core.annotate.AutomaticFeature +-dontwarn com.oracle.svm.core.annotate.Delete +-dontwarn com.oracle.svm.core.annotate.Substitute +-dontwarn com.oracle.svm.core.annotate.TargetClass +-dontwarn com.oracle.svm.core.configure.ResourcesRegistry +-dontwarn javax.lang.model.element.Modifier +-dontwarn org.graalvm.nativeimage.ImageSingletons +-dontwarn org.graalvm.nativeimage.hosted.Feature$BeforeAnalysisAccess +-dontwarn org.graalvm.nativeimage.hosted.Feature \ No newline at end of file diff --git a/app/src/main/java/com/fox2code/mmm/MainActivity.java b/app/src/main/java/com/fox2code/mmm/MainActivity.java index 1ac0f20..34d11e3 100644 --- a/app/src/main/java/com/fox2code/mmm/MainActivity.java +++ b/app/src/main/java/com/fox2code/mmm/MainActivity.java @@ -22,7 +22,6 @@ import android.widget.CheckBox; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SearchView; import androidx.cardview.widget.CardView; import androidx.core.app.NotificationManagerCompat; @@ -207,6 +206,8 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder); if (!NotificationType.NO_INTERNET.shouldRemove()) { moduleViewListBuilder.addNotification(NotificationType.NO_INTERNET); + } else if (!NotificationType.REPO_UPDATE_FAILED.shouldRemove()) { + moduleViewListBuilder.addNotification(NotificationType.REPO_UPDATE_FAILED); } else { // Compatibility data still needs to be updated AppUpdateManager appUpdateManager = AppUpdateManager.getAppUpdateManager(); @@ -406,10 +407,10 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe } else { // Compatibility data still needs to be updated AppUpdateManager appUpdateManager = AppUpdateManager.getAppUpdateManager(); - noodleDebug.replace("Check App Update"); + // noodleDebug.replace("Check App Update"); if (BuildConfig.ENABLE_AUTO_UPDATER && appUpdateManager.checkUpdate(true)) moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE); - noodleDebug.replace("Check Json Update"); + // noodleDebug.replace("Check Json Update"); if (max != 0) { int current = 0; noodleDebug.push(""); @@ -429,7 +430,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe noodleDebug.pop(); } } - noodleDebug.replace("Apply"); + // noodleDebug.replace("Apply"); runOnUiThread(() -> { this.progressIndicator.setVisibility(View.GONE); this.swipeRefreshLayout.setRefreshing(false); @@ -441,8 +442,8 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe RepoManager.getINSTANCE().updateEnabledStates(); RepoManager.getINSTANCE().runAfterUpdate(moduleViewListBuilder::appendRemoteModules); this.moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter); - noodleDebug.pop(); - noodleDebug.unbind(); + // noodleDebug.pop(); + // noodleDebug.unbind(); }, "Repo update thread").start(); } diff --git a/app/src/main/java/com/fox2code/mmm/MainApplication.java b/app/src/main/java/com/fox2code/mmm/MainApplication.java index 2666640..dac051b 100644 --- a/app/src/main/java/com/fox2code/mmm/MainApplication.java +++ b/app/src/main/java/com/fox2code/mmm/MainApplication.java @@ -34,6 +34,7 @@ import com.topjohnwu.superuser.Shell; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; +import java.util.Objects; import java.util.Random; import io.noties.markwon.Markwon; @@ -173,7 +174,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con } public static boolean isCrashReportingEnabled() { - return getSharedPreferences().getBoolean("pref_crash_reporting", BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING && !BuildConfig.DEBUG); + return getSharedPreferences().getBoolean("pref_crash_reporting", BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING); } public static SharedPreferences getBootSharedPreferences() { @@ -316,6 +317,13 @@ public class MainApplication extends FoxApplication implements androidx.work.Con } SentryMain.initialize(this); + if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) { + Log.w("MainApplication", "Androidacy client id is empty! Please set it in androidacy" + + ".properties. Will not enable Androidacy."); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean("pref_androidacy_repo_enabled", false); + editor.apply(); + } } @Override diff --git a/app/src/main/java/com/fox2code/mmm/NotificationType.java b/app/src/main/java/com/fox2code/mmm/NotificationType.java index 5c69ddb..abd4c17 100644 --- a/app/src/main/java/com/fox2code/mmm/NotificationType.java +++ b/app/src/main/java/com/fox2code/mmm/NotificationType.java @@ -68,6 +68,12 @@ public enum NotificationType implements NotificationTypeCst { RepoManager.getINSTANCE().hasConnectivity(); } }, + REPO_UPDATE_FAILED(R.string.repo_update_failed, R.drawable.ic_baseline_cloud_off_24) { + @Override + public boolean shouldRemove() { + return RepoManager.getINSTANCE().isLastUpdateSuccess(); + } + }, NEED_CAPTCHA_ANDROIDACY(R.string.androidacy_need_captcha, R.drawable.ic_baseline_refresh_24, v -> IntentHelper.openUrlAndroidacy(v.getContext(), "https://" + Http.needCaptchaAndroidacyHost() + "/", false)) { 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 14443df..e9d8f09 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java @@ -31,6 +31,7 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Objects; import okhttp3.HttpUrl; @@ -155,6 +156,13 @@ public final class AndroidacyRepoData extends RepoData { @Override protected boolean prepare() throws NoSuchAlgorithmException { + // If ANDROIDACY_CLIENT_ID is not set or is empty, disable this repo and return + if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) { + SharedPreferences.Editor editor = this.cachedPreferences.edit(); + editor.putBoolean("pref_androidacy_repo_enabled", false); + editor.apply(); + return false; + } if (Http.needCaptchaAndroidacy()) return false; // Implementation details discussed on telegram // First, ping the server to check if it's alive 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 472bd29..663b0ce 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java @@ -19,6 +19,9 @@ import com.fox2code.mmm.utils.PropUtils; import com.fox2code.mmm.utils.SyncManager; import java.io.File; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.HashMap; @@ -27,37 +30,74 @@ import java.util.LinkedHashSet; import java.util.List; public final class RepoManager extends SyncManager { - private static final String TAG = "RepoManager"; - - private static final String MAGISK_REPO_MANAGER = - "https://magisk-modules-repo.github.io/submission/modules.json"; public static final String MAGISK_REPO = "https://raw.githubusercontent.com/Magisk-Modules-Repo/submission/modules/modules.json"; public static final String MAGISK_REPO_HOMEPAGE = "https://github.com/Magisk-Modules-Repo"; - public static final String MAGISK_ALT_REPO = "https://raw.githubusercontent.com/Magisk-Modules-Alt-Repo/json/main/modules.json"; public static final String MAGISK_ALT_REPO_HOMEPAGE = "https://github.com/Magisk-Modules-Alt-Repo"; public static final String MAGISK_ALT_REPO_JSDELIVR = "https://cdn.jsdelivr.net/gh/Magisk-Modules-Alt-Repo/json@main/modules.json"; - public static final String ANDROIDACY_MAGISK_REPO_ENDPOINT = "https://production-api.androidacy.com/magisk/repo"; public static final String ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT = "https://staging-api.androidacy.com/magisk/repo"; public static final String ANDROIDACY_MAGISK_REPO_HOMEPAGE = "https://www.androidacy.com/modules-repo"; - public static final String DG_MAGISK_REPO = "https://repo.dergoogler.com/modules.json"; public static final String DG_MAGISK_REPO_GITHUB = "https://googlers-magisk-repo.github.io/modules.json"; public static final String DG_MAGISK_REPO_GITHUB_RAW = "https://raw.githubusercontent.com/Googlers-Repo/googlers-repo.github.io/master/modules.json"; - + private static final String TAG = "RepoManager"; + private static final String MAGISK_REPO_MANAGER = + "https://magisk-modules-repo.github.io/submission/modules.json"; private static final Object lock = new Object(); + private static final double STEP1 = 0.1D; + private static final double STEP2 = 0.8D; + private static final double STEP3 = 0.1D; private static volatile RepoManager INSTANCE; + private final MainApplication mainApplication; + private final LinkedHashMap repoData; + private final HashMap modules; + private final AndroidacyRepoData androidacyRepoData; + private final CustomRepoManager customRepoManager; + private boolean hasInternet; + private boolean repoLastError = false; + private boolean initialized; + public String repoLastErrorName = null; + + private RepoManager(MainApplication mainApplication) { + INSTANCE = this; // Set early fox XHooks + this.initialized = false; + this.mainApplication = mainApplication; + this.repoData = new LinkedHashMap<>(); + this.modules = new HashMap<>(); + // We do not have repo list config yet. + RepoData altRepo = this.addRepoData( + MAGISK_ALT_REPO, "Magisk Modules Alt Repo"); + altRepo.defaultWebsite = RepoManager.MAGISK_ALT_REPO_HOMEPAGE; + altRepo.defaultSubmitModule = + "https://github.com/Magisk-Modules-Alt-Repo/submission/issues"; + RepoData dgRepo = this.addRepoData( + DG_MAGISK_REPO_GITHUB_RAW, "Googlers Magisk Repo"); + dgRepo.defaultWebsite = "https://dergoogler.com/repo"; + this.androidacyRepoData = this.addAndroidacyRepoData(); + this.customRepoManager = new CustomRepoManager(mainApplication, this); + XHooks.onRepoManagerInitialize(); + // Populate default cache + boolean x = false; + for (RepoData repoData : this.repoData.values()) { + if (repoData == this.androidacyRepoData) { + if (x) return; + x = true; + } + this.populateDefaultCache(repoData); + } + this.initialized = true; + } public static RepoManager getINSTANCE() { if (INSTANCE == null || !INSTANCE.initialized) { @@ -93,44 +133,50 @@ public final class RepoManager extends SyncManager { return INSTANCE; } - private final MainApplication mainApplication; - private final LinkedHashMap repoData; - private final HashMap modules; - private final AndroidacyRepoData androidacyRepoData; - private final CustomRepoManager customRepoManager; - private boolean initialized; + public static String internalIdOfUrl(String url) { + switch (url) { + case MAGISK_ALT_REPO: + case MAGISK_ALT_REPO_JSDELIVR: + return "magisk_alt_repo"; + case ANDROIDACY_MAGISK_REPO_ENDPOINT: + case ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT: + return "androidacy_repo"; + case DG_MAGISK_REPO: + case DG_MAGISK_REPO_GITHUB: + case DG_MAGISK_REPO_GITHUB_RAW: + return "dg_magisk_repo"; + default: + return "repo_" + Hashes.hashSha1( + url.getBytes(StandardCharsets.UTF_8)); + } + } - private RepoManager(MainApplication mainApplication) { - INSTANCE = this; // Set early fox XHooks - this.initialized = false; - this.mainApplication = mainApplication; - this.repoData = new LinkedHashMap<>(); - this.modules = new HashMap<>(); - // We do not have repo list config yet. - RepoData altRepo = this.addRepoData( - MAGISK_ALT_REPO, "Magisk Modules Alt Repo"); - altRepo.defaultWebsite = RepoManager.MAGISK_ALT_REPO_HOMEPAGE; - altRepo.defaultSubmitModule = - "https://github.com/Magisk-Modules-Alt-Repo/submission/issues"; - RepoData dgRepo = this.addRepoData( - DG_MAGISK_REPO_GITHUB_RAW, "Googlers Magisk Repo"); - dgRepo.defaultWebsite = "https://dergoogler.com/repo"; - this.androidacyRepoData = this.addAndroidacyRepoData(); - this.customRepoManager = new CustomRepoManager(mainApplication, this); - XHooks.onRepoManagerInitialize(); - // Populate default cache - boolean x = false; - for (RepoData repoData:this.repoData.values()) { - if (repoData == this.androidacyRepoData) { - if (x) return; x = true; - } - this.populateDefaultCache(repoData); + static boolean isBuiltInRepo(String repo) { + switch (repo) { + case RepoManager.MAGISK_ALT_REPO: + case RepoManager.MAGISK_ALT_REPO_JSDELIVR: + case RepoManager.ANDROIDACY_MAGISK_REPO_ENDPOINT: + case RepoManager.ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT: + case RepoManager.DG_MAGISK_REPO: + case RepoManager.DG_MAGISK_REPO_GITHUB: + case RepoManager.DG_MAGISK_REPO_GITHUB_RAW: + return true; } - this.initialized = true; + return false; + } + + /** + * Safe way to do {@code RepoManager.getInstance().androidacyRepoData.isEnabled()} + * without initializing RepoManager + */ + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public static boolean isAndroidacyRepoEnabled() { + return INSTANCE != null && INSTANCE.androidacyRepoData != null && + INSTANCE.androidacyRepoData.isEnabled(); } private void populateDefaultCache(RepoData repoData) { - for (RepoModule repoModule:repoData.moduleHashMap.values()) { + for (RepoModule repoModule : repoData.moduleHashMap.values()) { if (!repoModule.moduleInfo.hasFlag(ModuleInfo.FLAG_METADATA_INVALID)) { RepoModule registeredRepoModule = this.modules.get(repoModule.id); if (registeredRepoModule == null) { @@ -181,12 +227,6 @@ public final class RepoManager extends SyncManager { return repoData; } - private boolean repoLastResult = true; - - private static final double STEP1 = 0.1D; - private static final double STEP2 = 0.8D; - private static final double STEP3 = 0.1D; - protected void scanInternal(@NonNull UpdateListener updateListener) { NoodleDebug noodleDebug = NoodleDebug.getNoodleDebug(); // First, check if we have internet connection @@ -216,7 +256,7 @@ public final class RepoManager extends SyncManager { noodleDebug.replace(repoData.getName()); Log.d(TAG, "Registering " + repoData.getName()); noodleDebug.push(""); - for (RepoModule repoModule:repoModules) { + for (RepoModule repoModule : repoModules) { noodleDebug.replace(repoModule.id); try { if (repoModule.propUrl != null && @@ -244,7 +284,7 @@ public final class RepoManager extends SyncManager { updateListener.update(STEP1 + (STEP2 / moduleToUpdate * updatedModules)); } noodleDebug.pop(); - for (RepoModule repoModule:repoUpdaters[i].toApply()) { + for (RepoModule repoModule : repoUpdaters[i].toApply()) { if ((repoModule.moduleInfo.flags & ModuleInfo.FLAG_METADATA_INVALID) == 0) { RepoModule registeredRepoModule = this.modules.get(repoModule.id); if (registeredRepoModule == null) { @@ -259,21 +299,45 @@ public final class RepoManager extends SyncManager { noodleDebug.pop(); noodleDebug.replace("Finishing update"); noodleDebug.push(""); - boolean hasInternet = false; - for (int i = 0; i < repoDatas.length; i++) { - noodleDebug.replace(repoUpdaters[i].repoData.getName()); - hasInternet |= repoUpdaters[i].finish(); - updateListener.update(STEP1 + STEP2 + (STEP3 / repoDatas.length * (i + 1))); + 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 + try { + HttpURLConnection urlConnection = (HttpURLConnection) new URL( + "https://connectivitycheck.gstatic.com/generate_204").openConnection(); + urlConnection.setInstanceFollowRedirects(false); + urlConnection.setConnectTimeout(1000); + urlConnection.setReadTimeout(1000); + urlConnection.setUseCaches(false); + urlConnection.getInputStream().close(); + if (urlConnection.getResponseCode() == 204 && + urlConnection.getContentLength() == 0) { + this.hasInternet = true; + } + } catch (IOException e) { + Log.e(TAG, "Failed to check internet connection", e); + } + noodleDebug.pop(); + if (hasInternet) { + for (int i = 0; i < repoDatas.length; i++) { + noodleDebug.replace(repoUpdaters[i].repoData.getName()); + this.repoLastError = !repoUpdaters[i].finish(); + if (this.repoLastError) { + Log.e(TAG, "Failed to update " + repoUpdaters[i].repoData.getName()); + this.repoLastErrorName = repoUpdaters[i].repoData.getName(); + } + updateListener.update(STEP1 + STEP2 + (STEP3 / repoDatas.length * (i + 1))); + } } noodleDebug.pop(); Log.i(TAG, "Got " + this.modules.size() + " modules!"); updateListener.update(1D); - this.repoLastResult = hasInternet; noodleDebug.pop(); // pop "Finishing update" } public void updateEnabledStates() { - for (RepoData repoData:this.repoData.values()) { + for (RepoData repoData : this.repoData.values()) { boolean wasEnabled = repoData.isEnabled(); repoData.updateEnabledState(); if (!wasEnabled && repoData.isEnabled()) { @@ -288,39 +352,7 @@ public final class RepoManager extends SyncManager { } public boolean hasConnectivity() { - return this.repoLastResult; - } - - public static String internalIdOfUrl(String url) { - switch (url) { - case MAGISK_ALT_REPO: - case MAGISK_ALT_REPO_JSDELIVR: - return "magisk_alt_repo"; - case ANDROIDACY_MAGISK_REPO_ENDPOINT: - case ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT: - return "androidacy_repo"; - case DG_MAGISK_REPO: - case DG_MAGISK_REPO_GITHUB: - case DG_MAGISK_REPO_GITHUB_RAW: - return "dg_magisk_repo"; - default: - return "repo_" + Hashes.hashSha1( - url.getBytes(StandardCharsets.UTF_8)); - } - } - - static boolean isBuiltInRepo(String repo) { - switch (repo) { - case RepoManager.MAGISK_ALT_REPO: - case RepoManager.MAGISK_ALT_REPO_JSDELIVR: - case RepoManager.ANDROIDACY_MAGISK_REPO_ENDPOINT: - case RepoManager.ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT: - case RepoManager.DG_MAGISK_REPO: - case RepoManager.DG_MAGISK_REPO_GITHUB: - case RepoManager.DG_MAGISK_REPO_GITHUB_RAW: - return true; - } - return false; + return this.hasInternet; } private RepoData addRepoData(String url, String fallBackName) { @@ -375,12 +407,7 @@ public final class RepoManager extends SyncManager { return new LinkedHashSet<>(this.repoData.values()); } - /** - * Safe way to do {@code RepoManager.getInstance().androidacyRepoData.isEnabled()} - * without initializing RepoManager - */ - public static boolean isAndroidacyRepoEnabled() { - return INSTANCE != null && INSTANCE.androidacyRepoData != null && - INSTANCE.androidacyRepoData.isEnabled(); + public boolean isLastUpdateSuccess() { + return this.repoLastError; } } 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 e3eb620..c7145ac 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java @@ -19,6 +19,7 @@ import org.json.JSONObject; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -51,7 +52,7 @@ public class RepoUpdater { } this.indexRaw = Http.doHttpGet(this.repoData.getUrl(), false); // Ensure it's a valid json and response code is 200 - if (this.indexRaw.hashCode() == 0) { + if (Arrays.hashCode(this.indexRaw) == 0) { this.indexRaw = null; this.toUpdate = Collections.emptyList(); this.toApply = this.repoData.moduleHashMap.values(); @@ -83,6 +84,10 @@ public class RepoUpdater { public boolean finish() { final boolean success = this.indexRaw != null; + // If repo is not enabled we don't need to do anything, just return true + if (!this.repoData.isEnabled()) { + return true; + } if (this.indexRaw != null) { try { Files.write(this.repoData.metaDataCache, this.indexRaw); diff --git a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java index ffe374a..4466717 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -529,10 +529,32 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { return true; }); } + // Disable toggling the pref_androidacy_repo_enabled on builds without an + // ANDROIDACY_CLIENT_ID or where the ANDROIDACY_CLIENT_ID is empty + Preference androidacyRepoEnabled = Objects.requireNonNull(findPreference("pref_androidacy_repo_enabled")); + if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) { + androidacyRepoEnabled.setOnPreferenceClickListener(preference -> { + new MaterialAlertDialogBuilder(this.requireContext()) + .setTitle(R.string.androidacy_repo_disabled) + .setMessage(R.string.androidacy_repo_disabled_message) + .setPositiveButton(R.string.download_full_app, (dialog, which) -> { + // User clicked OK button. Open GitHub releases page + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse( + "https://github.com/Fox2Code/FoxMagiskModuleManager/releases")); + startActivity(browserIntent); + }) + .show(); + // Revert the switch to off + SwitchPreferenceCompat switchPreferenceCompat = (SwitchPreferenceCompat) androidacyRepoEnabled; + switchPreferenceCompat.setChecked(false); + // Save the preference + MainApplication.getSharedPreferences().edit().putBoolean("pref_androidacy_repo_enabled", false).apply(); + return false; + }); + } String[] originalApiKeyRef = new String[]{MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", null)}; // Get the dummy pref_androidacy_repo_api_token EditTextPreference EditTextPreference prefAndroidacyRepoApiKey = Objects.requireNonNull(findPreference("pref_androidacy_api_token")); - prefAndroidacyRepoApiKey.setDependency("pref_androidacy_repo_enabled"); prefAndroidacyRepoApiKey.setTitle(R.string.api_key); prefAndroidacyRepoApiKey.setSummary(R.string.api_key_summary); prefAndroidacyRepoApiKey.setDialogTitle(R.string.api_key); diff --git a/app/src/main/java/com/fox2code/mmm/utils/Http.java b/app/src/main/java/com/fox2code/mmm/utils/Http.java index 297942c..5be3187 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/Http.java +++ b/app/src/main/java/com/fox2code/mmm/utils/Http.java @@ -19,7 +19,7 @@ import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.androidacy.AndroidacyUtil; import com.fox2code.mmm.installer.InstallerInitializer; import com.fox2code.mmm.repo.RepoManager; -import com.google.android.gms.net.CronetProviderInstaller; +import com.google.net.cronet.okhttptransport.CronetCallFactory; import com.google.net.cronet.okhttptransport.CronetInterceptor; import org.chromium.net.CronetEngine; @@ -141,14 +141,16 @@ public class Http { }); // Add cronet interceptor // install cronet - try { + /*try { // Detect if cronet is installed CronetProviderInstaller.installProvider(mainApplication); } catch (Exception e) { Log.e(TAG, "Failed to install cronet", e); - } + }*/ // init cronet try { + // Load the cronet library + System.loadLibrary("cronet.108.0.5359.95"); CronetEngine.Builder builder = new CronetEngine.Builder(mainApplication); builder.enableBrotli(true); builder.enableHttp2(true); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 65b89eb..c9e399d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -191,4 +191,11 @@ Forces AMOLED black backgrounds when using dark theme. Please be aware this may have contrast problems with some color themes. Force black theme + This repo is currently disabled + This build is missing client keys for the + Androidacy Repo. + Please + download the GitHub release if you\'d like to benefit from features like module reviews, automatic security checks, and more. + Download full version + Some repos have failed to update diff --git a/app/src/main/res/xml/repo_preferences.xml b/app/src/main/res/xml/repo_preferences.xml index eecfa44..9c479e0 100644 --- a/app/src/main/res/xml/repo_preferences.xml +++ b/app/src/main/res/xml/repo_preferences.xml @@ -44,6 +44,7 @@ app:icon="@drawable/ic_baseline_vpn_key_24" app:key="pref_androidacy_api_token" app:singleLineTitle="false" + app:dependency="pref_androidacy_repo_enabled" app:isPreferenceVisible="false" app:title="@string/api_key" /> - + + diff --git a/settings.gradle b/settings.gradle index 5c996dc..865356f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,4 +9,4 @@ dependencyResolutionManagement { } } rootProject.name = "MagiskModuleManager" -include ':app' +include ':app' \ No newline at end of file