diff --git a/Docs/Android_development.md b/Docs/Android_development.md index 231f24a863..4d0672648a 100644 --- a/Docs/Android_development.md +++ b/Docs/Android_development.md @@ -4,6 +4,7 @@ We are using capacitor. This is a tool which packages some files into an Android ## Developing +0. `nvm use` to make sure your using the correct android version 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`. diff --git a/src/Logic/Osm/OsmConnection.ts b/src/Logic/Osm/OsmConnection.ts index 4ee3b73b79..ba48e5cf5a 100644 --- a/src/Logic/Osm/OsmConnection.ts +++ b/src/Logic/Osm/OsmConnection.ts @@ -5,6 +5,7 @@ import { Utils } from "../../Utils" import { LocalStorageSource } from "../Web/LocalStorageSource" import { AuthConfig } from "./AuthConfig" import Constants from "../../Models/Constants" +import { AndroidPolyfill } from "../Web/AndroidPolyfill" interface OsmUserInfo { id: number @@ -520,6 +521,7 @@ export class OsmConnection { * To be called by land.html */ public finishLogin(callback: (previousURL: string) => void) { + console.log(">>> authenticating") this.auth.authenticate(function () { // Fully authed at this point console.log("Authentication successful!") @@ -529,17 +531,22 @@ export class OsmConnection { } private updateAuthObject() { + let redirect_uri = Utils.runningFromConsole + ? "https://mapcomplete.org/land.html" + : window.location.protocol + "//" + window.location.host + "/land.html" + if(AndroidPolyfill.inAndroid.data){ + redirect_uri = "https://app.mapcomplete.org/land.html" + AndroidPolyfill.requestLoginCodes(this) + } this.auth = new osmAuth({ client_id: this._oauth_config.oauth_client_id, url: this._oauth_config.url, scope: "read_prefs write_prefs write_api write_gpx write_notes", - redirect_uri: Utils.runningFromConsole - ? "https://mapcomplete.org/land.html" - : window.location.protocol + "//" + window.location.host + "/land.html", + redirect_uri, /* We use 'singlePage' as much as possible, it is the most stable - including in PWA. * However, this breaks in iframes so we open a popup in that case */ - singlepage: !this._iframeMode, + singlepage: !this._iframeMode && !AndroidPolyfill.inAndroid.data, auto: true, apiUrl: this._oauth_config.api_url ?? this._oauth_config.url, }) diff --git a/src/Logic/State/GeoLocationState.ts b/src/Logic/State/GeoLocationState.ts index 77c0f971eb..4806762b0d 100644 --- a/src/Logic/State/GeoLocationState.ts +++ b/src/Logic/State/GeoLocationState.ts @@ -3,6 +3,7 @@ import { LocalStorageSource } from "../Web/LocalStorageSource" import { QueryParameters } from "../Web/QueryParameters" import { Translation } from "../../UI/i18n/Translation" import Translations from "../../UI/i18n/Translations" +import { AndroidPolyfill } from "../Web/AndroidPolyfill" export type GeolocationPermissionState = "prompt" | "requested" | "granted" | "denied" @@ -157,12 +158,20 @@ export class GeoLocationState { this.permission.setData("denied") return } + if (this.permission.data !== "prompt" && this.permission.data !== "requested") { // If the user denies the first prompt, revokes the deny and then tries again, we have to run the flow as well // Hence that we continue the flow if it is "requested" return } + if(AndroidPolyfill.inAndroid.data){ + this.permission.setData("requested") + AndroidPolyfill.geolocationPermission.addCallbackAndRunD(state => this.permission.set(state)) + this.startWatching() + return + } + if (GeoLocationState.isSafari()) { /* This is probably safari diff --git a/src/Logic/Web/AndroidPolyfill.ts b/src/Logic/Web/AndroidPolyfill.ts index 637140a394..16663637b4 100644 --- a/src/Logic/Web/AndroidPolyfill.ts +++ b/src/Logic/Web/AndroidPolyfill.ts @@ -3,16 +3,17 @@ * If this is successful, it will patch some webAPIs */ import { registerPlugin } from "@capacitor/core" -import { UIEventSource } from "../UIEventSource" +import { Store, UIEventSource } from "../UIEventSource" +import { OsmConnection } from "../Osm/OsmConnection" export interface DatabridgePlugin { - request(options: { key: string }): Promise<{ value: string }>; + request(options: { key: string }): Promise<{ value: string | object }>; } const DatabridgePluginSingleton = registerPlugin("Databridge", { web: () => { return { - async request(options: { key: string }): Promise<{ value: string }> { + async request(options: { key: string }): Promise<{ value: string | object }> { return { value: "web" } }, } @@ -21,6 +22,10 @@ const DatabridgePluginSingleton = registerPlugin("Databridge", export class AndroidPolyfill { private readonly databridgePlugin: DatabridgePlugin = DatabridgePluginSingleton + private static readonly _inAndroid: UIEventSource = new UIEventSource(false) + public static readonly inAndroid: Store = AndroidPolyfill._inAndroid + private static readonly _geolocationPermission: UIEventSource<"granted" | "denied" | "prompt"> = new UIEventSource("prompt") + public static readonly geolocationPermission: Store<"granted" | "denied" | "prompt"> = this._geolocationPermission /** * Registers 'navigator.' @@ -28,26 +33,10 @@ export class AndroidPolyfill { */ 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 { - 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) - } - } + const src = UIEventSource.FromPromise(databridgePlugin.request({ key: "location:request-permission" })) + src.addCallbackAndRunD(permission => { + AndroidPolyfill._geolocationPermission.set(<"granted" | "denied">permission.value) + }) } public async init() { @@ -57,8 +46,20 @@ export class AndroidPolyfill { console.log("Not initing Android polyfill as not in a shell; web detected") return } + AndroidPolyfill._inAndroid.set(true) console.log("Detected shell:", shell.value) this.backfillGeolocation(this.databridgePlugin) } + public static async requestLoginCodes(osmConnection: OsmConnection) { + const result = await DatabridgePluginSingleton.request({ key: "request:login" }) + const code: string = result["code"] + const state: string = result["state"] + console.log("AndroidPolyfill: received code and state; trying to pass them to the oauth lib") + window.location.search = "?code=" + code + "&state=" + state + osmConnection.finishLogin((oldLocation) => { + console.log("Login should be completed, oldLocation is", oldLocation) + }) + } + } diff --git a/src/UI/BigComponents/MenuDrawer.svelte b/src/UI/BigComponents/MenuDrawer.svelte index d208a5f01b..b9baf04812 100644 --- a/src/UI/BigComponents/MenuDrawer.svelte +++ b/src/UI/BigComponents/MenuDrawer.svelte @@ -52,6 +52,7 @@ import PanoramaxLink from "./PanoramaxLink.svelte" import { UIEventSource } from "../../Logic/UIEventSource" import MagnifyingGlassCircle from "@babeard/svelte-heroicons/mini/MagnifyingGlassCircle" + import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill" export let state: ThemeViewState let userdetails = state.osmConnection.userDetails @@ -78,6 +79,7 @@ }) } }) + let isAndroid = AndroidPolyfill.inAndroid
- + + @@ -343,6 +346,9 @@
{Constants.vNumber} + {#if $isAndroid} + Android + {/if}