title: Saving Text Files - Scoped Storage Android 11
body: <p>Is there a way to create and save text files outside of the Android/data folder in external storage? I am creating a music app and I added a preset manager so users can save, load, and edit preset files (which worked great before Android 11), I don’t want these files to be automatically removed if the app is uninstalled. That would be like if Photoshop deleted all of your Photoshop documents when Photoshop is uninstalled, that’s terrible! These are files that the user saves and they can be deleted separately if the user wants to.</p>
  <p>There has to be a way around this but I haven’t been able to find anything that works. <code>ACTION_OPEN_DOCUMENT_TREE</code> looked very promising, until I saw that Android has removed this option too.</p>
  <p><a href="https://developer.android.com/about/versions/11/privacy/storage" rel="nofollow noreferrer">https://developer.android.com/about/versions/11/privacy/storage</a></p>
  <blockquote>
  <p>You can no longer use the ACTION_OPEN_DOCUMENT_TREE intent action to
  request access to the following directories:</p>
  <ul>
  <li>The root directory of the internal storage volume.</li>
  <li>The root directory of each SD card volume that the device manufacturer considers to be reliable, regardless of whether the card
  is emulated or removable. A reliable volume is one that an app can
  successfully access most of the time.</li>
  <li>The Download directory.</li>
  </ul>
  </blockquote>
  <p>I’ve read Google Play only allows <code>MANAGE_EXTERNAL_STORAGE</code> in apps that need it (like file browsers, or anti-virus, etc) which likely will not work in my case. I don’t want to rely on only targeting older API’s so <code>requestLegacyExternalStorage</code> won’t work either.</p>
  <p>Everything I’ve looked into appears to be a dead end. Is there anything else I can do?</p>
  <p>Here is a short test program (I’m using LibGDX), which at the moment can only save to the root location:</p>
  <pre><code>Android/data/com.mygdx.filetest/files/
  </code></pre>
  <p><strong>[core] FileTest.java</strong></p>
  <pre><code>package com.mygdx.filetest;
  
  import com.badlogic.gdx.ApplicationListener;
  import com.badlogic.gdx.Files;
  import com.badlogic.gdx.Gdx;
  import com.badlogic.gdx.files.FileHandle;
  import com.badlogic.gdx.utils.ScreenUtils;
  
  public class FileTest implements ApplicationListener {
      private NativePermissions permissions;
      private Files.FileType fileType;
      private String directory = &quot;TestDir/&quot;;
      private String name = &quot;text.txt&quot;;
         
      public FileTest(final NativePermissions permissions){     
          this.permissions = permissions;
      }
      
      @Override
      public void create(){
          fileType = getFileType();
          if (permissions != null){
              permissions.checkExternalStoragePermission();
          } else {
              permissionGranted();
          }
      }
      private Files.FileType getFileType(){
          switch(Gdx.app.getType()) {
              case Android:
                  return Files.FileType.External;
              default:
                  return Files.FileType.Local;
          }
      }
      
      @Override public void render(){ ScreenUtils.clear(0.4f, 0.4f, 0.4f, 1); }
      @Override public void resize(int width, int height) {}
      @Override public void pause(){}
      @Override public void resume(){}
      @Override public void dispose (){}
      
      public void permissionGranted() {
          FileHandle fileHandle = Gdx.files.getFileHandle(directory+name, fileType); 
          if (fileHandle!=null) fileHandle.writeString(&quot;test&quot;, false);
      }
  }
  </code></pre>
  <p><strong>[android] AndroidLauncher.java</strong></p>
  <pre><code>package com.mygdx.filetest;
  
  import android.Manifest;
  import android.app.AlertDialog;
  import android.content.DialogInterface;
  import android.content.pm.PackageManager;
  import android.os.Bundle;
  import android.widget.Toast;
  
  import androidx.core.app.ActivityCompat;
  
  import com.badlogic.gdx.backends.android.AndroidApplication;
  import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
  
  public class AndroidLauncher extends AndroidApplication {
      private final FileTest application;
      private final int STORAGE_PERMISSION_CODE = 1;
      private boolean dialogBoxShowing = false;
      
      public AndroidLauncher(){
          final AndroidPermissions permissions = new AndroidPermissions(this);
          application = new FileTest(permissions);
      }
      
      @Override
      protected void onCreate (Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
          initialize(application, config);
      }
      @Override
      public void onRequestPermissionsResult(final int requestCode, final String permissions[], final int[] grantResults) {
          dialogBoxShowing = false;
          if (requestCode == STORAGE_PERMISSION_CODE) {
              if (grantResults.length &gt; 0 &amp;&amp; grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                  Toast.makeText(this, &quot;Permission GRANTED&quot;, Toast.LENGTH_SHORT).show();
                  permissionGranted();
              } else {
                  Toast.makeText(this, &quot;Permission DENIED&quot;, Toast.LENGTH_SHORT).show();
              }
          }
      }
      public void promptExternalStoragePermission() {
          if (dialogBoxShowing) return;
          dialogBoxShowing = true;
          this.runOnUiThread(new Runnable() {
              @Override
              public void run() {
                  final AlertDialog.Builder builder = new AlertDialog.Builder(AndroidLauncher.this);
                  builder.setMessage(&quot;To save user presets and custom settings, allow access to your phone’s storage.&quot;);
                  builder.setCancelable(false);
                  // reverse these buttons to put &quot;NO&quot; on left and &quot;YES&quot; on right
                  builder.setPositiveButton(&quot;NOT NOW&quot;, new DialogInterface.OnClickListener(){
                      @Override
                      public void onClick(DialogInterface dialog, int which) {
                          dialogBoxShowing = false;
                          dialog.dismiss();
                      }
                  });
                  builder.setNegativeButton(&quot;CONTINUE&quot;, new DialogInterface.OnClickListener(){
                      @Override
                      public void onClick(DialogInterface dialog, int which) {
                          ActivityCompat.requestPermissions(AndroidLauncher.this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_PERMISSION_CODE);
                      }
                  });
                  builder.create().show();
              }
          });
      }
      public void permissionGranted(){ application.permissionGranted(); }
  }
  </code></pre>
  <p><strong>[core] NativePermissions.java</strong></p>
  <pre><code>package com.mygdx.filetest;
  
  public interface NativePermissions {
      public void checkExternalStoragePermission();
  }
  </code></pre>
  <p><strong>[android] AndroidPermissions.java</strong></p>
  <pre><code>package com.mygdx.filetest;
  
  import android.Manifest;
  import android.content.pm.PackageManager;
  
  import androidx.core.content.ContextCompat;
  
  public class AndroidPermissions implements NativePermissions {
      private final AndroidLauncher context;
      
      public AndroidPermissions(final AndroidLauncher context){
          this.context = context;
      }
      
      @Override
      public void checkExternalStoragePermission() {
          if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
              context.permissionGranted();
          } else {
              context.promptExternalStoragePermission();
          }
      }
  }
  </code></pre>
  <p><strong>[android] AndroidManifest.xml</strong></p>
  <pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
  &lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
      package=&quot;com.mygdx.filetest&quot;&gt;
      
      &lt;uses-permission android:name=&quot;android.permission.WRITE_EXTERNAL_STORAGE&quot;/&gt;
      &lt;uses-permission android:name=&quot;android.permission.READ_EXTERNAL_STORAGE&quot;/&gt;
      
      &lt;application
          android:allowBackup=&quot;true&quot;
          android:icon=&quot;@drawable/ic_launcher&quot;
          android:isGame=&quot;true&quot;
          android:appCategory=&quot;game&quot;
          android:label=&quot;@string/app_name&quot;
          android:theme=&quot;@style/GdxTheme&quot; &gt;
          &lt;activity
              android:name=&quot;com.mygdx.filetest.AndroidLauncher&quot;
              android:label=&quot;@string/app_name&quot; 
              android:screenOrientation=&quot;fullUser&quot;
              android:configChanges=&quot;keyboard|keyboardHidden|navigation|orientation|screenSize|screenLayout&quot;&gt;
              &lt;intent-filter&gt;
                  &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
                  &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
              &lt;/intent-filter&gt;
          &lt;/activity&gt;
      &lt;/application&gt;
  &lt;/manifest&gt;
  </code></pre>