2

I am working now on ImageCompressor app.

I need to delete and write (update) image file. In Internal storage works perfectly but **SD Card can't give me access to Delete and Write files.

How can my app able to do write and delete operation on sd card (removable storage)?

I've already done whole project without this, so I have to must find a way.

Update:

I am already research & discuss about this issue. And understood I have to use storage access framework But I'm new on SAF.

I used a library to compress photo that need File not Uri. For that I do Uri -> File and use Intent.ACTION_OPEN_DOCUMENT and pick image from Removable storage.

But for removable storage I can't find Image Real Path from uri.

I don't know it's the right way or not. If there have any way in SAF where I can Compress my Image using uri, let me know. Or How to get Image real path from uri of Removable Storage Photo.

Update code SAF:

//  -----------  Intent  -------------
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");

//  ------------  On Activity Result  --------------
Uri uri = data.getData();
try {
    ParcelFileDescriptor fileDescriptor = getContentResolver().openFileDescriptor(uri, "w");
    FileOutputStream fos = new FileOutputStream(fileDescriptor.getFileDescriptor());

    FileInputStream fis = new FileInputStream(getImageFilePath(uri));

    FileChannel source = fis.getChannel();
    FileChannel destination = fos.getChannel();
    destination.transferFrom(source, 0, source.size());

    fis.close();
    fos.close();
    fileDescriptor.close();
    Toast.makeText(this, "File save successfully.", Toast.LENGTH_SHORT).show();
}

Uri to File Path, I done where pick image from media apps(like Gallary, Photos) But pick from sd card what will instead of MediaStore.Images.Media.DATA I don't know. Code:

private File getImageFilePath(Uri uri) throws IOException {
    String image_id = null, imagePath = null;

    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    if (cursor != null) {
        cursor.moveToFirst();
        image_id = cursor.getString(0);
        image_id = image_id.substring(image_id.lastIndexOf(":") + 1);
        cursor.close();
    }
    cursor = getContentResolver().query(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Images.Media._ID + " = ? ", new String[]{image_id}, null);
    if (cursor!=null) {
        cursor.moveToFirst();
        imagePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
        cursor.close();
    }

    File file = new File(imagePath);
    return new Compressor(this).setQuality(50).compressToFile(file);
}
Sheikh Hasib
  • 7,423
  • 2
  • 25
  • 37
  • Did you try searching? https://stackoverflow.com/questions/2121833/permission-to-write-to-the-sd-card – BackSlash Aug 23 '18 at 08:44
  • 1
    @MikeM. Yes I'm talking about removable storage. – Sheikh Hasib Aug 23 '18 at 08:52
  • @BackSlash The questions and answere was very old. And after 5.0 Lolipop android changed their sd card access policy. – Sheikh Hasib Aug 23 '18 at 08:54
  • @B001ᛦ Yes I provide right permission, that's why in Phone memory evrything works perfectly. But problem with Sd Card. – Sheikh Hasib Aug 23 '18 at 08:55
  • Are you getting any exceptions? – Bek Aug 23 '18 at 09:01
  • Nope, When I'm going to delete sd card file. `file.delete()` provide false. But in same time Phone storage files delete successfully. @Bek – Sheikh Hasib Aug 23 '18 at 09:07
  • How did you find path to mico SD card? – greenapps Aug 23 '18 at 09:08
  • There was Many way, I know about 3-4 ways. Like I work with multiple files and need Files folder path then. `imageFileList.get(0).getParentFile().getAbsolutePath()` @greenapps – Sheikh Hasib Aug 23 '18 at 09:12
  • 1
    have a look https://developer.android.com/guide/topics/providers/document-provider – Bek Aug 23 '18 at 09:12
  • Sorry but you did not tell how you obtained path to micro SD card. Code for a list doest tell that. – greenapps Aug 23 '18 at 09:22
  • @greenapps ok I got you but where I have to send you the code? in chat are I invite you? – Sheikh Hasib Aug 23 '18 at 09:32
  • I dnt need complete code. Just tell how you obtained that path. – greenapps Aug 23 '18 at 09:35
  • `if(data.getClipData() != null){ int count = data.getClipData().getItemCount(); for (int i=0; i – Sheikh Hasib Aug 23 '18 at 09:36
  • What a nonsence code. How could i ever see how you obtained that path? You are just extracting some info from clipdata. But you should tell me what you did put in it before. How did you or somebody else obtain that path? – greenapps Aug 23 '18 at 09:40
  • So you let the user select some images!? With ACTION_GET_CONTENT ? `you can get find the real path from Uri`. Wrong habbit. Use the uri directly. Do not try to get a real path to begin with. – greenapps Aug 23 '18 at 09:42
  • But why what's problem with to work with real path. I must need the path to write on that file. – Sheikh Hasib Aug 23 '18 at 09:46
  • No you cannot even write on that file on sd card if you use the 'real' path. So dont tell that you need it. – greenapps Aug 23 '18 at 09:48
  • For internal storage I do the exactly same. and I'm new not like you pro. But I thought I'll create a new file from the real path and update the old file. – Sheikh Hasib Aug 23 '18 at 09:53

3 Answers3

5

Removable SD cards are write only on modern Android devices if you use File and FileOutputStream classes.

If you are lucky then your device using getExternalFilesDirs() returns as second item an app specific directory on the card where you still can write.

For the rest and instead use the Storage Access Framework.

Start with letting the user choose the sd card with Intent.ACTION_OPEN_DOCUMENT_TREE or a file with Intent.ACTION_OPEN_DOCUMENT.

ximaera
  • 2,348
  • 17
  • 18
greenapps
  • 11,154
  • 2
  • 16
  • 19
2

I digged into an old Android app of mine and retrieved this :

This method convert the root of the sdcard uri, to a File path.

public File getRootPath(Context context, Uri sdcardRootUri)
{
    List<String> pathSegments =  sdcardRootUri.getPathSegments();
    String[] tokens = pathSegments.get(pathSegments.size()-1).split(":");
    for (File f : ContextCompat.getExternalFilesDirs(context, null))
    {
        String path = f.getAbsolutePath().substring(0, f.getAbsolutePath().indexOf("/Android/"));
        if (path.contains(tokens[0]))
        {
            return new File(path);
        }
    }
    return null;
}

And in order to retrieved the uri of the sdcard root, I used that :

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, SDCARD_ROOT_CODE);

Then the user would choose the root of the sdcard and then, I handled the result like this :

protected void onActivityResult(int requestCode, int resultCode, Intent data) 
{
    if (resultCode == RESULT_OK && requestCode == SDCARD_ROOT_CODE)
    {
        // Persist access permissions
        Uri sdcdardRootUri = data.getData();
        grantUriPermission(getPackageName(), sdcdardRootUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        getContentResolver().takePersistableUriPermission(sdcdardRootUri, takeFlags);

        // Do whatever you want with sdcdardRootUri
    }
}

I hope it's what you are looking for. With that you can read/write/delete any file you want on the sdcard.

ToYonos
  • 16,469
  • 2
  • 54
  • 70
  • First I'm sorry for delay, I was busy. It's the answere what I'm looking for. But right now I can't test this code and obiously I'll let you know after run this code. Thank you! – Sheikh Hasib Sep 01 '18 at 22:08
  • Hi, your `getRootPath(Context context, Uri sdcardRootUri)` isn't working. Here `path` veriable return `/storage/3034-6134` this string that is SD Card path not that folder files path. – Sheikh Hasib Sep 20 '18 at 11:26
  • Yes the sdcard root path, just append next the relative path of your file – ToYonos Sep 20 '18 at 11:43
  • I'm not professional, you can say beginner. I don't understand how this can help me. For `ACTION_OPEN_DOCUMENT_TREE` I need the specefic folders files. How can I got this? can you add some code for me? – Sheikh Hasib Sep 20 '18 at 12:07
  • For instance : `File yourFile = new File(getRootPath(context, sdcdardRootUri), "path/to/file/in/sd/card");` – ToYonos Sep 20 '18 at 12:20
-1

you have try

    try {
        Runtime runtime = Runtime.getRuntime();
        Process proc = runtime.exec("mount");
        InputStream is = proc.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        String line;
        BufferedReader br = new BufferedReader(isr);
        while ((line = br.readLine()) != null) {
            //Filter common Linux partitions
            if (line.contains("secure"))
                continue;
            if (line.contains("asec"))
                continue;
            if (line.contains("media"))
                continue;
            if (line.contains("system") || line.contains("cache")
                || line.contains("sys") || line.contains("data")
                || line.contains("tmpfs") || line.contains("shell")
                || line.contains("root") || line.contains("acct")
                || line.contains("proc") || line.contains("misc")
                || line.contains("obb")) {
                continue;
            }

            if (line.contains("fat") || line.contains("fuse") || (line
                .contains("ntfs"))) {

                String columns[] = line.split(" ");
                if (columns != null && columns.length > 1) {
                    String path = columns[1];
                    if (path!=null&&!SdList.contains(path)&&path.contains("sd"))
                        SdList.add(columns[1]);
                }
            }
        }
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
TanZhenxing
  • 144
  • 2