2024-11-18 20:43:19 +01:00
/ * *
* The Android Polyfill will attempt to communicate with the Anrdoid Shell .
* If this is successful , it will patch some webAPIs
* /
import { registerPlugin } from "@capacitor/core"
2024-12-12 00:46:24 +01:00
import { Store , UIEventSource } from "../UIEventSource"
2024-12-10 04:28:50 +01:00
export interface DatabridgePlugin {
2025-02-10 02:04:58 +01:00
request < T extends string | object = string | object > ( options : {
key : string
} ) : Promise < { value : T } >
2024-12-10 04:28:50 +01:00
}
const DatabridgePluginSingleton = registerPlugin < DatabridgePlugin > ( "Databridge" , {
web : ( ) = > {
return < DatabridgePlugin > {
2024-12-12 00:46:24 +01:00
async request ( options : { key : string } ) : Promise < { value : string | object } > {
2025-01-21 21:06:50 +01:00
console . log ( "Android polyfill got request for" , options . key )
2025-01-12 01:53:58 +01:00
if ( options . key === "meta" ) {
return { value : "web" }
}
2025-01-21 21:06:50 +01:00
return null
2025-01-22 01:26:12 +01:00
} ,
2024-12-10 04:28:50 +01:00
}
2025-01-22 01:26:12 +01:00
} ,
2024-12-10 04:28:50 +01:00
} )
2024-11-18 20:43:19 +01:00
export class AndroidPolyfill {
2025-01-23 13:47:59 +01:00
private static readonly databridgePlugin : DatabridgePlugin = DatabridgePluginSingleton
2024-12-12 00:46:24 +01:00
private static readonly _inAndroid : UIEventSource < boolean > = new UIEventSource < boolean > ( false )
public static readonly inAndroid : Store < boolean > = AndroidPolyfill . _inAndroid
2025-02-10 02:04:58 +01:00
private static readonly _geolocationPermission : UIEventSource < "granted" | "denied" | "prompt" > =
new UIEventSource ( "prompt" )
public static readonly geolocationPermission : Store < "granted" | "denied" | "prompt" > =
this . _geolocationPermission
2024-12-10 04:28:50 +01:00
/ * *
* Registers 'navigator.'
* @private
* /
2025-01-23 13:47:59 +01:00
private static backfillGeolocation ( databridgePlugin : DatabridgePlugin ) {
2025-08-01 03:07:37 +02:00
const src = UIEventSource . fromPromise (
2025-02-10 02:04:58 +01:00
databridgePlugin . request ( { key : "location:has-permission" } )
)
src . addCallbackAndRunD ( ( permission ) = > {
console . log (
"> Checking geopermission gave: " ,
JSON . stringify ( permission ) ,
permission . value
)
2025-01-22 01:26:12 +01:00
const granted = permission . value === "true"
AndroidPolyfill . _geolocationPermission . set ( granted ? "granted" : "denied" )
2024-12-12 00:46:24 +01:00
} )
2024-11-18 20:43:19 +01:00
}
2025-01-22 01:26:12 +01:00
public static async requestGeoPermission ( ) : Promise < { value : string | object } > {
return DatabridgePluginSingleton . request ( { key : "location:request-permission" } )
}
2025-01-23 13:47:59 +01:00
public static async init() {
2024-12-10 04:28:50 +01:00
console . log ( "Sniffing shell version" )
2025-01-23 13:47:59 +01:00
const shell = await AndroidPolyfill . databridgePlugin . request ( { key : "meta" } )
2024-12-10 04:28:50 +01:00
if ( shell . value === "web" ) {
console . log ( "Not initing Android polyfill as not in a shell; web detected" )
2024-11-18 20:43:19 +01:00
return
}
2024-12-12 00:46:24 +01:00
AndroidPolyfill . _inAndroid . set ( true )
2024-11-18 20:43:19 +01:00
console . log ( "Detected shell:" , shell . value )
2025-01-23 13:47:59 +01:00
AndroidPolyfill . backfillGeolocation ( AndroidPolyfill . databridgePlugin )
2024-11-18 20:43:19 +01:00
}
2025-04-27 02:00:53 +02:00
/ * *
* Actually opens the login page
* Note that the actual token is handled by requestLoginCodes
* /
public static async openLoginPage ( ) : Promise < void > {
await DatabridgePluginSingleton . request ( { key : "open:login" } )
2025-02-13 22:07:45 +01:00
}
2025-04-27 02:00:53 +02:00
/ * *
* Installs a callback that might eventually get the login token .
* It requests the login codes to the shell , not to the user .
* This method can be called without bothering the user .
* /
2024-12-31 19:55:08 +01:00
public static async requestLoginCodes() {
2025-02-10 02:04:58 +01:00
const result = await DatabridgePluginSingleton . request < { oauth_token : string } > ( {
key : "request:login" ,
} )
2024-12-19 13:49:14 +01:00
const token : string = result . value . oauth_token
2025-02-10 02:04:58 +01:00
console . log (
"AndroidPolyfill: received oauth_token; trying to pass them to the oauth lib" ,
token
)
2024-12-31 19:55:08 +01:00
return token
2024-12-12 00:46:24 +01:00
}
2025-01-12 01:53:58 +01:00
2025-07-14 00:38:00 +02:00
/ * *
* Gets how much padding we should add on top and at the bottom ; in pixels
* /
private static insets : { top : Store < number > , bottom : Store < number > } = undefined
public static getInsetSizes ( ) : Readonly < { top : Store < number > , bottom : Store < number > } > {
if ( AndroidPolyfill . insets ) {
return AndroidPolyfill . insets
}
const insets = {
top : new UIEventSource ( 0 ) ,
bottom : new UIEventSource ( 0 ) ,
}
AndroidPolyfill . insets = insets
console . log ( "Web: requesting inset sizes" )
DatabridgePluginSingleton . request < { top : number , bottom : number } > ( {
key : "insets" ,
} ) . then ( ( result ) = > {
2025-07-18 14:15:37 +02:00
if ( ! result ) {
return
}
2025-07-14 00:38:00 +02:00
let v = result . value
if ( typeof v === "string" ) {
v = JSON . parse ( v )
}
console . log ( "Got inset sizes:" , result )
2025-08-10 15:21:30 +02:00
insets . bottom . set ( v . bottom / window . devicePixelRatio )
insets . top . set ( v . top / window . devicePixelRatio )
2025-07-14 00:38:00 +02:00
} )
return insets
}
2025-02-10 02:04:58 +01:00
public static onBackButton (
callback : ( ) = > boolean ,
options : {
returnToIndex : Store < boolean >
}
) {
2025-01-12 01:53:58 +01:00
console . log ( "Registering back button callback" , callback )
2025-02-10 02:04:58 +01:00
DatabridgePluginSingleton . request ( { key : "backbutton" } ) . then ( ( ev ) = > {
2025-01-12 01:53:58 +01:00
console . log ( "AndroidPolyfill: received backbutton: " , ev )
2025-01-22 01:26:12 +01:00
if ( ev === null ) {
2025-01-21 21:06:50 +01:00
// Probably in web environment
return
}
// We have to re-register every time
AndroidPolyfill . onBackButton ( callback , options )
2025-01-12 01:53:58 +01:00
if ( callback ( ) ) {
return
}
// Nothing more to close - we return (if not a single theme) to the index
if ( options . returnToIndex ) {
console . log ( "Back to the index!" )
window . location . href = "/"
}
} )
}
2025-01-22 01:26:12 +01:00
2025-02-10 02:04:58 +01:00
public static watchLocation (
writeInto : UIEventSource < GeolocationCoordinates > ,
callback : ( location ) = > void
) {
2025-01-22 01:26:12 +01:00
DatabridgePluginSingleton . request ( {
key : "location:watch" ,
2025-02-10 02:04:58 +01:00
} ) . then (
( l : {
value : {
latitude : number
longitude : number
accuraccy : number
altidude : number
heading : number
speed : number
}
} ) = > {
// example l: {"value":{"latitude":51.0618627,"longitude":3.730468566666667,"accuracy":2.0393495559692383,"altitude":46.408,"heading":168.2969970703125}}
console . log ( "Received location from Android:" , JSON . stringify ( l ) )
const loc = l . value
writeInto . set ( {
latitude : loc.latitude ,
longitude : loc.longitude ,
heading : loc.heading ,
accuracy : loc.accuraccy ,
altitude : loc.altidude ,
altitudeAccuracy : undefined ,
speed : loc.speed ,
} )
}
)
2025-01-22 01:26:12 +01:00
}
2024-11-18 20:43:19 +01:00
}