(Re)Adding background geolocation.
This commit is contained in:
parent
4dbdcecadd
commit
acc1d7cc1e
|
@ -11,6 +11,7 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@awesome-cordova-plugins/geolocation": "^6.2.0",
|
"@awesome-cordova-plugins/geolocation": "^6.2.0",
|
||||||
|
"@capacitor-community/background-geolocation": "^1.2.6",
|
||||||
"@capacitor/android": "^3.4.3",
|
"@capacitor/android": "^3.4.3",
|
||||||
"@capacitor/core": "^3.4.3",
|
"@capacitor/core": "^3.4.3",
|
||||||
"@capacitor/ios": "^3.4.3",
|
"@capacitor/ios": "^3.4.3",
|
||||||
|
@ -550,6 +551,14 @@
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@capacitor-community/background-geolocation": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@capacitor-community/background-geolocation/-/background-geolocation-1.2.6.tgz",
|
||||||
|
"integrity": "sha512-zYe2gUf+CXoE7tbBzd6CNv3yW1SRCVAxVKg5t63zHbMQR2JfurVlfIEnXjLyc+strAMPavGwbIXEjiGv5jJvdA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@capacitor/core": ">=3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@capacitor/android": {
|
"node_modules/@capacitor/android": {
|
||||||
"version": "3.9.0",
|
"version": "3.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@capacitor/android/-/android-3.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@capacitor/android/-/android-3.9.0.tgz",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@awesome-cordova-plugins/geolocation": "^6.2.0",
|
"@awesome-cordova-plugins/geolocation": "^6.2.0",
|
||||||
|
"@capacitor-community/background-geolocation": "^1.2.6",
|
||||||
"@capacitor/android": "^3.4.3",
|
"@capacitor/android": "^3.4.3",
|
||||||
"@capacitor/core": "^3.4.3",
|
"@capacitor/core": "^3.4.3",
|
||||||
"@capacitor/ios": "^3.4.3",
|
"@capacitor/ios": "^3.4.3",
|
||||||
|
|
|
@ -12,13 +12,54 @@ import Dialog from '../dialog';
|
||||||
import { useI18n } from '@solid-primitives/i18n';
|
import { useI18n } from '@solid-primitives/i18n';
|
||||||
import GpxChooser from '../gpx-chooser';
|
import GpxChooser from '../gpx-chooser';
|
||||||
import { currentGpxId } from '../gpx-dialog';
|
import { currentGpxId } from '../gpx-dialog';
|
||||||
|
import {
|
||||||
|
startBackgroundGeolocation,
|
||||||
|
stopBackgroundGeolocation,
|
||||||
|
} from '../../lib/background-geolocation';
|
||||||
|
import dispatch from '../../workers/dispatcher-main';
|
||||||
|
import { emptyTrk } from '../../db/trk';
|
||||||
|
import getUri from '../../lib/ids';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var $lastValidLocationTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
const GpxRecord: Component<{}> = (props) => {
|
const GpxRecord: Component<{}> = (props) => {
|
||||||
const [t] = useI18n();
|
const [t] = useI18n();
|
||||||
const [state, setState] = createSignal('stopped');
|
const [state, setState] = createSignal('stopped');
|
||||||
const [open, setOpen] = createSignal(false);
|
const [open, setOpen] = createSignal(false);
|
||||||
|
const [watcherId, setWatcherId] = createSignal();
|
||||||
const [gpxId, setGpxId] = createSignal<string>('');
|
const [gpxId, setGpxId] = createSignal<string>('');
|
||||||
const [trkId, setTrkId] = createSignal<string>('');
|
const [trksegId, setTrksegId] = createSignal<IdTrkseg>();
|
||||||
|
|
||||||
|
const newLocationHandler = async (location: any) => {
|
||||||
|
console.log(
|
||||||
|
`Location filtering, elapsed time: ${
|
||||||
|
location.time - globalThis.$lastValidLocationTime
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
if (location.time - globalThis.$lastValidLocationTime > 15000) {
|
||||||
|
globalThis.$lastValidLocationTime = location.time;
|
||||||
|
await dispatch({
|
||||||
|
action: 'appendTrkpt',
|
||||||
|
params: {
|
||||||
|
trksegIdObj: trksegId(),
|
||||||
|
trkpt: {
|
||||||
|
$: {
|
||||||
|
lat: location.latitude,
|
||||||
|
lon: location.longitude,
|
||||||
|
},
|
||||||
|
ele: location.altitude,
|
||||||
|
time: new Date(location.time).toISOString(),
|
||||||
|
extensions: {
|
||||||
|
speed: location.speed,
|
||||||
|
accuracy: location.accuracy,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
const handleClickOpen = () => {
|
||||||
if (state() === 'stopped') {
|
if (state() === 'stopped') {
|
||||||
|
@ -29,22 +70,37 @@ const GpxRecord: Component<{}> = (props) => {
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
const startRecordingHandler = () => {
|
const startRecordingHandler = async () => {
|
||||||
|
const trkId = await dispatch({
|
||||||
|
action: 'appendTrk',
|
||||||
|
params: { gpxId: gpxId(), trk: emptyTrk },
|
||||||
|
});
|
||||||
|
const trkIdObj = getUri('trk', trkId);
|
||||||
|
setTrksegId({ ...trkIdObj, trkseg: 0 });
|
||||||
|
globalThis.$lastValidLocationTime = 0;
|
||||||
|
const id = await startBackgroundGeolocation(newLocationHandler, 20);
|
||||||
|
setWatcherId(id);
|
||||||
setState('recording');
|
setState('recording');
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
const pauseRecordingHandler = () => {
|
const pauseRecordingHandler = () => {
|
||||||
|
stopBackgroundGeolocation(watcherId());
|
||||||
setState('paused');
|
setState('paused');
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
const resumeRecordingHandler = () => {
|
const resumeRecordingHandler = async () => {
|
||||||
|
setTrksegId({ ...trksegId()!, trkseg: trksegId()!.trkseg + 1 });
|
||||||
|
const id = await startBackgroundGeolocation(newLocationHandler, 20);
|
||||||
|
setWatcherId(id);
|
||||||
setState('recording');
|
setState('recording');
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
const stopRecordingHandler = () => {
|
const stopRecordingHandler = () => {
|
||||||
|
stopBackgroundGeolocation(watcherId());
|
||||||
setState('stopped');
|
setState('stopped');
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div class={style.control}>
|
<div class={style.control}>
|
||||||
|
@ -81,6 +137,7 @@ const GpxRecord: Component<{}> = (props) => {
|
||||||
color='primary'
|
color='primary'
|
||||||
variant='contained'
|
variant='contained'
|
||||||
onClick={startRecordingHandler}
|
onClick={startRecordingHandler}
|
||||||
|
disabled={gpxId() === 'new'}
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
margin: '10px',
|
margin: '10px',
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { slice } from 'lodash';
|
||||||
import { getWpt } from '../db/wpt';
|
import { getWpt } from '../db/wpt';
|
||||||
import { returnAgain } from '../workers/dispatcher-worker';
|
import { returnAgain } from '../workers/dispatcher-worker';
|
||||||
import { getAllGpxes, getGpx } from './gpx';
|
import { getAllGpxes, getGpx } from './gpx';
|
||||||
|
@ -31,14 +32,24 @@ const sendUpdate = async (params: any) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeHandler = async (change: any) => {
|
const changeHandler = async (change: any) => {
|
||||||
console.log({ caller: 'ChangeHandler', change });
|
console.log({ caller: 'ChangeHandler', change, watches: globalThis.watches });
|
||||||
const { id } = change;
|
const { id } = change;
|
||||||
if (!globalThis.watches) {
|
if (!globalThis.watches) {
|
||||||
globalThis.watches = new Map();
|
globalThis.watches = new Map();
|
||||||
}
|
}
|
||||||
sendUpdate(globalThis.watches.get(id));
|
sendUpdate(globalThis.watches.get(id));
|
||||||
|
|
||||||
const parentId = id.substring(0, id.lastIndexOf('/'));
|
const tokens = id.split('/').slice(0, -1);
|
||||||
|
if (['1wpt', '2rte', '3trk'].includes(tokens.at(-1))) {
|
||||||
|
tokens.splice(-1);
|
||||||
|
}
|
||||||
|
const parentId = tokens.join('/');
|
||||||
|
console.log({
|
||||||
|
caller: 'ChangeHandler',
|
||||||
|
change,
|
||||||
|
watches: globalThis.watches,
|
||||||
|
parentId,
|
||||||
|
});
|
||||||
|
|
||||||
if (parentId === 'gpx') {
|
if (parentId === 'gpx') {
|
||||||
const gpxes = await getAllGpxes();
|
const gpxes = await getAllGpxes();
|
||||||
|
@ -64,12 +75,23 @@ const changeHandler = async (change: any) => {
|
||||||
export default changeHandler;
|
export default changeHandler;
|
||||||
|
|
||||||
export const getAndWatch = async (params: any) => {
|
export const getAndWatch = async (params: any) => {
|
||||||
|
console.log({
|
||||||
|
caller: 'ChangeHandler / getAndWatch',
|
||||||
|
params,
|
||||||
|
watches: globalThis.watches,
|
||||||
|
});
|
||||||
const { method, _dispatchId, id, ...otherParams } = params;
|
const { method, _dispatchId, id, ...otherParams } = params;
|
||||||
if (!globalThis.watches) {
|
if (!globalThis.watches) {
|
||||||
globalThis.watches = new Map();
|
globalThis.watches = new Map();
|
||||||
}
|
}
|
||||||
globalThis.watches.set(id, params);
|
globalThis.watches.set(id, params);
|
||||||
const returnValue = await methods[<keyof typeof methods>method](params);
|
const returnValue = await methods[<keyof typeof methods>method](params);
|
||||||
|
console.log({
|
||||||
|
caller: 'ChangeHandler / getAndWatch',
|
||||||
|
params,
|
||||||
|
returnValue,
|
||||||
|
watches: globalThis.watches,
|
||||||
|
});
|
||||||
return returnValue;
|
return returnValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -392,6 +392,31 @@ export const getGpx = async (params: any) => {
|
||||||
return gpx;
|
return gpx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getNextGpxTrkId = async (gpxId: string) => {
|
||||||
|
const docs = await getFamily(gpxId, { include_docs: true });
|
||||||
|
const gpxIdObj = getUri('gpx', gpxId);
|
||||||
|
console.log({ caller: 'getNextGpxTrkId', gpxId, gpxIdObj, docs });
|
||||||
|
let trkId = '';
|
||||||
|
docs.rows.forEach((row: any) => {
|
||||||
|
if (row.doc.type === 'trk') {
|
||||||
|
trkId = row.doc._id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (trkId !== '') {
|
||||||
|
const trkIdObj = getUri('trk', trkId);
|
||||||
|
return getUri('trk', { gpx: gpxIdObj.gpx, trk: trkIdObj.trk + 1 });
|
||||||
|
}
|
||||||
|
return getUri('trk', { gpx: gpxIdObj.gpx, trk: 0 });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const appendTrk = async (params: any) => {
|
||||||
|
const { gpxId, trk } = params;
|
||||||
|
const trkId = await getNextGpxTrkId(gpxId);
|
||||||
|
console.log({ caller: 'appendTrk', gpxId, trk, trkId });
|
||||||
|
await put(trkId, 'trk', (doc) => trk, {});
|
||||||
|
return trkId;
|
||||||
|
};
|
||||||
|
|
||||||
export const putGpx = async (params: any) => {
|
export const putGpx = async (params: any) => {
|
||||||
let { id, gpx } = params;
|
let { id, gpx } = params;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ export const put = async (
|
||||||
update: (doc: any) => any,
|
update: (doc: any) => any,
|
||||||
defaultDoc: any
|
defaultDoc: any
|
||||||
) => {
|
) => {
|
||||||
var current;
|
let current;
|
||||||
try {
|
try {
|
||||||
current = await db.get(_id);
|
current = await db.get(_id);
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import getUri from '../lib/ids';
|
import getUri, { intToTrkptId } from '../lib/ids';
|
||||||
import { appendToArray } from './gpx';
|
import { appendToArray } from './gpx';
|
||||||
import { getFamily, put } from './lib';
|
import { getFamily, put } from './lib';
|
||||||
import { putNewTrk } from './trk';
|
import { putNewTrk } from './trk';
|
||||||
|
import { emptyWpt } from './wpt';
|
||||||
|
|
||||||
const emptyTrkseg: Trkseg = {
|
const emptyTrkseg: Trkseg = {
|
||||||
trkpt: undefined,
|
trkpt: undefined,
|
||||||
|
@ -46,3 +47,30 @@ export const getTrkseg = async (params: any) => {
|
||||||
});
|
});
|
||||||
return trkseg;
|
return trkseg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const putTrkseg = async (trkSegId: string, trkSeg = emptyTrkseg) => {
|
||||||
|
await put(
|
||||||
|
trkSegId,
|
||||||
|
'trkseg',
|
||||||
|
(doc) => {
|
||||||
|
return trkSeg;
|
||||||
|
},
|
||||||
|
emptyTrkseg
|
||||||
|
);
|
||||||
|
return trkSegId;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const appendTrkpt = async (params: any) => {
|
||||||
|
const { trksegIdObj, trkpt } = params;
|
||||||
|
const idTrkseg = getUri('trkseg', trksegIdObj);
|
||||||
|
try {
|
||||||
|
await db.get(idTrkseg);
|
||||||
|
} catch {
|
||||||
|
await put(idTrkseg, 'trkseg', (doc) => doc, emptyTrkseg);
|
||||||
|
}
|
||||||
|
const trkptIdPart = intToTrkptId(new Date(trkpt.time as string).valueOf());
|
||||||
|
const trkptIdObj = { ...trksegIdObj, trkpt: trkptIdPart };
|
||||||
|
const trkptId = getUri('trkpt', trkptIdObj);
|
||||||
|
await put(trkptId, 'trkpt', (doc) => trkpt, emptyWpt);
|
||||||
|
return trkptId;
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
import { BackgroundGeolocationPlugin } from '@capacitor-community/background-geolocation';
|
||||||
|
import { registerPlugin } from '@capacitor/core';
|
||||||
|
|
||||||
|
const BackgroundGeolocation = registerPlugin<BackgroundGeolocationPlugin>(
|
||||||
|
'BackgroundGeolocation'
|
||||||
|
);
|
||||||
|
|
||||||
|
const backgroundGeolocationConfig = {
|
||||||
|
// If the "backgroundMessage" option is defined, the watcher will
|
||||||
|
// provide location updates whether the app is in the background or the
|
||||||
|
// foreground. If it is not defined, location updates are only
|
||||||
|
// guaranteed in the foreground. This is true on both platforms.
|
||||||
|
// On Android, a notification must be shown to continue receiving
|
||||||
|
// location updates in the background. This option specifies the text of
|
||||||
|
// that notification.
|
||||||
|
backgroundMessage: 'Cancel to prevent battery drain.',
|
||||||
|
|
||||||
|
// The title of the notification mentioned above. Defaults to "Using
|
||||||
|
// your location".
|
||||||
|
backgroundTitle: 'Tracking You.',
|
||||||
|
|
||||||
|
// Whether permissions should be requested from the user automatically,
|
||||||
|
// if they are not already granted. Defaults to "true".
|
||||||
|
requestPermissions: true,
|
||||||
|
|
||||||
|
// If "true", stale locations may be delivered while the device
|
||||||
|
// obtains a GPS fix. You are responsible for checking the "time"
|
||||||
|
// property. If "false", locations are guaranteed to be up to date.
|
||||||
|
// Defaults to "false".
|
||||||
|
stale: false,
|
||||||
|
|
||||||
|
// The minimum number of metres between subsequent locations. Defaults
|
||||||
|
// to 0.
|
||||||
|
distanceFilter: 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const startBackgroundGeolocation = async (
|
||||||
|
newLocationHandler: any,
|
||||||
|
distanceFilter: number
|
||||||
|
) => {
|
||||||
|
backgroundGeolocationConfig.distanceFilter = distanceFilter;
|
||||||
|
const locationHandler = (location: any, error: any) => {
|
||||||
|
console.log('com.dyomedea.dyomedea LOG', ' - Callback');
|
||||||
|
if (error) {
|
||||||
|
if (error.code === 'NOT_AUTHORIZED') {
|
||||||
|
if (
|
||||||
|
window.confirm(
|
||||||
|
'This app needs your location, ' +
|
||||||
|
'but does not have permission.\n\n' +
|
||||||
|
'Open settings now?'
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// It can be useful to direct the user to their device's
|
||||||
|
// settings when location permissions have been denied. The
|
||||||
|
// plugin provides the 'openSettings' method to do exactly
|
||||||
|
// this.
|
||||||
|
BackgroundGeolocation.openSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return console.error('com.dyomedea.dyomedea LOG', ' - error: ', error);
|
||||||
|
}
|
||||||
|
console.log(location);
|
||||||
|
if (location !== undefined) {
|
||||||
|
newLocationHandler(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
return console.log('com.dyomedea.dyomedea LOG', ' - location: ', location);
|
||||||
|
};
|
||||||
|
|
||||||
|
var watcher_id;
|
||||||
|
|
||||||
|
console.log('com.dyomedea.dyomedea LOG', ' - Adding the watcher');
|
||||||
|
await BackgroundGeolocation.addWatcher(
|
||||||
|
backgroundGeolocationConfig,
|
||||||
|
locationHandler
|
||||||
|
)
|
||||||
|
.then(function after_the_watcher_has_been_added(id) {
|
||||||
|
// When a watcher is no longer needed, it should be removed by calling
|
||||||
|
// 'removeWatcher' with an object containing its ID.
|
||||||
|
console.log('com.dyomedea.dyomedea LOG', ' - Watcher added');
|
||||||
|
watcher_id = id;
|
||||||
|
/*BackgroundGeolocation.removeWatcher({
|
||||||
|
id: watcher_id,
|
||||||
|
}); */
|
||||||
|
})
|
||||||
|
.catch((reason) => {
|
||||||
|
console.error('com.dyomedea.dyomedea LOG', ' - reason: ', reason);
|
||||||
|
});
|
||||||
|
return watcher_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const stopBackgroundGeolocation = (watcher_id: any) => {
|
||||||
|
BackgroundGeolocation.removeWatcher({
|
||||||
|
id: watcher_id,
|
||||||
|
});
|
||||||
|
};
|
|
@ -7,11 +7,12 @@ import {
|
||||||
pruneAndSaveImportedGpx,
|
pruneAndSaveImportedGpx,
|
||||||
getGpx,
|
getGpx,
|
||||||
putGpx,
|
putGpx,
|
||||||
|
appendTrk,
|
||||||
getAllGpxes,
|
getAllGpxes,
|
||||||
getAllGpxesWithSummary,
|
getAllGpxesWithSummary,
|
||||||
} from '../db/gpx';
|
} from '../db/gpx';
|
||||||
import { getTrk, putNewTrk } from '../db/trk';
|
import { getTrk, putNewTrk } from '../db/trk';
|
||||||
import { getTrkseg } from '../db/trkseg';
|
import { getTrkseg, appendTrkpt } from '../db/trkseg';
|
||||||
import { getWpt, putWpt } from '../db/wpt';
|
import { getWpt, putWpt } from '../db/wpt';
|
||||||
|
|
||||||
//const self = globalThis as unknown as WorkerGlobalScope;
|
//const self = globalThis as unknown as WorkerGlobalScope;
|
||||||
|
@ -29,8 +30,10 @@ onmessage = async function (e) {
|
||||||
getAllGpxesWithSummary,
|
getAllGpxesWithSummary,
|
||||||
getGpx,
|
getGpx,
|
||||||
putGpx,
|
putGpx,
|
||||||
|
appendTrk,
|
||||||
getTrk,
|
getTrk,
|
||||||
getTrkseg,
|
getTrkseg,
|
||||||
|
appendTrkpt,
|
||||||
getWpt,
|
getWpt,
|
||||||
|
|
||||||
putWpt,
|
putWpt,
|
||||||
|
|
Loading…
Reference in New Issue