How to get Access to External Storage in Android 10 (Android Q)?

18,474

Solution 1

If you target Android 10 or higher, set the value of requestLegacyExternalStorage to true in your app's manifest file:

<manifest ... >
  <!-- This attribute is "false" by default on apps targeting
       Android 10 or higher. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

Access files

To load media files, call one of the following methods from ContentResolver:

 Uri contentUri = ContentUris.withAppendedId(
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
        cursor.getLong(Integer.parseInt(BaseColumns._ID)));
String fileOpenMode = "r";
ParcelFileDescriptor parcelFd = resolver.openFileDescriptor(uri, fileOpenMode);
if (parcelFd != null) {
    int fd = parcelFd.detachFd();
    // Pass the integer value "fd" into your native code. Remember to call
    // close(2) on the file descriptor when you're done using it.
}

On devices running Android 10 (API level 29) and higher, your app can get exclusive access to a media file as it's written to disk by using the IS_PENDING flag.

ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "IMG1024.JPG");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.IS_PENDING, 1);

ContentResolver resolver = context.getContentResolver();
Uri collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
Uri item = resolver.insert(collection, values);

try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(item, "w", null)) {
    // Write data into the pending image.
} catch (IOException e) {
    e.printStackTrace();
}

// Now that we're finished, release the "pending" status, and allow other apps
// to view the image.
values.clear();
values.put(MediaStore.Images.Media.IS_PENDING, 0);
resolver.update(item, values, null, null);

Solution 2

I have been stuck for a long time and this worked fine for me

  StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
    StrictMode.setVmPolicy(builder.build());
    File = new File(filepath);

and don't forget to request legacy Storage in manifest file.

  <application android:requestLegacyExternalStorage="true" >
Share:
18,474

Related videos on Youtube

HavanaSun
Author by

HavanaSun

Updated on September 15, 2022

Comments

  • HavanaSun
    HavanaSun over 1 year

    I just migrated my sourcecode to Androidx, since I did that my share function to share a sound is no longer working. the Logcat says:

    Failed to save file: /storage/emulated/0/appfolder/testsound.mp3 (Permission denied)
    

    This is the part where it saves the sound:

                final String fileName = soundObject.getItemName() + ".mp3";
                File storage = Environment.getExternalStorageDirectory();
                File directory = new File(storage.getAbsolutePath() + "/appfolder/");
                directory.mkdirs();
                final File file = new File(directory, fileName);
                InputStream in = view.getContext().getResources().openRawResource(soundObject.getItemID());
    
                try{
                    Log.i(LOG_TAG, "Saving sound " + soundObject.getItemName());
                    OutputStream out = new FileOutputStream(file);
                    byte[] buffer = new byte[1024];
    
                    int len;
                    while ((len = in.read(buffer, 0, buffer.length)) != -1){
                        out.write(buffer, 0 , len);
                    }
                    in.close();
                    out.close();
    
                } catch (IOException e){
    
                    Log.e(LOG_TAG, "Failed to save file: " + e.getMessage());
                }
    

    And this is the code where it shares the sound:

    try{
                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1){
    
    
                                    if (ActivityCompat.checkSelfPermission(view.getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
                                        ActivityCompat.requestPermissions((Activity) view.getContext(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
    
                                    }else {
                                        final String AUTHORITY = view.getContext().getPackageName() + ".fileprovider";
                                        Uri contentUri = FileProvider.getUriForFile(view.getContext(), AUTHORITY, file);
                                        final Intent intent = new Intent(Intent.ACTION_SEND);
                                        intent.putExtra(Intent.EXTRA_STREAM, contentUri);
                                        intent.setType("audio/mp3");
                                        view.getContext().startActivity(Intent.createChooser(intent, "Share sound via..."));
                                    }
    
    
                                }
                                else {
                                    final Intent intent = new Intent(Intent.ACTION_SEND);
                                    Uri fileUri = Uri.parse(file.getAbsolutePath());
                                    intent.putExtra(Intent.EXTRA_STREAM, fileUri);
                                    intent.setType("audio/mp3");
                                    view.getContext().startActivity(Intent.createChooser(intent, "Share sound via..."));
                                }
    
                            } catch (Exception e){
                                Log.e(LOG_TAG, "Failed to share sound: " + e.getMessage());
                            }
                        }
    

    What do I have to change and how can I archieve that everyone no matter which Android Version he is using (minSdkVersion 16) can download and share the sound?

    Sadly I'm getting many bad reviews atm because no one can share (Not even Android 9 and below eventhough they used to be able to) Thank you

    • Vladyslav Matviienko
      Vladyslav Matviienko over 4 years
      Android 10 (KitKat) Android 10 is definitely not KitKat
  • HavanaSun
    HavanaSun over 4 years
    So If I add this will the user be able again to share a sound with Android 9 and lower? Why isn't he able to do so with my current code?
  • Jaspalsinh Gohil
    Jaspalsinh Gohil over 4 years
    because in Android 10 Privacy policy changed so you can refer this link to better understanding : developer.android.com/training/data-storage/files/…
  • Marc Alexander
    Marc Alexander over 3 years
    requestLegacyExternalStorage seems like bad practice and just a temporary fix, to inevitably just have to fix it again in the near future.
  • mightyWOZ
    mightyWOZ about 3 years
    If you target Android 10 or higher, wrong. this will not work on android 11, Please see this
  • Shubham Rohila
    Shubham Rohila almost 3 years
    legacy Storage work around is not recommended as it can only work for Android 10, for Android 11 and higher it will again not work.
  • Jaspalsinh Gohil
    Jaspalsinh Gohil about 2 years
    For android 11 & 12 please check this stackoverflow.com/a/71017801/9223264