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"
|
||||
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.WAKE_LOCK"/>
|
||||
<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
|
||||
android:allowBackup="false"
|
||||
|
@ -37,9 +40,95 @@
|
|||
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"/>
|
||||
|
||||
<!-- 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
|
||||
android:name=".activities.ConfirmationActivity"
|
||||
|
@ -53,10 +142,10 @@
|
|||
|
||||
<activity
|
||||
android:name=".activities.ErrorActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:label="@string/write_failed"
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity=""
|
||||
android:label="@string/write_failed"
|
||||
android:excludeFromRecents="true"
|
||||
>
|
||||
</activity>
|
||||
|
||||
|
|
|
@ -1,18 +1,74 @@
|
|||
package eu.depau.etchdroid.activities
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import eu.depau.etchdroid.R
|
||||
import eu.depau.etchdroid.kotlin_exts.toast
|
||||
import eu.depau.etchdroid.utils.DoNotShowAgainDialogFragment
|
||||
import eu.depau.etchdroid.utils.NightModeHelper
|
||||
|
||||
|
||||
abstract class ActivityBase : AppCompatActivity() {
|
||||
protected lateinit var nightModeHelper: NightModeHelper
|
||||
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?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
|
@ -1,30 +1,22 @@
|
|||
package eu.depau.etchdroid.activities
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
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 eu.depau.etchdroid.utils.DoNotShowAgainDialogFragment
|
||||
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: Boolean = false
|
||||
|
||||
var shouldShowDMGAlertDialog: Boolean
|
||||
|
@ -39,20 +31,6 @@ class StartActivity : ActivityBase() {
|
|||
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?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_start)
|
||||
|
@ -60,7 +38,7 @@ class StartActivity : ActivityBase() {
|
|||
|
||||
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)
|
||||
StateKeeper.flashMethod = when (view.id) {
|
||||
R.id.btn_image_raw -> FlashMethod.FLASH_API
|
||||
|
@ -68,13 +46,20 @@ class StartActivity : ActivityBase() {
|
|||
else -> null
|
||||
}
|
||||
|
||||
if (StateKeeper.flashMethod != FlashMethod.FLASH_DMG_API || !shouldShowDMGAlertDialog || !showDialog)
|
||||
showFilePicker()
|
||||
else
|
||||
showDMGBetaAlertDialog()
|
||||
if (showAndroidPieDialog && shouldShowAndroidPieAlertDialog) {
|
||||
showAndroidPieAlertDialog { onButtonClicked(view, showDMGDialog, false) }
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
dialogFragment.title = getString(R.string.here_be_dragons)
|
||||
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 onDialogPositive(dialog: DoNotShowAgainDialogFragment, showAgain: Boolean) {
|
||||
shouldShowDMGAlertDialog = showAgain
|
||||
showFilePicker()
|
||||
callback()
|
||||
}
|
||||
}
|
||||
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) {
|
||||
if (showDialog && shouldShowAndroidPieAlertDialog) {
|
||||
showAndroidPieAlertDialog()
|
||||
return
|
||||
}
|
||||
fun showFilePicker() {
|
||||
when (StateKeeper.flashMethod) {
|
||||
FlashMethod.FLASH_API -> {
|
||||
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) {
|
||||
when (requestCode) {
|
||||
READ_EXTERNAL_STORAGE_PERMISSION -> {
|
||||
if (delayedButtonClicked)
|
||||
onButtonClicked(null, showDialog = false)
|
||||
return
|
||||
}
|
||||
else -> {
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
if (delayedButtonClicked)
|
||||
onButtonClicked(null, showDMGDialog = false, showAndroidPieDialog = false)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -184,8 +134,7 @@ class StartActivity : ActivityBase() {
|
|||
// Pull that URI using resultData.getData().
|
||||
var uri: Uri? = null
|
||||
if (data != null) {
|
||||
uri = data.getData()
|
||||
StateKeeper.imageFile = uri
|
||||
StateKeeper.imageFile = data.data
|
||||
|
||||
nextStep()
|
||||
}
|
||||
|
|
|
@ -5,8 +5,10 @@ import android.content.BroadcastReceiver
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.hardware.usb.UsbDevice
|
||||
import android.hardware.usb.UsbManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
|
@ -18,12 +20,13 @@ 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.enums.FlashMethod
|
||||
import eu.depau.etchdroid.kotlin_exts.*
|
||||
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.*
|
||||
import java.io.File
|
||||
|
||||
class UsbDrivePickerActivity : ActivityBase(), SwipeRefreshLayout.OnRefreshListener {
|
||||
val USB_PERMISSION = "eu.depau.etchdroid.USB_PERMISSION"
|
||||
|
@ -35,8 +38,68 @@ class UsbDrivePickerActivity : ActivityBase(), SwipeRefreshLayout.OnRefreshListe
|
|||
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?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
handleIntent(intent)
|
||||
|
||||
setContentView(R.layout.activity_usb_drive_picker)
|
||||
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="reset_warnings">Reimposta tutti gli avvisi</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>
|
|
@ -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="reset_warnings">Reset all warnings</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>
|
||||
|
|
Loading…
Reference in a new issue