Android intent filter: associate app with file extension
Asked Answered
J

17

101

I have a custom file type/extension that I want to associate my app with.

As far as I know, the data element is made for this purpose, but I can't get it working. http://developer.android.com/guide/topics/manifest/data-element.html According to the docs, and a lot of forum posts, it should work like this:

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:mimeType="application/pdf" />
</intent-filter>

Well, it does not work. What did I do wrong? I simply want to declare my own file type.

Jubilee answered 21/9, 2010 at 12:32 Comment(1)
Add android:label="@string/app_name" in intent filterHypoderma
P
119

You need multiple intent filters to address different situation you want to handle.

Example 1, handle http requests without mimetypes:

  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.BROWSABLE" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="http" />
    <data android:host="*" />
    <data android:pathPattern=".*\\.pdf" />
  </intent-filter>

Handle with mimetypes, where the suffix is irrelevant:

  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.BROWSABLE" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="http" />
    <data android:host="*" />
    <data android:mimeType="application/pdf" />
  </intent-filter>

Handle intent from a file browser app:

  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="file" />
    <data android:host="*" />
    <data android:pathPattern=".*\\.pdf" />
  </intent-filter>
Prescriptive answered 23/2, 2011 at 22:0 Comment(9)
I thought that the host="*" could be left out, but it started to be too broad.Prescriptive
@Phyrum Tea: Is there any way to associate a file extension to OTHER app using my app? My app generates .list files that I want to tell android they are text files and to open them with a text editor (my app is NOT a text editor).Algorithm
@LuisA.Florit did you try to call your files *.txt? It might work.Np
custom extension not working with out MimeType. If i give mimetype as / my app is showing when i click Gmail notification also.Athamas
Whilst this answer works in the particular special case of .pdf (or another common extension), it doesn't work for content scheme intents in the general case of a file type with no recognised mime type. Or, to be clear, it will accept content of ANY kind, and your application will be listed as an alternative for ALL file types.Kaiserdom
I tried tha last one and it didn't work. I needed to add "<data android:mimeType="*/*" />" to get it to work.Burgeon
Not working .. I'm fedup now searching from the past 2 days didt work. im using android 9Assyria
After adding android:label="@string/app_name" in intent filter it works fineHypoderma
@Hypoderma Can you please post complete example? I can't get this work on Android 11 for custom xxx extension.Tolidine
O
53

The other solutions did not work reliably for me until I added:

android:mimeType="*/*" 

Before that it worked in some applications, in some not...

complete solution for me:

<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <data android:scheme="file"  android:host="*" android:pathPattern=".*\\.EXT" android:mimeType="*/*"  />
</intent-filter>
Ora answered 16/10, 2012 at 12:59 Comment(4)
wow! this helped me. It seems that the android documentation is incorrect. All (scheme,host,path[Pattern],mimeType) has to be declared to workNp
You should have both a rule with the mimeType and one without if you want to be complete. See developer.android.com/guide/components/…Jeanettajeanette
What if you want to open your file app from Gmail?Abrogate
Thank you, you nailed it fella. There is so much conflicting information out there, I think Android has screwed the key file sharing feature. It has made it way more complex that it needs to be.Pontic
M
43

The answeres given by Phyrum Tea and yuku are very informative already.

I want to add that starting with Android 7.0 Nougat there is a change to the way file sharing between apps is handled:

From official Android 7.0 Changes:

For apps targeting Android 7.0, the Android framework enforces the StrictMode API policy that prohibits exposing file:// URIs outside your app. If an intent containing a file URI leaves your app, the app fails with a FileUriExposedException exception.

To share files between applications, you should send a content:// URI and grant a temporary access permission on the URI. The easiest way to grant this permission is by using the FileProvider class. For more information on permissions and sharing files, see Sharing Files.

If you have your own custom file ending without a specific mime-type (or i guess even with one) you may have to add a second scheme value to your intent-filter to make it work with FileProviders too.

Example:

<intent-filter>
    <action android:name="android.intent.action.VIEW" />

    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <data android:scheme="file" />
    <data android:scheme="content" />
    <data android:mimeType="*/*" />
    <!--
        Work around Android's ugly primitive PatternMatcher
        implementation that can't cope with finding a . early in
        the path unless it's explicitly matched.
    -->
    <data android:host="*" />
    <data android:pathPattern=".*\\.sfx" />
    <data android:pathPattern=".*\\..*\\.sfx" />
    <data android:pathPattern=".*\\..*\\..*\\.sfx" />
    <data android:pathPattern=".*\\..*\\..*\\..*\\.sfx" />
    <!-- keep going if you need more -->

</intent-filter>

The important thing here is the addition of

<data android:scheme="content" />

to the filter.

I had a hard time finding out about this little change which kept my activity from opening on Android 7.0 devices while everything was fine on older versions. I hope it helps someone.

Morse answered 6/10, 2016 at 0:45 Comment(3)
Without this tip and extra information I couldn't get my app to register with file explorers. This is crucial and should be upvotedOrlop
You saved a day! Big tnx. This should be accepted answer!Laster
⚠️ If you try to get the file provided via content scheme, like File(uri.path), it'll crash due to No such file or directory - will have to handle that scenario differently when updating to support Nougat+!Beleaguer
C
22

My findings:

You need several filters to deal with the different ways of retrieving a file. ie, by gmail attachment, by file explorer, by HTTP, by FTP... They all send very different intents.

And you need to filter out the intent that trigger your activity in your activity code.

For the example below, I created a fake file type new.mrz. And I retrieved it from gmail attachment and file explorer.

Activity code added in the onCreate():

        Intent intent = getIntent();
        String action = intent.getAction();

        if (action.compareTo(Intent.ACTION_VIEW) == 0) {
            String scheme = intent.getScheme();
            ContentResolver resolver = getContentResolver();

            if (scheme.compareTo(ContentResolver.SCHEME_CONTENT) == 0) {
                Uri uri = intent.getData();
                String name = getContentName(resolver, uri);

                Log.v("tag" , "Content intent detected: " + action + " : " + intent.getDataString() + " : " + intent.getType() + " : " + name);
                InputStream input = resolver.openInputStream(uri);
                String importfilepath = "/sdcard/My Documents/" + name; 
                InputStreamToFile(input, importfilepath);
            }
            else if (scheme.compareTo(ContentResolver.SCHEME_FILE) == 0) {
                Uri uri = intent.getData();
                String name = uri.getLastPathSegment();

                Log.v("tag" , "File intent detected: " + action + " : " + intent.getDataString() + " : " + intent.getType() + " : " + name);
                InputStream input = resolver.openInputStream(uri);
                String importfilepath = "/sdcard/My Documents/" + name; 
                InputStreamToFile(input, importfilepath);
            }
            else if (scheme.compareTo("http") == 0) {
                // TODO Import from HTTP!
            }
            else if (scheme.compareTo("ftp") == 0) {
                // TODO Import from FTP!
            }
        }

Gmail attachement filter:

        <intent-filter android:label="@string/app_name">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="content" />
            <data android:mimeType="application/octet-stream" />
        </intent-filter>
  • LOG: Content intent detected: android.intent.action.VIEW : content://gmail-ls/[email protected]/messages/2950/attachments/0.1/BEST/false : application/octet-stream : new.mrz

File explorer filter:

        <intent-filter android:label="@string/app_name">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file" />
            <data android:pathPattern=".*\\.mrz" />
        </intent-filter>
  • LOG: File intent detected: android.intent.action.VIEW : file:///storage/sdcard0/My%20Documents/new.mrz : null : new.mrz

HTTP filter:

        <intent-filter android:label="@string/rbook_viewer">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="http" />
            <data android:pathPattern=".*\\.mrz" />
        </intent-filter>

Private functions used above:

private String getContentName(ContentResolver resolver, Uri uri){
    Cursor cursor = resolver.query(uri, null, null, null, null);
    cursor.moveToFirst();
    int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
    if (nameIndex >= 0) {
        return cursor.getString(nameIndex);
    } else {
        return null;
    }
}

private void InputStreamToFile(InputStream in, String file) {
    try {
        OutputStream out = new FileOutputStream(new File(file));

        int size = 0;
        byte[] buffer = new byte[1024];

        while ((size = in.read(buffer)) != -1) {
            out.write(buffer, 0, size);
        }

        out.close();
    }
    catch (Exception e) {
        Log.e("MainActivity", "InputStreamToFile exception: " + e.getMessage());
    }
}
Cochleate answered 25/9, 2014 at 9:7 Comment(1)
Does the activity have to be launcher or main type? Because I tried the gmail attachment filter and it's not working.Robinia
C
18

The pathPattern

<data android:pathPattern=".*\\.pdf" />

does not work if the file path contains one or more dots before ".pdf".

This will work:

<data android:pathPattern=".*\\.pdf" />
<data android:pathPattern=".*\\..*\\.pdf" />
<data android:pathPattern=".*\\..*\\..*\\.pdf" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.pdf" />

Add more if you want to support more dots.

Cambrian answered 28/8, 2012 at 5:44 Comment(2)
As of now this is the only option for going in to sub foldersZosima
I'm using the same code but IDE give me a warning, see this question #35834276Cerebro
S
8

I've been trying to get this to work for ages and have tried basicly all the suggested solutions and still cannot get Android to recognise specific file extensions. I have an intent-filter with a "*/*" mimetype which is the only thing that seems to work and file-browsers now list my app as an option for opening files, however my app is now shown as an option for opening ANY KIND of file even though I've specified specific file extensions using the pathPattern tag. This goes so far that even when I try to view/edit a contact in my contacts list Android asks me if I want to use my app to view the contact, and that is just one of many situations where this occurs, VERY VERY annoying.

Eventually I found this google groups post with a similar question to which an actual Android framework engineer replied. She explains that Android simply does not know anything about file-extensions, only MIME-types (https://groups.google.com/forum/#!topic/android-developers/a7qsSl3vQq0).

So from what I've seen, tried and read, Android simply cannot distinguish between file-extensions and the pathPattern tag is basicly a gigantic waste of time and energy. If you are fortunate enough to only need files of a certain mime-type (say text, video or audio), you can use an intent-filter with a mime-type. If you need a specific file-extension or a mime-type not known by Android however then you're out of luck.

If I'm wrong about any of this please tell me, so far I've read every post and tried every proposed solution I could find but none have worked.

I could write another page or two about how common these kinds of things seem to be in Android and how screwed up the developer experience is, but I'll save you my angry rantings ;). Hope I saved someone some trouble.

Supposed answered 29/10, 2014 at 16:1 Comment(0)
P
5

Markus Ressel is correct. Android 7.0 Nougat no longer permits file sharing between apps using a file URI. A content URI must be used. However, a content URI does not allow a file path to be shared, only a mime type. So you cannot use a content URI to associate your app with your own file extension.

Drobpox has an interesting behavior on Android 7.0. When it meets an unknown file extension it appears to form a file URI intent but instead of launching the intent it calls the operating system to find out which apps can accept the intent. If there is only one app that can accept that file URI it then sends an explicit content URI directly to that app. So to work with Dropbox you do not need to change the intent filters on your app. It does not require a content URI intent filter. Just make sure the app can receive a content URI and your app with your own file extension will work with Dropbox just like it did before Android 7.0.

Here is an example of my file loading code modified to accept a content URI:

Uri uri = getIntent().getData();
if (uri != null) {
    File myFile = null;
    String scheme = uri.getScheme();
    if (scheme.equals("file")) {
        String fileName = uri.getEncodedPath();
        myFile = new File(filename);
    }
    else if (!scheme.equals("content")) {
        //error
        return;
    }
    try {
        InputStream inStream;
        if (myFile != null) inStream = new FileInputStream(myFile);
        else inStream = getContentResolver().openInputStream(uri);
        InputStreamReader rdr = new InputStreamReader(inStream);
        ...
    }
}
Paltry answered 11/6, 2017 at 23:50 Comment(0)
B
2

Try adding

<action android:name="android.intent.action.VIEW"/>
Bloomy answered 21/9, 2010 at 13:3 Comment(5)
Now that works with factory set file types like application/pdf, how would I declare my own file type? And when I say file type, I mean mimeType ;)Jubilee
What kind of file do you want this mimetype to catch? Also, is this file opened from the browser or file manager or sent from another application you made?Bloomy
could be from the browser, mail client, file manager, or anywhere.. Or my own app ofc :) file extension is custom, specified by client.Jubilee
Well, I'm still stuck...could anybody help please?Jubilee
@Jubilee did you every get this sorted. I'm stuck on this!Oliana
N
2

Those who having issue with other File Manager\Explorer apps, as @yuku and @phyrum-tea answered

This works with LG default file manager app

     <intent-filter android:label="@string/app_name_decrypt">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file" />
            <data android:pathPattern=".*\\.lock" />
            <data android:pathPattern=".*\\..*\\.lock" />
            <data android:pathPattern=".*\\..*\\..*\\.lock" />
        </intent-filter>

but could not work with ES File Explorer and other File Managers so i added

 android:mimeType="*/*"

then it works with ES Explorer but LG file manager could not detect file type so my solution is

     <intent-filter android:label="@string/app_name_decrypt">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file" />
            <data android:pathPattern=".*\\.lock" />
            <data android:pathPattern=".*\\..*\\.lock" />
            <data android:pathPattern=".*\\..*\\..*\\.lock" />
        </intent-filter>
        <intent-filter android:label="@string/app_name_decrypt">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file"/>
            <data android:scheme="content" />
            <data android:mimeType="*/*" />
            <data android:pathPattern=".*\\.lock" />
            <data android:pathPattern=".*\\..*\\.lock" />
            <data android:pathPattern=".*\\..*\\..*\\.lock" />
        </intent-filter>
Nedanedda answered 26/10, 2017 at 15:21 Comment(0)
A
2

You try this it will help for you.Instead of pdf you can use other extensions also. First you have to add read external storage permission in androidmanifest.xml file.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Then in the androidmanifest file in the Activity tag, you add an intent-filter as shown below.

            <action android:name="android.intent.action.SEND" />

            <action android:name="android.intent.action.VIEW" />

             <category android:name="android.intent.category.DEFAULT" />

            <data android:mimeType= "application/pdf" />

            <data android:host="*" />

        </intent-filter>

Finally in your code, you get path of the pdf file as shown below:

Intent intent=getIntent();

if(intent!=null) {          

        String action=intent.getAction();

        String type=intent.getType();

        if(Intent.ACTION_VIEW.equals(action) && type.endsWith("pdf")) {

            // Get the file from the intent object

            Uri file_uri=intent.getData();

            if(file_uri!=null)

                filepath=file_uri.getPath();

            else

                filepath="No file";

        }

        else if(Intent.ACTION_SEND.equals(action) && type.endsWith("pdf")){

            Uri uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);

            filepath = uri.getPath();

        }
Angeloangelology answered 26/11, 2018 at 13:46 Comment(2)
For this Intent.ACTION_VIEW, unable to grab file path it causes error : java.io.FileNotFoundException: Permission denied. Its only from some specific applications, not every time. any solutions?Timisoara
@HardikJoshi The apps may not be setting permission GRANT_READ_URI_PERMISSION.Panthea
S
2

Read opening file in kotlin:

private fun checkFileOpening(intent: Intent) {
    if (intent.action == Intent.ACTION_VIEW && (intent.scheme == ContentResolver.SCHEME_FILE
                    || intent.scheme == ContentResolver.SCHEME_CONTENT)) {

        val text = intent.data?.let {
            contentResolver.openInputStream(it)?.bufferedReader()?.use(BufferedReader::readText) 
        }
    }
}
Sideboard answered 12/6, 2019 at 16:46 Comment(0)
R
1

For Gmail attachment, you can use:

<intent-filter android:label="@string/app_name">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <data android:scheme="content" />
  <data android:mimeType="application/pdf" /> <!-- .pdf -->
  <data android:mimeType="application/msword" /> <!-- .doc / .dot -->
  <data android:mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document" /> <!-- .docx -->
  <data android:mimeType="application/vnd.ms-excel" />  <!-- .xls / .xlt / .xla -->
  <data android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />  <!-- .xlsx -->
  <data android:mimeType="application/vnd.ms-powerpoint" />  <!-- .ppt / .pps / .pot / .ppa -->
  <data android:mimeType="application/vnd.openxmlformats-officedocument.presentationml.presentation" /> <!-- .pptx -->
  <data android:mimeType="application/vnd.openxmlformats-officedocument.presentationml.slideshow" /> <!-- .ppsx -->
  <data android:mimeType="application/zip" /> <!-- .zip -->
  <data android:mimeType="image/jpeg" /> <!-- .jpeg -->
  <data android:mimeType="image/png" /> <!-- .png -->
  <data android:mimeType="image/gif" /> <!-- .gif -->
  <data android:mimeType="text/plain" /> <!-- .txt / .text / .log / .c / .c++ / ... -->

Add as many mime types as needed. I only need those for my project.

Regressive answered 14/1, 2015 at 10:28 Comment(0)
G
1
         <!--
            Works for Files, Drive and DropBox
        -->
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file" />
            <data android:mimeType="*/*" />
            <data android:host="*" />
            <data android:pathPattern=".*\\.teamz" />
        </intent-filter>

        <!--
            Works for Gmail
        -->
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <category android:name="android.intent.category.BROWSABLE" />
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:host="gmail-ls" android:scheme="content" android:mimeType="application/octet-stream"/>
        </intent-filter>

Notice that this will get your app open all gmail file attachments, there is no way to work around it

Glick answered 28/9, 2015 at 18:39 Comment(1)
This will handle every single file emailed to the user, not just .teamzAbrogate
C
1

Content URI ftw, and with the intent filter in the manifest... if your files have a custom extension .xyz, add a matching mime type:

        <intent-filter>
            <action android:name="android.intent.action.VIEW" />

            <category android:name="android.intent.category.DEFAULT" />

            <data
                android:host="*"
                android:mimeType="application/xyz"
                android:scheme="content" />
        </intent-filter>

Some apps such as email seem to convert the extension into a mime type. Now I can click on the attachment in email and have it open in my app.

Catarinacatarrh answered 14/2, 2018 at 2:42 Comment(0)
M
0

Put this intent filter inside the activity tag in manifest you'd like to open touching the file:

<intent-filter android:priority="999">
    <action android:name="android.intent.action.VIEW" />

    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <category android:name="android.intent.category.OPENABLE" />

    <data android:host="*" />
    <data android:mimeType="application/octet-stream" />
    <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\.yourextension" />
    <data android:scheme="content" />
</intent-filter>
Merit answered 3/5, 2019 at 17:9 Comment(0)
L
0

None of the above solution worked for me, I had to combine all of them to make it work like this:

<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="http"/>
    <data android:host="*"/>
    <data android:pathPattern=".*\\.pdf"/>
</intent-filter>
<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="content"/>
    <data android:scheme="file"/>
    <data android:mimeType="application/pdf"/>
</intent-filter>
<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="content"/>
    <data android:scheme="file"/>
    <data android:host="*"/>
    <data android:pathPattern=".*\\.pdf"/>
</intent-filter>
Lafferty answered 9/6, 2021 at 16:52 Comment(0)
C
-1

// I have tried this code. And it is working well. You can use this code to accept pdf file.

<intent-filter>
   <action android:name="android.intent.action.SEND" />
   <category android:name="android.intent.category.DEFAULT" />
   <data android:mimeType="application/pdf" />
   <data android:pathPattern=".*\\.pdf" />
   <data android:pathPattern=".*\\..*\\.pdf" />
   <data android:pathPattern=".*\\..*\\..*\\.pdf" />
   <data android:pathPattern=".*\\..*\\..*\\..*\\.pdf" />
</intent-filter>
Chandlery answered 24/5, 2019 at 18:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.