Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Browser Facebook Redirect Does Not Always Trigger Intent for URL

I'm in a real pickle and desperately need some help with a critical problem I'm having.

I've spent months writing a HTML5 app/website along with a native Android application that is simply a WebView wrapper for the HTML5 website. One of the core features of the application is that users can share app specific URLs to Facebook and Twitter etc so that their friends can follow the shared URLs which will open up either the HTML5 version of my app in their browser OR MOST IMPORTANTLY if they are on Android and they have my native Android app installed they are PROMPTED TO OPEN IN MY APP.

This is a rather long complicated issue so to simplify I'll use some certain terms consistently through-out this post:

  • "MyApp user" a Android device user who DOES have my native Android app installed
  • "non-MyApp user" a Android device user who DOES NOT have my native Android app installed
  • "stock browser user" a stock Android user who uses a stock browser
  • "non-stock browser user" a Android user who uses a non-stock browser (note the Samsung Galaxy SII stock "Internet" app is considered non-stock)
  • "choose app dialog" on Android the OS dialog that appears asking the user what application they want to open the action/intent/url/document with and if they want to default to use this application always

My AndroidManifest.xml contains the following...

<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="myapp.com" android:pathPrefix="/" />
</intent-filter>

When tested this WORKS PERFECTLY in any normal scenario. For example if a "MyApp user" follows a direct link to http://myapp.com/sharedpage, the "choose app dialog" appears. Great, I've definitely configured my AndroidManifest.xml correctly.

However things do NOT ALWAYS WORK in the critical Facebook sharing scenerio. It does not always work because both Facebook's native Android app and Facebook's mobile website (http://m.facebook.com) DO NOT LINK DIRECTLY to the shared URLs but instead link via a Facebook redirect page. For example if http://myapp.com/sharedpage is shared, Facebook will ultimately deliver the following URL:

http://m.facebook.com/l.php?u=http%3A%2F%2Fmyapp.com%2Fsharedpage&h=EAQGpLtuF&enc=AZMXYZg7XwQ39zlWkKSGnLw62lEbtbMeWFmRwRZINoOcg0UgZe3fUVPgqQzV1nuTipSVnquV3a3ovqu7HQFUf3bb3ZJ1gYG8dEOJXzPf6RJGflf9_x8w-6CCXu8G2VZqgfD7lx6EbLTSKLnF56_o5khHybycPUlhpdfLsk6M9muer4jMOmPK6_kfjTq2gvnYNNpStcF0ilJD6nacPqx_1xsdYkUMpKYWbJfSo7qqv1S5xT5KRaLPxl8zmAkYc0FhwyTdn-tUGwRBbbdM4QCd2Z75Tb_VeJG3LvbDwFAbp6G3kH3LOSxVtTd5MST4pUW8xmhNeTUVBVXV16OD27QcsSWOlEfL72fxn11PDE5s4WWsXMnwhDJLUAWOAna7lziBnWzjZdlQK_amI9nhcegaOLDLNFCp125rZS3jxFXf7gtF9g0BsmnPZ2Gjxkc6UgQXhEYldllq9nwpShGbnZDlSg0_&s=1

If a "MyApp user" follows one of these Facebook links one of two things might happen depending on the browser they are using...

MyApp Stock Browser User:

If the user has stock Android installed and uses the stock Android browser then things work FINE because the following events occur...

  • Intent fires for the FB l.php URL
  • FB URL gets opened in the ANDROID STOCK BROWSER
  • The FB l.php is loaded and initiates the REDIRECT to http://myapp.com/sharedpage
  • Intent fires for the http://myapp.com/sharedpage URL
  • "choose app dialog" prompts user to open in MY NATIVE APP or in browser

MyApp Non-Stock Browser User:

HOWEVER if the user...

  1. has installed and uses a NON-stock Android browers, e.g. Dolphin HD/mini, Opera Mobile/Mini etc, or
  2. has a manufacturer customised version of Android (e.g. on Samsung Galaxy SII, etc) and therefore has a customised browser

... then here's the MAJOR ISSUE because the following events occur...

  • Intent fires for the FB l.php URL
  • FB URL gets opened in the NON-STOCK BROWSER
  • The FB l.php is loaded and initiates the REDIRECT to http://myapp.com/sharedpage
  • Intent IS NOT FIRED by the non-stock browser, the "choose app dialog" DOES NOT appear, the user is NOT prompted to open URL in my app
  • The http://myapp.com/sharedpage URL is loaded and rendered in a tab of the non-stock browser

Client Side Redirect

In order to confirm its the redirect that was causing the problem, I created a very simple HTML page named "clientSideRedirector.htm"

<html><body><script>
   window.location.href = "http://myapp.com/sharedpage";
</script></body></html>

If My-App/non-stock browser user opens http://myapp.com/clientSideRedirector.htm the "app choice dialog" does NOT appear. FAIL.

If My-App/stock browser user opens http://myapp.com/clientSideRedirector.htm the "app choice dialog" DOES appear.

This would seem to tally up with what we're seeing with the Facebook share/redirect.

Server Side (302) Redirect

I also thought I'd try a server side redirect so I created a .NET dotNetRedirect.ashx page:

public class ShareRedirect : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
            context.Response.Redirect("http://myapp.com/sharedpage");
    }
    public bool IsReusable { get { return false; } }
} 

This has a very interesting and different result to the client redirect.
If My-App user opens the http://myapp.com/dotNetRedirect.htm the "app choice dialog" DOES appear REGARDLESS of the users browser. So this type of redirect seems to work!


Attempted Solution A

(Double redirect: Facebook l.php redirect to server-side redirect to app URL)

I thought this could be the solution to my Facebook sharing problems. If I shared http://myapp.com/dotNetRedirect.ashx URL to Facebook then maybe Facebook would redirect to the dotNetRedirect.htm page and then the server side redirect would force the "choose app dialog" prompt to open.

Unfortunately this DOES NOT work, in a non-stock browser it would appear if the first redirect doesn't trigger the intent/"choose app dialog" further redirects won't either. Massively gutting.


Attempted Solution B

(Using a custom scheme)

Having exhausted the server-side idea I thought I'd investigate using a custom URI scheme to trigger the "app choice dialog".

AndroidManifest.xml:

<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>

redirect.htm

<html><body><script>
   window.location.href = "myapp.com://sharedpage";
</script></body></html>

This does actually work, the "choose app dialog" appears via the DB l.php URL even when using a non-stock browser. However it isn't really a feasible solution as it stands because "non MyApp users" are left with a "Webpage not available" page when redirected to the myapp.com://sharedpage URL. ]


Does anyone else have any other bright ideas or suggestions?

like image 209
Oliver Pearmain Avatar asked Aug 20 '12 11:08

Oliver Pearmain


2 Answers

I made some further developments in my search and found that performing a "fake click" was the solution that worked in the majority of scenarios (but not all).

If the user wishes to share URL http://myapp.com/sharedpage, then I actually post the following URL to Facebook http://myapp.com/share.htm?redirectUrl=sharedpage

share.htm is just a javascript redirect page that immediately redirects to the appropriate page. It is clever though as on Android instead of just using window.location.replace it uses a fake click of a button with the link which can force an intent to be triggered on some devices/some browsers. Code looks like below.

<!DOCTYPE HTML>
<html>
<body>
    <script type="text/javascript">
        var redirectUrlRelativeToThisPage = ""; // Read off the querystring here
        var isAndroidDevice = (/android/gi).test(navigator.userAgent);

        if (isAndroidDevice) {
            // Android device. If the user isn't using a stock browser then window.location.redirect() doesn't always
            // trigger an Intent (and prompt to open Native app) so instead attempt to fake click a hyperlink with the
            // URL as this works more reliably (but not always).
            var linkToFakeClick = document.createElement("a");
            linkToFakeClick.href = redirectUrlRelativeToThisPage;
            var fakeMouseClickEvent = document.createEvent("MouseEvents");
            fakeMouseClickEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
            linkToFakeClick.dispatchEvent(fakeMouseClickEvent);
        }
        // If we got here either we're not on an Android device or the fake click didn't work so just redirect normally
        window.location.replace(redirectUrlRelativeToThisPage);
    </script>
</body>
</html>

In addition to the example code above and if it suited your scenario, you could also incorporate (in the event the fake click didn't work) two different "I do/don't have the app installed" buttons which link to the custom scheme URL (to force an intent) and the usual URL.

like image 94
Oliver Pearmain Avatar answered Oct 29 '22 18:10

Oliver Pearmain


A very dirty workaround would be to put an intent filter on http://m.facebook.com/l.php:

<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="m.facebook.com" 
        android:path="/l.php" />
</intent-filter>

Then parse the query string and relay the actual URL through another intent if it was not meant for your app.

Wouldn't recommend it though, since it might look a bit odd, seeing your app as a choice for a link not at all related to your app. Also, it will cause users to be prompted to select an app twice if they've chosen your app the first time. The only time this would work nicely (in the case where the link is not meant for your app) is if phone has your app set as default for all http://m.facebook.com/l.php requests. Still you would be messing with the inner workings of facebook.

Hopefully android browsers will work more with implicit intents in the future, here is a start: https://code.google.com/p/chromium/issues/detail?id=235060

like image 42
petter Avatar answered Oct 29 '22 18:10

petter