From 9542ca773f8b0a76dda7586d473925f938e9c2a2 Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Tue, 9 Jul 2019 20:08:00 +0200 Subject: [PATCH] Added new CompoundDownloadService which will encapsulate all downloads and should manage post-download events as well As of now it's still in a development stage and isn't connected to anything --- app/src/main/AndroidManifest.xml | 7 +- app/src/main/java/a/k.kt | 5 + .../java/com/topjohnwu/magisk/ClassMap.kt | 2 + .../main/java/com/topjohnwu/magisk/Config.kt | 2 + .../magisk/data/repository/FileRepository.kt | 11 ++ .../topjohnwu/magisk/di/RepositoryModule.kt | 2 + .../model/download/CompoundDownloadService.kt | 75 ++++++++++ .../download/SubstrateDownloadService.kt | 135 ++++++++++++++++++ .../magisk/model/entity/UpdateInfo.kt | 5 +- .../model/entity/internal/Configuration.kt | 5 + .../model/entity/internal/DownloadSubject.kt | 35 +++++ app/src/main/res/values/strings.xml | 2 + 12 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/a/k.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/data/repository/FileRepository.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/download/CompoundDownloadService.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/download/SubstrateDownloadService.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/entity/internal/Configuration.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/entity/internal/DownloadSubject.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4bf6ef98d..3416c06b7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,8 +9,8 @@ @@ -41,9 +41,9 @@ @@ -66,6 +66,9 @@ + { + if (subject.configuration == Configuration.FLASH) { + FlashActivity.flashMagisk(this, file) + } + } + is DownloadSubject.Module -> { + if (subject.configuration == Configuration.FLASH) { + FlashActivity.flashModule(this, file) + } + } + } + } + + // --- + + override fun NotificationCompat.Builder.addActions(file: File, subject: DownloadSubject) = + when (subject) { + is DownloadSubject.Magisk -> addMagiskActions(file, subject.configuration) + is DownloadSubject.Module -> addModuleActions(file, subject.configuration) + } + + private fun NotificationCompat.Builder.addMagiskActions( + file: File, + configuration: Configuration + ) = apply { + when (configuration) { + Configuration.FLASH -> { + val inner = FlashActivity.flashMagiskIntent(context, file) + val intent = PendingIntent + .getActivity(context, nextInt(), inner, PendingIntent.FLAG_ONE_SHOT) + + setContentIntent(intent) + } + } + + } + + private fun NotificationCompat.Builder.addModuleActions( + file: File, + configuration: Configuration + ) = apply { + + } + + companion object { + + fun download(context: Context, subject: DownloadSubject) = + Intent(context, ClassMap[CompoundDownloadService::class.java]) + .putExtra(ARG_URL, subject) + .let { context.startService(it); Unit } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/SubstrateDownloadService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/SubstrateDownloadService.kt new file mode 100644 index 000000000..ac6afec4b --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/SubstrateDownloadService.kt @@ -0,0 +1,135 @@ +package com.topjohnwu.magisk.model.download + +import android.app.NotificationManager +import android.app.Service +import android.content.Intent +import android.os.IBinder +import androidx.core.app.NotificationCompat +import androidx.core.content.getSystemService +import com.skoumal.teanity.extensions.subscribeK +import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.Const +import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.data.repository.FileRepository +import com.topjohnwu.magisk.model.entity.internal.DownloadSubject +import com.topjohnwu.magisk.utils.writeToCachedFile +import com.topjohnwu.magisk.view.Notifications +import com.topjohnwu.superuser.ShellUtils +import io.reactivex.Single +import okhttp3.ResponseBody +import org.koin.android.ext.android.inject +import java.io.File +import kotlin.random.Random.Default.nextInt + +abstract class SubstrateDownloadService : Service() { + + private var _notification: NotificationCompat.Builder? = null + + private val repo by inject() + + private val notification: NotificationCompat.Builder + get() = _notification ?: Notifications.progress(this, "") + .setContentText(getString(R.string.download_local)) + .also { _notification = it } + + override fun onBind(p0: Intent?): IBinder? = null + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + intent?.getParcelableExtra(ARG_URL)?.let { + updateNotification { notification -> notification.setContentTitle(it.fileName) } + start(it) + } + return START_REDELIVER_INTENT + } + + // --- + + private fun start(subject: DownloadSubject) = search(subject) + .onErrorResumeNext(download(subject)) + .subscribeK { + runCatching { onFinished(it, subject) } + finish(it, subject) + } + + private fun search(subject: DownloadSubject) = Single.fromCallable { + if (!Config.isDownloadCacheEnabled) { + throw IllegalStateException("The download cache is disabled") + } + + val file = runCatching { + cacheDir.list().orEmpty() + .first { it == subject.fileName } // this throws an exception if not found + .let { File(cacheDir, it) } + }.getOrElse { + Const.EXTERNAL_PATH.list().orEmpty() + .first { it == subject.fileName } // this throws an exception if not found + .let { File(Const.EXTERNAL_PATH, it) } + } + + if (subject is DownloadSubject.Magisk) { + if (!ShellUtils.checkSum("MD5", file, subject.magisk.hash)) { + throw IllegalStateException("The given file doesn't match the hash") + } + } + + file + } + + private fun download(subject: DownloadSubject) = repo.downloadFile(subject.url) + .map { it.toFile(subject.fileName) } + + // --- + + private fun ResponseBody.toFile(name: String): File { + val maxRaw = contentLength() + val max = maxRaw / 1_000_000f + + return writeToCachedFile(this@SubstrateDownloadService, name) { + val progress = it / 1_000_000f + + updateNotification { notification -> + notification + .setProgress(maxRaw.toInt(), it.toInt(), false) + .setContentText(getString(R.string.download_progress, progress, max)) + } + } + } + + private fun finish(file: File, subject: DownloadSubject) { + stopForeground(false) + + val notification = notification.addActions(file, subject) + .setContentText(getString(R.string.download_complete)) + .setSmallIcon(android.R.drawable.stat_sys_download_done) + .setProgress(0, 0, false) + .setOngoing(false) + .setAutoCancel(true) + .build() + + getSystemService()?.notify(nextInt(), notification) + + stopSelf() + } + + private inline fun updateNotification(body: (NotificationCompat.Builder) -> Unit = {}) { + startForeground(ID, notification.also(body).build()) + } + + // --- + + + @Throws(Throwable::class) + protected abstract fun onFinished(file: File, subject: DownloadSubject) + + protected abstract fun NotificationCompat.Builder.addActions( + file: File, + subject: DownloadSubject + ): NotificationCompat.Builder + + + companion object { + private const val ID = 300 + const val ARG_URL = "arg_url" + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/UpdateInfo.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/UpdateInfo.kt index ed80529b0..52b1633bc 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/UpdateInfo.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/UpdateInfo.kt @@ -1,6 +1,8 @@ package com.topjohnwu.magisk.model.entity +import android.os.Parcelable import com.squareup.moshi.Json +import kotlinx.android.parcel.Parcelize import se.ansman.kotshi.JsonSerializable @JsonSerializable @@ -15,6 +17,7 @@ data class UninstallerJson( val link: String = "" ) +@Parcelize @JsonSerializable data class MagiskJson( val version: String = "", @@ -22,7 +25,7 @@ data class MagiskJson( val link: String = "", val note: String = "", @Json(name = "md5") val hash: String = "" -) +) : Parcelable @JsonSerializable data class ManagerJson( diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/internal/Configuration.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/internal/Configuration.kt new file mode 100644 index 000000000..25bbebcb9 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/internal/Configuration.kt @@ -0,0 +1,5 @@ +package com.topjohnwu.magisk.model.entity.internal + +enum class Configuration { + FLASH, DOWNLOAD +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/internal/DownloadSubject.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/internal/DownloadSubject.kt new file mode 100644 index 000000000..a6ea53fc5 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/internal/DownloadSubject.kt @@ -0,0 +1,35 @@ +package com.topjohnwu.magisk.model.entity.internal + +import android.os.Parcelable +import com.topjohnwu.magisk.model.entity.MagiskJson +import kotlinx.android.parcel.Parcelize +import com.topjohnwu.magisk.model.entity.Module as MagiskModule + +sealed class DownloadSubject : Parcelable { + + abstract val fileName: String + abstract val url: String + + @Parcelize + data class Module( + val module: MagiskModule, + val configuration: Configuration + ) : DownloadSubject() { + + override val url: String get() = module.path + override val fileName: String get() = "${module.name}-v${module.version}(${module.versionCode}).zip" + + } + + @Parcelize + data class Magisk( + val magisk: MagiskJson, + val configuration: Configuration + ) : DownloadSubject() { + + override val url: String get() = magisk.link + override val fileName get() = "Magisk-v${magisk.version}(${magisk.versionCode}).zip" + + } + +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e34c43b60..e8a4f1710 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -71,6 +71,8 @@ Magisk Updates Progress Notifications Download complete + Looking for local copies… + %1$.2f / %2$.2f MB Error downloading file Magisk Update Available! Magisk Manager Update Available!