From e70e8088ad592b4683192bc45ac8f9045c6302d8 Mon Sep 17 00:00:00 2001 From: vvb2060 Date: Tue, 14 Apr 2026 01:26:05 -0700 Subject: [PATCH] app: support download image and patch 1/2 --- .../topjohnwu/magisk/dialog/DownloadDialog.kt | 59 +++ .../magisk/ui/flash/FlashFragment.kt | 7 + .../magisk/ui/flash/FlashViewModel.kt | 8 + .../magisk/ui/install/InstallViewModel.kt | 5 + .../main/res/layout/fragment_install_md2.xml | 10 +- app/core/build.gradle.kts | 20 + app/core/proguard-rules.pro | 3 + .../java/com/topjohnwu/magisk/core/Const.kt | 1 + .../magisk/core/tasks/MagiskInstaller.kt | 64 +++ .../magisk/core/utils/MediaStoreUtils.kt | 2 + app/core/src/main/proto/update_metadata.proto | 445 ++++++++++++++++++ app/core/src/main/res/values/strings.xml | 3 + app/gradle.properties | 1 + app/gradle/libs.versions.toml | 5 + 14 files changed, 632 insertions(+), 1 deletion(-) create mode 100644 app/apk/src/main/java/com/topjohnwu/magisk/dialog/DownloadDialog.kt create mode 100644 app/core/src/main/proto/update_metadata.proto diff --git a/app/apk/src/main/java/com/topjohnwu/magisk/dialog/DownloadDialog.kt b/app/apk/src/main/java/com/topjohnwu/magisk/dialog/DownloadDialog.kt new file mode 100644 index 000000000..d8d48f6f2 --- /dev/null +++ b/app/apk/src/main/java/com/topjohnwu/magisk/dialog/DownloadDialog.kt @@ -0,0 +1,59 @@ +package com.topjohnwu.magisk.dialog + +import android.net.Uri +import android.text.InputType +import android.widget.EditText +import androidx.core.net.toUri +import com.topjohnwu.magisk.core.R +import com.topjohnwu.magisk.events.DialogBuilder +import com.topjohnwu.magisk.view.MagiskDialog + +class DownloadDialog(private val callback: (Uri) -> Unit) : DialogBuilder { + + override fun build(dialog: MagiskDialog) { + val editText = EditText(dialog.context).apply { + inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI + hint = context.getString(R.string.download_dialog_msg) + requestFocus() + } + + dialog.apply { + setTitle(R.string.download_dialog_title) + setView(editText) + setButton(MagiskDialog.ButtonType.POSITIVE) { + text = android.R.string.ok + onClick { + val url = editText.text.toString().trim() + isValidUrl(url)?.let { + doNotDismiss = false + callback(it) + } ?: run { + doNotDismiss = true + editText.error = context.getString(R.string.download_dialog_title) + } + } + } + setButton(MagiskDialog.ButtonType.NEGATIVE) { + text = android.R.string.cancel + } + setCancelable(true) + } + } + + private fun isValidUrl(url: String): Uri? { + if (url.isEmpty()) { + return null + } + val uri = url.toUri() + if (!uri.scheme.equals("https", ignoreCase = true)) { + return null + } + if (uri.host.isNullOrEmpty()) { + return null + } + if (uri.path.isNullOrEmpty()) { + return null + } + return uri + } +} diff --git a/app/apk/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt b/app/apk/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt index 7231a9b9f..fd694cfe4 100644 --- a/app/apk/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt +++ b/app/apk/src/main/java/com/topjohnwu/magisk/ui/flash/FlashFragment.kt @@ -127,6 +127,13 @@ class FlashFragment : BaseFragment(), MenuProvider { additionalData = uri ) + // Downloading is understood as downloading file then patch */ + + fun download(uri: Uri) = MainDirections.actionFlashFragment( + action = Const.Value.DOWNLOAD, + additionalData = uri + ) + /* Uninstalling is understood as removing magisk entirely */ fun uninstall() = MainDirections.actionFlashFragment( diff --git a/app/apk/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt b/app/apk/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt index 56704d869..661f40dea 100644 --- a/app/apk/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt +++ b/app/apk/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt @@ -1,5 +1,6 @@ package com.topjohnwu.magisk.ui.flash +import android.os.Build import android.view.MenuItem import androidx.databinding.Bindable import androidx.databinding.ObservableArrayList @@ -80,6 +81,13 @@ class FlashViewModel : BaseViewModel() { showReboot = false MagiskInstaller.Patch(uri, outItems, logItems).exec() } + Const.Value.DOWNLOAD -> { + if (uri == null || Build.VERSION.SDK_INT < 29) { + return@launch + } + showReboot = false + MagiskInstaller.Download(uri.toString(), outItems, logItems).exec() + } else -> { back() return@launch diff --git a/app/apk/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt b/app/apk/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt index 6508ce4e1..b6a64dffd 100644 --- a/app/apk/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt +++ b/app/apk/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt @@ -21,6 +21,7 @@ import com.topjohnwu.magisk.core.base.ContentResultCallback import com.topjohnwu.magisk.core.ktx.toast import com.topjohnwu.magisk.core.repository.NetworkService import com.topjohnwu.magisk.databinding.set +import com.topjohnwu.magisk.dialog.DownloadDialog import com.topjohnwu.magisk.dialog.SecondSlotWarningDialog import com.topjohnwu.magisk.events.GetContentEvent import com.topjohnwu.magisk.ui.flash.FlashFragment @@ -54,6 +55,9 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel() R.id.method_patch -> { GetContentEvent("*/*", UriCallback()).publish() } + R.id.method_download -> { + DownloadDialog { url -> uri.value = url }.show() + } R.id.method_inactive_slot -> { SecondSlotWarningDialog().show() } @@ -92,6 +96,7 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel() fun install() { when (method) { R.id.method_patch -> FlashFragment.patch(data.value!!).navigate(true) + R.id.method_download -> FlashFragment.download(data.value!!).navigate(true) R.id.method_direct -> FlashFragment.flash(false).navigate(true) R.id.method_inactive_slot -> FlashFragment.flash(true).navigate(true) else -> error("Unknown value") diff --git a/app/apk/src/main/res/layout/fragment_install_md2.xml b/app/apk/src/main/res/layout/fragment_install_md2.xml index 7a7364379..5305a87a2 100644 --- a/app/apk/src/main/res/layout/fragment_install_md2.xml +++ b/app/apk/src/main/res/layout/fragment_install_md2.xml @@ -161,7 +161,7 @@