I've got a test app that fetches lat/lng values from the HTML5 geolocation service. Unfortunately, it's firing at rather inconsistent time intervals, anywhere from 500ms to 10000ms. I've tried changing the maximimAge and timeout parameters for the watch but those don't seem to change anything. I'm testing it in Chrome as well as via a simple Cordova app on an Android Lollipop build. The code below simply displays the timestamp value of the watch to eliminate any other delays that could be causing the issue. It appears that the interval is close to a 1 second then 5 second repeating pattern. I've also tried placing the geolocation fetch function inside a setInterval function and it behaves with the same 1 and 5 second repeating interval.
<html>
<body>
<h1>Timestamp</h1>
<div id="mytimestamp"></div>
<button id="btnstopwatch">Stop GPS</button>
</body>
</html>
<script type="text/javascript">
var mytimestamp = document.getElementById("mytimestamp");
//start watching location
watchPositionId = navigator.geolocation.watchPosition(updateCompass,handleerror,{
enableHighAccuracy:true,
maximumAge:3000,
timeout:3000
});
function updateCompass(p)
{
mytimestamp.innerHTML = p.timestamp;
}
function handleerror(err)
{
if(err.code ==1)
{
//user said no
alert('Please allow access to GPS');
}
else
{
alert(err.code);
}
}
</script>
Relating to watchPosition(), on the following link w3 says "the successCallback is only invoked when a new position is obtained .......implementations may impose limitations on the frequency of callbacks so as to avoid inadvertently consuming a disproportionate amount of resources."
What it means is that watchPosition() is waiting for events which will cause changes in position to happen, otherwise it does nothing. So watchPosition() will not be calling updateCompass() unless it sees the changes in position.
Also, in regard watchPosition(), maximumAge and timeout parameters are related to the acquiring new position, and these parameters has nothing to do with how much times and when watchPosition() is called...
WatchPosition() prevents continuous calls to see if position changes or not, and reacts only if some events related to position changes occur, this help to save resources like battery. But if you really want to see the position, let's say every three second, no matter if position changed or not I think setting an interval will be good approach.
var watchPositionId;
//start watching location
setInterval(function () {
watchPositionId = navigator.geolocation.watchPosition(updateCompass, handleerror, {
enableHighAccuracy: true,
maximumAge: 3000,
timeout: 3000
});
}, 3000);
function updateCompass(p) {
mytimestamp.innerHTML = p.timestamp;
// if you have alert message it will counts seconds once you dismiss the alert,
//this may be part of the issue why you kept getting diffrent intervals.
//alert(p.timestamp);
navigator.geolocation.clearWatch(watchPositionId);
}
You mentioned that you tried to setInterval and it didn't work, so please note that I commented alert() in the code above because it caused me to have delays, without it the time stamp in html updates every 3000ms with an accuracy about 5ms.
The above approach works if you want to use watchPosition() because clearWatch() stoppes the watchPosition() and it further callbacks. Basically the code above initiates new watchPosition() every three seconds and kills it with clearWatch(). That is being said I think watchPosition() is not the best method to use in this approach unless you expect significant changes in position within this time interval. I would rather use getCurrentPosition() since it does the job and doesn't wait for changes in position.
var watchPositionId;
//start watching location
setInterval(function () {
watchPositionId = navigator.geolocation.getCurrentPosition(updateCompass, handleerror, {
enableHighAccuracy: true,
maximumAge: 3000,
timeout: 3000
});
}, 3000);
function updateCompass(p) {
mytimestamp.innerHTML = p.timestamp;
}
I ended up changing the solution to use both geolocation as well as the deviceOrientationEvent. Using the geolocation event alone created a very 'jerky' response and the device orientation event smooths this out.
Here's the setInterval function set to fetch the geolocation every ten seconds:
window.onload = function()
{
//fetch new GPS info every 10000 milliseconds
window.setInterval(function ()
{
navigator.geolocation.getCurrentPosition(onGeoSuccess, onGeoFailure, {
enableHighAccuracy: true,
maximumAge: 0,
timeout: 30000 //large timeout to accomodate slow GPS lock on some devices
});
}, 10000); //update current location every ten seconds
};
On geo success:
function onGeoSuccess(location)
{
//call function to calculate bearing
var cluebearing = geo.bearing(currentlat, currentlng, cluelat, cluelng);
//call function to rotate arrow
rotateArrow(cluebearing);
}//end onGeoSuccess()
Function to fetch device orientation and process:
function rotateArrow(mybearing)
{
//Check for support for DeviceOrientation event
if(window.DeviceOrientationEvent)
{
//orientation listener
window.addEventListener('deviceorientation', function(event)
{
var myalpha = event.alpha; //rotation from north
//process orientation change
}//end deviceorientation listener
}
}//end rotateArrow()
On geo failure:
function onGeoFailure()
{
alert('Please turn on your phone\'s GPS');
}
Related
in my Ionic app, I implemented the geolocation watchPosition with the following options. My options are set to fire every 10 seconds, but its firing every second. And often it fires two times per second.
function watchPosition() {
var options = {
maximumAge: 10000,
timeout: 10000,
enableHighAccuracy: true,
}
watchID = navigator.geolocation.watchPosition(onSuccess, onError, options);
function onSuccess(pos) {
...
}
function onError(error) {
...
}
}
And when my device is ready, I trigger the watchPosition function with an addListener:
Original call was like this:
var watchID = null ;
document.addEventListener("deviceready", watchPosition);
I then changed to this:
document.addEventListener("deviceready",setupWatch) ;
function setupWatch() {
// global var here so it can be cleared on logout (or whenever).
activeWatch = setInterval(watchPosition, 5000);
}
And it is still firing every second.
Then I replaced the addEventListener simply with, I realized the eventListener was already inside of Ionics $ionicPlatform.ready() { .. } thus an eventListener wasn't needed. However, just calling this now triggers the watchPosition like 5 times per second, not once per 5 seconds.:
setupWatch() ;
Additional question regarding best practices for watchPosition. What is the ideal timeout for continuing to call this function...is every second OK, is it too much load/processing, does it drain batteries faster, etc?
The timeout and maximumAge you're using are not defining how frequently you have an answer from the browser. Here is what MDN says about these properties:
PositionOptions.timeout Is a positive long value representing the maximum length of time (in milliseconds) the device is allowed to take in order to return a position.
PositionOptions.maximumAge Is a positive long value indicating the maximum age in milliseconds of a possible cached position that is acceptable to return.
source
I would advice you to store the value from the callback in a variable and user another setInterval to return the value at a rate you can define.
EDIT
If you want to handle the rate of answer, you can use the function getCurrentPosition, that will fire the callback only once (source)
In my code the watchPosition was firing twice in succession but the getCurrentPostion always just fires once for obvious reasons.
Solution 1 would be to use getCurrentPosition, and then set a timer to re-check that position X milliseconds later and update as required.
Solution 2 (which I used, for various other reasons) would be to use watchPosition but have the callback double-check that the long/lat has actually changed. If not then ignore the rest of the function. So as an example:
var oldlong = "";
var oldlat = "";
navigator.geolocation.watchPosition(onGpsChangeSuccess,onGpsError,{
maximumAge: 10000,
enableHighAccuracy: false,
timeout: 15000
}
);
var onGpsChangeSuccess = function(){
//Watch was triggered...
if(oldlong != position.coords.longitude && oldlat!=position.coords.longitude){
//Changes detected
oldlong = position.coords.longitude;
oldlat = position.coords.latitude;
performTheRestOfYourFunctionHere
}else{
//No changes detected
}
}
We shouldn't have to perform this additional check, but if the watcher has a bug then it's better to be safe.
I would be surprised that your successive Lat/Lng readings are identical. Perhaps the "accuracy" meters have varied or one source was GPS and another Telco Tower. Outdoors or indoors?
Regardless I recommend filtering readings as appropriate for your App. Ignoring inaccurate or uninteresting readings is IMHO something ALL GPS Apps have to cater for.
I have a function called using setInterval of JavaScript, which in some scenarios is called multiple times without the interval gap defined (I suspect this is because the intervals are not cleared properly and I'm creating multiple intervals, but I'm not sure).
I can not reproduce the problem locally.
The code uses Twirl but it's basically JS:
function refreshCheckInRequests() {
if (interval) { // If there is an interval running stop it.
clearInterval(interval);
}
jsRoutes.controllers.ExtranetSecuredController.findPendingCheckInRequests("#gymId").ajax({ // Ajax call using Play Framework
success: function (data) {
$("#checkin-request-container").html(data);
addRowListeners()
},
error: function (data) {
if (data.status == 401) {
errorSwitchGym("#Messages("extranet.switch.gym")");
//location.reload();
}
else {
unexpectedError(data)
}
},
complete: function() {
interval = initInterval(); // At the end of the call init the interval again
}
});
}
function initInterval() {
return setInterval(function () { refreshCheckInRequests(); },
20000);
}
var interval;
refreshCheckInRequests();
$("#checkin-request-refresh").click(function (event) {
refreshCheckInRequests();
event.preventDefault();
});
I could use setTimeout instead because at the end, I always call refreshCheckInRequests once, I stop the interval, and at the end I create a new one.
If I use timeout I have to call again my function at the end of the execution of the callback of timeout (like I'm doing right now). If something goes wrong, my callback will never be called again.
Anyway, I would like to know what's going on here. Am I missing something? Am I doing something wrong? Any suggestions?
You're clearing the current interval every time refreshCheckInRequests is called, but there is a delay between when refreshCheckInRequests is called and the new interval is assigned. Because refreshCheckInRequests also runs when an element is clicked, the following scenario could result in an unterminated interval:
User clicks, current interval is cleared, asynchronous findPendingCheckInRequests runs
User clicks again, no interval currently exists (nothing to clear), another asynchronous findPendingCheckInRequests runs
Response from first findPendingCheckInRequests comes back. complete handler runs, interval is assigned to the new interval
Response from second findPendingCheckInRequests comes back. complete handler runs, interval is assigned to the new interval over the old interval
The first created interval remains running, but there no longer exists a reference to it, so that first interval continues repeating forever.
So, try clearing the interval at the moment you reassign interval, ensuring that every new interval will always clear the old one, if an old one is running:
complete: function() {
clearInterval(interval);
interval = initInterval();
}
I'm developing a simple app with phonegap which every 30 seconds send user current coordinates to my database via ajax call.It works very well
$(document).ready(function() {
setInterval(function() {
SetLocationUpdates();
}, 30000);
});
If the app is in the foreground there is no problem and everything works fine, but if I open google maps app with this code
<div><a href="geo:41.897096,27.036545">Open maps app</div>
My app goes in the background(my app and google map app work separately.Two apps work at same time) and the interval function is not executed any more.
Is it possible to have the javascript code (a timer function) executing in the background with phonegap?
Edited:
I use cordova-plugin-background-mode(https://github.com/katzer/cordova-plugin-background-mode) but it still does not work.And alert give false.What is wrong with this?
document.addEventListener('deviceready', function () {
cordova.plugins.backgroundMode.enable();
alert(cordova.plugins.backgroundMode.isActive());
}, false);
I was looking for a solution to the same problem, and I think I just found one, though I haven't been able to fully try this out yet, I thought I'd post the answer here anyways, so you can try it out too.
One solution would be to set the timer in native code, instead of JavaScript, and there seems to be a Cordova plugin for just such a purpose:
https://github.com/dukhanov/cordova-background-timer
After installing the Cordova plugin, you can use it like this (pasted from the documentation):
var eventCallback = function() {
// timer event fired
}
var successCallback = function() {
// timer plugin configured successfully
}
var errorCallback = function(e) {
// an error occurred
}
var settings = {
timerInterval: 60000, // interval between ticks of the timer in milliseconds (Default: 60000)
startOnBoot: true, // enable this to start timer after the device was restarted (Default: false)
stopOnTerminate: true, // set to true to force stop timer in case the app is terminated (User closed the app and etc.) (Default: true)
hours: 12, // delay timer to start at certain time (Default: -1)
minutes: 0, // delay timer to start at certain time (Default: -1)
}
window.BackgroundTimer.onTimerEvent(eventCallback); // subscribe on timer event
// timer will start at 12:00
window.BackgroundTimer.start(successCallback, errorCallback, settings);
if you are still looking for an answer for this issue , or it can help someone else:
im sure that you are facing this problem in android so to resolve this you should add this piece of code after you add background-mode cordova plugin to your project :
this.backgroundMode.enable();
this.backgroundMode.on('activate').subscribe(()=>{
this.backgroundMode.disableWebViewOptimizations();
});
I have a jQuery Mobile web app which targets iOS and Android devices. A component of the application is a background task, which periodically checks for a.) changes to local data and b.) connectivity to the server. If both are true, the task pushes the changes.
I'm using a simple setTimeout()-based function to execute this task. Each failure or success condition calls setTimeout() on the background task, ensuring that it runs on 30 second intervals. I update a status div with the timestamp of the last task runtime for debugging purposes.
In any desktop browser, this works just fine; however, on iOS or Android, after some period of time, the task stops executing. I'm wondering if this is related to the power conservation settings of the devices--when iOS enters stand-by, does it terminate JavaScript execution? That is what appears to happen.
If so, what is the best way to resume? Is there an on-wake event which I can hook into? If not, what other options are there which don't involve hooking into events dependent on user interaction (I don't want to bind the entire page to a click event just to restart the background task).
Looks like Javascript execution is paused on MobileSafari when the browser page isn't focused. It also seems if setInterval() events are late, they are simply fired as soon as the browser is focused. This means we should be able to keep a setInterval() running, and assume the browser lost/regained focus if the setInterval function took much longer than usual.
This code alerts after switching back from a browser tab, after switching back from another app, and after resuming from sleep. If you set your threshold a bit longer than your setTimeout(), you can assume your timeout wouldn't finish if this fires.
If you wanted to stay on the safe side: you could save your timeout ID (returned by setTimeout) and set this to a shorter threshold than your timeout, then run clearTimeout() and setTimeout() again if this fires.
<script type="text/javascript">
var lastCheck = 0;
function sleepCheck() {
var now = new Date().getTime();
var diff = now - lastCheck;
if (diff > 3000) {
alert('took ' + diff + 'ms');
}
lastCheck = now;
}
window.onload = function() {
lastCheck = new Date().getTime();
setInterval(sleepCheck, 1000);
}
</script>
Edit: It appears this can sometimes trigger more than once in a row on resume, so you'd need to handle that somehow. (After letting my android browser sleep all night, it woke up to two alert()s. I bet Javascript got resumed at some arbitrary time before fully sleeping.)
I tested on Android 2.2 and the latest iOS - they both alert as soon as you resume from sleep.
When the user switches to another app or the screen sleeps, timers seem to pause until the user switches back to the app (or when the screen awakens).
Phonegap has a resume event you can listen to instead of polling for state (as well as a pause event if you want to do things before it is out of focus). You start listening to it after deviceReady fires.
document.addEventListener("deviceready", function () {
// do something when the app awakens
document.addEventListener('resume', function () {
// re-create a timer.
// ...
}, false);
}, false);
I use angular with phonegap and I have a service implemented that manages a certain timeout for me but basically you could create an object that sets the timer, cancels the timer and most importantly, updates the timer (update is what is called during the 'resume' event).
In angular I have a scopes and root scope that I can attach data to, my timeout is global so I attach it to root scope but for the purpose of this example, I'll simply attach it to the document object. I don't condone that because you need should apply it to some sort of scope or namespace.
var timeoutManager = function () {
return {
setTimer: function (expiresMsecs) {
document.timerData = {
timerId: setTimeout(function () {
timeoutCallback();
},
expiresMsecs),
totalDurationMsecs: expiresMsecs,
expirationDate: new Date(Date.now() += expiresMsecs)
};
},
updateTimer: function () {
if (document.timerData) {
//
// Calculate the msecs remaining so it can be used to set a new timer.
//
var timerMsecs = document.timerData.expirationDate - new Date();
//
// Kill the previous timer because a new one needs to be set or the callback
// needs to be fired.
//
this.cancelTimer();
if (timerMsecs > 0) {
this.setTimer(timerMsecs);
} else {
timeoutCallback();
}
}
},
cancelTimer: function () {
if (document.timerData && document.timerData.timerId) {
clearTimeout(document.timerData.timerId);
document.timerData = null;
}
}
};
};
You could have the manager function take a millisecond parameter instead of passing it into set, but again this is modeled somewhat after the angular service I wrote. The operations should be clear and concise enough to do something with them and add them to your own app.
var timeoutCallback = function () { console.log('timer fired!'); };
var manager = timeoutManager();
manager.setTimer(20000);
You will want to update the timer once you get the resume event in your event listener, like so:
// do something when the app awakens
document.addEventListener('resume', function () {
var manager = timeoutManager();
manager.updateTimer();
}, false);
The timeout manager also has cancelTimer() which can be used to kill the timer at any time.
You can use this class github.com/mustafah/background-timer based on #jlafay answer , where you can use as follow:
coffeescript
timer = new BackgroundTimer 10 * 1000, ->
# This callback will be called after 10 seconds
console.log 'finished'
timer.enableTicking 1000, (remaining) ->
# This callback will get called every second (1000 millisecond) till the timer ends
console.log remaining
timer.start()
javascript
timer = new BackgroundTimer(10 * 1000, function() {
// This callback will be called after 10 seconds
console.log("finished");
});
timer.enableTicking(1000, function(remaining) {
// This callback will get called every second (1000 millisecond) till the timer ends
console.log(remaining);
});
timer.start();
Hope it helps, Thank you ...
You should use the Page Visibility API (MDN) which is supported just about everywhere. It can detect if a page or tab has become visible again and you can then resume your timeouts or carry out some actions.
So i'm working on an android application for android, and I'm using the compass plugin.
I followed the instructions from the phonegap website, and it all works fine, except one thing.
The first example (http://docs.phonegap.com/en/2.0.0/cordova_compass_compass.md.html#Compass) should give the heading (the degree) in an alert once, but the value is always 0! But why?
So I have worked on the code, so it would look like the second example (without buttons). This code checks en displays the degree every second. Now the value isn't 0, but between 1 and 360.
this is the value i want, but i don't want it every second, i want it just once. Is there a way to check it just once?
document.addEventListener("deviceready", onDeviceReady, false);
// Cordova is ready
function onDeviceReady() {
navigator.compass.getCurrentHeading(onSuccess, onError);
}
// onSuccess: Get the current heading
function onSuccess(heading) {
alert('Heading: ' + heading.magneticHeading);
}
// onError: Failed to get the heading
function onError(compassError) {
alert('Compass Error: ' + compassError.code);
}
So this is the standard javascript, and this code should give the heading juist once, but it always gives 0.
$(document).ready(function() {
// The watch id references the current `watchHeading`
var watchID = null;
// Wait for Cordova to load
document.addEventListener("deviceready", onDeviceReady, false);
// Cordova is ready
function onDeviceReady() {
startWatch();
}
// Start watching the compass
function startWatch() {
var options = { frequency: 100 };
watchID = navigator.compass.watchHeading(onSuccess, onError, options);
}
// Stop watching the compass
function stopWatch() {
if (watchID) {
navigator.compass.clearWatch(watchID);
watchID = null;
}
}
// onSuccess: Get the current heading
function onSuccess(heading) {
var element = document.getElementById('heading');
element.innerHTML = heading.magneticHeading;
if (heading.magneticHeading > 180) {
document.getElementById('background').style.backgroundColor = 'green';
} else {
document.getElementById('background').style.backgroundColor = 'blue';
}
}
// onError: Failed to get the heading
function onError(compassError) {
alert('Compass error: ' + compassError.code);
}
});
This code works, but it checks if not once, but multple times a second.
Without seeing the full code you're using, I'd say the first example may be giving zero because the device is not ready (you'll notice that in the second example, the call to watchHeading is not made until after the deviceready event fires).
As for the second issue, this is because watchHeading continuously requests feedback (controlled by the frequency option). After you get the ID for the watch, you need to call clearWatch and pass it that ID to stop.
If you're just trying to get a single heading, just call getHeading rather than watchHeading. But do so only after the device is ready.
---Edit---
This bugged me enough to build your project and test it out on my Droid 4 (running 4.1.2). Returning 0 -is- normal behavior; that's just the compass warming up (similar to GPS where you want to throw out the first number of coordinates you receive as they are typically stale). Usually just one call is enough to warm it up, and after that you will receive good data.
Since you don't want a constant feedback, I would warm up the compass on app start (just call getCurrentHeading once), then call getCurrentHeading again whenever you need.
Alternatively, go ahead and receive feedback every few seconds as there's no harm in just storing the latest compass heading and it keeps the compass primed. Then when you want your app to make use of the data, you can either just call getCurrentHeading or use the stored compass data.
You need to call stopWatch() from inside onSuccess() since youv'e told watchHeading() to repeat every 100 ms.