Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

As getExternalStoragePublicDirectory has been deprecated in Android Q , and the recommendation is to use other means. then how can we specify that we want to store the generated photos from a camera app into the DCIM folder, or a custom sub-folder within the DCIM ?

The documentation states that the next 3 options are the new preferred alternatives:

  • Context#getExternalFilesDir(String)
  • Intent#ACTION_OPEN_DOCUMENT
  • MediaStore
  • Option 1 is out of the questions as it would mean that the photos get deleted if the app gets uninstalled.

    Option 2 is also not a choice, as it would require the user to pick the location through the SAF file explorer.

    We are left with option 3, the MediaStore; but at the time of this question there is no documentation on how to use it as a replacement for getExternalStoragePublicDirectory in Android Q.

    Based on the docs, use DCIM/... for the RELATIVE_PATH , where ... is whatever your custom subdirectory would be. So, you would wind up with something like this:

          val resolver = context.contentResolver
          val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, "CuteKitten001")
            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
            put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/PerracoLabs")
          val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
          resolver.openOutputStream(uri).use {
            // TODO something with the stream
    

    Note that since RELATIVE_PATH is new to API Level 29, you would need to use this approach on newer devices and use getExternalStoragePublicDirectory() on older ones.

    Thanks for the answer. As a side note, I just saw in the documentation that there is also a new field "IS_PENDING", which I'm guessing that maybe I should also include when creating the ContentValues, so to be safe while writing the file, and then update it once the file is fully saved. – PerracoLabs Jun 5, 2019 at 22:41 I wonder is there really no option to implement this once. This works, but I figured this much before. I don't necessarily wanna keep using getExternalStoragePublicDirectory() as well as the new approach. Now I'm having multiple if / else to determine Android version threwout the codebase to do it both the new and the old way. – Max Jul 20, 2019 at 16:18 This new approach doesn't let you get access to previously created files, no? For example, if I made a camera app that puts the files in the folder you've mentioned, and then I remove and re-install my app, I can't access them anymore, no? I think the storage permission was replaced by other permissions, that are more restricted, no (maybe can handle only media files) ? Also, is there a list of all possible files types that are allowed to be created? Are those only media files, or any file (like PDF, ZIP, APK,...) ? – android developer Jul 23, 2019 at 18:06 @androiddeveloper: "So there is now only SAF to access files?" -- AFAIK, yes. "And MediaStore to get global ones that are only for media files (which I don't know what "media" could be, except for maybe some common image, video and audio files) ?" -- AFAIK, yes. – CommonsWare Jul 23, 2019 at 18:23

    @CommonsWare answer is amazing. But for those who want it in Java, you need to try this:

    ContentResolver resolver = context.getContentResolver();
    ContentValues contentValues = new ContentValues();
    contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
    contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
    contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
    Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
    

    As per the suggestion of @SamChen the code should look like this for text files:

    Uri uri = resolver.insert(MediaStore.Files.getContentUri("external"), contentValues);
    

    Because we wouldn't want txt files lingering in the Images folder.

    So, the place where I have mimeType, you enter the mime type you want. For example if you wanted txt (@Panache) you should replace mimeType with this string: "text/plain". Here is a list of mime types: https://www.freeformatter.com/mime-types-list.html

    Also, where I have the variable name, you replace it with the name of the file in your case.

    Gaurav, you answer is good for java guys but can u mention what is the syntax for txt file? – Panache Dec 26, 2019 at 4:20 Gaurav i m getting error, can not insert text/plain in to MediaStore.Images.Media. I have passed name as file.getName and mimetype = text/plain. pls help – Panache Dec 28, 2019 at 14:30 Gaurav, just a question where is path of source file in this code, is it not required? let us discuss here stackoverflow.com/questions/59511147/… – Panache Dec 28, 2019 at 14:55 For text file remember this Uri uri = getContentResolver().insert(MediaStore.Files.getContentUri("external"), values); – Sam Chen Mar 23, 2020 at 15:11

    Apps targeting Android Q - API 29+ disabled storage access by default due to security issues. If you want to enable it to add the following attribute in the AndroidManifest.xml:

    <manifest ... >
        <!-- This attribute is "false" by default for Android Q or higher -->
        <application android:requestLegacyExternalStorage="true" ... >
        </application>
    </manifest>
    

    then you have to use getExternalStorageDirectory() instead of getExternalStoragePublicDirectory().

    Example: If you want to create a directory in the internal storage if not exists.

     File mediaStorageDir = new File(Environment.getExternalStorageDirectory() + "/SampleFolder");
     // Create the storage directory if it does not exist
     if (! mediaStorageDir.exists()){
         if (! mediaStorageDir.mkdirs()){
             Log.d("error", "failed to create directory");
                    Do you really need to use requestLegacyExternalStorage="true" for scoped storage? I think once you use getExternalStorageDirectory(), it's not necessary. Also, legacy flag is ignored on Api 30+.
    – Micer
                    Feb 2, 2021 at 17:20
                    It won't work if you missed requestLegacyExternalStorage="true" on the target device api version is greater than 28+
    – Codemaker
                    Feb 3, 2021 at 7:33
                    This is not good as it is just a temporary solution according to documentation as well. Use MediaStore and ContentValues
    – Undefined function
                    Apr 26, 2021 at 16:02
                    requestLegacyExternalStorage will be ignored once you target API 30+ (Android 11+)  For me getExternalStoragePublicDirectory() is still working and is not marked as deprecated with targetSdk 31 and compileSdk 32
    – tuxdost
                    Sep 2, 2022 at 15:40
    import android.widget.EditText
    import android.widget.TextView
    import androidx.annotation.RequiresApi
    import androidx.appcompat.app.AppCompatActivity
    class MyActivity : AppCompatActivity() {
        @RequiresApi(Build.VERSION_CODES.Q)
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_mine)
            val editText: EditText = findViewById(R.id.edt)
            val write: Button = findViewById(R.id.Output)
            val read: Button = findViewById(R.id.Input)
            val textView: TextView = findViewById(R.id.textView)
            val resolver = this.contentResolver
            val contentValues = ContentValues().apply {
                put(MediaStore.MediaColumns.DISPLAY_NAME, "myDoc1")
                put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")
                put(MediaStore.MediaColumns.RELATIVE_PATH, "Documents")
            val uri: Uri? = resolver.insert(MediaStore.Files.getContentUri("external"), contentValues)
            Log.d("Uri", "$uri")
            write.setOnClickListener {
                val edt : String = editText.text.toString()
                if (uri != null) {
                    resolver.openOutputStream(uri).use {
                        it?.write("$edt".toByteArray())
                        it?.close()
            read.setOnClickListener {
                if (uri != null) {
                    resolver.openInputStream(uri).use {
                        val data = ByteArray(50)
                        it?.read(data)
                        textView.text = String(data)
    

    Here, I am storing a text file in phone's Document folder by writing text into edit text and by clicking button 'Write' it will save the file with the text written. On clicking button 'Read' it will bring the text from that file and then display it in the text view.

    It will not run on devices that are below android Q or android 10 as RELATIVE_PATH can only be used in these versions.

    How to move my files from public directory to Android/Media/com.myapp directory in android 11 like whatsapp did without asking any permission? how to get public directory path if getExternalStoragePublicDirectory is now deprecated in Q? – Bhavin Patel Jul 5, 2021 at 13:12

    If you want to save your file in a app specific external storage, yes you can use context.getExternalFilesDir(). Many answers point out that.

    However, this is not the answer of this question because getExternalFilesDir() is app specific external storage, getExternalStoragePublicDirectory() is shared storage.

    For example, you want to save a downloaded pdf file to "Shared" Download directory. How do you do that ? For api 29 and above, you can do that without no permission.

    For api 28 and below, you need getExternalStoragePublicDirectory() method but it is deprecated. What if you don't want to use that deprecated method? Then you can use SAF file explorer(Intent#ACTION_OPEN_DOCUMENT). As said in the question, this requires the user to pick the location manually.

    This is what Google wants exactly. To improve user privacy, direct access to shared/external storage devices is deprecated.

    When an app targets Build.VERSION_CODES.Q, the path returned from this method is no longer directly accessible to apps. Apps can continue to access content stored on shared/external storage by migrating to alternatives such as Context#getExternalFilesDir(String), MediaStore, or Intent#ACTION_OPEN_DOCUMENT.

    Details are given in the following link:

    https://developer.android.com/reference/android/os/Environment#getExternalStorageDirectory()

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.