Commit 07859456 authored by Viktor De Pasquale's avatar Viktor De Pasquale Committed by John Wu

Added appending installers to modules

parent 967bdeae
...@@ -43,6 +43,7 @@ open class DownloadService : RemoteFileService() { ...@@ -43,6 +43,7 @@ open class DownloadService : RemoteFileService() {
override fun onFinished(file: File, subject: DownloadSubject) = when (subject) { override fun onFinished(file: File, subject: DownloadSubject) = when (subject) {
is Magisk -> onFinishedInternal(file, subject) is Magisk -> onFinishedInternal(file, subject)
is Module -> onFinishedInternal(file, subject) is Module -> onFinishedInternal(file, subject)
else -> Unit
} }
private fun onFinishedInternal( private fun onFinishedInternal(
...@@ -73,6 +74,7 @@ open class DownloadService : RemoteFileService() { ...@@ -73,6 +74,7 @@ open class DownloadService : RemoteFileService() {
) = when (subject) { ) = when (subject) {
is Magisk -> addActionsInternal(file, subject) is Magisk -> addActionsInternal(file, subject)
is Module -> addActionsInternal(file, subject) is Module -> addActionsInternal(file, subject)
else -> this
} }
private fun NotificationCompat.Builder.addActionsInternal( private fun NotificationCompat.Builder.addActionsInternal(
......
...@@ -8,14 +8,21 @@ import com.topjohnwu.magisk.Const ...@@ -8,14 +8,21 @@ import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.data.repository.FileRepository import com.topjohnwu.magisk.data.repository.FileRepository
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
import com.topjohnwu.magisk.utils.cachedFile
import com.topjohnwu.magisk.utils.withStreams
import com.topjohnwu.magisk.utils.writeToCachedFile import com.topjohnwu.magisk.utils.writeToCachedFile
import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import okhttp3.ResponseBody import okhttp3.ResponseBody
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
abstract class RemoteFileService : NotificationService() { abstract class RemoteFileService : NotificationService() {
...@@ -33,14 +40,17 @@ abstract class RemoteFileService : NotificationService() { ...@@ -33,14 +40,17 @@ abstract class RemoteFileService : NotificationService() {
// --- // ---
private fun start(subject: DownloadSubject) = search(subject) private fun startInternal(subject: DownloadSubject) = search(subject)
.onErrorResumeNext(download(subject)) .onErrorResumeNext(download(subject))
.doOnSubscribe { update(subject.hashCode()) { it.setContentTitle(subject.fileName) } } .doOnSubscribe { update(subject.hashCode()) { it.setContentTitle(subject.fileName) } }
.subscribeK { .observeOn(AndroidSchedulers.mainThread())
.doOnSuccess {
runCatching { onFinished(it, subject) }.onFailure { Timber.e(it) } runCatching { onFinished(it, subject) }.onFailure { Timber.e(it) }
finish(it, subject) finish(it, subject)
} }
private fun start(subject: DownloadSubject) = startInternal(subject).subscribeK()
private fun search(subject: DownloadSubject) = Single.fromCallable { private fun search(subject: DownloadSubject) = Single.fromCallable {
if (!Config.isDownloadCacheEnabled) { if (!Config.isDownloadCacheEnabled) {
throw IllegalStateException("The download cache is disabled") throw IllegalStateException("The download cache is disabled")
...@@ -56,7 +66,7 @@ abstract class RemoteFileService : NotificationService() { ...@@ -56,7 +66,7 @@ abstract class RemoteFileService : NotificationService() {
.let { File(Const.EXTERNAL_PATH, it) } .let { File(Const.EXTERNAL_PATH, it) }
} }
if (subject is DownloadSubject.Magisk) { if (subject is Magisk) {
if (!ShellUtils.checkSum("MD5", file, subject.magisk.hash)) { if (!ShellUtils.checkSum("MD5", file, subject.magisk.hash)) {
throw IllegalStateException("The given file doesn't match the hash") throw IllegalStateException("The given file doesn't match the hash")
} }
...@@ -66,8 +76,57 @@ abstract class RemoteFileService : NotificationService() { ...@@ -66,8 +76,57 @@ abstract class RemoteFileService : NotificationService() {
} }
private fun download(subject: DownloadSubject) = repo.downloadFile(subject.url) private fun download(subject: DownloadSubject) = repo.downloadFile(subject.url)
.map { it.toFile(subject.hashCode(), subject.fileName) } .map {
.map { map(subject, it) } when (subject) {
is Module -> appendInstaller(it, subject)
else -> it.toFile(subject.hashCode(), subject.fileName)
}
}
private fun appendInstaller(body: ResponseBody, subject: DownloadSubject): File {
update(subject.hashCode()) {
it.setContentText(getString(R.string.download_module))
}
val installer = startInternal(Installer).blockingGet()
val target = cachedFile(subject.fileName)
val input = ZipInputStream(body.byteStream())
val output = ZipOutputStream(target.outputStream())
withStreams(input, output) { zin, zout ->
zout.putNextEntry(ZipEntry("META-INF/"))
zout.putNextEntry(ZipEntry("META-INF/com/"))
zout.putNextEntry(ZipEntry("META-INF/com/google/"))
zout.putNextEntry(ZipEntry("META-INF/com/google/android/"))
zout.putNextEntry(ZipEntry("META-INF/com/google/android/update-binary"))
installer.inputStream().copyTo(zout).also { zout.flush() }
zout.putNextEntry(ZipEntry("META-INF/com/google/android/updater-script"))
zout.write("#MAGISK\n".toByteArray(charset("UTF-8")))
var off = -1
var entry: ZipEntry? = zin.nextEntry
while (entry != null) {
Timber.i("Let's gooo (${entry.name})")
if (off < 0) {
off = entry.name.indexOf('/') + 1
}
val path = entry.name.substring(off)
if (path.isNotEmpty() && !path.startsWith("META-INF")) {
zout.putNextEntry(ZipEntry(path))
if (!entry.isDirectory) {
zin.copyTo(zout).also { zout.flush() }
}
}
entry = zin.nextEntry
}
}
return target
}
// --- // ---
...@@ -87,6 +146,8 @@ abstract class RemoteFileService : NotificationService() { ...@@ -87,6 +146,8 @@ abstract class RemoteFileService : NotificationService() {
} }
private fun finish(file: File, subject: DownloadSubject) = finishWork(subject.hashCode()) { private fun finish(file: File, subject: DownloadSubject) = finishWork(subject.hashCode()) {
if (subject is Installer) return@finishWork null
it.addActions(file, subject) it.addActions(file, subject)
.setContentText(getString(R.string.download_complete)) .setContentText(getString(R.string.download_complete))
.setSmallIcon(android.R.drawable.stat_sys_download_done) .setSmallIcon(android.R.drawable.stat_sys_download_done)
......
package com.topjohnwu.magisk.model.entity.internal package com.topjohnwu.magisk.model.entity.internal
import android.os.Parcelable import android.os.Parcelable
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.model.entity.MagiskJson import com.topjohnwu.magisk.model.entity.MagiskJson
import com.topjohnwu.magisk.model.entity.Repo import com.topjohnwu.magisk.model.entity.Repo
...@@ -33,4 +35,10 @@ sealed class DownloadSubject : Parcelable { ...@@ -33,4 +35,10 @@ sealed class DownloadSubject : Parcelable {
} }
@Parcelize
object Installer : DownloadSubject() {
override val fileName: String get() = "module_installer(${BuildConfig.VERSION_CODE}).sh"
override val url: String get() = Const.Url.MODULE_INSTALLER
}
} }
\ No newline at end of file
...@@ -10,16 +10,30 @@ inline fun ResponseBody.writeToCachedFile( ...@@ -10,16 +10,30 @@ inline fun ResponseBody.writeToCachedFile(
context: Context, context: Context,
fileName: String, fileName: String,
progress: (Long) -> Unit = {} progress: (Long) -> Unit = {}
): File { ): File = byteStream().writeToCachedFile(context, fileName, progress)
val file = File(context.cacheDir, fileName)
withStreams(byteStream(), file.outputStream()) { inStream, outStream -> inline fun InputStream.writeToCachedFile(
context: Context,
fileName: String,
progress: (Long) -> Unit = {}
) = context.cachedFile(fileName).apply {
writeToFile(this, progress)
}
inline fun InputStream.writeToFile(file: File, progress: (Long) -> Unit = {}) = file.apply {
writeTo(file.outputStream(), progress)
}
inline fun InputStream.writeTo(output: OutputStream, progress: (Long) -> Unit = {}) {
withStreams(this, output) { inStream, outStream ->
inStream.copyToWithProgress(outStream, progress) inStream.copyToWithProgress(outStream, progress)
} }
return file
} }
fun ResponseBody.writeToString() = string() fun ResponseBody.writeToString() = string()
fun Context.cachedFile(name: String) = File(cacheDir, name)
inline fun InputStream.copyToWithProgress( inline fun InputStream.copyToWithProgress(
out: OutputStream, out: OutputStream,
progressEmitter: (Long) -> Unit, progressEmitter: (Long) -> Unit,
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
<string name="download_complete">Download complete</string> <string name="download_complete">Download complete</string>
<string name="download_local">Looking for local copies…</string> <string name="download_local">Looking for local copies…</string>
<string name="download_progress">%1$.2f / %2$.2f MB</string> <string name="download_progress">%1$.2f / %2$.2f MB</string>
<string name="download_module">Injecting installer…</string>
<string name="download_file_error">Error downloading file</string> <string name="download_file_error">Error downloading file</string>
<string name="magisk_update_title">Magisk Update Available!</string> <string name="magisk_update_title">Magisk Update Available!</string>
<string name="manager_update_title">Magisk Manager Update Available!</string> <string name="manager_update_title">Magisk Manager Update Available!</string>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment