diff --git a/app/src/main/java/eu/depau/etchdroid/ui/misc/SparseFile.kt b/app/src/main/java/eu/depau/etchdroid/ui/misc/SparseFile.kt new file mode 100644 index 0000000..cf87063 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/ui/misc/SparseFile.kt @@ -0,0 +1,19 @@ +package eu.depau.etchdroid.ui.misc + +import java.io.File +import java.io.FileDescriptor +import java.io.RandomAccessFile + +class SparseFile : RandomAccessFile { + constructor(file: File, mode: String) : super(file, mode) + constructor(path: String, mode: String) : super(path, mode) + + fun lseek(fd: FileDescriptor, offset: Long, whence: Int): Long { + val libcore = Class.forName("libcore.io.Libcore") + val os = libcore.getField("os").get(null) + val lseek = os.javaClass.getMethod( + "lseek", FileDescriptor::class.java, Long::class.java, Int::class.java + ) + return lseek.invoke(null, fd, offset, whence) as Long + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/DMGImage.kt b/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/DMGImage.kt index 8c73f0a..8780b07 100644 --- a/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/DMGImage.kt +++ b/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/DMGImage.kt @@ -2,78 +2,21 @@ package eu.depau.etchdroid.utils.imagetypes import android.content.Context import android.net.Uri +import eu.depau.etchdroid.utils.Partition +import eu.depau.etchdroid.utils.PartitionBuilder import eu.depau.etchdroid.utils.enums.FilesystemType import eu.depau.etchdroid.utils.enums.PartitionTableType import eu.depau.etchdroid.utils.ktexts.getBinary import eu.depau.etchdroid.utils.ktexts.readString -import eu.depau.etchdroid.utils.Partition -import eu.depau.etchdroid.utils.PartitionBuilder +import eu.depau.etchdroid.utils.streams.AbstractSizedInputStream +import eu.depau.etchdroid.utils.streams.SizedInputStream import java.io.File val SECTOR_SIZE = 512 private val partRegex = Regex("partition (\\d+): begin=(\\d+), size=(\\d+), decoded=(\\d+), firstsector=(\\d+), sectorcount=(\\d+), blocksruncount=(\\d+)\\s+(.*) \\((.+) : \\d+\\)", RegexOption.MULTILINE) - -private fun readPartitionTable(dmg2img: File, libDir: String, file: File): Triple?, Long?> { - val pt = ArrayList() - var ptType: PartitionTableType? = null - var imgSize = 0L - - val pb = ProcessBuilder(dmg2img.path, "-v", "-l", file.path) - pb.environment()["LD_LIBRARY_PATH"] = libDir - pb.redirectErrorStream(true) - - val p = pb.start() - val out = p.inputStream.readString() - System.err.println(out) - p.waitFor() - val matches = partRegex.findAll(out) - - matchloop@ for (m in matches) { - val ( - number, begin, size, - decoded, firstsector, - sectorcount, blocksruncount, - label, type - ) = m.destructured - - val pb = PartitionBuilder() - - pb.number = number.toInt() - pb.size = SECTOR_SIZE * sectorcount.toLong() - imgSize += pb.size!! - - if (label.isNotEmpty()) - pb.fsLabel = label - - pb.partLabel = type - - when (type) { - "Apple_partition_map" -> { - ptType = PartitionTableType.MAC - } - "MBR" -> { - ptType = PartitionTableType.MSDOS - continue@matchloop - } - } - - pb.fsType = when { - type == "Apple_Empty" || type == "Apple_Scratch" || type == "Apple_Free" -> FilesystemType.FREE - type == "Apple_HFS" -> FilesystemType.HFSPLUS - type == "Apple_HFSX" -> FilesystemType.HFSPLUS - type == "Windows_FAT_32" -> FilesystemType.FAT32 - type.startsWith("Apple_") || type == "DDM" -> FilesystemType.APT_DATA - else -> FilesystemType.UNKNOWN - } - - pt.add(pb.build()) - } - - return Triple(ptType, pt, imgSize) -} +private val partitionListRegex = Regex("\\s*partition (\\d+): begin=(\\d+), size=(\\d+), decoded=(\\d+), firstsector=(\\d+), sectorcount=(\\d+), blocksruncount=(\\d+)\\s*") class DMGImage(private val uri: Uri, private val context: Context) : Image { - private val dmg2img: File = context.getBinary("dmg2img") private val libDir: String = context.applicationInfo.nativeLibraryDir private var loaded: Boolean = false private var partTable: List? = null @@ -83,7 +26,7 @@ class DMGImage(private val uri: Uri, private val context: Context) : Image { private fun readInfo() { if (loaded) return - val triple = readPartitionTable(dmg2img, libDir, File(uri.path)) + val triple = readPartitionTable(context, libDir, File(uri.path)) loaded = true partTableType = triple.first partTable = triple.second @@ -109,4 +52,102 @@ class DMGImage(private val uri: Uri, private val context: Context) : Image { return imgSize } + override val inputStream: AbstractSizedInputStream + get() = getRawImageInputStream(context, uri) + + companion object { + private fun getDmg2ImgProcessBuilder(context: Context, vararg args: String): ProcessBuilder = + ProcessBuilder(context.getBinary("dmg2img").path, *args) + .apply { + environment()["LD_LIBRARY_PATH"] = context.applicationInfo.nativeLibraryDir + } + + private fun readPartitionTable(context: Context, libDir: String, file: File): Triple?, Long?> { + val pt = ArrayList() + var ptType: PartitionTableType? = null + var imgSize = 0L + + val pb = getDmg2ImgProcessBuilder(context, "-v", "-l", file.path) + pb.redirectErrorStream(true) + + val p = pb.start() + val out = p.inputStream.readString() + System.err.println(out) + p.waitFor() + val matches = partRegex.findAll(out) + + matchloop@ for (m in matches) { + val ( + number, begin, size, + decoded, firstsector, + sectorcount, blocksruncount, + label, type + ) = m.destructured + + val pb = PartitionBuilder() + + pb.number = number.toInt() + pb.size = SECTOR_SIZE * sectorcount.toLong() + imgSize += pb.size!! + + if (label.isNotEmpty()) + pb.fsLabel = label + + pb.partLabel = type + + when (type) { + "Apple_partition_map" -> { + ptType = PartitionTableType.MAC + } + "MBR" -> { + ptType = PartitionTableType.MSDOS + continue@matchloop + } + } + + pb.fsType = when { + type == "Apple_Empty" || type == "Apple_Scratch" || type == "Apple_Free" -> FilesystemType.FREE + type == "Apple_HFS" -> FilesystemType.HFSPLUS + type == "Apple_HFSX" -> FilesystemType.HFSPLUS + type == "Windows_FAT_32" -> FilesystemType.FAT32 + type.startsWith("Apple_") || type == "DDM" -> FilesystemType.APT_DATA + else -> FilesystemType.UNKNOWN + } + + pt.add(pb.build()) + } + + return Triple(ptType, pt, imgSize) + } + + fun getRawImageInputStream( + context: Context, + uri: Uri, + sectorSize: Int = 512 + ): AbstractSizedInputStream { + val pb = getDmg2ImgProcessBuilder(context, "-v", uri.path!!, "-") + val process = pb.start() + val errReader = process.errorStream.bufferedReader() + + // Read blocksruncount + var matched = false + var lastSector = 0L + + while (true) { + val line = errReader.readLine() ?: break + val match = partitionListRegex.find(line) ?: if (matched) break else continue + matched = true + + val (begin, size, decoded, firstsector, sectorcount, blocksruncount) = match.destructured + + val partLastSector = firstsector.toLong() + sectorcount.toLong() + if (partLastSector > lastSector) + lastSector = partLastSector + } + + val bytesTotal = lastSector * sectorSize + return SizedInputStream(bytesTotal, process.inputStream) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/Image.kt b/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/Image.kt index 93722f9..0237d4a 100644 --- a/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/Image.kt +++ b/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/Image.kt @@ -1,10 +1,13 @@ package eu.depau.etchdroid.utils.imagetypes -import eu.depau.etchdroid.utils.enums.PartitionTableType +import android.content.Context import eu.depau.etchdroid.utils.Partition +import eu.depau.etchdroid.utils.enums.PartitionTableType +import eu.depau.etchdroid.utils.streams.AbstractSizedInputStream interface Image { val partitionTable: List? val tableType: PartitionTableType? val size: Long? + val inputStream: AbstractSizedInputStream } \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/RawImage.kt b/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/RawImage.kt new file mode 100644 index 0000000..4ee0334 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/imagetypes/RawImage.kt @@ -0,0 +1,16 @@ +package eu.depau.etchdroid.utils.imagetypes + +import eu.depau.etchdroid.utils.Partition +import eu.depau.etchdroid.utils.enums.PartitionTableType +import eu.depau.etchdroid.utils.streams.AbstractSizedInputStream + +class RawImage: Image { + override val partitionTable: List? + get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + override val tableType: PartitionTableType? + get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + override val size: Long? + get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + override val inputStream: AbstractSizedInputStream + get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/streams/AbstractSizedInputStream.kt b/app/src/main/java/eu/depau/etchdroid/utils/streams/AbstractSizedInputStream.kt new file mode 100644 index 0000000..4ff1f25 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/streams/AbstractSizedInputStream.kt @@ -0,0 +1,7 @@ +package eu.depau.etchdroid.utils.streams + +import java.io.InputStream + +abstract class AbstractSizedInputStream: InputStream() { + abstract val size: Long +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/streams/AbstractSizedOutputStream.kt b/app/src/main/java/eu/depau/etchdroid/utils/streams/AbstractSizedOutputStream.kt new file mode 100644 index 0000000..2b33e74 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/streams/AbstractSizedOutputStream.kt @@ -0,0 +1,7 @@ +package eu.depau.etchdroid.utils.streams + +import java.io.OutputStream + +abstract class AbstractSizedOutputStream: OutputStream() { + abstract val size: Long +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/streams/SizedInputStream.kt b/app/src/main/java/eu/depau/etchdroid/utils/streams/SizedInputStream.kt new file mode 100644 index 0000000..f524d82 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/streams/SizedInputStream.kt @@ -0,0 +1,48 @@ +package eu.depau.etchdroid.utils.streams + +import java.io.InputStream + +/** + * This class simply wraps an existing InputStream adding some provided size field. + */ +class SizedInputStream( + override val size: Long, + private val inputStream: InputStream +) : AbstractSizedInputStream() { + + override fun skip(n: Long): Long { + return inputStream.skip(n) + } + + override fun available(): Int { + return inputStream.available() + } + + override fun reset() { + inputStream.reset() + } + + override fun close() { + inputStream.close() + } + + override fun mark(readlimit: Int) { + inputStream.mark(readlimit) + } + + override fun markSupported(): Boolean { + return inputStream.markSupported() + } + + override fun read(): Int { + return inputStream.read() + } + + override fun read(b: ByteArray?): Int { + return inputStream.read(b) + } + + override fun read(b: ByteArray?, off: Int, len: Int): Int { + return inputStream.read(b, off, len) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/utils/streams/SizedOutputStream.kt b/app/src/main/java/eu/depau/etchdroid/utils/streams/SizedOutputStream.kt new file mode 100644 index 0000000..9e73ce7 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/utils/streams/SizedOutputStream.kt @@ -0,0 +1,31 @@ +package eu.depau.etchdroid.utils.streams + +import java.io.OutputStream + +/** + * This class simply wraps an existing OutputStream adding some provided size field. + */ +class SizedOutputStream( + override val size: Long, + private val outputStream: OutputStream +) : AbstractSizedOutputStream() { + override fun write(b: Int) { + outputStream.write(b) + } + + override fun write(b: ByteArray?) { + outputStream.write(b) + } + + override fun write(b: ByteArray?, off: Int, len: Int) { + outputStream.write(b, off, len) + } + + override fun flush() { + outputStream.flush() + } + + override fun close() { + outputStream.close() + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/AbstractAsyncWorker.kt b/app/src/main/java/eu/depau/etchdroid/worker/AbstractAsyncWorker.kt new file mode 100644 index 0000000..f53c935 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/AbstractAsyncWorker.kt @@ -0,0 +1,8 @@ +package eu.depau.etchdroid.worker + +abstract class AbstractAsyncWorker : IAsyncWorker, AbstractProgressSender() { + override suspend fun run() { + while (runStep()) { + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/AbstractAutoProgressAsyncWorker.kt b/app/src/main/java/eu/depau/etchdroid/worker/AbstractAutoProgressAsyncWorker.kt new file mode 100644 index 0000000..fd1b2a8 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/AbstractAutoProgressAsyncWorker.kt @@ -0,0 +1,41 @@ +package eu.depau.etchdroid.worker + +import eu.depau.etchdroid.worker.dto.ProgressUpdateDTO + +const val UPDATE_INTERVAL = 500 + +abstract class AbstractAutoProgressAsyncWorker(private val totalToDo: Long) : AbstractAsyncWorker() { + abstract val progressUpdateDTO: ProgressUpdateDTO + + private var doneAccumulator = 0L + private var doneSinceLastUpdate = 0L + private var lastUpdateTime = 0L + + fun progressUpdate(lastDone: Long) { + val currentTime = System.currentTimeMillis() + + if (lastUpdateTime == 0L) + lastUpdateTime = currentTime + + doneSinceLastUpdate += lastDone + doneAccumulator += lastDone + + if (currentTime > lastUpdateTime + UPDATE_INTERVAL) { + val progress = doneAccumulator.toDouble() / totalToDo + val speedUnitPerMillis = doneSinceLastUpdate.toDouble() / (currentTime - lastUpdateTime) + val timeRemainingMillis: Long = ((totalToDo - doneAccumulator) / speedUnitPerMillis).toLong() + + val dto = progressUpdateDTO.copy( + operationProgress = progress, + stepProgress = progress, + timeRemaining = timeRemainingMillis, + currentRate = speedUnitPerMillis * 1000 + ) + + notifyProgress(dto) + + doneSinceLastUpdate = 0 + lastUpdateTime = currentTime + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/AbstractProgressSender.kt b/app/src/main/java/eu/depau/etchdroid/worker/AbstractProgressSender.kt new file mode 100644 index 0000000..0174ff8 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/AbstractProgressSender.kt @@ -0,0 +1,24 @@ +package eu.depau.etchdroid.worker + +import eu.depau.etchdroid.worker.dto.ProgressDoneDTO +import eu.depau.etchdroid.worker.dto.ProgressStartDTO +import eu.depau.etchdroid.worker.dto.ProgressUpdateDTO + +abstract class AbstractProgressSender : IProgressSender { + private val listeners = ArrayList() + + override fun attachProgressListener(listener: IProgressListener) = + listeners.add(listener) + + override fun detachProgressListener(listener: IProgressListener) = + listeners.remove(listener) + + protected fun notifyStart(dto: ProgressStartDTO) = + listeners.forEach { it.notifyStart(dto) } + + protected fun notifyProgress(dto: ProgressUpdateDTO) = + listeners.forEach { it.notifyProgress(dto) } + + protected fun notifyDone(dto: ProgressDoneDTO) = + listeners.forEach { it.notifyDone(dto) } +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/IAsyncWorker.kt b/app/src/main/java/eu/depau/etchdroid/worker/IAsyncWorker.kt new file mode 100644 index 0000000..33a89ea --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/IAsyncWorker.kt @@ -0,0 +1,6 @@ +package eu.depau.etchdroid.worker + +interface IAsyncWorker: IProgressSender { + suspend fun run() + suspend fun runStep(): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/IProgressListener.kt b/app/src/main/java/eu/depau/etchdroid/worker/IProgressListener.kt new file mode 100644 index 0000000..2682cef --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/IProgressListener.kt @@ -0,0 +1,11 @@ +package eu.depau.etchdroid.worker + +import eu.depau.etchdroid.worker.dto.ProgressDoneDTO +import eu.depau.etchdroid.worker.dto.ProgressStartDTO +import eu.depau.etchdroid.worker.dto.ProgressUpdateDTO + +interface IProgressListener { + fun notifyStart(dto: ProgressStartDTO) + fun notifyProgress(dto: ProgressUpdateDTO) + fun notifyDone(dto: ProgressDoneDTO) +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/IProgressSender.kt b/app/src/main/java/eu/depau/etchdroid/worker/IProgressSender.kt new file mode 100644 index 0000000..2a9bf4b --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/IProgressSender.kt @@ -0,0 +1,6 @@ +package eu.depau.etchdroid.worker + +interface IProgressSender { + fun attachProgressListener(listener: IProgressListener): Boolean + fun detachProgressListener(listener: IProgressListener): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/dto/ProgressDoneDTO.kt b/app/src/main/java/eu/depau/etchdroid/worker/dto/ProgressDoneDTO.kt new file mode 100644 index 0000000..58df979 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/dto/ProgressDoneDTO.kt @@ -0,0 +1,10 @@ +package eu.depau.etchdroid.worker.dto + +import android.os.Parcelable +import eu.depau.etchdroid.worker.enums.ErrorType + +data class ProgressDoneDTO( + val operationId: Int, + val error: ErrorType?, + val errorData: Parcelable? +) \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/dto/ProgressStartDTO.kt b/app/src/main/java/eu/depau/etchdroid/worker/dto/ProgressStartDTO.kt new file mode 100644 index 0000000..2b39041 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/dto/ProgressStartDTO.kt @@ -0,0 +1,13 @@ +package eu.depau.etchdroid.worker.dto + +import eu.depau.etchdroid.worker.enums.OperationType +import eu.depau.etchdroid.worker.enums.StepType +import java.util.* + +data class ProgressStartDTO( + val operationId: Int, + val operationType: OperationType, + val inputName: String, + val outputName: String, + val steps: List> +) \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/dto/ProgressUpdateDTO.kt b/app/src/main/java/eu/depau/etchdroid/worker/dto/ProgressUpdateDTO.kt new file mode 100644 index 0000000..db7a9ac --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/dto/ProgressUpdateDTO.kt @@ -0,0 +1,13 @@ +package eu.depau.etchdroid.worker.dto + +import eu.depau.etchdroid.worker.enums.RateUnit + +data class ProgressUpdateDTO( + val operationId: Int, + val currentStep: Int, + val operationProgress: Double, + val stepProgress: Double?, + val timeRemaining: Long?, + val currentRate: Double?, + val rateUnit: RateUnit? +) \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/enums/ErrorType.kt b/app/src/main/java/eu/depau/etchdroid/worker/enums/ErrorType.kt new file mode 100644 index 0000000..06604b1 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/enums/ErrorType.kt @@ -0,0 +1,11 @@ +package eu.depau.etchdroid.worker.enums + +enum class ErrorType { + UNPLUGGED, + INPUT_FORMAT_ERROR, + ENOSPC_ON_USB, + ENOSPC_ON_DEVICE, + PERMISSION_DENIED_ANDROID, + PERMISSION_DENIED_FS, + PERMISSION_DENIED_USB +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/enums/OperationType.kt b/app/src/main/java/eu/depau/etchdroid/worker/enums/OperationType.kt new file mode 100644 index 0000000..4808c6e --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/enums/OperationType.kt @@ -0,0 +1,9 @@ +package eu.depau.etchdroid.worker.enums + +enum class OperationType { + FLASH_RAW_IMAGE, + FLASH_DMG, + FLASH_ELTORITO, + DUMP_RAW_IMAGE, + CREATE_WINDOWS_INSTALLER +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/enums/RateUnit.kt b/app/src/main/java/eu/depau/etchdroid/worker/enums/RateUnit.kt new file mode 100644 index 0000000..54f9ce3 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/enums/RateUnit.kt @@ -0,0 +1,6 @@ +package eu.depau.etchdroid.worker.enums + +enum class RateUnit { + BYTES_PER_SECOND, + FURLONGS_PER_FORTNIGHT +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/enums/StepType.kt b/app/src/main/java/eu/depau/etchdroid/worker/enums/StepType.kt new file mode 100644 index 0000000..8a4af00 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/enums/StepType.kt @@ -0,0 +1,8 @@ +package eu.depau.etchdroid.worker.enums + +enum class StepType { + STREAM_COPY, + FORMAT_DRIVE, + COPY_FILES, + INSTALL_BOOTLOADER +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/impl/Dmg2OutputStreamConvertAsyncService.kt b/app/src/main/java/eu/depau/etchdroid/worker/impl/Dmg2OutputStreamConvertAsyncService.kt new file mode 100644 index 0000000..8387b8b --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/impl/Dmg2OutputStreamConvertAsyncService.kt @@ -0,0 +1,6 @@ +package eu.depau.etchdroid.worker.impl + +import eu.depau.etchdroid.worker.AbstractAutoProgressAsyncWorker + +class Dmg2OutputStreamConvertAsyncService: AbstractAutoProgressAsyncWorker() { +} \ No newline at end of file diff --git a/app/src/main/java/eu/depau/etchdroid/worker/impl/Input2OutputStreamCopyAsyncWorker.kt b/app/src/main/java/eu/depau/etchdroid/worker/impl/Input2OutputStreamCopyAsyncWorker.kt new file mode 100644 index 0000000..2eee691 --- /dev/null +++ b/app/src/main/java/eu/depau/etchdroid/worker/impl/Input2OutputStreamCopyAsyncWorker.kt @@ -0,0 +1,30 @@ +package eu.depau.etchdroid.worker.impl + +import eu.depau.etchdroid.worker.AbstractAutoProgressAsyncWorker +import eu.depau.etchdroid.worker.dto.ProgressUpdateDTO +import java.io.InputStream +import java.io.OutputStream + + +open class Input2OutputStreamCopyAsyncWorker( + private val source: InputStream, + private val dest: OutputStream, + chunkSize: Int, + override val progressUpdateDTO: ProgressUpdateDTO, + size: Long +) : AbstractAutoProgressAsyncWorker(size) { + + private val buffer = ByteArray(chunkSize) + + override suspend fun runStep(): Boolean { + val readBytes = source.read(buffer) + + if (readBytes < 0) + return false + + dest.write(buffer, 0, readBytes) + progressUpdate(readBytes.toLong()) + + return true + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index a1c567b..62c71dc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = '1.0.0' ext.kotlin_version = '1.3.11' repositories { jcenter()