Pick from Google Photos provider with ACTION_GET_CONTENT or OPEN_DOCUMENT

后端 未结 4 1939
死守一世寂寞
死守一世寂寞 2021-01-17 18:33

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:<

相关标签:
4条回答
  • 2021-01-17 18:45

    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()
            }
    
        }
    }
    
    0 讨论(0)
  • 2021-01-17 18:55

    The ACTION_PICK will give the option to pick the image and you can get the file path

        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();
                    }
    
    0 讨论(0)
  • 2021-01-17 18:58

    I've done it with this code.

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

    and for result

     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) method in Kotlin

    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
    }
    

    for getBitmap from file path I used this method

     fun bitmap(path : String) : Bitmap{
        val image = File(path)
        val bmOptions = BitmapFactory.Options()
        return BitmapFactory.decodeFile(image.absolutePath, bmOptions)
    }
    
    0 讨论(0)
  • 2021-01-17 18:59

    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
        )
    }
    
    0 讨论(0)
提交回复
热议问题