Skip to content

Commit

Permalink
Add location service
Browse files Browse the repository at this point in the history
  • Loading branch information
pipe01 committed Mar 23, 2024
1 parent de39ec8 commit d88ee93
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 12 deletions.
32 changes: 22 additions & 10 deletions api/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,14 @@ declare interface PlaybackState {
get album(): string | null;
}

declare interface Watches {
declare interface Location {
get latitude(): number;
get longitude(): number;
get altitude(): number | null;
get accuracy(): number | null;
}

declare interface WatchesService {
get all(): Watch[];

addEventListener(event: "connected", cb: (watch: Watch) => void): void;
Expand All @@ -57,7 +64,7 @@ declare interface Watches {
removeEventListener(event: "disconnected", cb: (watchAddress: string) => void): void;
}

declare interface Notifications {
declare interface NotificationsService {
addEventListener(event: "received", cb: (notif: Notification) => void): void;
removeEventListener(event: "received", cb: (notif: Notification) => void): void;
}
Expand All @@ -68,12 +75,12 @@ declare type HTTPOptions = {
headers?: Record<string, string>;
}

declare interface HTTP {
declare interface HTTPService {
request(method: HTTPMethod, url: string, cb: (response: string) => void): void;
request(method: HTTPMethod, url: string, options: HTTPOptions, cb: (response: string) => void): void;
}

declare interface Volume {
declare interface VolumeService {
get voiceCallStream(): VolumeStream;
get systemStream(): VolumeStream;
get ringStream(): VolumeStream;
Expand All @@ -83,7 +90,7 @@ declare interface Volume {
get accessibilityStream(): VolumeStream;
}

declare interface Media {
declare interface MediaService {
get state(): PlaybackState;

play(): void;
Expand All @@ -92,8 +99,13 @@ declare interface Media {
previous(): void;
}

declare function require(module: "watches"): Watches;
declare function require(module: "notifications"): Notifications;
declare function require(module: "http"): HTTP;
declare function require(module: "volume"): Volume;
declare function require(module: "media"): Media;
declare interface LocationService {
get location(): Location
}

declare function require(module: "watches"): WatchesService;
declare function require(module: "notifications"): NotificationsService;
declare function require(module: "http"): HTTPService;
declare function require(module: "volume"): VolumeService;
declare function require(module: "media"): MediaService;
declare function require(module: "location"): LocationService;
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ dependencies {
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.1-Beta")

implementation("com.google.accompanist:accompanist-drawablepainter:0.34.0")

val nav_version = "2.7.7"
Expand All @@ -104,4 +106,6 @@ dependencies {
implementation(files("libs/rhino-1.7.15-SNAPSHOT.jar"))

implementation("com.github.qawaz:compose-code-editor:2.0.3")

implementation("com.google.android.gms:play-services-location:21.2.0")
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ enum class Permission(val title: String) {
HTTP("Send HTTP requests"),
VOLUME_CONTROL("Control the phone's volume"),
MEDIA_CONTROL("Control media playback"),
LOCATION("Access location services"),
}
9 changes: 8 additions & 1 deletion app/src/main/java/net/pipe01/pinepartner/scripting/Runner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,25 @@ package net.pipe01.pinepartner.scripting
import android.media.AudioManager
import android.media.session.MediaSessionManager
import android.util.Log
import com.google.android.gms.location.FusedLocationProviderClient
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.newSingleThreadContext
import net.pipe01.pinepartner.data.AppDatabase
import net.pipe01.pinepartner.data.Plugin
import net.pipe01.pinepartner.scripting.api.Finalizeable
import net.pipe01.pinepartner.scripting.api.HTTP
import net.pipe01.pinepartner.scripting.api.Location
import net.pipe01.pinepartner.scripting.api.Media
import net.pipe01.pinepartner.scripting.api.Notifications
import net.pipe01.pinepartner.scripting.api.Require
import net.pipe01.pinepartner.scripting.api.Volume
import net.pipe01.pinepartner.scripting.api.Watches
import net.pipe01.pinepartner.scripting.api.adapters.BLECharacteristicAdapter
import net.pipe01.pinepartner.scripting.api.adapters.BLEServiceAdapter
import net.pipe01.pinepartner.scripting.api.adapters.LocationAdapter
import net.pipe01.pinepartner.scripting.api.adapters.NotificationAdapter
import net.pipe01.pinepartner.scripting.api.adapters.PlaybackStateAdapter
import net.pipe01.pinepartner.scripting.api.adapters.VolumeStreamAdapter
Expand All @@ -41,6 +45,7 @@ data class ScriptDependencies(
val deviceManager: DeviceManager,
val audioManager: AudioManager,
val mediaSessionManager: MediaSessionManager,
val fusedLocationProviderClient: FusedLocationProviderClient,
)

class Runner(val plugin: Plugin, deps: ScriptDependencies) {
Expand Down Expand Up @@ -97,13 +102,15 @@ class Runner(val plugin: Plugin, deps: ScriptDependencies) {
ScriptableObject.defineClass(scope, HTTP::class.java)
ScriptableObject.defineClass(scope, Volume::class.java)
ScriptableObject.defineClass(scope, Media::class.java)
ScriptableObject.defineClass(scope, Location::class.java)

ScriptableObject.defineClass(scope, WatchAdapter::class.java)
ScriptableObject.defineClass(scope, NotificationAdapter::class.java)
ScriptableObject.defineClass(scope, BLEServiceAdapter::class.java)
ScriptableObject.defineClass(scope, BLECharacteristicAdapter::class.java)
ScriptableObject.defineClass(scope, VolumeStreamAdapter::class.java)
ScriptableObject.defineClass(scope, PlaybackStateAdapter::class.java)
ScriptableObject.defineClass(scope, LocationAdapter::class.java)

ScriptableObject.putProperty(scope, "require", Require(deps, plugin.permissions, contextFactory, dispatcher, ::addEvent))

Expand All @@ -129,7 +136,7 @@ class Runner(val plugin: Plugin, deps: ScriptDependencies) {

fun start() {
if (!_hasStarted.getAndSet(true)) {
CoroutineScope(dispatcher).run {
CoroutineScope(dispatcher).launch {
contextFactory.call {
try {
script.exec(it, scope)
Expand Down
35 changes: 35 additions & 0 deletions app/src/main/java/net/pipe01/pinepartner/scripting/api/Location.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package net.pipe01.pinepartner.scripting.api

import android.annotation.SuppressLint
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.Priority
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.tasks.await
import net.pipe01.pinepartner.scripting.api.adapters.LocationAdapter
import org.mozilla.javascript.annotations.JSGetter

class Location : ApiScriptableObject(CLASS_NAME) {
companion object {
const val CLASS_NAME = "Location"
}

private lateinit var fusedLocationClient: FusedLocationProviderClient

fun init(fusedLocationClient: FusedLocationProviderClient) {
this.fusedLocationClient = fusedLocationClient
}

@SuppressLint("MissingPermission")
@JSGetter
fun getCurrent(): LocationAdapter {
val task = fusedLocationClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, null)

val location = runBlocking {
task.await()
}

return newObject<LocationAdapter>(LocationAdapter.CLASS_NAME).apply {
init(location)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ class Require(
}
}

"location" -> {
checkPermission(cx, Permission.LOCATION)

createInstance<Location>(cx, scope) {
init(deps.fusedLocationProviderClient)
}
}

else -> throw ScriptRuntime.throwError(cx, scope, "Unknown module $className")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.pipe01.pinepartner.scripting.api.adapters

import android.location.Location
import net.pipe01.pinepartner.scripting.api.ApiScriptableObject
import org.mozilla.javascript.annotations.JSGetter

class LocationAdapter : ApiScriptableObject(CLASS_NAME) {
companion object {
const val CLASS_NAME = "LocationAdapter"
}

private lateinit var location: Location

fun init(location: Location) {
this.location = location
}

@JSGetter
fun getLatitude() = location.latitude

@JSGetter
fun getLongitude() = location.longitude

@JSGetter
fun getAltitude() = if (location.hasAltitude()) location.altitude else null

@JSGetter
fun getAccuracy() = if (location.hasAccuracy()) location.accuracy else null
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import android.os.IBinder
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import com.google.android.gms.location.LocationServices
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -98,6 +99,8 @@ class BackgroundService : Service() {

val mediaSessionManager = getSystemService(Context.MEDIA_SESSION_SERVICE) as MediaSessionManager

val fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(applicationContext)

BuiltInPlugins.init(assets)

pluginManager = PluginManager(
Expand All @@ -107,7 +110,8 @@ class BackgroundService : Service() {
notifManager,
deviceManager,
audioManager,
mediaSessionManager
mediaSessionManager,
fusedLocationProviderClient,
),
)

Expand Down

0 comments on commit d88ee93

Please sign in to comment.