I downloaded the sample Location services project from https://mobilefirstplatform.ibmcloud.com/tutorials/en/foundation/7.1/advanced-client-side-development/location-services-hybrid-applications/. From the sample I kept posChange trigger policy and i deleted the other two. And I am getting the location when app is opened. But not in the app is closed. Below the snippet
function getFirstPositionAndTrack() {
var geoPolicy = WL.Device.Geo.Profiles.RoughTracking();
geoPolicy.timeout = 60000;
geoPolicy.maximumAge = 10000;
geoPolicy.minChangeDistance = 3;
WL.Device.Geo.acquirePosition(
function(pos) {
var triggers = {
Geo: {
posChange: { // display all movement
type: "PositionChange",
minChangeDistance: 5,
callback: function(deviceContext) {
WL.Client.transmitEvent({ event: 'deviceLocation'}, true);
}
},
}
};
WL.Device.startAcquisition({ Geo: geoPolicy }, triggers, { Geo: alertOnGeoAcquisitionErr } );
},
function(geoErr) {
console.log(geoErr);
// try again:
getFirstPositionAndTrack();
},
geoPolicy
);
}
And in adapter to check background
WL.Server.setEventHandlers([
WL.Server.createEventHandler({ event: 'deviceLocation'}, deviceEvent)
]);
Procedure
function deviceEvent(event) {
WL.Logger.error(JSON.stringify(event));
}
And in onconnectSuccess I am calling the function and setting the keepAlive Property
getFirstPositionAndTrack()
WL.App.setKeepAliveInBackground(true);
And is there any other way to debug when app is in background or closed?
Related
Inside Android webview I consistent get a PermissionDenied error back from a call to navigator.mediaDevices.getUserMedia despite asking for permission first.
My process starts with a call to StartRecordAudio function below with the CheckPermissionFirst parameter set to true if working in Android.
var AudioRecorder ; // recorder
var RawAudioDataArray ; // raw recorded data
var AudioSaveFormat = "audio/wav" ; // waveform audio - alternatives "audio/mpeg-3" and "audio/webm" ;
var AudioStartCallback ; // function to be called at start of input
var AudioErrorCallback ; // function to be called if audio cannot be recorded (no capability orno permission)
var AudioDoneCallback ; // function to be called at end of input
function StartRecordAudio (CheckPermissionFirst,StartCallback,ErrorCallback,DoneCallback)
{
AudioStartCallback = StartCallback ;
AudioErrorCallback = ErrorCallback ;
AudioDoneCallback = DoneCallback ;
RawAudioDataArray = [] ;
if (AudioRecorder)
{
StartRecording ();
}
else if (CheckPermissionFirst)
{
CheckAppMicrophonePermission ()
}
else
{
navigator.mediaDevices.getUserMedia ({audio:true}).then (AudioDeviceFound).catch (AudioErrorCallback);
};
};
CheckAppMicrophonePermission is as follows
function CheckAppMicrophonePermission ()
{
AndroidBridge.call ("CheckMicrophonePermission" , []);
};
Skipping over the details of the AndroidBridge, this call triggers the following java function in the Android wrapper code
if ("CheckMicrophonePermission".equals(command))
{
if (ContextCompat.checkSelfPermission (MainActivity.this, android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions (MainActivity.this, new String[]{android.Manifest.permission.RECORD_AUDIO}, requestMicrophoneId );
}
else
{
MicrophonePermissionsOK ();
}
}
which when finished interrogating the user will call
public void onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults)
{
switch (requestCode)
{
case requestMicrophoneId:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
MicrophonePermissionsOK();
break;
}
else if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE))
{
MicrophonePermissionsNOTOK(false);
}
else
{
MicrophonePermissionsNOTOK(true);
};
break;
}
}
Function MicrophonePermissionsOK is as follows :
public void MicrophonePermissionsOK ()
{
String jsString = "AppMicrophonePermissionGranted()";
mWebView.evaluateJavascript (jsString);
}
Back on the original side of the Android Bridge
function AppMicrophonePermissionGranted ()
{
AndroidMicrophonePermission = true ;
ShowAlert ("Please select OK to activate the app microphone link");
AlertCallback = function () {HideAlert (); AudioPermissionGranted ()};
};
Finally when the user ok's my custom alert dialog (details of this function irrelevant here I think), we get to:
function AudioPermissionGranted ()
{
navigator.mediaDevices.getUserMedia ({audio:true}).then (AudioDeviceFound).catch (AudioErrorCallback);
};
For anyone interested the promise success route is to
function AudioDeviceFound (Stream)
{
AudioRecorder = new MediaRecorder (Stream);
AudioRecorder.ondataavailable = function (e)
{
RawAudioDataArray.push (e.data);
if (AudioRecorder.state == "inactive") // recorder has been stopped either by user or after max seconds
{
var blob = new Blob (RawAudioDataArray, {type:AudioSaveFormat});
var reader = new FileReader ();
reader.onload = function () {AudioDoneCallback (reader.result.split (',')[1])};
reader.readAsDataURL (blob);
};
};
StartRecording ();
};
function StartRecording ()
{
AudioStartCallback ();
AudioRecorder.start (1000); // record in 1 second chunks
SetAudioTimeout ();
};
function SetAudioTimeout ()
{
AudioTimeout = window.setTimeout (function () {AudioRecorder.stop();
AudioTimeout = null}, MaxAudioSeconds * 1000);
};
All of this code works fine on Chrome and inside a Chrome Packaged App.
However I have traced the code inside an Android webview and I get the correct sequence of actions right through all the code up until invoking the getUserMedia promise.
This is the case for a new loaded app or an existing loaded app. Also when the permission has not been requested before, when the permission has been requested denied and when the permission is granted, both through the permission dialog shown above directly in the settings app.
However (in Android webview), invoking the getUserMedia promise always triggers the catch condition with error parameter name set to PermissionDenied and the message set to empty string.
Please can someone help me?
Running a server on nodejs, since I jumped to payload firebase notifications following google's tutorial (and using 'node-gcm' package), everything works fine. The registration on my Android phone (Galaxy A3 2017 on Android 8.0.0) is working, I do receive notifications BUT it is not permanent. After a few days, my machine is not registered anymore. It does work well on desktop chrome so I would not suspect the server side.
Below is the code on the client side
var reg;
navigator.serviceWorker.register('sw.js').then(function() {
return navigator.serviceWorker.ready;
}).then(function(serviceWorkerRegistration) {
reg = serviceWorkerRegistration;
console.log('Service Worker is ready :^)', reg);
checkSubscription();
}).catch(function(error) {
console.log('Service Worker Error :^(', error);
});
};
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (var i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
const vapidPublicKey = '<Your Public Key from generateVAPIDKeys()>';
const convertedVapidKey = urlBase64ToUint8Array(vapidPublicKey);
function subscribe() {
if (reg){
reg.pushManager.getSubscription().then(function(pushSubscription) {
if (pushSubscription==null){
reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: convertedVapidKey
}).then(function(pushSubscription) {
var sub = pushSubscription;
console.log('Subscribed!', sub);
socket.emit("notification subscription payload",sub);
isSubscribed = true;
}).catch(function(error){
alexUI.view.log.addLog({severity:"ERROR"},clientSocketID,"Erreur de souscription");
alexUI.view.radioButton($("#subscription"),"Off");
});
}else{
var sub = pushSubscription;
// socket.emit("notification subscription",sub.endpoint);
socket.emit("notification subscription payload",sub);
isSubscribed = true;
};
}).catch(function(e){
// to remove message later Alex
alexUI.view.log.addLog({severity:"ERROR",message:e},clientSocketID,"Notifications indisponibles");
alexUI.view.radioButton($("#subscription"),{set:"Off"});
});
}else{
alexUI.view.log.addLog({severity:"ERROR"},clientSocketID,"Navigateur incompatible");
alexUI.view.radioButton($("#subscription"),"Off");
};
};
After cleaning my phone storage, the problem disapears. Service worker were not able to load properly with phone memory full.
I'm building an app that needs to track location of user and I use
Ti.Geolocation.accuracy = Ti.Geolocation.ACCURACY_BEST;
Ti.Geolocation.distanceFilter = 0;
Ti.Geolocation.preferredProvider = Ti.Geolocation.PROVIDER_GPS
Ti.Geolocation.addEventListener('location', locationChange);
On iOS when device is in the move the event is not fired regularly and when it's fired I dont have heading and speed ( even tested it on while driving )
...
heading : -1,
speed : -1
...
but if I run another navigation app on background (like Plans) the event is fired constantly and I have heading and speed of device, as if i'm only getting the events because the other apps.
its the same problem on android the event is not fired correctly
testing with ti SDK 5.1.2 and 5.5.1
This has tripped me up in the past. Add
Ti.Geolocation.accuracy = Ti.Geolocation.ACCURACY_BEST_FOR_NAVIGATION;
Also note that very small numbers in the distance filter may cause some problems.
I use this
if (OS_IOS) {
Ti.Geolocation.accuracy = Ti.Geolocation.ACCURACY_BEST_FOR_NAVIGATION;
Ti.Geolocation.distanceFilter = Alloy.CFG.minUpdateDistance;
Ti.Geolocation.preferredProvider = Ti.Geolocation.PROVIDER_GPS;
Ti.Geolocation.pauseLocationUpdateAutomatically = true;
Ti.Geolocation.activityType = Ti.Geolocation.ACTIVITYTYPE_OTHER_NAVIGATION;
} else { //Android
Ti.Geolocation.Android.manualMode = true;
var gpsProvider = Ti.Geolocation.Android.createLocationProvider({
name: Ti.Geolocation.PROVIDER_GPS,
minUpdateTime: Alloy.CFG.minAge / 1000,
minUpdateDistance: Alloy.CFG.minUpdateDistance
});
var gpsRule = Ti.Geolocation.Android.createLocationRule({
provider: Ti.Geolocation.PROVIDER_GPS,
accuracy: Alloy.CFG.accuracy,
maxAge: Alloy.CFG.maxAge,
minAge: Alloy.CFG.minAge,
});
Ti.Geolocation.Android.addLocationProvider(gpsProvider);
Ti.Geolocation.Android.addLocationRule(gpsRule);
Ti.Geolocation.Android.manualMode = true;
}
The Alloy.CFG settings are set in the config.json file.
{
"global": {
"minUpdateDistance": 10,
"os:android": {
"accuracy": 20,
"minAge": 10000,
"maxAge": 30000
},...
I intend to get users geolocation even when the app sits dormant in the background and store the same in the database.
I'm using katzer's Cordova Background Plug-in,
When I try to access navigator.geolocation.getCurrentPosition inside backgroundMode.onactivate function, nothing happens, Whereas when I try passing hard coded values api is called, data is stored in database.
following is my code
document.addEventListener('deviceready', function() {
// Android customization
cordova.plugins.backgroundMode.setDefaults({
text: 'Doing heavy tasks.'
});
// Enable background mode
cordova.plugins.backgroundMode.enable();
// Called when background mode has been activated
cordova.plugins.backgroundMode.onactivate = function() {
console.log('inside background')
a();
}
var a = function() {
console.log('a called')
navigator.geolocation.getCurrentPosition(function(pos) {
console.log('inside navigate');
var data = {
Lati: '123456',
Longi: '132456',
//LoginID: JSON.parse(window.localStorage.getItem('LoginId'))
EmpCode: localStorage.getItem('LoginId')
};
$http.post("https://app.sbismart.com/bo/ContactManagerApi/UpdateEmployeeLoc", data).success(function(rsdata, status) {
console.log('inside rsdata');
console.log(data.Lati + "," + data.Longi);
})
}, function(error) {
alert('Unable to get location: ' + error.message);
});
}
}, false);
cordova.plugins.backgroundMode.onfailure = function(errorCode) {
console.log(errorCode)
};`
and check as to why is it failing....then again u need to run the locationService function in a timeout function in the background to get updated about the location and check the location from previously got location.
Something like this...
cordova.plugins.backgroundMode.onactivate = function () {
setTimeout(function () {
a();
}, 5000);
}
Hope this helps.
I'm using the InAppBrowser plugin (v1.1.1) with Cordova for an OAuth login process. Unfortunately, the InAppBrowser doesn't appear to be closing the browser. My "closeBrowser" function instead continually triggers the interval, and the browser remains on-screen on the Android (I have not tried other devices at this time.)
Is there a way to forcibly close the InAppBrowser other than .close(), or hide it? Or maybe there's a flaw in my code somewhere that is locking the browser.
LogInPage.prototype.handleExternalLogin = function (externalLogin) {
var _this = this;
var ref = window.open(Environment_1.settings.baseUrl + externalLogin.route.url, "_blank", "location=no");
ref.addEventListener('loadstart', function (event) {
if (_.startsWith(event.url, Environment_1.settings.baseUrl + "/api/Account/account/ExternalLoginCallback")) {
// Now we want to load a different url that will give us the mobile access token
console.log('get external-mobile-token');
_this.closeBrowser(ref);
var ref2 = window.open(Environment_1.settings.baseUrl + "/api/Account/external-mobile-token", "_blank" /*, "location=no"*/);
ref2.addEventListener('loadstop', function (event) {
console.log('loadstop ' + event.url);
if (event.url == Environment_1.settings.baseUrl + "/api/Account/external-mobile-token") {
ref2.executeScript({ code: 'window.document.documentElement.innerText' }, function (contents) {
_this.login(contents);
_this.closeBrowser(ref2);
});
}
});
ref2.addEventListener('loaderror', function (event) {
console.log(event);
_this.closeBrowser(ref2);
// TODO - do something?
});
}
});
ref.addEventListener('loaderror', function (event) {
console.log(event);
_this.closeBrowser(ref);
// TODO - do something?
});
};
LogInPage.prototype.closeBrowser = function (browser) {
var interval = setInterval(function () {
console.log('closing');
browser.close();
}, 10);
browser.addEventListener('exit', function () {
console.log('closed');
clearInterval(interval);
});
};
LogInPage.prototype.login = function (token) {
console.log(token);
};
The above code is actually generated from TypeScript, but I figured I wouldn't confuse the issue.
It appears that closing an InAppBrowser and opening another at the same time was causing the issue; rewriting the process to only need one window solved my issue.