Structure refactor
This commit is contained in:
parent
b286782a79
commit
53c21a814e
51 changed files with 579 additions and 196 deletions
|
@ -21,7 +21,7 @@
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
tools:ignore="GoogleAppIndexingWarning">
|
tools:ignore="GoogleAppIndexingWarning">
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.StartActivity"
|
android:name="eu.depau.etchdroid.ui.activities.StartActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/MaterialAppTheme">
|
android:theme="@style/MaterialAppTheme">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -31,14 +31,14 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.UsbDrivePickerActivity"
|
android:name="eu.depau.etchdroid.ui.activities.UsbDrivePickerActivity"
|
||||||
android:label="@string/title_activity_usb_drive_picker"
|
android:label="@string/title_activity_usb_drive_picker"
|
||||||
android:parentActivityName=".activities.StartActivity"
|
android:parentActivityName="eu.depau.etchdroid.ui.activities.StartActivity"
|
||||||
android:theme="@style/MaterialAppTheme">
|
android:theme="@style/MaterialAppTheme">
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="eu.depau.etchdroid.activities.StartActivity"/>
|
android:value="eu.depau.etchdroid.ui.activities.StartActivity"/>
|
||||||
|
|
||||||
<!-- Open ISO files by mimetype -->
|
<!-- Open ISO files by mimetype -->
|
||||||
<intent-filter android:label="@string/app_name">
|
<intent-filter android:label="@string/app_name">
|
||||||
|
@ -126,17 +126,17 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.ConfirmationActivity"
|
android:name="eu.depau.etchdroid.ui.activities.ConfirmationActivity"
|
||||||
android:label="@string/title_activity_confirmation"
|
android:label="@string/title_activity_confirmation"
|
||||||
android:parentActivityName=".activities.UsbDrivePickerActivity"
|
android:parentActivityName="eu.depau.etchdroid.ui.activities.UsbDrivePickerActivity"
|
||||||
android:theme="@style/MaterialAppTheme">
|
android:theme="@style/MaterialAppTheme">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="eu.depau.etchdroid.activities.UsbDrivePickerActivity"/>
|
android:value="eu.depau.etchdroid.ui.activities.UsbDrivePickerActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.ErrorActivity"
|
android:name="eu.depau.etchdroid.ui.activities.ErrorActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:label="@string/write_failed"
|
android:label="@string/write_failed"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
|
|
|
@ -3,8 +3,8 @@ package eu.depau.etchdroid
|
||||||
import android.hardware.usb.UsbDevice
|
import android.hardware.usb.UsbDevice
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.github.mjdev.libaums.UsbMassStorageDevice
|
import com.github.mjdev.libaums.UsbMassStorageDevice
|
||||||
import eu.depau.etchdroid.enums.FlashMethod
|
import eu.depau.etchdroid.utils.enums.FlashMethod
|
||||||
import eu.depau.etchdroid.img_types.Image
|
import eu.depau.etchdroid.utils.imagetypes.Image
|
||||||
|
|
||||||
object StateKeeper {
|
object StateKeeper {
|
||||||
var flashMethod: FlashMethod? = null
|
var flashMethod: FlashMethod? = null
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
package eu.depau.etchdroid.activities
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import eu.depau.etchdroid.R
|
|
||||||
import eu.depau.etchdroid.adapters.LicenseRecyclerViewAdapter
|
|
||||||
import eu.depau.etchdroid.utils.ClickListener
|
|
||||||
import eu.depau.etchdroid.utils.License
|
|
||||||
import eu.depau.etchdroid.utils.RecyclerViewTouchListener
|
|
||||||
import kotlinx.android.synthetic.main.activity_licenses.*
|
|
||||||
|
|
||||||
|
|
||||||
class LicensesActivity : AppCompatActivity() {
|
|
||||||
private lateinit var recyclerView: RecyclerView
|
|
||||||
private lateinit var viewManager: RecyclerView.LayoutManager
|
|
||||||
private lateinit var viewAdapter: LicenseRecyclerViewAdapter
|
|
||||||
|
|
||||||
lateinit var licenses: Array<License>
|
|
||||||
|
|
||||||
private fun updateLicenses() {
|
|
||||||
if (!::licenses.isInitialized) {
|
|
||||||
licenses = arrayOf(
|
|
||||||
License(getString(R.string.this_app), Uri.parse("https://github.com/Depau/EtchDroid"), getString(R.string.license_gpl3)),
|
|
||||||
License("Storage Chooser 2.0", Uri.parse("https://github.com/codekidX/storage-chooser"), getString(R.string.license_mpl_2_0), getString(R.string.storagechooser_license_description)),
|
|
||||||
License("libaums (fork)", Uri.parse("https://github.com/Depau/libaums"), getString(R.string.license_apache2_0), getString(R.string.libaums_license_desc)),
|
|
||||||
License("dmg2img (fork)", Uri.parse("https://github.com/Depau/dmg2img-cmake"), getString(R.string.license_gpl2), getString(R.string.dmg2img_license_desc)),
|
|
||||||
License("bzip2", Uri.parse("https://github.com/LuaDist/bzip2/"), getString(R.string.license_bzip2)),
|
|
||||||
License("LibreSSL", Uri.parse("https://github.com/libressl-portable/portable"), getString(R.string.license_custom))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setContentView(R.layout.activity_licenses)
|
|
||||||
updateLicenses()
|
|
||||||
|
|
||||||
// Enable back button in action bar
|
|
||||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
viewManager = LinearLayoutManager(this)
|
|
||||||
recyclerView = licenses_recycler_view
|
|
||||||
viewAdapter = LicenseRecyclerViewAdapter(licenses)
|
|
||||||
recyclerView.adapter = viewAdapter
|
|
||||||
recyclerView.layoutManager = viewManager
|
|
||||||
|
|
||||||
recyclerView.addOnItemTouchListener(RecyclerViewTouchListener(this, recyclerView, object : ClickListener {
|
|
||||||
override fun onClick(view: View, position: Int) {
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW, viewAdapter.get(position).url)
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLongClick(view: View, position: Int) {}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
|
||||||
when (item?.itemId) {
|
|
||||||
android.R.id.home -> {
|
|
||||||
finish()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
package eu.depau.etchdroid.adapters
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import eu.depau.etchdroid.R
|
|
||||||
import eu.depau.etchdroid.utils.License
|
|
||||||
import kotlinx.android.synthetic.main.license_row.view.*
|
|
||||||
|
|
||||||
|
|
||||||
class LicenseRecyclerViewAdapter(private val dataset: Array<License>) : RecyclerView.Adapter<LicenseRecyclerViewAdapter.ViewHolder>() {
|
|
||||||
|
|
||||||
class ViewHolder(val relLayout: RelativeLayout) : RecyclerView.ViewHolder(relLayout)
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
|
|
||||||
ViewHolder {
|
|
||||||
|
|
||||||
val relLayout = LayoutInflater.from(parent.context)
|
|
||||||
.inflate(R.layout.license_row, parent, false) as RelativeLayout
|
|
||||||
return ViewHolder(relLayout)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
|
||||||
val license = dataset[position]
|
|
||||||
|
|
||||||
holder.relLayout.software_name.text = license.name
|
|
||||||
holder.relLayout.software_desc.text = license.description
|
|
||||||
holder.relLayout.software_license.text = license.license
|
|
||||||
holder.relLayout.software_url.text = license.url.toString()
|
|
||||||
|
|
||||||
holder.relLayout.software_desc.visibility = if (license.description == null) View.GONE else View.VISIBLE
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = dataset.size
|
|
||||||
|
|
||||||
fun get(position: Int): License {
|
|
||||||
return dataset[position]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
package eu.depau.etchdroid.enums
|
|
||||||
|
|
||||||
enum class PartitionType
|
|
|
@ -5,10 +5,10 @@ import android.net.Uri
|
||||||
import com.google.common.util.concurrent.SimpleTimeLimiter
|
import com.google.common.util.concurrent.SimpleTimeLimiter
|
||||||
import com.google.common.util.concurrent.TimeLimiter
|
import com.google.common.util.concurrent.TimeLimiter
|
||||||
import com.google.common.util.concurrent.UncheckedTimeoutException
|
import com.google.common.util.concurrent.UncheckedTimeoutException
|
||||||
import eu.depau.etchdroid.kotlin_exts.getBinary
|
import eu.depau.etchdroid.utils.ktexts.getBinary
|
||||||
import eu.depau.etchdroid.kotlin_exts.getFileName
|
import eu.depau.etchdroid.utils.ktexts.getFileName
|
||||||
import eu.depau.etchdroid.kotlin_exts.getFileSize
|
import eu.depau.etchdroid.utils.ktexts.getFileSize
|
||||||
import eu.depau.etchdroid.kotlin_exts.name
|
import eu.depau.etchdroid.utils.ktexts.name
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
|
@ -2,9 +2,9 @@ package eu.depau.etchdroid.services
|
||||||
|
|
||||||
import android.hardware.usb.UsbDevice
|
import android.hardware.usb.UsbDevice
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import eu.depau.etchdroid.kotlin_exts.getFileName
|
import eu.depau.etchdroid.utils.ktexts.getFileName
|
||||||
import eu.depau.etchdroid.kotlin_exts.getFileSize
|
import eu.depau.etchdroid.utils.ktexts.getFileSize
|
||||||
import eu.depau.etchdroid.kotlin_exts.name
|
import eu.depau.etchdroid.utils.ktexts.name
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
class UsbApiImgWriteService : UsbApiWriteService("UsbApiImgWriteService") {
|
class UsbApiImgWriteService : UsbApiWriteService("UsbApiImgWriteService") {
|
||||||
|
|
|
@ -5,9 +5,9 @@ import android.hardware.usb.UsbDevice
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.github.mjdev.libaums.UsbMassStorageDevice
|
import com.github.mjdev.libaums.UsbMassStorageDevice
|
||||||
import eu.depau.etchdroid.exceptions.UsbWriteException
|
import eu.depau.etchdroid.utils.exception.UsbWriteException
|
||||||
import eu.depau.etchdroid.kotlin_exts.getFileName
|
import eu.depau.etchdroid.utils.ktexts.getFileName
|
||||||
import eu.depau.etchdroid.kotlin_exts.name
|
import eu.depau.etchdroid.utils.ktexts.name
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
|
@ -7,9 +7,9 @@ import android.os.Build
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
||||||
import eu.depau.etchdroid.activities.ErrorActivity
|
import eu.depau.etchdroid.ui.activities.ErrorActivity
|
||||||
import eu.depau.etchdroid.kotlin_exts.toHRSize
|
import eu.depau.etchdroid.utils.ktexts.toHRSize
|
||||||
import eu.depau.etchdroid.kotlin_exts.toHRTime
|
import eu.depau.etchdroid.utils.ktexts.toHRTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.activities
|
package eu.depau.etchdroid.ui.activities
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
@ -10,9 +10,9 @@ import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
||||||
import eu.depau.etchdroid.kotlin_exts.toast
|
import eu.depau.etchdroid.utils.ktexts.toast
|
||||||
import eu.depau.etchdroid.utils.DoNotShowAgainDialogFragment
|
import eu.depau.etchdroid.ui.misc.DoNotShowAgainDialogFragment
|
||||||
import eu.depau.etchdroid.utils.NightModeHelper
|
import eu.depau.etchdroid.ui.misc.NightModeHelper
|
||||||
import me.jfenn.attribouter.Attribouter
|
import me.jfenn.attribouter.Attribouter
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.activities
|
package eu.depau.etchdroid.ui.activities
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
@ -8,13 +8,13 @@ import android.widget.Toast
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
||||||
import eu.depau.etchdroid.StateKeeper
|
import eu.depau.etchdroid.StateKeeper
|
||||||
import eu.depau.etchdroid.adapters.PartitionTableRecyclerViewAdapter
|
import eu.depau.etchdroid.ui.adapters.PartitionTableRecyclerViewAdapter
|
||||||
import eu.depau.etchdroid.enums.FlashMethod
|
import eu.depau.etchdroid.utils.enums.FlashMethod
|
||||||
import eu.depau.etchdroid.img_types.DMGImage
|
import eu.depau.etchdroid.utils.imagetypes.DMGImage
|
||||||
import eu.depau.etchdroid.kotlin_exts.*
|
import eu.depau.etchdroid.utils.ktexts.*
|
||||||
import eu.depau.etchdroid.services.UsbApiDmgWriteService
|
import eu.depau.etchdroid.services.UsbApiDmgWriteService
|
||||||
import eu.depau.etchdroid.services.UsbApiImgWriteService
|
import eu.depau.etchdroid.services.UsbApiImgWriteService
|
||||||
import eu.depau.etchdroid.utils.DoNotShowAgainDialogFragment
|
import eu.depau.etchdroid.ui.misc.DoNotShowAgainDialogFragment
|
||||||
import kotlinx.android.synthetic.main.activity_confirmation.*
|
import kotlinx.android.synthetic.main.activity_confirmation.*
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.activities
|
package eu.depau.etchdroid.ui.activities
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.activities
|
package eu.depau.etchdroid.ui.activities
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
@ -10,8 +10,8 @@ import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.codekidlabs.storagechooser.StorageChooser
|
import com.codekidlabs.storagechooser.StorageChooser
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
||||||
import eu.depau.etchdroid.StateKeeper
|
import eu.depau.etchdroid.StateKeeper
|
||||||
import eu.depau.etchdroid.enums.FlashMethod
|
import eu.depau.etchdroid.utils.enums.FlashMethod
|
||||||
import eu.depau.etchdroid.utils.DoNotShowAgainDialogFragment
|
import eu.depau.etchdroid.ui.misc.DoNotShowAgainDialogFragment
|
||||||
import kotlinx.android.synthetic.main.activity_start.*
|
import kotlinx.android.synthetic.main.activity_start.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.activities
|
package eu.depau.etchdroid.ui.activities
|
||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
|
@ -19,12 +19,12 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import com.github.mjdev.libaums.UsbMassStorageDevice
|
import com.github.mjdev.libaums.UsbMassStorageDevice
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
||||||
import eu.depau.etchdroid.StateKeeper
|
import eu.depau.etchdroid.StateKeeper
|
||||||
import eu.depau.etchdroid.adapters.UsbDrivesRecyclerViewAdapter
|
import eu.depau.etchdroid.ui.adapters.UsbDrivesRecyclerViewAdapter
|
||||||
import eu.depau.etchdroid.enums.FlashMethod
|
import eu.depau.etchdroid.utils.enums.FlashMethod
|
||||||
import eu.depau.etchdroid.kotlin_exts.*
|
import eu.depau.etchdroid.utils.ktexts.*
|
||||||
import eu.depau.etchdroid.utils.ClickListener
|
import eu.depau.etchdroid.ui.misc.ClickListener
|
||||||
import eu.depau.etchdroid.utils.EmptyRecyclerView
|
import eu.depau.etchdroid.ui.misc.EmptyRecyclerView
|
||||||
import eu.depau.etchdroid.utils.RecyclerViewTouchListener
|
import eu.depau.etchdroid.ui.misc.RecyclerViewTouchListener
|
||||||
import kotlinx.android.synthetic.main.activity_usb_drive_picker.*
|
import kotlinx.android.synthetic.main.activity_usb_drive_picker.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package eu.depau.etchdroid.adapters
|
package eu.depau.etchdroid.ui.adapters
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
||||||
import eu.depau.etchdroid.kotlin_exts.toHRSize
|
import eu.depau.etchdroid.utils.ktexts.toHRSize
|
||||||
import eu.depau.etchdroid.utils.Partition
|
import eu.depau.etchdroid.utils.Partition
|
||||||
import kotlinx.android.synthetic.main.part_data_keyvalue.view.*
|
import kotlinx.android.synthetic.main.part_data_keyvalue.view.*
|
||||||
import kotlinx.android.synthetic.main.partition_row.view.*
|
import kotlinx.android.synthetic.main.partition_row.view.*
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.adapters
|
package eu.depau.etchdroid.ui.adapters
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
@ -8,7 +8,7 @@ import android.widget.RelativeLayout
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.mjdev.libaums.UsbMassStorageDevice
|
import com.github.mjdev.libaums.UsbMassStorageDevice
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
||||||
import eu.depau.etchdroid.kotlin_exts.vidpid
|
import eu.depau.etchdroid.utils.ktexts.vidpid
|
||||||
import kotlinx.android.synthetic.main.usb_device_row.view.*
|
import kotlinx.android.synthetic.main.usb_device_row.view.*
|
||||||
|
|
||||||
class UsbDrivesRecyclerViewAdapter(private val dataset: Array<UsbMassStorageDevice>) : RecyclerView.Adapter<UsbDrivesRecyclerViewAdapter.ViewHolder>() {
|
class UsbDrivesRecyclerViewAdapter(private val dataset: Array<UsbMassStorageDevice>) : RecyclerView.Adapter<UsbDrivesRecyclerViewAdapter.ViewHolder>() {
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.utils
|
package eu.depau.etchdroid.ui.misc
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.utils
|
package eu.depau.etchdroid.ui.misc
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.utils
|
package eu.depau.etchdroid.ui.misc
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.utils
|
package eu.depau.etchdroid.ui.misc
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.utils
|
package eu.depau.etchdroid.ui.misc
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.GestureDetector
|
import android.view.GestureDetector
|
|
@ -1,10 +0,0 @@
|
||||||
package eu.depau.etchdroid.utils
|
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
|
|
||||||
data class License(
|
|
||||||
val name: String,
|
|
||||||
val url: Uri,
|
|
||||||
val license: String,
|
|
||||||
val description: String? = null
|
|
||||||
)
|
|
|
@ -1,7 +1,7 @@
|
||||||
package eu.depau.etchdroid.utils
|
package eu.depau.etchdroid.utils
|
||||||
|
|
||||||
import eu.depau.etchdroid.enums.FilesystemType
|
import eu.depau.etchdroid.utils.enums.FilesystemType
|
||||||
import eu.depau.etchdroid.enums.PartitionType
|
import eu.depau.etchdroid.utils.enums.PartitionType
|
||||||
|
|
||||||
data class Partition(
|
data class Partition(
|
||||||
val number: Int?,
|
val number: Int?,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package eu.depau.etchdroid.utils
|
package eu.depau.etchdroid.utils
|
||||||
|
|
||||||
import eu.depau.etchdroid.enums.FilesystemType
|
import eu.depau.etchdroid.utils.enums.FilesystemType
|
||||||
import eu.depau.etchdroid.enums.PartitionType
|
import eu.depau.etchdroid.utils.enums.PartitionType
|
||||||
|
|
||||||
class PartitionBuilder {
|
class PartitionBuilder {
|
||||||
var number: Int? = null
|
var number: Int? = null
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.enums
|
package eu.depau.etchdroid.utils.enums
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.enums
|
package eu.depau.etchdroid.utils.enums
|
||||||
|
|
||||||
enum class FlashMethod {
|
enum class FlashMethod {
|
||||||
FLASH_API,
|
FLASH_API,
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.enums
|
package eu.depau.etchdroid.utils.enums
|
||||||
|
|
||||||
enum class ImageLocation {
|
enum class ImageLocation {
|
||||||
REMOTE,
|
REMOTE,
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.enums
|
package eu.depau.etchdroid.utils.enums
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
|
@ -0,0 +1,3 @@
|
||||||
|
package eu.depau.etchdroid.utils.enums
|
||||||
|
|
||||||
|
enum class PartitionType
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.depau.etchdroid.enums
|
package eu.depau.etchdroid.utils.enums
|
||||||
|
|
||||||
enum class WizardStep {
|
enum class WizardStep {
|
||||||
SELECT_FLASH_METHOD,
|
SELECT_FLASH_METHOD,
|
|
@ -0,0 +1,3 @@
|
||||||
|
package eu.depau.etchdroid.utils.exception
|
||||||
|
|
||||||
|
class CannotGetFilePathException(cause: Exception) : Exception(cause)
|
|
@ -0,0 +1,8 @@
|
||||||
|
package eu.depau.etchdroid.utils.exception
|
||||||
|
|
||||||
|
import eu.depau.etchdroid.utils.ktexts.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
|
||||||
|
)
|
|
@ -0,0 +1,112 @@
|
||||||
|
package eu.depau.etchdroid.utils.imagetypes
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
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 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package eu.depau.etchdroid.utils.imagetypes
|
||||||
|
|
||||||
|
import eu.depau.etchdroid.utils.enums.PartitionTableType
|
||||||
|
import eu.depau.etchdroid.utils.Partition
|
||||||
|
|
||||||
|
interface Image {
|
||||||
|
val partitionTable: List<Partition>?
|
||||||
|
val tableType: PartitionTableType?
|
||||||
|
val size: Long?
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package eu.depau.etchdroid.utils.ktexts
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package eu.depau.etchdroid.utils.ktexts
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.widget.Toast
|
||||||
|
|
||||||
|
fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_LONG) {
|
||||||
|
Toast.makeText(this, message, duration).show()
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package eu.depau.etchdroid.utils.ktexts
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package eu.depau.etchdroid.utils.ktexts
|
||||||
|
|
||||||
|
// 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)
|
|
@ -0,0 +1,31 @@
|
||||||
|
package eu.depau.etchdroid.utils.ktexts
|
||||||
|
|
||||||
|
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)
|
|
@ -0,0 +1,29 @@
|
||||||
|
package eu.depau.etchdroid.utils.ktexts
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package eu.depau.etchdroid.utils.ktexts
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package eu.depau.etchdroid.utils.ktexts
|
||||||
|
|
||||||
|
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.utils.exception.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
|
|
@ -0,0 +1,39 @@
|
||||||
|
package eu.depau.etchdroid.utils.ktexts
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package eu.depau.etchdroid.utils.ktexts
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package eu.depau.etchdroid.utils.ktexts
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:theme="@style/DarkThemeOverlay"
|
android:theme="@style/DarkThemeOverlay"
|
||||||
tools:context=".activities.ConfirmationActivity">
|
tools:context=".ui.activities.ConfirmationActivity">
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:id="@+id/nestedScrollView"
|
android:id="@+id/nestedScrollView"
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
android:paddingBottom="@dimen/row_padding"
|
android:paddingBottom="@dimen/row_padding"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:theme="@style/CardContentStyle"
|
android:theme="@style/CardContentStyle"
|
||||||
tools:context=".activities.ErrorActivity">
|
tools:context=".ui.activities.ErrorActivity">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:theme="@style/CardContentStyle"
|
android:theme="@style/CardContentStyle"
|
||||||
tools:context=".activities.LicensesActivity">
|
tools:context=".ui.activities.LicensesActivity">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:theme="@style/DarkThemeOverlay"
|
android:theme="@style/DarkThemeOverlay"
|
||||||
tools:context=".activities.StartActivity">
|
tools:context=".ui.activities.StartActivity">
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:theme="@style/DarkThemeOverlay"
|
android:theme="@style/DarkThemeOverlay"
|
||||||
tools:context=".activities.UsbDrivePickerActivity">
|
tools:context=".ui.activities.UsbDrivePickerActivity">
|
||||||
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context="eu.depau.etchdroid.activities.StartActivity">
|
tools:context=".ui.activities.StartActivity">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_about"
|
android:id="@+id/action_about"
|
||||||
android:orderInCategory="100"
|
android:orderInCategory="100"
|
||||||
|
|
Loading…
Reference in a new issue