diff --git a/app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt b/app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt index ebdfd1025..817cbf188 100644 --- a/app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt +++ b/app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt @@ -4,6 +4,8 @@ import android.content.Context import androidx.preference.PreferenceManager import com.skoumal.teanity.rxbus.RxBus import com.topjohnwu.magisk.App +import com.topjohnwu.magisk.model.download.ModuleTransformer +import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import org.koin.dsl.module @@ -15,4 +17,6 @@ val applicationModule = module { factory(Protected) { get().protectedContext } single(SUTimeout) { get(Protected).getSharedPreferences("su_timeout", 0) } single { PreferenceManager.getDefaultSharedPreferences(get(Protected)) } + + factory { (subject: DownloadSubject) -> ModuleTransformer(get(), subject) } } \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/ModuleTransformer.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/ModuleTransformer.kt new file mode 100644 index 000000000..d1dbd7ef9 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/ModuleTransformer.kt @@ -0,0 +1,68 @@ +package com.topjohnwu.magisk.model.download + +import android.content.Context +import com.topjohnwu.magisk.model.entity.internal.DownloadSubject +import com.topjohnwu.magisk.utils.cachedFile +import com.topjohnwu.magisk.utils.withStreams +import java.io.File +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream + +class ModuleTransformer( + private val context: Context, + subject: DownloadSubject +) { + + private val destination = context.cachedFile(subject.fileName) + + fun inject(file: File, installer: File): File { + return injectInternal(move(file), installer) + } + + // --- + + private fun injectInternal(file: File, installer: File): File { + val input = ZipInputStream(file.inputStream()) + val output = ZipOutputStream(destination.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) { + 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 + } + } + + file.delete() + + return destination + } + + private fun move(file: File) = context.cachedFile("temp").apply { + file.renameTo(this) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt index 83265ee66..fd73019ba 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt @@ -9,20 +9,17 @@ import com.topjohnwu.magisk.R 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.utils.cachedFile -import com.topjohnwu.magisk.utils.withStreams import com.topjohnwu.magisk.utils.writeToCachedFile import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.superuser.ShellUtils import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import okhttp3.ResponseBody +import org.koin.android.ext.android.get import org.koin.android.ext.android.inject +import org.koin.core.parameter.parametersOf import timber.log.Timber import java.io.File -import java.util.zip.ZipEntry -import java.util.zip.ZipInputStream -import java.util.zip.ZipOutputStream abstract class RemoteFileService : NotificationService() { @@ -40,7 +37,7 @@ abstract class RemoteFileService : NotificationService() { // --- - private fun startInternal(subject: DownloadSubject) = search(subject) + private fun startInternal(subject: DownloadSubject): Single = search(subject) .onErrorResumeNext(download(subject)) .doOnSubscribe { update(subject.hashCode()) { it.setContentTitle(subject.fileName) } } .observeOn(AndroidSchedulers.mainThread()) @@ -76,57 +73,22 @@ abstract class RemoteFileService : NotificationService() { } private fun download(subject: DownloadSubject) = repo.downloadFile(subject.url) + .map { it.toFile(subject.hashCode(), subject.fileName) } .map { 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) { - 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() } + is Module -> { + update(subject.hashCode()) { + it.setContentText(getString(R.string.download_module)) + .setProgress(0, 0, true) } - } - entry = zin.nextEntry + get { parametersOf(subject) } + .inject(it, startInternal(Installer).blockingGet()) + } + else -> it } } - return target - } - // --- private fun ResponseBody.toFile(id: Int, name: String): File {