Complete app redesign

- Switched to AndroidX namespace
- Switched from Fragments to Activities
- Automatically detects USB drives on (dis)connection
- UX is simpler, less taps are needed
This commit is contained in:
Davide Depau 2018-09-01 03:06:06 +02:00
parent 309434ebae
commit 53dc07490c
45 changed files with 1072 additions and 1017 deletions

View file

@ -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'
}

View file

@ -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

View file

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="eu.depau.etchdroid">
<uses-feature android:name="android.hardware.usb.host" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
@ -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">
<activity
android:name=".activities.MainActivity"
android:name=".activities.StartActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
android:theme="@style/MaterialAppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".activities.LicensesActivity"
android:label="@string/licenses"
android:theme="@style/MaterialAppTheme">
</activity>
<activity
android:name=".activities.UsbDrivePickerActivity"
android:label="@string/title_activity_usb_drive_picker"
android:parentActivityName=".activities.StartActivity"
android:theme="@style/MaterialAppTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="eu.depau.etchdroid.activities.StartActivity"/>
</activity>
<activity
android:name=".activities.ConfirmationActivity"
android:label="@string/title_activity_confirmation"
android:parentActivityName=".activities.UsbDrivePickerActivity"
android:theme="@style/MaterialAppTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="eu.depau.etchdroid.activities.UsbDrivePickerActivity"/>
</activity>
<service
android:name=".services.UsbApiImgWriteService"
@ -30,9 +56,6 @@
<service
android:name=".services.UsbApiDmgWriteService"
android:exported="false"/>
<activity android:name=".activities.LicensesActivity">
</activity>
</application>
</manifest>

View file

@ -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

View file

@ -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)
}
}
}

View file

@ -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()
}
}
}

View file

@ -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)

View file

@ -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)
}
}
}

View file

@ -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<out String>, 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)
}
}

View file

@ -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)
}
}

View file

@ -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<out String>, grantResults: IntArray) {
StateKeeper.currentFragment?.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
StateKeeper.currentFragment?.onActivityResult(requestCode, resultCode, data)
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
}
}

View file

@ -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
}
}
}

View file

@ -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<out String>, 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()
}
}
}
}

View file

@ -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)
}
}

View file

@ -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<out String>, grantResults: IntArray) {}
}

View file

@ -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) {

View file

@ -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

View file

@ -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()
}
}

View file

@ -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

View file

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="64"
android:viewportHeight="64">
<path
android:pathData="m0,0 l64,64L64,34.6L29.4,0Z"
android:strokeAlpha="1"
android:strokeWidth="3.24922633"
android:fillColor="#c62828"
android:strokeColor="#00000000"
android:fillAlpha="1"
android:strokeLineCap="square"/>
<path
android:pathData="m29.8847,6.8837l3.1187,3.1187q0.6608,0.6608 0.9275,1.5072 0.2782,0.8579 0.1043,1.7043 -0.1739,0.8463 -0.7884,1.4608 -0.6145,0.6145 -1.3796,0.7536 -0.7536,0.1507 -1.484,-0.1159l-0.0696,0.0696q0.4058,0.8463 0.2782,1.739 -0.1159,0.9043 -0.8,1.5883 -0.684,0.684 -1.5767,0.8579 -0.8811,0.1855 -1.7854,-0.1159 -0.8811,-0.3014 -1.5883,-1.0086l-3.2578,-3.2578zM29.4674,13.4225q0.5565,0.5565 1.2405,0.5449 0.6956,0 1.1478,-0.4522 0.4522,-0.4522 0.4522,-1.1246 0.0232,-0.6724 -0.5101,-1.2057l-1.8086,-1.8086l-2.2839,2.2839zM26.0473,17.2368q0.5913,0.5913 1.3101,0.5681 0.7304,-0.0116 1.2173,-0.4985 0.4985,-0.4985 0.4869,-1.2289 0.0116,-0.7304 -0.6029,-1.3449l-1.9129,-1.9129l-2.4578,2.4578z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillAlpha="1"/>
<path
android:pathData="m37.176,14.175l5.0432,5.0432l-1.2405,1.2405l-3.7447,-3.7447l-2.2955,2.2955l3.3737,3.3737l-1.2289,1.2289l-3.3737,-3.3737l-2.2955,2.2955l3.7447,3.7447l-1.2405,1.2405l-5.0432,-5.0432z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillAlpha="1"/>
<path
android:pathData="m44.2738,23.7538l-2.3187,-2.3187l1.2405,-1.2405l5.9359,5.9359l-1.2405,1.2405l-2.3187,-2.3187l-7.0605,7.0605l-1.2985,-1.2985z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillAlpha="1"/>
<path
android:pathData="m51.972,28.9709l1.484,1.484l-5.1823,11.4197l-1.4376,-1.4376l1.3796,-2.8636l-3.3505,-3.3505l-2.8752,1.368l-1.4376,-1.4376zM48.9808,35.9271 L50.5692,32.5765 51.2068,31.2897l-0.0696,-0.0696l-1.2869,0.6376 -3.3505,1.5883z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillAlpha="1"/>
</vector>

View file

@ -0,0 +1,16 @@
<vector android:height="96dp" android:viewportHeight="512"
android:viewportWidth="512" android:width="96dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="0.3" android:fillColor="#FF000000"
android:pathData="m170.667,341.333h256v-256h-256z"
android:strokeAlpha="0.3" android:strokeWidth="21.33333206"/>
<path android:fillColor="#FF000000"
android:pathData="m426.667,42.667h-256c-23.467,0 -42.667,19.2 -42.667,42.667v256C128,364.8 147.2,384 170.667,384h256c23.467,0 42.667,-19.2 42.667,-42.667v-256c0,-23.467 -19.2,-42.667 -42.667,-42.667zM426.667,341.333h-256v-256h256z" android:strokeWidth="21.33333206"/>
<path android:fillColor="#FF000000"
android:pathData="M85.333,128L42.667,128v298.667c0,23.467 19.2,42.667 42.667,42.667L384,469.333L384,426.667L85.333,426.667Z" android:strokeWidth="21.33333206"/>
<path android:fillColor="#000000"
android:pathData="m277.992,209.953l11.969,0l0,41.891l17.953,0l0,-42.011l11.969,0L319.883,263.814l17.953,0L337.837,203.969C337.837,197.386 332.451,192 325.868,192l-53.86,0C265.424,192 260.038,197.386 260.038,203.969L260.038,263.814l17.953,0z" android:strokeWidth="0.83333331"/>
<path android:fillAlpha="1" android:fillColor="#000000"
android:pathData="M394.169,191.68L358.263,191.68c-7.181,0 -11.969,5.985 -11.969,11.969l0,47.876c0,5.984 4.788,11.969 11.969,11.969l35.907,0c7.181,0 11.969,-5.985 11.969,-11.969l0,-23.938l-17.953,0l0,17.953l-23.938,0l0,-35.907l41.891,0L406.139,203.649c0,-5.984 -4.788,-11.969 -11.969,-11.969z" android:strokeWidth="0.83333331"/>
<path android:fillColor="#000000"
android:pathData="M233.098,192L191.195,192L191.195,263.814l41.903,0c10.24,0 17.92,-7.815 17.92,-17.92L251.017,209.92c0,-10.105 -7.68,-17.92 -17.92,-17.92zM233.098,245.894L209.115,245.894L209.115,209.92l23.983,0z" android:strokeWidth="0.93808633"/>
</vector>

View file

@ -0,0 +1,13 @@
<vector android:height="96dp" android:viewportHeight="512"
android:viewportWidth="512" android:width="96dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="0.3" android:fillColor="#FF000000"
android:pathData="M171.1,340.9L427.3,340.9L427.3,84.7L171.1,84.7Z" android:strokeAlpha="0.3"/>
<path android:fillColor="#FF000000" android:pathData="M427.3,42L171.1,42C147.615,42 128.4,61.215 128.4,84.7l0,256.2c0,23.485 19.215,42.7 42.7,42.7l256.2,0c23.485,0 42.7,-19.215 42.7,-42.7L470,84.7C470,61.215 450.785,42 427.3,42ZM427.3,340.9L171.1,340.9L171.1,84.7l256.2,0z"/>
<path android:fillColor="#FF000000" android:pathData="M85.7,127.4L43,127.4l0,298.9c0,23.485 19.215,42.7 42.7,42.7L384.6,469L384.6,426.3L85.7,426.3Z"/>
<path android:fillColor="#FF000000"
android:pathData="M253.711,221.384L253.711,209.384c0,-10.247 -7.82,-17.932 -17.932,-17.932L193.847,191.451L193.847,263.316l17.932,0l0,-24l13.753,0l10.247,24l17.932,0l-10.786,-25.213c5.932,-2.966 10.786,-9.573 10.786,-16.719zM235.779,221.384L211.779,221.384L211.779,209.384l24,0z" android:strokeWidth="0.63151968"/>
<path android:fillColor="#FF000000"
android:pathData="m280.71,245.35l23.955,0l0,17.966l17.966,0L322.631,203.429c0,-6.588 -5.39,-11.977 -11.977,-11.977l-35.932,0c-6.588,0 -11.977,5.39 -11.977,11.977L262.744,263.316l17.966,0zM280.71,209.418l23.955,0L304.665,227.384l-23.955,0z" android:strokeWidth="0.56099999"/>
<path android:fillColor="#FF000000"
android:pathData="m332.835,191.451l0,59.935c0,6.546 5.383,11.929 11.929,11.929l47.861,0c6.546,0 11.929,-5.383 11.929,-11.929L404.553,191.451L389.57,191.451L389.57,245.422L376.186,245.422L376.186,203.38l-14.984,0l0,42.187l-13.529,0L347.673,191.451Z" android:strokeWidth="0.68137652"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19,12v7H5v-7H3v7c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-7H19zM13,12.67l2.59,-2.58L17,11.5l-5,5l-5,-5l1.41,-1.41L11,12.67V3h2V12.67z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:alpha="0.4" android:height="200dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="200dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M15,7v4h1v2h-3V5h2l-3,-4 -3,4h2v8H8v-2.07c0.7,-0.37 1.2,-1.08 1.2,-1.93 0,-1.21 -0.99,-2.2 -2.2,-2.2 -1.21,0 -2.2,0.99 -2.2,2.2 0,0.85 0.5,1.56 1.2,1.93V13c0,1.11 0.89,2 2,2h3v3.05c-0.71,0.37 -1.2,1.1 -1.2,1.95 0,1.22 0.99,2.2 2.2,2.2 1.21,0 2.2,-0.98 2.2,-2.2 0,-0.85 -0.49,-1.58 -1.2,-1.95V15h3c1.11,0 2,-0.89 2,-2v-2h1V7h-4z"/>
</vector>

View file

@ -0,0 +1,219 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.ConfirmationActivity">
<androidx.core.widget.NestedScrollView
android:id="@+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativeLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/row_padding"
>
<TextView
android:id="@+id/confirm_sel_method_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:ellipsize="end"
android:text="@string/selected_method"
android:textColor="@color/name"
android:textSize="16sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/confirm_sel_method"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/confirm_sel_method_title"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/row_padding"
>
<TextView
android:id="@+id/confirm_sel_image_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:ellipsize="end"
android:text="@string/selected_image"
android:textColor="@color/name"
android:textSize="16sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/confirm_sel_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/confirm_sel_image_title"/>
<TextView
android:id="@+id/confirm_sel_image_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:textColor="@color/info"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativeLayout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/row_padding"
>
<TextView
android:id="@+id/confirm_sel_usbdev_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:ellipsize="end"
android:text="@string/selected_usbdev"
android:textColor="@color/name"
android:textSize="16sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/confirm_sel_usbdev"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/confirm_sel_usbdev_title"/>
<TextView
android:id="@+id/confirm_sel_usbdev_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:textColor="@color/info"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/row_padding"
>
<TextView
android:id="@+id/confirm_extra_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:ellipsize="end"
android:textColor="@color/name"
android:textSize="16sp"/>
</RelativeLayout>
<TextView
android:id="@+id/part_table_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/row_padding"
android:textColor="@color/name"
android:textSize="16sp"
android:textStyle="bold"
/>
<TextView
android:id="@+id/part_table_header_side"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/row_padding"
android:textColor="@color/info"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/part_table_recycler"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/row_padding"
app:layout_constraintTop_toBottomOf="@+id/relativeLayout2"
/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/confirm_fab"
style="@style/Widget.MaterialComponents.FloatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:onClick="onButtonClicked"
app:srcCompat="@drawable/ic_twotone_save_alt_24px"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@ -7,11 +7,11 @@
android:layout_height="match_parent"
tools:context=".activities.LicensesActivity">
<android.support.v7.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/licenses_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"/>
</android.support.constraint.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<include layout="@layout/wizard_fragment_layout"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@drawable/ic_navigate_next_black_24dp"/>
</android.support.design.widget.CoordinatorLayout>

View file

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activities.StartActivity">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".fragments.FlashMethodFragment"
tools:showIn="@layout/activity_start">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:layout_marginLeft="@dimen/card_margin"
android:layout_marginTop="@dimen/card_margin"
android:layout_marginRight="@dimen/card_margin"
android:minHeight="200dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/btn_image_raw"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:onClick="onButtonClicked">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="@drawable/ic_raw"/>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/imageView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.502">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/write_image_or_iso"
android:textColor="@color/name"
android:textStyle="bold"/>
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/textView"
android:text="@string/raw_image_desc"/>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:layout_marginLeft="@dimen/card_margin"
android:layout_marginTop="@dimen/card_margin"
android:layout_marginRight="@dimen/card_margin"
android:layout_marginBottom="@dimen/card_margin"
android:minHeight="200dp">
<!--
android:background="?attr/selectableItemBackground"-->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/btn_image_dmg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:onClick="onButtonClicked">
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="@drawable/ic_dmg"/>
<ImageView
android:id="@+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="-2dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_beta"/>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="36dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/imageView2"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/write_apple_dmg"
android:textColor="@color/name"
android:textStyle="bold"/>
<TextView
android:id="@+id/textView5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/textView4"
android:text="@string/dmg_image_desc"/>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/usbdevs_swiperefreshlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.UsbDrivePickerActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<eu.depau.etchdroid.utils.EmptyRecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/usbdevs_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"/>
<TextView
android:id="@+id/usbdevs_recycler_empty_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="@string/no_usb_drives_detected"
android:textAlignment="center"
android:drawableTop="@drawable/ic_usb_black_200dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View file

@ -1,144 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".fragments.ConfirmInfoFragment">
<!--tools:showIn="@layout/activity_main"-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="@dimen/row_padding"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding">
<TextView
android:id="@+id/confirm_sel_method_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:ellipsize="end"
android:text="@string/selected_method"
android:textColor="@color/name"
android:textSize="16sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/confirm_sel_method"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/confirm_sel_method_title"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="@dimen/row_padding"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding">
<TextView
android:id="@+id/confirm_sel_image_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:ellipsize="end"
android:text="@string/selected_image"
android:textColor="@color/name"
android:textSize="16sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/confirm_sel_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/confirm_sel_image_title"/>
<TextView
android:id="@+id/confirm_sel_image_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:textColor="@color/info"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="@dimen/row_padding"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding">
<TextView
android:id="@+id/confirm_sel_usbdev_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:ellipsize="end"
android:text="@string/selected_usbdev"
android:textColor="@color/name"
android:textSize="16sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/confirm_sel_usbdev"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/confirm_sel_usbdev_title"/>
<TextView
android:id="@+id/confirm_sel_usbdev_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:textColor="@color/info"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="@dimen/row_padding"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding">
<TextView
android:id="@+id/confirm_extra_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:ellipsize="end"
android:textColor="@color/name"
android:textSize="16sp"/>
</RelativeLayout>
</LinearLayout>

View file

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".fragments.FlashMethodFragment"
tools:showIn="@layout/activity_main">
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/flash_usb_api_radio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onRadioButtonClicked"
android:text="@string/flash_dd_usb_api"/>
<RadioButton
android:id="@+id/flash_dmg_api_radio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onRadioButtonClicked"
android:text="@string/flash_dmg_api"/>
<RadioButton
android:id="@+id/flash_unetbootin_radio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:onClick="onRadioButtonClicked"
android:text="@string/flash_unetbootin"/>
<RadioButton
android:id="@+id/flash_woeusb_radio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:onClick="onRadioButtonClicked"
android:text="@string/flash_woeusb"/>
</RadioGroup>
</LinearLayout>

View file

@ -1,59 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".fragments.FlashMethodFragment">
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/pick_file_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onButtonClicked"
android:text="@string/pick_a_file"/>
</RadioGroup>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:paddingBottom="@dimen/row_padding"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/row_padding">
<TextView
android:id="@+id/part_table_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/grid_padding"
android:textColor="@color/name"
android:textSize="16sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/part_table_header_side"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/grid_padding"
android:textColor="@color/info"/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/part_table_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/usbdevs_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/usbdevs_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"/>
</android.support.v4.widget.SwipeRefreshLayout>

View file

@ -1,7 +1,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="eu.depau.etchdroid.activities.MainActivity">
tools:context="eu.depau.etchdroid.activities.StartActivity">
<item
android:id="@+id/action_licenses"
android:orderInCategory="100"

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="colorPrimary">#4caf50</color>
<color name="colorPrimaryDark">#087f23</color>
<color name="colorAccent">#4caf50</color>
<color name="info">#999999</color>
<color name="name">#222222</color>
</resources>

View file

@ -4,4 +4,5 @@
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="row_padding">10dp</dimen>
<dimen name="grid_padding">5dp</dimen>
<dimen name="card_margin">12dp</dimen>
</resources>

View file

@ -24,7 +24,7 @@
<string name="please_select_writing_method">Please select writing method</string>
<string name="image_bigger_than_usb">Image is bigger than the USB drive, so it can\'t be written</string>
<string name="cant_read_usbdev">Cannot read USB device</string>
<string name="tap_next_to_write">Tap Next to write the image to the USB drive</string>
<string name="tap_next_to_write">Tap Write to write the image to the USB drive</string>
<string name="notchan_writestatus_title">USB write status</string>
<string name="notchan_writestatus_desc">Used to display the status of images being written to USB drives</string>
<string name="notif_initializing">Initializing...</string>
@ -81,4 +81,15 @@
<string name="license_custom">Custom</string>
<string name="licenses">Licenses</string>
<string name="dmg2img_license_desc">Converts compressed Apple® DMG images</string>
<string name="title_activity_usb_drive_picker">Select USB drive</string>
<string name="title_activity_confirmation">Ready to write</string>
<string name="cannot_write">Cannot write image to USB drive</string>
<string name="check_notification_progress">Check notification for progress</string>
<string name="partition_table_title">Partition table:</string>
<string name="could_not_access_usb_error">Could not access USB device. Maybe you ran the app previously and it crashed? Remove and reinsert the USB drive, then restart the app.</string>
<string name="write_image_or_iso">Write raw image or ISO</string>
<string name="raw_image_desc">Good for GNU/Linux distribution ISOs and disk images created on Windows or Linux</string>
<string name="write_apple_dmg">Write Apple® DMG image</string>
<string name="dmg_image_desc">Restore an image created with macOS Disk Utility</string>
<string name="no_usb_drives_detected">No USB drives detected</string>
</resources>

View file

@ -1,8 +1,7 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Base.Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<style name="MaterialAppTheme" parent="Base.Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
@ -10,13 +9,14 @@
<item name="floatingActionButtonStyle">@style/Widget.Design.FloatingActionButton</item>
</style>
<style name="AppTheme.NoActionBar">
<style name="MaterialAppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
<style name="MaterialAppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
<style name="MaterialAppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
</resources>

View file

@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.2.50'
ext.kotlin_version = '1.2.51'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'com.android.tools.build:gradle:3.2.0-rc02'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// For libaums

View file

@ -6,6 +6,8 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit

View file

@ -1,6 +1,6 @@
#Sat Aug 11 18:29:21 CEST 2018
#Thu Aug 30 23:16:18 CEST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip