12

I am taking External Sd Card's PersistableUriPermission and storing it for further use. Now I want that when user provides me with the file path, from list of files in my application, I want to edit the document and rename it.

So I have the file path of the file to edit.

My question is how do I get that file's Uri from my TreeUri so as edit file.

Anku Agarwal
  • 459
  • 1
  • 3
  • 12

1 Answers1

23

Access Sd-Card's files

Use DOCUMENT_TREE dialog to get sd-card's Uri.

Inform the user about how to choose sd-card on the dialog. (with pictures or gif animations)

// call for document tree dialog
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT_TREE);

On onActivityResult you'll have the selected directory Uri. (sdCardUri)

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case REQUEST_CODE_OPEN_DOCUMENT_TREE:
            if (resultCode == Activity.RESULT_OK) {
                sdCardUri = data.getData();
             }
             break;
     }
  }

Now must check if the user,

a. selected the sd-card

b. selected the sd-card that our file is on (some devices could have multiple sd-cards).


We check both a and b by finding the file through the hierarchy, from sd root to our file. If the file found, both of a and b conditions are acquired.

//First we get `DocumentFile` from the `TreeUri` which in our case is `sdCardUri`.
DocumentFile documentFile = DocumentFile.fromTreeUri(this, sdCardUri);

//Then we split file path into array of strings.
//ex: parts:{"", "storage", "extSdCard", "MyFolder", "MyFolder", "myImage.jpg"}
// There is a reason for having two similar names "MyFolder" in 
//my exmple file path to show you similarity in names in a path will not 
//distract our hiarchy search that is provided below.
String[] parts = (file.getPath()).split("\\/");

// findFile method will search documentFile for the first file 
// with the expected `DisplayName`

// We skip first three items because we are already on it.(sdCardUri = /storage/extSdCard)
for (int i = 3; i < parts.length; i++) {
    if (documentFile != null) {
        documentFile = documentFile.findFile(parts[i]);
    }
  }

if (documentFile == null) {

    // File not found on tree search
    // User selected a wrong directory as the sd-card
    // Here must inform the user about how to get the correct sd-card
    // and invoke file chooser dialog again.  

    // If the user selects a wrong path instead of the sd-card itself,  
    // you should ask the user to select a correct path.  
    // I've developed a gallery app with this behavior implemented in it.  
    // https://play.google.com/store/apps/details?id=com.majidpooreftekhari.galleryfarsi
    // After you installed the app, try to delete one image from the  
    // sd-card and when the app requests the sd-card, select a wrong path  
    // to see how the app behaves.  

 } else {

    // File found on sd-card and it is a correct sd-card directory
    // save this path as a root for sd-card on your database(SQLite, XML, txt,...)

    // Now do whatever you like to do with documentFile.
    // Here I do deletion to provide an example.


    if (documentFile.delete()) {// if delete file succeed 
        // Remove information related to your media from ContentResolver,
        // which documentFile.delete() didn't do the trick for me. 
        // Must do it otherwise you will end up with showing an empty
        // ImageView if you are getting your URLs from MediaStore.
        // 
        Uri mediaContentUri = ContentUris.withAppendedId(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                longMediaId);
        getContentResolver().delete(mediaContentUri , null, null);
    }


 }

Wrong sd-card path selection behavior on my app:

To check the behavior on the wrong sd-card path selection install the app and try to delete an image that is on your sd-card and select a wrong path instead of your sd-card directory.
Calendar Gallery: https://play.google.com/store/apps/details?id=com.majidpooreftekhari.galleryfarsi

Note:

You must provide access permission to the external storage inside your manifest and for os>=Marshmallow inside the app. https://stackoverflow.com/a/32175771/2123400


Edit Sd-Card's Files

For editing an existing image on your sd-card you don't need any of the above steps if you want to invoke another app to do it for you.

Here we invoke all the activities (from all the installed apps) with the capability of editing the images. (Programmers mark their apps in the manifest for its capabilities to provide accessibility from other apps (activities)).

on your editButton click event:

String mimeType = getMimeTypeFromMediaContentUri(mediaContentUri);
startActivityForResult(Intent.createChooser(new Intent(Intent.ACTION_EDIT).setDataAndType(mediaContentUri, mimeType).putExtra(Intent.EXTRA_STREAM, mediaContentUri).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION), "Edit"), REQUEST_CODE_SHARE_EDIT_SET_AS_INTENT);

and this is how to get mimeType:

public String getMimeTypeFromMediaContentUri(Uri uri) {
    String mimeType;
    if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
        ContentResolver cr = getContentResolver();
        mimeType = cr.getType(uri);
    } else {
        String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri
                .toString());
        mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
                fileExtension.toLowerCase());
    }
    return mimeType;
}

Note:

On Android KitKat(4.4) don't ask the user to select the sd-card because on this version of Android DocumentProvider is not applicable, hence we have no chance to have access to the sd-card with this approach. Look at the API level for the DocumentProvider https://developer.android.com/reference/android/provider/DocumentsProvider.html
I couldn't find anything that works on Android KitKat(4.4). If you found anything useful with KitKat please share it with us.

On versions below the KitKat access to sd-card is already provided by the OS.

Eftekhari
  • 1,001
  • 1
  • 19
  • 37
  • I don't want to delete file – Ankesh kumar Jaisansaria Aug 23 '16 at 14:37
  • Copy from external to sd? Copy from Sd to external? – Eftekhari Aug 23 '16 at 14:39
  • 2
    I think the code you provided I to delete the whole Sd Card directory. – Ankesh kumar Jaisansaria Aug 23 '16 at 14:40
  • I want to edit an Image File that is in Sd Card directory – Ankesh kumar Jaisansaria Aug 23 '16 at 14:41
  • "I think the code you provided I to delete the whole Sd Card directory" No. It only deletes the file represented by file path. "I want to edit an Image File that is in Sd Card directory" >> I'm updating my answer. – Eftekhari Aug 23 '16 at 14:53
  • I dont know why my documentGile is null – Ankesh kumar Jaisansaria Aug 23 '16 at 14:59
  • What's your `sdCardUri` value? – Eftekhari Aug 23 '16 at 15:11
  • As in your edit to answer, I dont want to open a create choser. I want to edit file in background with my own data. – Ankesh kumar Jaisansaria Aug 23 '16 at 15:12
  • I liked your first answer. – Ankesh kumar Jaisansaria Aug 23 '16 at 15:13
  • I have posted one answer to this question. Initially documentFile in not null. But when for loop runs I get Exception and then document file becomes null – Ankesh kumar Jaisansaria Aug 23 '16 at 15:16
  • For edit an image or video you must do huge amount of job. It's not what you could get from one post on stack. – Eftekhari Aug 23 '16 at 15:17
  • What's your android version? – Eftekhari Aug 23 '16 at 15:23
  • You must provide access permission on your manifest. And if you are on android M and above must provide permissions inside your app run time. – Eftekhari Aug 23 '16 at 15:29
  • I think there is some confusion on statment editing an image file. So just to simplify I would say I want to edit a text file which is located in SD Card. (Just add a line to a txt file) – Ankesh kumar Jaisansaria Aug 23 '16 at 15:33
  • It's the same. You need to have access to your sd-card. On android Marshmallow you need to provide access to external storage inside your app and not on your manifest. – Eftekhari Aug 23 '16 at 15:36
  • I am using android v5.1.1 and taking permission from manifeaast file and still getting this error – Ankesh kumar Jaisansaria Aug 23 '16 at 15:40
  • Use breakpoints and get the value of `sdCardUri` and copy paste it here to tell you what's wrong. – Eftekhari Aug 23 '16 at 15:44
  • Using your above code (The very first one) I am able to edit file.Thanks for help. – Ankesh kumar Jaisansaria Aug 26 '16 at 11:14
  • 1
    Is there any other method faster then this – Ankesh kumar Jaisansaria Aug 26 '16 at 11:18
  • @AnkeshkumarJaisansaria I think it would be better to make a new question about deletion speed problem.:) – Eftekhari Sep 29 '16 at 19:35
  • @Eftekhari wow you are great buddy. Thanks for your nice and clean explanation answer. it really helpful for me. – Palanivelraghul Mar 22 '17 at 10:12
  • @Eftekhari I have a doubt, if user select sub folder instead of selecting root directory in SD card. then how to delete a file using that tree uri. please help me – Palanivelraghul May 30 '17 at 06:40
  • 1
    @Palanivelraghul If the user selects a wrong path instead of the sd-card itself, the method I provided here as the answer will return false and with this false return, you should ask the user to select a correct path. I've developed a gallery app with this behavior implemented in it. After you installed the app, try to delete one image from your sd-card and when the app requests your sd-card, select a wrong path to see how the app behaves. https://play.google.com/store/apps/details?id=com.majidpooreftekhari.dailygallery&hl=en_GB – Eftekhari May 31 '17 at 14:04
  • @Eftekhari thanks for your response and i saw your app, its looks great. See I am using Moto G3. In this device its not asking that permission, but while checking the same thing in OnePlusOne it ask permission. Is there any easy way to check whether the permission is need for this device or not? instead of geeting false while deletion happens. Note: If you found any mistake(s) in this please correctt it. thanks – Palanivelraghul Jun 01 '17 at 05:57
  • It depends on your OS version. On kitkat (4.4) it will not ask you to select the sd-card, because on this version of android `DocumentProvider`is not applicable, hence we have no chance to have access to the sd-card. Look at the api level for the `DocumentProvider` https://developer.android.com/reference/android/provider/DocumentsProvider.html On versions below the kitkat access to sd-card is already provided by OS. – Eftekhari Jun 01 '17 at 12:09
  • While looping your code inside a Aysnc Task many times the DocumentFile is null. – Rahulrr2602 Jul 19 '17 at 12:23
  • What is longMediaId ? – MSeiz5 Jul 22 '19 at 08:35
  • 1
    @MSeiz5 When we query `ContentResolver` each entry or record has an id. When we want to delete an entry we need to provide the id. On query (search) save the id in a file or database. If you want performance don't save in `SharedPreferences`. – Eftekhari Jul 22 '19 at 09:26