Add licenses activity
This commit is contained in:
parent
1d0c1e82fa
commit
bbfb2e0054
9 changed files with 397 additions and 78 deletions
|
@ -3,11 +3,14 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="eu.depau.etchdroid">
|
package="eu.depau.etchdroid">
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.usb.host" />
|
<uses-feature android:name="android.hardware.usb.host"/>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
|
<uses-permission
|
||||||
|
android:name="android.permission.READ_PHONE_STATE"
|
||||||
|
tools:node="remove"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
@ -37,9 +40,95 @@
|
||||||
android:label="@string/title_activity_usb_drive_picker"
|
android:label="@string/title_activity_usb_drive_picker"
|
||||||
android:parentActivityName=".activities.StartActivity"
|
android:parentActivityName=".activities.StartActivity"
|
||||||
android:theme="@style/MaterialAppTheme">
|
android:theme="@style/MaterialAppTheme">
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="eu.depau.etchdroid.activities.StartActivity"/>
|
android:value="eu.depau.etchdroid.activities.StartActivity"/>
|
||||||
|
|
||||||
|
<!-- Open ISO files by mimetype -->
|
||||||
|
<intent-filter android:label="@string/app_name">
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data android:scheme="file"/>
|
||||||
|
<data android:scheme="content"/>
|
||||||
|
<data android:mimeType="application/x-iso9660-image"/>
|
||||||
|
<data android:host="*"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Open ISO files by extension -->
|
||||||
|
<intent-filter android:label="@string/app_name">
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data android:scheme="file"/>
|
||||||
|
<data android:scheme="content"/>
|
||||||
|
<data android:mimeType="*/*"/>
|
||||||
|
<data android:host="*"/>
|
||||||
|
<!--
|
||||||
|
Work around Android's ugly primitive PatternMatcher
|
||||||
|
implementation that can't cope with finding a . early in
|
||||||
|
the path unless it's explicitly matched.
|
||||||
|
https://stackoverflow.com/a/31028507/1124621
|
||||||
|
-->
|
||||||
|
<data android:pathPattern=".*\\.iso"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\.iso"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\.iso"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\.iso"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.iso"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.iso"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.iso"/>
|
||||||
|
<data android:pathPattern=".*\\.img"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\.img"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\.img"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\.img"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.img"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.img"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.img"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Open DMG files by mimetype -->
|
||||||
|
<intent-filter android:label="@string/app_name">
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data android:scheme="file"/>
|
||||||
|
<data android:scheme="content"/>
|
||||||
|
<data android:mimeType="application/x-apple-diskimage"/>
|
||||||
|
<data android:host="*"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Open DMG files by extension -->
|
||||||
|
<intent-filter android:label="@string/app_name">
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
|
||||||
|
<data android:scheme="file"/>
|
||||||
|
<data android:scheme="content"/>
|
||||||
|
<data android:mimeType="*/*"/>
|
||||||
|
<data android:host="*"/>
|
||||||
|
<!--
|
||||||
|
Work around Android's ugly primitive PatternMatcher
|
||||||
|
implementation that can't cope with finding a . early in
|
||||||
|
the path unless it's explicitly matched.
|
||||||
|
https://stackoverflow.com/a/31028507/1124621
|
||||||
|
-->
|
||||||
|
<data android:pathPattern=".*\\.dmg"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\.dmg"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\.dmg"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\.dmg"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.dmg"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.dmg"/>
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.dmg"/>
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.ConfirmationActivity"
|
android:name=".activities.ConfirmationActivity"
|
||||||
|
@ -53,10 +142,10 @@
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.ErrorActivity"
|
android:name=".activities.ErrorActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:label="@string/write_failed"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:taskAffinity=""
|
android:taskAffinity=""
|
||||||
android:label="@string/write_failed"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
>
|
>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,74 @@
|
||||||
package eu.depau.etchdroid.activities
|
package eu.depau.etchdroid.activities
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
||||||
import eu.depau.etchdroid.kotlin_exts.toast
|
import eu.depau.etchdroid.kotlin_exts.toast
|
||||||
|
import eu.depau.etchdroid.utils.DoNotShowAgainDialogFragment
|
||||||
import eu.depau.etchdroid.utils.NightModeHelper
|
import eu.depau.etchdroid.utils.NightModeHelper
|
||||||
|
|
||||||
|
|
||||||
abstract class ActivityBase : AppCompatActivity() {
|
abstract class ActivityBase : AppCompatActivity() {
|
||||||
protected lateinit var nightModeHelper: NightModeHelper
|
protected lateinit var nightModeHelper: NightModeHelper
|
||||||
val DISMISSED_DIALOGS_PREFS = "dismissed_dialogs"
|
val DISMISSED_DIALOGS_PREFS = "dismissed_dialogs"
|
||||||
|
val READ_REQUEST_CODE = 42
|
||||||
|
val READ_EXTERNAL_STORAGE_PERMISSION = 29
|
||||||
|
|
||||||
|
var shouldShowAndroidPieAlertDialog: Boolean
|
||||||
|
get() {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
|
||||||
|
return false
|
||||||
|
val settings = getSharedPreferences(DISMISSED_DIALOGS_PREFS, 0)
|
||||||
|
return !settings.getBoolean("Android_Pie_alert", false)
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
val settings = getSharedPreferences(DISMISSED_DIALOGS_PREFS, 0)
|
||||||
|
val editor = settings.edit()
|
||||||
|
editor.putBoolean("Android_Pie_alert", !value)
|
||||||
|
editor.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showAndroidPieAlertDialog(callback: () -> Unit) {
|
||||||
|
val dialogFragment = DoNotShowAgainDialogFragment(nightModeHelper.nightMode)
|
||||||
|
dialogFragment.title = getString(R.string.android_pie_bug)
|
||||||
|
dialogFragment.message = getString(R.string.android_pie_bug_dialog_text)
|
||||||
|
dialogFragment.positiveButton = getString(R.string.i_understand)
|
||||||
|
dialogFragment.listener = object : DoNotShowAgainDialogFragment.DialogListener {
|
||||||
|
override fun onDialogNegative(dialog: DoNotShowAgainDialogFragment, showAgain: Boolean) {}
|
||||||
|
override fun onDialogPositive(dialog: DoNotShowAgainDialogFragment, showAgain: Boolean) {
|
||||||
|
shouldShowAndroidPieAlertDialog = showAgain
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dialogFragment.show(supportFragmentManager, "DMGBetaAlertDialogFragment")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkAndRequestStorageReadPerm(): Boolean {
|
||||||
|
if ((ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) {
|
||||||
|
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
|
||||||
|
Manifest.permission.READ_EXTERNAL_STORAGE)) {
|
||||||
|
toast(getString(R.string.storage_permission_required))
|
||||||
|
} else {
|
||||||
|
ActivityCompat.requestPermissions(this,
|
||||||
|
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
|
||||||
|
READ_EXTERNAL_STORAGE_PERMISSION)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Permission granted
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
|
@ -1,30 +1,22 @@
|
||||||
package eu.depau.etchdroid.activities
|
package eu.depau.etchdroid.activities
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.content.Intent
|
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.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import com.codekidlabs.storagechooser.StorageChooser
|
import com.codekidlabs.storagechooser.StorageChooser
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
||||||
import eu.depau.etchdroid.StateKeeper
|
import eu.depau.etchdroid.StateKeeper
|
||||||
import eu.depau.etchdroid.enums.FlashMethod
|
import eu.depau.etchdroid.enums.FlashMethod
|
||||||
import eu.depau.etchdroid.kotlin_exts.snackbar
|
|
||||||
import eu.depau.etchdroid.utils.DoNotShowAgainDialogFragment
|
import eu.depau.etchdroid.utils.DoNotShowAgainDialogFragment
|
||||||
import kotlinx.android.synthetic.main.activity_start.*
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
class StartActivity : ActivityBase() {
|
class StartActivity : ActivityBase() {
|
||||||
val TAG = "StartActivity"
|
val TAG = "StartActivity"
|
||||||
val READ_REQUEST_CODE = 42
|
|
||||||
val READ_EXTERNAL_STORAGE_PERMISSION = 29
|
|
||||||
var delayedButtonClicked: Boolean = false
|
var delayedButtonClicked: Boolean = false
|
||||||
|
|
||||||
var shouldShowDMGAlertDialog: Boolean
|
var shouldShowDMGAlertDialog: Boolean
|
||||||
|
@ -39,20 +31,6 @@ class StartActivity : ActivityBase() {
|
||||||
editor.apply()
|
editor.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
var shouldShowAndroidPieAlertDialog: Boolean
|
|
||||||
get() {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
|
|
||||||
return false
|
|
||||||
val settings = getSharedPreferences(DISMISSED_DIALOGS_PREFS, 0)
|
|
||||||
return !settings.getBoolean("Android_Pie_alert", false)
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
val settings = getSharedPreferences(DISMISSED_DIALOGS_PREFS, 0)
|
|
||||||
val editor = settings.edit()
|
|
||||||
editor.putBoolean("Android_Pie_alert", !value)
|
|
||||||
editor.apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_start)
|
setContentView(R.layout.activity_start)
|
||||||
|
@ -60,7 +38,7 @@ class StartActivity : ActivityBase() {
|
||||||
|
|
||||||
fun onButtonClicked(view: View) = onButtonClicked(view, true)
|
fun onButtonClicked(view: View) = onButtonClicked(view, true)
|
||||||
|
|
||||||
private fun onButtonClicked(view: View?, showDialog: Boolean = true) {
|
private fun onButtonClicked(view: View?, showDMGDialog: Boolean = true, showAndroidPieDialog: Boolean = true) {
|
||||||
if (view != null)
|
if (view != null)
|
||||||
StateKeeper.flashMethod = when (view.id) {
|
StateKeeper.flashMethod = when (view.id) {
|
||||||
R.id.btn_image_raw -> FlashMethod.FLASH_API
|
R.id.btn_image_raw -> FlashMethod.FLASH_API
|
||||||
|
@ -68,13 +46,20 @@ class StartActivity : ActivityBase() {
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StateKeeper.flashMethod != FlashMethod.FLASH_DMG_API || !shouldShowDMGAlertDialog || !showDialog)
|
if (showAndroidPieDialog && shouldShowAndroidPieAlertDialog) {
|
||||||
showFilePicker()
|
showAndroidPieAlertDialog { onButtonClicked(view, showDMGDialog, false) }
|
||||||
else
|
return
|
||||||
showDMGBetaAlertDialog()
|
}
|
||||||
|
|
||||||
|
if (showDMGDialog && shouldShowDMGAlertDialog && StateKeeper.flashMethod == FlashMethod.FLASH_DMG_API) {
|
||||||
|
showDMGBetaAlertDialog {onButtonClicked(view, false, showAndroidPieDialog)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showFilePicker()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showDMGBetaAlertDialog() {
|
fun showDMGBetaAlertDialog(callback: () -> Unit) {
|
||||||
val dialogFragment = DoNotShowAgainDialogFragment(nightModeHelper.nightMode)
|
val dialogFragment = DoNotShowAgainDialogFragment(nightModeHelper.nightMode)
|
||||||
dialogFragment.title = getString(R.string.here_be_dragons)
|
dialogFragment.title = getString(R.string.here_be_dragons)
|
||||||
dialogFragment.message = getString(R.string.dmg_alert_dialog_text)
|
dialogFragment.message = getString(R.string.dmg_alert_dialog_text)
|
||||||
|
@ -83,32 +68,14 @@ class StartActivity : ActivityBase() {
|
||||||
override fun onDialogNegative(dialog: DoNotShowAgainDialogFragment, showAgain: Boolean) {}
|
override fun onDialogNegative(dialog: DoNotShowAgainDialogFragment, showAgain: Boolean) {}
|
||||||
override fun onDialogPositive(dialog: DoNotShowAgainDialogFragment, showAgain: Boolean) {
|
override fun onDialogPositive(dialog: DoNotShowAgainDialogFragment, showAgain: Boolean) {
|
||||||
shouldShowDMGAlertDialog = showAgain
|
shouldShowDMGAlertDialog = showAgain
|
||||||
showFilePicker()
|
callback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dialogFragment.show(supportFragmentManager, "DMGBetaAlertDialogFragment")
|
dialogFragment.show(supportFragmentManager, "DMGBetaAlertDialogFragment")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showAndroidPieAlertDialog() {
|
|
||||||
val dialogFragment = DoNotShowAgainDialogFragment(nightModeHelper.nightMode)
|
|
||||||
dialogFragment.title = getString(R.string.android_pie_bug)
|
|
||||||
dialogFragment.message = getString(R.string.android_pie_bug_dialog_text)
|
|
||||||
dialogFragment.positiveButton = getString(R.string.i_understand)
|
|
||||||
dialogFragment.listener = object : DoNotShowAgainDialogFragment.DialogListener {
|
|
||||||
override fun onDialogNegative(dialog: DoNotShowAgainDialogFragment, showAgain: Boolean) {}
|
|
||||||
override fun onDialogPositive(dialog: DoNotShowAgainDialogFragment, showAgain: Boolean) {
|
|
||||||
shouldShowAndroidPieAlertDialog = showAgain
|
|
||||||
showFilePicker(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dialogFragment.show(supportFragmentManager, "DMGBetaAlertDialogFragment")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showFilePicker(showDialog: Boolean = true) {
|
fun showFilePicker() {
|
||||||
if (showDialog && shouldShowAndroidPieAlertDialog) {
|
|
||||||
showAndroidPieAlertDialog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
when (StateKeeper.flashMethod) {
|
when (StateKeeper.flashMethod) {
|
||||||
FlashMethod.FLASH_API -> {
|
FlashMethod.FLASH_API -> {
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||||
|
@ -147,31 +114,14 @@ class StartActivity : ActivityBase() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
when (requestCode) {
|
when (requestCode) {
|
||||||
READ_EXTERNAL_STORAGE_PERMISSION -> {
|
READ_EXTERNAL_STORAGE_PERMISSION -> {
|
||||||
if (delayedButtonClicked)
|
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
onButtonClicked(null, showDialog = false)
|
if (delayedButtonClicked)
|
||||||
return
|
onButtonClicked(null, showDMGDialog = false, showAndroidPieDialog = false)
|
||||||
}
|
return
|
||||||
else -> {
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,8 +134,7 @@ class StartActivity : ActivityBase() {
|
||||||
// Pull that URI using resultData.getData().
|
// Pull that URI using resultData.getData().
|
||||||
var uri: Uri? = null
|
var uri: Uri? = null
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
uri = data.getData()
|
StateKeeper.imageFile = data.data
|
||||||
StateKeeper.imageFile = uri
|
|
||||||
|
|
||||||
nextStep()
|
nextStep()
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,10 @@ import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.hardware.usb.UsbDevice
|
import android.hardware.usb.UsbDevice
|
||||||
import android.hardware.usb.UsbManager
|
import android.hardware.usb.UsbManager
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
@ -18,12 +20,13 @@ import com.github.mjdev.libaums.UsbMassStorageDevice
|
||||||
import eu.depau.etchdroid.R
|
import eu.depau.etchdroid.R
|
||||||
import eu.depau.etchdroid.StateKeeper
|
import eu.depau.etchdroid.StateKeeper
|
||||||
import eu.depau.etchdroid.adapters.UsbDrivesRecyclerViewAdapter
|
import eu.depau.etchdroid.adapters.UsbDrivesRecyclerViewAdapter
|
||||||
import eu.depau.etchdroid.kotlin_exts.name
|
import eu.depau.etchdroid.enums.FlashMethod
|
||||||
import eu.depau.etchdroid.kotlin_exts.snackbar
|
import eu.depau.etchdroid.kotlin_exts.*
|
||||||
import eu.depau.etchdroid.utils.ClickListener
|
import eu.depau.etchdroid.utils.ClickListener
|
||||||
import eu.depau.etchdroid.utils.EmptyRecyclerView
|
import eu.depau.etchdroid.utils.EmptyRecyclerView
|
||||||
import eu.depau.etchdroid.utils.RecyclerViewTouchListener
|
import eu.depau.etchdroid.utils.RecyclerViewTouchListener
|
||||||
import kotlinx.android.synthetic.main.activity_usb_drive_picker.*
|
import kotlinx.android.synthetic.main.activity_usb_drive_picker.*
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class UsbDrivePickerActivity : ActivityBase(), SwipeRefreshLayout.OnRefreshListener {
|
class UsbDrivePickerActivity : ActivityBase(), SwipeRefreshLayout.OnRefreshListener {
|
||||||
val USB_PERMISSION = "eu.depau.etchdroid.USB_PERMISSION"
|
val USB_PERMISSION = "eu.depau.etchdroid.USB_PERMISSION"
|
||||||
|
@ -35,8 +38,68 @@ class UsbDrivePickerActivity : ActivityBase(), SwipeRefreshLayout.OnRefreshListe
|
||||||
private lateinit var refreshLayout: SwipeRefreshLayout
|
private lateinit var refreshLayout: SwipeRefreshLayout
|
||||||
|
|
||||||
|
|
||||||
|
fun handleIntent(intent: Intent) {
|
||||||
|
if (intent.action == Intent.ACTION_VIEW) {
|
||||||
|
val uri = intent.data
|
||||||
|
val ext = uri?.getExtension(contentResolver)
|
||||||
|
|
||||||
|
when (ext) {
|
||||||
|
in listOf("iso", "img") -> {
|
||||||
|
StateKeeper.flashMethod = FlashMethod.FLASH_API
|
||||||
|
StateKeeper.imageFile = uri
|
||||||
|
}
|
||||||
|
"dmg" -> {
|
||||||
|
val path: String?
|
||||||
|
try {
|
||||||
|
path = uri.getFilePath(this)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
toast(getString(R.string.cannot_find_file_in_storage))
|
||||||
|
// Rethrow exception so it's logged in Google Developer Console
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
if (path == null) {
|
||||||
|
toast(getString(R.string.cannot_find_file_in_storage))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
StateKeeper.flashMethod = FlashMethod.FLASH_DMG_API
|
||||||
|
StateKeeper.imageFile = Uri.fromFile(File(path))
|
||||||
|
|
||||||
|
checkAndRequestStorageReadPerm()
|
||||||
|
}
|
||||||
|
null -> {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
toast(getString(R.string.file_type_not_supported))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldShowAndroidPieAlertDialog)
|
||||||
|
showAndroidPieAlertDialog {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
|
when (requestCode) {
|
||||||
|
READ_EXTERNAL_STORAGE_PERMISSION -> {
|
||||||
|
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
|
||||||
|
toast(getString(R.string.storage_permission_required))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
handleIntent(intent)
|
||||||
|
|
||||||
setContentView(R.layout.activity_usb_drive_picker)
|
setContentView(R.layout.activity_usb_drive_picker)
|
||||||
actionBar?.setDisplayHomeAsUpEnabled(true)
|
actionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
package eu.depau.etchdroid.exceptions
|
||||||
|
|
||||||
|
class CannotGetFilePathException(cause: Exception) : Exception(cause)
|
|
@ -0,0 +1,16 @@
|
||||||
|
package eu.depau.etchdroid.kotlin_exts
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.net.Uri
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun Uri.getExtension(contentResolver: ContentResolver): String {
|
||||||
|
return when (scheme) {
|
||||||
|
ContentResolver.SCHEME_CONTENT -> {
|
||||||
|
val mime = MimeTypeMap.getSingleton()
|
||||||
|
mime.getExtensionFromMimeType(contentResolver.getType(this))!!
|
||||||
|
}
|
||||||
|
else -> MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(File(path)).toString())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package eu.depau.etchdroid.kotlin_exts
|
||||||
|
|
||||||
|
import android.content.ContentUris
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.DocumentsContract
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import eu.depau.etchdroid.exceptions.CannotGetFilePathException
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a file path from a Uri. This will get the the path for Storage Access
|
||||||
|
* Framework Documents, as well as the _data field for the MediaStore and
|
||||||
|
* other file-based ContentProviders.
|
||||||
|
*
|
||||||
|
* @param context The context.
|
||||||
|
* @author paulburke
|
||||||
|
*
|
||||||
|
* https://stackoverflow.com/a/27271131/1124621
|
||||||
|
*/
|
||||||
|
fun Uri.getFilePath(context: Context): String? {
|
||||||
|
val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isKitKat && DocumentsContract.isDocumentUri(context, this)) {
|
||||||
|
// DocumentProvider
|
||||||
|
|
||||||
|
if (isExternalStorageDocument) {
|
||||||
|
// ExternalStorageProvider
|
||||||
|
|
||||||
|
val docId = DocumentsContract.getDocumentId(this)
|
||||||
|
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
|
val type = split[0]
|
||||||
|
|
||||||
|
if (type.equals("primary", ignoreCase = true)) {
|
||||||
|
return Environment.getExternalStorageDirectory().path + "/" + split[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO handle non-primary volumes
|
||||||
|
|
||||||
|
} else if (isDownloadsDocument) {
|
||||||
|
// DownloadsProvider
|
||||||
|
|
||||||
|
val id = DocumentsContract.getDocumentId(this)
|
||||||
|
|
||||||
|
if (id.startsWith("raw:/"))
|
||||||
|
return Uri.parse(id).path
|
||||||
|
|
||||||
|
val contentUri = ContentUris.withAppendedId(
|
||||||
|
Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
|
||||||
|
|
||||||
|
|
||||||
|
return contentUri.getDataColumn(context, null, null)
|
||||||
|
|
||||||
|
} else if (isMediaDocument) {
|
||||||
|
// MediaProvider
|
||||||
|
|
||||||
|
val docId = DocumentsContract.getDocumentId(this)
|
||||||
|
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
|
val type = split[0]
|
||||||
|
|
||||||
|
val contentUri = when (type) {
|
||||||
|
"image" -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||||
|
"video" -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
||||||
|
"audio" -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
val selection = "_id=?"
|
||||||
|
val selectionArgs = arrayOf(split[1])
|
||||||
|
|
||||||
|
return contentUri?.getDataColumn(context, selection, selectionArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ("content".equals(scheme, ignoreCase = true)) {
|
||||||
|
// MediaStore (and general)
|
||||||
|
|
||||||
|
return getDataColumn(context, null, null)
|
||||||
|
|
||||||
|
} else if ("file".equals(scheme, ignoreCase = true)) {
|
||||||
|
// File
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Wrap into own exception to make debugging easier
|
||||||
|
throw CannotGetFilePathException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the data column for this Uri. This is useful for
|
||||||
|
* MediaStore Uris, and other file-based ContentProviders.
|
||||||
|
*
|
||||||
|
* @param context The context.
|
||||||
|
* @param selection (Optional) Filter used in the query.
|
||||||
|
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||||
|
* @return The value of the _data column, which is typically a file path.
|
||||||
|
*/
|
||||||
|
fun Uri.getDataColumn(context: Context, selection: String?, selectionArgs: Array<String>?): String? {
|
||||||
|
val column = "_data"
|
||||||
|
val projection = arrayOf(column)
|
||||||
|
|
||||||
|
context.contentResolver.query(this, projection, selection, selectionArgs, null)?.use {
|
||||||
|
if (it.moveToFirst()) {
|
||||||
|
val columnIndex = it.getColumnIndexOrThrow(column)
|
||||||
|
return it.getString(columnIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the Uri authority is ExternalStorageProvider.
|
||||||
|
*/
|
||||||
|
val Uri.isExternalStorageDocument: Boolean
|
||||||
|
get() = "com.android.externalstorage.documents" == authority
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the Uri authority is DownloadsProvider.
|
||||||
|
*/
|
||||||
|
val Uri.isDownloadsDocument: Boolean
|
||||||
|
get() = "com.android.providers.downloads.documents" == authority
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the Uri authority is MediaProvider.
|
||||||
|
*/
|
||||||
|
val Uri.isMediaDocument: Boolean
|
||||||
|
get() = "com.android.providers.media.documents" == authority
|
|
@ -108,4 +108,8 @@
|
||||||
<string name="android_pie_bug_dialog_text">A causa di un bug di Android 9, alcune scritture potrebbero fallire.\nSe appare un messaggio di errore \"Scrittura fallita\", riavvia il dispositivo e prova di nuovo.</string>
|
<string name="android_pie_bug_dialog_text">A causa di un bug di Android 9, alcune scritture potrebbero fallire.\nSe appare un messaggio di errore \"Scrittura fallita\", riavvia il dispositivo e prova di nuovo.</string>
|
||||||
<string name="reset_warnings">Reimposta tutti gli avvisi</string>
|
<string name="reset_warnings">Reimposta tutti gli avvisi</string>
|
||||||
<string name="warnings_reset">Tutti gli avvisi sono stati ripristinati</string>
|
<string name="warnings_reset">Tutti gli avvisi sono stati ripristinati</string>
|
||||||
|
<string name="flash_iso_image">Scrivi ISO su USB</string>
|
||||||
|
<string name="file_type_not_supported">Tipo di file non supportato</string>
|
||||||
|
<string name="cannot_find_file_in_storage">Impossibile trovare file nella memoria interna. Prova ad aprirlo da dentro l\'app.</string>
|
||||||
|
<string name="storage_permission_required">Il permesso per l\'archiviazione è richiesto per leggere i file DMG</string>
|
||||||
</resources>
|
</resources>
|
|
@ -107,4 +107,8 @@
|
||||||
<string name="android_pie_bug_dialog_text">There is a bug on Android 9 which causes some writes to fail.\nIf it says \"Write failed\", reboot your device and try again.</string>
|
<string name="android_pie_bug_dialog_text">There is a bug on Android 9 which causes some writes to fail.\nIf it says \"Write failed\", reboot your device and try again.</string>
|
||||||
<string name="reset_warnings">Reset all warnings</string>
|
<string name="reset_warnings">Reset all warnings</string>
|
||||||
<string name="warnings_reset">All warning dialogs restored</string>
|
<string name="warnings_reset">All warning dialogs restored</string>
|
||||||
|
<string name="flash_iso_image">Flash ISO to USB</string>
|
||||||
|
<string name="file_type_not_supported">File type not supported</string>
|
||||||
|
<string name="cannot_find_file_in_storage">Cannot find file in internal storage. Try opening it from within the app.</string>
|
||||||
|
<string name="storage_permission_required">Storage permission is required to read DMG images</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Add table
Reference in a new issue