Enable DMG file selection

This commit is contained in:
Davide Depau 2018-08-16 16:21:43 +02:00
parent 9b12ebf8b1
commit c5fae3d869
Signed by: depau
GPG key ID: C7D999B6A55EFE86
8 changed files with 110 additions and 36 deletions

View file

@ -30,9 +30,13 @@ dependencies {
implementation 'com.android.support.constraint:constraint-layout:1.1.2' implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:design:28.0.0-rc01' implementation 'com.android.support:design:28.0.0-rc01'
implementation 'com.android.support:recyclerview-v7:28.0.0-rc01' implementation 'com.android.support:recyclerview-v7:28.0.0-rc01'
implementation 'com.github.isabsent:filepicker:1.1.01'
// implementation 'com.github.mjdev:libaums:0.5.5' // implementation 'com.github.mjdev:libaums:0.5.5'
implementation project(':libaums') implementation project(':libaums')
implementation project(':dmg2img') implementation project(':dmg2img')
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

View file

@ -1,11 +1,24 @@
package eu.depau.etchdroid.abc package eu.depau.etchdroid.abc
import android.content.Intent import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.view.View import android.view.View
import com.github.isabsent.filepicker.SimpleFilePickerDialog
import eu.depau.etchdroid.StateKeeper import eu.depau.etchdroid.StateKeeper
abstract class WizardActivity : AppCompatActivity() { abstract class WizardActivity : AppCompatActivity(), SimpleFilePickerDialog.InteractionListenerString {
override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean {
if (StateKeeper.currentFragment is SimpleFilePickerDialog.InteractionListenerString)
return (StateKeeper.currentFragment as SimpleFilePickerDialog.InteractionListenerString).onResult(dialogTag, which, extras)
throw RuntimeException("Wrong fragment type")
}
override fun showListItemDialog(title: String?, folderPath: String?, mode: SimpleFilePickerDialog.CompositeMode?, dialogTag: String?) {
if (StateKeeper.currentFragment is SimpleFilePickerDialog.InteractionListenerString)
return (StateKeeper.currentFragment as SimpleFilePickerDialog.InteractionListenerString).showListItemDialog(title, folderPath, mode, dialogTag)
}
abstract fun goToNewFragment(fragment: WizardFragment) abstract fun goToNewFragment(fragment: WizardFragment)
open fun onCheckBoxClicked(view: View) { open fun onCheckBoxClicked(view: View) {

View file

@ -46,7 +46,7 @@ class ConfirmInfoFragment : WizardFragment() {
view.confirm_sel_method.text = when (StateKeeper.flashMethod) { view.confirm_sel_method.text = when (StateKeeper.flashMethod) {
FlashMethod.FLASH_API -> getString(R.string.flash_dd_usb_api) FlashMethod.FLASH_API -> getString(R.string.flash_dd_usb_api)
FlashMethod.FLASH_DD -> getString(R.string.flash_dd_root) FlashMethod.FLASH_DMG_API -> getString(R.string.flash_dmg_api)
FlashMethod.FLASH_UNETBOOTIN -> getString(R.string.flash_unetbootin) FlashMethod.FLASH_UNETBOOTIN -> getString(R.string.flash_unetbootin)
FlashMethod.FLASH_WOEUSB -> getString(R.string.flash_woeusb) FlashMethod.FLASH_WOEUSB -> getString(R.string.flash_woeusb)
else -> null else -> null

View file

@ -33,8 +33,8 @@ class FlashMethodFragment : WizardFragment() {
override fun onRadioButtonClicked(view: View) { override fun onRadioButtonClicked(view: View) {
StateKeeper.flashMethod = when (view.id) { StateKeeper.flashMethod = when (view.id) {
R.id.flash_dd_root_radio -> FlashMethod.FLASH_DD R.id.flash_dmg_api_radio -> FlashMethod.FLASH_DMG_API
R.id.flash_dd_usb_api_radio -> FlashMethod.FLASH_API R.id.flash_usb_api_radio -> FlashMethod.FLASH_API
R.id.flash_unetbootin_radio -> FlashMethod.FLASH_UNETBOOTIN R.id.flash_unetbootin_radio -> FlashMethod.FLASH_UNETBOOTIN
R.id.flash_woeusb_radio -> FlashMethod.FLASH_WOEUSB R.id.flash_woeusb_radio -> FlashMethod.FLASH_WOEUSB
else -> null else -> null

View file

@ -6,13 +6,14 @@ import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.support.design.widget.Snackbar import android.os.Environment
import android.support.v4.app.ActivityCompat import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat import android.support.v4.content.ContextCompat
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.github.isabsent.filepicker.SimpleFilePickerDialog
import eu.depau.etchdroid.R import eu.depau.etchdroid.R
import eu.depau.etchdroid.StateKeeper import eu.depau.etchdroid.StateKeeper
import eu.depau.etchdroid.abc.WizardActivity import eu.depau.etchdroid.abc.WizardActivity
@ -24,21 +25,23 @@ import eu.depau.etchdroid.values.ImageLocation
import eu.depau.etchdroid.values.WizardStep import eu.depau.etchdroid.values.WizardStep
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_select_location.* import kotlinx.android.synthetic.main.fragment_select_location.*
import kotlinx.android.synthetic.main.wizard_fragment_layout.* import java.io.File
/** /**
* A placeholder fragment containing a simple view. * A placeholder fragment containing a simple view.
*/ */
class ImageLocationFragment : WizardFragment() { class ImageLocationFragment : WizardFragment(), SimpleFilePickerDialog.InteractionListenerString {
val READ_REQUEST_CODE = 42 val READ_REQUEST_CODE = 42
val MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 29 val MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 29
val TAG = "ImageLocationFragment" val TAG = "ImageLocationFragment"
val PICKER_DIALOG_TAG = "eu.depau.etchdroid.filepicker.DIALOG_TAG"
fun isStreamingAvailable(): Boolean { fun isStreamingAvailable(): Boolean {
if (StateKeeper.imageLocation != ImageLocation.REMOTE) if (StateKeeper.imageLocation != ImageLocation.REMOTE)
return false return false
if (StateKeeper.flashMethod != FlashMethod.FLASH_DD && StateKeeper.flashMethod != FlashMethod.FLASH_API) if (StateKeeper.flashMethod != FlashMethod.FLASH_DMG_API && StateKeeper.flashMethod != FlashMethod.FLASH_API)
return false return false
return true return true
} }
@ -83,13 +86,46 @@ class ImageLocationFragment : WizardFragment() {
override fun onButtonClicked(view: View) { override fun onButtonClicked(view: View) {
if (view.id == R.id.pick_file_btn) { if (view.id == R.id.pick_file_btn) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) when (StateKeeper.flashMethod) {
intent.addCategory(Intent.CATEGORY_OPENABLE) FlashMethod.FLASH_API -> {
intent.setType("*/*"); val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
activity?.startActivityForResult(intent, READ_REQUEST_CODE) intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.setType("*/*");
activity?.startActivityForResult(intent, READ_REQUEST_CODE)
}
FlashMethod.FLASH_DMG_API -> {
if (checkAndRequestStorageReadPerm()) {
val sdcard = Environment.getExternalStorageDirectory().absolutePath
showListItemDialog("Select a DMG file", sdcard, SimpleFilePickerDialog.CompositeMode.FILE_ONLY_DIRECT_CHOICE_IMMEDIATE, PICKER_DIALOG_TAG)
}
}
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?) { override fun nextStep(view: View?) {
if (StateKeeper.imageLocation == null) { if (StateKeeper.imageLocation == null) {
view?.snackbar(getString(R.string.select_image_location)) view?.snackbar(getString(R.string.select_image_location))
@ -112,16 +148,16 @@ class ImageLocationFragment : WizardFragment() {
} }
if (StateKeeper.imageLocation == ImageLocation.REMOTE && !StateKeeper.streamingWrite) { if (StateKeeper.imageLocation == ImageLocation.REMOTE && !StateKeeper.streamingWrite) {
// Request permission to download file // // Request permission to download file
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE) // if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) { // != PackageManager.PERMISSION_GRANTED) {
//
// Permission is not granted // // Permission is not granted
ActivityCompat.requestPermissions(activity!!, // ActivityCompat.requestPermissions(activity!!,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), // arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE) // MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE)
return // return
} // }
} }
@ -130,13 +166,14 @@ class ImageLocationFragment : WizardFragment() {
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) { when (requestCode) {
MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE -> { MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE -> {
// If request is cancelled, the result arrays are empty. onButtonClicked(pick_file_btn)
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // // If request is cancelled, the result arrays are empty.
nextStep(fragment_layout) // if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
} else { // nextStep(fragment_layout)
Snackbar.make(fragment_layout, getString(R.string.storage_perm_required_explaination), Snackbar.LENGTH_LONG).show() // } else {
} // Snackbar.make(fragment_layout, getString(R.string.storage_perm_required_explaination), Snackbar.LENGTH_LONG).show()
// }
return return
} }
@ -190,6 +227,27 @@ class ImageLocationFragment : WizardFragment() {
updateFileButtonLabel(activity as WizardActivity) updateFileButtonLabel(activity as WizardActivity)
} }
} }
}
override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean {
when (dialogTag) {
PICKER_DIALOG_TAG -> {
if (extras.containsKey(SimpleFilePickerDialog.SELECTED_SINGLE_PATH)) {
val path = extras.getString(SimpleFilePickerDialog.SELECTED_SINGLE_PATH)
StateKeeper.imageFile = Uri.fromFile(File(path))
updateFileButtonLabel(activity as WizardActivity)
}
}
}
return false
}
override fun showListItemDialog(title: String?, folderPath: String?, mode: SimpleFilePickerDialog.CompositeMode?, dialogTag: String?) {
SimpleFilePickerDialog.build(folderPath, mode)
.title(title)
.filterable(true, true)
.show(this, dialogTag)
} }
} }

View file

@ -2,7 +2,7 @@ package eu.depau.etchdroid.values
enum class FlashMethod { enum class FlashMethod {
FLASH_API, FLASH_API,
FLASH_DD, FLASH_DMG_API,
FLASH_UNETBOOTIN, FLASH_UNETBOOTIN,
FLASH_WOEUSB FLASH_WOEUSB
} }

View file

@ -13,19 +13,18 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<RadioButton <RadioButton
android:id="@+id/flash_dd_usb_api_radio" android:id="@+id/flash_usb_api_radio"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:onClick="onRadioButtonClicked" android:onClick="onRadioButtonClicked"
android:text="@string/flash_dd_usb_api"/> android:text="@string/flash_dd_usb_api"/>
<RadioButton <RadioButton
android:id="@+id/flash_dd_root_radio" android:id="@+id/flash_dmg_api_radio"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:enabled="false"
android:onClick="onRadioButtonClicked" android:onClick="onRadioButtonClicked"
android:text="@string/flash_dd_root"/> android:text="@string/flash_dmg_api"/>
<RadioButton <RadioButton
android:id="@+id/flash_unetbootin_radio" android:id="@+id/flash_unetbootin_radio"

View file

@ -4,7 +4,7 @@
<string name="download_image_from_url">Download image from URL</string> <string name="download_image_from_url">Download image from URL</string>
<string name="use_local_image">Use local image</string> <string name="use_local_image">Use local image</string>
<string name="flash_dd_usb_api">Write image directly to disk (using Android API)</string> <string name="flash_dd_usb_api">Write image directly to disk (using Android API)</string>
<string name="flash_dd_root">Write using dd (requires root)</string> <string name="flash_dmg_api">Restore macOS DMG image to disk (using Android API)</string>
<string name="flash_unetbootin">Unetbootin-style flash (MBR only, requires root)</string> <string name="flash_unetbootin">Unetbootin-style flash (MBR only, requires root)</string>
<string name="flash_woeusb">Write Windows image (using WoeUSB, requires root)</string> <string name="flash_woeusb">Write Windows image (using WoeUSB, requires root)</string>
<string name="download_streaming">Stream directly to USB drive</string> <string name="download_streaming">Stream directly to USB drive</string>