I noticed one of the big video apps on Android has all sorts of path patterns, I'm assuming to catch subdirectories or weird names? they basically end up having a bunch of pattersn that look like this: <data android:pathPattern=".*..*..*..*..*..*..*..*..*..*..*..*..*..*..*.3gp" />
Is there no easier way to simplify that path?
Here is a longer example:
</intent-filter>
<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="http" android:host="*" />
<data android:pathPattern=".*.3gp" />
<data android:pathPattern=".*..*.3gp" />
<data android:pathPattern=".*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*..*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*..*..*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*..*..*..*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*..*..*..*..*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*..*..*..*..*..*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*..*..*..*..*..*..*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*..*..*..*..*..*..*..*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*..*..*..*..*..*..*..*..*..*..*.3gp" />
<data android:pathPattern=".*..*..*..*..*..*..*..*..*..*..*..*..*..*..*.3gp" />
Reference
<activity name="com.keepassdroid.PasswordActivity">
<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:mimeType="*/*" />
<data android:pathPattern=".*\\.3gp" />
<data android:host="*" />
</intent-filter>
</activity>
The scheme of file indicates that this should happen when a local file is opened (rather than protocol like HTTP).
mimeType can be set to */* to match any mime type.
pathPattern is where you specify what extension you want to match (in this example .3gp). The .* at the beginning matches any squence of characters. These strings require double escaping, so \\. matches a literal period. Then, you end with your file extension. One caveat with pathPattern is that .* is not a greedy match like you would expect if this was a regular expression. This pattern will fail to match paths that contain a . before the .3gp. For a more detailed discussion of this issue and a workaround see here
Finally, according to the Android documentation, both host and scheme attributes are required for the pathPattern attribute to work, so just set that to the wildcard to match anything.
Now, if you select a .3gp file in an app like Linda File Manager, my app shows up as an option. I should note that this alone does not allow you to download this filetype in a browser, since this only registers with the file scheme. Having an app like Linda File Manager on your phone resisters itself generically allowing you to download any file type.
Related
Background
It is a very known feature on modern desktop OSs to be able to handle files, allowing the user to open them from the file-manager and other apps, as "file assosiation" configuration.
The problem
So far, it wasn't such a convenient thing on Android, for both users and developers, to set an association of a file type.
Up to Android API 30 (Android 11, AKA Android R), you had to use some weird workarounds, especially if the file isn't a known one.
Example for "xyz" :
<intent-filter>
<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:scheme="content" />
<data android:scheme="file" />
<data android:host="*" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.xyz" />
<data android:pathPattern=".*\\..*\\.xyz" />
<data android:pathPattern=".*\\..*\\..*\\.xyz" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.xyz" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.xyz" />
...
</intent-filter>
And if it's a known one, such as a ZIP file, maybe something like this (not sure if it's the minimal/best one) :
<intent-filter>
<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:scheme="package" />
<data android:scheme="content" />
<data android:scheme="file" />
<data android:mimeType="application/x-zip" />
<data android:mimeType="application/x-zip-compressed" />
<data android:mimeType="application/zip" />
</intent-filter>
In fact, even if it's a known one, you should still consider using both ways, as some apps handle only the first way.
But on Android API 31 (Android 12, AKA Android S) , it seems it has changed and we might be able to write less in the manifest (sadly probably only if minimal API is 31).
What I've found
The only thing I've found for this is on the docs:
https://developer.android.com/reference/android/R.attr#pathSuffix
https://developer.android.com/reference/android/R.attr#pathAdvancedPattern
Sadly, there were no examples that I could find, and I don't even know if it's the official way to handle files now.
The questions
Is it now really the true, valid, official way to handle files on Android ?
How should I use it now? Do I still need to set mimeType ? Will this work for files of any kind, whether known or not?
Is it possible to set it up this way, and stop using the ways I've mentioned? Or this is only if I set the minSdk to be 31 ?
Does it affect the experience for users, in any way ?
OK after some research by myself, here are my answers to my questions:
Is it now really the true, valid, official way to handle files on Android ?
Seems so.
How should I use it now? Do I still need to set mimeType ? Will this work for files of any kind, whether known or not?
Seems it's possible to just use this for the first part:
<intent-filter>
<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:scheme="content" />
<data android:scheme="file" />
<data android:host="*" />
<data android:mimeType="*/*" />
<data android:pathSuffix=".xyz" />
...
</intent-filter>
The second part stays the same because it is used in special cases.
However, I failed to use the alternative of pathAdvancedPattern for some reason (tried using <data android:pathAdvancedPattern=".*\\.xyz" />).
Is it possible to set it up this way, and stop using the ways I've mentioned? Or this is only if I set the minSdk to be 31 ?
Sadly no. I've requested it to be a syntactic sugar from pathSuffix to pathPattern for pre-Android-12, here.
Does it affect the experience for users, in any way ?
Doesn't seem so.
I'm creating a custom intent filter which will open my app when I tap a file with a certain file extension (.myextension in this case.) The intent-filter is below. This currently works perfectly every time I open a .myextension file from my emulator. However, when I try it from my device, it no longer works.
On my device, when I tap a .myextension file in the Files browser app, I see Unable to preview file. instead of automatically opening the app. I've tried opening the file from quite a few locations (Files app, GDrive, Downloads folder, Slack/Gmail, both internal storage and SDCard.) I'm using Android 10 on both my emulator and my device.
<intent-filter android:label="My App">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:scheme="content"
android:mimeType="*/*"
android:host="*"
android:pathPattern=".*\\.myextension" />
</intent-filter>
I've also tried replacing that data tag/adding a second tag with this block but it doesn't seem to help:
<data
android:scheme="file"
android:mimeType="*/*"
android:host="*"
android:pathPattern=".*\\.myextension" />
Am I missing something obvious? Any help would be greatly appreciated. Thanks!
This currently works perfectly every time I open a .myextension file from my emulator.
It will fail much of the time, as it is not really tied to an emulator versus a device.
However, when I try it from my device, it no longer works.
A content Uri is not required to have a file extension, just as an https URL is not required to have a file extension. Much of the time, a content Uri will not have a file extension, and such a Uri will not match your <intent-filter>.
ACTION_VIEW is mostly for files with a widely-recognized MIME type.
I've also tried replacing that data tag/adding a second tag with this block but it doesn't seem to help
file Uri values have been generally banned since Android 7.0.
<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:pathPattern=".*\\.myextension" />
<data android:pathPattern="/*.*\\.myextension" />
<data android:pathPattern="/*.*\\..*\\.myextension" />
<data android:pathPattern="/*.*\\..*\\..*\\.myextension" />
<data android:pathPattern="/*.*\\..*\\..*\\..*\\.myextension" />
<data android:pathPattern="/*.*\\..*\\..*\\..*\\..*\\.myextension" />
<data android:pathPattern="/*.*\\..*\\..*\\..*\\..*\\..*\\.myextension" />
<data android:pathPattern="/*.*\\..*\\..*\\..*\\..*\\..*\\..*\\.myextension" />
<data android:pathPattern="/*.*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.myextension" />
<data android:host="*" />
</intent-filter>
Though it doesnt work in some of the file managers Better first try in ESExplorer.
I am saving a binary to a file with a custom extension for instance .custom. How do I save it to a specific mime type? I want my app to be called to open that custom file. In the manifest I used / as mimeType but the app gets called even when tapping an image. I used octet-stream but the file doesn't get recognized and the app does not get opened. I just want to save a binary with custom extension and the OS would call my app when it encounters this file.
<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:mimeType="application/octet-stream" />
<data android:pathPattern=".*\\.test" />
<data android:host="*" />
</intent-filter>
Finally, after several views only without answers I figured it out. The post on this link helped me resolve my issue.
https://publish.illinois.edu/weiyang-david/2013/04/11/how-to-write-intent-filter-dealing-with-mime-type-problem/
This example was it
<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>
I replaced the scheme to "file" and replaced with my pathPattern. Removing the mimeType was key to solving my issue. File browsers now suggests to open the custom file with my app.
Want to create an android application, which opens a custom-build file extension (for example, I want to open .abcd files)
It is something like Adobe Reader that opens .pdf files, or Photo Viewer that opens .jpg files
Specific conditions:
1. The .abcd file should be outside / external from the application itself. (as .pdf is to Adobe Reader)
2. The .abcd file would be a zipped file, which contains few folders and .xml, .txt, and .jpg files. I think I want to extract it - maybe temporarily - to somewhere in the storage (definitely need a zipper/unzipper library), then read the individual .xml, .txt, and .jpg files.
Looking for insights and answers for this problem.
Additional information:
I am relatively new to Android programming.
I think you need to do that type of customization via intent-filter something like:
<intent-filter android:icon="your_drawable-resource"
android:label="your_string_resource"
android:priority="integer">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:host="*" />
<data android:pathPattern=".*\\.YOUR_CUSTOM_FILE_EXTENSION" />
</intent-filter>
Also you should look:
Custom Filetype in Android not working
Android intent filter for a particular file extension?
android intent filter for custom file extension
One possible answer is shown
here
. Try some customisation for intent filters.
<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>
I have seen numerous answers on here about creating an intent filter for a custom file extension, but none of them seem to be answering my question:
I have an intent filter that works right now... when I browse for my file or when I open it from an email attachment, my app will appear in the list. The file itself has a custom extension of "tgtp", but it is basically just an xml file.
The problem I am having is that although this intent filter works, it also appears to add my app to every chooser for every type of file on my phone. An example would be if I clear my contacts app defaults and click on one of my contacts, it says my app can open it.
I've tried dozens of different combinations of intent filters with different schemes, mime types, etc... and some still let me open the file if i browse with a file browser, but I specifically need to be able to open email attachments and open as file browser. I am yet to find an intent filter(s) that allow me to do that without making my app available for every other intent chooser.
Here is my current intent-filter that uses my app to open everything:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.tgtp" />
</intent-filter>
Thank you in advance
The only way to solve this problem is add scheme and host attributes to your intent filter:
<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:pathPattern=".*\\.tgtp" />
<data android:host="*" />
</intent-filter>
That is because in documentation says that android:pathPattern only works if has an scheme and host defined.
http://developer.android.com/guide/topics/manifest/data-element.html
Hope it helps.
I've been struggling with this quite a bit for a custom file extension, myself. After a lot of searching, I found this web page where the poster discovered that Android's patternMatcher class (which is used for the pathPattern matching in Intent-Filters) has unexpected behavior when your path contains the first character of your match pattern elsewhere in the path (like if you're trying to match "*.xyz", the patternMatcher class stops if there's an "x" earlier in your path). Here's what he found for a workaround, and worked for me, although it is a bit of a hack:
PatternMatcher is used for pathPattern at IntentFilter But,
PatternMatcher's algorithm is quite strange to me. Here is algorithm
of Android PatternMatcher.
If there is 'next character' of '.*' pattern in the middle of string,
PatternMatcher stops loop at that point. (See PatternMatcher.java of
Android framework.)
Ex. string : "this is a my attachment" pattern : ".att.". Android
PatternMatcher enter loop to match '.' pattern until meet the next
character of pattern (at this example, 'a') So, '.' matching loop
stops at index 8 - 'a' between 'is' and 'my'. Therefore result of this
match returns 'false'.
Quite strange, isn't it. To workaround this - actually reduce
possibility - developer should use annoying stupid pathPattern.
Ex. Goal : Matching uri path which includes 'message'.
<intent-filter>
...
<data android:pathPattern=".*message.*" />
<data android:pathPattern=".*m.*message.*" />
<data android:pathPattern=".*m.*m.*message.*" />
<data android:pathPattern=".*m.*m.*m.*message.*" />
<data android:pathPattern=".*m.*m.*m.*m.*message.*" />
...
</intent-filter>
This is especially issued when matching with custom file extention.
I have the exact same problem, and in my case, both other answers don't work. The closest I came is when I combine both benjamin and sabadow's answers, -and- leave out the dot in the extension, so like this: (i'm using my custom ".trk" extension)
<!-- For opening/viewing only trk-files: (works in google drive and "File Manager", not gmail) -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEND_MULTIPLE" />
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" android:mimeType="*/*" android:pathPattern=".*trk" android:host="*" />
<data android:scheme="file" android:mimeType="*/*" android:pathPattern=".*t.*trk" android:host="*" />
<data android:scheme="file" android:mimeType="*/*" android:pathPattern=".*t.*t.*trk" android:host="*" />
<data android:scheme="file" android:mimeType="*/*" android:pathPattern=".*t.*t.*t.*trk" android:host="*" />
<data android:scheme="file" android:mimeType="*/*" android:pathPattern=".*t.*t.*t.*t.*trk" android:host="*" />
<data android:scheme="file" android:mimeType="*/*" android:pathPattern=".*t.*t.*t.*t.*t.*trk" android:host="*" />
<data android:scheme="file" android:mimeType="*/*" android:pathPattern=".*t.*t.*t.*t.*t.*t.*trk" android:host="*" />
<data android:scheme="file" android:mimeType="*/*" android:pathPattern=".*t.*t.*t.*t.*t.*t.*t.*trk" android:host="*" />
</intent-filter>
<!-- For catching attachments in Gmail: (also triggers non-trk-files that aren't associated with some other app, but not non-trk-files that already have one of more associations, strangely) -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEND_MULTIPLE" />
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="*/*" android:scheme="content" />
</intent-filter>
<!-- For catching share actions from apps: (also triggered by sharing actions for all other file types) -->
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/*" />
</intent-filter>
It's a bit long, but you might not need all lines, like the SENDTO and SEND_MULTIPLE actions. I just need it to work in all cases where it could work. Sadly it also triggers in some, but not all, other cases.
One possible answer is shown
here
. Basically, try the following intent filter inside the desired activity tag to open:
<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>