G'day,
Disclaimer: I'm not an Android dev, I'm QAing an Android app with the issue I'm describing. The technical terms I use to describe this issue might be wrong.
I'm testing an Android app that describes in its manifest that it can handle web intents with the address of type https://www.example.com/app/(.*)
. The way it should handle these URLs is that it gets the first match group $1
and sends a request to https://api.example.com/$1
and if the response is a HTTP200, it renders the response within the app. If not, it should open the URL in any browser app the user has installed on their device (by sending an intent to open the URL). If the user has no browser apps installed on their device, we show an error prompt saying they don't have a browser installed which can handle this URL.
Now, this works fine except when the user marks this app as the default to handle URLs like https://www.example.com/app/(.*)
when it first tries to open a URL like https://www.example.com/app/(.*)
. Then, even if the user has browser apps installed on their system, when they open a link that needs to be opened in a browser, the only option seems to be the our original app and we have to show the error message (as it seems like there are no other browser apps installed on the system which can handle this URL).
One way to tackle this is to show a message asking the user to clear the defaults for this app when we encounter a URL that needs to be opened in a browser app but the only option is our own app — but this is terrible UX. Is there another work-around for this issue?
Sample code to understand the issue: https://gist.github.com/GVRV/5879fcf0b1838b495e3a2151449e0da3
Edit 1: Added sample code link
Sadly, there is no official solution for this problem (see this SO question).
A workaround is the following: Use
PackageManager.queryIntentActivities()
, modify the result to not include your app and show it in a custom chooser dialog. If you don't want your users to choose a browser every time, you can manage a custom default inside your app.If you control the domain, there is a cleaner workaround: Lets say your url is
http://www.example.com
. Your AndroidIntentFilter
should listen for that schema. Now you create a second schema, e.g.http://web.example.com
, which displays the same content as the normal url. If you want to redirect to the web from your app, use the second schema. Everywhere else, use the first one.Note that you should not use a custom schema like
example://
, because this will cause problems if your app is not present.if website https://www.example.com/ is under your supervision, you could change the logic and use an unique schema like example://app/(.) to handle your case. The website could then use redirection to for its navigation. In this way when you broadcast https://www.example.com/ for action view only browser apps could handle this and your app would be only listening to your custom schema example://app/(.) and wont launch.
Else you could check for default activity and clear it instead of showing an alert.
To solve this problem and keep the systems default handling of intents you need 2 additional activities and 1
<activity-alias>
:Create a new invisible empty Activity. I called it IntentFilterDelegationActivity. This activity is responsible to receive URL intents from the
activity-alias
(defined in the next step). Manifest:Code:
Create an
<activity-alias>
. The activity alias is responsible to delegate your URL intents to yourIntentFilterDelegationActivity
. Only a Manifest entry is needed:Now you are able to do the trick: You can deactivate the
activity-alias
before you launch your own URL intent and activate the alias after the launch. This causes android that your app won't be listed as app which can handle the URL intent. To implement the activation and deactivation you need an additional Activity. I called itForceOpenInBrowserActivity
. Manifest:Code:
Now you can send any URL intent which must be opened in an external browser to the
ForceOpenInBrowserActivity
: