Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pick from Google Photos provider with ACTION_GET_CONTENT or OPEN_DOCUMENT

I have no clue at why this happens, but I am not able to pick images from the Google Photos provider. Testing on API 27.

With ACTION_GET_CONTENT

If I use:

val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
  • I can see Google Photos among the provider
  • I can browse to some picture and select it
  • Then I am directed back to the providers list (not to my app), as if the provider had crashed in a try-catch

When I open the Photos provider and navigate through folders I see lots of these:

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)

When I click on the picture, I see these:

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

With ACTION_OPEN_DOCUMENT

In this case I don't even see Google Photos in the providers drawer.

Question

How can I solve this, preferably with ACTION_GET_CONTENT?

like image 976
natario Avatar asked Mar 02 '19 11:03

natario


2 Answers

I had the same problem, I could not get image URI from Google Photos with a ACTION_GET_CONTENT intent. The problem was related to launchMode of my activity. I could not find any reference to Android documentations. but this way the problem completely solved:

I had a single activity application and my ACTION_GET_CONTENT intent was something like this:

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

the problem was singleInstance launch mode in activity's definition in AndroidManifest.xml.

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

By removing the android:launchMode line the problem was solved. If your activity needs to be singleInstance, creating a dummy activity will be a good solution. here you can start a dummy activity for result and then do your Intent stuff inside its onCreate and then setResult() for your requested activity:

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()
    }
}

and here is your code in your MainActivity:

gallery.setOnClickListener {
    startActivityForResult(
        Intent(requireActivity(), ImagePickerActivity::class.java),
        GALLERY_RESULT
    )
}
like image 138
Babak Avatar answered Oct 24 '22 13:10

Babak


EDIT 2

I think i found the solution of the problem. It is mentioned in the Google docs that accessing a shared file will give you the URI.

The server app sends the file's content URI back to the client app in an Intent. This Intent is passed to the client app in its override of onActivityResult(). Once the client app has the file's content URI, it can access the file by getting its FileDescriptor.

Below is the updated code i am using inside onActivityResult. Make sure to call the super method of onActivityResult at last.

super.onActivityResult(requestCode, resultCode, data)

Working code

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)
}

EDIT

Also check this stackoverflow post

Original

I am using it on Android oreo 8.1.0(API 27) on my Redmi 6 pro phone and it is working fine.

You haven't posted the onActivityResult method may be this is where you need to do some modifications. I have tried it both

Below is my code snippet

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

startActivityForResult(pickIntent, SELECT_PICTURE)

and in onActivityResult i am parsing it like this

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))
    }
}

Method to create a new file

@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)
}

Method to read from inputstream

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()
        }

    }
}
like image 32
Rahul Khurana Avatar answered Oct 24 '22 15:10

Rahul Khurana