60

Is it possible to detect if the user is accessing through the browser or application using JavaScript?

I'm developing a hybrid application to several mobile OS through a web page and a PhoneGap application and the goal would be to:

  1. Use the same code independently of the deployment target
  2. Add PhoneGap.js file only when the user agent is an application
Diogo Cardoso
  • 19,349
  • 24
  • 92
  • 137

16 Answers16

59

You could check if the current URL contains http protocol.

var app = document.URL.indexOf( 'http://' ) === -1 && document.URL.indexOf( 'https://' ) === -1;
if ( app ) {
    // PhoneGap application
} else {
    // Web page
}
FabianCook
  • 18,692
  • 14
  • 59
  • 110
EricL
  • 911
  • 7
  • 8
  • This way you dont need to wait for PhoneGap/Cordova to load before you can check it. – EricL Sep 04 '12 at 17:27
  • 1
    I've tested and works fine! That's exactly the type of solution I was looking for, one that imports PhoneGap only when it's an application. – Diogo Cardoso Sep 08 '12 at 00:14
  • 6
    shorter check: var fromBrowser = document.URL.match(/^https?:/) – Afanasii Kurakin Mar 15 '13 at 08:05
  • In my builds document.URL is coming out to be [http://local/](http://local/) so the http protocol test is still coming back as positive. I had to add to alter this code slightly to make it work for me... – dreamerkumar Mar 18 '13 at 23:37
  • 5
    This also does not work if we host the cordova code on the web and point apps to it. – Ray Foss Dec 17 '13 at 19:15
  • This won't work if you're testing your app in a desktop browser, the protocols are the same. – Grux Dec 08 '14 at 04:35
  • If you're loading the app remotely then just detect some predictable portion of the URL. – EricL Oct 23 '15 at 15:38
15

Quick solution comes to mind is,

onDeviceReady

shall help you. As this JS call is invoked only by the Native bridge (objC or Java), the safari mobile browser will fail to detect this. So your on device app(phone gap) source base will initiate from onDeviceReady.

And if any of the Phonegap's JS calls like Device.platform or Device.name is NaN or null then its obviously a mobile web call.

Please check and let me know the results.

Futur
  • 8,314
  • 5
  • 26
  • 34
  • OK after seeing your response to Zvona, now i understood you even want to decide whether to load or not phonegap. Let me check again – Futur Apr 27 '12 at 09:45
  • 3
    The only problem with this solution is that does an extra request in order to get phonegap file when deploying a web page. – Diogo Cardoso May 14 '12 at 09:03
  • if you really can reverse engineer the Phonegap source (JS). the piece of code which triggers the onDeviceReady native call. this will make it very light weight comparatively. you can load rest of the Phonegap's JS when its on device. A costly operation to test. Try if possible. – Futur May 20 '12 at 05:13
11

I figured out a way to do this and not rely on deviceready events thus, keeping the web codebase intact...

The current problem with using the built in deviceready event, is that when the page is loaded, you have no way of telling the app: "Hey this is NOT running on an mobile device, there's no need to wait for the device to be ready to start".

1.- In the native portion of the code, for example for iOS, in MainViewController.m there's a method viewDidLoad, I am sending a javascript variable that I later check for in the web code, if that variable is around, I will wait to start the code for my page until everything is ready (for example, navigator geolocation)

Under MainViewController.m:

- (void) viewDidLoad
{
    [super viewDidLoad];
    NSString* jsString = [NSString stringWithFormat:@"isAppNative = true;"];
    [self.webView stringByEvaluatingJavaScriptFromString:jsString];
}

2.- index.html the code goes like this:

function onBodyLoad()
{
    document.addEventListener("deviceready", onDeviceReady, false);
}

function onDeviceReady(){;
    myApp.run();
}

try{
    if(isAppNative!=undefined);
}catch(err){
    $(document).ready(function(){
        myApp.run();
    });
}
Juan Carlos Moreno
  • 2,666
  • 3
  • 20
  • 21
  • We could also go a step further and change the user agent to be something like MyAppLoader XXHDPI. Which would help with loading better images. It is rather unfortunate that cordova does not come with this functionality. – Ray Foss Dec 17 '13 at 19:20
  • this solution works really well for me, but note that [there are a couple small changes needed for Cordova 4](http://stackoverflow.com/questions/34896815/self-viewcontroller-webview-stringbyevaluatingjavascriptfromstring-does-not-work/34967395). `if ([self.webView isKindOfClass:[UIWebView class]]) { [(UIWebView*)self.webView stringByEvaluatingJavaScriptFromString:jsString]; }` – diffalot Jul 03 '16 at 19:47
7

PhoneGap has window.PhoneGap (or in Cordova, it's window.cordova or window.Cordova) object set. Check whether that object exists and do the magic.

Samuli Hakoniemi
  • 16,628
  • 1
  • 55
  • 71
  • 1
    The problem with that solution is that I must include or not the PhoneGap file depending if I'm building an page or application. The goal would be to use the same code independently of the deploying target. – Diogo Cardoso Apr 27 '12 at 08:59
  • Then it sounds a problem since UIWebView contains the same Mobile Safari, I think. Let me check if I find something later. – Samuli Hakoniemi Apr 27 '12 at 09:04
6

Inside the native call where the url for the phonegap app is loaded you add a parameter target with value phonegap. So the call for android becomes something like this.

super.loadUrl("file:///android_asset/www/index.html?target=phonegap");
Your website using this code won't be called with the extra parameter, so we now have something different between the two deploying platforms.
Inside the javascript we check if the parameter exists and if so we add the script tag for phonegap/cordova.
    var urlVars = window.location.href.split('?');
    if(urlVars.length > 1 && urlVars[1].search('target=phonegap') != -1){
        //phonegap was used for the call
        $('head').append('<script src="cordova.js"></script>');
    }
    
A small caveat: this method requires to change the call to index.html in phonegap for each different targeted mobile platform. I am unfamiliar where to do this for most platforms.
Martijn
  • 14,522
  • 4
  • 29
  • 61
Pirokiko
  • 199
  • 6
4

I am using the same code for both phonegap app and our web client. Here is the code that I use to detect if phonegap is available:

window.phonegap = false;
$.getScript("cordova-1.7.0.js", function(){
    window.phonegap = true;
});

Keep in mind that phonegap js file is loaded asynchronously. You can load it synchronously by setting the correct option of a nifty jquery $.getScript function.

Note that approach does make an extra GET request to grab phonegap js file even in your webclient. In my case, it did not affect the performance of my webclient; so it ended up being a nice/clean way to do this.Well at least until someone else finds a quick one-line solution :)

Aki
  • 3,515
  • 2
  • 25
  • 35
  • Not all phonegap apps are built with cordova. It might be better to detect phonegap.js. – TMB May 14 '12 at 01:22
4

It sounds like you are loading another webpage once the webview starts in the Phonegap app, is that correct? If that's true then you could add a param to the request url based on configuration.

For example, assuming PHP,

App.Config = {
  target: "phonegap"
};

<body onload="onbodyload()">

var onbodyload = function () {
  var target = App.Config.target;
  document.location = "/home?target=" + target;
};

Then on the server side, include the phonegap js if the target is phonegap.

There is no way to detect the difference using the user agent.

sciritai
  • 3,543
  • 1
  • 15
  • 21
4

The way I'm doing it with is using a global variable that is overwritten by a browser-only version of cordova.js. In your main html file (usually index.html) I have the following scripts that are order-dependent:

    <script>
        var __cordovaRunningOnBrowser__ = false
    </script>
    <script src="cordova.js"></script> <!-- must be included after __cordovaRunningOnBrowser__ is initialized -->
    <script src="index.js"></script> <!-- must be included after cordova.js so that __cordovaRunningOnBrowser__ is set correctly -->

And inside cordova.js I have simply:

__cordovaRunningOnBrowser__ = true

When building for a mobile device, the cordova.js will not be used (and instead the platform-specific cordova.js file will be used), so this method has the benefit of being 100% correct regardless of protocols, userAgents, or library variables (which may change). There may be other things I should include in cordova.js, but I don't know what they are yet.

B T
  • 46,771
  • 31
  • 164
  • 191
4

Ive ben struggling with this aswell, and i know this is an old thread, but i havent seen my approach anywhere, so thought id share incase itll help someone.

i set a custom useragent after the actual useragent :

String useragent = settings.getUserAgentString();
settings.setUserAgentString(useragent + ";phonegap");

that just adds the phonegap string so other sites relying on detecting your mobile useragent still works.

Then you can load phonegap like this:

if( /phonegap/i.test(navigator.userAgent) ) 
{
//you are on a phonegap app, $getScript etc
} else {
alert("not phonegap");
}
Havihavi
  • 582
  • 1
  • 6
  • 24
4

what if you try following :

if(window._cordovaNative) {
  alert("loading cordova");
  requirejs(["...path/to/cordova.js"], function () { 
         alert("Finished loading cordova");
  });
}
SashaZd
  • 3,279
  • 1
  • 21
  • 48
  • Checking `window._cordovaNative` worked for me! On Android at least. There's a comment elsewhere in cordova.js that hints it won't work on Android <= 3.2 though. – phreakhead Apr 01 '14 at 05:36
1

To my mind you try to make issue for self. You didn't mentioned your development platform but most of them have different deployment configuration. You can define two configurations. And set variable that indicates in which way code was deployed. In this case you don't need to care about devices where you deployed your app.

RredCat
  • 4,775
  • 3
  • 54
  • 91
1

Short and effective:

if (document.location.protocol == 'file:') { //Phonegap is present }
AmpT
  • 1,770
  • 1
  • 21
  • 23
1

Similar to B T's solution, but simpler:

I have an empty cordova.js in my www folder, which gets overwritten by Cordova when building. Don't forget to include cordova.js before your app script file (it took my one hour to find out that I had them in wrong order...).

You can then check for the Cordova object:

document.addEventListener('DOMContentLoaded', function(){
    if (window.Cordova) {
        document.addEventListener('DeviceReady', bootstrap);
    } else {
        bootstrap();
    }
});

function bootstrap() {
   do_something()
}
Community
  • 1
  • 1
0

New solution:

var isPhoneGapWebView = location.href.match(/^file:/); // returns true for PhoneGap app

Old solution:
Use jQuery, run like this

$(document).ready(function(){
   alert(window.innerHeight);
});

Take iPhone as example for your mobile application,

When using PhoneGap or Cordova, you'll get 460px of WebView, but in safari, you'll lose some height because of browser's default header and footer.

If window.innerHeight is equal to 460, you can load phonegap.js, and call onDeviceReady function

Ranganadh Paramkusam
  • 3,768
  • 2
  • 21
  • 32
0

Nobody mentioned this yet, but it seems Cordova now supports adding the browser as a platform:

cordova platforms add browser

This will automatically add cordova.js during run-time, which features the onDeviceReady event, so that you do not need to fake it. Also, many plugins have browser support, so no more browser hacks in your code.

To use your app in the browser, you should use cordova run browser. If you want to deploy it, you can do so using the same commands as the other platforms.

EDIT: forgot to mention my source.

Ricardo Cruz
  • 3,135
  • 6
  • 27
  • 50
0

Solution: Patch index.html in Cordova and add cordova-platform="android" to <html> tag, so that cordova-platform attribute will be only present in Cordova build and missing from original index.html used for web outside of Cordova.

Pros: Not rely on user agent, url schema or cordova API. Does not need to wait for deviceready event. Can be extended in various ways, for example cordova-platform="browser" may be included or not, in order to distinguish between web app outside of Cordova with Cordova's browser platform build.

Merge with config.xml

    <platform name="android">
        <hook src="scripts/patch-android-index.js" type="after_prepare" />
    </platform>

Add file scripts/patch-android-index.js

module.exports = function(ctx) {
    var fs = ctx.requireCordovaModule('fs');
    var path = ctx.requireCordovaModule('path');

    var platformRoot = path.join(ctx.opts.projectRoot, 'platforms/android');
    var indexPath = platformRoot + '/app/src/main/assets/www/index.html';

    var indexSource = fs.readFileSync(indexPath, 'utf-8');

    indexSource = indexSource.replace('<html', '<html cordova-platform="android"');

    fs.writeFileSync(indexPath, indexSource, 'utf-8');
}

Notes: For other than android, the paths platforms/android and /app/src/main/assets/www/index.html should be adjusted.

App can check for cordova-platform with

if (! document.documentElement.getAttribute('cordova-platform')) {
  // Not in Cordova
}

or

if (document.documentElement.getAttribute('cordova-platform') === 'android') {
  // Cordova, Android
}
Ventzy Kunev
  • 186
  • 1
  • 4