Как показать больше провайдеров с ACTION_OPEN_DOCUMENT

Я хочу использовать диалоговое окно системы Android, представленное как часть Framework доступа к хранилищу, чтобы открыть файл. Я делаю это с помощью

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
startActivityForResult(intent, EDIT_REQUEST);

а затем обработайте возвращенный URI в onActivityResult().

Проблема заключается в том, что в полученном меню я получаю гораздо меньше контент-провайдеров, чем я ожидал. Только Google Диск и Загрузки (см. Левый снимок экрана ниже). Другие, такие как Dropbox, Solid Explorer,... не отображаются.

Я подозреваю, что причина в том, что эти приложения просто не устанавливают необходимый фильтр намерений в этом списке.

Однако другие приложения, например Kaiten Mail или Chrome, каким-то образом отображают системный диалог с полностью реализованными поставщиками контента в верхней части списка, а затем другие, такие как Dropbox и Solid Explorer, ниже, разделенные тонкой панелью (см. правый снимок экрана).

Как я могу получить это поведение?

Comparison of the behavior I get (left) and the one I want (right)

Ответ 1

Используйте 'ACTION_GET_CONTENT:

Intent intent = new Intent(Intent. ACTION_GET_CONTENT);
intent.setType("application/pdf");
startActivityForResult(intent, EDIT_REQUEST);

Ответ 2

У нас тоже была эта проблема. Кажется, есть несколько способов получить выбор.

Вот что мы сделали:

object ThirdPartyIntentsUtil {
        //    https://medium.com/@louis993546/how-to-ask-system-to-open-intent-to-select-jpg-and-png-only-on-android-i-e-no-gif-e0491af240bf
        //example usage: mainType= "*/*"  extraMimeTypes= arrayOf("image/*", "video/*") - choose all images and videos
        //example usage: mainType= "*/image"  extraMimeTypes= arrayOf("image/jpeg", "image/png") - choose all images of png and jpeg types
        /**note that this only requests to choose the files, but it not guaranteed that this is what you will get*/
        @JvmStatic
        fun getPickFileIntent(context: Context, mainType: String = "*/*", extraMimeTypes: Array<String>? = null): Intent? {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
                return null
            val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
            intent.addCategory(Intent.CATEGORY_OPENABLE)
            intent.type = mainType
            if (!extraMimeTypes.isNullOrEmpty())
                intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeTypes)
            if (context.packageManager.queryIntentActivities(intent, 0).isNullOrEmpty())
                return null
            return intent
        }

        //    https://github.com/linchaolong/ImagePicker/blob/master/library/src/main/java/com/linchaolong/android/imagepicker/cropper/CropImage.java
        @JvmStatic
        fun getPickFileChooserIntent(
                context: Context, title: CharSequence?, preferDocuments: Boolean = true,includeCameraIntents:Boolean, mainType: String
                , extraMimeTypes: Array<String>? = null, extraIntents: ArrayList<Intent>? = null
        ): Intent? {
            val packageManager = context.packageManager
            var allIntents =
                    getGalleryIntents(packageManager, Intent.ACTION_GET_CONTENT, mainType, extraMimeTypes)
            if (allIntents.isEmpty()) {
                // if no intents found for get-content try pick intent action (Huawei P9).
                allIntents =
                        getGalleryIntents(packageManager, Intent.ACTION_PICK, mainType, extraMimeTypes)
            }
            val cameraIntents = getCameraIntents(packageManager)
            allIntents.addAll(0, cameraIntents)
    //        Log.d("AppLog", "got ${allIntents.size} intents")
            if (allIntents.isEmpty())
                return null
            if (preferDocuments)
                for (intent in allIntents)
                    if (intent.component!!.packageName == "com.android.documentsui")
                        return intent
            if (allIntents.size == 1)
                return allIntents[0]
            var target: Intent? = null
            for ((index, intent) in allIntents.withIndex()) {
                if (intent.component!!.packageName == "com.android.documentsui") {
                    target = intent
                    allIntents.removeAt(index)
                    break
                }
            }
            if (target == null)
                target = allIntents[allIntents.size - 1]
            allIntents.removeAt(allIntents.size - 1)
            // Create a chooser from the main  intent
            val chooserIntent = Intent.createChooser(target, title)
            if (extraIntents != null && extraIntents.isNotEmpty())
                allIntents.addAll(extraIntents)
            // Add all other intents
            chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, allIntents.toTypedArray<Parcelable>())
            return chooserIntent
        }

        private fun getCameraIntents(packageManager: PackageManager): ArrayList<Intent> {
            val cameraIntent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)
            val listCamera = packageManager.queryIntentActivities(cameraIntent, 0)
            val intents = ArrayList<Intent>()
            for (res in listCamera) {
                val intent = Intent(cameraIntent)
                intent.component = ComponentName(res.activityInfo.packageName, res.activityInfo.name)
                intent.'package' = res.activityInfo.packageName
                intents.add(intent)
            }
            return intents
        }

        /**
         * Get all Gallery intents for getting image from one of the apps of the device that handle
         * images. Intent.ACTION_GET_CONTENT and then Intent.ACTION_PICK
         */
        @TargetApi(Build.VERSION_CODES.KITKAT)
        private fun getGalleryIntents(
                packageManager: PackageManager, action: String,
                mainType: String , extraMimeTypes: Array<String>? = null
        ): ArrayList<Intent> {
            val galleryIntent = if (action == Intent.ACTION_GET_CONTENT)
                Intent(action)
            else
                Intent(action, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
            galleryIntent.type = mainType
            if (!extraMimeTypes.isNullOrEmpty()) {
                galleryIntent.addCategory(Intent.CATEGORY_OPENABLE)
                galleryIntent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeTypes)
            }
            val listGallery = packageManager.queryIntentActivities(galleryIntent, 0)
            val intents = ArrayList<Intent>()
            for (res in listGallery) {
                val intent = Intent(galleryIntent)
                intent.component = ComponentName(res.activityInfo.packageName, res.activityInfo.name)
                intent.'package' = res.activityInfo.packageName
                intents.add(intent)
            }
            return intents
        }

        @JvmStatic
        fun getMimeType(context: Context, uri: Uri): String? {
            val mimeType: String? = if (ContentResolver.SCHEME_CONTENT == uri.scheme) {
                val cr = context.contentResolver
                cr.getType(uri)
            } else {
                val fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri.toString())
                MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension.toLowerCase())
            }
            return mimeType
        }

    }

Итак, предположим, что вы хотите выбрать видеофайл, вы можете сделать так:

private val mimeTypeMap = MimeTypeMap.getSingleton()
private val videosMimeTypes = arrayOf(mimeTypeMap.getMimeTypeFromExtension("mkv"), mimeTypeMap.getMimeTypeFromExtension("mp4"), mimeTypeMap.getMimeTypeFromExtension("3gp"))

...
val intentForChoosingVideos = ThirdPartyIntentsUtil .getPickFileChooserIntent(this,null,
            true, "videos/*", videosMimeTypes)
            ?: getPickFileIntent(this, "video/*,", videosMimeTypes)

Вы можете изменить его для обработки других типов файлов, конечно. Здесь для файлов изображений:

private val mimeTypeMap = MimeTypeMap.getSingleton()
private val imagesMimeTypes = arrayOf(mimeTypeMap.getMimeTypeFromExtension("png"), mimeTypeMap.getMimeTypeFromExtension("jpg"), mimeTypeMap.getMimeTypeFromExtension("webp"))

...
val intentForChoosingImages = ThirdPartyIntentsUtil .getPickFileChooserIntent(this, null,
            true, "image/*", imagesMimeTypes)
            ?: getPickFileIntent(this, "image/*,", imagesMimeTypes)

Он попытается получить расширенную версию, а в случае неудачи попытается выбрать приложение для использования. Если это не удастся, он вернет ноль.

Обратите внимание, что по какой-то причине приложение "Google Фото" не позволяет выбирать только видео, поэтому, если вы хотите включить его, используйте более общую форму:

    val intentForChoosingVideos = ThirdPartyIntentsUtil.getPickFileChooserIntent(this, null,
            false, true,"*/*", videosMimeTypes)
            ?: ThirdPartyIntentsUtil.getPickFileIntent(this, "video/*,", videosMimeTypes)