Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Intent.ACTION_OPEN_DOCUMENT in Android Pie

I'm making a profile photo change function using Retrofit in android pie.

So I succeeded in uploading the photos taken with the camera to the server. But I don't know how to transfer the photos selected from the gallery to my server. (I'm fine with any kind of code in Java Kotlin.)

I will upload the video later.

I searched a lot on Google, but it was hard to get the information I wanted.

Well done in the google doc but i don't know how to do it. https://developer.android.com/guide/topics/providers/document-provider

The Google doc shows examples of using bitmap or inputstream or something.

Do I need a bitmap or inputstream to upload photos using Retrofit?

I actually need a valid uri.

public void performFileSearch() {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        startActivityForResult(intent, PICTURES_DIR_ACCESS_REQUEST_CODE);
}


@Override
public void onActivityResult(int requestCode, int resultCode,Intent resultData) {
    if (requestCode == READ_REQUEST_CODE && resultCode ==Activity.RESULT_OK) {
        Uri uri = null;
        if (resultData != null) {
            uri = resultData.getData();
            Log.i(TAG, "Uri: " + uri.toString());
            showImage(uri);
        }
    }
}


public void Edit_Profile (String Image_Uri) {
    File file = new File(Image_Uri);
    RequestBody requestBody = RequestBody.create(file, MediaType.parse("image/*"));

    MultipartBody.Part body = MultipartBody.Part.createFormData("uploaded_file", Num+ID+".jpg", requestBody);
}

In fact, onActivityResult returns the following type of uri.

content://com.android.providers.media.documents/document/image%3A191474

So when I try to send it to my server using that uri I get a FileNotFoundException error.

like image 611
kim.h.w Avatar asked Oct 24 '19 23:10

kim.h.w


People also ask

What is put EXTRA in intent?

Using putExtra() and getExtras() in android Intents are asynchronous messages which allow Android components to request functionality from other components of the Android system. For example an Activity can send an Intents to the Android system which starts another Activity .

What does android intent ACTION VIEW do?

An intent allows you to start an activity in another app by describing a simple action you'd like to perform (such as "view a map" or "take a picture") in an Intent object.


1 Answers

This is a privacy restriction introduced in Android-Q. Direct access to shared/external storage devices is deprecated when an app targets API 29 and the path returned from getExternalStorageDirectory method is no longer directly accessible to apps. Use the app-specific directory to write & read files.

By default, apps targeting Android 10 and higher are given scoped access into external storage, or scoped storage. Such apps can see the following types of files within an external storage device without needing to request any storage-related user permissions:

Files in the app-specific directory, accessed using getExternalFilesDir(). Photos, videos, and audio clips that the app created from the media store.

  • Files in the app-specific directory, accessed using getExternalFilesDir().
  • Photos, videos, and audio clips that the app created from the media store.

Go through the documentation Open files using storage access framework

Coming to the context, One thing that you can do is that as CommonsWare suggested use InputStreamRequestBody. Otherwise, copy the selected file to your app sandbox folder IE, app-specific directory, then access the file from there without any permission. Just see the below implementation that works in Android-Q and later.

Perform file search

private void performFileSearch(String messageTitle) {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
        intent.setType("application/*");
        String[] mimeTypes = new String[]{"application/x-binary,application/octet-stream"};
        if (mimeTypes.length > 0) {
            intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
        }

        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(Intent.createChooser(intent, messageTitle), OPEN_DIRECTORY_REQUEST_CODE);
        } else {
            Log.d("Unable to resolve Intent.ACTION_OPEN_DOCUMENT {}");
        }
    }

onActivityResult returned

@Override
public void onActivityResult(int requestCode, int resultCode, final Intent resultData) {
        // The ACTION_OPEN_DOCUMENT intent was sent with the request code OPEN_DIRECTORY_REQUEST_CODE.
        // If the request code seen here doesn't match, it's the response to some other intent,
        // and the below code shouldn't run at all.
        if (requestCode == OPEN_DIRECTORY_REQUEST_CODE) {
            if (resultCode == Activity.RESULT_OK) {
                // The document selected by the user won't be returned in the intent.
                // Instead, a URI to that document will be contained in the return intent
                // provided to this method as a parameter.  Pull that uri using "resultData.getData()"
                if (resultData != null && resultData.getData() != null) {
                    new CopyFileToAppDirTask().execute(resultData.getData());
                } else {
                    Log.d("File uri not found {}");
                }
            } else {
                Log.d("User cancelled file browsing {}");
            }
        }
    }

File writing to app specific path

public static final String FILE_BROWSER_CACHE_DIR = "CertCache";

@SuppressLint("StaticFieldLeak")
private class CopyFileToAppDirTask extends AsyncTask<Uri, Void, String> {
    private ProgressDialog mProgressDialog;

    private CopyFileToAppDirTask() {
        mProgressDialog = new ProgressDialog(YourActivity.this);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mProgressDialog.setMessage("Please Wait..");
        mProgressDialog.show();
    }

    protected String doInBackground(Uri... uris) {
        try {
            return writeFileContent(uris[0]);
        } catch (IOException e) {
            Log.d("Failed to copy file {}" + e.getMessage());
            return null;
        }
    }

    protected void onPostExecute(String cachedFilePath) {
        mProgressDialog.dismiss();
          if (cachedFilePath != null) {
                Log.d("Cached file path {}" + cachedFilePath);
            } else {
               Log.d("Writing failed {}");
         }

    }
}

private String writeFileContent(final Uri uri) throws IOException {
    InputStream selectedFileInputStream =
            getContentResolver().openInputStream(uri);
    if (selectedFileInputStream != null) {
        final File certCacheDir = new File(getExternalFilesDir(null), FILE_BROWSER_CACHE_DIR);
        boolean isCertCacheDirExists = certCacheDir.exists();
        if (!isCertCacheDirExists) {
            isCertCacheDirExists = certCacheDir.mkdirs();
        }
        if (isCertCacheDirExists) {
            String filePath = certCacheDir.getAbsolutePath() + "/" + getFileDisplayName(uri);
            OutputStream selectedFileOutPutStream = new FileOutputStream(filePath);
            byte[] buffer = new byte[1024];
            int length;
            while ((length = selectedFileInputStream.read(buffer)) > 0) {
                selectedFileOutPutStream.write(buffer, 0, length);
            }
            selectedFileOutPutStream.flush();
            selectedFileOutPutStream.close();
            return filePath;
        }
        selectedFileInputStream.close();
    }
    return null;
}

  // Returns file display name.
    @Nullable
    private String getFileDisplayName(final Uri uri) {
        String displayName = null;
        try (Cursor cursor = getContentResolver()
                .query(uri, null, null, null, null, null)) {
            if (cursor != null && cursor.moveToFirst()) {
                displayName = cursor.getString(
                        cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
                Log.i("Display Name {}" + displayName);

            }
        }

        return displayName;
    }
like image 110
Anoop M Maddasseri Avatar answered Sep 22 '22 19:09

Anoop M Maddasseri