PhoneGap navigator.compass.getCurrentHeading called multiple times on iPhone - ios

I would appreciate any help in solving this - or at least where to look to solve it.
What I have is calling on iPhone navigator.compass.getCurrentHeading(succ, fail), the success function is called every time the device is moved even slighly. In the XCode debug log I see lots of entries of navigator.compass.setHeading calls being generated for every movement. If I try to poll for heading data again - the request just hangs. Here's the code:
function onBodyLoad() {
if (typeof navigator.device == "undefined") {
document.addEventListener("deviceready", onDeviceReady, false);
} else {
onDeviceReady();
}
}
function succ(heading) {
alert("compass " + heading);
}
function fail() {
alert('fail');
}
function onDeviceReady() {
navigator.compass.getCurrentHeading(succ, fail);
}
This is really strange behaviour, as I expect getCurrentHeading to be called just once and return a single result, instead of the unstoppable flurry of events.
I use PhoneGap 1.0.0. The same code on Android works perfectly. I've removed all custom JS code to prevent possibility of conflicts.

It is odd that noone else seems to encounter this. In any case, this (hacky) solution may help anyone who comes looking for an answer.
We had to stop using getCurrentHeading because of this issue, and replaced it with navigator.compass.watchHeading instead. On clearing the watch we also call navigator.compass.stop() function to prevent from further compass spamming (for iPhone platform only - Android is fine), and before calling watchHeading again we call navigator.compass.stop() and navigator.compass.start(), to reinitialize the compass "just in case" (again, on iPhone only).
After taking these measures the page that user compass no longer hangs on second entry, and there is no heading spamming outside of this page.

Related

.play() for audio tags lag on iOS (and possibly other mobile devices)

I am attempting to rebuild a game that works on itch to be compatible on most major devices and browsers. I have a problem where on iOS (and possibly other mobile devices) a call to play audio tags from click and touch events has quite a significant delay.
I have read about several potential causes, including the 300ms delay, preventDefault for the second click event, stopPropagation for potential parent clicks, different audio formats causing lag in decompression, etc. Nothing seems to work.
Initially my intent was to keep everything in vanilla js without outside libraries to force myself to really learn what's going on under the hood. However, I have been having some success with some outside libraries for other problems, so I tried fastclick.js for this problem. That didn't work for me either. So, if someone knows how to address this issue without a library that would be great, but after looking at the fastclick code, that may be beyond my level of comprehension.
A current build can be found at www.teachersteve.net/assett_loading_with_ian/assett_loading_with_ian.html
Some explanation of my thought process:
I removed anything that is actually game related to try and isolate the problem. I put all the assetts directly in the html to simplify the loading process and wait to start the js after the DOM loaded
document.addEventListener("DOMContentLoaded", doSomething);
I am currently only calling one audio tag to play as I read somewhere that calling multiple tags can overload the decompression process and cause a delay. That seems to not be the issue.
I have 3 different file formats so far for compatibility attempts... I did read that LEI16 (a wav format) might be best because it eliminates compression, although I haven't tried it yet.
This is the rest of the doSomething() function
function doSomething() {
document.body.style.opacity = 1;
document.addEventListener("click", playAudio);
document.addEventListener('click', preventInputDefault);
document.addEventListener('ontouchend', preventInputDefault);
console.log("assetts loaded");
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
function playAudio() {
// backgroundMusic.play();
letterAudio.play();
// correctAnswerAudio.play();
// letterAudio.play();
// correctAnswerAudio.play();
}
function preventInputDefault(evt) {
evt.preventDefault();
console.log("hello preventInputDefault");
evt.stopPropagation();
console.log("hello stopPropagation");
}
}
Thanks!

Any way yet to auto-update (or just clear the cache on) a PWA on iOS?

I have been struggling on iOS with something that works easily on Android: Getting my PWA to auto-update when there is a new version. I am not at all sure this is even possible on iOS. I have used vue.js and Quasar to build my app, and everything works out of the box on Android. Here is the (ugly, terrible) way things stand currently on the iOS version:
I can check my own server for the version and compare it against the current one stored in my app (in indexedDB) and throw up a notice that there is a new version. So far so good.
Other than having the user MANUALLY CLEAR THE SAFARI CACHE (!!) there is no way I can figure out how to programmatically clear the PWA cache from within the app or force an upload in another way.
So at this point I guess my questions are:
Has ANYONE been able to get a PWA on iOS (11.3 or later) to auto-update when a new version is available?
Is there a way to clear the (safari) app cache from within my PWA?
Obviously it is an incredibly awful user experience to notify the user that in order to update they must perform several steps outside of the app to be able to refresh it, but it seems this is where iOS stands at the moment unless I am missing something. Has anyone anywhere made this work?
After weeks and weeks of searching, I finally found a solution:
I add a check for versionstring on the server, and return it to the app as mentioned above.
I look for it in localtstorage (IndexedDB) and if I don’t find it, I add it. If I do find it, I compare versions and if there is a newer one on the server, I throw up a dialog.
Dismissing this dialog (my button is labeled “update”) runs window.location.reload(true) and then stores the new versionstring in localstorage
Voila! My app is updated! I can't believe it came down to something this simple, I could not find a solution anywhere. Hope this helps someone else!
UPDATE SEPT 2019:
There were a few problems with the technique above, notably that it was bypassing the PWA service worker mechanisms and that sometimes reload would not immediately load new content (because the current SW would not release the page). I have now a new solution to this that seems to work on all platforms:
function forceSWupdate() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
for (let registration of registrations) {
registration.update()
}
})
}
}
forceSWupdate()
And inside my serviceworker I now throw up the dialog if there is an update, and do my location.reload(true) from there. This always results in my app being refreshed immediately (with the important caveat that I have added skipWaiting and clientsClaim directives to my registration).
This works on every platform the same, and I can programatically check for the update or wait for the service worker to do it by itself (although the times it checks vary greatly by platform, device, and other unknowable factors. Usually not more than 24 hours though.)
If anyone is still having issues with this, registration.update() did not work for me. I used the exact solution but when the version from my server did not match my local stored version, I had to unregister the service workers for it to work.
if ('serviceWorker' in navigator) {
await this.setState({ loadingMessage: 'Updating Your Experience' })
navigator.serviceWorker.getRegistrations().then(function(registrations) {
registrations.map(r => {
r.unregister()
})
})
await AsyncStorage.setItem('appVersion', this.state.serverAppVersion)
window.location.reload(true)
}
Then when the app reloads, the service worker is reregistered and the current version of the app is visible on iOS safari browsers and 'bookmarked' PWAs.
Instead of prompting the user, that a new version is available, you can also extend the 'activate' Eventlistener to delete your old cache whenever you publish a new serviceworker version.
Add version and name variables
var version = "v3" // increase for new version
var staticCacheName = version + "_pwa-static";
var dynamicCacheName = version + "_pwa-dynamic";
Delete caches, when their names do not fit the current version:
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.filter(function(cacheName) {
if (!cacheName.startsWith(staticCacheName) &&
!cacheName.startsWith(dynamicCacheName)) {
return true;
}
}).map(function(cacheName) {
console.log('Removing old cache.', cacheName);
return caches.delete(cacheName);
})
);
})
);
});
(credits: https://stackoverflow.com/a/45468998/14678591)
In order to make this work for iOS safari browsers and 'bookmarked' PWAs too, I just added the sligthly reduced function by #jbone107:
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.filter(function(cacheName) {
if (!cacheName.startsWith(staticCacheName) &&
!cacheName.startsWith(dynamicCacheName)) {
return true;
}
}).map(function(cacheName) {
// completely deregister for ios to get changes too
console.log('deregistering Serviceworker')
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function(registrations) {
registrations.map(r => {
r.unregister()
})
})
window.location.reload(true)
}
console.log('Removing old cache.', cacheName);
return caches.delete(cacheName);
})
);
})
);
});
This way you just have to increase the version number and updating is done by the serviceworker automatically.

Cocos2d-x v3 iOS app not registering first x touch events

I have a really weird issue with cocos2d-x v3, the first 15 touches or so are not registered on my iOS device (tried iPad 2 and iPad air). As soon as a touch is finally registered, everything works fine (aka all touches after that trigger the onTouch functions).
The touch events work perfectly fine in the simulator.
Also, the same code works perfectly fin in my Windows and Android builds.
Has anyone had this happen, or maybe know what could be causing it?
I'm using the listener, and I debugged up to the spot where touchesBegan forwards the input events to the listener, but even there the events don't come in until after the 15th tap or so.
It's really weird... And I figured I'd give it a shot here, as someone might have encountered this as well, before I start stripping code to as clean as possible, and then try to work my way back from there...
Kind regards,
Michaël
EDIT: As requested, here is some code. The desired behaviour is that it works in iOS devices like it should: First touch triggers the onTouchBegan.
I didn't add it as it didn't think it would matter, since the code works fine for Android.
But I appreciate that you'd like to see it, just in case I might have missed something
GameLayer is a Cocos2d::Layer.
void GameLayer::onEnter()
{
cocos2d::CCLayer::onEnter();
// Register Touch Event
auto pEventDispatcher = cocos2d::Director::getInstance()->getEventDispatcher();
if (pEventDispatcher)
{
// Touch listener
auto pTouchListener = cocos2d::EventListenerTouchOneByOne::create();
if (pTouchListener)
{
pTouchListener->setSwallowTouches( true );
pTouchListener->onTouchBegan = CC_CALLBACK_2( GameLayer::onTouchBegan, this );
pTouchListener->onTouchMoved = CC_CALLBACK_2( GameLayer::onTouchMoved, this );
pTouchListener->onTouchEnded = CC_CALLBACK_2( GameLayer::onTouchEnded, this );
pTouchListener->onTouchCancelled = CC_CALLBACK_2( GameLayer::onTouchCancelled, this );
pEventDispatcher->addEventListenerWithSceneGraphPriority( pTouchListener, this );
}
}
}
bool GameLayer::onTouchBegan( cocos2d::Touch* pTouch, cocos2d::Event* /*pEvent*/ )
{
// Breakpoint here triggers fine on first touch for Android/Windows/iOS Simulator,
// but not on iOS device (iPad/iPhone)
bool breakHere = true;
<<snip actual code>>
}
EDIT:
The problem was an std::ofstream trying to open() on the iOS device (most likely in a folder it didn't have access to).
I have lots of layers in my game and I don't do it like you do. In your code the need to get the EventDispatcher locally and create the touch listener like how you are seems odd to me. I've never seen it down that way in so many steps.
I do:
auto listener = cocos2d::EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = [&](cocos2d::Touch* touch, cocos2d::Event* event)
{
return true;
};
listener->onTouchEnded = [=](cocos2d::Touch* touch, cocos2d::Event* event)
{
// ... do something
};
cocos2d::Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(listener, 31);
I got it fixed.
The problem was seemingly totally unrelated, I was trying to open an std::ofstream file (my log file), most likely in a folder it didn't have (any and/or write) access to.
Which is not required, nor wanted on the iOS device.
Once I added IOS to the exclusion list (just like Android and some more targets) everything started to work perfect.
I do not know what goes wrong exactly, and why it does start working after a few touch inputs, but I'm guess it was waiting or retrying something in the background.
I found the issue while debugging another one :)
Hopefully this helps anyone else who might stumble onto the same or a related issue.
Kind regards,
Michaël

Call function in a dart polymer element [duplicate]

Having a weird issue. In my Dart code I have some polymer components on the screen and one of them has a method I call from my main().
I grab a reference to it by doing
PolyComp poly = querySelector("#idOfPolymer");
poly.flash();
This works perfectly in dart. The page loads up and PolyComp starts to flash. However when I run this in Chrome by running Build Polymer app from the Dart IDE, I get an error that says cannot call flash() on null.
I ended up making it flash by just using an event bus and letting PolyComp listen to my event, but this is overkill.
What am I doing wrong? This happens in the latest Chrome, Firefox and Safari.
Edit:
I built the following polymer app to JS also and ran into the same issue.
https://github.com/sethladd/dart-polymer-dart-examples/blob/master/web/todo_element/todo.html
Works on DartVM, not in Chrome because its calling a method on a null element.
When you run this code from the main() method it is probably a timing issue.
You can try something like
import "package:polymer/polymer.dart";
main() {
initPolymer().run(() {
// code here works most of the time
Polymer.onReady.then((e) {
// some things must wait until onReady callback is called
});
});
}
see also how to implement a main function in polymer apps

How to detect user idle time since last touch and return the app to home page using phonegap

I would like to check for the user idle time since last touch and return the app to the home page after some period of time. I want this to be done using phonegap.
I googled and did find few solutions but I want to detect the idle time and return the app to the home page.
Thanks.
Using jQuery you *could bind a start touch event and end touch event then using a timer to execute a function
$('body').bind('touchstart',function() {
clearInterval(myTimer);
});
$('body').bind('touchend', function() {
myTimer = setInterval(function() {
/* return user to homepage */
},30000);
});
Touch events are a little buggy in mobile devices. But you set an Interval timer to run after a set amount of time after the last touch is detected. Remembering to clear it on the next touchstart event. Its a bit messy but should work (I havent tested it btw)
I got this working by setTimeout('Redirect()', 10000); where Redirect fn is function Redirect() { window.location.href="mylink.html"; }

Resources