Выберите поставщика Google Фото с помощью ACTION_GET_CONTENT или OPEN_DOCUMENT.

Я понятия не имею, почему это происходит, но я не могу выбрать изображения из поставщика Google Фото. Тестирование по API 27.

С ACTION_GET_CONTENT

Если я использую:

val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
  • Я вижу Google Фото среди провайдера
  • Я могу перейти к какой-либо картинке и выбрать ее
  • Затем меня направляют обратно к списку провайдеров (не к моему приложению), как будто провайдер потерпел крах в попытке поймать

Когда я открываю поставщика фотографий и перемещаюсь по папкам, я вижу много таких:

2019-03-02 12:04:15.164 17641-13395/? W/NetworkManagementSocketTagger: untagSocket(120) failed with errno -22
2019-03-02 12:04:22.528 13217-13217/? E/ResourceType: Style contains key with bad entry: 0x01010586
2019-03-02 12:04:22.535 13217-13217/? W/ResourceType: For resource 0x7f020366, entry index(870) is beyond type entryCount(468)

Когда я нажимаю на картинку, я вижу это:

2019-03-02 12:04:34.150 13217-13217/? W/ResourceType: For resource 0x7f02036c, entry index(876) is beyond type entryCount(468)
2019-03-02 12:04:34.151 13217-13217/? W/ResourceType: For resource 0x7f02036c, entry index(876) is beyond type entryCount(468)
2019-03-02 12:04:34.229 2907-16891/? W/MediaExtractor: FAILED to autodetect media content.
2019-03-02 12:04:34.569 10839-10839/? W/ResourceType: ResTable_typeSpec entry count inconsistent: given 468, previously 1330

С ACTION_OPEN_DOCUMENT

В этом случае я даже не вижу Google Фото в ящике поставщиков.

Вопрос

Как я могу это решить, желательно с помощью ACTION_GET_CONTENT?


person natario    schedule 02.03.2019    source источник
comment
Попробуйте с ACTION_PICK   -  person Saurabh Thorat    schedule 02.03.2019
comment
Спасибо, но мне нужно использовать один из этих двух, и я хотел бы знать, почему он не работает.   -  person natario    schedule 02.03.2019
comment
@natario какое устройство ты используешь?!   -  person Rahul Khurana    schedule 06.03.2019
comment
@Рахул Nexus 5X, API 27   -  person natario    schedule 06.03.2019
comment
@natario Вы тестируете это на эмуляторе? или реальное устройство   -  person Rahul Khurana    schedule 06.03.2019
comment
@natario Также проверьте эту ссылку   -  person Rahul Khurana    schedule 06.03.2019
comment
Это настоящее устройство, Nexus 5X.   -  person natario    schedule 06.03.2019
comment
Может быть, это ошибка. Но я тестировал его на эмуляторе Nexus 5X API 27. который такой же, как ваше устройство. И код здесь тоже работает нормально. P.S. Проверить наличие обновлений   -  person Rahul Khurana    schedule 07.03.2019
comment
@natario проверьте мой обновленный ответ. и дайте мне знать, если вам нужно объяснение.   -  person Rahul Khurana    schedule 07.03.2019
comment
@Rahul onActivityResult никогда не вызывается, потому что поток не возвращается в мое приложение, он остается в средстве выбора, как я уже сказал.   -  person natario    schedule 07.03.2019
comment
@natario интересно, почему он работает не на реальном устройстве, а на эмуляторе.   -  person Rahul Khurana    schedule 08.03.2019
comment
@RahulKhurana Я проверил этот код на телефоне той же модели, что и у вас, и он работает без проблем. у вас сталь есть эта проблема.   -  person Ali shatergholi    schedule 11.03.2019
comment
@ Alishatergholi OP сталкивается с этой проблемой. Я просто пытаюсь решить это в любом случае   -  person Rahul Khurana    schedule 11.03.2019
comment
@RahulKhurana, пожалуйста, проверьте мой код, он хорошо работает на Nexus 5X.   -  person Ali shatergholi    schedule 11.03.2019
comment
@Алишатерголи Хорошо. Я буду   -  person Rahul Khurana    schedule 11.03.2019
comment
Я понимаю, что это работает для вас, но это все еще не для меня.   -  person natario    schedule 11.03.2019


Ответы (4)


ИЗМЕНИТЬ 2

Я думаю, что нашел решение проблемы. В документах Google упоминается, что доступ к общий файл предоставит вам URI.

Серверное приложение отправляет URI содержимого файла обратно клиентскому приложению в Intent. Это намерение передается клиентскому приложению в его переопределении onActivityResult(). Получив URI содержимого файла, клиентское приложение может получить доступ к файлу, получив его FileDescriptor.

Ниже приведен обновленный код, который я использую внутри onActivityResult. Не забудьте наконец вызвать метод super для onActivityResult.

super.onActivityResult (код запроса, код результата, данные)

Рабочий код

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
data?.data?.let {
    util._log(TAG, it.toString())
}
if (data!!.data != null && data.data != null) {
    try {

        val stream = if (data.data!!.toString().contains("com.google.android.apps.photos.contentprovider")) {
            val ff = contentResolver.openFileDescriptor(data.data!!, "r")
            FileInputStream(ff?.fileDescriptor)
        } else {
            contentResolver.openInputStream(data.data!!)
        }
        val createFile = createImageFile()
        util.copyInputStreamToFile(stream, createFile)
        selectedImagePath = createFile.absolutePath

    } catch (e: Exception) {
        util._log(TAG, Log.getStackTraceString(e))
    }
}
    super.onActivityResult(requestCode, resultCode, data)
}

ИЗМЕНИТЬ

Также проверьте этот сообщение о stackoverflow.

Исходный

Я использую его на Android oreo 8.1.0(API 27) на своем телефоне Redmi 6 pro, и он работает нормально.

Вы не опубликовали метод onActivityResult, возможно, именно здесь вам нужно внести некоторые изменения. я пробовал и то и другое

Ниже мой фрагмент кода

val pickIntent = Intent(Intent.ACTION_VIEW)
pickIntent.type = "image/*"
pickIntent.action = Intent.ACTION_GET_CONTENT
pickIntent.addCategory(Intent.CATEGORY_OPENABLE)

startActivityForResult(pickIntent, SELECT_PICTURE)

и в onActivityResult я разбираю это так

if (data!!.data != null && data.data != null) {
    try {
        //                    CommonUtilities._Log(TAG, "Data Type " + data.getType());
        if (!isFinishing) {
            val inputStream = contentResolver.openInputStream(data.data!!)
            val createFile = createImageFile()
            copyInputStreamToFile(inputStream!!, createFile)
            //                        CommonUtilities._Log(TAG, "File Path " + createFile.getAbsolutePath());
            selectedImagePath = createFile.absolutePath
        }
    } catch (e: IOException) {
        util._log(TAG, Log.getStackTraceString(e))
    }
}

Способ создания нового файла

@Throws(IOException::class)
private fun createImageFile(): File {
    // Create an image file name
    val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(Date())
    val imageFileName = "yesqueen_" + timeStamp + "_"
    val storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
    return File.createTempFile(imageFileName, ".jpg", storageDir)
}

Метод чтения из потока ввода

fun copyInputStreamToFile(`in`: InputStream, file: File) {
    var out: OutputStream? = null
    try {
        out = FileOutputStream(file)
        val buf = ByteArray(1024)
        var len: Int = 0
        while (`in`.read(buf).apply { len = this } > 0) {
            out.write(buf, 0, len)
        }

        /*while (`in`.read(buf).let {
                    len = it
                    true
                }) {
            out.write(buf, 0, len)
        }*/
        /* while ((len = `in`.read(buf)) > 0) {
             out.write(buf, 0, len)
         }*/
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        try {
            out?.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }

        try {
            `in`.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }
}
person Rahul Khurana    schedule 05.03.2019
comment
Спасибо, но onActivityResult никогда не вызывается, потому что устройство даже не возвращается к моему приложению. Он возвращается к выбору системного образа (тому, что с ящиком). Итак: приложение -> выбор системы -> выберите Google Photos -> выберите изображение -> вернуться к выбору. - person natario; 05.03.2019
comment
Вы попробовали, как в моем ответе ?? - person Rahul Khurana; 06.03.2019
comment
@natario я также прикрепил GIF, который показывает, что он работает :) - person Rahul Khurana; 06.03.2019
comment
Вы использовали те же 3 строки, что и в моем вопросе — ACTION_GET_CONTENT, CATEGORY_OPENABLE и image/*, так что да, я пытался. Я тоже считаю, что это правильно. - person natario; 06.03.2019

Я сделал это с этим кодом.

val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
startActivityForResult(intent,100)

и за результат

 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
        100 -> {
            if (resultCode == Activity.RESULT_OK) {
                val bitmap = generateBitmap(this,data!!.data)
                //TODO show bitmap to your View
                showPicture(bitmap!!)
            }
        }
    }
}

generateBitmap(context,uri) метод в Котлине

fun generateBitmap(context: Context, uri: Uri): Bitmap? {
    var filePath = ""
    var cursor: Cursor?
    var columnIndex = 0
    try {
        val column = arrayOf(MediaStore.Images.Media.DATA)
        val sel = arrayOf(MediaStore.Images.Media._ID + "=?")
        if (uri.toString().startsWith("content://com.google.android.apps.photos.contentprovider")){
            val content = this.contentResolver.openInputStream(uri) ?: return null
            val pictureBitmap = BitmapFactory.decodeStream(content)
            return pictureBitmap
        } else {
            filePath = ""
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                val wholeID = DocumentsContract.getDocumentId(uri)
                val id = arrayOf(wholeID.split(":")[1])
                cursor = context.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, column, sel[0], id, null)
                columnIndex = cursor.getColumnIndex(column[0])
                if (cursor.moveToFirst()) {
                    filePath = cursor.getString(columnIndex)
                }
                cursor.close()
            } else {
                val cursorLoader = CursorLoader(context, uri, column, null, null, null)
                val cursor = cursorLoader.loadInBackground()
                if (cursor != null) {
                    var column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
                    cursor.moveToFirst()
                    filePath = cursor.getString(column_index)
                }
            }
            //TODO generate bitmap from file path
            return bitmap(filePath)
        }
    } catch (e: Exception) {
        print(e.message)
    }
    return null
}

для getBitmap из пути к файлу я использовал этот метод

 fun bitmap(path : String) : Bitmap{
    val image = File(path)
    val bmOptions = BitmapFactory.Options()
    return BitmapFactory.decodeFile(image.absolutePath, bmOptions)
}
person Ali shatergholi    schedule 11.03.2019
comment
@natario я изменил часть ответа, надеюсь, это решит вашу проблему - person Ali shatergholi; 13.03.2019

У меня была та же проблема: я не мог получить URI изображения из Google Фото с намерением ACTION_GET_CONTENT. Проблема была связана с launchMode моей деятельностью. Я не смог найти ссылку на документацию Android. но таким образом проблема полностью решена:

У меня было приложение с одной активностью, и мое намерение ACTION_GET_CONTENT было примерно таким:

val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
      type = "image/*"
}
startActivityForResult(intent, GALLERY_RESULT)

проблема заключалась в режиме запуска singleInstance в определении активности в AndroidManifest.xml.

<activity
android:name=".ui.MainActivity"
android:configChanges="orientation"
android:launchMode="singleInstance"
android:screenOrientation="portrait"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateHidden"/>

Удалив строку android:launchMode, проблема была решена. Если ваша активность должна быть singleInstance, создание фиктивной активности будет хорошим решением. здесь вы можете запустить фиктивное действие для результата, а затем выполнить свои намерения внутри его onCreate, а затем setResult() для запрошенного действия:

class ImagePickerActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
            type = "image/*"
        }

        if (intent.resolveActivity(packageManager!!) != null) {
            startActivityForResult(intent, GALLERY_RESULT)
        } else {
            Toast.makeText(
                this,
                "No Gallery APP installed",
                Toast.LENGTH_LONG
            ).show()
            finish()
        }

    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        //here data is resulted URI from ACTION_GET_CONTENT
        //and we pass it to our MainActivy using setResult
        setResult(resultCode, data)
        finish()
    }
}

и вот ваш код в вашей MainActivity:

gallery.setOnClickListener {
    startActivityForResult(
        Intent(requireActivity(), ImagePickerActivity::class.java),
        GALLERY_RESULT
    )
}
person Babak    schedule 10.10.2020
comment
Спасибо! Это также решило мою проблему здесь: список результатов после выбора просмотра"> stackoverflow.com/questions/65975177/ - person okhobb; 31.01.2021

ACTION_PICK даст возможность выбрать изображение, и вы сможете получить путь к файлу

    Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Image.Media.EXTERNAL_CONTENT_URI);
                    intent.setType("image/*");

                Intent sIntent = new Intent("com.sec.android.app.myfiles.PICK_DATA");
                sIntent.addCategory(Intent.CATEGORY_DEFAULT);
                sIntent.setType("image/*");

                Intent chooserIntent;
                if (getPackageManager().resolveActivity(sIntent, 0) != null) {
                    // it is device with samsung file manager
                    chooserIntent = Intent.createChooser(sIntent, "Select file");
                    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{intent});
                } else {
                    chooserIntent = Intent.createChooser(intent, "Select file");
                }
                try {
                    startActivityForResult(chooserIntent, REQUEST_TAKE_GALLERY_VIDEO);
                } catch (android.content.ActivityNotFoundException ex) {
                    Toast.makeText(getApplicationContext(), "No suitable File Manager was found.", Toast.LENGTH_SHORT).show();
                }
person Sachin Yadav    schedule 04.03.2019
comment
Спасибо, но я не хочу использовать ACTION_PICK. Мне нужно использовать один из двух (получить содержимое/открыть документ). - person natario; 04.03.2019