(Re)Adding background geolocation.
This commit is contained in:
parent
4dbdcecadd
commit
acc1d7cc1e
|
@ -11,6 +11,7 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@awesome-cordova-plugins/geolocation": "^6.2.0",
|
||||
"@capacitor-community/background-geolocation": "^1.2.6",
|
||||
"@capacitor/android": "^3.4.3",
|
||||
"@capacitor/core": "^3.4.3",
|
||||
"@capacitor/ios": "^3.4.3",
|
||||
|
@ -550,6 +551,14 @@
|
|||
"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": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/android/-/android-3.9.0.tgz",
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@awesome-cordova-plugins/geolocation": "^6.2.0",
|
||||
"@capacitor-community/background-geolocation": "^1.2.6",
|
||||
"@capacitor/android": "^3.4.3",
|
||||
"@capacitor/core": "^3.4.3",
|
||||
"@capacitor/ios": "^3.4.3",
|
||||
|
|
|
@ -12,13 +12,54 @@ import Dialog from '../dialog';
|
|||
import { useI18n } from '@solid-primitives/i18n';
|
||||
import GpxChooser from '../gpx-chooser';
|
||||
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 [t] = useI18n();
|
||||
const [state, setState] = createSignal('stopped');
|
||||
const [open, setOpen] = createSignal(false);
|
||||
const [watcherId, setWatcherId] = createSignal();
|
||||
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 = () => {
|
||||
if (state() === 'stopped') {
|
||||
|
@ -29,22 +70,37 @@ const GpxRecord: Component<{}> = (props) => {
|
|||
const handleClose = () => {
|
||||
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');
|
||||
setOpen(false);
|
||||
};
|
||||
const pauseRecordingHandler = () => {
|
||||
stopBackgroundGeolocation(watcherId());
|
||||
setState('paused');
|
||||
setOpen(false);
|
||||
};
|
||||
const resumeRecordingHandler = () => {
|
||||
const resumeRecordingHandler = async () => {
|
||||
setTrksegId({ ...trksegId()!, trkseg: trksegId()!.trkseg + 1 });
|
||||
const id = await startBackgroundGeolocation(newLocationHandler, 20);
|
||||
setWatcherId(id);
|
||||
setState('recording');
|
||||
setOpen(false);
|
||||
};
|
||||
const stopRecordingHandler = () => {
|
||||
stopBackgroundGeolocation(watcherId());
|
||||
setState('stopped');
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class={style.control}>
|
||||
|
@ -81,6 +137,7 @@ const GpxRecord: Component<{}> = (props) => {
|
|||
color='primary'
|
||||
variant='contained'
|
||||
onClick={startRecordingHandler}
|
||||
disabled={gpxId() === 'new'}
|
||||
sx={{
|
||||
width: '100%',
|
||||
margin: '10px',
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { slice } from 'lodash';
|
||||
import { getWpt } from '../db/wpt';
|
||||
import { returnAgain } from '../workers/dispatcher-worker';
|
||||
import { getAllGpxes, getGpx } from './gpx';
|
||||
|
@ -31,14 +32,24 @@ const sendUpdate = async (params: any) => {
|
|||
};
|
||||
|
||||
const changeHandler = async (change: any) => {
|
||||
console.log({ caller: 'ChangeHandler', change });
|
||||
console.log({ caller: 'ChangeHandler', change, watches: globalThis.watches });
|
||||
const { id } = change;
|
||||
if (!globalThis.watches) {
|
||||
globalThis.watches = new Map();
|
||||
}
|
||||
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') {
|
||||
const gpxes = await getAllGpxes();
|
||||
|
@ -64,12 +75,23 @@ const changeHandler = async (change: any) => {
|
|||
export default changeHandler;
|
||||
|
||||
export const getAndWatch = async (params: any) => {
|
||||
console.log({
|
||||
caller: 'ChangeHandler / getAndWatch',
|
||||
params,
|
||||
watches: globalThis.watches,
|
||||
});
|
||||
const { method, _dispatchId, id, ...otherParams } = params;
|
||||
if (!globalThis.watches) {
|
||||
globalThis.watches = new Map();
|
||||
}
|
||||
globalThis.watches.set(id, params);
|
||||
const returnValue = await methods[<keyof typeof methods>method](params);
|
||||
console.log({
|
||||
caller: 'ChangeHandler / getAndWatch',
|
||||
params,
|
||||
returnValue,
|
||||
watches: globalThis.watches,
|
||||
});
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
|
|
|
@ -392,6 +392,31 @@ export const getGpx = async (params: any) => {
|
|||
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) => {
|
||||
let { id, gpx } = params;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ export const put = async (
|
|||
update: (doc: any) => any,
|
||||
defaultDoc: any
|
||||
) => {
|
||||
var current;
|
||||
let current;
|
||||
try {
|
||||
current = await db.get(_id);
|
||||
} catch {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import getUri from '../lib/ids';
|
||||
import getUri, { intToTrkptId } from '../lib/ids';
|
||||
import { appendToArray } from './gpx';
|
||||
import { getFamily, put } from './lib';
|
||||
import { putNewTrk } from './trk';
|
||||
import { emptyWpt } from './wpt';
|
||||
|
||||
const emptyTrkseg: Trkseg = {
|
||||
trkpt: undefined,
|
||||
|
@ -46,3 +47,30 @@ export const getTrkseg = async (params: any) => {
|
|||
});
|
||||
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,
|
||||
getGpx,
|
||||
putGpx,
|
||||
appendTrk,
|
||||
getAllGpxes,
|
||||
getAllGpxesWithSummary,
|
||||
} from '../db/gpx';
|
||||
import { getTrk, putNewTrk } from '../db/trk';
|
||||
import { getTrkseg } from '../db/trkseg';
|
||||
import { getTrkseg, appendTrkpt } from '../db/trkseg';
|
||||
import { getWpt, putWpt } from '../db/wpt';
|
||||
|
||||
//const self = globalThis as unknown as WorkerGlobalScope;
|
||||
|
@ -29,8 +30,10 @@ onmessage = async function (e) {
|
|||
getAllGpxesWithSummary,
|
||||
getGpx,
|
||||
putGpx,
|
||||
appendTrk,
|
||||
getTrk,
|
||||
getTrkseg,
|
||||
appendTrkpt,
|
||||
getWpt,
|
||||
|
||||
putWpt,
|
||||
|
|
Loading…
Reference in New Issue