Backup of work in progress so I can pick it up later
This commit is contained in:
parent
e987268ad6
commit
f130e3a1d4
24 changed files with 439 additions and 64 deletions
19
app/src/main/java/eu/depau/etchdroid/ui/misc/SparseFile.kt
Normal file
19
app/src/main/java/eu/depau/etchdroid/ui/misc/SparseFile.kt
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,78 +2,21 @@ package eu.depau.etchdroid.utils.imagetypes
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
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.FilesystemType
|
||||||
import eu.depau.etchdroid.utils.enums.PartitionTableType
|
import eu.depau.etchdroid.utils.enums.PartitionTableType
|
||||||
import eu.depau.etchdroid.utils.ktexts.getBinary
|
import eu.depau.etchdroid.utils.ktexts.getBinary
|
||||||
import eu.depau.etchdroid.utils.ktexts.readString
|
import eu.depau.etchdroid.utils.ktexts.readString
|
||||||
import eu.depau.etchdroid.utils.Partition
|
import eu.depau.etchdroid.utils.streams.AbstractSizedInputStream
|
||||||
import eu.depau.etchdroid.utils.PartitionBuilder
|
import eu.depau.etchdroid.utils.streams.SizedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
val SECTOR_SIZE = 512
|
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 val partRegex = Regex("partition (\\d+): begin=(\\d+), size=(\\d+), decoded=(\\d+), firstsector=(\\d+), sectorcount=(\\d+), blocksruncount=(\\d+)\\s+(.*) \\((.+) : \\d+\\)", RegexOption.MULTILINE)
|
||||||
|
private val partitionListRegex = Regex("\\s*partition (\\d+): begin=(\\d+), size=(\\d+), decoded=(\\d+), firstsector=(\\d+), sectorcount=(\\d+), blocksruncount=(\\d+)\\s*")
|
||||||
private fun readPartitionTable(dmg2img: File, libDir: String, file: File): Triple<PartitionTableType?, List<Partition>?, Long?> {
|
|
||||||
val pt = ArrayList<Partition>()
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
class DMGImage(private val uri: Uri, private val context: Context) : Image {
|
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 val libDir: String = context.applicationInfo.nativeLibraryDir
|
||||||
private var loaded: Boolean = false
|
private var loaded: Boolean = false
|
||||||
private var partTable: List<Partition>? = null
|
private var partTable: List<Partition>? = null
|
||||||
|
@ -83,7 +26,7 @@ class DMGImage(private val uri: Uri, private val context: Context) : Image {
|
||||||
private fun readInfo() {
|
private fun readInfo() {
|
||||||
if (loaded)
|
if (loaded)
|
||||||
return
|
return
|
||||||
val triple = readPartitionTable(dmg2img, libDir, File(uri.path))
|
val triple = readPartitionTable(context, libDir, File(uri.path))
|
||||||
loaded = true
|
loaded = true
|
||||||
partTableType = triple.first
|
partTableType = triple.first
|
||||||
partTable = triple.second
|
partTable = triple.second
|
||||||
|
@ -109,4 +52,102 @@ class DMGImage(private val uri: Uri, private val context: Context) : Image {
|
||||||
return imgSize
|
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<PartitionTableType?, List<Partition>?, Long?> {
|
||||||
|
val pt = ArrayList<Partition>()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
package eu.depau.etchdroid.utils.imagetypes
|
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.Partition
|
||||||
|
import eu.depau.etchdroid.utils.enums.PartitionTableType
|
||||||
|
import eu.depau.etchdroid.utils.streams.AbstractSizedInputStream
|
||||||
|
|
||||||
interface Image {
|
interface Image {
|
||||||
val partitionTable: List<Partition>?
|
val partitionTable: List<Partition>?
|
||||||
val tableType: PartitionTableType?
|
val tableType: PartitionTableType?
|
||||||
val size: Long?
|
val size: Long?
|
||||||
|
val inputStream: AbstractSizedInputStream
|
||||||
}
|
}
|
|
@ -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<Partition>?
|
||||||
|
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.
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package eu.depau.etchdroid.utils.streams
|
||||||
|
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
abstract class AbstractSizedInputStream: InputStream() {
|
||||||
|
abstract val size: Long
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package eu.depau.etchdroid.utils.streams
|
||||||
|
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
abstract class AbstractSizedOutputStream: OutputStream() {
|
||||||
|
abstract val size: Long
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package eu.depau.etchdroid.worker
|
||||||
|
|
||||||
|
abstract class AbstractAsyncWorker : IAsyncWorker, AbstractProgressSender() {
|
||||||
|
override suspend fun run() {
|
||||||
|
while (runStep()) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<IProgressListener>()
|
||||||
|
|
||||||
|
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) }
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package eu.depau.etchdroid.worker
|
||||||
|
|
||||||
|
interface IAsyncWorker: IProgressSender {
|
||||||
|
suspend fun run()
|
||||||
|
suspend fun runStep(): Boolean
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package eu.depau.etchdroid.worker
|
||||||
|
|
||||||
|
interface IProgressSender {
|
||||||
|
fun attachProgressListener(listener: IProgressListener): Boolean
|
||||||
|
fun detachProgressListener(listener: IProgressListener): Boolean
|
||||||
|
}
|
|
@ -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?
|
||||||
|
)
|
|
@ -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<Pair<String, StepType>>
|
||||||
|
)
|
|
@ -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?
|
||||||
|
)
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package eu.depau.etchdroid.worker.enums
|
||||||
|
|
||||||
|
enum class RateUnit {
|
||||||
|
BYTES_PER_SECOND,
|
||||||
|
FURLONGS_PER_FORTNIGHT
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package eu.depau.etchdroid.worker.enums
|
||||||
|
|
||||||
|
enum class StepType {
|
||||||
|
STREAM_COPY,
|
||||||
|
FORMAT_DRIVE,
|
||||||
|
COPY_FILES,
|
||||||
|
INSTALL_BOOTLOADER
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package eu.depau.etchdroid.worker.impl
|
||||||
|
|
||||||
|
import eu.depau.etchdroid.worker.AbstractAutoProgressAsyncWorker
|
||||||
|
|
||||||
|
class Dmg2OutputStreamConvertAsyncService: AbstractAutoProgressAsyncWorker() {
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
|
ext.kotlin_version = '1.0.0'
|
||||||
ext.kotlin_version = '1.3.11'
|
ext.kotlin_version = '1.3.11'
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
|
|
Loading…
Reference in a new issue