6

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?

Oliver Pearmain
  • 19,885
  • 13
  • 86
  • 90
  • The solution is simple: kill the Facebook. You could look at the `Referer` [sic] header and if it comes from Facebook manually insert a button at the top of your page to ask the user if they want to open the page in your app. – Chloe Sep 20 '19 at 19:59

3 Answers3

5

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.

Oliver Pearmain
  • 19,885
  • 13
  • 86
  • 90
  • I tried your solution. In my case, since a non-stock browser isnt openning my app from some of the applications i've been spreading my links to (S-Planner, HTMLViewer) im trying to "fake click" the exact same url in the javascript code. But this doesnt work. any idea why ? – AsafK Jun 19 '14 at 18:15
  • What version of Android? The most recent version (4.4) has switched to using Chromium for WebView's, this could be why. https://developer.chrome.com/multidevice/webview/overview – Oliver Pearmain Jun 20 '14 at 07:48
  • im testing on Galaxy S3 version 4.3. All im doing in my javascript is checking the url for a certain flag. if the flag doesnt exist than im using your code to fake click a hyperlink with the same url but with this flag added. This way im not going into infinite loop. Still this doesnt identify my app's intent filter. Appreciate if you could help here. Thanks. – AsafK Jun 20 '14 at 13:39
  • I can't really offer any more info than I've already provided. I have already specifically mentioned, as I have found in my own endeavours, that this solution works "in the majority of scenarios ---> but not all <----. In my experience Samsung devices are one of the worst offenders. – Oliver Pearmain Jun 20 '14 at 13:53
  • Crap. Thanks anyway. – AsafK Jun 20 '14 at 14:57
  • This no longer works. Tested on Samsung S9+ device. – Chloe Sep 20 '19 at 22:49
2

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

petter
  • 1,765
  • 18
  • 22
0

Have you considered to use Facebook deeplinking ? http://developers.facebook.com/docs/mobile/android/deep_linking/

Benoit
  • 1,922
  • 16
  • 25
  • Thanks for the suggestion but I already looked into deep linking and unfortunately this wasn't a feasible solution as it drives ALL Facebook users to the Android Google Play store install regardless if they're on Android or not. My app is available as a webapp to iPhone users and therefore they need to just be redirected to the web app URL and not the play store. This isn't possible with deep linking. – Oliver Pearmain Nov 05 '12 at 15:18
  • Are you sure? If true this is a bug on the iPhone's facebook application. With deeplinking you just share any URL, and the android facebook app will launch your application's entry point with the url provided in the Intent instead of opening the browser. On iPhone, it should behave the same as usual and open the link. – Benoit Nov 07 '12 at 12:19