Android: setup data bridge, polyfill geolocation
This commit is contained in:
parent
17450deb82
commit
76e9381650
23 changed files with 187 additions and 106 deletions
12
Docs/Android_development.md
Normal file
12
Docs/Android_development.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Creating an APK from the code
|
||||
|
||||
We are using capacitor. This is a tool which packages some files into an Android shell.
|
||||
|
||||
## Developing
|
||||
|
||||
1. Build all the necessary files.
|
||||
a. If no layer/theme changes were made, `npm run build` is sufficient
|
||||
b. Otherwise, run `npm run prepare-deploy`.
|
||||
2. All the web assets will now be in `dist/`
|
||||
3. Run `scripts/prepareAndroid.sh`
|
||||
4. Switch to Android Studio, open the subproject "Android" in it
|
|
@ -3238,7 +3238,7 @@
|
|||
},
|
||||
{
|
||||
"id": "name",
|
||||
"question":{
|
||||
"question": {
|
||||
"en": "What is the name of this place?"
|
||||
},
|
||||
"render": {
|
||||
|
|
|
@ -460,7 +460,8 @@
|
|||
"activateButton": "Hjælp med at oversætte MapComplete",
|
||||
"missing": "{count} uoversatte strenge"
|
||||
},
|
||||
"userinfo": {},
|
||||
"userinfo": {
|
||||
},
|
||||
"validation": {
|
||||
"color": {
|
||||
"description": "En farve eller hex-kode"
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
{}
|
||||
{
|
||||
}
|
|
@ -94,7 +94,8 @@
|
|||
"question_opinion": "Kamusta ang iyong karanasan?",
|
||||
"reviewPlaceholder": "Ilarawan ang iyong karanasan…"
|
||||
},
|
||||
"translations": {},
|
||||
"translations": {
|
||||
},
|
||||
"unknown": {
|
||||
"clear": "Tanggalin ang sagot"
|
||||
},
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
{}
|
||||
{
|
||||
}
|
|
@ -150,7 +150,8 @@
|
|||
"split": {
|
||||
"cancel": "Batal"
|
||||
},
|
||||
"translations": {},
|
||||
"translations": {
|
||||
},
|
||||
"validation": {
|
||||
"date": {
|
||||
"description": "Tanggal, dimulai dari tahun"
|
||||
|
|
|
@ -959,6 +959,30 @@
|
|||
"render": "BBQ"
|
||||
}
|
||||
},
|
||||
"beehive": {
|
||||
"description": "Layer showing beehives",
|
||||
"name": "Beehives",
|
||||
"presets": {
|
||||
"0": {
|
||||
"title": "a beehive"
|
||||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"capacity": {
|
||||
"freeform": {
|
||||
"placeholder": "Number of beehives"
|
||||
},
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "There is 1 beehive"
|
||||
}
|
||||
},
|
||||
"question": "How many beehives are there?",
|
||||
"render": "There are {capacity} beehives"
|
||||
}
|
||||
},
|
||||
"title": "Beehive"
|
||||
},
|
||||
"bench": {
|
||||
"description": "A bench is a wooden, metal, stone, … surface where a human can sit. This layers visualises them and asks a few questions about them.",
|
||||
"filter": {
|
||||
|
@ -6358,6 +6382,16 @@
|
|||
"render": "Information board"
|
||||
}
|
||||
},
|
||||
"insect_hotel": {
|
||||
"description": "Layer showing insect hotels",
|
||||
"name": "Insect Hotels",
|
||||
"presets": {
|
||||
"0": {
|
||||
"title": "an insect hotel"
|
||||
}
|
||||
},
|
||||
"title": "Insect Hotel"
|
||||
},
|
||||
"item_with_image": {
|
||||
"name": "Items with at least one image",
|
||||
"title": {
|
||||
|
@ -8682,6 +8716,9 @@
|
|||
"render": "This elevator goes to floors {level}"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"question": "What is the name of this place?"
|
||||
},
|
||||
"nothing_known": {
|
||||
"render": {
|
||||
"special": {
|
||||
|
|
|
@ -5322,6 +5322,16 @@
|
|||
"render": "Informatiebord"
|
||||
}
|
||||
},
|
||||
"insect_hotel": {
|
||||
"description": "Laag met insectenhotels",
|
||||
"name": "Insectenhotels",
|
||||
"presets": {
|
||||
"0": {
|
||||
"title": "een insectenhotel"
|
||||
}
|
||||
},
|
||||
"title": "Insectenhotel"
|
||||
},
|
||||
"kerbs": {
|
||||
"description": "Een laag met stoepranden.",
|
||||
"filter": {
|
||||
|
|
|
@ -274,7 +274,8 @@
|
|||
"importInspector": {
|
||||
"title": "Inspiser og håndter importnotater"
|
||||
},
|
||||
"importLayer": {},
|
||||
"importLayer": {
|
||||
},
|
||||
"index": {
|
||||
"intro": "MapComplete er en OpenStreetMap-viser og redigerer, som viser deg info om funksjoner for et gitt tema og tillater oppdatering av det.",
|
||||
"logIn": "Logg inn for å vise tema du har besøkt tidligere",
|
||||
|
@ -369,7 +370,8 @@
|
|||
"activateButton": "Bistå oversettelsen av MapComplete",
|
||||
"missing": "{count} uoversatte strenger"
|
||||
},
|
||||
"userinfo": {},
|
||||
"userinfo": {
|
||||
},
|
||||
"validation": {
|
||||
"color": {
|
||||
"description": "En farge eller heksadesimal kode"
|
||||
|
|
|
@ -53,7 +53,8 @@
|
|||
"search": "ستھتیاں وچ کھوجو",
|
||||
"searching": "کھوجیا جا رہا اے۔ ۔ ۔"
|
||||
},
|
||||
"sharescreen": {},
|
||||
"sharescreen": {
|
||||
},
|
||||
"weekdays": {
|
||||
"abbreviations": {
|
||||
"friday": "جـ",
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
{}
|
||||
{
|
||||
}
|
|
@ -488,6 +488,11 @@
|
|||
"override": {
|
||||
"=name": "Sport places without etymology information"
|
||||
}
|
||||
},
|
||||
"8": {
|
||||
"override": {
|
||||
"=name": "Parks without etymology information"
|
||||
}
|
||||
}
|
||||
},
|
||||
"shortDescription": "What is the origin of a toponym?",
|
||||
|
@ -721,6 +726,10 @@
|
|||
"description": "On this map, publicly accessible indoor places are shown",
|
||||
"title": "Indoors"
|
||||
},
|
||||
"insects": {
|
||||
"description": "Insect hotels provide shelter for insects.",
|
||||
"title": "Insect Hotels"
|
||||
},
|
||||
"items_with_image": {
|
||||
"description": "A map showing all items on OSM which have an image. This theme is a very bad fit for MapComplete as someone is not able to directly add a picture. However, this theme is mostly here to include this all into the database, which'll allow this to quickly fetch images nearby for other features",
|
||||
"title": "All items with images"
|
||||
|
|
|
@ -777,6 +777,10 @@
|
|||
"description": "Op deze kaart worden publiek toegankelijke binnenruimtes getoond",
|
||||
"title": "Binnenruimtes"
|
||||
},
|
||||
"insects": {
|
||||
"description": "Insectenhotels bieden onderdak aan insecten.",
|
||||
"title": "Insectenhotels"
|
||||
},
|
||||
"items_with_image": {
|
||||
"description": "Een kaart die alle items op OSM toont die een afbeelding hebben. Dit thema past heel slecht bij MapComplete omdat het niet mogelijk is een afbeelding toe te voegen. Dit thema is er vooral om alles in de database op te nemen, waardoor het snel afbeeldingen in de buurt kan ophalen voor andere functies",
|
||||
"title": "Alle items met afbeeldingen"
|
||||
|
|
|
@ -532,7 +532,8 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"importLayer": {},
|
||||
"importLayer": {
|
||||
},
|
||||
"index": {
|
||||
"about": "Про MapComplete",
|
||||
"intro": "Тематичні мапи, до створення яких ви можете долучитися",
|
||||
|
@ -591,7 +592,8 @@
|
|||
"removedKeys": "Наступні ключі будуть видалені:",
|
||||
"title": "Позначити як невідомий?"
|
||||
},
|
||||
"userinfo": {},
|
||||
"userinfo": {
|
||||
},
|
||||
"validation": {
|
||||
"opening_hours": {
|
||||
"description": "Години роботи"
|
||||
|
|
15
package-lock.json
generated
15
package-lock.json
generated
|
@ -12,7 +12,6 @@
|
|||
"@capacitor/android": "^6.1.2",
|
||||
"@capacitor/assets": "^3.0.5",
|
||||
"@capacitor/core": "^6.1.2",
|
||||
"@capacitor/geolocation": "^6.0.1",
|
||||
"@comunica/core": "^3.0.1",
|
||||
"@comunica/query-sparql": "^3.0.1",
|
||||
"@comunica/query-sparql-link-traversal": "^0.3.0",
|
||||
|
@ -2041,14 +2040,6 @@
|
|||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@capacitor/geolocation": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/geolocation/-/geolocation-6.0.1.tgz",
|
||||
"integrity": "sha512-QOkIrSzG6E0vD2MF3gZmtuILQiuVro4LGPjqrUjCzhX10zl/4lx6bq4T+hj2YLUmMUnCiV1hWTOJHcpdVRMz7w==",
|
||||
"peerDependencies": {
|
||||
"@capacitor/core": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@colors/colors": {
|
||||
"version": "1.6.0",
|
||||
"license": "MIT",
|
||||
|
@ -24098,12 +24089,6 @@
|
|||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"@capacitor/geolocation": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/geolocation/-/geolocation-6.0.1.tgz",
|
||||
"integrity": "sha512-QOkIrSzG6E0vD2MF3gZmtuILQiuVro4LGPjqrUjCzhX10zl/4lx6bq4T+hj2YLUmMUnCiV1hWTOJHcpdVRMz7w==",
|
||||
"requires": {}
|
||||
},
|
||||
"@colors/colors": {
|
||||
"version": "1.6.0"
|
||||
},
|
||||
|
|
|
@ -145,7 +145,10 @@
|
|||
"generate:summaryCache": "vite-node scripts/generateSummaryTileCache.ts",
|
||||
"create:database": "vite-node scripts/osm2pgsql/createNewDatabase.ts",
|
||||
"delete:database:old": "vite-node scripts/osm2pgsql/deleteOldDbs.ts",
|
||||
"upload:panoramax": "vite-node scripts/ImgurToPanoramax.ts # && josm imgur_to_panoramax.osc"
|
||||
"upload:panoramax": "vite-node scripts/ImgurToPanoramax.ts # && josm imgur_to_panoramax.osc",
|
||||
|
||||
"#": "Android development"
|
||||
|
||||
},
|
||||
"keywords": [
|
||||
"OpenStreetMap",
|
||||
|
@ -163,7 +166,6 @@
|
|||
"@capacitor/android": "^6.1.2",
|
||||
"@capacitor/assets": "^3.0.5",
|
||||
"@capacitor/core": "^6.1.2",
|
||||
"@capacitor/geolocation": "^6.0.1",
|
||||
"@comunica/core": "^3.0.1",
|
||||
"@comunica/query-sparql": "^3.0.1",
|
||||
"@comunica/query-sparql-link-traversal": "^0.3.0",
|
||||
|
|
|
@ -3,7 +3,6 @@ import { LocalStorageSource } from "../Web/LocalStorageSource"
|
|||
import { QueryParameters } from "../Web/QueryParameters"
|
||||
import { Translation } from "../../UI/i18n/Translation"
|
||||
import Translations from "../../UI/i18n/Translations"
|
||||
import { Geolocation } from "@capacitor/geolocation"
|
||||
|
||||
export type GeolocationPermissionState = "prompt" | "requested" | "granted" | "denied"
|
||||
|
||||
|
@ -179,20 +178,19 @@ export class GeoLocationState {
|
|||
this.permission.setData("requested")
|
||||
try {
|
||||
const status = await navigator?.permissions?.query({ name: "geolocation" })
|
||||
const self = this
|
||||
console.log("Got geolocation state", status.state)
|
||||
if (status.state === "granted" || status.state === "denied") {
|
||||
self.permission.setData(status.state)
|
||||
self.startWatching()
|
||||
this.permission.setData(status.state)
|
||||
this.startWatching()
|
||||
return
|
||||
}
|
||||
status.addEventListener("change", () => {
|
||||
self.permission.setData(status.state)
|
||||
this.permission.setData(status.state)
|
||||
})
|
||||
// The code above might have reset it to 'prompt', but we _did_ request permission!
|
||||
this.permission.setData("requested")
|
||||
// We _must_ call 'startWatching', as that is the actual trigger for the popup...
|
||||
self.startWatching()
|
||||
this.startWatching()
|
||||
} catch (e) {
|
||||
console.error("Could not get permission:", e)
|
||||
}
|
||||
|
@ -203,46 +201,29 @@ export class GeoLocationState {
|
|||
* @private
|
||||
*/
|
||||
private async startWatching() {
|
||||
console.log("Starts watching", navigator.geolocation, Geolocation)
|
||||
const self = this
|
||||
try {
|
||||
await Geolocation.requestPermissions({ permissions: ["location"] })
|
||||
console.log("Requested permission")
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
try {
|
||||
await Geolocation.watchPosition(
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
maximumAge: 120000,
|
||||
},
|
||||
(position: GeolocationPosition, error: GeolocationPositionError) => {
|
||||
if (error) {
|
||||
if (error.code === 2 || error.code === 3) {
|
||||
self._gpsAvailable.set(false)
|
||||
return
|
||||
}
|
||||
self._gpsAvailable.set(true) // We go back to the default assumption that the location is physically available
|
||||
if (error.code === 1) {
|
||||
self.permission.set("denied")
|
||||
self._grantedThisSession.setData(false)
|
||||
return
|
||||
}
|
||||
console.warn("Could not get location with navigator.geolocation due to", error)
|
||||
}
|
||||
|
||||
|
||||
console.log("Got position:", position, JSON.stringify(position))
|
||||
if (!position) {
|
||||
return
|
||||
}
|
||||
this._gpsAvailable.set(true)
|
||||
this.currentGPSLocation.setData(position.coords)
|
||||
this._previousLocationGrant.setData(true)
|
||||
})
|
||||
} catch (e) {
|
||||
console.error("Could not get geolocation due to", e)
|
||||
}
|
||||
navigator.geolocation.watchPosition(
|
||||
(position: GeolocationPosition) => {
|
||||
this._gpsAvailable.set(true)
|
||||
this.currentGPSLocation.setData(position.coords)
|
||||
this._previousLocationGrant.setData(true)
|
||||
},
|
||||
(e: GeolocationPositionError) => {
|
||||
if (e.code === 2 || e.code === 3) {
|
||||
this._gpsAvailable.set(false)
|
||||
return
|
||||
}
|
||||
this._gpsAvailable.set(true) // We go back to the default assumption that the location is physically available
|
||||
if (e.code === 1) {
|
||||
this.permission.set("denied")
|
||||
this._grantedThisSession.setData(false)
|
||||
return
|
||||
}
|
||||
console.warn("Could not get location with navigator.geolocation due to", e)
|
||||
},
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,36 +3,62 @@
|
|||
* If this is successful, it will patch some webAPIs
|
||||
*/
|
||||
import { registerPlugin } from "@capacitor/core"
|
||||
|
||||
export class AndroidPolyfill {
|
||||
private readonly databridgePlugin: DatabridgePlugin
|
||||
|
||||
constructor() {
|
||||
this.databridgePlugin = registerPlugin<DatabridgePlugin>("Databridge", {
|
||||
web: () => {
|
||||
return <DatabridgePlugin>{
|
||||
async request(options: { key: string }): Promise<{ value: string }> {
|
||||
return { value: "web" }
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
public async init(){
|
||||
const shell = await this.databridgePlugin.request({ key: "meta" })
|
||||
if(shell.value === "web"){
|
||||
console.log("Not initing Android polyfill; web detected")
|
||||
return
|
||||
}
|
||||
console.log("Detected shell:", shell.value)
|
||||
}
|
||||
|
||||
}
|
||||
import { UIEventSource } from "../UIEventSource"
|
||||
|
||||
export interface DatabridgePlugin {
|
||||
request(options: { key: string }): Promise<{ value: string }>;
|
||||
}
|
||||
|
||||
new AndroidPolyfill().init()
|
||||
const DatabridgePluginSingleton = registerPlugin<DatabridgePlugin>("Databridge", {
|
||||
web: () => {
|
||||
return <DatabridgePlugin>{
|
||||
async request(options: { key: string }): Promise<{ value: string }> {
|
||||
return { value: "web" }
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export class AndroidPolyfill {
|
||||
private readonly databridgePlugin: DatabridgePlugin = DatabridgePluginSingleton
|
||||
|
||||
/**
|
||||
* Registers 'navigator.'
|
||||
* @private
|
||||
*/
|
||||
private backfillGeolocation(databridgePlugin: DatabridgePlugin) {
|
||||
const origQueryFunc = navigator?.permissions?.query
|
||||
navigator.permissions.query = async (descr: PermissionDescriptor) => {
|
||||
if (descr.name === "geolocation") {
|
||||
console.log("Got a geolocation permission request")
|
||||
const src = UIEventSource.FromPromise(databridgePlugin.request({ key: "location:request-permission" }))
|
||||
|
||||
return <PermissionStatus>{
|
||||
state: undefined,
|
||||
addEventListener(key: "change", f: (value: "granted" | "denied") => void) {
|
||||
src.addCallbackAndRunD(v => {
|
||||
const content = <"granted" | "denied">v.value
|
||||
f(content)
|
||||
return true
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
if (origQueryFunc) {
|
||||
return await origQueryFunc(descr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async init() {
|
||||
console.log("Sniffing shell version")
|
||||
const shell = await this.databridgePlugin.request({ key: "meta" })
|
||||
if (shell.value === "web") {
|
||||
console.log("Not initing Android polyfill as not in a shell; web detected")
|
||||
return
|
||||
}
|
||||
console.log("Detected shell:", shell.value)
|
||||
this.backfillGeolocation(this.databridgePlugin)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
import ThemeSearch from "../Logic/Search/ThemeSearch"
|
||||
import SearchUtils from "../Logic/Search/SearchUtils"
|
||||
import ChevronDoubleRight from "@babeard/svelte-heroicons/mini/ChevronDoubleRight"
|
||||
|
||||
import { AndroidPolyfill } from "../Logic/Web/AndroidPolyfill"
|
||||
new AndroidPolyfill().init().then(() => console.log("Android polyfill setup completed"))
|
||||
const featureSwitches = new OsmConnectionFeatureSwitches()
|
||||
const osmConnection = new OsmConnection({
|
||||
fakeUser: featureSwitches.featureSwitchFakeUser.data,
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"properties":{"name":"Bing Maps Aerial","id":"Bing","url":"https://ecn.t3.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=14738&pr=odbl&n=f","type":"bing","category":"photo","min_zoom":1,"max_zoom":22},"type":"Feature","geometry":null}
|
||||
{"properties":{"name":"Bing Maps Aerial","id":"Bing","url":"https://ecn.t1.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=14860&pr=odbl&n=f","type":"bing","category":"photo","min_zoom":1,"max_zoom":22},"type":"Feature","geometry":null}
|
|
@ -8,6 +8,7 @@ import { SubtleButton } from "./UI/Base/SubtleButton"
|
|||
import { Utils } from "./Utils"
|
||||
import Constants from "./Models/Constants"
|
||||
import ArrowDownTray from "@babeard/svelte-heroicons/mini/ArrowDownTray"
|
||||
import { AndroidPolyfill } from "./Logic/Web/AndroidPolyfill"
|
||||
|
||||
function webgl_support() {
|
||||
try {
|
||||
|
@ -48,6 +49,7 @@ async function main() {
|
|||
if (!webgl_support()) {
|
||||
throw "WebGL is not supported or not enabled. This is essential for MapComplete to function, please enable this."
|
||||
}
|
||||
new AndroidPolyfill().init().then(() => console.log("Android polyfill setup completed"))
|
||||
const [theme, availableLayers] = await Promise.all([
|
||||
DetermineTheme.getTheme(),
|
||||
await getAvailableLayers(),
|
||||
|
|
|
@ -45,6 +45,7 @@ async function main() {
|
|||
if (!webgl_support()) {
|
||||
new FixedUiElement("WebGL is not supported or not enabled. This is essential for MapComplete to function, please enable this.").SetClass("block alert").AttachTo("maindiv")
|
||||
}else{
|
||||
new AndroidPolyfill().init().then(() => console.log("Android polyfill setup completed"))
|
||||
const availableLayers = await getAvailableLayers()
|
||||
MetaTagging.setThemeMetatagging(new ThemeMetaTagging())
|
||||
// LAYOUT.ADD_LAYERS
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue