Android 10: What are my options to save files on external storage into a directory called "/sdcard/my-app/"
In Android Q direct File access is disabled by default for the apps outside their private folders. Here few strategies you can use in your case:
- Use the manifest option
requestLegacyExternalStorage
to have the old behavior but it won't work anymore with Android R, so it's really a short term solution; - Save your files using
getExternalFilesDir()
method. It's your private folder, other apps could access these files only if they haveREAD_EXTERNAL_STORAGE
permission. In this case it would be good to useFileProvider
to grant access to other apps to your files. - Use the method
getPrimaryStorageVolume().createOpenDocumentTreeIntent()
of classStorageManager
to ask for access to the extenal primary volume. In this case you need user consent and you won't be able to useFile
api directly anyway, but usingDocumentFile
class you have a very similar interface, so this is the solution closer to the old behavior. It works if you need to perform operations in foreground and background, i.e. without user interaction with the exception the first interaction to request the permission.
I link Flipper library for point 3, it helps to manage files like in older android versions.
From Android 11 onwards you need to add MANAGE_EXTERNAL_STORAGE
permission in the Manifest if you need access to external file directories.
An app can request All files access from the user by doing the following:
1. Declare the MANAGE_EXTERNAL_STORAGE
permission in the manifest.
2. Use the ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
intent action to direct users to a system settings page where they can enable the following option for your app: Allow access to manage all files.
3. To determine whether your app has been granted the MANAGE_EXTERNAL_STORAGE
permission, call Environment.isExternalStorageManager()
.
Read it here here
for above android QThis is code is working fine to save Image on External Storage//don't forget to add Manifest permission//
package com.myretro.saveimager; import androidx.appcompat.app.AppCompatActivity; import android.content.ContentResolver; import android.content.ContentValues; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import java.io.File; import java.io.FileNotFoundException; import java.io.OutputStream; public class MainActivity extends AppCompatActivity { private Button btnsave; private ImageView myImage; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnsave = findViewById(R.id.button); myImage = findViewById(R.id.imageView); btnsave.setOnClickListener(view -> { BitmapDrawable bitmap1 = (BitmapDrawable) myImage.getDrawable(); Bitmap bitmap = bitmap1.getBitmap(); saveImmageIntoExternalStrogaeaboveQ(bitmap); }); } private void saveImmageIntoExternalStrogaeaboveQ(Bitmap bitmap) { OutputStream outputStream; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ContentResolver contentResolver = getContentResolver(); ContentValues mValue = new ContentValues(); mValue.put(MediaStore.MediaColumns.DISPLAY_NAME, "hacker" + ".jpg"); mValue.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg"); mValue.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + "myimage"); Uri imageuri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mValue); try { outputStream = contentResolver.openOutputStream(imageuri); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); Toast.makeText(this, "Image Saved SuccessFull", Toast.LENGTH_SHORT).show(); } catch (FileNotFoundException e) { Toast.makeText(this, "Something went wrong " + e.getMessage(), Toast.LENGTH_SHORT).show(); e.printStackTrace(); } } else { //old method to write } }