mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-04-16 19:01:24 +00:00
Added appending installers to modules
This commit is contained in:
parent
967bdeae7b
commit
0785945635
@ -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() {
|
|||||||
) = 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
|
|||||||
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() {
|
|||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
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() {
|
|||||||
.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() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
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 {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
object Installer : DownloadSubject() {
|
||||||
|
override val fileName: String get() = "module_installer(${BuildConfig.VERSION_CODE}).sh"
|
||||||
|
override val url: String get() = Const.Url.MODULE_INSTALLER
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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 @@
|
|||||||
<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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user