I\'m developing a web application that uses PhoneGap:Build for a mobile version and want to have a single codebase for the \'desktop\' and mobile versions. I want to be able
The following works for me with the most recent PhoneGap / Cordova (2.1.0).
How it works:
Advantages:
Disadvantages:
==
Create a brand new blank PhoneGap project. In the provided sample index.js , replace the "app" variable at the bottom with this:
var app = {
// denotes whether we are within a mobile device (otherwise we're in a browser)
iAmPhoneGap: false,
// how long should we wait for PhoneGap to say the device is ready.
howPatientAreWe: 10000,
// id of the 'too_impatient' timeout
timeoutID: null,
// id of the 'impatience_remaining' interval reporting.
impatienceProgressIntervalID: null,
// Application Constructor
initialize: function() {
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// `load`, `deviceready`, `offline`, and `online`.
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
// after 10 seconds, if we still think we're NOT phonegap, give up.
app.timeoutID = window.setTimeout(function(appReference) {
if (!app.iAmPhoneGap) // jeepers, this has taken too long.
// manually trigger (fudge) the receivedEvent() method.
appReference.receivedEvent('too_impatient');
}, howPatientAreWe, this);
// keep us updated on the console about how much longer to wait.
app.impatienceProgressIntervalID = window.setInterval(function areWeThereYet() {
if (typeof areWeThereYet.howLongLeft == "undefined") {
areWeThereYet.howLongLeft = app.howPatientAreWe; // create a static variable
}
areWeThereYet.howLongLeft -= 1000; // not so much longer to wait.
console.log("areWeThereYet: Will give PhoneGap another " + areWeThereYet.howLongLeft + "ms");
}, 1000);
},
// deviceready Event Handler
//
// The scope of `this` is the event. In order to call the `receivedEvent`
// function, we must explicity call `app.receivedEvent(...);`
onDeviceReady: function() {
app.iAmPhoneGap = true; // We have a device.
app.receivedEvent('deviceready');
// clear the 'too_impatient' timeout .
window.clearTimeout(app.timeoutID);
},
// Update DOM on a Received Event
receivedEvent: function(id) {
// clear the "areWeThereYet" reporting.
window.clearInterval(app.impatienceProgressIntervalID);
console.log('Received Event: ' + id);
myCustomJS(app.iAmPhoneGap); // run my application.
}
};
app.initialize();
function myCustomJS(trueIfIAmPhoneGap) {
// put your custom javascript here.
alert("I am "+ (trueIfIAmPhoneGap?"PhoneGap":"a Browser"));
}
Not really an answer to the question, butwhen I test in a desktop browser, I just set a localstorage value to make the browser load the app dispite deviceready not fireing.
function main() {
// Initiating the app here.
};
/* Listen for ready events from pheongap */
document.addEventListener("deviceready", main, false);
// When testing outside ipad app, use jquerys ready event instead.
$(function() {
if (localStorage["notPhonegap"]) {
main();
}
});
Try this approach:
/**
* Returns true if the application is running on an actual mobile device.
*/
function isOnDevice(){
return navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry)/);
}
function isDeviceiOS(){
return navigator.userAgent.match(/(iPhone)/);
}
/**
* Method for invoking functions once the DOM and the device are ready. This is
* a replacement function for the JQuery provided method i.e.
* $(document).ready(...).
*/
function invokeOnReady(callback){
$(document).ready(function(){
if (isOnDevice()) {
document.addEventListener("deviceready", callback, false);
} else {
invoke(callback);
}
});
}
The most trustable way we found to tell if we are in a cordova/phonegap application is to modify the cordova application's user agent using this config AppendUserAgent.
In config.xml
add:
<preference name="AppendUserAgent" value="Cordova" />
Then call:
var isCordova = navigator.userAgent.match(/Cordova/i))
Why?
window.cordova
and document.addEventListener('deviceready', function(){});
are subject to racing conditions navigator.standalone
does not work when <content src="index.html" />
is a website (Ex: <content src="https://www.example.com/index.html" />
or with cordova-plugin-remote-injection)Another way, based on SlavikMe's solution:
Just use a query parameter passed to index.html
from your PhoneGap source. Ie, in Android, instead of
super.loadUrl("file:///android_asset/www/index.html");
use
super.loadUrl("file:///android_asset/www/index.html?phonegap=1");
SlavikMe has a great list on where to do this on other platforms.
Then your index.html
can simply do this:
if (window.location.href.match(/phonegap=1/)) {
alert("phonegap");
}
else {
alert("not phonegap");
}
I've stumbled on this problem several months ago when beginning our app, because we wanted the app to be "browser-compatible
" also (with the understanding that some functionality would be blocked in that scenario: audio recording, compass, etc.).
The only 100%
(and I insist on the 100-hundred-percent condition) solution to PRE-determine the app execution context was this:
initialize a JS "flag" variable to true, and change it to false when in an all-web context;
therefore you can use a call like "willIBeInPhoneGapSometimesInTheNearFuture()
" (that's PRE-PG, of course you still need a POST-PG method of checking if you can call PG APIs, but that one is trivial).
Then you say: "but how do you determine the execution context
?"; the answer is: "you don`t" (because I don't think you can reliably, unless those brilliant folks at PG would do it in their API code);
you write a build script that does it for you: one codebase with two variants.