From 1063a3024d835d34dea09b3c62412cb17bc77e9c Mon Sep 17 00:00:00 2001 From: Davide Depau Date: Thu, 16 Aug 2018 16:21:43 +0200 Subject: [PATCH] Enable DMG file selection --- app/build.gradle | 4 + .../eu/depau/etchdroid/abc/WizardActivity.kt | 15 ++- .../fragments/ConfirmInfoFragment.kt | 2 +- .../fragments/FlashMethodFragment.kt | 4 +- .../fragments/ImageLocationFragment.kt | 110 +++++++++++++----- .../eu/depau/etchdroid/values/FlashMethod.kt | 2 +- .../layout/fragment_select_flash_method.xml | 7 +- app/src/main/res/values/strings.xml | 2 +- 8 files changed, 110 insertions(+), 36 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6ebe23b..c8245d4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,9 +30,13 @@ dependencies { 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.github.isabsent:filepicker:1.1.01' // implementation 'com.github.mjdev:libaums:0.5.5' implementation project(':libaums') implementation project(':dmg2img') + + 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' diff --git a/app/src/main/java/eu/depau/etchdroid/abc/WizardActivity.kt b/app/src/main/java/eu/depau/etchdroid/abc/WizardActivity.kt index 3866cde..50d00c1 100644 --- a/app/src/main/java/eu/depau/etchdroid/abc/WizardActivity.kt +++ b/app/src/main/java/eu/depau/etchdroid/abc/WizardActivity.kt @@ -1,11 +1,24 @@ package eu.depau.etchdroid.abc import android.content.Intent +import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.view.View +import com.github.isabsent.filepicker.SimpleFilePickerDialog 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) open fun onCheckBoxClicked(view: View) { diff --git a/app/src/main/java/eu/depau/etchdroid/fragments/ConfirmInfoFragment.kt b/app/src/main/java/eu/depau/etchdroid/fragments/ConfirmInfoFragment.kt index 50de19d..5594bc0 100644 --- a/app/src/main/java/eu/depau/etchdroid/fragments/ConfirmInfoFragment.kt +++ b/app/src/main/java/eu/depau/etchdroid/fragments/ConfirmInfoFragment.kt @@ -46,7 +46,7 @@ class ConfirmInfoFragment : WizardFragment() { view.confirm_sel_method.text = when (StateKeeper.flashMethod) { 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_WOEUSB -> getString(R.string.flash_woeusb) else -> null diff --git a/app/src/main/java/eu/depau/etchdroid/fragments/FlashMethodFragment.kt b/app/src/main/java/eu/depau/etchdroid/fragments/FlashMethodFragment.kt index 0268816..9f9bc8e 100644 --- a/app/src/main/java/eu/depau/etchdroid/fragments/FlashMethodFragment.kt +++ b/app/src/main/java/eu/depau/etchdroid/fragments/FlashMethodFragment.kt @@ -33,8 +33,8 @@ class FlashMethodFragment : WizardFragment() { override fun onRadioButtonClicked(view: View) { StateKeeper.flashMethod = when (view.id) { - R.id.flash_dd_root_radio -> FlashMethod.FLASH_DD - R.id.flash_dd_usb_api_radio -> FlashMethod.FLASH_API + 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 index fd50f7b..331e2df 100644 --- a/app/src/main/java/eu/depau/etchdroid/fragments/ImageLocationFragment.kt +++ b/app/src/main/java/eu/depau/etchdroid/fragments/ImageLocationFragment.kt @@ -6,13 +6,14 @@ import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Bundle -import android.support.design.widget.Snackbar +import android.os.Environment import android.support.v4.app.ActivityCompat import android.support.v4.content.ContextCompat import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import com.github.isabsent.filepicker.SimpleFilePickerDialog import eu.depau.etchdroid.R import eu.depau.etchdroid.StateKeeper import eu.depau.etchdroid.abc.WizardActivity @@ -24,21 +25,23 @@ import eu.depau.etchdroid.values.ImageLocation import eu.depau.etchdroid.values.WizardStep import kotlinx.android.synthetic.main.activity_main.* 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. */ -class ImageLocationFragment : WizardFragment() { +class ImageLocationFragment : WizardFragment(), SimpleFilePickerDialog.InteractionListenerString { + 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 PICKER_DIALOG_TAG = "eu.depau.etchdroid.filepicker.DIALOG_TAG" fun isStreamingAvailable(): Boolean { if (StateKeeper.imageLocation != ImageLocation.REMOTE) 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 true } @@ -83,13 +86,46 @@ class ImageLocationFragment : WizardFragment() { override fun onButtonClicked(view: View) { if (view.id == R.id.pick_file_btn) { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.setType("*/*"); - activity?.startActivityForResult(intent, READ_REQUEST_CODE) + 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 + 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?) { if (StateKeeper.imageLocation == null) { view?.snackbar(getString(R.string.select_image_location)) @@ -112,16 +148,16 @@ class ImageLocationFragment : WizardFragment() { } if (StateKeeper.imageLocation == ImageLocation.REMOTE && !StateKeeper.streamingWrite) { - // Request permission to download file - if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED) { - - // Permission is not granted - ActivityCompat.requestPermissions(activity!!, - arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), - MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE) - return - } +// // Request permission to download file +// if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE) +// != PackageManager.PERMISSION_GRANTED) { +// +// // Permission is not granted +// ActivityCompat.requestPermissions(activity!!, +// arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), +// MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) +// return +// } } @@ -130,13 +166,14 @@ class ImageLocationFragment : WizardFragment() { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { when (requestCode) { - MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE -> { - // If request is cancelled, the result arrays are empty. - if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { - nextStep(fragment_layout) - } else { - Snackbar.make(fragment_layout, getString(R.string.storage_perm_required_explaination), Snackbar.LENGTH_LONG).show() - } + MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE -> { + onButtonClicked(pick_file_btn) +// // If request is cancelled, the result arrays are empty. +// if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { +// nextStep(fragment_layout) +// } else { +// Snackbar.make(fragment_layout, getString(R.string.storage_perm_required_explaination), Snackbar.LENGTH_LONG).show() +// } return } @@ -190,6 +227,27 @@ class ImageLocationFragment : WizardFragment() { 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) } } diff --git a/app/src/main/java/eu/depau/etchdroid/values/FlashMethod.kt b/app/src/main/java/eu/depau/etchdroid/values/FlashMethod.kt index dfad0ba..2ee0e8b 100644 --- a/app/src/main/java/eu/depau/etchdroid/values/FlashMethod.kt +++ b/app/src/main/java/eu/depau/etchdroid/values/FlashMethod.kt @@ -2,7 +2,7 @@ package eu.depau.etchdroid.values enum class FlashMethod { FLASH_API, - FLASH_DD, + FLASH_DMG_API, FLASH_UNETBOOTIN, FLASH_WOEUSB } \ 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 index f6b774a..0364cc1 100644 --- a/app/src/main/res/layout/fragment_select_flash_method.xml +++ b/app/src/main/res/layout/fragment_select_flash_method.xml @@ -13,19 +13,18 @@ android:layout_height="wrap_content"> + android:text="@string/flash_dmg_api"/> Download image from URL Use local image Write image directly to disk (using Android API) - Write using dd (requires root) + Restore macOS DMG image to disk (using Android API) Unetbootin-style flash (MBR only, requires root) Write Windows image (using WoeUSB, requires root) Stream directly to USB drive