diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3416c06b7..0512f7bb9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -65,9 +65,7 @@
-
-
diff --git a/app/src/main/java/a/j.java b/app/src/main/java/a/j.java
index 937100012..a7be03859 100644
--- a/app/src/main/java/a/j.java
+++ b/app/src/main/java/a/j.java
@@ -1,7 +1,7 @@
package a;
-import com.topjohnwu.magisk.model.download.DownloadModuleService;
+import com.topjohnwu.magisk.model.download.DownloadService;
-public class j extends DownloadModuleService {
+public class j extends DownloadService {
/* stub */
}
diff --git a/app/src/main/java/a/k.kt b/app/src/main/java/a/k.kt
deleted file mode 100644
index 6d450e761..000000000
--- a/app/src/main/java/a/k.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package a
-
-import com.topjohnwu.magisk.model.download.DownloadService
-
-class k : DownloadService()
\ No newline at end of file
diff --git a/app/src/main/java/com/topjohnwu/magisk/ClassMap.kt b/app/src/main/java/com/topjohnwu/magisk/ClassMap.kt
index 5f5f6afd7..2d8aef096 100644
--- a/app/src/main/java/com/topjohnwu/magisk/ClassMap.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/ClassMap.kt
@@ -1,6 +1,5 @@
package com.topjohnwu.magisk
-import com.topjohnwu.magisk.model.download.DownloadModuleService
import com.topjohnwu.magisk.model.download.DownloadService
import com.topjohnwu.magisk.model.receiver.GeneralReceiver
import com.topjohnwu.magisk.model.update.UpdateCheckService
@@ -17,8 +16,7 @@ object ClassMap {
FlashActivity::class.java to a.f::class.java,
UpdateCheckService::class.java to a.g::class.java,
GeneralReceiver::class.java to a.h::class.java,
- DownloadModuleService::class.java to a.j::class.java,
- DownloadService::class.java to a.k::class.java,
+ DownloadService::class.java to a.j::class.java,
SuRequestActivity::class.java to a.m::class.java
)
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 817cbf188..ebdfd1025 100644
--- a/app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt
@@ -4,8 +4,6 @@ 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
@@ -17,6 +15,4 @@ 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/DownloadModuleService.java b/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadModuleService.java
deleted file mode 100644
index ceb3ea494..000000000
--- a/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadModuleService.java
+++ /dev/null
@@ -1,156 +0,0 @@
-package com.topjohnwu.magisk.model.download;
-
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.IBinder;
-
-import androidx.annotation.Nullable;
-
-import com.topjohnwu.magisk.App;
-import com.topjohnwu.magisk.ClassMap;
-import com.topjohnwu.magisk.Const;
-import com.topjohnwu.magisk.model.entity.Repo;
-import com.topjohnwu.magisk.ui.flash.FlashActivity;
-import com.topjohnwu.magisk.view.Notifications;
-import com.topjohnwu.magisk.view.ProgressNotification;
-import com.topjohnwu.net.Networking;
-import com.topjohnwu.superuser.Shell;
-import com.topjohnwu.superuser.ShellUtils;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
-
-public class DownloadModuleService extends Service {
-
- private List notifications;
-
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public void onCreate() {
- notifications = new ArrayList<>();
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Shell.EXECUTOR.execute(() -> {
- Repo repo = intent.getParcelableExtra("repo");
- boolean install = intent.getBooleanExtra("install", false);
- dlProcessInstall(repo, install);
- });
- return START_REDELIVER_INTENT;
- }
-
- @Override
- public synchronized void onTaskRemoved(Intent rootIntent) {
- for (ProgressNotification n : notifications) {
- Notifications.mgr.cancel(n.hashCode());
- }
- notifications.clear();
- }
-
- private synchronized void addNotification(ProgressNotification n) {
- if (notifications.isEmpty()) {
- // Start foreground
- startForeground(n.hashCode(), n.getNotification());
- }
- notifications.add(n);
- }
-
- private synchronized void removeNotification(ProgressNotification n) {
- notifications.remove(n);
- if (notifications.isEmpty()) {
- // No more tasks, stop service
- stopForeground(true);
- stopSelf();
- } else {
- // Pick another notification as our foreground notification
- n = notifications.get(0);
- startForeground(n.hashCode(), n.getNotification());
- }
- }
-
- private void dlProcessInstall(Repo repo, boolean install) {
- File output = new File(Const.EXTERNAL_PATH, repo.getDownloadFilename());
- ProgressNotification progress = new ProgressNotification(output.getName());
- addNotification(progress);
- try {
- InputStream in = Networking.get(repo.getZipUrl())
- .setDownloadProgressListener(progress)
- .execForInputStream().getResult();
- OutputStream out = new BufferedOutputStream(new FileOutputStream(output));
- processZip(in, out);
- Intent intent = new Intent(this, ClassMap.get(FlashActivity.class));
- intent.setData(Uri.fromFile(output))
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- .putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
- synchronized (getApplication()) {
- if (install && App.foreground() != null &&
- !(App.foreground() instanceof FlashActivity)) {
- /* Only start flashing if there is a foreground activity and the
- * user is not also flashing another module at the same time */
- App.foreground().startActivity(intent);
- } else {
- /* Or else we preset a notification notifying that we are done */
- PendingIntent pi = PendingIntent.getActivity(this, progress.hashCode(), intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- progress.dlDone(pi);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- progress.dlFail();
- }
- removeNotification(progress);
- }
-
- private void processZip(InputStream in, OutputStream out)
- throws IOException {
- try (ZipInputStream zin = new ZipInputStream(in);
- ZipOutputStream zout = new ZipOutputStream(out)) {
-
- // Inject latest module-installer.sh as update-binary
- zout.putNextEntry(new ZipEntry("META-INF/"));
- zout.putNextEntry(new ZipEntry("META-INF/com/"));
- zout.putNextEntry(new ZipEntry("META-INF/com/google/"));
- zout.putNextEntry(new ZipEntry("META-INF/com/google/android/"));
- zout.putNextEntry(new ZipEntry("META-INF/com/google/android/update-binary"));
- try (InputStream update_bin = Networking.get(Const.Url.MODULE_INSTALLER)
- .execForInputStream().getResult()) {
- ShellUtils.pump(update_bin, zout);
- }
- zout.putNextEntry(new ZipEntry("META-INF/com/google/android/updater-script"));
- zout.write("#MAGISK\n".getBytes("UTF-8"));
-
- int off = -1;
- ZipEntry entry;
- while ((entry = zin.getNextEntry()) != null) {
- if (off < 0)
- off = entry.getName().indexOf('/') + 1;
- String path = entry.getName().substring(off);
- if (path.isEmpty())
- continue;
- if (path.startsWith("META-INF"))
- continue;
- zout.putNextEntry(new ZipEntry(path));
- if (!entry.isDirectory())
- ShellUtils.pump(zin, zout);
- }
- }
- }
-}
diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/ModuleProcessor.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/ModuleProcessor.kt
new file mode 100644
index 000000000..0d1a23cb6
--- /dev/null
+++ b/app/src/main/java/com/topjohnwu/magisk/model/download/ModuleProcessor.kt
@@ -0,0 +1,44 @@
+package com.topjohnwu.magisk.model.download
+
+import com.topjohnwu.magisk.utils.withStreams
+import java.io.File
+import java.io.InputStream
+import java.util.zip.ZipEntry
+import java.util.zip.ZipInputStream
+import java.util.zip.ZipOutputStream
+
+fun InputStream.toModule(file: File, installer: InputStream) {
+
+ val input = ZipInputStream(buffered())
+ val output = ZipOutputStream(file.outputStream().buffered())
+
+ 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.copyTo(zout)
+
+ 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)
+ }
+ }
+
+ entry = zin.nextEntry
+ }
+ }
+}
\ 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
deleted file mode 100644
index d1dbd7ef9..000000000
--- a/app/src/main/java/com/topjohnwu/magisk/model/download/ModuleTransformer.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-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 ac77544eb..31c7d7f60 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,18 +9,19 @@ 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.ProgInputStream
+import com.topjohnwu.magisk.utils.cachedFile
import com.topjohnwu.magisk.utils.firstMap
-import com.topjohnwu.magisk.utils.writeToCachedFile
+import com.topjohnwu.magisk.utils.writeTo
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.io.InputStream
abstract class RemoteFileService : NotificationService() {
@@ -73,19 +74,14 @@ abstract class RemoteFileService : NotificationService() {
}
private fun download(subject: DownloadSubject) = repo.downloadFile(subject.url)
- .map { it.toFile(subject.hashCode(), subject.fileName) }
+ .map { it.toStream(subject.hashCode()) }
.map {
- when (subject) {
- is Module -> {
- update(subject.hashCode()) {
- it.setContentText(getString(R.string.download_module))
- .setProgress(0, 0, true)
- }
-
- get { parametersOf(subject) }
- .inject(it, startInternal(Installer).blockingGet())
+ cachedFile(subject.fileName).apply {
+ when (subject) {
+ is Module -> it.toModule(this,
+ repo.downloadFile(Const.Url.MODULE_INSTALLER).blockingGet().byteStream())
+ else -> it.writeTo(this)
}
- else -> it
}
}
@@ -95,11 +91,11 @@ abstract class RemoteFileService : NotificationService() {
.firstOrNull { it == name }
?.let { File(this, it) }
- private fun ResponseBody.toFile(id: Int, name: String): File {
+ private fun ResponseBody.toStream(id: Int): InputStream {
val maxRaw = contentLength()
val max = maxRaw / 1_000_000f
- return writeToCachedFile(this@RemoteFileService, name) {
+ return ProgInputStream(byteStream()) {
val progress = it / 1_000_000f
update(id) { notification ->
diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt
index 200e9d8a7..5f81cd5da 100644
--- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeFragment.kt
@@ -11,7 +11,7 @@ import com.topjohnwu.magisk.model.events.*
import com.topjohnwu.magisk.ui.base.MagiskActivity
import com.topjohnwu.magisk.ui.base.MagiskFragment
import com.topjohnwu.magisk.utils.ISafetyNetHelper
-import com.topjohnwu.magisk.utils.copyTo
+import com.topjohnwu.magisk.utils.writeTo
import com.topjohnwu.magisk.utils.inject
import com.topjohnwu.magisk.view.MarkDownWindow
import com.topjohnwu.magisk.view.dialogs.*
@@ -73,7 +73,7 @@ class HomeFragment : MagiskFragment(),
private fun downloadSafetyNet(requiresUserInput: Boolean = true) {
fun download() = magiskRepo.fetchSafetynet()
- .map { it.byteStream().copyTo(EXT_FILE) }
+ .map { it.byteStream().writeTo(EXT_FILE) }
.subscribeK { updateSafetyNet(true) }
if (!requiresUserInput) {
diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt
index beab751ea..a7272c2c9 100644
--- a/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt
@@ -95,10 +95,12 @@ fun File.provide(context: Context = get()): Uri {
}
fun File.mv(destination: File) {
- inputStream().copyTo(destination)
+ inputStream().writeTo(destination)
deleteRecursively()
}
fun String.toFile() = File(this)
-fun Intent.chooser(title: String = "Pick an app") = Intent.createChooser(this, title)
\ No newline at end of file
+fun Intent.chooser(title: String = "Pick an app") = Intent.createChooser(this, title)
+
+fun Context.cachedFile(name: String) = File(cacheDir, name)
diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/XJava.kt b/app/src/main/java/com/topjohnwu/magisk/utils/XJava.kt
index cbcab88bc..41a7b8b94 100644
--- a/app/src/main/java/com/topjohnwu/magisk/utils/XJava.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/utils/XJava.kt
@@ -2,8 +2,6 @@ package com.topjohnwu.magisk.utils
import android.net.Uri
import androidx.core.net.toFile
-import org.kamranzafar.jtar.TarInputStream
-import org.kamranzafar.jtar.TarOutputStream
import java.io.File
import java.io.InputStream
import java.io.OutputStream
@@ -18,12 +16,10 @@ fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) {
}
}
-fun Uri.copyTo(file: File) = toFile().copyTo(file)
-fun InputStream.copyTo(file: File) =
- withStreams(this, file.outputStream()) { reader, writer -> reader.copyTo(writer) }
+fun Uri.writeTo(file: File) = toFile().copyTo(file)
-fun File.tarInputStream() = TarInputStream(inputStream())
-fun File.tarOutputStream() = TarOutputStream(this)
+fun InputStream.writeTo(file: File) =
+ withStreams(this, file.outputStream()) { reader, writer -> reader.copyTo(writer) }
inline fun withStreams(
inStream: In,
diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/XNetwork.kt b/app/src/main/java/com/topjohnwu/magisk/utils/XNetwork.kt
index 591ae5187..e86229166 100644
--- a/app/src/main/java/com/topjohnwu/magisk/utils/XNetwork.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/utils/XNetwork.kt
@@ -1,8 +1,10 @@
package com.topjohnwu.magisk.utils
import android.content.Context
+import com.topjohnwu.superuser.internal.UiThreadHandler
import okhttp3.ResponseBody
import java.io.File
+import java.io.FilterInputStream
import java.io.InputStream
import java.io.OutputStream
@@ -32,8 +34,6 @@ inline fun InputStream.writeTo(output: OutputStream, progress: (Long) -> Unit =
fun ResponseBody.writeToString() = string()
-fun Context.cachedFile(name: String) = File(cacheDir, name)
-
inline fun InputStream.copyToWithProgress(
out: OutputStream,
progressEmitter: (Long) -> Unit,
@@ -49,4 +49,32 @@ inline fun InputStream.copyToWithProgress(
progressEmitter(bytesCopied)
}
return bytesCopied
+}
+
+class ProgInputStream(base: InputStream,
+ val progressEmitter: (Long) -> Unit = {}) : FilterInputStream(base) {
+
+ private var bytesRead : Long = 0
+
+ override fun read(): Int {
+ val b = read()
+ if (b >= 0) {
+ bytesRead++
+ UiThreadHandler.run { progressEmitter(bytesRead) }
+ }
+ return b
+ }
+
+ override fun read(b: ByteArray): Int {
+ return read(b, 0, b.size)
+ }
+
+ override fun read(b: ByteArray, off: Int, len: Int): Int {
+ val sz = super.read(b, off, len)
+ if (sz > 0) {
+ bytesRead += sz
+ UiThreadHandler.run { progressEmitter(bytesRead) }
+ }
+ return sz
+ }
}
\ No newline at end of file