diff --git a/app/build.gradle b/app/build.gradle
index ad7466d..8e08895 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -12,7 +12,7 @@ android {
targetSdkVersion 28
versionCode 1
versionName "1.0"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
@@ -26,11 +26,11 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
- implementation 'com.android.support.constraint:constraint-layout:1.1.2'
- implementation 'com.android.support:design:28.0.0-rc01'
- implementation 'com.android.support:recyclerview-v7:28.0.0-rc01'
- implementation 'com.android.support:gridlayout-v7:28.0.0-rc01'
+ implementation 'androidx.appcompat:appcompat:1.0.0-rc02'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
+ implementation 'com.google.android.material:material:1.0.0-rc01'
+ implementation 'androidx.recyclerview:recyclerview:1.0.0-rc02'
+ implementation 'androidx.gridlayout:gridlayout:1.0.0-rc02'
api 'com.google.guava:guava:26.0-android'
implementation 'com.github.codekidX:storage-chooser:2.0.4.2'
@@ -39,7 +39,9 @@ dependencies {
implementation project(':dmg2img')
+ implementation 'com.android.support.constraint:constraint-layout:1.1.2'
+ implementation 'com.android.support:design:28.0.0-rc01'
testImplementation 'junit:junit:4.12'
- androidTestImplementation 'com.android.support.test:runner:1.0.2'
- androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+ androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
}
diff --git a/app/src/androidTest/java/eu/depau/etchdroid/ExampleInstrumentedTest.kt b/app/src/androidTest/java/eu/depau/etchdroid/ExampleInstrumentedTest.kt
index 61c26bb..802f23a 100644
--- a/app/src/androidTest/java/eu/depau/etchdroid/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/eu/depau/etchdroid/ExampleInstrumentedTest.kt
@@ -1,7 +1,7 @@
package eu.depau.etchdroid
-import android.support.test.InstrumentationRegistry
-import android.support.test.runner.AndroidJUnit4
+import androidx.test.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5cce9b4..70a77f6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,7 +1,9 @@
+
@@ -12,17 +14,41 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
- android:theme="@style/AppTheme">
+ android:theme="@style/AppTheme"
+ tools:ignore="GoogleAppIndexingWarning">
+
+ android:theme="@style/MaterialAppTheme">
+
-
+
+
+
+
+
+
+
+
-
-
-
\ No newline at end of file
diff --git a/app/src/main/java/eu/depau/etchdroid/StateKeeper.kt b/app/src/main/java/eu/depau/etchdroid/StateKeeper.kt
index 69d85fd..fcb54f8 100644
--- a/app/src/main/java/eu/depau/etchdroid/StateKeeper.kt
+++ b/app/src/main/java/eu/depau/etchdroid/StateKeeper.kt
@@ -3,18 +3,11 @@ package eu.depau.etchdroid
import android.hardware.usb.UsbDevice
import android.net.Uri
import com.github.mjdev.libaums.UsbMassStorageDevice
-import eu.depau.etchdroid.fragments.WizardFragment
import eu.depau.etchdroid.enums.FlashMethod
-import eu.depau.etchdroid.enums.ImageLocation
-import eu.depau.etchdroid.enums.WizardStep
import eu.depau.etchdroid.img_types.Image
object StateKeeper {
- var wizardStep: WizardStep = WizardStep.SELECT_FLASH_METHOD
- var currentFragment: WizardFragment? = null
var flashMethod: FlashMethod? = null
- var imageLocation: ImageLocation? = ImageLocation.LOCAL
- var streamingWrite: Boolean = false
var imageFile: Uri? = null
var imageRepr: Image? = null
diff --git a/app/src/main/java/eu/depau/etchdroid/activities/ActivityBase.kt b/app/src/main/java/eu/depau/etchdroid/activities/ActivityBase.kt
new file mode 100644
index 0000000..899d3d6
--- /dev/null
+++ b/app/src/main/java/eu/depau/etchdroid/activities/ActivityBase.kt
@@ -0,0 +1,30 @@
+package eu.depau.etchdroid.activities
+
+import android.content.Intent
+import android.view.Menu
+import android.view.MenuItem
+import androidx.appcompat.app.AppCompatActivity
+import eu.depau.etchdroid.R
+
+abstract class ActivityBase: AppCompatActivity() {
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ menuInflater.inflate(R.menu.menu_main, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ return when (item.itemId) {
+ R.id.action_licenses -> {
+ val intent = Intent(this, LicensesActivity::class.java)
+ startActivity(intent)
+ return true
+ }
+ else -> super.onOptionsItemSelected(item)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/depau/etchdroid/activities/ConfirmationActivity.kt b/app/src/main/java/eu/depau/etchdroid/activities/ConfirmationActivity.kt
new file mode 100644
index 0000000..9cbfb34
--- /dev/null
+++ b/app/src/main/java/eu/depau/etchdroid/activities/ConfirmationActivity.kt
@@ -0,0 +1,150 @@
+package eu.depau.etchdroid.activities
+
+import android.os.Bundle
+import android.app.Activity
+import android.content.Intent
+import android.os.Build
+import android.view.View
+import android.widget.Toast
+import androidx.recyclerview.widget.LinearLayoutManager
+import eu.depau.etchdroid.R
+import eu.depau.etchdroid.StateKeeper
+import eu.depau.etchdroid.adapters.PartitionTableRecyclerViewAdapter
+import eu.depau.etchdroid.enums.FlashMethod
+import eu.depau.etchdroid.img_types.DMGImage
+import eu.depau.etchdroid.kotlin_exts.*
+import eu.depau.etchdroid.services.UsbApiDmgWriteService
+import eu.depau.etchdroid.services.UsbApiImgWriteService
+
+import kotlinx.android.synthetic.main.activity_confirmation.*
+import java.io.IOException
+
+class ConfirmationActivity : ActivityBase() {
+ var canContinue: Boolean = false
+ var issuesFound: Boolean = false
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_confirmation)
+ actionBar?.setDisplayHomeAsUpEnabled(true)
+
+ displayDetails()
+ displayImageLayout()
+ }
+
+ fun displayDetails() {
+ confirm_sel_method.text = when (StateKeeper.flashMethod) {
+ FlashMethod.FLASH_API -> getString(R.string.flash_dd_usb_api)
+ FlashMethod.FLASH_DMG_API -> getString(R.string.flash_dmg_api)
+ FlashMethod.FLASH_UNETBOOTIN -> getString(R.string.flash_unetbootin)
+ FlashMethod.FLASH_WOEUSB -> getString(R.string.flash_woeusb)
+ else -> null
+ }
+
+ confirm_sel_image.text = StateKeeper.imageFile?.getFileName(this)
+
+ if (confirm_sel_image.text == null)
+ confirm_sel_image.text = getString(R.string.unknown_filename)
+
+ val imgSize = StateKeeper.imageFile?.getFileSize(this)
+ confirm_sel_image_size.text = imgSize?.toHRSize()
+
+ confirm_sel_usbdev.text = StateKeeper.usbDevice?.name
+
+ for (trial in 0..1) {
+ try {
+ StateKeeper.usbMassStorageDevice!!.init()
+ val blockDev = StateKeeper.usbMassStorageDevice?.blockDevice
+
+ if (blockDev != null) {
+ val devSize = (blockDev.size.toLong() * blockDev.blockSize.toLong())
+ confirm_sel_usbdev_size.text = devSize.toHRSize()
+
+ if (imgSize!! > devSize)
+ confirm_extra_info.text = getString(R.string.image_bigger_than_usb)
+ else {
+ var text =
+ if (StateKeeper.flashMethod == FlashMethod.FLASH_DMG_API)
+ getString(R.string.no_image_size_check_dmg) + "\n"
+ else
+ ""
+ text += getString(R.string.tap_next_to_write)
+ confirm_extra_info.text = text
+ canContinue = true
+ }
+ } else {
+ confirm_extra_info.text = getString(R.string.cant_read_usbdev)
+ }
+ } catch (e: IOException) {
+ if (trial == 0) {
+ StateKeeper.usbMassStorageDevice!!.close()
+ continue
+ } else {
+ confirm_extra_info.text = getString(R.string.could_not_access_usb_error)
+ break
+ }
+ }
+ }
+ }
+
+ fun displayImageLayout() {
+ val uri = StateKeeper.imageFile ?: return
+ val text = uri.getFileName(this)
+
+ if (StateKeeper.flashMethod == FlashMethod.FLASH_DMG_API) {
+ StateKeeper.imageRepr = DMGImage(uri, this)
+ val imgRepr = StateKeeper.imageRepr as DMGImage
+
+ if (imgRepr.tableType == null && (imgRepr.partitionTable == null || imgRepr.partitionTable?.size == 0)) {
+ part_table_header.text = getString(R.string.image_is_not_dmg)
+ issuesFound = true
+ return
+ } else {
+ part_table_header.text = if (imgRepr.tableType != null) getString(R.string.partition_table_title) else ""
+ part_table_header_side.text = imgRepr.tableType?.getString(this) ?: ""
+ issuesFound = false
+
+ val viewAdapter = PartitionTableRecyclerViewAdapter(imgRepr.partitionTable!!)
+ part_table_recycler.apply {
+ setHasFixedSize(true)
+ layoutManager = LinearLayoutManager(context)
+ adapter = viewAdapter
+ }
+ }
+ }
+ }
+
+ fun nextStep() {
+ if (!canContinue) {
+ confirm_fab.snackbar(getString(R.string.cannot_write))
+ return
+ }
+
+ toast(getString(R.string.check_notification_progress), Toast.LENGTH_LONG)
+
+ val intent: Intent = when (StateKeeper.flashMethod) {
+ FlashMethod.FLASH_API -> Intent(this, UsbApiImgWriteService::class.java)
+ FlashMethod.FLASH_DMG_API -> Intent(this, UsbApiDmgWriteService::class.java)
+ else -> null!!
+ }
+
+ intent.setDataAndType(StateKeeper.imageFile, "application/octet-stream")
+ intent.putExtra("usbDevice", StateKeeper.usbDevice)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ startForegroundService(intent)
+ else
+ startService(intent)
+
+ moveTaskToBack(true);
+ finish()
+ }
+
+
+ fun onButtonClicked(view: View) {
+ when (view.id) {
+ R.id.confirm_fab -> nextStep()
+ }
+ }
+
+}
diff --git a/app/src/main/java/eu/depau/etchdroid/activities/LicensesActivity.kt b/app/src/main/java/eu/depau/etchdroid/activities/LicensesActivity.kt
index 0f81157..64fa9ff 100644
--- a/app/src/main/java/eu/depau/etchdroid/activities/LicensesActivity.kt
+++ b/app/src/main/java/eu/depau/etchdroid/activities/LicensesActivity.kt
@@ -3,10 +3,9 @@ package eu.depau.etchdroid.activities
import android.content.Intent
import android.net.Uri
import android.os.Bundle
-import android.support.v7.app.AppCompatActivity
-import android.support.v7.widget.LinearLayoutManager
-import android.support.v7.widget.RecyclerView
-import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import android.view.MenuItem
import android.view.View
import eu.depau.etchdroid.R
@@ -41,8 +40,6 @@ class LicensesActivity : AppCompatActivity() {
setContentView(R.layout.activity_licenses)
updateLicenses()
- title = getString(R.string.licenses)
-
// Enable back button in action bar
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
diff --git a/app/src/main/java/eu/depau/etchdroid/activities/MainActivity.kt b/app/src/main/java/eu/depau/etchdroid/activities/MainActivity.kt
deleted file mode 100644
index 6aed5ca..0000000
--- a/app/src/main/java/eu/depau/etchdroid/activities/MainActivity.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package eu.depau.etchdroid.activities
-
-import android.app.PendingIntent
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.hardware.usb.UsbDevice
-import android.hardware.usb.UsbManager
-import android.os.Bundle
-import android.view.Menu
-import android.view.MenuItem
-import eu.depau.etchdroid.R
-import eu.depau.etchdroid.StateKeeper
-import eu.depau.etchdroid.fragments.WizardFragment
-import eu.depau.etchdroid.fragments.FlashMethodFragment
-import eu.depau.etchdroid.fragments.UsbDriveFragment
-import kotlinx.android.synthetic.main.activity_main.*
-
-class MainActivity : WizardActivity() {
- val TAG = "MainActivity"
- val ACTION_USB_PERMISSION = "eu.depau.etchdroid.USB_PERMISSION"
- lateinit var mUsbPermissionIntent: PendingIntent
-
- private val mUsbReceiver = object : BroadcastReceiver() {
-
- override fun onReceive(context: Context, intent: Intent) {
- if (intent.action == ACTION_USB_PERMISSION) {
- synchronized(this) {
- val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
-
- val result = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
- if (result)
- device?.apply {
- StateKeeper.usbDevice = this
- }
-
- if (StateKeeper.currentFragment is UsbDriveFragment)
- (StateKeeper.currentFragment as UsbDriveFragment).onUsbPermissionResult(device, result)
- }
- }
- }
- }
-
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- setSupportActionBar(toolbar)
- fab.setOnClickListener(::nextStep)
-
- // Create new fragment and transaction
- val fragment = FlashMethodFragment()
- val transaction = supportFragmentManager.beginTransaction()
- transaction.replace(R.id.fragment_layout, fragment)
- transaction.commit()
- fragment.onFragmentAdded(this)
-
- val usbManager = getSystemService(Context.USB_SERVICE) as UsbManager
- mUsbPermissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), 0)
- val filter = IntentFilter(ACTION_USB_PERMISSION)
- registerReceiver(mUsbReceiver, filter)
- }
-
- override fun onDestroy() {
- super.onDestroy()
-
- unregisterReceiver(mUsbReceiver)
- }
-
- override fun goToNewFragment(fragment: WizardFragment) {
- StateKeeper.currentFragment?.onFragmentRemoving(this)
-
- val transaction = supportFragmentManager.beginTransaction()
- transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left)
- transaction.replace(R.id.fragment_layout, fragment)
- transaction.addToBackStack(null)
- transaction.commit()
-
- fragment.onFragmentAdded(this)
- }
-
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- // Inflate the menu; this adds items to the action bar if it is present.
- menuInflater.inflate(R.menu.menu_main, menu)
- return true
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- // Handle action bar item clicks here. The action bar will
- // automatically handle clicks on the Home/Up button, so long
- // as you specify a parent activity in AndroidManifest.xml.
- return when (item.itemId) {
- R.id.action_licenses -> {
- val intent = Intent(this, LicensesActivity::class.java)
- startActivity(intent)
- return true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-}
diff --git a/app/src/main/java/eu/depau/etchdroid/activities/StartActivity.kt b/app/src/main/java/eu/depau/etchdroid/activities/StartActivity.kt
new file mode 100644
index 0000000..e9dd21b
--- /dev/null
+++ b/app/src/main/java/eu/depau/etchdroid/activities/StartActivity.kt
@@ -0,0 +1,128 @@
+package eu.depau.etchdroid.activities
+
+import android.Manifest
+import android.app.Activity
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.Bundle
+import android.os.Environment
+import android.view.View
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import com.codekidlabs.storagechooser.StorageChooser
+import eu.depau.etchdroid.R
+import eu.depau.etchdroid.StateKeeper
+import eu.depau.etchdroid.enums.FlashMethod
+import eu.depau.etchdroid.kotlin_exts.snackbar
+import kotlinx.android.synthetic.main.activity_start.*
+import java.io.File
+
+class StartActivity : ActivityBase() {
+ val TAG = "StartActivity"
+ val READ_REQUEST_CODE = 42
+ val READ_EXTERNAL_STORAGE_PERMISSION = 29
+ var delayedButtonClicked: View? = null
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_start)
+ }
+
+ fun onButtonClicked(view: View) {
+ StateKeeper.flashMethod = when (view.id) {
+ R.id.btn_image_raw -> FlashMethod.FLASH_API
+ R.id.btn_image_dmg -> FlashMethod.FLASH_DMG_API
+ else -> null
+ }
+
+ when (StateKeeper.flashMethod) {
+ FlashMethod.FLASH_API -> {
+ val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
+ intent.addCategory(Intent.CATEGORY_OPENABLE)
+ intent.setType("*/*");
+ startActivityForResult(intent, READ_REQUEST_CODE)
+ }
+ FlashMethod.FLASH_DMG_API -> {
+ if (checkAndRequestStorageReadPerm()) {
+ val sdcard = Environment.getExternalStorageDirectory().absolutePath
+
+ val chooser = StorageChooser.Builder()
+ .withActivity(this)
+ .withFragmentManager(fragmentManager)
+ .withMemoryBar(true)
+ .allowCustomPath(true)
+ .setType(StorageChooser.FILE_PICKER)
+ .customFilter(arrayListOf("dmg"))
+ .build()
+ chooser.show()
+ chooser.setOnSelectListener {
+ StateKeeper.imageFile = Uri.fromFile(File(it))
+ nextStep()
+ }
+ } else {
+ delayedButtonClicked = view
+ }
+ }
+ FlashMethod.FLASH_UNETBOOTIN -> {
+ }
+ FlashMethod.FLASH_WOEUSB -> {
+ }
+ null -> {
+ }
+
+ }
+ }
+
+ private fun checkAndRequestStorageReadPerm(): Boolean {
+ if ((ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) {
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this,
+ Manifest.permission.READ_EXTERNAL_STORAGE)) {
+ btn_image_dmg.snackbar("Storage permission is required to read DMG images")
+ } else {
+ ActivityCompat.requestPermissions(this,
+ arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
+ READ_EXTERNAL_STORAGE_PERMISSION)
+ }
+ } else {
+ // Permission granted
+ return true
+ }
+ return false
+ }
+
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
+ when (requestCode) {
+ READ_EXTERNAL_STORAGE_PERMISSION -> {
+ if (delayedButtonClicked != null)
+ onButtonClicked(delayedButtonClicked!!)
+ return
+ }
+ else -> {
+ }
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+ // The document selected by the user won't be returned in the intent.
+ // Instead, a URI to that document will be contained in the return intent
+ // provided to this method as a parameter.
+ // Pull that URI using resultData.getData().
+ var uri: Uri? = null
+ if (data != null) {
+ uri = data.getData()
+ StateKeeper.imageFile = uri
+
+ nextStep()
+ }
+ }
+ }
+
+
+ fun nextStep() {
+ val intent = Intent(this, UsbDrivePickerActivity::class.java)
+ startActivity(intent)
+ }
+}
diff --git a/app/src/main/java/eu/depau/etchdroid/activities/UsbDrivePickerActivity.kt b/app/src/main/java/eu/depau/etchdroid/activities/UsbDrivePickerActivity.kt
new file mode 100644
index 0000000..18c4639
--- /dev/null
+++ b/app/src/main/java/eu/depau/etchdroid/activities/UsbDrivePickerActivity.kt
@@ -0,0 +1,150 @@
+package eu.depau.etchdroid.activities
+
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.hardware.usb.UsbDevice
+import android.hardware.usb.UsbManager
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import com.github.mjdev.libaums.UsbMassStorageDevice
+import eu.depau.etchdroid.R
+import eu.depau.etchdroid.StateKeeper
+import eu.depau.etchdroid.adapters.UsbDrivesRecyclerViewAdapter
+import eu.depau.etchdroid.kotlin_exts.name
+import eu.depau.etchdroid.kotlin_exts.snackbar
+import eu.depau.etchdroid.utils.ClickListener
+import eu.depau.etchdroid.utils.EmptyRecyclerView
+import eu.depau.etchdroid.utils.RecyclerViewTouchListener
+import kotlinx.android.synthetic.main.activity_usb_drive_picker.*
+
+class UsbDrivePickerActivity : ActivityBase(), SwipeRefreshLayout.OnRefreshListener {
+ val USB_PERMISSION = "eu.depau.etchdroid.USB_PERMISSION"
+ lateinit var mUsbPermissionIntent: PendingIntent
+
+ private lateinit var recyclerView: EmptyRecyclerView
+ private lateinit var viewAdapter: UsbDrivesRecyclerViewAdapter
+ private lateinit var viewManager: RecyclerView.LayoutManager
+ private lateinit var refreshLayout: SwipeRefreshLayout
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_usb_drive_picker)
+ actionBar?.setDisplayHomeAsUpEnabled(true)
+
+ mUsbPermissionIntent = PendingIntent.getBroadcast(this, 0, Intent(USB_PERMISSION), 0)
+ val usbPermissionFilter = IntentFilter(USB_PERMISSION)
+ val usbAttachedFilter = IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED)
+ val usbDetachedFilter = IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED)
+
+ registerReceiver(mUsbReceiver, usbPermissionFilter)
+ registerReceiver(mUsbReceiver, usbAttachedFilter)
+ registerReceiver(mUsbReceiver, usbDetachedFilter)
+
+ refreshLayout = usbdevs_swiperefreshlayout
+ refreshLayout.setOnRefreshListener(this)
+ refreshLayout.post {
+ refreshLayout.isRefreshing = true
+ loadUsbDevices()
+ }
+
+ viewManager = LinearLayoutManager(this)
+ recyclerView = usbdevs_recycler_view
+ recyclerView.emptyView = usbdevs_recycler_empty_view
+
+ recyclerView.addOnItemTouchListener(RecyclerViewTouchListener(this, recyclerView, object : ClickListener {
+ override fun onClick(view: View, position: Int) {
+ val device = viewAdapter.get(position)
+ val manager = getSystemService(Context.USB_SERVICE) as UsbManager
+ manager.requestPermission(device.usbDevice, mUsbPermissionIntent)
+ }
+
+ override fun onLongClick(view: View, position: Int) {}
+ }))
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ unregisterReceiver(mUsbReceiver)
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.usb_devices_menu, menu)
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ override fun onContextItemSelected(item: MenuItem): Boolean {
+ return when (item.itemId) {
+ R.id.action_refresh -> {
+ loadUsbDevices()
+ true
+ }
+ else -> super.onOptionsItemSelected(item)
+ }
+ }
+
+ override fun onRefresh() {
+ loadUsbDevices()
+ }
+
+
+ private val mUsbReceiver = object : BroadcastReceiver() {
+
+ override fun onReceive(context: Context, intent: Intent) {
+ when (intent.action) {
+ USB_PERMISSION -> synchronized(this) {
+ val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
+
+ val result = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
+ if (result)
+ device?.apply {
+ StateKeeper.usbDevice = this
+ }
+
+ if (!result) {
+ if (device != null) {
+ recyclerView.snackbar(getString(R.string.usb_perm_denied) + device.name)
+ } else {
+ recyclerView.snackbar(getString(R.string.usb_perm_denied_noname))
+ }
+ return
+ }
+
+ StateKeeper.usbDevice = device
+ StateKeeper.usbMassStorageDevice = UsbMassStorageDevice.getMassStorageDevices(context).find { it.usbDevice == device }
+
+ nextStep()
+ }
+ UsbManager.ACTION_USB_DEVICE_ATTACHED, UsbManager.ACTION_USB_DEVICE_DETACHED -> loadUsbDevices()
+ }
+ }
+ }
+
+ fun loadUsbDevices() {
+ try {
+ viewAdapter = UsbDrivesRecyclerViewAdapter(UsbMassStorageDevice.getMassStorageDevices(this))
+
+ recyclerView.apply {
+ setHasFixedSize(true)
+ layoutManager = viewManager
+ adapter = viewAdapter
+ }
+ } finally {
+ refreshLayout.isRefreshing = false
+ }
+ }
+
+
+ fun nextStep() {
+ val intent = Intent(this, ConfirmationActivity::class.java)
+ startActivity(intent)
+ }
+}
diff --git a/app/src/main/java/eu/depau/etchdroid/activities/WizardActivity.kt b/app/src/main/java/eu/depau/etchdroid/activities/WizardActivity.kt
deleted file mode 100644
index b888d03..0000000
--- a/app/src/main/java/eu/depau/etchdroid/activities/WizardActivity.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package eu.depau.etchdroid.activities
-
-import android.content.Intent
-import android.support.v7.app.AppCompatActivity
-import android.view.View
-import eu.depau.etchdroid.StateKeeper
-import eu.depau.etchdroid.fragments.WizardFragment
-
-abstract class WizardActivity : AppCompatActivity() {
- abstract fun goToNewFragment(fragment: WizardFragment)
-
- open fun onCheckBoxClicked(view: View) {
- StateKeeper.currentFragment?.onCheckBoxClicked(view)
- }
-
- open fun onButtonClicked(view: View) {
- StateKeeper.currentFragment?.onButtonClicked(view)
- }
-
- open fun onRadioButtonClicked(view: View) {
- StateKeeper.currentFragment?.onRadioButtonClicked(view)
- }
-
- open fun nextStep(view: View) {
- StateKeeper.currentFragment?.nextStep(view)
- }
-
- override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
- StateKeeper.currentFragment?.onRequestPermissionsResult(requestCode, permissions, grantResults)
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- StateKeeper.currentFragment?.onActivityResult(requestCode, resultCode, data)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/depau/etchdroid/adapters/LicenseRecyclerViewAdapter.kt b/app/src/main/java/eu/depau/etchdroid/adapters/LicenseRecyclerViewAdapter.kt
index 31fa6be..cebc51d 100644
--- a/app/src/main/java/eu/depau/etchdroid/adapters/LicenseRecyclerViewAdapter.kt
+++ b/app/src/main/java/eu/depau/etchdroid/adapters/LicenseRecyclerViewAdapter.kt
@@ -1,7 +1,7 @@
package eu.depau.etchdroid.adapters
import android.annotation.SuppressLint
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
diff --git a/app/src/main/java/eu/depau/etchdroid/adapters/PartitionTableRecyclerViewAdapter.kt b/app/src/main/java/eu/depau/etchdroid/adapters/PartitionTableRecyclerViewAdapter.kt
index d0b2780..3389185 100644
--- a/app/src/main/java/eu/depau/etchdroid/adapters/PartitionTableRecyclerViewAdapter.kt
+++ b/app/src/main/java/eu/depau/etchdroid/adapters/PartitionTableRecyclerViewAdapter.kt
@@ -1,6 +1,6 @@
package eu.depau.etchdroid.adapters
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.LinearLayout
diff --git a/app/src/main/java/eu/depau/etchdroid/adapters/UsbDrivesRecyclerViewAdapter.kt b/app/src/main/java/eu/depau/etchdroid/adapters/UsbDrivesRecyclerViewAdapter.kt
index 4dbbd92..cf0c311 100644
--- a/app/src/main/java/eu/depau/etchdroid/adapters/UsbDrivesRecyclerViewAdapter.kt
+++ b/app/src/main/java/eu/depau/etchdroid/adapters/UsbDrivesRecyclerViewAdapter.kt
@@ -2,7 +2,7 @@ package eu.depau.etchdroid.adapters
import android.annotation.SuppressLint
import android.os.Build
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.RelativeLayout
diff --git a/app/src/main/java/eu/depau/etchdroid/fragments/ConfirmInfoFragment.kt b/app/src/main/java/eu/depau/etchdroid/fragments/ConfirmInfoFragment.kt
deleted file mode 100644
index b202673..0000000
--- a/app/src/main/java/eu/depau/etchdroid/fragments/ConfirmInfoFragment.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-package eu.depau.etchdroid.fragments
-
-import android.content.Intent
-import android.os.Build
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import eu.depau.etchdroid.R
-import eu.depau.etchdroid.StateKeeper
-import eu.depau.etchdroid.enums.FlashMethod
-import eu.depau.etchdroid.enums.WizardStep
-import eu.depau.etchdroid.kotlin_exts.*
-import eu.depau.etchdroid.services.UsbApiDmgWriteService
-import eu.depau.etchdroid.services.UsbApiImgWriteService
-import kotlinx.android.synthetic.main.fragment_confirminfo.view.*
-import java.io.IOException
-
-/**
- * A placeholder fragment containing a simple view.
- */
-class ConfirmInfoFragment : WizardFragment() {
- val TAG = "ConfirmInfoFragment"
- var canContinue = false
-
- override fun nextStep(view: View?) {
- if (!canContinue) {
- view?.snackbar("Cannot write image to USB drive")
- return
- }
-
- context?.toast("Check notification for progress")
-
- val intent: Intent = when (StateKeeper.flashMethod) {
- FlashMethod.FLASH_API -> Intent(activity, UsbApiImgWriteService::class.java)
- FlashMethod.FLASH_DMG_API -> Intent(activity, UsbApiDmgWriteService::class.java)
- else -> null!!
- }
-
- intent.setDataAndType(StateKeeper.imageFile, "application/octet-stream")
- intent.putExtra("usbDevice", StateKeeper.usbDevice)
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
- activity!!.startForegroundService(intent)
- else
- activity!!.startService(intent)
-
- activity!!.finish()
- }
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?): View? {
- StateKeeper.currentFragment = this
- StateKeeper.wizardStep = WizardStep.CONFIRM
-
- val view = inflater.inflate(R.layout.fragment_confirminfo, container, false)
-
- view.confirm_sel_method.text = when (StateKeeper.flashMethod) {
- FlashMethod.FLASH_API -> getString(R.string.flash_dd_usb_api)
- FlashMethod.FLASH_DMG_API -> getString(R.string.flash_dmg_api)
- FlashMethod.FLASH_UNETBOOTIN -> getString(R.string.flash_unetbootin)
- FlashMethod.FLASH_WOEUSB -> getString(R.string.flash_woeusb)
- else -> null
- }
-
- view.confirm_sel_image.text = StateKeeper.imageFile?.getFileName(context!!)
-
- if (view.confirm_sel_image.text == null)
- view.confirm_sel_image.text = getString(R.string.unknown_filename)
-
- val imgSize = StateKeeper.imageFile?.getFileSize(context!!)
- view.confirm_sel_image_size.text = imgSize?.toHRSize()
-
- view.confirm_sel_usbdev.text = StateKeeper.usbDevice?.name
-
- for (trial in 0..1) {
- try {
- StateKeeper.usbMassStorageDevice!!.init()
- val blockDev = StateKeeper.usbMassStorageDevice?.blockDevice
-
- if (blockDev != null) {
- val devSize = (blockDev.size.toLong() * blockDev.blockSize.toLong())
- view.confirm_sel_usbdev_size.text = devSize.toHRSize()
-
- if (imgSize!! > devSize)
- view.confirm_extra_info.text = getString(R.string.image_bigger_than_usb)
- else {
- var text =
- if (StateKeeper.flashMethod == FlashMethod.FLASH_DMG_API)
- getString(R.string.no_image_size_check_dmg) + "\n"
- else
- ""
- text += getString(R.string.tap_next_to_write)
- view.confirm_extra_info.text = text
- canContinue = true
- }
- } else {
- view.confirm_extra_info.text = getString(R.string.cant_read_usbdev)
- }
- } catch (e: IOException) {
- if (trial == 0) {
- StateKeeper.usbMassStorageDevice!!.close()
- continue
- } else {
- view.confirm_extra_info.text = "Could not access USB device. Maybe you ran the app previously and it crashed? Remove and reinsert the USB drive, then restart the app."
- break
- }
- }
- }
-
- return view
- }
-
-
-}
diff --git a/app/src/main/java/eu/depau/etchdroid/fragments/FlashMethodFragment.kt b/app/src/main/java/eu/depau/etchdroid/fragments/FlashMethodFragment.kt
deleted file mode 100644
index 3cb7422..0000000
--- a/app/src/main/java/eu/depau/etchdroid/fragments/FlashMethodFragment.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package eu.depau.etchdroid.fragments
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import eu.depau.etchdroid.R
-import eu.depau.etchdroid.StateKeeper
-import eu.depau.etchdroid.activities.WizardActivity
-import eu.depau.etchdroid.kotlin_exts.snackbar
-import eu.depau.etchdroid.enums.FlashMethod
-import eu.depau.etchdroid.enums.WizardStep
-
-/**
- * A placeholder fragment containing a simple view.
- */
-class FlashMethodFragment : WizardFragment() {
- override fun nextStep(view: View?) {
- if (StateKeeper.flashMethod == null)
- view?.snackbar(getString(R.string.please_select_writing_method))
- else
- (activity as WizardActivity).goToNewFragment(ImageLocationFragment())
- }
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?): View? {
- StateKeeper.currentFragment = this
- StateKeeper.wizardStep = WizardStep.SELECT_FLASH_METHOD
-
- return inflater.inflate(R.layout.fragment_select_flash_method, container, false)
- }
-
- override fun onRadioButtonClicked(view: View) {
- StateKeeper.flashMethod = when (view.id) {
- R.id.flash_dmg_api_radio -> FlashMethod.FLASH_DMG_API
- R.id.flash_usb_api_radio -> FlashMethod.FLASH_API
- R.id.flash_unetbootin_radio -> FlashMethod.FLASH_UNETBOOTIN
- R.id.flash_woeusb_radio -> FlashMethod.FLASH_WOEUSB
- else -> null
- }
- }
-}
diff --git a/app/src/main/java/eu/depau/etchdroid/fragments/ImageLocationFragment.kt b/app/src/main/java/eu/depau/etchdroid/fragments/ImageLocationFragment.kt
deleted file mode 100644
index e527fcc..0000000
--- a/app/src/main/java/eu/depau/etchdroid/fragments/ImageLocationFragment.kt
+++ /dev/null
@@ -1,211 +0,0 @@
-package eu.depau.etchdroid.fragments
-
-import android.Manifest
-import android.app.Activity
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.net.Uri
-import android.os.Bundle
-import android.os.Environment
-import android.support.v4.app.ActivityCompat
-import android.support.v4.content.ContextCompat
-import android.support.v7.widget.LinearLayoutManager
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import com.codekidlabs.storagechooser.StorageChooser
-import eu.depau.etchdroid.R
-import eu.depau.etchdroid.StateKeeper
-import eu.depau.etchdroid.activities.WizardActivity
-import eu.depau.etchdroid.adapters.PartitionTableRecyclerViewAdapter
-import eu.depau.etchdroid.enums.FlashMethod
-import eu.depau.etchdroid.enums.ImageLocation
-import eu.depau.etchdroid.enums.WizardStep
-import eu.depau.etchdroid.img_types.DMGImage
-import eu.depau.etchdroid.kotlin_exts.getFileName
-import eu.depau.etchdroid.kotlin_exts.snackbar
-import kotlinx.android.synthetic.main.activity_main.*
-import kotlinx.android.synthetic.main.fragment_select_location.*
-import java.io.File
-
-
-/**
- * A placeholder fragment containing a simple view.
- */
-class ImageLocationFragment : WizardFragment() {
- val READ_REQUEST_CODE = 42
- val MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 29
- val TAG = "ImageLocationFragment"
- val PICKER_DIALOG_TAG = "eu.depau.etchdroid.filepicker.DIALOG_TAG"
- var issuesFound = false
-
- fun isStreamingAvailable(): Boolean {
- if (StateKeeper.imageLocation != ImageLocation.REMOTE)
- return false
- if (StateKeeper.flashMethod != FlashMethod.FLASH_DMG_API && StateKeeper.flashMethod != FlashMethod.FLASH_API)
- return false
- return true
- }
-
- override fun onRadioButtonClicked(view: View) {
- StateKeeper.imageLocation = ImageLocation.LOCAL
- activity?.fab?.show()
- pick_file_btn?.isEnabled = StateKeeper.imageLocation == ImageLocation.LOCAL
- loadImageChanges(activity as WizardActivity)
- }
-
- override fun onButtonClicked(view: View) {
- if (view.id == R.id.pick_file_btn) {
- when (StateKeeper.flashMethod) {
- FlashMethod.FLASH_API -> {
- val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
- intent.addCategory(Intent.CATEGORY_OPENABLE)
- intent.setType("*/*");
- activity?.startActivityForResult(intent, READ_REQUEST_CODE)
- }
- FlashMethod.FLASH_DMG_API -> {
- if (checkAndRequestStorageReadPerm()) {
- val sdcard = Environment.getExternalStorageDirectory().absolutePath
-
-
- val chooser = StorageChooser.Builder()
- .withActivity(activity)
- .withFragmentManager(activity!!.fragmentManager)
- .withMemoryBar(true)
- .allowCustomPath(true)
- .setType(StorageChooser.FILE_PICKER)
- .customFilter(arrayListOf("dmg"))
- .build()
- chooser.show()
- chooser.setOnSelectListener {
- StateKeeper.imageFile = Uri.fromFile(File(it))
- loadImageChanges(activity as WizardActivity)
-
- activity?.fab?.show()
- }
- }
- }
- FlashMethod.FLASH_UNETBOOTIN -> {
- }
- FlashMethod.FLASH_WOEUSB -> {
- }
- null -> {
- }
- }
- }
- }
-
- fun checkAndRequestStorageReadPerm(): Boolean {
- if ((ContextCompat.checkSelfPermission(activity!!, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) {
- if (ActivityCompat.shouldShowRequestPermissionRationale(activity!!,
- Manifest.permission.READ_EXTERNAL_STORAGE)) {
- view!!.snackbar("Storage permission is required to read DMG images")
- } else {
- ActivityCompat.requestPermissions(activity!!,
- arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
- MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE)
- }
- } else {
- // Permission granted
- return true
- }
- return false
- }
-
- override fun nextStep(view: View?) {
- if (issuesFound) {
- view?.snackbar(getString(R.string.issues_found_expl))
- return
- }
-
- if (StateKeeper.imageLocation == null) {
- view?.snackbar(getString(R.string.select_image_location))
- return
- }
-
- if (StateKeeper.imageFile == null) {
- view?.snackbar(getString(R.string.provide_image_file))
- return
- }
-
- (activity as WizardActivity).goToNewFragment(UsbDriveFragment())
- }
-
- override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
- when (requestCode) {
- MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE -> {
- onButtonClicked(pick_file_btn)
- return
- }
-
- else -> {}
- }
-
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- activity?.fab?.show()
- }
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?): View? {
- StateKeeper.currentFragment = this
- StateKeeper.wizardStep = WizardStep.SELECT_LOCATION
-
- return inflater.inflate(R.layout.fragment_select_location, container, false)
- }
-
- fun loadImageChanges(context: WizardActivity) {
- val button = pick_file_btn
- val uri = StateKeeper.imageFile ?: return
-
- val text = uri.getFileName(context)
-
- if (text != null)
- button.text = text
- else
- button.text = getString(R.string.pick_a_file)
-
- if (StateKeeper.flashMethod == FlashMethod.FLASH_DMG_API) {
- StateKeeper.imageRepr = DMGImage(uri, context)
- val imgRepr = StateKeeper.imageRepr as DMGImage
-
- if (imgRepr.tableType == null && (imgRepr.partitionTable == null || imgRepr.partitionTable?.size == 0)) {
- part_table_header.text = getString(R.string.image_is_not_dmg)
- issuesFound = true
- return
- } else {
- part_table_header.text = if (imgRepr.tableType != null) "Partition table:" else ""
- part_table_header_side.text = imgRepr.tableType?.getString(context) ?: ""
- issuesFound = false
-
- val viewAdapter = PartitionTableRecyclerViewAdapter(imgRepr.partitionTable!!)
- part_table_recycler.apply {
- setHasFixedSize(true)
- layoutManager = LinearLayoutManager(activity)
- adapter = viewAdapter
- }
- }
- }
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
- // The document selected by the user won't be returned in the intent.
- // Instead, a URI to that document will be contained in the return intent
- // provided to this method as a parameter.
- // Pull that URI using resultData.getData().
- var uri: Uri? = null
- if (data != null) {
- uri = data.getData()
- Log.d(TAG, "Uri: " + uri!!.toString())
- StateKeeper.imageFile = uri
- loadImageChanges(activity as WizardActivity)
-
- activity?.fab?.show()
- }
- }
- }
-}
diff --git a/app/src/main/java/eu/depau/etchdroid/fragments/UsbDriveFragment.kt b/app/src/main/java/eu/depau/etchdroid/fragments/UsbDriveFragment.kt
deleted file mode 100644
index a76c900..0000000
--- a/app/src/main/java/eu/depau/etchdroid/fragments/UsbDriveFragment.kt
+++ /dev/null
@@ -1,139 +0,0 @@
-package eu.depau.etchdroid.fragments
-
-import android.content.Context
-import android.hardware.usb.UsbDevice
-import android.hardware.usb.UsbManager
-import android.os.Bundle
-import android.support.v4.widget.SwipeRefreshLayout
-import android.support.v7.widget.LinearLayoutManager
-import android.support.v7.widget.RecyclerView
-import android.view.*
-import com.github.mjdev.libaums.UsbMassStorageDevice
-import eu.depau.etchdroid.R
-import eu.depau.etchdroid.StateKeeper
-import eu.depau.etchdroid.utils.ClickListener
-import eu.depau.etchdroid.activities.WizardActivity
-import eu.depau.etchdroid.activities.MainActivity
-import eu.depau.etchdroid.adapters.UsbDrivesRecyclerViewAdapter
-import eu.depau.etchdroid.kotlin_exts.name
-import eu.depau.etchdroid.kotlin_exts.snackbar
-import eu.depau.etchdroid.enums.WizardStep
-import eu.depau.etchdroid.utils.RecyclerViewTouchListener
-import kotlinx.android.synthetic.main.activity_main.*
-import kotlinx.android.synthetic.main.fragment_select_usb_drive.view.*
-
-
-/**
- * A placeholder fragment containing a simple view.
- */
-class UsbDriveFragment : WizardFragment(), SwipeRefreshLayout.OnRefreshListener {
- val TAG = "UsbDriveFragment"
-
- private lateinit var recyclerView: RecyclerView
- private lateinit var viewAdapter: UsbDrivesRecyclerViewAdapter
- private lateinit var viewManager: RecyclerView.LayoutManager
- private lateinit var refreshLayout: SwipeRefreshLayout
-
-
- override fun onRefresh() {
- loadUsbDevices()
- }
-
-
- override fun nextStep(view: View?) {
- (activity as WizardActivity).goToNewFragment(ConfirmInfoFragment())
- }
-
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?): View? {
- StateKeeper.currentFragment = this
- StateKeeper.wizardStep = WizardStep.SELECT_USB_DRIVE
-
- val view = inflater.inflate(R.layout.fragment_select_usb_drive, container, false)
-
- refreshLayout = view.usbdevs_refresh_layout
- refreshLayout.setOnRefreshListener(this)
- refreshLayout.post {
- refreshLayout.isRefreshing = true
- loadUsbDevices()
- }
-
- viewManager = LinearLayoutManager(activity)
- recyclerView = view.usbdevs_recycler_view
-
- recyclerView.addOnItemTouchListener(RecyclerViewTouchListener(activity!!, recyclerView, object : ClickListener {
- override fun onClick(view: View, position: Int) {
- val device = viewAdapter.get(position)
- val manager = activity!!.getSystemService(Context.USB_SERVICE) as UsbManager
- manager.requestPermission(device.usbDevice, (activity as MainActivity).mUsbPermissionIntent)
- }
-
- override fun onLongClick(view: View, position: Int) {}
- }))
-
- return view
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setHasOptionsMenu(true)
- }
-
- fun loadUsbDevices() {
- try {
- viewAdapter = UsbDrivesRecyclerViewAdapter(UsbMassStorageDevice.getMassStorageDevices(activity))
-
- recyclerView.apply {
- setHasFixedSize(true)
- layoutManager = viewManager
- adapter = viewAdapter
- }
- } finally {
- refreshLayout.isRefreshing = false
- }
- }
-
- override fun onFragmentAdded(activity: WizardActivity) {
- activity.fab.hide()
- }
-
- override fun onFragmentRemoving(activity: WizardActivity) {
- activity.fab.show()
- }
-
- override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
- // Inflate the menu; this adds items to the action bar if it is present.
- menuInflater.inflate(R.menu.usb_devices_menu, menu)
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- // Handle action bar item clicks here. The action bar will
- // automatically handle clicks on the Home/Up button, so long
- // as you specify a parent activity in AndroidManifest.xml.
- return when (item.itemId) {
- R.id.action_refresh -> {
- loadUsbDevices()
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- fun onUsbPermissionResult(usbDevice: UsbDevice?, granted: Boolean) {
- if (!granted) {
- if (usbDevice != null) {
- recyclerView.snackbar(getString(R.string.usb_perm_denied) + usbDevice.name)
- } else {
- recyclerView.snackbar(getString(R.string.usb_perm_denied_noname))
- }
- return
- }
-
- StateKeeper.usbDevice = usbDevice
- StateKeeper.usbMassStorageDevice = UsbMassStorageDevice.getMassStorageDevices(activity).find { it.usbDevice == usbDevice }
-
- nextStep(null)
- }
-}
-
diff --git a/app/src/main/java/eu/depau/etchdroid/fragments/WizardFragment.kt b/app/src/main/java/eu/depau/etchdroid/fragments/WizardFragment.kt
deleted file mode 100644
index 85fac52..0000000
--- a/app/src/main/java/eu/depau/etchdroid/fragments/WizardFragment.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package eu.depau.etchdroid.fragments
-
-import android.content.Intent
-import android.support.v4.app.Fragment
-import android.view.View
-import eu.depau.etchdroid.activities.WizardActivity
-
-abstract class WizardFragment() : Fragment() {
- private lateinit var wizardActivity: WizardActivity
-
- abstract fun nextStep(view: View?)
-
- open fun onFragmentAdded(activity: WizardActivity) {}
- open fun onFragmentRemoving(activity: WizardActivity) {}
- open fun onRadioButtonClicked(view: View) {}
- open fun onCheckBoxClicked(view: View) {}
- open override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {}
- open fun onButtonClicked(view: View) {}
- open override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {}
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/depau/etchdroid/kotlin_exts/ViewSnackbar.kt b/app/src/main/java/eu/depau/etchdroid/kotlin_exts/ViewSnackbar.kt
index e34c342..c90ecb6 100644
--- a/app/src/main/java/eu/depau/etchdroid/kotlin_exts/ViewSnackbar.kt
+++ b/app/src/main/java/eu/depau/etchdroid/kotlin_exts/ViewSnackbar.kt
@@ -1,6 +1,6 @@
package eu.depau.etchdroid.kotlin_exts
-import android.support.design.widget.Snackbar
+import com.google.android.material.snackbar.Snackbar
import android.view.View
fun View.snackbar(message: CharSequence, duration: Int = Snackbar.LENGTH_LONG) {
diff --git a/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt b/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt
index 094c362..e2976b4 100644
--- a/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt
+++ b/app/src/main/java/eu/depau/etchdroid/services/UsbWriteService.kt
@@ -8,7 +8,7 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.PowerManager
-import android.support.v4.app.NotificationCompat
+import androidx.core.app.NotificationCompat
import eu.depau.etchdroid.R
import eu.depau.etchdroid.kotlin_exts.toHRSize
import eu.depau.etchdroid.kotlin_exts.toHRTime
diff --git a/app/src/main/java/eu/depau/etchdroid/utils/EmptyRecyclerView.kt b/app/src/main/java/eu/depau/etchdroid/utils/EmptyRecyclerView.kt
new file mode 100644
index 0000000..b0479ce
--- /dev/null
+++ b/app/src/main/java/eu/depau/etchdroid/utils/EmptyRecyclerView.kt
@@ -0,0 +1,42 @@
+package eu.depau.etchdroid.utils
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+
+import androidx.recyclerview.widget.RecyclerView
+
+class EmptyRecyclerView : RecyclerView {
+ var emptyView: View? = null
+ set(value) {
+ field = value
+ checkIfEmpty()
+ }
+
+ private val observer: RecyclerView.AdapterDataObserver = object : RecyclerView.AdapterDataObserver() {
+ override fun onChanged() {
+ super.onChanged()
+ checkIfEmpty()
+ }
+ }
+
+ constructor(context: Context) : super(context)
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
+
+ constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
+
+ internal fun checkIfEmpty() {
+ if (emptyView != null)
+ emptyView!!.visibility = if (adapter?.itemCount ?: 0 > 0) View.GONE else View.VISIBLE
+ }
+
+ override fun setAdapter(adapter: RecyclerView.Adapter<*>?) {
+ val oldAdapter = this.adapter
+ oldAdapter?.unregisterAdapterDataObserver(observer)
+ super.setAdapter(adapter)
+ adapter?.registerAdapterDataObserver(observer)
+ checkIfEmpty()
+ }
+
+}
diff --git a/app/src/main/java/eu/depau/etchdroid/utils/RecyclerViewTouchListener.kt b/app/src/main/java/eu/depau/etchdroid/utils/RecyclerViewTouchListener.kt
index 368b8cd..474391b 100644
--- a/app/src/main/java/eu/depau/etchdroid/utils/RecyclerViewTouchListener.kt
+++ b/app/src/main/java/eu/depau/etchdroid/utils/RecyclerViewTouchListener.kt
@@ -1,7 +1,7 @@
package eu.depau.etchdroid.utils
import android.content.Context
-import android.support.v7.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView
import android.view.GestureDetector
import android.view.MotionEvent
diff --git a/app/src/main/res/drawable/ic_beta.xml b/app/src/main/res/drawable/ic_beta.xml
new file mode 100644
index 0000000..9dc7684
--- /dev/null
+++ b/app/src/main/res/drawable/ic_beta.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_dmg.xml b/app/src/main/res/drawable/ic_dmg.xml
new file mode 100644
index 0000000..48e4688
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dmg.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_raw.xml b/app/src/main/res/drawable/ic_raw.xml
new file mode 100644
index 0000000..41fcb79
--- /dev/null
+++ b/app/src/main/res/drawable/ic_raw.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_twotone_save_alt_24px.xml b/app/src/main/res/drawable/ic_twotone_save_alt_24px.xml
new file mode 100644
index 0000000..28a36b9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_twotone_save_alt_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_usb_black_200dp.xml b/app/src/main/res/drawable/ic_usb_black_200dp.xml
new file mode 100644
index 0000000..248fd72
--- /dev/null
+++ b/app/src/main/res/drawable/ic_usb_black_200dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_confirmation.xml b/app/src/main/res/layout/activity_confirmation.xml
new file mode 100644
index 0000000..7124c65
--- /dev/null
+++ b/app/src/main/res/layout/activity_confirmation.xml
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_licenses.xml b/app/src/main/res/layout/activity_licenses.xml
index be3e695..61e2f8c 100644
--- a/app/src/main/res/layout/activity_licenses.xml
+++ b/app/src/main/res/layout/activity_licenses.xml
@@ -1,5 +1,5 @@
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 3bf3c36..0000000
--- a/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_start.xml b/app/src/main/res/layout/activity_start.xml
new file mode 100644
index 0000000..c087bc1
--- /dev/null
+++ b/app/src/main/res/layout/activity_start.xml
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_usb_drive_picker.xml b/app/src/main/res/layout/activity_usb_drive_picker.xml
new file mode 100644
index 0000000..071ce4e
--- /dev/null
+++ b/app/src/main/res/layout/activity_usb_drive_picker.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_confirminfo.xml b/app/src/main/res/layout/fragment_confirminfo.xml
deleted file mode 100644
index 286b885..0000000
--- a/app/src/main/res/layout/fragment_confirminfo.xml
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_select_flash_method.xml b/app/src/main/res/layout/fragment_select_flash_method.xml
deleted file mode 100644
index 0364cc1..0000000
--- a/app/src/main/res/layout/fragment_select_flash_method.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_select_location.xml b/app/src/main/res/layout/fragment_select_location.xml
deleted file mode 100644
index f22c403..0000000
--- a/app/src/main/res/layout/fragment_select_location.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_select_usb_drive.xml b/app/src/main/res/layout/fragment_select_usb_drive.xml
deleted file mode 100644
index a4dd688..0000000
--- a/app/src/main/res/layout/fragment_select_usb_drive.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index 2fa8c13..70407c2 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -1,7 +1,7 @@