95

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.

Peter O.
  • 28,965
  • 14
  • 72
  • 87
Tamas
  • 1,294
  • 1
  • 12
  • 14

16 Answers16

117

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>
Phyrum Tea
  • 2,573
  • 1
  • 17
  • 20
  • I thought that the host="*" could be left out, but it started to be too broad. – Phyrum Tea Feb 24 '11 at 07:13
  • 1
    @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). – Luis A. Florit Jun 18 '13 at 20:37
  • @LuisA.Florit did you try to call your files *.txt? It might work. – Gavriel Jan 16 '15 at 11:29
  • custom extension not working with out MimeType. If i give mimetype as */* my app is showing when i click Gmail notification also. – madan V Apr 27 '15 at 04:40
  • 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. – David Sainty Jun 16 '15 at 12:05
  • I tried tha last one and it didn't work. I needed to add "" to get it to work. – grAPPfruit Jul 25 '16 at 10:05
  • 2
    Not working .. I'm fedup now searching from the past 2 days didt work. im using android 9 – Ahmad Arslan Aug 29 '19 at 08:46
  • After adding android:label="@string/app_name" in intent filter it works fine – Prashant Jan 01 '20 at 20:29
49

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>
Emmanuel Touzery
  • 8,012
  • 2
  • 55
  • 67
  • 1
    wow! this helped me. It seems that the android documentation is incorrect. All (scheme,host,path[Pattern],mimeType) has to be declared to work – Gavriel Jan 16 '15 at 11:34
  • You should have **both** a rule with the `mimeType` and one without if you want to be complete. See https://developer.android.com/guide/components/intents-filters.html#DataTest – Andrew Sun Dec 16 '16 at 03:33
  • 1
    What if you want to open your file app from Gmail? – IgorGanapolsky Oct 18 '17 at 14:43
37

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.

Community
  • 1
  • 1
Markus Ressel
  • 1,061
  • 1
  • 13
  • 12
  • Without this tip and extra information I couldn't get my app to register with file explorers. This is crucial and should be upvoted – u2tall Jan 21 '19 at 18:35
  • You saved a day! Big tnx. This should be accepted answer! – Andris Feb 14 '19 at 07:59
  • ⚠️ 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+! – Jordan H Feb 21 '19 at 17:07
20

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/l.foul@gmail.com/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());
    }
}
LFOR
  • 201
  • 2
  • 3
  • Does the activity have to be launcher or main type? Because I tried the gmail attachment filter and it's not working. – Amit Jun 30 '18 at 16:55
16

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.

Randy Sugianto 'Yuku'
  • 64,635
  • 54
  • 168
  • 216
  • 1
    As of now this is the only option for going in to sub folders – LokiDroid May 27 '14 at 09:23
  • I'm using the same code but IDE give me a warning, see this question http://stackoverflow.com/questions/35833776/manifest-custom-file-format-opening-pattern-not-well-formatted?noredirect=1#comment59334593_35833776 – AndreaF Mar 07 '16 at 17:01
5

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.

ntninja
  • 760
  • 7
  • 15
PeeGee85
  • 184
  • 2
  • 3
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);
        ...
    }
}
Peter Newman
  • 51
  • 1
  • 3
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>
Sumit
  • 832
  • 10
  • 18
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();

        }
Arjun Othayoth
  • 151
  • 1
  • 5
  • 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? – Hardik Joshi May 01 '19 at 08:51
  • @HardikJoshi The apps may not be setting permission GRANT_READ_URI_PERMISSION. – Mira_Cole Aug 23 '19 at 15:41
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) 
        }
    }
}
brucemax
  • 413
  • 4
  • 10
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.

Hrk
  • 2,661
  • 5
  • 27
  • 41
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

1

Try adding

<action android:name="android.intent.action.VIEW"/>
magaio
  • 645
  • 4
  • 6
  • 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 ;) – Tamas Sep 21 '10 at 13:34
  • 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? – magaio Sep 22 '10 at 00:02
  • could be from the browser, mail client, file manager, or anywhere.. Or my own app ofc :) file extension is custom, specified by client. – Tamas Sep 22 '10 at 09:16
  • Well, I'm still stuck...could anybody help please? – Tamas Sep 28 '10 at 09:40
  • @Tamas did you every get this sorted. I'm stuck on this! – StuStirling Feb 03 '14 at 13:55
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.

Sofi Software LLC
  • 3,516
  • 1
  • 31
  • 31
-1

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>
Ráfagan
  • 1,724
  • 2
  • 11
  • 18
-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>
vimuth
  • 2,928
  • 13
  • 43
  • 73