diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5a30cd4..5b29193 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -39,6 +39,7 @@
+
\ No newline at end of file
diff --git a/app/src/main/java/luyao/util/ktx/ui/ChooseFileActivity.kt b/app/src/main/java/luyao/util/ktx/ui/ChooseFileActivity.kt
new file mode 100644
index 0000000..7109ab3
--- /dev/null
+++ b/app/src/main/java/luyao/util/ktx/ui/ChooseFileActivity.kt
@@ -0,0 +1,78 @@
+package luyao.util.ktx.ui
+
+import android.Manifest
+import android.app.Activity
+import android.content.Intent
+import android.content.pm.PackageManager
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import kotlinx.android.synthetic.main.activity_choose_file.*
+import luyao.ktx.R
+import luyao.util.ktx.base.BaseActivity
+import luyao.util.ktx.ext.startFileChooser
+import luyao.util.ktx.ext.toRealFile
+
+/**
+ * Created by luyao
+ * on 2019/12/9 15:00
+ */
+class ChooseFileActivity : BaseActivity() {
+
+ private var allowMultiple = false
+ override fun getLayoutResId() = R.layout.activity_choose_file
+
+ override fun initView() {
+ singleChoose.setOnClickListener { chooseFile(1001, false) }
+ multipleChoose.setOnClickListener { chooseFile(1001, true) }
+ }
+
+ private fun chooseFile(requestCode: Int, multiple: Boolean) {
+ allowMultiple = multiple
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ == PackageManager.PERMISSION_GRANTED
+ ) {
+ startFileChooser(requestCode, allowMultiple)
+ } else {
+ ActivityCompat.requestPermissions(
+ this,
+ arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
+ 1002
+ )
+ }
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ) {
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ startFileChooser(1001, allowMultiple)
+ }
+ }
+
+ override fun initData() {
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+
+ if ((requestCode == 1001) && (resultCode == Activity.RESULT_OK)) {
+
+ data?.data?.let { // single
+ uriResult.text = it.toRealFile()?.path ?: ""
+ }
+
+ data?.clipData?.let {
+ // multiple
+ val builder = StringBuilder()
+ for (i in 0 until it.itemCount) {
+ it.getItemAt(i).uri.let { uri ->
+ builder.append("${uri.toRealFile()?.path}\n\n")
+ }
+ }
+ uriResult.text = builder.toString()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/luyao/util/ktx/ui/ExtActivity.kt b/app/src/main/java/luyao/util/ktx/ui/ExtActivity.kt
index b9f3423..ed79d2e 100644
--- a/app/src/main/java/luyao/util/ktx/ui/ExtActivity.kt
+++ b/app/src/main/java/luyao/util/ktx/ui/ExtActivity.kt
@@ -22,6 +22,7 @@ class ExtActivity : CommonListActivity() {
add(ItemBean("SharedPreferencesExt", SharedPreferencesActivity::class.java))
add(ItemBean("ShellExt", ShellExtActivity::class.java))
add(ItemBean("ToastExt", ToastExtActivity::class.java))
+ add(ItemBean("UriExt",ChooseFileActivity::class.java))
}
}
diff --git a/app/src/main/res/layout/activity_choose_file.xml b/app/src/main/res/layout/activity_choose_file.xml
new file mode 100644
index 0000000..c902dc5
--- /dev/null
+++ b/app/src/main/res/layout/activity_choose_file.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ktx/src/main/java/luyao/util/ktx/ext/CommonExt.kt b/ktx/src/main/java/luyao/util/ktx/ext/CommonExt.kt
index af097e7..017e760 100644
--- a/ktx/src/main/java/luyao/util/ktx/ext/CommonExt.kt
+++ b/ktx/src/main/java/luyao/util/ktx/ext/CommonExt.kt
@@ -32,7 +32,7 @@ val Context.screenWidth
val Context.screenHeight
get() = resources.displayMetrics.heightPixels
-
+fun fromKitKat() = fromSpecificVersion(Build.VERSION_CODES.KITKAT)
fun fromM() = fromSpecificVersion(Build.VERSION_CODES.M)
fun beforeM() = beforeSpecificVersion(Build.VERSION_CODES.M)
fun fromN() = fromSpecificVersion(Build.VERSION_CODES.N)
diff --git a/ktx/src/main/java/luyao/util/ktx/ext/IntentExt.kt b/ktx/src/main/java/luyao/util/ktx/ext/IntentExt.kt
index b2ba03d..82da6d0 100644
--- a/ktx/src/main/java/luyao/util/ktx/ext/IntentExt.kt
+++ b/ktx/src/main/java/luyao/util/ktx/ext/IntentExt.kt
@@ -1,5 +1,6 @@
package luyao.util.ktx.ext
+import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
@@ -153,4 +154,15 @@ fun Context.sendEmail(email: String, subject: String?, text: String?) {
}
}
+fun Activity.startFileChooser(requestCode: Int, allowMultiple: Boolean = false) {
+ val intent = Intent(Intent.ACTION_GET_CONTENT)
+ intent.type = "*/*"
+ intent.addCategory(Intent.CATEGORY_OPENABLE)
+ if (fromSpecificVersion(Build.VERSION_CODES.JELLY_BEAN_MR2)) intent.putExtra(
+ Intent.EXTRA_ALLOW_MULTIPLE,
+ allowMultiple
+ )
+ startActivityForResult(Intent.createChooser(intent, "Choose File"), requestCode)
+}
+
diff --git a/ktx/src/main/java/luyao/util/ktx/ext/UriExt.kt b/ktx/src/main/java/luyao/util/ktx/ext/UriExt.kt
new file mode 100644
index 0000000..8fe6009
--- /dev/null
+++ b/ktx/src/main/java/luyao/util/ktx/ext/UriExt.kt
@@ -0,0 +1,161 @@
+package luyao.util.ktx.ext
+
+import android.content.ContentResolver
+import android.content.ContentUris
+import android.content.Context
+import android.net.Uri
+import android.os.Environment
+import android.provider.DocumentsContract
+import android.provider.MediaStore
+import android.text.TextUtils
+import luyao.util.ktx.Ktx
+import java.io.File
+import java.lang.reflect.Array
+
+/*
+ * from Blankj
+ * https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/UriUtils.java
+ */
+
+/**
+ * Created by luyao
+ * on 2019/12/9 15:20
+ */
+fun Uri.toRealFile(): File? {
+ val authority = authority
+ val scheme = scheme
+ val path = path
+
+ if (fromN() && path != null && path.startsWith("/external/")) {
+ val file = File(Environment.getExternalStorageDirectory().absolutePath + path.replace("/external", ""))
+ if (file.exists()) {
+ return file
+ }
+ }
+
+ if (ContentResolver.SCHEME_FILE == scheme) {
+ return if (path != null) File(path) else null
+ } else if (fromKitKat() && DocumentsContract.isDocumentUri(Ktx.app, this)) {
+ if ("com.android.externalstorage.documents" == authority) {
+ val docId = DocumentsContract.getDocumentId(this)
+ val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ val type = split[0]
+ if ("primary".equals(type, ignoreCase = true)) {
+ return File(Environment.getExternalStorageDirectory().toString() + "/" + split[1])
+ } else {
+ // Below logic is how External Storage provider build URI for documents
+ // http://stackoverflow.com/questions/28605278/android-5-sd-card-label
+ val mStorageManager = Ktx.app.storageManager
+ try {
+ val storageVolumeClazz = Class.forName("android.os.storage.StorageVolume")
+ val getVolumeList = mStorageManager?.javaClass?.getMethod("getVolumeList")
+ val getUuid = storageVolumeClazz.getMethod("getUuid")
+ val getState = storageVolumeClazz.getMethod("getState")
+ val getPath = storageVolumeClazz.getMethod("getPath")
+ val isPrimary = storageVolumeClazz.getMethod("isPrimary")
+ val isEmulated = storageVolumeClazz.getMethod("isEmulated")
+
+ val result = getVolumeList?.invoke(mStorageManager)
+
+ val length = Array.getLength(result)
+ for (i in 0 until length) {
+ val storageVolumeElement = Array.get(result, i)
+ //String uuid = (String) getUuid.invoke(storageVolumeElement);
+
+ val mounted =
+ Environment.MEDIA_MOUNTED == getState.invoke(storageVolumeElement) || Environment.MEDIA_MOUNTED_READ_ONLY == getState.invoke(
+ storageVolumeElement
+ )
+
+ //if the media is not mounted, we need not get the volume details
+ if (!mounted) continue
+
+ //Primary storage is already handled.
+ if (isPrimary.invoke(storageVolumeElement) as Boolean &&
+ isEmulated.invoke(storageVolumeElement) as Boolean) {
+ continue
+ }
+
+ val uuid = getUuid.invoke(storageVolumeElement) as String
+
+ if (uuid == type) {
+ return File(getPath.invoke(storageVolumeElement).toString() + "/" + split[1])
+ }
+ }
+ } catch (ex: Exception) {
+ ex.printStackTrace()
+ }
+ }
+ return null
+ } else if ("com.android.providers.downloads.documents" == authority) {
+ val id = DocumentsContract.getDocumentId(this)
+ if (!TextUtils.isEmpty(id)) {
+ try {
+ val contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"),
+ java.lang.Long.valueOf(id)
+ )
+ return Ktx.app.getFileFromUri(contentUri, "1_1")
+ } catch (e: NumberFormatException) {
+ if (id.startsWith("raw:")) {
+ return File(id.substring(4))
+ }
+ }
+ }
+ return null
+ } else if ("com.android.providers.media.documents" == authority) {
+ val docId = DocumentsContract.getDocumentId(this)
+ val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ val type = split[0]
+ val contentUri: Uri
+ 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 -> {
+ return null
+ }
+ }
+ val selection = "_id=?"
+ val selectionArgs = arrayOf(split[1])
+ return Ktx.app.getFileFromUri(contentUri, selection, selectionArgs)
+ } else if (ContentResolver.SCHEME_CONTENT == scheme) {
+ return Ktx.app.getFileFromUri(this)
+ } else {
+ return null
+ }
+ } else if (ContentResolver.SCHEME_CONTENT == scheme) run {
+ return Ktx.app.getFileFromUri(this)
+ } else return null
+
+}
+
+
+private fun Context.getFileFromUri(
+ uri: Uri,
+ selection: String? = null,
+ selectionArgs: kotlin.Array? = null
+): File? {
+ val cursor = contentResolver.query(
+ uri, arrayOf("_data"), selection, selectionArgs, null
+ )
+ if (cursor == null) {
+ return null
+ }
+ try {
+ return if (cursor.moveToFirst()) {
+ val columnIndex = cursor.getColumnIndex("_data")
+ if (columnIndex > -1) {
+ File(cursor.getString(columnIndex))
+ } else {
+ null
+ }
+ } else {
+ null
+ }
+ } catch (e: Exception) {
+ return null
+ } finally {
+ cursor.close()
+ }
+}
\ No newline at end of file