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.
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 webpage that I'm using to print 'pages' of data as PDF files via the firefox 'Print to File' printer on my laptop. The code I'm invoking is as follows:
document.body.controls.cmdPrint.click = function () // Create a function that will be called when this object is clicked upon
{if (parseInt(document.body.controls.page.innerHTML) !== 0) // If we are not on the Front Cover
{return false;} // Function complete: Abnormal Termination
document.body.controls.style.pointerEvents = 'none'; // Lock down the controls so they cannot be interfered with
do // Do...
{window.print(); // Print this page
// document.body.sleep(); // Removed as this does not work as expected (see below...)
} while (document.body.controls.cmdNext.click()) // ...while we are able to advance.
document.body.controls.style.pointerEvents = ''; // Release the controls lockout
this.blur(); // Blur the focus
return true;}; // Function complete: Normal Termination
When executing, the pages flip as expected (as the cmdNext.click() function returns a true when successful and a false when it's on the last page and trying to advance), but it runs too quickly. Namely every odd page is caught out, as the 'printer' is unavailable....the window.print() is being released before the printer is ready for the next page.
I tried slowing down execution by adding a reference to a secondary function within the loop (now commented out), but this just locks up the CPU and keeps the printer from processing in another thread....so it's not a valid solution. This function (which I wrote but did not provide the expected cushioning to allow odd pages to print) is as follows.
document.body.sleep = function (delay) // Create a new function
{delay = delay || 1; // Default to a delay of 1 second
var timestamp = new Date(); // Get current time
timestamp = new Date(timestamp.getTime() + (delay * 1000)); // Add in the delay (in seconds)
while (new Date() < timestamp) {} // While we are waiting for the delay, do nothing
return true;}; // Function complete: Normal Termination
Basically what I need is a way to hold loop execution long enough to let the 'Print to File' to go through before the next window.print() is called. Using the sleep function above also keeps the window.print() from running even though (when I was trying to use it as a fix) I had put the call to this function immediately after the window.print() command.
So I ask, can anyone here supply a fix for this project so I don't have to manually cycle through each page (which could get annoyingly temporally expensive with a page count above 10)?
As it is currently, when the second page tries to print, I get the popup window holding an error from firefox: "Printer Error - Some printing functionality is not currently available." Tracking this down lead to PERR_NOT_AVAILABLE....probably because the printer (Print to File) is busy printing the prior page....so I just need to wait for that to resolve before going to the next printing. An error handler to catch this PERR_NOT_AVAILABLE instead of letting it bounce to the user (me) as a popup window that has to be clicked out of would be nice, though a spammy way of getting the pages to print in sequence as quickly as the Print to File system can process them.
If window.print() actually returned an error in this condition, I could just rerun the command...
In my experimentation, I found this workaround...it's quite fairly considered a kludge, but in the absence of a more elegant solution, it works.
document.body.controls.cmdPrint.click = function () // Create a function that will be called when this object is clicked upon
{if (parseInt(document.body.controls.page.innerHTML) !== 0) // If we are not on the Front Cover
{return false;} // Function complete: Abnormal Termination
document.body.controls.style.pointerEvents = 'none'; // Lock down the controls so they cannot be interfered with
window.onafterprint = function () // Set up a handler for after a print operation
{setTimeout(function () // Run a delayed operation
{if (document.body.controls.cmdNext.click()) // Move to next page and if this is successful
{window.print(); // Continue printing
return false;} // Function complete: Still Printing
do // Do nothing...
{} while (document.body.controls.cmdPrevious.click()) // ...while we cycle back to the start
document.body.controls.style.pointerEvents = ''; // Release the controls lockout
this.blur(); // Blur the focus
window.onafterprint = function () {}; // Remove this handler
return true;}, 2000);}; // Function complete: Normal Termination
window.print(); // Begin printing the page
return true;}; // Function complete: Normal Termination
If a page is too complex to print to PDF in 2 seconds, that 2000 figure in the setTimeout() will have to be increased to account for it. For my test case (the current 7 page document), 2000 seems to be the sweet spot between not-working and spitting out pages as quickly as Firefox will permit on my system.
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');
}
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.
For some reason this check for new chat messages causes a larger amount of browser (and to some extent server) load than I would expect. Anyone see any ways that I can make it more efficient, to lessen the load?
// Begin the cycle of refreshing the mini chat after the standard delay.
function startRefreshingMinichat(){
var secs = 30; // Chat checking frequency.
setTimeout(function (){
checkForNewChats();
startRefreshingMinichat(); // Loop the check for refresh.
}, secs*1000);
}
// Check for the latest chat and update if it's different.
function checkForNewChats(){
// Check whether the latest chat doesn't match the latest displayed chat.
// NOTE THAT THIS CALLBACK DOES NOT TRIGGER IMMEDIATELY.
$.getJSON('api.php?type=latest_chat_id&jsoncallback=?', function(data){
var newChats = false;
// Update global data stores if an update is needed.
if(updateDataStore(data.latest_chat_id, 'chat_id', 'latestChatId', 'chat_id')){
newChats = true;
}
if(newChats){ // there are new chats to show.
refreshMinichat(null, 50); // loads new chat content.
}
// Since this callback isn't immediate, any feedback has to occur whenever the callback finishes.
}); // End of getJSON function call.
}
Check out CometD. It's a js long-polling system I've used with some success for simple chat systems integrated with jQuery. (Last time I looked, there were a few jQuery specific implemetations, but I never found one that was robust enough for me.)
you can checkout this push engine so that you have not to poll for new data anymore.
check it out, its really cool.