Android 13 MANAGE_EXTERNAL_STORAGE stops working, what can I do?

I’m using Processing 3.5.4. My app has been working fine on Android 12. Now my phone is updated to Android 13 and my app is no longer working, due to permission changes.

My app needs the permission to generate a file in an directory (in external storage) where user can open it with a 3rd party editor and modify it. Then the app reads the updated file.
If I use the Processing “data” directory I don’t have permission issue. But I have no idea where I can open the file to edit.

According to what I found, Android 13 no longer allows apps to write to external storage (MANAGE_EXTERNAL_STORAGE).

What can I do now?

@bigboss97

Google have changed how external storage can be accessed. It is no longer possible to have simple write external storage permission. This was a real headache for me when my app stopped being able to use a external SD card!

You are going to have to use the android Storage Access Framework … It means learning new stuff, and I initially thought it pointless, but it does have advantages.

You can read about it in the Android API documentation and I will try to post some getting started code for you later today!

Thanks for reply, any update?

I’ll post code today :blush:

@bigboss97

Here it is :astonished:

import android.content.Intent;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.content.Context;
import android.content.ContentResolver;
import android.app.Activity;


Context context ;
Activity activity ;
byte[] saveArray = {0, 1, 2, 3, 4, 5, 6, 7} ; //dummy data
byte[] loadArray ;
String displayText1 = "tap HERE to save";
String displayText2 = "tap HERE to load";
int REQUEST_CODE ;

void setup() {
  activity = getActivity();
  context = getContext();
  fill(255);
  textSize(width/20) ;
  textAlign(CENTER, CENTER);
}

void draw() {
  background(0);
  text(displayText1, 0, 0, width, height/2) ;
  text(displayText2, 0, height/2, width, height/2) ;
}

void mouseReleased() {
  if (mouseY <height/2) {
    //start an Intent to save
    REQUEST_CODE = 1;
    Intent saveIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    saveIntent.setType("*/*");
    activity.startActivityForResult(saveIntent, REQUEST_CODE);
  } else {
    //start an Intent to load
    REQUEST_CODE = 2;
    Intent loadIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    loadIntent.setType("*/*"); //any file type
    activity.startActivityForResult(loadIntent, REQUEST_CODE);
  }
}

@Override
  public void onActivityResult(int requestCode, int resultCode, Intent resultData) {

  if (resultCode == Activity.RESULT_OK && resultData != null) {
    ContentResolver contentResolver = context.getContentResolver();
    Uri uri = resultData.getData();


    switch (REQUEST_CODE) {
    case 1: // save file
      try {
        OutputStream os = contentResolver.openOutputStream(uri);
        os.write(saveArray);
        os.close();
        displayText1 = "File saved sccessfully" ;
        //saveArray should now be saved
      }
      catch (Exception e) {
        displayText1 = "File Save Error ..." ;
      }
      break;
    case 2:
      try { //load file
        InputStream is = contentResolver.openInputStream(uri);
        loadArray = new byte[0] ;
        int i = is.read();
        while (i != -1) {
          loadArray = expand(loadArray, loadArray.length + 1);
          loadArray[loadArray.length -1] = (byte) i;
          i = is.read();
        }
        is.close();
        displayText2 = "File loaded successfully"  ;
        //do whatever you need with loadArray ....
      }
      catch (Exception e) {
        displayText2 = "File Load Error ..." ;
      }
      break;
    }
  }
}//end onActivityResult

Thanks for the code. It works beautifully :+1:

I have few questions…
As I can see, the file can only be saved under “Downloads”. That’s fine for me. But is it possible to create sub-directory in that and save the file?
I also noticed, when I access my phone via PC I see my file saved under “Download” (without ‘s’) and that has one file less than “Downloads”. That’s a bit weird.

Uri is used in the code. I had a look and it’s something like:
content://com.android.providers.downloads.documents/document/msf%3A1000005220

I’m using loadJSONObject() in my program. So I need a path. I tried:

JSONObject jo= loadJSONObject( uri.getPath());

But it returned:

Couldn’t create a reader for /document/msf:1000005220

Then I googled and found something:

The looks so complicated to me. Do I have to do all these or is there a easier way?

@bigboss97
You should be able to save or load from anywhere your phone has access to. If I remember correctly, the default is Downloads, but you should be able to navigate to the SD card, create folders etc and save your file.

@bigboss97

Yes it is using a uri … I too wanted to use a file path but you can’t! I think there may be ways to convert between paths and URIs but I’d have to look into it.

I think you will need to use the DocumentFile class … I want to know how to do this too … so I will try!