Remove files lost during previous refactoring
This commit is contained in:
parent
9c68672310
commit
6755b3ca7f
16 changed files with 1 additions and 509 deletions
|
@ -1,3 +0,0 @@
|
|||
package eu.depau.etchdroid.exceptions
|
||||
|
||||
class CannotGetFilePathException(cause: Exception) : Exception(cause)
|
|
@ -1,8 +0,0 @@
|
|||
package eu.depau.etchdroid.exceptions
|
||||
|
||||
import eu.depau.etchdroid.kotlin_exts.toHRSize
|
||||
import java.io.IOException
|
||||
|
||||
class UsbWriteException(offset: Long, writtenBytes: Long, exc: Exception) : IOException(
|
||||
"Write failed at block $offset, ${writtenBytes.toHRSize()} written. Error: $exc", exc
|
||||
)
|
|
@ -1,112 +0,0 @@
|
|||
package eu.depau.etchdroid.img_types
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import eu.depau.etchdroid.enums.FilesystemType
|
||||
import eu.depau.etchdroid.enums.PartitionTableType
|
||||
import eu.depau.etchdroid.kotlin_exts.getBinary
|
||||
import eu.depau.etchdroid.kotlin_exts.readString
|
||||
import eu.depau.etchdroid.utils.Partition
|
||||
import eu.depau.etchdroid.utils.PartitionBuilder
|
||||
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<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 {
|
||||
private val dmg2img: File = context.getBinary("dmg2img")
|
||||
private val libDir: String = context.applicationInfo.nativeLibraryDir
|
||||
private var loaded: Boolean = false
|
||||
private var partTable: List<Partition>? = null
|
||||
private var partTableType: PartitionTableType? = null
|
||||
private var imgSize: Long? = null
|
||||
|
||||
private fun readInfo() {
|
||||
if (loaded)
|
||||
return
|
||||
val triple = readPartitionTable(dmg2img, libDir, File(uri.path))
|
||||
loaded = true
|
||||
partTableType = triple.first
|
||||
partTable = triple.second
|
||||
imgSize = triple.third
|
||||
}
|
||||
|
||||
override val partitionTable: List<Partition>?
|
||||
get() {
|
||||
readInfo()
|
||||
return partTable
|
||||
}
|
||||
|
||||
|
||||
override val tableType: PartitionTableType?
|
||||
get() {
|
||||
readInfo()
|
||||
return partTableType
|
||||
}
|
||||
|
||||
override val size: Long?
|
||||
get() {
|
||||
readInfo()
|
||||
return imgSize
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package eu.depau.etchdroid.img_types
|
||||
|
||||
import eu.depau.etchdroid.enums.PartitionTableType
|
||||
import eu.depau.etchdroid.utils.Partition
|
||||
|
||||
interface Image {
|
||||
val partitionTable: List<Partition>?
|
||||
val tableType: PartitionTableType?
|
||||
val size: Long?
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package eu.depau.etchdroid.kotlin_exts
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
|
||||
private val copiedBinaries: MutableMap<String, File> = HashMap()
|
||||
|
||||
|
||||
fun Context.getBinary(name: String): File {
|
||||
if (name in copiedBinaries.keys)
|
||||
return copiedBinaries[name]!!
|
||||
|
||||
val abi = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
Build.SUPPORTED_ABIS[0]
|
||||
else
|
||||
Build.CPU_ABI
|
||||
|
||||
val arch = when {
|
||||
abi.contains("armeabi-v7a") -> "armeabi-v7a"
|
||||
abi.contains("x86_64") -> "x86_64"
|
||||
abi.contains("x86") -> "x86"
|
||||
abi.contains("arm64-v8a") -> "arm64-v8a"
|
||||
else -> null!!
|
||||
}
|
||||
|
||||
val assetManager = assets
|
||||
val assetIn = assetManager.open("bin/$arch/$name")
|
||||
|
||||
val bin = File("$filesDir/bin/$arch/$name")
|
||||
bin.parentFile?.mkdirs()
|
||||
if (!bin.exists())
|
||||
bin.createNewFile()
|
||||
val binOut = FileOutputStream(bin)
|
||||
|
||||
// Copy executable
|
||||
var size: Long = 0
|
||||
val buff = ByteArray(1024)
|
||||
var nRead = assetIn.read(buff)
|
||||
|
||||
while (nRead != -1) {
|
||||
binOut.write(buff, 0, nRead)
|
||||
size += nRead.toLong()
|
||||
nRead = assetIn.read(buff)
|
||||
}
|
||||
assetIn.close()
|
||||
binOut.close()
|
||||
|
||||
bin.setExecutable(true)
|
||||
|
||||
copiedBinaries[name] = bin
|
||||
|
||||
return bin
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package eu.depau.etchdroid.kotlin_exts
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
|
||||
fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_LONG) {
|
||||
Toast.makeText(this, message, duration).show()
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package eu.depau.etchdroid.kotlin_exts
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun InputStream.readString(): String {
|
||||
val baos = ByteArrayOutputStream()
|
||||
val buffer = ByteArray(1024)
|
||||
var length = this.read(buffer)
|
||||
|
||||
while (length != -1) {
|
||||
baos.write(buffer, 0, length)
|
||||
length = this.read(buffer)
|
||||
}
|
||||
return baos.toString("UTF-8")
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package eu.depau.etchdroid.kotlin_exts
|
||||
|
||||
// https://stackoverflow.com/a/3758880/1124621
|
||||
|
||||
private fun <T> humanReadableByteCount(bytes: T, si: Boolean = true): String where T : Comparable<T>, T : Number {
|
||||
val unit: Long = if (si) 1000 else 1024
|
||||
if (bytes.toLong() < unit) return String.format("%.1f B", bytes.toDouble())
|
||||
val exp = (Math.log(bytes.toDouble()) / Math.log(unit.toDouble())).toInt()
|
||||
val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i"
|
||||
return String.format("%.1f %sB", bytes.toDouble() / Math.pow(unit.toDouble(), exp.toDouble()), pre)
|
||||
}
|
||||
|
||||
fun Long.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si)
|
||||
fun Float.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si)
|
||||
fun Double.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si)
|
||||
fun Int.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si)
|
||||
fun Byte.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si)
|
||||
fun Short.toHRSize(si: Boolean = true) = humanReadableByteCount(this, si)
|
|
@ -1,31 +0,0 @@
|
|||
package eu.depau.etchdroid.kotlin_exts
|
||||
|
||||
private val timeStrings = arrayOf("s", "m", "h", "d")
|
||||
private val timeDivs = arrayOf(60, 60, 24)
|
||||
|
||||
fun <T> humanReadableTimeDelta(time: T): String where T : Number {
|
||||
var dbTime = time.toDouble() / 1000.0
|
||||
var outString = ""
|
||||
|
||||
for (i in 0..(timeDivs.size - 1)) {
|
||||
val div = timeDivs[i]
|
||||
val str = timeStrings[i]
|
||||
|
||||
outString = "${(dbTime % div).toInt()}$str$outString"
|
||||
|
||||
if (dbTime < div)
|
||||
return outString
|
||||
|
||||
outString = " $outString"
|
||||
dbTime /= div
|
||||
}
|
||||
|
||||
return "${dbTime.toInt()}${timeStrings[-1]} $outString"
|
||||
}
|
||||
|
||||
fun Long.toHRTime() = humanReadableTimeDelta(this)
|
||||
fun Float.toHRTime() = humanReadableTimeDelta(this)
|
||||
fun Double.toHRTime() = humanReadableTimeDelta(this)
|
||||
fun Int.toHRTime() = humanReadableTimeDelta(this)
|
||||
fun Byte.toHRTime() = humanReadableTimeDelta(this)
|
||||
fun Short.toHRTime() = humanReadableTimeDelta(this)
|
|
@ -1,29 +0,0 @@
|
|||
package eu.depau.etchdroid.kotlin_exts
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
|
||||
|
||||
fun Uri.getFileName(context: Context): String? {
|
||||
val TAG = "UriGetFileNameExt"
|
||||
var result: String? = null
|
||||
|
||||
if (this.scheme == "content") {
|
||||
val cursor = context.getContentResolver().query(this, null, null, null, null)
|
||||
cursor.use {
|
||||
if (it != null && it.moveToFirst()) {
|
||||
result = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = this.getPath()
|
||||
val cut = result!!.lastIndexOf('/')
|
||||
if (cut != -1) {
|
||||
result = result!!.substring(cut + 1)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package eu.depau.etchdroid.kotlin_exts
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import android.webkit.MimeTypeMap
|
||||
import java.io.File
|
||||
|
||||
fun Uri.getExtension(contentResolver: ContentResolver): String {
|
||||
return when (scheme) {
|
||||
ContentResolver.SCHEME_CONTENT -> {
|
||||
val mime = MimeTypeMap.getSingleton()
|
||||
mime.getExtensionFromMimeType(contentResolver.getType(this))!!
|
||||
}
|
||||
else -> MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(File(path)).toString())
|
||||
}
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
package eu.depau.etchdroid.kotlin_exts
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
import eu.depau.etchdroid.exceptions.CannotGetFilePathException
|
||||
|
||||
|
||||
/**
|
||||
* Get a file path from a Uri. This will get the the path for Storage Access
|
||||
* Framework Documents, as well as the _data field for the MediaStore and
|
||||
* other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @author paulburke
|
||||
*
|
||||
* https://stackoverflow.com/a/27271131/1124621
|
||||
*/
|
||||
fun Uri.getFilePath(context: Context): String? {
|
||||
val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
|
||||
|
||||
try {
|
||||
if (isKitKat && DocumentsContract.isDocumentUri(context, this)) {
|
||||
// DocumentProvider
|
||||
|
||||
if (isExternalStorageDocument) {
|
||||
// ExternalStorageProvider
|
||||
|
||||
val docId = DocumentsContract.getDocumentId(this)
|
||||
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val type = split[0]
|
||||
|
||||
if (type.equals("primary", ignoreCase = true)) {
|
||||
return Environment.getExternalStorageDirectory().path + "/" + split[1]
|
||||
}
|
||||
|
||||
// TODO handle non-primary volumes
|
||||
|
||||
} else if (isDownloadsDocument) {
|
||||
// DownloadsProvider
|
||||
|
||||
val id = DocumentsContract.getDocumentId(this)
|
||||
|
||||
if (id.startsWith("raw:/"))
|
||||
return Uri.parse(id).path
|
||||
|
||||
val contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
|
||||
|
||||
|
||||
return contentUri.getDataColumn(context, null, null)
|
||||
|
||||
} else if (isMediaDocument) {
|
||||
// MediaProvider
|
||||
|
||||
val docId = DocumentsContract.getDocumentId(this)
|
||||
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val type = split[0]
|
||||
|
||||
val contentUri = when (type) {
|
||||
"image" -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||
"video" -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
||||
"audio" -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
||||
else -> null
|
||||
}
|
||||
|
||||
val selection = "_id=?"
|
||||
val selectionArgs = arrayOf(split[1])
|
||||
|
||||
return contentUri?.getDataColumn(context, selection, selectionArgs)
|
||||
}
|
||||
|
||||
} else if ("content".equals(scheme, ignoreCase = true)) {
|
||||
// MediaStore (and general)
|
||||
|
||||
return getDataColumn(context, null, null)
|
||||
|
||||
} else if ("file".equals(scheme, ignoreCase = true)) {
|
||||
// File
|
||||
|
||||
return path
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Wrap into own exception to make debugging easier
|
||||
throw CannotGetFilePathException(e)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the data column for this Uri. This is useful for
|
||||
* MediaStore Uris, and other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param selection (Optional) Filter used in the query.
|
||||
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||
* @return The value of the _data column, which is typically a file path.
|
||||
*/
|
||||
fun Uri.getDataColumn(context: Context, selection: String?, selectionArgs: Array<String>?): String? {
|
||||
val column = "_data"
|
||||
val projection = arrayOf(column)
|
||||
|
||||
context.contentResolver.query(this, projection, selection, selectionArgs, null)?.use {
|
||||
if (it.moveToFirst()) {
|
||||
val columnIndex = it.getColumnIndexOrThrow(column)
|
||||
return it.getString(columnIndex)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether the Uri authority is ExternalStorageProvider.
|
||||
*/
|
||||
val Uri.isExternalStorageDocument: Boolean
|
||||
get() = "com.android.externalstorage.documents" == authority
|
||||
|
||||
/**
|
||||
* Whether the Uri authority is DownloadsProvider.
|
||||
*/
|
||||
val Uri.isDownloadsDocument: Boolean
|
||||
get() = "com.android.providers.downloads.documents" == authority
|
||||
|
||||
/**
|
||||
* Whether the Uri authority is MediaProvider.
|
||||
*/
|
||||
val Uri.isMediaDocument: Boolean
|
||||
get() = "com.android.providers.media.documents" == authority
|
|
@ -1,39 +0,0 @@
|
|||
package eu.depau.etchdroid.kotlin_exts
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.database.SQLException
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import java.io.File
|
||||
|
||||
// https://github.com/android-rcs/rcsjta/blob/master/RI/src/com/gsma/rcs/ri/utils/FileUtils.java#L214
|
||||
|
||||
fun Uri.getFileSize(context: Context): Long {
|
||||
when (this.scheme) {
|
||||
ContentResolver.SCHEME_FILE -> {
|
||||
val f = File(this.path)
|
||||
return f.length()
|
||||
}
|
||||
|
||||
ContentResolver.SCHEME_CONTENT -> {
|
||||
val cursor: Cursor? = context.contentResolver.query(this, null, null, null, null)
|
||||
|
||||
cursor.use {
|
||||
if (it == null) {
|
||||
throw SQLException("Failed to query file $this")
|
||||
}
|
||||
return if (it.moveToFirst()) {
|
||||
java.lang.Long.valueOf(it.getString(it
|
||||
.getColumnIndexOrThrow(OpenableColumns.SIZE)))
|
||||
} else {
|
||||
throw IllegalArgumentException(
|
||||
"Error in retrieving this size form the URI")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unsupported URI scheme")
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package eu.depau.etchdroid.kotlin_exts
|
||||
|
||||
import android.hardware.usb.UsbDevice
|
||||
import android.os.Build
|
||||
|
||||
fun formatID(id: Int): String = Integer.toHexString(id).padStart(4, '0')
|
||||
|
||||
val UsbDevice.vidpid: String
|
||||
get() = "${formatID(this.vendorId)}:${formatID(this.productId)}"
|
||||
|
||||
|
||||
val UsbDevice.name: String
|
||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
"${this.manufacturerName} ${this.productName}"
|
||||
} else {
|
||||
this.deviceName
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package eu.depau.etchdroid.kotlin_exts
|
||||
|
||||
import android.view.View
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
fun View.snackbar(message: CharSequence, duration: Int = Snackbar.LENGTH_LONG) {
|
||||
Snackbar.make(this, message, duration).show()
|
||||
}
|
|
@ -120,7 +120,7 @@ class UsbDrivePickerActivity : ActivityBase(), SwipeRefreshLayout.OnRefreshListe
|
|||
}
|
||||
|
||||
viewManager = LinearLayoutManager(this)
|
||||
recyclerView = usbdevs_recycler_view
|
||||
recyclerView = usbdevs_recycler_view as EmptyRecyclerView
|
||||
recyclerView.emptyView = usbdevs_recycler_empty_view
|
||||
|
||||
recyclerView.addOnItemTouchListener(RecyclerViewTouchListener(this, recyclerView, object : ClickListener {
|
||||
|
|
Loading…
Reference in a new issue