20

I am new to intents and I am trying to figure out how to use parse(URI) and/or setType() to get the right types of application to open up and allow me to select things.

I want to launch an intent from my app that will allow the user to pick one of many types of files (.PDF, .DOCX, .XLSX, .PPTX, .DOC, .JPG, .PNG, .TXT, .LOG, etc.). What I need the activity to return is a full path to that file.

Right now I am using setType("*/*") with a chooser that I found on here, but this is automatically opening some documents selector in Android. I have file manager and other apps, and want to know what the standard setType is or MIME type. Thanks in advance.

Also, I apologize if this has already been answered. I have looked online, but think I am searching for the wrong thing because the results I am getting are for intents that just want one of these or don't return the path.

My applicable code is below: (Note: this is being done inside a fragment)

static final int PICK_FILE_REQUEST = 101;

private String pathToFile = "";

public String selectFile()  {
    String path = "";
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT), chooser = null;
    intent.setType("*/*");
    chooser = Intent.createChooser(intent, "Find file to Print");
    startActivityForResult(chooser, PICK_FILE_REQUEST);
    path = pathToFile;
    return path;
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)   {
    if(requestCode == PICK_FILE_REQUEST){
        if(resultCode == Activity.RESULT_OK){
            pathToFile = data.getDataString();
            String temp = data.getStringExtra("path");
            Log.d("Files Fragment: ", pathToFile);
            Log.d("Files Fragment: ", temp);
        }
    }
}
Paul
  • 4,160
  • 3
  • 30
  • 56
napkinsterror
  • 1,915
  • 4
  • 18
  • 27
  • 1
    "What I need the activity to return is a full path to that file" -- many, if not most, will not return you a path to the file on the filesystem, as your app will not have access to the file. If you get a `content://` `Uri` back, you will need to use `ContentResolver` and methods like `openInputStream()` to read in the contents. – CommonsWare Dec 08 '14 at 21:31
  • I knew I was missing something. I didn't realize it was about access. Are you saying that usually they return content:// Uri as a standard? I just want this to work for most file browsers. I will right some logic to handle the content resolver. I have to rethink my process somewhat. Thanks! – napkinsterror Dec 08 '14 at 21:37
  • "Are you saying that usually they return content:// Uri as a standard?" -- that depends on the "they". `ACTION_GET_CONTENT` can be handled by anything from `MediaStore` (which will return `content://` `Uri` values) to a third-party file manager (which will return... something, though I'll be most return a traditional file path). `ACTION_OPEN_DOCUMENT`, as suggested in the one answer, will *always* return a `content://` `Uri`. And the percentage of times you get a `content://` `Uri` will only grow with time. – CommonsWare Dec 08 '14 at 21:40
  • I know, I was just asking your opinion for what is most common (which is a bad question for android apps!). If it doesn't come up with anything I will suggest a free app for them to download. I will have to create an awesome method to filter these content:// Uri 's !!! Looking forward to it. Thanks CommonsWare. I am still curious as to whether both of these actions will be viable for the file formats I listed in the original question. If you know great, if not I can find it out via testing. – napkinsterror Dec 08 '14 at 21:48
  • `ACTION_GET_CONTENT` and `ACTION_OPEN_DOCUMENT` are reasonable choices. With respect to MIME types, use the real MIME types for those file formats (available in seconds via your favorite search engine). The only file extension that you list that has no particular meaning is `.LOG`. – CommonsWare Dec 08 '14 at 21:49
  • For .LOG, I was just giving a general text file format to be an example for general text files, so I will do a partial MIME "text/*" or something similar. I think that thing you call a search engine will be able to help me with that as well. – napkinsterror Dec 08 '14 at 21:57

4 Answers4

26

I had a similar problem and spent at least 15 minutes searching for an example of how to actually code the support for EXTRA_MIME_TYPES

Thankfully I did find an example http://android-er.blogspot.co.uk/2015/09/open-multi-files-using.html, tried it, tested it, and it seems to work for my use-case which is to be able to find and load two similar, but not identical, mime-types (for whatever quirky reason the same csv file has one mime-type if I copy it to the device over USB and another if I download it from Google Drive). Here's my code snippet. Presumably you'd add more mime-types in the array of Strings to suit your needs.

public void findReviewsToLoad() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    String [] mimeTypes = {"text/csv", "text/comma-separated-values"};
    intent.setType("*/*");
    intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
    startActivityForResult(intent, FIND_FILE_REQUEST_CODE);
}
JulianHarty
  • 3,178
  • 3
  • 32
  • 46
  • Thanks a lot for the code. Was looking for an example to use Intent.EXTRA_MIME_TYPES and your post showed that right away. – Vinay Vissh Mar 15 '17 at 07:51
  • where i can know the mimeTypes ? i need from excel google drive – Yogi Arif Widodo Jan 31 '22 at 18:13
  • 4
    Seems to work perfectly with `("image/*", "application/pdf")`. Thanks ! – Jonathan F. Apr 11 '22 at 09:07
  • @YogiArifWidodo, see https://stackoverflow.com/questions/4212861/what-is-a-correct-mime-type-for-docx-pptx-etc. – CoolMind Mar 13 '23 at 10:20
  • 1
    Thanks, it works, but shows all files, while allows to select only supported. So other files will be shown grayed. But on some devices all files are still available to select. To change this behaviour, see the accepted answer or https://vadzimv.dev/2021/01/01/android-pick-file.html. – CoolMind Mar 13 '23 at 13:26
12

You can use Intent.ACTION_OPEN_DOCUMENT,

Each document is represented as a content:// URI backed by a DocumentsProvider, which can be opened as a stream with openFileDescriptor(Uri, String), or queried for DocumentsContract.Document metadata.

All selected documents are returned to the calling application with persistable read and write permission grants. If you want to maintain access to the documents across device reboots, you need to explicitly take the persistable permissions using takePersistableUriPermission(Uri, int).

Callers must indicate the acceptable document MIME types through setType(String). For example, to select photos, use image/*. If multiple disjoint MIME types are acceptable, define them in EXTRA_MIME_TYPES and setType(String) to */*.

For the more details, please refer here.

bjiang
  • 6,068
  • 2
  • 22
  • 35
  • 4
    Note that this is only available on API Level 19+. – CommonsWare Dec 08 '14 at 21:39
  • Thanks, this "EXTRA_MIME_TYPES" is what I couldn't find either. I'm going to have to look up some examples. I know basic MIME types, but I just wanted to accept 1 file, but I don't know what file type that is. The user gets to choose that. I think this is the right method by the little documentation I read through your link. It also will be for one time use and not persistent through reboots, but I appreciate the knowledge. – napkinsterror Dec 08 '14 at 21:42
  • @CommonsWare So, can you elaborate how to support multiple mime types for the api below 19 as well (API 16 to onwards) – Sagar Patel Oct 26 '17 at 04:43
  • 2
    @SagarPatel: There is no support for multiple MIME types prior to API Level 19, IIRC. In your UI, you would need to ask the user what MIME type to retrieve, then use that with `ACTION_GET_CONTENT`. Or, use a wildcard MIME type, then validate the actual MIME type of the content that the user chooses. – CommonsWare Oct 26 '17 at 10:39
2
private static final int CHOOSE_FILE_REQUEST = 1;
///////////////////////////////////////////////////
 public void chooseFile(){
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        String[] extraMimeTypes = {"application/pdf", "application/doc"};
        intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeTypes);
        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
        startActivityForResult(intent, CHOOSE_FILE_REQUEST);


    }

/////////////////////////////////////////////////////

   @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        String path = "";
        if (resultCode == RESULT_OK) {
            if (requestCode == CHOOSE_FILE_REQUEST) {
                ClipData clipData = data.getClipData();
                //null and not null path
                if(clipData == null){
                    path += data.getData().toString();
                }else{
                    for(int i=0; i<clipData.getItemCount(); i++){
                        ClipData.Item item = clipData.getItemAt(i);
                        Uri uri = item.getUri();
                        path += uri.toString() + "\n";
                    }
                }
            }
        }
        selectedFileTV.setText(path);
    }
Fakhar
  • 3,946
  • 39
  • 35
  • 1
    Thanks! According to https://stackoverflow.com/questions/4212861/what-is-a-correct-mime-type-for-docx-pptx-etc `doc` is `application/msword`. – CoolMind Mar 13 '23 at 10:17
2

Following code works for me when needed to select multiple MIME-types (This is in C# and Xamarin for Android):

List<string> mimeTypes = new List<string>();

// Filling mimeTypes with needed MIME-type strings, with * allowed.

if (mimeTypes.Count > 0)
{
    // Specify first MIME type
    intent.SetType(mimeTypes[0]);
    mimeTypes.RemoveAt(0);
}
if (mimeTypes.Count > 0)
{
    // Specify the rest MIME types but the first if any
    intent.PutExtra(Intent.ExtraMimeTypes, mimeTypes.ToArray());
}

Note, I tried intent.SetType("*.*") as described in other answers, but this resulted in wrong behavior.
E.g. when I implement the code to select photos from gallery, videos where selected as well:

intent.SetType("*.*");
intent.PutExtra(Intent.ExtraMimeTypes, new string[] {"image/*"});

So I come up with the code above: specify first MIME-type in intent.SetType, all the rest via intent.PutExtra(Intent.ExtraMimeTypes, ...)

sergtk
  • 10,714
  • 15
  • 75
  • 130
  • "specify first MIME-type in intent.SetType, all the rest via intent.PutExtra(Intent.ExtraMimeTypes, ...)". This worked for me – Francis S Aug 17 '23 at 08:32