Enable DMG file selection
This commit is contained in:
parent
9b12ebf8b1
commit
c5fae3d869
8 changed files with 110 additions and 36 deletions
|
@ -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'
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue