Android has a guide about how to implement app links. That is, if my app declares that it handles certain web links, and I try to open this link in any other app, the system intercepts this and takes the user straight to my app, instead of the browser, so that I can show relevant content straight in my app. Very handy.
What I am missing in the guide is two things:
How to implement app links with wildcard domains. I would like my app to handle links to *.example.com, that is, all links to subdomains of example.com (test.example.com, something.example.com etc);
How to implement app links only to specific paths on my site. For example I want to intercept test.example.com/something, but not test.example.com/other. The first one should come to my app, the other one to my browser;
The corresponding iOS guide shows that iOS handles both of these scenarios (though the wildcard part was unclear from the docs and I had to clarify with Apple Support that you need to place the association file in the root domain, not a subdomain).
Can Android App Links handle wildcard domains and only a subset of paths?
Update: now you can handle wildcard domains, using the Digital Asset Links
aurilio explained it in his newer answer
The whole process is documented here: https://developer.android.com/training/app-links/verify-site-associations
To sum it up, now you can use a wildcard in the host tag, and you must upload a json file called assetlinks.json to the /.well-known folder/route on your root domain.
Alternatively, if you declare your hostname with a wildcard (such as *.example.com), you must publish your assetlinks.json file at the root hostname (example.com)
You will also need to add the attribute android:autoVerify="true" to your intent-filter tag.
Here's the whole example on the Android side:
<application>
<activity android:name=”MainActivity”>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="*.example.com" />
</intent-filter>
</activity>
</application>
Here's the previous answer from 2016:
Unfortunately it seems that Android cannot handle wildcard domains.
If you look at the API guide for the data tag (https://developer.android.com/guide/topics/manifest/data-element.html), you can see they mention that wildcard is available for pathPattern and mimeType, but not for host.
The thing is that, as explained by CommonsWare in an other post on the subject (https://stackoverflow.com/a/34068591/4160079),
the domains are checked at install time, and there is no means to add new domains except by shipping a new edition of the app with a new manifest.
So you will have to manually list all the subdomains available, and update the app whenever a new subdomain is launched.
Here's how you declare multiple subdomains:
<activity android:name="MainActivity">
<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" />
<data android:host="subdomain1.example.com" />
<data android:host="subdomain2.example.com" />
<data android:host="subdomain3.example.com" />
</intent-filter>
</activity>
Yes, you can handle only a subset of paths
It is the same idea, just list the paths you want using the path attribute (again, see data tag API guide above).
If you use query strings or path params, prefer using pathPrefix.
If necessary, you can use wildcards here, by choosing pathPattern instead.
The path part of a URI which must begin with a /. The path attribute specifies a complete path that is matched against the complete path in an Intent object. The pathPrefix attribute specifies a partial path that is matched against only the initial part of the path in the Intent object. The pathPattern attribute specifies a complete path that is matched against the complete path in the Intent object, but it can contain the following wildcards:
An asterisk ('') matches a sequence of 0 to many occurrences of the immediately preceding character.
A period followed by an asterisk (".") matches any sequence of 0 to many characters.
Here's a few examples:
<activity android:name="MainActivity">
<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" />
<data android:host="subdomain1.example.com" />
<data android:host="subdomain2.example.com" />
<data android:host="subdomain3.example.com" />
<data android:path="/path1" /> <!-- matches /path1 only -->
<data android:pathPrefix="/path2" /> <!-- matches /path2, /path2/something or also /path2?key=value etc... -->
<data android:pathPattern="/wild.*" /> <!-- matches /wild, /wild3, /wilderness etc... -->
</intent-filter>
</activity>
Android cannot handle wildcard domain as such(as per their documentation as of today), but, this will answer your query on inclusion and exclusion of certain /paths.
To implement deep linking for URL's like-
http://example.com/gizmos?1234,
http://example.com/gizmos/1234,
http://example.com/gizmos/toys/1234,
etc.
Your XML should look something like this-
<activity android:name="com.example.android.GizmosActivity" android:label="#string/title_gizmos" >
<intent-filter android:label="#string/filter_title_viewgizmos">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <!-- Accepts URIs that begin with "example://gizmos” -->
<data android:scheme="example" android:host="gizmos" />
</intent-filter>
<intent-filter android:label="#string/filter_title_viewgizmos">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with "http://example.com/gizmos” -->
<data android:scheme="http" android:host="example.com" android:pathPrefix="/gizmos" />
</intent-filter>
</activity>
Now considering that you were able to achieve this, Here is how you restrict access to parts of your app content-
<?xml version="1.0" encoding="utf-8"?>
<search-engine xmlns:android="http://schemas.android.com/apk/res/android">
<noindex uri="http://example.com/gizmos/hidden_uri"/>
<noindex uriPrefix="http://example.com/gizmos/hidden_prefix"/>
<noindex uri="gizmos://hidden_path"/>
<noindex uriPrefix="gizmos://hidden_prefix"/>
</search-engine>
And The Manifest part-
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.Gizmos">
<application>
<activity android:name="com.example.android.GizmosActivity" android:label="#string/title_gizmos" >
<intent-filter android:label="#string/filter_title_viewgizmos">
<action android:name="android.intent.action.VIEW"/> ...
</activity>
<meta-data android:name="search-engine" android:resource="#xml/noindex"/>
</application>
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
For more details and explanation of this example, you can take a look at-
Android Deep Linking
Hope it helps, Happy Coding
Quoting from: https://developer.android.com/training/app-links/verify-site-associations.html
Alternatively, if you declare your hostname with a wildcard (such as *.example.com), you must publish your assetlinks.json file at the root hostname (example.com). For example, an app with the following intent filter will pass verification for any sub-name of example.com (such as foo.example.com) as long as the assetlinks.json file is published at https:/ /example.com/.well- known/assetlinks.json:
<application>
<activity android:name=”MainActivity”>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="www.example.com" />
<data android:scheme="https" android:host="mobile.example.com" />
</intent-filter>
</activity>
</application>
Related
I'm currently trying to open an Android application from a webpage. And to pass two parameters.
I went for the intent solution as it seems that custom scheme are not recommended, and in this case, I don't need a deeplink.
Currently, in debug, the only thing that happens is that my intent url is opened inside chrome and display a white page.
It never opens the application.
This is my AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.MyApp">
<application android:allowBackup="true" android:icon="#mipmap/appicon" android:roundIcon="#mipmap/appicon_round" android:supportsRtl="true">
<activity
android:name="com.MyApp.activity.MainActivity"
android:exported="true"
android:label="MyApp">
<intent-filter>
<action android:name="com.MyApp.LAUNCH"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:scheme="MyScheme" android:host="MyHost" android:path="/"/>
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
This is my javascript:
if (navigator.userAgent.match(/Android/i)) {
var uri = "intent://MyApp#Intent;scheme=MyScheme;action=com.MyApp.LAUNCH;package=com.MyApp;S.p=" + p + ";S.c=" + c + ";end";
window.open(uri);
}
I've seen a lot of way of doing this and tried a lot of things, but I don't get what is the good way to do with API 33.
I tried to use "intent:#Intent", "intent://#Intent", I tried with and without data property under activity, I tried my own scheme "MyScheme://".
I would like to avoid using deeplink as I would like to keep my website accessible without launching the app (different goals).
You need to check Handling Android App Link documentation.
Basically, to open the app you need to specify some specific scheme, for example:
<activity
android:name=".MyMapActivity"
android:exported="true"
...>
<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="myapp" />
</intent-filter>
</activity>
And the link to open will be: myapp://anypath_here?param1=value1¶m2=value2
Try to use a unique scheme, otherwise, you will have an 'Open with..' dialog opening if another app can open it.
To enable link handling verification for your app, add intent filters that match the following format:
<!-- Make sure you explicitly set android:autoVerify to "true". -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- If a user clicks on a shared link that uses the "http" scheme, your
app should be able to delegate that traffic to "https". -->
<data android:scheme="http" />
<data android:scheme="https" />
<!-- Include one or more domains that should be verified. -->
<data android:host="..." />
</intent-filter>
More information you can refer to Verify Android App Links.
I am attempting to implement deep links within my Instant App, and keep running into issues that seem to be coming from something to do with the base application. Currently, the error I'm receiving from the Play Console states:
You should have at least one active installed app APK that mapped to
site 'example.site.com' through a web intent-filter.
I do have an intent-filters set up in both the instant app and in the full base app. The intent-filters in the base application look like this:
<intent-filter>
<data
android:scheme="http"
android:host="example.site.com"
android:pathPattern="/test/app/" />
</intent-filter>
<intent-filter>
<data
android:scheme="https"
android:host="example.site.com"
android:pathPattern="/test/app/" />
</intent-filter>
And the intent-filters in the instant app module's manifest look like this:
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="example.site.com"
android:pathPattern="/test/app/" />
<data
android:scheme="http"
android:host="example.site.com"
android:pathPattern="/test/app/" />
</intent-filter>
With the instant app manifest also containing a default url, set up within the activity tag.
I'm not sure what I am missing here. I have set up the intent filters as I've seen elsewhere on the web, in Google's documentation, and in other forum posts where I've seen people encountering issues with this process. I've also hosted the assetlinks.json file in the .well-known directory on my domain, and verified that it is correct and accessible using Google's verification API. Does anyone know what the issue causing this error may be? Thanks!
From what I have read in the documentation you need to make sure that you use an intent-filter with autoVerify, action and categories for the app as well. In your example you can simply copy & paste the intent-filter that you are using in the instant-app to your full app.
In case you are using string resources for scheme, host or pathPattern I would try to avoid this and directly enter your values in the manifest instead.
e.g. (crafted from your post)
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="example.site.com"
android:pathPattern="/test/app/" />
<data
android:scheme="http"
android:host="example.site.com"
android:pathPattern="/test/app/" />
</intent-filter>
I add two url schemes to manifest:
<intent-filter android:autoVerify="true"
android:label="#string/app_name">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="appinsider.mobile/"
android:scheme="http"/>
<data
android:host="appinsiderlink/"
android:scheme="http" />
</intent-filter>
But apart from these URL, the application opens all links starting with "http"
How to open only links declared in manifest?
I believe the android:host="appinsiderlink/" tag needs to be a valid URI host (e.g., google.com). It probably also shouldn't have a trailing slash.
You're also setting <intent-filter android:autoVerify="true">, but haven't mentioned if you also took the steps necessary to declare website associations...
I am stuck with the following scenario. I defined the following deep link intent filters in the AndroidManifest.xml
Expected behavior is when I found a url of format http://www.domain.com/a/blabla or when there is link in SMS/eMail of format domain/xyz the system should trigger my activity.
Case #1: Working fine
<activity
android:name=".MYActivity">
<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:host="xyz"
android:scheme="domain" />
</intent-filter>
</activity>
Case #2: Working fine
<activity
android:name=".MYActivity">
<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="www.domain.com"
android:pathPrefix="/a"
/>
</intent-filter>
</activity>
Case #3: NOT working
<activity
android:name=".MYActivity">
<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:host="xyz"
android:scheme="domain" />
<data
android:scheme="http"
android:host="www.domain.com"
android:pathPrefix="/a"
/>
</intent-filter>
</activity>
Any suggestions/points/help is really appreciated
I placed both the deeplinks in two different intent filters and it worked!!!.
See the documentation of <data>: it states that:
All the <data> elements contained within the same <intent-filter> element contribute to the same filter.
Hence
<intent-filter>
<data
android:host="xyz"
android:scheme="domain" />
<data
android:scheme="http"
android:host="www.domain.com"
android:pathPrefix="/a" />
<intent-filter>
is interpreted equivalently as (not real code)
<intent-filter>
<data
android:host="xyz"
android:scheme="domain"
android:scheme="http"
android:host="www.domain.com"
android:pathPrefix="/a" />
<intent-filter>
which clearly has some contradictions, for example host being xyz VS www.domain.com.
From Android official documentation site :
Although it's possible to include multiple elements in the same
filter, it's important that you create separate filters when your
intention is to declare unique URLs (such as a specific combination of
scheme and host), because multiple elements in the same intent
filter are actually merged together to account for all variations of
their combined attributes.
You will have to create separate intent-filters.
Slightly off topic but relates the the OP question above with extra information that might help someone else.
If you have intent filters with deeplinks across multiple manifests and want to merge them without overriding each other you can add a unique android:label on your <intent-filter>.
Example:
main/AndroidManifest.xml
(These deeplinks are available to all app flavours, debug, prodcution etc)
<intent-filter android:label="main_deeplink1">
<data
android:host="domain.com"
android:scheme="https" />
<intent-filter>
debug/AndroidManifest.xml
(These deeplinks are available only to the debug build flavour)
<intent-filter android:label="debug_deeplink1">
<data
android:host="dev.domain.com"
android:scheme="https" />
<intent-filter>
The merged Manifest file will now contain both deeplinks without overriding each other.
Here is the tutorial I hop it helps
Tutorial link
I want my app to handle files called *.mcmidi from browser, email or storage.
I have found that the following intent-filter data element works for android native browser and for android chrome:
<data android:scheme="content" android:mimeType="*/*" android:pathPattern=".*\\.mcmidi" />
and this works for android firefox:
<data android:scheme="file" android:pathPattern=".*\\.mcmidi" />
but if I try to use both then firefox stops being able to open the downloaded files. It seems like firefox doesn't work if any intent-filter specifies a mimeType.
(I've been testing so many combinations of these intent-filters trying to find one that works everywhere)
Does anyone know why firefox is doing this, or any way to work around it?
Ok I worked it out. Chrome and Android browsers use the scheme="content" entry. Firefox uses the scheme="file" entry. If you use the mimeType="*/*" entry then all new email alerts will open the app chooser with my app, which is also no good. Opening file attachments from gmail use scheme="content" but with a different mimeType. File explorer apps generally use scheme="file" with the path extension.
Because they all need different intent-filters, I have used activity-alias to alias my activity three different ways for the different apps, so that they all pass the file content to my app and don't interfere with each other or add themselves as generic file handlers (which is always very irritating). So I have this:
<activity
android:name="my.Activity"
android:label="#string/label1"
android:exported="true" >
<!-- the main intent filter allows chrome and native browsers to open .mcmidi files with my app -->
<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="content" android:mimeType="application/x-mcmidi" android:pathPattern=".*\\.mcmidi" />
</intent-filter>
</activity>
<!-- This alias will cause file explorer apps to open *.mcmidi files with my app -->
<activity-alias
android:name="my.ActivityFileAlias"
android:targetActivity="my.Activity"
android:label="#string/label1"
android:exported="true" >
<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" android:pathPattern=".*\\.mcmidi" />
</intent-filter>
</activity-alias>
<!-- This alias will cause gmail and the native mail apps to open *.mcmidi files with my app -->
<activity-alias
android:name="my.ActivityEmailAlias"
android:targetActivity="my.Activity"
android:label="#string/label1"
android:exported="true" >
<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="content" android:mimeType="application/octet-stream" android:pathPattern=".*\\.mcmidi" />
</intent-filter>
</activity-alias>
Which I worked out with a few clues and loads of trial and error. Hope it helps someone.