diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cbfff90..24d0e10 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -398,14 +398,14 @@ configurations { dependencies { // UI implementation("androidx.appcompat:appcompat:1.6.1") - implementation("androidx.activity:activity-ktx:1.7.1") + implementation("androidx.activity:activity-ktx:1.7.2") implementation("androidx.emoji2:emoji2:1.3.0") implementation("androidx.emoji2:emoji2-views-helper:1.3.0") implementation("androidx.preference:preference-ktx:1.2.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.recyclerview:recyclerview:1.3.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") - implementation("androidx.webkit:webkit:1.6.1") + implementation("androidx.webkit:webkit:1.7.0") implementation("com.google.android.material:material:1.9.0") implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.3.0") implementation("dev.rikka.rikkax.insets:insets:1.3.0") @@ -426,7 +426,7 @@ dependencies { // implementation("com.google.protobuf:protobuf-javalite:3.22.2") // google guava, maybe fix a bug - implementation("com.google.guava:guava:31.1-jre") + implementation("com.google.guava:guava:32.0.0-jre") val libsuVersion = "5.0.5" @@ -443,12 +443,12 @@ dependencies { implementation("com.github.Fox2Code:AndroidANSI:1.2.1") // sentry - implementation("io.sentry:sentry-android:6.19.1") - implementation("io.sentry:sentry-android-timber:6.19.1") - implementation("io.sentry:sentry-android-fragment:6.19.1") - implementation("io.sentry:sentry-android-okhttp:6.19.1") - implementation("io.sentry:sentry-kotlin-extensions:6.19.1") - implementation("io.sentry:sentry-android-ndk:6.19.1") + implementation("io.sentry:sentry-android:6.20.0") + implementation("io.sentry:sentry-android-timber:6.20.0") + implementation("io.sentry:sentry-android-fragment:6.20.0") + implementation("io.sentry:sentry-android-okhttp:6.20.0") + implementation("io.sentry:sentry-kotlin-extensions:6.20.0") + implementation("io.sentry:sentry-android-ndk:6.20.0") // Markdown // TODO: switch to an updated implementation @@ -488,7 +488,7 @@ dependencies { } android { - ndkVersion = "23.0.7344513" + ndkVersion = "25.2.9519653" dependenciesInfo { includeInApk = false includeInBundle = false diff --git a/app/src/main/java/com/fox2code/mmm/NotificationType.java b/app/src/main/java/com/fox2code/mmm/NotificationType.java deleted file mode 100644 index c479e54..0000000 --- a/app/src/main/java/com/fox2code/mmm/NotificationType.java +++ /dev/null @@ -1,231 +0,0 @@ -package com.fox2code.mmm; - -import android.view.View; -import android.widget.Toast; - -import androidx.annotation.AttrRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.StringRes; - -import com.fox2code.foxcompat.app.FoxActivity; -import com.fox2code.mmm.installer.InstallerInitializer; -import com.fox2code.mmm.module.ModuleViewListBuilder; -import com.fox2code.mmm.repo.RepoManager; -import com.fox2code.mmm.utils.IntentHelper; -import com.fox2code.mmm.utils.io.Files; -import com.fox2code.mmm.utils.io.net.Http; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import timber.log.Timber; - -public enum NotificationType implements NotificationTypeCst { - DEBUG(R.string.debug_build, R.drawable.ic_baseline_bug_report_24, com.google.android.material.R.attr.colorSecondary, com.google.android.material.R.attr.colorOnSecondary) { - @Override - public boolean shouldRemove() { - return !BuildConfig.DEBUG; - } - }, - SHOWCASE_MODE(R.string.showcase_mode, R.drawable.ic_baseline_lock_24, - androidx.appcompat.R.attr.colorPrimary, com.google.android.material.R.attr.colorOnPrimary) { - @Override - public boolean shouldRemove() { - return !MainApplication.isShowcaseMode(); - } - }, - NO_MAGISK(R.string.fail_magisk_missing, R.drawable.ic_baseline_numbers_24, v -> - IntentHelper.openUrl(v.getContext(), - "https://github.com/topjohnwu/Magisk/blob/master/docs/install.md")) { - @Override - public boolean shouldRemove() { - return InstallerInitializer.getErrorNotification() != this; - } - }, - NO_ROOT(R.string.fail_root_magisk, R.drawable.ic_baseline_numbers_24) { - @Override - public boolean shouldRemove() { - return InstallerInitializer.getErrorNotification() != this; - } - }, - ROOT_DENIED(R.string.fail_root_denied, R.drawable.ic_baseline_numbers_24) { - @Override - public boolean shouldRemove() { - return InstallerInitializer.getErrorNotification() != this; - } - }, - MAGISK_OUTDATED(R.string.magisk_outdated, R.drawable.ic_baseline_update_24, v -> IntentHelper.openUrl(v.getContext(), "https://github.com/topjohnwu/Magisk/releases")) { - @Override - public boolean shouldRemove() { - return InstallerInitializer.peekMagiskPath() == null || - InstallerInitializer.peekMagiskVersion() >= - Constants.MAGISK_VER_CODE_INSTALL_COMMAND; - } - }, - NO_INTERNET(R.string.fail_internet, R.drawable.ic_baseline_cloud_off_24) { - @Override - public boolean shouldRemove() { - return 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)) { - @Override - public boolean shouldRemove() { - return !RepoManager.isAndroidacyRepoEnabled() - || !Http.needCaptchaAndroidacy(); - } - }, - NO_WEB_VIEW(R.string.no_web_view, R.drawable.ic_baseline_android_24) { - @Override - public boolean shouldRemove() { - return Http.hasWebView(); - } - }, - UPDATE_AVAILABLE(R.string.app_update_available, R.drawable.ic_baseline_system_update_24, - androidx.appcompat.R.attr.colorPrimary, com.google.android.material.R.attr.colorOnPrimary, v -> IntentHelper.openUrl(v.getContext(), - "https://github.com/Androidacy/MagiskModuleManager/releases"), false) { - @Override - public boolean shouldRemove() { - return !AppUpdateManager.getAppUpdateManager().peekShouldUpdate(); - } - }, - INSTALL_FROM_STORAGE(R.string.install_from_storage, R.drawable.ic_baseline_storage_24, - androidx.appcompat.R.attr.colorBackgroundFloating, com.google.android.material.R.attr.colorOnBackground, v -> { - FoxActivity compatActivity = FoxActivity.getFoxActivity(v); - final File module = new File(compatActivity.getCacheDir(), - "installer" + File.separator + "module.zip"); - IntentHelper.openFileTo(compatActivity, module, (d, u, s) -> { - if (s == IntentHelper.RESPONSE_FILE) { - try { - if (needPatch(d)) { - Files.patchModuleSimple(Files.read(d), - new FileOutputStream(d)); - } - if (needPatch(d)) { - if (d.exists() && !d.delete()) - Timber.w("Failed to delete non module zip"); - Toast.makeText(compatActivity, - R.string.invalid_format, Toast.LENGTH_SHORT).show(); - } else { - IntentHelper.openInstaller(compatActivity, d.getAbsolutePath(), - compatActivity.getString( - R.string.local_install_title), null, null, false, - BuildConfig.DEBUG && // Use debug mode if no root - InstallerInitializer.peekMagiskPath() == null); - } - } catch (IOException ignored) { - if (d.exists() && !d.delete()) - Timber.w("Failed to delete invalid module"); - Toast.makeText(compatActivity, - R.string.invalid_format, Toast.LENGTH_SHORT).show(); - } - } else if (s == IntentHelper.RESPONSE_URL) { - IntentHelper.openInstaller(compatActivity, u.toString(), - compatActivity.getString( - R.string.remote_install_title), null, null, false, - BuildConfig.DEBUG && // Use debug mode if no root - InstallerInitializer.peekMagiskPath() == null); - } - }); - }, false) { - @Override - public boolean shouldRemove() { - return !BuildConfig.DEBUG && - (MainApplication.isShowcaseMode() || - InstallerInitializer.peekMagiskPath() == null); - } - }; - - public static boolean needPatch(File target) { - try (ZipFile zipFile = new ZipFile(target)) { - boolean validEntries = zipFile.getEntry("module.prop") != null; - // ensure there's no anykernel.sh - validEntries &= zipFile.getEntry("anykernel.sh") == null; - if (validEntries) { - // Ensure id of module is not empty and matches ^[a-zA-Z][a-zA-Z0-9._-]+$ regex - // We need to get the module.prop and parse the id= line - ZipEntry moduleProp = zipFile.getEntry("module.prop"); - // Parse the module.prop - if (moduleProp != null) { - // Find the line with id=, and check if it matches the regex - try (BufferedReader reader = new BufferedReader(new InputStreamReader(zipFile.getInputStream(moduleProp)))) { - String line; - while ((line = reader.readLine()) != null) { - if (line.startsWith("id=")) { - String id = line.substring(3); - return id.isEmpty() || !id.matches("^[a-zA-Z][a-zA-Z0-9._-]+$"); - } - } - } - } else { - return true; - } - } else { - return true; - } - } catch (IOException e) { - return true; - } - return false; - } - - @StringRes - public final int textId; - @DrawableRes - public final int iconId; - @AttrRes - public final int backgroundAttr; - @AttrRes - public final int foregroundAttr; - public final View.OnClickListener onClickListener; - public final boolean special; - - NotificationType(@StringRes int textId, int iconId) { - this(textId, iconId, androidx.appcompat.R.attr.colorError, com.google.android.material.R.attr.colorOnPrimary); - } - - NotificationType(@StringRes int textId, int iconId, View.OnClickListener onClickListener) { - this(textId, iconId, androidx.appcompat.R.attr.colorError, com.google.android.material.R.attr.colorOnPrimary, onClickListener); - } - - NotificationType(@StringRes int textId, int iconId, int backgroundAttr, int foregroundAttr) { - this(textId, iconId, backgroundAttr, foregroundAttr, null); - } - - NotificationType(@StringRes int textId, int iconId, int backgroundAttr, int foregroundAttr, - View.OnClickListener onClickListener) { - this(textId, iconId, backgroundAttr, foregroundAttr, onClickListener, false); - } - - NotificationType(@StringRes int textId, int iconId, int backgroundAttr, int foregroundAttr, - View.OnClickListener onClickListener, @SuppressWarnings("SameParameterValue") boolean special) { - this.textId = textId; - this.iconId = iconId; - this.backgroundAttr = backgroundAttr; - this.foregroundAttr = foregroundAttr; - this.onClickListener = onClickListener; - this.special = special; - } - - public boolean shouldRemove() { - // By default, remove the notification` - return false; - } - - public final void autoAdd(ModuleViewListBuilder moduleViewListBuilder) { - if (!shouldRemove()) moduleViewListBuilder.addNotification(this); - } -} diff --git a/app/src/main/java/com/fox2code/mmm/NotificationType.kt b/app/src/main/java/com/fox2code/mmm/NotificationType.kt new file mode 100644 index 0000000..41d512f --- /dev/null +++ b/app/src/main/java/com/fox2code/mmm/NotificationType.kt @@ -0,0 +1,256 @@ +@file:Suppress("KotlinConstantConditions", "UNINITIALIZED_ENUM_COMPANION_WARNING", + "ktConcatNullable", "BlockingMethodInNonBlockingContext" +) + +package com.fox2code.mmm + +import android.net.Uri +import android.view.View +import android.widget.Toast +import androidx.annotation.AttrRes +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import com.fox2code.foxcompat.app.FoxActivity +import com.fox2code.mmm.installer.InstallerInitializer +import com.fox2code.mmm.module.ModuleViewListBuilder +import com.fox2code.mmm.repo.RepoManager +import com.fox2code.mmm.utils.IntentHelper +import com.fox2code.mmm.utils.io.Files.Companion.patchModuleSimple +import com.fox2code.mmm.utils.io.Files.Companion.read +import com.fox2code.mmm.utils.io.net.Http +import timber.log.Timber +import java.io.BufferedReader +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStreamReader +import java.util.zip.ZipFile + +enum class NotificationType constructor( + @field:StringRes @param:StringRes val textId: Int, + @field:DrawableRes val iconId: Int, + @field:AttrRes val backgroundAttr: Int = androidx.appcompat.R.attr.colorError, + @field:AttrRes val foregroundAttr: Int = com.google.android.material.R.attr.colorOnPrimary, + val onClickListener: View.OnClickListener? = null, + val special: Boolean = false +) : NotificationTypeCst { + DEBUG( + R.string.debug_build, + R.drawable.ic_baseline_bug_report_24, + com.google.android.material.R.attr.colorSecondary, + com.google.android.material.R.attr.colorOnSecondary + ) { + override fun shouldRemove(): Boolean { + return !BuildConfig.DEBUG + } + }, + SHOWCASE_MODE( + R.string.showcase_mode, R.drawable.ic_baseline_lock_24, + androidx.appcompat.R.attr.colorPrimary, com.google.android.material.R.attr.colorOnPrimary + ) { + override fun shouldRemove(): Boolean { + return !MainApplication.isShowcaseMode() + } + }, + NO_MAGISK( + R.string.fail_magisk_missing, + R.drawable.ic_baseline_numbers_24, + View.OnClickListener { v: View -> + IntentHelper.openUrl( + v.context, + "https://github.com/topjohnwu/Magisk/blob/master/docs/install.md" + ) + }) { + override fun shouldRemove(): Boolean { + return InstallerInitializer.getErrorNotification() !== this + } + }, + NO_ROOT(R.string.fail_root_magisk, R.drawable.ic_baseline_numbers_24) { + override fun shouldRemove(): Boolean { + return InstallerInitializer.getErrorNotification() !== this + } + }, + ROOT_DENIED(R.string.fail_root_denied, R.drawable.ic_baseline_numbers_24) { + override fun shouldRemove(): Boolean { + return InstallerInitializer.getErrorNotification() !== this + } + }, + MAGISK_OUTDATED( + R.string.magisk_outdated, + R.drawable.ic_baseline_update_24, + View.OnClickListener { v: View -> + IntentHelper.openUrl( + v.context, + "https://github.com/topjohnwu/Magisk/releases" + ) + }) { + override fun shouldRemove(): Boolean { + return InstallerInitializer.peekMagiskPath() == null || + InstallerInitializer.peekMagiskVersion() >= + Constants.MAGISK_VER_CODE_INSTALL_COMMAND + } + }, + NO_INTERNET(R.string.fail_internet, R.drawable.ic_baseline_cloud_off_24) { + override fun shouldRemove(): Boolean { + return RepoManager.getINSTANCE().hasConnectivity() + } + }, + REPO_UPDATE_FAILED(R.string.repo_update_failed, R.drawable.ic_baseline_cloud_off_24) { + override fun shouldRemove(): Boolean { + return RepoManager.getINSTANCE().isLastUpdateSuccess + } + }, + NEED_CAPTCHA_ANDROIDACY( + R.string.androidacy_need_captcha, + R.drawable.ic_baseline_refresh_24, + View.OnClickListener { v: View -> + IntentHelper.openUrlAndroidacy( + v.context, + "https://" + Http.needCaptchaAndroidacyHost() + "/", false + ) + }) { + override fun shouldRemove(): Boolean { + return (!RepoManager.isAndroidacyRepoEnabled() + || !Http.needCaptchaAndroidacy()) + } + }, + NO_WEB_VIEW(R.string.no_web_view, R.drawable.ic_baseline_android_24) { + override fun shouldRemove(): Boolean { + return Http.hasWebView() + } + }, + UPDATE_AVAILABLE( + R.string.app_update_available, + R.drawable.ic_baseline_system_update_24, + androidx.appcompat.R.attr.colorPrimary, + com.google.android.material.R.attr.colorOnPrimary, + View.OnClickListener { v: View -> + IntentHelper.openUrl( + v.context, + "https://github.com/Androidacy/MagiskModuleManager/releases" + ) + }, + false + ) { + override fun shouldRemove(): Boolean { + return !AppUpdateManager.getAppUpdateManager().peekShouldUpdate() + } + }, + INSTALL_FROM_STORAGE( + R.string.install_from_storage, + R.drawable.ic_baseline_storage_24, + androidx.appcompat.R.attr.colorBackgroundFloating, + com.google.android.material.R.attr.colorOnBackground, + View.OnClickListener { v: View? -> + val compatActivity = FoxActivity.getFoxActivity(v) + val module = File( + compatActivity.cacheDir, + "installer" + File.separator + "module.zip" + ) + IntentHelper.openFileTo(compatActivity, module) { d: File, u: Uri, s: Int -> + if (s == IntentHelper.RESPONSE_FILE) { + try { + if (needPatch(d)) { + patchModuleSimple( + read(d), + FileOutputStream(d) + ) + } + if (needPatch(d)) { + if (d.exists() && !d.delete()) Timber.w("Failed to delete non module zip") + Toast.makeText( + compatActivity, + R.string.invalid_format, Toast.LENGTH_SHORT + ).show() + } else { + IntentHelper.openInstaller( + compatActivity, d.absolutePath, + compatActivity.getString( + R.string.local_install_title + ), null, null, false, + BuildConfig.DEBUG && // Use debug mode if no root + InstallerInitializer.peekMagiskPath() == null + ) + } + } catch (ignored: IOException) { + if (d.exists() && !d.delete()) Timber.w("Failed to delete invalid module") + Toast.makeText( + compatActivity, + R.string.invalid_format, Toast.LENGTH_SHORT + ).show() + } + } else if (s == IntentHelper.RESPONSE_URL) { + IntentHelper.openInstaller( + compatActivity, u.toString(), + compatActivity.getString( + R.string.remote_install_title + ), null, null, false, + BuildConfig.DEBUG && // Use debug mode if no root + InstallerInitializer.peekMagiskPath() == null + ) + } + } + }, + false + ) { + override fun shouldRemove(): Boolean { + return !BuildConfig.DEBUG && + (MainApplication.isShowcaseMode() || + InstallerInitializer.peekMagiskPath() == null) + } + }; + + constructor(@StringRes textId: Int, iconId: Int, onClickListener: View.OnClickListener) : this( + textId, + iconId, + androidx.appcompat.R.attr.colorError, + com.google.android.material.R.attr.colorOnPrimary, + onClickListener + ) + + open fun shouldRemove(): Boolean { + // By default, remove the notification` + return false + } + + fun autoAdd(moduleViewListBuilder: ModuleViewListBuilder) { + if (!shouldRemove()) moduleViewListBuilder.addNotification(this) + } + + companion object { + fun needPatch(target: File?): Boolean { + try { + ZipFile(target).use { zipFile -> + var validEntries = zipFile.getEntry("module.prop") != null + // ensure there's no anykernel.sh + validEntries = validEntries and (zipFile.getEntry("anykernel.sh") == null) + if (validEntries) { + // Ensure id of module is not empty and matches ^[a-zA-Z][a-zA-Z0-9._-]+$ regex + // We need to get the module.prop and parse the id= line + val moduleProp = zipFile.getEntry("module.prop") + // Parse the module.prop + if (moduleProp != null) { + // Find the line with id=, and check if it matches the regex + BufferedReader(InputStreamReader(zipFile.getInputStream(moduleProp))).use { reader -> + var line: String + while (reader.readLine().also { line = it } != null) { + if (line.startsWith("id=")) { + val id = line.substring(3) + return id.isEmpty() || !id.matches(Regex("^[a-zA-Z][a-zA-Z0-9._-]+$")) + } + } + } + } else { + return true + } + } else { + return true + } + } + } catch (e: IOException) { + return true + } + return false + } + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index b7d3206..f842e58 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,14 +14,14 @@ buildscript { set("sentryVersion", "6.18.1") } dependencies { - classpath("com.android.tools.build:gradle:8.0.1") + classpath("com.android.tools.build:gradle:8.0.2") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21") classpath("com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:10.6.2") // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files classpath("io.realm:realm-gradle-plugin:10.15.1") - classpath("io.sentry:sentry-android-gradle-plugin:3.5.0") + classpath("io.sentry:sentry-android-gradle-plugin:3.7.0") } } diff --git a/gradle.properties b/gradle.properties index 06c3c5b..c32672c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -#Mon May 08 13:44:15 EDT 2023 +#Mon May 29 11:36:28 EDT 2023 android.defaults.buildfeatures.buildconfig=true android.enableJetifier=false android.enableR8.fullMode=true @@ -18,5 +18,5 @@ android.useAndroidX=true org.gradle.caching=true org.gradle.configuration-cache=true org.gradle.configuration-cache.problems=warn -org.gradle.jvmargs=-Xmx1024M -Dkotlin.daemon.jvm.options\="-Xmx1024M" -Dfile.encoding\=UTF-8 -XX\:+UseParallelGC -XX\:ReservedCodeCacheSize\=768m +org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx1536M" -Dfile.encoding\=UTF-8 -XX\:+UseParallelGC -XX\:ReservedCodeCacheSize\=768m org.gradle.parallel=true