Mercurial > dwindows
comparison android/DWindows.kt @ 2594:2c15b3d41fe4
Android: Add preliminary new file browser that uses the system ACTION_GET_CONTENT Intent.
This doesn't quite work right yet, so includingit as fileBrowseNew() until I can get it
working well.
author | bsmith@81767d24-ef19-dc11-ae90-00e081727c95 |
---|---|
date | Fri, 28 May 2021 01:02:07 +0000 |
parents | 8352c38bc20b |
children | 6b5057dd6b8e |
comparison
equal
deleted
inserted
replaced
2593:cc2befdc97e8 | 2594:2c15b3d41fe4 |
---|---|
3 import android.R | 3 import android.R |
4 import android.app.Activity | 4 import android.app.Activity |
5 import android.app.Dialog | 5 import android.app.Dialog |
6 import android.app.NotificationChannel | 6 import android.app.NotificationChannel |
7 import android.app.NotificationManager | 7 import android.app.NotificationManager |
8 import android.content.ClipData | 8 import android.content.* |
9 import android.content.ClipboardManager | |
10 import android.content.Context | |
11 import android.content.DialogInterface | |
12 import android.content.pm.ActivityInfo | 9 import android.content.pm.ActivityInfo |
13 import android.content.res.Configuration | 10 import android.content.res.Configuration |
11 import android.database.Cursor | |
14 import android.graphics.* | 12 import android.graphics.* |
15 import android.graphics.drawable.BitmapDrawable | 13 import android.graphics.drawable.BitmapDrawable |
16 import android.graphics.drawable.Drawable | 14 import android.graphics.drawable.Drawable |
17 import android.graphics.drawable.GradientDrawable | 15 import android.graphics.drawable.GradientDrawable |
18 import android.media.AudioManager | 16 import android.media.AudioManager |
19 import android.media.ToneGenerator | 17 import android.media.ToneGenerator |
18 import android.net.Uri | |
20 import android.os.* | 19 import android.os.* |
20 import android.provider.DocumentsContract | |
21 import android.provider.MediaStore | |
21 import android.text.InputFilter | 22 import android.text.InputFilter |
22 import android.text.InputFilter.LengthFilter | 23 import android.text.InputFilter.LengthFilter |
23 import android.text.InputType | 24 import android.text.InputType |
24 import android.text.method.PasswordTransformationMethod | 25 import android.text.method.PasswordTransformationMethod |
25 import android.util.Base64 | 26 import android.util.Base64 |
738 var darkMode: Int = -1 | 739 var darkMode: Int = -1 |
739 private var paint = Paint() | 740 private var paint = Paint() |
740 private var bgcolor: Int = 0 | 741 private var bgcolor: Int = 0 |
741 private var menuBar: DWMenu? = null | 742 private var menuBar: DWMenu? = null |
742 private var defaultItem: View? = null | 743 private var defaultItem: View? = null |
744 private var fileURI: Uri? = null | |
743 | 745 |
744 // Our version of runOnUiThread that waits for execution | 746 // Our version of runOnUiThread that waits for execution |
745 fun waitOnUiThread(runnable: Runnable) | 747 fun waitOnUiThread(runnable: Runnable) |
746 { | 748 { |
747 if(Looper.myLooper() == Looper.getMainLooper()) { | 749 if(Looper.myLooper() == Looper.getMainLooper()) { |
3219 fun debugMessage(text: String) | 3221 fun debugMessage(text: String) |
3220 { | 3222 { |
3221 Log.d(null, text) | 3223 Log.d(null, text) |
3222 } | 3224 } |
3223 | 3225 |
3226 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | |
3227 super.onActivityResult(requestCode, resultCode, data) | |
3228 if(requestCode == 100 && resultCode == Activity.RESULT_OK) { | |
3229 fileURI = data!!.data | |
3230 throw java.lang.RuntimeException() | |
3231 } | |
3232 } | |
3233 | |
3234 fun fileBrowseNew(title: String, defpath: String?, ext: String?, flags: Int): String? | |
3235 { | |
3236 var retval: String? = null | |
3237 | |
3238 waitOnUiThread { | |
3239 val fileintent = Intent(Intent.ACTION_GET_CONTENT) | |
3240 fileintent.type = "text/plain" | |
3241 fileintent.addCategory(Intent.CATEGORY_OPENABLE) | |
3242 startActivityForResult(fileintent, 100) | |
3243 } | |
3244 | |
3245 // loop till a runtime exception is triggered. | |
3246 try { | |
3247 Looper.loop() | |
3248 } catch (e2: RuntimeException) { | |
3249 } | |
3250 | |
3251 retval = getUriRealPath(this, fileURI) | |
3252 return retval | |
3253 } | |
3254 | |
3224 fun fileBrowse(title: String, defpath: String?, ext: String?, flags: Int): String? | 3255 fun fileBrowse(title: String, defpath: String?, ext: String?, flags: Int): String? |
3225 { | 3256 { |
3226 var retval: String? = null | 3257 var retval: String? = null |
3227 | 3258 |
3228 waitOnUiThread { | 3259 waitOnUiThread { |
3229 val fc = DWFileChooser(this) | 3260 val fc = DWFileChooser(this) |
3230 fc.setFileListener(object: DWFileChooser.FileSelectedListener { | 3261 fc.setFileListener(object: DWFileChooser.FileSelectedListener { |
3231 override fun fileSelected(file: File?) { | 3262 override fun fileSelected(file: File?) { |
3232 // do something with the file | 3263 // do something with the file |
3233 retval = file!!.absolutePath | 3264 retval = file!!.absolutePath |
3234 throw java.lang.RuntimeException() | 3265 throw java.lang.RuntimeException() |
3235 } | 3266 } |
3236 }) | 3267 }) |
3237 if(ext != null) { | 3268 if(ext != null) { |
3238 fc.setExtension(ext) | 3269 fc.setExtension(ext) |
3239 } | 3270 } |
3240 fc.showDialog() | 3271 fc.showDialog() |
3241 } | 3272 } |
3416 with(NotificationManagerCompat.from(this)) { | 3447 with(NotificationManagerCompat.from(this)) { |
3417 // notificationId is a unique int for each notification that you must define | 3448 // notificationId is a unique int for each notification that you must define |
3418 notify(notificationID, builder.build()) | 3449 notify(notificationID, builder.build()) |
3419 } | 3450 } |
3420 } | 3451 } |
3452 } | |
3453 | |
3454 /* | |
3455 * This method will parse out the real local file path from the file content URI. | |
3456 */ | |
3457 private fun getUriRealPath(ctx: Context?, uri: Uri?): String? { | |
3458 var ret: String? = "" | |
3459 if (ctx != null && uri != null) { | |
3460 if (isContentUri(uri)) { | |
3461 if (isGooglePhotoDoc(uri.authority)) { | |
3462 ret = uri.lastPathSegment | |
3463 } else { | |
3464 ret = getImageRealPath(contentResolver, uri, null) | |
3465 } | |
3466 } else if (isFileUri(uri)) { | |
3467 ret = uri.path | |
3468 } else if (isDocumentUri(ctx, uri)) { | |
3469 | |
3470 // Get uri related document id. | |
3471 val documentId = DocumentsContract.getDocumentId(uri) | |
3472 | |
3473 // Get uri authority. | |
3474 val uriAuthority: String? = uri.authority | |
3475 if (isMediaDoc(uriAuthority)) { | |
3476 val idArr = documentId.split(":").toTypedArray() | |
3477 if (idArr.size == 2) { | |
3478 // First item is document type. | |
3479 val docType = idArr[0] | |
3480 | |
3481 // Second item is document real id. | |
3482 val realDocId = idArr[1] | |
3483 | |
3484 // Get content uri by document type. | |
3485 var mediaContentUri: Uri? = MediaStore.Images.Media.EXTERNAL_CONTENT_URI | |
3486 if ("image" == docType) { | |
3487 mediaContentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI | |
3488 } else if ("video" == docType) { | |
3489 mediaContentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI | |
3490 } else if ("audio" == docType) { | |
3491 mediaContentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI | |
3492 } | |
3493 | |
3494 // Get where clause with real document id. | |
3495 val whereClause = MediaStore.Images.Media._ID + " = " + realDocId | |
3496 ret = getImageRealPath(contentResolver, mediaContentUri, whereClause) | |
3497 } | |
3498 } else if (isDownloadDoc(uriAuthority)) { | |
3499 // Build download uri. | |
3500 val downloadUri: Uri = Uri.parse("content://downloads/public_downloads") | |
3501 | |
3502 // Append download document id at uri end. | |
3503 val downloadUriAppendId: Uri = | |
3504 ContentUris.withAppendedId(downloadUri, java.lang.Long.valueOf(documentId)) | |
3505 ret = getImageRealPath(contentResolver, downloadUriAppendId, null) | |
3506 } else if (isExternalStoreDoc(uriAuthority)) { | |
3507 val idArr = documentId.split(":").toTypedArray() | |
3508 if (idArr.size == 2) { | |
3509 val type = idArr[0] | |
3510 val realDocId = idArr[1] | |
3511 if ("primary".equals(type, ignoreCase = true)) { | |
3512 ret = Environment.getExternalStorageDirectory() | |
3513 .toString() + "/" + realDocId | |
3514 } | |
3515 } | |
3516 } | |
3517 } | |
3518 } | |
3519 return ret | |
3520 } | |
3521 | |
3522 /* Check whether this uri represent a document or not. */ | |
3523 private fun isDocumentUri(ctx: Context?, uri: Uri?): Boolean { | |
3524 var ret = false | |
3525 if (ctx != null && uri != null) { | |
3526 ret = DocumentsContract.isDocumentUri(ctx, uri) | |
3527 } | |
3528 return ret | |
3529 } | |
3530 | |
3531 /* Check whether this uri is a content uri or not. | |
3532 * content uri like content://media/external/images/media/1302716 | |
3533 */ | |
3534 private fun isContentUri(uri: Uri?): Boolean { | |
3535 var ret = false | |
3536 if (uri != null) { | |
3537 val uriSchema: String? = uri.scheme | |
3538 if ("content".equals(uriSchema, ignoreCase = true)) { | |
3539 ret = true | |
3540 } | |
3541 } | |
3542 return ret | |
3543 } | |
3544 | |
3545 /* Check whether this uri is a file uri or not. | |
3546 * file uri like file:///storage/41B7-12F1/DCIM/Camera/IMG_20180211_095139.jpg | |
3547 */ | |
3548 private fun isFileUri(uri: Uri?): Boolean { | |
3549 var ret = false | |
3550 if (uri != null) { | |
3551 val uriSchema: String? = uri.scheme | |
3552 if ("file".equals(uriSchema, ignoreCase = true)) { | |
3553 ret = true | |
3554 } | |
3555 } | |
3556 return ret | |
3557 } | |
3558 | |
3559 | |
3560 /* Check whether this document is provided by ExternalStorageProvider. Return true means the file is saved in external storage. */ | |
3561 private fun isExternalStoreDoc(uriAuthority: String?): Boolean { | |
3562 var ret = false | |
3563 if (uriAuthority != null && "com.android.externalstorage.documents" == uriAuthority) { | |
3564 ret = true | |
3565 } | |
3566 return ret | |
3567 } | |
3568 | |
3569 /* Check whether this document is provided by DownloadsProvider. return true means this file is a downloaed file. */ | |
3570 private fun isDownloadDoc(uriAuthority: String?): Boolean { | |
3571 var ret = false | |
3572 if (uriAuthority != null && "com.android.providers.downloads.documents" == uriAuthority) { | |
3573 ret = true | |
3574 } | |
3575 return ret | |
3576 } | |
3577 | |
3578 /* | |
3579 * Check if MediaProvider provide this document, if true means this image is created in android media app. | |
3580 */ | |
3581 private fun isMediaDoc(uriAuthority: String?): Boolean { | |
3582 var ret = false | |
3583 if (uriAuthority != null && "com.android.providers.media.documents" == uriAuthority) { | |
3584 ret = true | |
3585 } | |
3586 return ret | |
3587 } | |
3588 | |
3589 /* | |
3590 * Check whether google photos provide this document, if true means this image is created in google photos app. | |
3591 */ | |
3592 private fun isGooglePhotoDoc(uriAuthority: String?): Boolean { | |
3593 var ret = false | |
3594 if (uriAuthority != null && "com.google.android.apps.photos.content" == uriAuthority) { | |
3595 ret = true | |
3596 } | |
3597 return ret | |
3598 } | |
3599 | |
3600 /* Return uri represented document file real local path.*/ | |
3601 private fun getImageRealPath( | |
3602 contentResolver: ContentResolver, | |
3603 uri: Uri?, | |
3604 whereClause: String? | |
3605 ): String { | |
3606 var ret = "" | |
3607 | |
3608 if(uri != null) { | |
3609 // Query the uri with condition. | |
3610 val cursor: Cursor? = contentResolver.query(uri, null, whereClause, null, null) | |
3611 if (cursor != null) { | |
3612 val moveToFirst: Boolean = cursor.moveToFirst() | |
3613 if (moveToFirst) { | |
3614 | |
3615 // Get columns name by uri type. | |
3616 var columnName = MediaStore.Images.Media.DATA | |
3617 if (uri === MediaStore.Images.Media.EXTERNAL_CONTENT_URI) { | |
3618 columnName = MediaStore.Images.Media.DATA | |
3619 } else if (uri === MediaStore.Audio.Media.EXTERNAL_CONTENT_URI) { | |
3620 columnName = MediaStore.Audio.Media.DATA | |
3621 } else if (uri === MediaStore.Video.Media.EXTERNAL_CONTENT_URI) { | |
3622 columnName = MediaStore.Video.Media.DATA | |
3623 } | |
3624 | |
3625 // Get column index. | |
3626 val imageColumnIndex: Int = cursor.getColumnIndex(columnName) | |
3627 | |
3628 // Get column value which is the uri related file local path. | |
3629 ret = cursor.getString(imageColumnIndex) | |
3630 // Clean up | |
3631 cursor.close() | |
3632 } | |
3633 } | |
3634 } | |
3635 return ret | |
3421 } | 3636 } |
3422 | 3637 |
3423 /* | 3638 /* |
3424 * Native methods that are implemented by the 'dwindows' native library, | 3639 * Native methods that are implemented by the 'dwindows' native library, |
3425 * which is packaged with this application. | 3640 * which is packaged with this application. |