Joel Broström
Joel Broström

Reputation: 4090

Get PDF from MediaStore after receiving "content://" Uri in onActivityResult?

I start an ACTION_GET_CONTENT intent in order to pick a PDF:

override fun routeToFilePicker() {
    val intent = Intent()
    intent.type = MediaType.PDF.toString()
    intent.action = Intent.ACTION_GET_CONTENT
    activity.startActivityForResult(
        Intent.createChooser(intent, "Select PDF"),
        REQUEST_CODE_PDF_PICKER
    )
}

Then on onActivityResult I try to create a PDF from the Uri (content//:path):

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_PDF_PICKER ) {
        data?.data?.let { pdfUri: Uri ->
            val pdfFile: File = pdfUri.toFile() <-- chrash
            ...
        }
    }
}

pdfUri.toFile() causes a fatal Exception:

java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1003, result=-1, data=Intent { dat=content://com.android.providers.downloads.documents/document/3569 flg=0x1 }} to activity {my.package.name.activity}: java.lang.IllegalArgumentException: Uri lacks 'file' scheme: content://com.android.providers.downloads.documents/document/3569

Caused by: java.lang.IllegalArgumentException: Uri lacks 'file' scheme: content://com.android.providers.downloads.documents/document/3569

I need a File in order to convert the pages into Images.
How can I get the PDF as a File from the Uri returned by MediaStore?

Upvotes: 4

Views: 2944

Answers (3)

Sreehari
Sreehari

Reputation: 5655

With the latest API updates, Android has pushed usage of Content resolver for file related processing.

Using the path received , the same needs to be resolved using Content resolver..

Samples are available in Google Github

To give an overview, the URI received after file selection needs to be passed like below in Fragment.

Create a Fragment for showing PDF. And there will be a ViewModel associated.

val parcelFileDescriptor = activity?.contentResolver?.openFileDescriptor(fileURI, "r")

In ViewModel, with the help of Coroutine's, we will use process like below

val scope = CoroutineScope(executor.asCoroutineDispatcher() + job)

scope.launch {
            openPdfRenderer()
            showPage(0)
        }

fun openPdfRenderer() {
        
        pdfRenderer = PdfRenderer(fileURI!!) //android.graphics.pdf.PdfRenderer
    }

fun showPage(index: Int) {

        currentPage?.let { page ->
            currentPage = null
            page.close()
        }
        pdfRenderer?.let { renderer ->

            val page = renderer.openPage(index).also {
                currentPage = it
            }
            val bitmap = Bitmap.createBitmap(page.width, page.height, Bitmap.Config.ARGB_8888)
            page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_PRINT)
            _pageBitmap.postValue(bitmap)
            val count = renderer.pageCount
            _pageInfo.postValue(index to count)
            _previousEnabled.postValue(index > 0)
            _nextEnabled.postValue(index + 1 < count)
        }
    }

Full sample source can be found over Google Github Sample Link

I agree, this may seem to be an overhead ! But so are the changes in Android Geography due to multiple reasons.

Happy Coding ! Cheers !

Upvotes: 2

chand mohd
chand mohd

Reputation: 2550

This is how I'm getting pdf as file :

val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
                            type = "application/pdf"
                            addCategory(Intent.CATEGORY_OPENABLE)
                            flags = flags or Intent.FLAG_GRANT_READ_URI_PERMISSION
                        }
                        startActivityForResult(intent, 111)

Inside your OnActivityResult(requestCode:Int,resultCode:Int,data:Intent?)

if (resultCode == Activity.RESULT_OK) {
            when (requestCode) {
//                101 -> {
//                    data?.data?.also { uri ->
//                        Log.i(TAG, "Uri: $uri")
//                        baseAdapter?.add(ImageArray(null, null, uri.toString()))
//                    }
//                }
                111 -> {
                    data?.data?.also { documentUri ->
                        baseActivity.contentResolver?.takePersistableUriPermission(
                                documentUri,
                                Intent.FLAG_GRANT_READ_URI_PERMISSION
                        )
                        val file = DocumentUtils.getFile(baseActivity,documentUri)//use pdf as file 
                    }
                }
            }

        }

Singleton class to covert Uri to file:

object DocumentUtils {
    fun getFile(mContext: BaseActivity?, documentUri: Uri): File {
        val inputStream = mContext?.contentResolver?.openInputStream(documentUri)
        var file =  File("")
        inputStream.use { input ->
            file =
                File(mContext?.cacheDir, System.currentTimeMillis().toString()+".pdf")
            FileOutputStream(file).use { output ->
                val buffer =
                    ByteArray(4 * 1024) // or other buffer size
                var read: Int = -1
                while (input?.read(buffer).also {
                        if (it != null) {
                            read = it
                        }
                    } != -1) {
                    output.write(buffer, 0, read)
                }
                output.flush()
            }
        }
        return file
    }
} 

P.S: Do not forget to ask permission at runtime

Manifest.permission.WRITE_EXTERNAL_STORAGE
Manifest.permission.READ_EXTERNAL_STORAGE

Upvotes: 5

simon5678
simon5678

Reputation: 259

    public static File getFileFromUri(Uri uri, Context context) {
        if (uri == null) {
            return null;
        }
        switch (uri.getScheme()) {
            case "content":
                return getFileFromContentUri(uri, context);
            case "file":
                return new File(uri.getPath());
            default:
                return null;
        }
    }
    
    private static File getFileFromContentUri(Uri contentUri, Context context) {
        if (contentUri == null) {
            return null;
        }
        File file = null;
        String filePath;
        String fileName;
        String[] filePathColumn = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME};
        ContentResolver contentResolver = context.getContentResolver();
        Cursor cursor = contentResolver.query(contentUri, filePathColumn, null,
                null, null);
        if (cursor != null) {
            cursor.moveToFirst();
            filePath = cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
            fileName = cursor.getString(cursor.getColumnIndex(filePathColumn[1]));
            cursor.close();
            if (!TextUtils.isEmpty(filePath)) {
                file = new File(filePath);
            }
            if (!file.exists() || file.length() <= 0 || TextUtils.isEmpty(filePath)) {
                filePath = getPathFromInputStreamUri(context, contentUri, fileName);
            }
            if (!TextUtils.isEmpty(filePath)) {
                file = new File(filePath);
            }
        }
        return file;
    }

Upvotes: -1

Related Questions