forked from MapComplete/MapComplete
		
	Android: get polyfills for geolocation working, some steps for logging in
This commit is contained in:
		
							parent
							
								
									76e9381650
								
							
						
					
					
						commit
						14e5f1efb3
					
				
					 5 changed files with 52 additions and 28 deletions
				
			
		| 
						 | 
					@ -4,6 +4,7 @@ We are using capacitor. This is a tool which packages some files into an Android
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Developing
 | 
					## Developing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0. `nvm use` to make sure your using the correct android version
 | 
				
			||||||
1. Build all the necessary files.
 | 
					1. Build all the necessary files.
 | 
				
			||||||
   a. If no layer/theme changes were made, `npm run build` is sufficient
 | 
					   a. If no layer/theme changes were made, `npm run build` is sufficient
 | 
				
			||||||
   b. Otherwise, run `npm run prepare-deploy`.
 | 
					   b. Otherwise, run `npm run prepare-deploy`.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import { Utils } from "../../Utils"
 | 
				
			||||||
import { LocalStorageSource } from "../Web/LocalStorageSource"
 | 
					import { LocalStorageSource } from "../Web/LocalStorageSource"
 | 
				
			||||||
import { AuthConfig } from "./AuthConfig"
 | 
					import { AuthConfig } from "./AuthConfig"
 | 
				
			||||||
import Constants from "../../Models/Constants"
 | 
					import Constants from "../../Models/Constants"
 | 
				
			||||||
 | 
					import { AndroidPolyfill } from "../Web/AndroidPolyfill"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface OsmUserInfo {
 | 
					interface OsmUserInfo {
 | 
				
			||||||
    id: number
 | 
					    id: number
 | 
				
			||||||
| 
						 | 
					@ -520,6 +521,7 @@ export class OsmConnection {
 | 
				
			||||||
     * To be called by land.html
 | 
					     * To be called by land.html
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public finishLogin(callback: (previousURL: string) => void) {
 | 
					    public finishLogin(callback: (previousURL: string) => void) {
 | 
				
			||||||
 | 
					        console.log(">>> authenticating")
 | 
				
			||||||
        this.auth.authenticate(function () {
 | 
					        this.auth.authenticate(function () {
 | 
				
			||||||
            // Fully authed at this point
 | 
					            // Fully authed at this point
 | 
				
			||||||
            console.log("Authentication successful!")
 | 
					            console.log("Authentication successful!")
 | 
				
			||||||
| 
						 | 
					@ -529,17 +531,22 @@ export class OsmConnection {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private updateAuthObject() {
 | 
					    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({
 | 
					        this.auth = new osmAuth({
 | 
				
			||||||
            client_id: this._oauth_config.oauth_client_id,
 | 
					            client_id: this._oauth_config.oauth_client_id,
 | 
				
			||||||
            url: this._oauth_config.url,
 | 
					            url: this._oauth_config.url,
 | 
				
			||||||
            scope: "read_prefs write_prefs write_api write_gpx write_notes",
 | 
					            scope: "read_prefs write_prefs write_api write_gpx write_notes",
 | 
				
			||||||
            redirect_uri: Utils.runningFromConsole
 | 
					            redirect_uri,
 | 
				
			||||||
                ? "https://mapcomplete.org/land.html"
 | 
					 | 
				
			||||||
                : window.location.protocol + "//" + window.location.host + "/land.html",
 | 
					 | 
				
			||||||
            /* We use 'singlePage' as much as possible, it is the most stable - including in PWA.
 | 
					            /* 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
 | 
					             * However, this breaks in iframes so we open a popup in that case
 | 
				
			||||||
             */
 | 
					             */
 | 
				
			||||||
            singlepage: !this._iframeMode,
 | 
					            singlepage: !this._iframeMode && !AndroidPolyfill.inAndroid.data,
 | 
				
			||||||
            auto: true,
 | 
					            auto: true,
 | 
				
			||||||
            apiUrl: this._oauth_config.api_url ?? this._oauth_config.url,
 | 
					            apiUrl: this._oauth_config.api_url ?? this._oauth_config.url,
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ import { LocalStorageSource } from "../Web/LocalStorageSource"
 | 
				
			||||||
import { QueryParameters } from "../Web/QueryParameters"
 | 
					import { QueryParameters } from "../Web/QueryParameters"
 | 
				
			||||||
import { Translation } from "../../UI/i18n/Translation"
 | 
					import { Translation } from "../../UI/i18n/Translation"
 | 
				
			||||||
import Translations from "../../UI/i18n/Translations"
 | 
					import Translations from "../../UI/i18n/Translations"
 | 
				
			||||||
 | 
					import { AndroidPolyfill } from "../Web/AndroidPolyfill"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type GeolocationPermissionState = "prompt" | "requested" | "granted" | "denied"
 | 
					export type GeolocationPermissionState = "prompt" | "requested" | "granted" | "denied"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -157,12 +158,20 @@ export class GeoLocationState {
 | 
				
			||||||
            this.permission.setData("denied")
 | 
					            this.permission.setData("denied")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this.permission.data !== "prompt" && this.permission.data !== "requested") {
 | 
					        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
 | 
					            // 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"
 | 
					            // Hence that we continue the flow if it is "requested"
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(AndroidPolyfill.inAndroid.data){
 | 
				
			||||||
 | 
					            this.permission.setData("requested")
 | 
				
			||||||
 | 
					            AndroidPolyfill.geolocationPermission.addCallbackAndRunD(state => this.permission.set(state))
 | 
				
			||||||
 | 
					            this.startWatching()
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (GeoLocationState.isSafari()) {
 | 
					        if (GeoLocationState.isSafari()) {
 | 
				
			||||||
            /*
 | 
					            /*
 | 
				
			||||||
             This is probably safari
 | 
					             This is probably safari
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,16 +3,17 @@
 | 
				
			||||||
 * If this is successful, it will patch some webAPIs
 | 
					 * If this is successful, it will patch some webAPIs
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
import { registerPlugin } from "@capacitor/core"
 | 
					import { registerPlugin } from "@capacitor/core"
 | 
				
			||||||
import { UIEventSource } from "../UIEventSource"
 | 
					import { Store, UIEventSource } from "../UIEventSource"
 | 
				
			||||||
 | 
					import { OsmConnection } from "../Osm/OsmConnection"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface DatabridgePlugin {
 | 
					export interface DatabridgePlugin {
 | 
				
			||||||
    request(options: { key: string }): Promise<{ value: string }>;
 | 
					    request(options: { key: string }): Promise<{ value: string | object }>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DatabridgePluginSingleton = registerPlugin<DatabridgePlugin>("Databridge", {
 | 
					const DatabridgePluginSingleton = registerPlugin<DatabridgePlugin>("Databridge", {
 | 
				
			||||||
    web: () => {
 | 
					    web: () => {
 | 
				
			||||||
        return <DatabridgePlugin>{
 | 
					        return <DatabridgePlugin>{
 | 
				
			||||||
            async request(options: { key: string }): Promise<{ value: string }> {
 | 
					            async request(options: { key: string }): Promise<{ value: string | object }> {
 | 
				
			||||||
                return { value: "web" }
 | 
					                return { value: "web" }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -21,6 +22,10 @@ const DatabridgePluginSingleton = registerPlugin<DatabridgePlugin>("Databridge",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class AndroidPolyfill {
 | 
					export class AndroidPolyfill {
 | 
				
			||||||
    private readonly databridgePlugin: DatabridgePlugin = DatabridgePluginSingleton
 | 
					    private readonly databridgePlugin: DatabridgePlugin = DatabridgePluginSingleton
 | 
				
			||||||
 | 
					    private static readonly _inAndroid: UIEventSource<boolean> = new UIEventSource<boolean>(false)
 | 
				
			||||||
 | 
					    public static readonly inAndroid: Store<boolean> = 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.'
 | 
					     * Registers 'navigator.'
 | 
				
			||||||
| 
						 | 
					@ -28,26 +33,10 @@ export class AndroidPolyfill {
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private backfillGeolocation(databridgePlugin: DatabridgePlugin) {
 | 
					    private backfillGeolocation(databridgePlugin: DatabridgePlugin) {
 | 
				
			||||||
        const origQueryFunc = navigator?.permissions?.query
 | 
					        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" }))
 | 
					        const src = UIEventSource.FromPromise(databridgePlugin.request({ key: "location:request-permission" }))
 | 
				
			||||||
 | 
					        src.addCallbackAndRunD(permission => {
 | 
				
			||||||
                return <PermissionStatus>{
 | 
					            AndroidPolyfill._geolocationPermission.set(<"granted" | "denied">permission.value)
 | 
				
			||||||
                    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() {
 | 
					    public async init() {
 | 
				
			||||||
| 
						 | 
					@ -57,8 +46,20 @@ export class AndroidPolyfill {
 | 
				
			||||||
            console.log("Not initing Android polyfill as not in a shell; web detected")
 | 
					            console.log("Not initing Android polyfill as not in a shell; web detected")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        AndroidPolyfill._inAndroid.set(true)
 | 
				
			||||||
        console.log("Detected shell:", shell.value)
 | 
					        console.log("Detected shell:", shell.value)
 | 
				
			||||||
        this.backfillGeolocation(this.databridgePlugin)
 | 
					        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)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,6 +52,7 @@
 | 
				
			||||||
  import PanoramaxLink from "./PanoramaxLink.svelte"
 | 
					  import PanoramaxLink from "./PanoramaxLink.svelte"
 | 
				
			||||||
  import { UIEventSource } from "../../Logic/UIEventSource"
 | 
					  import { UIEventSource } from "../../Logic/UIEventSource"
 | 
				
			||||||
  import MagnifyingGlassCircle from "@babeard/svelte-heroicons/mini/MagnifyingGlassCircle"
 | 
					  import MagnifyingGlassCircle from "@babeard/svelte-heroicons/mini/MagnifyingGlassCircle"
 | 
				
			||||||
 | 
					  import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let state: ThemeViewState
 | 
					  export let state: ThemeViewState
 | 
				
			||||||
  let userdetails = state.osmConnection.userDetails
 | 
					  let userdetails = state.osmConnection.userDetails
 | 
				
			||||||
| 
						 | 
					@ -78,6 +79,7 @@
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					  let isAndroid = AndroidPolyfill.inAndroid
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div
 | 
					<div
 | 
				
			||||||
| 
						 | 
					@ -255,7 +257,8 @@
 | 
				
			||||||
    </If>
 | 
					    </If>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <a class="sidebar-button flex" href="geo:{$location.lat},{$location.lon}">
 | 
					    <a class="sidebar-button flex" href="geo:{$location.lat},{$location.lon}">
 | 
				
			||||||
      <ShareIcon /><Tr t={t.openHereDifferentApp} />
 | 
					      <ShareIcon />
 | 
				
			||||||
 | 
					      <Tr t={t.openHereDifferentApp} />
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
  </SidebarUnit>
 | 
					  </SidebarUnit>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -343,6 +346,9 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div class="subtle self-end">
 | 
					    <div class="subtle self-end">
 | 
				
			||||||
      {Constants.vNumber}
 | 
					      {Constants.vNumber}
 | 
				
			||||||
 | 
					      {#if $isAndroid}
 | 
				
			||||||
 | 
					        Android
 | 
				
			||||||
 | 
					      {/if}
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </SidebarUnit>
 | 
					  </SidebarUnit>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue