updated 280925-01
This commit is contained in:
parent
448824a187
commit
f5be8180f2
48
README.md
48
README.md
@ -94,6 +94,52 @@ configureEdgeToEdge(options: {
|
||||
|
||||
Mengatur status bar dan navigasi bar agar transparan, dengan warna dan icon style sesuai UI.
|
||||
|
||||
### setOptions()
|
||||
|
||||
```typescript
|
||||
setOptions(options: {
|
||||
distanceThresholdMeters?: number;
|
||||
speedChangeThreshold?: number;
|
||||
directionChangeThreshold?: number;
|
||||
emitDebounceMs?: number;
|
||||
drivingEmitIntervalMs?: number;
|
||||
wifiScanIntervalMs?: number;
|
||||
enableWifiRtt?: boolean;
|
||||
enableLogging?: boolean;
|
||||
enableForwardPrediction?: boolean;
|
||||
maxPredictionSeconds?: number;
|
||||
emitGnssStatus?: boolean;
|
||||
suppressMockedUpdates?: boolean;
|
||||
keepScreenOn?: boolean;
|
||||
}): Promise<void>
|
||||
```
|
||||
|
||||
Mengubah parameter runtime tanpa rebuild. Semua default menjaga perilaku saat ini.
|
||||
|
||||
### getGnssStatus()
|
||||
|
||||
```typescript
|
||||
getGnssStatus(): Promise<SatelliteStatus | null>
|
||||
```
|
||||
|
||||
Mengambil status GNSS terakhir untuk debugging.
|
||||
|
||||
### getLocationServicesStatus()
|
||||
|
||||
```typescript
|
||||
getLocationServicesStatus(): Promise<{ gpsEnabled: boolean; networkEnabled: boolean }>
|
||||
```
|
||||
|
||||
Memeriksa apakah provider lokasi aktif.
|
||||
|
||||
### addListener('onGnssStatus', …)
|
||||
|
||||
```typescript
|
||||
addListener('onGnssStatus', (data: SatelliteStatus) => void): PluginListenerHandle
|
||||
```
|
||||
|
||||
Menerima update status GNSS jika `emitGnssStatus: true` di setOptions.
|
||||
|
||||
---
|
||||
|
||||
## Interfaces
|
||||
@ -102,7 +148,7 @@ PositioningData
|
||||
|
||||
```typescript
|
||||
interface PositioningData {
|
||||
source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK' | 'PREDICTED';
|
||||
source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';
|
||||
timestamp: number;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
|
||||
@ -25,6 +25,7 @@ import com.dumon.plugin.geolocation.wifi.WifiPositioningManager
|
||||
import com.dumon.plugin.geolocation.wifi.WifiScanResult
|
||||
//import com.dumon.plugin.geolocation.fusion.SensorFusionManager
|
||||
import com.dumon.plugin.geolocation.utils.PermissionUtils
|
||||
import com.dumon.plugin.geolocation.utils.LogUtils
|
||||
import com.getcapacitor.annotation.PermissionCallback
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
@ -67,9 +68,9 @@ class DumonGeolocation : Plugin() {
|
||||
private var prevSpeed = 0f
|
||||
private var prevDirection = 0f
|
||||
// private val significantChangeThreshold = 0.00007 // ~7 meters
|
||||
private val significantChangeThreshold = 7 // ~7 meters
|
||||
private val speedChangeThreshold = 0.5f // m/s
|
||||
private val directionChangeThreshold = 0.17f // ~10 deg
|
||||
private var significantChangeThreshold = 7.0 // ~7 meters
|
||||
private var speedChangeThreshold = 0.5f // m/s
|
||||
private var directionChangeThreshold = 0.17f // ~10 deg
|
||||
|
||||
// private val emitIntervalMs: Long = 500L
|
||||
private var emitIntervalMs: Long = 1000L // hard debounce
|
||||
@ -80,17 +81,32 @@ class DumonGeolocation : Plugin() {
|
||||
private var bufferedDrivingLocation: Location? = null
|
||||
private var drivingEmitHandler: Handler? = null
|
||||
private var drivingEmitRunnable: Runnable? = null
|
||||
private val drivingEmitIntervalMs = 1600L
|
||||
private var drivingEmitIntervalMs = 1600L
|
||||
|
||||
private var wifiScanIntervalMs = 3000L
|
||||
private var enableWifiRtt = true
|
||||
private var enableForwardPrediction = false
|
||||
private var maxPredictionSeconds = 1.0
|
||||
private var emitGnssStatus = false
|
||||
private var suppressMockedUpdates = false
|
||||
private var keepScreenOn = false
|
||||
|
||||
private var currentTrackingMode = GpsTrackingMode.NORMAL
|
||||
|
||||
override fun load() {
|
||||
gpsManager = GpsStatusManager(
|
||||
context,
|
||||
onSatelliteStatusUpdate = { satelliteStatus = it },
|
||||
onSatelliteStatusUpdate = { status ->
|
||||
satelliteStatus = status
|
||||
if (emitGnssStatus) {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
notifyListeners("onGnssStatus", buildGnssStatusData(status))
|
||||
}
|
||||
}
|
||||
},
|
||||
onLocationUpdate = onLocationUpdate@{ location, isMocked ->
|
||||
if (location.latitude == 0.0 && location.longitude == 0.0) {
|
||||
Log.w("GPS_LOCATION", "Ignored location update: (0.0, 0.0)")
|
||||
LogUtils.w("GPS_LOCATION", "Ignored location update: (0.0, 0.0)")
|
||||
return@onLocationUpdate
|
||||
}
|
||||
|
||||
@ -148,7 +164,9 @@ class DumonGeolocation : Plugin() {
|
||||
}
|
||||
|
||||
private fun stopDrivingEmitLoop() {
|
||||
drivingEmitHandler?.removeCallbacks(drivingEmitRunnable!!)
|
||||
drivingEmitRunnable?.let { runnable ->
|
||||
drivingEmitHandler?.removeCallbacks(runnable)
|
||||
}
|
||||
drivingEmitHandler = null
|
||||
drivingEmitRunnable = null
|
||||
bufferedDrivingLocation = null
|
||||
@ -163,7 +181,9 @@ class DumonGeolocation : Plugin() {
|
||||
|
||||
gpsManager?.start()
|
||||
imuManager?.start()
|
||||
wifiManager?.startPeriodicScan(3000L)
|
||||
wifiManager?.setEnableRtt(enableWifiRtt)
|
||||
wifiManager?.startPeriodicScan(wifiScanIntervalMs)
|
||||
applyKeepScreenOn(keepScreenOn)
|
||||
call.resolve()
|
||||
}
|
||||
|
||||
@ -173,6 +193,7 @@ class DumonGeolocation : Plugin() {
|
||||
imuManager?.stop()
|
||||
wifiManager?.stopPeriodicScan()
|
||||
stopDrivingEmitLoop()
|
||||
applyKeepScreenOn(false)
|
||||
call.resolve()
|
||||
}
|
||||
|
||||
@ -184,7 +205,18 @@ class DumonGeolocation : Plugin() {
|
||||
@PluginMethod
|
||||
fun checkAndRequestPermissions(call: PluginCall) {
|
||||
// requestAllPermissions(call, "checkAndRequestPermissions")
|
||||
val isLocationGranted =
|
||||
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||
val isWifiGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
ActivityCompat.checkSelfPermission(context, Manifest.permission.NEARBY_WIFI_DEVICES) == PackageManager.PERMISSION_GRANTED
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
if (!isLocationGranted || !isWifiGranted) {
|
||||
requestAllPermissions(call, "onPermissionResult")
|
||||
return
|
||||
}
|
||||
|
||||
val locationStatus = PermissionUtils.getPermissionStatus(
|
||||
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
@ -272,12 +304,12 @@ class DumonGeolocation : Plugin() {
|
||||
gpsManager?.startContinuousMode()
|
||||
currentTrackingMode = GpsTrackingMode.DRIVING
|
||||
startDrivingEmitLoop()
|
||||
Log.d("DUMON_GEOLOCATION", "Switched to driving mode (continuous GPS)")
|
||||
LogUtils.d("DUMON_GEOLOCATION", "Switched to driving mode (continuous GPS)")
|
||||
} else {
|
||||
gpsManager?.startPollingMode()
|
||||
currentTrackingMode = GpsTrackingMode.NORMAL
|
||||
stopDrivingEmitLoop()
|
||||
Log.d("DUMON_GEOLOCATION", "Switched to normal mode (polling GPS)")
|
||||
LogUtils.d("DUMON_GEOLOCATION", "Switched to normal mode (polling GPS)")
|
||||
}
|
||||
call.resolve()
|
||||
}
|
||||
@ -302,9 +334,89 @@ class DumonGeolocation : Plugin() {
|
||||
call.resolve(result)
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
fun setOptions(call: PluginCall) {
|
||||
call.getDouble("distanceThresholdMeters")?.let { significantChangeThreshold = it }
|
||||
call.getDouble("speedChangeThreshold")?.let { speedChangeThreshold = it.toFloat() }
|
||||
call.getDouble("directionChangeThreshold")?.let { directionChangeThreshold = it.toFloat() }
|
||||
call.getInt("emitDebounceMs")?.let {
|
||||
emitIntervalMs = it.toLong().coerceAtLeast(0L)
|
||||
gpsManager?.setPollingInterval(emitIntervalMs)
|
||||
}
|
||||
call.getInt("drivingEmitIntervalMs")?.let {
|
||||
drivingEmitIntervalMs = it.toLong().coerceAtLeast(200L)
|
||||
if (currentTrackingMode == GpsTrackingMode.DRIVING) {
|
||||
stopDrivingEmitLoop()
|
||||
startDrivingEmitLoop()
|
||||
}
|
||||
}
|
||||
call.getInt("wifiScanIntervalMs")?.let {
|
||||
wifiScanIntervalMs = it.toLong().coerceAtLeast(1000L)
|
||||
}
|
||||
call.getBoolean("enableWifiRtt")?.let {
|
||||
enableWifiRtt = it
|
||||
wifiManager?.setEnableRtt(it)
|
||||
}
|
||||
call.getBoolean("enableLogging")?.let { LogUtils.enabled = it }
|
||||
call.getBoolean("enableForwardPrediction")?.let { enableForwardPrediction = it }
|
||||
call.getDouble("maxPredictionSeconds")?.let { maxPredictionSeconds = it.coerceIn(0.0, 5.0) }
|
||||
call.getBoolean("emitGnssStatus")?.let { emitGnssStatus = it }
|
||||
call.getBoolean("suppressMockedUpdates")?.let { suppressMockedUpdates = it }
|
||||
call.getBoolean("keepScreenOn")?.let {
|
||||
keepScreenOn = it
|
||||
applyKeepScreenOn(keepScreenOn)
|
||||
}
|
||||
call.resolve()
|
||||
}
|
||||
|
||||
private fun applyKeepScreenOn(enabled: Boolean) {
|
||||
val webView = bridge?.webView
|
||||
val activity = bridge?.activity
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
webView?.keepScreenOn = enabled
|
||||
val window = activity?.window
|
||||
if (window != null) {
|
||||
if (enabled) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
fun getGnssStatus(call: PluginCall) {
|
||||
val status = satelliteStatus
|
||||
if (status == null) {
|
||||
call.resolve(JSObject())
|
||||
return
|
||||
}
|
||||
val obj = JSObject()
|
||||
obj.put("satellitesInView", status.satellitesInView)
|
||||
obj.put("usedInFix", status.usedInFix)
|
||||
val counts = JSObject()
|
||||
status.constellationCounts.forEach { (k, v) -> counts.put(k, v) }
|
||||
obj.put("constellationCounts", counts)
|
||||
call.resolve(obj)
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
fun getLocationServicesStatus(call: PluginCall) {
|
||||
val lm = context.getSystemService(android.content.Context.LOCATION_SERVICE) as android.location.LocationManager
|
||||
val gpsEnabled = try { lm.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER) } catch (_: Exception) { false }
|
||||
val netEnabled = try { lm.isProviderEnabled(android.location.LocationManager.NETWORK_PROVIDER) } catch (_: Exception) { false }
|
||||
val obj = JSObject().apply {
|
||||
put("gpsEnabled", gpsEnabled)
|
||||
put("networkEnabled", netEnabled)
|
||||
}
|
||||
call.resolve(obj)
|
||||
}
|
||||
|
||||
private fun emitPositionUpdate(forceEmit: Boolean = false) {
|
||||
val now = System.currentTimeMillis()
|
||||
if (!forceEmit && now - lastEmitTimestamp < emitIntervalMs) return
|
||||
if (suppressMockedUpdates && isMockedLocation) return
|
||||
|
||||
val distance = calculateDistance(latestLatitude, latestLongitude, prevLatitude, prevLongitude)
|
||||
val speedNow = latestImu?.speed ?: 0f
|
||||
@ -330,9 +442,12 @@ class DumonGeolocation : Plugin() {
|
||||
prevDirection = directionNow
|
||||
lastEmitTimestamp = now
|
||||
|
||||
// Ensure listener notifications run on the main thread for consistency
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
notifyListeners("onPositionUpdate", buildPositionData())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun adjustIntervalAndSensorRate(speed: Float) {
|
||||
val targetInterval = when {
|
||||
@ -345,7 +460,7 @@ class DumonGeolocation : Plugin() {
|
||||
if (emitIntervalMs != targetInterval) {
|
||||
emitIntervalMs = targetInterval
|
||||
gpsManager?.setPollingInterval(targetInterval)
|
||||
Log.d("DUMON_GEOLOCATION", "Auto-set emitIntervalMs = $emitIntervalMs ms")
|
||||
LogUtils.d("DUMON_GEOLOCATION", "Auto-set emitIntervalMs = $emitIntervalMs ms")
|
||||
}
|
||||
|
||||
imuManager?.setSensorDelayBySpeed(speed)
|
||||
@ -369,23 +484,67 @@ class DumonGeolocation : Plugin() {
|
||||
return R * c
|
||||
}
|
||||
|
||||
private fun buildGnssStatusData(status: SatelliteStatus): JSObject {
|
||||
val obj = JSObject()
|
||||
obj.put("satellitesInView", status.satellitesInView)
|
||||
obj.put("usedInFix", status.usedInFix)
|
||||
val counts = JSObject()
|
||||
status.constellationCounts.forEach { (k, v) -> counts.put(k, v) }
|
||||
obj.put("constellationCounts", counts)
|
||||
return obj
|
||||
}
|
||||
|
||||
private fun buildPositionData(): JSObject {
|
||||
val obj = JSObject()
|
||||
val now = System.currentTimeMillis()
|
||||
|
||||
var outLat = latestLatitude
|
||||
var outLon = latestLongitude
|
||||
var predicted = false
|
||||
|
||||
val imu = latestImu
|
||||
val dtSec = ((now - (if (latestTimestamp > 0) latestTimestamp else now)).toDouble() / 1000.0)
|
||||
if (enableForwardPrediction && imu != null && !isMockedLocation && dtSec > 0) {
|
||||
val clampedDt = dtSec.coerceAtMost(maxPredictionSeconds)
|
||||
val speed = imu.speed
|
||||
val dir = imu.directionRad
|
||||
val dNorth = speed * clampedDt * kotlin.math.cos(dir)
|
||||
val dEast = speed * clampedDt * kotlin.math.sin(dir)
|
||||
val R = 6371000.0
|
||||
val latRad = degToRad(latestLatitude)
|
||||
val dLat = (dNorth / R) * (180.0 / PI)
|
||||
val dLon = (dEast / (R * kotlin.math.cos(latRad))) * (180.0 / PI)
|
||||
outLat = latestLatitude + dLat
|
||||
outLon = latestLongitude + dLon
|
||||
predicted = true
|
||||
}
|
||||
|
||||
obj.put("source", latestSource)
|
||||
obj.put("timestamp", if (latestTimestamp > 0) latestTimestamp else System.currentTimeMillis())
|
||||
obj.put("latitude", latestLatitude)
|
||||
obj.put("longitude", latestLongitude)
|
||||
obj.put("timestamp", if (latestTimestamp > 0) latestTimestamp else now)
|
||||
obj.put("latitude", outLat)
|
||||
obj.put("longitude", outLon)
|
||||
obj.put("accuracy", latestAccuracy)
|
||||
obj.put("isMocked", isMockedLocation)
|
||||
|
||||
latestImu?.let {
|
||||
obj.put("speed", it.speed)
|
||||
obj.put("acceleration", it.acceleration)
|
||||
obj.put("directionRad", it.directionRad)
|
||||
}
|
||||
// Always provide IMU-related fields to match TS definitions
|
||||
val speedVal = latestImu?.speed ?: 0f
|
||||
val accelVal = latestImu?.acceleration ?: 0f
|
||||
val dirVal = latestImu?.directionRad ?: 0f
|
||||
obj.put("speed", speedVal)
|
||||
obj.put("acceleration", accelVal)
|
||||
obj.put("directionRad", dirVal)
|
||||
|
||||
obj.put("predicted", latestSource == "PREDICTED")
|
||||
obj.put("predicted", predicted)
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
override fun handleOnDestroy() {
|
||||
gpsManager?.stop()
|
||||
imuManager?.stop()
|
||||
wifiManager?.stopPeriodicScan()
|
||||
stopDrivingEmitLoop()
|
||||
applyKeepScreenOn(false)
|
||||
super.handleOnDestroy()
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,9 @@ import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.text.format.DateFormat
|
||||
import android.util.Log
|
||||
import com.dumon.plugin.geolocation.utils.LogUtils
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
enum class GpsTrackingMode {
|
||||
NORMAL,
|
||||
@ -73,7 +75,7 @@ class GpsStatusManager(
|
||||
}
|
||||
|
||||
val info = "$providerTag Lat: ${location.latitude}, Lon: ${location.longitude}, Acc: ${location.accuracy} m @ $timestamp | Mock=$isMocked"
|
||||
Log.d("GPS_LOCATION", info)
|
||||
LogUtils.d("GPS_LOCATION", info)
|
||||
|
||||
onLocationUpdate(location, isMocked)
|
||||
locationManager.removeUpdates(this)
|
||||
@ -92,7 +94,7 @@ class GpsStatusManager(
|
||||
oneShotListener
|
||||
)
|
||||
} else {
|
||||
Log.e("GPS_STATUS", "Missing location permission")
|
||||
LogUtils.e("GPS_STATUS", "Missing location permission")
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,6 +108,10 @@ class GpsStatusManager(
|
||||
|
||||
fun setPollingInterval(intervalMs: Long) {
|
||||
this.pollingIntervalMs = intervalMs
|
||||
if (isPolling) {
|
||||
handler.removeCallbacks(pollingRunnable)
|
||||
handler.postDelayed(pollingRunnable, pollingIntervalMs)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@ -114,11 +120,18 @@ class GpsStatusManager(
|
||||
currentMode = mode
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.e("GPS_STATUS", "Missing location permissions")
|
||||
LogUtils.e("GPS_STATUS", "Missing location permissions")
|
||||
return
|
||||
}
|
||||
|
||||
locationManager.registerGnssStatusCallback(gnssStatusCallback, null)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
locationManager.registerGnssStatusCallback(
|
||||
ContextCompat.getMainExecutor(context),
|
||||
gnssStatusCallback
|
||||
)
|
||||
} else {
|
||||
locationManager.registerGnssStatusCallback(gnssStatusCallback, handler)
|
||||
}
|
||||
|
||||
if (mode == GpsTrackingMode.DRIVING) {
|
||||
startContinuousUpdates()
|
||||
@ -139,12 +152,12 @@ class GpsStatusManager(
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("GPS_STATUS", "GPS started with mode: $mode")
|
||||
LogUtils.d("GPS_STATUS", "GPS started with mode: $mode")
|
||||
}
|
||||
|
||||
private fun startContinuousUpdates() {
|
||||
if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.e("GPS_STATUS", "Missing ACCESS_FINE_LOCATION permission")
|
||||
LogUtils.e("GPS_STATUS", "Missing ACCESS_FINE_LOCATION permission")
|
||||
return
|
||||
}
|
||||
|
||||
@ -166,7 +179,7 @@ class GpsStatusManager(
|
||||
continuousListener!!
|
||||
)
|
||||
|
||||
Log.d("GPS_STATUS", "Started continuous location updates")
|
||||
LogUtils.d("GPS_STATUS", "Started continuous location updates")
|
||||
}
|
||||
|
||||
fun startContinuousMode() {
|
||||
@ -188,7 +201,7 @@ class GpsStatusManager(
|
||||
locationManager.removeUpdates(it)
|
||||
continuousListener = null
|
||||
}
|
||||
Log.d("GPS_STATUS", "GPS stopped for mode: $currentMode")
|
||||
LogUtils.d("GPS_STATUS", "GPS stopped for mode: $currentMode")
|
||||
}
|
||||
|
||||
private fun getConstellationName(type: Int): String {
|
||||
|
||||
@ -3,6 +3,7 @@ package com.dumon.plugin.geolocation.imu
|
||||
import android.content.Context
|
||||
import android.hardware.*
|
||||
import android.util.Log
|
||||
import com.dumon.plugin.geolocation.utils.LogUtils
|
||||
import kotlin.math.*
|
||||
|
||||
class ImuSensorManager(
|
||||
@ -35,12 +36,12 @@ class ImuSensorManager(
|
||||
accelerometer?.let { sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_GAME) }
|
||||
gyroscope?.let { sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_GAME) }
|
||||
rotationVector?.let { sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_GAME) }
|
||||
Log.d("IMU_SENSOR", "IMU sensor tracking started")
|
||||
LogUtils.d("IMU_SENSOR", "IMU sensor tracking started")
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
sensorManager.unregisterListener(this)
|
||||
Log.d("IMU_SENSOR", "IMU sensor tracking stopped")
|
||||
LogUtils.d("IMU_SENSOR", "IMU sensor tracking stopped")
|
||||
}
|
||||
|
||||
fun setSensorDelayBySpeed(speed: Float) {
|
||||
@ -54,7 +55,7 @@ class ImuSensorManager(
|
||||
if (desiredDelay != currentDelay) {
|
||||
currentDelay = desiredDelay
|
||||
restartSensorsWithDelay(currentDelay)
|
||||
Log.d("IMU_SENSOR", "Sensor delay changed to $currentDelay")
|
||||
LogUtils.d("IMU_SENSOR", "Sensor delay changed to $currentDelay")
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,7 +111,7 @@ class ImuSensorManager(
|
||||
|
||||
emitCombinedImuData(speed, acceleration, latestDirectionRad)
|
||||
|
||||
Log.d("IMU_SENSOR", "Accel x: %.3f y: %.3f z: %.3f | Speed: %.3f | AccelMag: %.3f | Dir: %.2f rad".format(
|
||||
LogUtils.d("IMU_SENSOR", "Accel x: %.3f y: %.3f z: %.3f | Speed: %.3f | AccelMag: %.3f | Dir: %.2f rad".format(
|
||||
lastAccel[0], lastAccel[1], lastAccel[2], speed, acceleration, latestDirectionRad
|
||||
))
|
||||
}
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package com.dumon.plugin.geolocation.utils
|
||||
|
||||
import android.util.Log
|
||||
|
||||
object LogUtils {
|
||||
@Volatile
|
||||
var enabled: Boolean = true
|
||||
|
||||
fun d(tag: String, msg: String) {
|
||||
if (enabled) Log.d(tag, msg)
|
||||
}
|
||||
|
||||
fun w(tag: String, msg: String) {
|
||||
if (enabled) Log.w(tag, msg)
|
||||
}
|
||||
|
||||
fun e(tag: String, msg: String, tr: Throwable? = null) {
|
||||
if (!enabled) return
|
||||
if (tr != null) Log.e(tag, msg, tr) else Log.e(tag, msg)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,9 @@ package com.dumon.plugin.geolocation.wifi
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.wifi.ScanResult
|
||||
import android.net.wifi.WifiManager
|
||||
@ -10,8 +13,9 @@ import android.net.wifi.rtt.RangingResultCallback
|
||||
import android.net.wifi.rtt.WifiRttManager
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
import com.dumon.plugin.geolocation.utils.LogUtils
|
||||
import androidx.core.app.ActivityCompat
|
||||
import java.util.concurrent.Executors
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
class WifiPositioningManager(
|
||||
private val context: Context,
|
||||
@ -30,6 +34,9 @@ class WifiPositioningManager(
|
||||
private var isScanning = false
|
||||
|
||||
private var lastWifiScanAps: MutableList<WifiAp> = mutableListOf()
|
||||
private var scanResultsReceiver: BroadcastReceiver? = null
|
||||
private var scanReceiverRegistered: Boolean = false
|
||||
private var enableRtt: Boolean = true
|
||||
|
||||
fun isRttSupported(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && wifiRttManager != null && wifiManager.isWifiEnabled
|
||||
@ -37,6 +44,7 @@ class WifiPositioningManager(
|
||||
|
||||
fun startPeriodicScan(intervalMs: Long = 3000L) {
|
||||
isScanning = true
|
||||
registerScanResultsReceiverIfNeeded()
|
||||
handler.post(object : Runnable {
|
||||
override fun run() {
|
||||
if (isScanning) {
|
||||
@ -50,6 +58,7 @@ class WifiPositioningManager(
|
||||
fun stopPeriodicScan() {
|
||||
isScanning = false
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
unregisterScanResultsReceiverIfNeeded()
|
||||
}
|
||||
|
||||
fun startWifiScan() {
|
||||
@ -59,19 +68,47 @@ class WifiPositioningManager(
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
val success = wifiManager.startScan()
|
||||
if (success) {
|
||||
if (!success) {
|
||||
LogUtils.e("WIFI_POSITION", "Wi-Fi scan failed")
|
||||
onWifiPositioningUpdate(WifiScanResult(0, emptyList()))
|
||||
}
|
||||
} else {
|
||||
LogUtils.e("WIFI_POSITION", "Missing ACCESS_FINE_LOCATION permission")
|
||||
onWifiPositioningUpdate(WifiScanResult(0, emptyList()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerScanResultsReceiverIfNeeded() {
|
||||
if (scanReceiverRegistered) return
|
||||
scanResultsReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.action == WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) {
|
||||
val results = wifiManager.scanResults
|
||||
processScanResults(results)
|
||||
if (isRttSupported()) {
|
||||
if (enableRtt && isRttSupported()) {
|
||||
startRttRanging(results)
|
||||
}
|
||||
} else {
|
||||
Log.e("WIFI_POSITION", "Wi-Fi scan failed")
|
||||
onWifiPositioningUpdate(WifiScanResult(0, emptyList()))
|
||||
}
|
||||
} else {
|
||||
Log.e("WIFI_POSITION", "Missing ACCESS_FINE_LOCATION permission")
|
||||
onWifiPositioningUpdate(WifiScanResult(0, emptyList()))
|
||||
}
|
||||
}
|
||||
val filter = IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
|
||||
try {
|
||||
context.registerReceiver(scanResultsReceiver, filter)
|
||||
scanReceiverRegistered = true
|
||||
} catch (e: Exception) {
|
||||
LogUtils.e("WIFI_POSITION", "Failed to register scan receiver", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun unregisterScanResultsReceiverIfNeeded() {
|
||||
if (!scanReceiverRegistered) return
|
||||
try {
|
||||
context.unregisterReceiver(scanResultsReceiver)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// Receiver already unregistered
|
||||
} finally {
|
||||
scanReceiverRegistered = false
|
||||
scanResultsReceiver = null
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,9 +121,9 @@ class WifiPositioningManager(
|
||||
)
|
||||
}.toMutableList()
|
||||
|
||||
Log.d("WIFI_POSITION", "Wi-Fi scan → AP count: ${lastWifiScanAps.size}")
|
||||
LogUtils.d("WIFI_POSITION", "Wi-Fi scan → AP count: ${lastWifiScanAps.size}")
|
||||
lastWifiScanAps.forEach {
|
||||
Log.d("WIFI_POSITION", "SSID: ${it.ssid}, BSSID: ${it.bssid}, RSSI: ${it.rssi} dBm")
|
||||
LogUtils.d("WIFI_POSITION", "SSID: ${it.ssid}, BSSID: ${it.bssid}, RSSI: ${it.rssi} dBm")
|
||||
}
|
||||
|
||||
onWifiPositioningUpdate(WifiScanResult(lastWifiScanAps.size, lastWifiScanAps))
|
||||
@ -94,13 +131,13 @@ class WifiPositioningManager(
|
||||
|
||||
private fun startRttRanging(results: List<ScanResult>) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P || wifiRttManager == null) {
|
||||
Log.d("WIFI_POSITION", "RTT not supported or WifiRttManager is null.")
|
||||
LogUtils.d("WIFI_POSITION", "RTT not supported or WifiRttManager is null.")
|
||||
return
|
||||
}
|
||||
|
||||
val rttCapableAps = results.filter { it.is80211mcResponder }
|
||||
if (rttCapableAps.isEmpty()) {
|
||||
Log.d("WIFI_POSITION", "No RTT-capable AP found.")
|
||||
LogUtils.d("WIFI_POSITION", "No RTT-capable AP found.")
|
||||
return
|
||||
}
|
||||
|
||||
@ -111,10 +148,10 @@ class WifiPositioningManager(
|
||||
try {
|
||||
wifiRttManager.startRanging(
|
||||
rangingRequest,
|
||||
Executors.newSingleThreadExecutor(),
|
||||
ContextCompat.getMainExecutor(context),
|
||||
object : RangingResultCallback() {
|
||||
override fun onRangingFailure(code: Int) {
|
||||
Log.e("WIFI_POSITION", "RTT Ranging failed: $code")
|
||||
LogUtils.e("WIFI_POSITION", "RTT Ranging failed: $code")
|
||||
}
|
||||
|
||||
override fun onRangingResults(results: List<android.net.wifi.rtt.RangingResult>) {
|
||||
@ -123,7 +160,7 @@ class WifiPositioningManager(
|
||||
val distance = if (result.status == android.net.wifi.rtt.RangingResult.STATUS_SUCCESS) {
|
||||
result.distanceMm / 1000.0
|
||||
} else {
|
||||
Log.w("WIFI_POSITION", "RTT distance unavailable for ${result.macAddress}")
|
||||
LogUtils.w("WIFI_POSITION", "RTT distance unavailable for ${result.macAddress}")
|
||||
null
|
||||
}
|
||||
|
||||
@ -131,7 +168,7 @@ class WifiPositioningManager(
|
||||
lastWifiScanAps[idx] = lastWifiScanAps[idx].copy(distance = distance)
|
||||
}
|
||||
|
||||
Log.d(
|
||||
LogUtils.d(
|
||||
"WIFI_POSITION",
|
||||
if (distance != null)
|
||||
"RTT → ${mac}, Distance: ${distance} m"
|
||||
@ -145,9 +182,13 @@ class WifiPositioningManager(
|
||||
}
|
||||
)
|
||||
} catch (e: SecurityException) {
|
||||
Log.e("WIFI_POSITION", "SecurityException: Missing NEARBY_WIFI_DEVICES permission", e)
|
||||
LogUtils.e("WIFI_POSITION", "SecurityException: Missing NEARBY_WIFI_DEVICES permission", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun setEnableRtt(enable: Boolean) {
|
||||
enableRtt = enable
|
||||
}
|
||||
}
|
||||
|
||||
data class WifiAp(
|
||||
|
||||
194
dist/docs.json
vendored
194
dist/docs.json
vendored
@ -49,6 +49,46 @@
|
||||
],
|
||||
"slug": "checkandrequestpermissions"
|
||||
},
|
||||
{
|
||||
"name": "setOptions",
|
||||
"signature": "(options: DumonGeoOptions) => Promise<void>",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "options",
|
||||
"docs": "",
|
||||
"type": "DumonGeoOptions"
|
||||
}
|
||||
],
|
||||
"returns": "Promise<void>",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [
|
||||
"DumonGeoOptions"
|
||||
],
|
||||
"slug": "setoptions"
|
||||
},
|
||||
{
|
||||
"name": "getGnssStatus",
|
||||
"signature": "() => Promise<SatelliteStatus | null>",
|
||||
"parameters": [],
|
||||
"returns": "Promise<SatelliteStatus | null>",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [
|
||||
"SatelliteStatus"
|
||||
],
|
||||
"slug": "getgnssstatus"
|
||||
},
|
||||
{
|
||||
"name": "getLocationServicesStatus",
|
||||
"signature": "() => Promise<{ gpsEnabled: boolean; networkEnabled: boolean; }>",
|
||||
"parameters": [],
|
||||
"returns": "Promise<{ gpsEnabled: boolean; networkEnabled: boolean; }>",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"slug": "getlocationservicesstatus"
|
||||
},
|
||||
{
|
||||
"name": "configureEdgeToEdge",
|
||||
"signature": "(options: { bgColor: string; style: 'DARK' | 'LIGHT'; overlay?: boolean; }) => Promise<void>",
|
||||
@ -104,6 +144,30 @@
|
||||
"PositioningData"
|
||||
],
|
||||
"slug": "addlisteneronpositionupdate-"
|
||||
},
|
||||
{
|
||||
"name": "addListener",
|
||||
"signature": "(eventName: 'onGnssStatus', listenerFunc: (data: SatelliteStatus) => void) => PluginListenerHandle",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "eventName",
|
||||
"docs": "",
|
||||
"type": "'onGnssStatus'"
|
||||
},
|
||||
{
|
||||
"name": "listenerFunc",
|
||||
"docs": "",
|
||||
"type": "(data: SatelliteStatus) => void"
|
||||
}
|
||||
],
|
||||
"returns": "PluginListenerHandle",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [
|
||||
"PluginListenerHandle",
|
||||
"SatelliteStatus"
|
||||
],
|
||||
"slug": "addlistenerongnssstatus-"
|
||||
}
|
||||
],
|
||||
"properties": []
|
||||
@ -211,6 +275,136 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DumonGeoOptions",
|
||||
"slug": "dumongeooptions",
|
||||
"docs": "",
|
||||
"tags": [],
|
||||
"methods": [],
|
||||
"properties": [
|
||||
{
|
||||
"name": "distanceThresholdMeters",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "number | undefined"
|
||||
},
|
||||
{
|
||||
"name": "speedChangeThreshold",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "number | undefined"
|
||||
},
|
||||
{
|
||||
"name": "directionChangeThreshold",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "number | undefined"
|
||||
},
|
||||
{
|
||||
"name": "emitDebounceMs",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "number | undefined"
|
||||
},
|
||||
{
|
||||
"name": "drivingEmitIntervalMs",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "number | undefined"
|
||||
},
|
||||
{
|
||||
"name": "wifiScanIntervalMs",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "number | undefined"
|
||||
},
|
||||
{
|
||||
"name": "enableWifiRtt",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "boolean | undefined"
|
||||
},
|
||||
{
|
||||
"name": "enableLogging",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "boolean | undefined"
|
||||
},
|
||||
{
|
||||
"name": "enableForwardPrediction",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "boolean | undefined"
|
||||
},
|
||||
{
|
||||
"name": "maxPredictionSeconds",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "number | undefined"
|
||||
},
|
||||
{
|
||||
"name": "emitGnssStatus",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "boolean | undefined"
|
||||
},
|
||||
{
|
||||
"name": "suppressMockedUpdates",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "boolean | undefined"
|
||||
},
|
||||
{
|
||||
"name": "keepScreenOn",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "boolean | undefined"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SatelliteStatus",
|
||||
"slug": "satellitestatus",
|
||||
"docs": "",
|
||||
"tags": [],
|
||||
"methods": [],
|
||||
"properties": [
|
||||
{
|
||||
"name": "satellitesInView",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"name": "usedInFix",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"name": "constellationCounts",
|
||||
"tags": [],
|
||||
"docs": "",
|
||||
"complexTypes": [],
|
||||
"type": "{ [key: string]: number; }"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PluginListenerHandle",
|
||||
"slug": "pluginlistenerhandle",
|
||||
|
||||
29
dist/esm/definitions.d.ts
vendored
29
dist/esm/definitions.d.ts
vendored
@ -11,6 +11,28 @@ export interface PositioningData {
|
||||
isMocked: boolean;
|
||||
predicted?: boolean;
|
||||
}
|
||||
export interface SatelliteStatus {
|
||||
satellitesInView: number;
|
||||
usedInFix: number;
|
||||
constellationCounts: {
|
||||
[key: string]: number;
|
||||
};
|
||||
}
|
||||
export interface DumonGeoOptions {
|
||||
distanceThresholdMeters?: number;
|
||||
speedChangeThreshold?: number;
|
||||
directionChangeThreshold?: number;
|
||||
emitDebounceMs?: number;
|
||||
drivingEmitIntervalMs?: number;
|
||||
wifiScanIntervalMs?: number;
|
||||
enableWifiRtt?: boolean;
|
||||
enableLogging?: boolean;
|
||||
enableForwardPrediction?: boolean;
|
||||
maxPredictionSeconds?: number;
|
||||
emitGnssStatus?: boolean;
|
||||
suppressMockedUpdates?: boolean;
|
||||
keepScreenOn?: boolean;
|
||||
}
|
||||
export interface PermissionStatus {
|
||||
location: 'granted' | 'denied';
|
||||
wifi: 'granted' | 'denied';
|
||||
@ -20,6 +42,12 @@ export interface DumonGeolocationPlugin {
|
||||
stopPositioning(): Promise<void>;
|
||||
getLatestPosition(): Promise<PositioningData>;
|
||||
checkAndRequestPermissions(): Promise<PermissionStatus>;
|
||||
setOptions(options: DumonGeoOptions): Promise<void>;
|
||||
getGnssStatus(): Promise<SatelliteStatus | null>;
|
||||
getLocationServicesStatus(): Promise<{
|
||||
gpsEnabled: boolean;
|
||||
networkEnabled: boolean;
|
||||
}>;
|
||||
configureEdgeToEdge(options: {
|
||||
bgColor: string;
|
||||
style: 'DARK' | 'LIGHT';
|
||||
@ -29,4 +57,5 @@ export interface DumonGeolocationPlugin {
|
||||
mode: 'normal' | 'driving';
|
||||
}): Promise<void>;
|
||||
addListener(eventName: 'onPositionUpdate', listenerFunc: (data: PositioningData) => void): PluginListenerHandle;
|
||||
addListener(eventName: 'onGnssStatus', listenerFunc: (data: SatelliteStatus) => void): PluginListenerHandle;
|
||||
}
|
||||
|
||||
2
dist/esm/definitions.js.map
vendored
2
dist/esm/definitions.js.map
vendored
@ -1 +1 @@
|
||||
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n// export interface SatelliteStatus {\n// satellitesInView: number;\n// usedInFix: number;\n// constellationCounts: { [key: string]: number };\n// }\n\n// export interface WifiAp {\n// ssid: string;\n// bssid: string;\n// rssi: number;\n// distance?: number;\n// }\n\n// export interface WifiScanResult {\n// apCount: number;\n// aps: WifiAp[];\n// }\n\n// export interface ImuData {\n// accelX: number;\n// accelY: number;\n// accelZ: number;\n// gyroX: number;\n// gyroY: number;\n// gyroZ: number;\n// speed?: number;\n// acceleration?: number;\n// directionRad?: number;\n// }\n\n// export interface GpsData {\n// latitude: number;\n// longitude: number;\n// accuracy: number;\n// satellitesInView?: number;\n// usedInFix?: number;\n// constellationCounts?: { [key: string]: number };\n// }\n\n// export interface PositioningData {\n// source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';\n// timestamp: number;\n// latitude: number;\n// longitude: number;\n// accuracy: number;\n\n// gnssData?: SatelliteStatus;\n// wifiData?: WifiAp[];\n// imuData?: ImuData;\n// }\n\nexport interface PositioningData {\n source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';\n timestamp: number;\n latitude: number;\n longitude: number;\n accuracy: number;\n speed: number;\n acceleration: number;\n directionRad: number;\n isMocked: boolean;\n predicted?: boolean;\n}\n\nexport interface PermissionStatus {\n location: 'granted' | 'denied';\n wifi: 'granted' | 'denied';\n}\n\nexport interface DumonGeolocationPlugin {\n startPositioning(): Promise<void>;\n stopPositioning(): Promise<void>;\n getLatestPosition(): Promise<PositioningData>;\n checkAndRequestPermissions(): Promise<PermissionStatus>;\n\n configureEdgeToEdge(options: {\n bgColor: string;\n style: 'DARK' | 'LIGHT';\n overlay?: boolean;\n }): Promise<void>;\n\n setGpsMode(options: { mode: 'normal' | 'driving' }): Promise<void>;\n\n addListener(\n eventName: 'onPositionUpdate',\n listenerFunc: (data: PositioningData) => void\n ): PluginListenerHandle;\n}"]}
|
||||
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n// export interface SatelliteStatus {\n// satellitesInView: number;\n// usedInFix: number;\n// constellationCounts: { [key: string]: number };\n// }\n\n// export interface WifiAp {\n// ssid: string;\n// bssid: string;\n// rssi: number;\n// distance?: number;\n// }\n\n// export interface WifiScanResult {\n// apCount: number;\n// aps: WifiAp[];\n// }\n\n// export interface ImuData {\n// accelX: number;\n// accelY: number;\n// accelZ: number;\n// gyroX: number;\n// gyroY: number;\n// gyroZ: number;\n// speed?: number;\n// acceleration?: number;\n// directionRad?: number;\n// }\n\n// export interface GpsData {\n// latitude: number;\n// longitude: number;\n// accuracy: number;\n// satellitesInView?: number;\n// usedInFix?: number;\n// constellationCounts?: { [key: string]: number };\n// }\n\n// export interface PositioningData {\n// source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';\n// timestamp: number;\n// latitude: number;\n// longitude: number;\n// accuracy: number;\n\n// gnssData?: SatelliteStatus;\n// wifiData?: WifiAp[];\n// imuData?: ImuData;\n// }\n\nexport interface PositioningData {\n source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';\n timestamp: number;\n latitude: number;\n longitude: number;\n accuracy: number;\n speed: number;\n acceleration: number;\n directionRad: number;\n isMocked: boolean;\n predicted?: boolean;\n}\n\nexport interface SatelliteStatus {\n satellitesInView: number;\n usedInFix: number;\n constellationCounts: { [key: string]: number };\n}\n\nexport interface DumonGeoOptions {\n distanceThresholdMeters?: number;\n speedChangeThreshold?: number;\n directionChangeThreshold?: number;\n emitDebounceMs?: number;\n drivingEmitIntervalMs?: number;\n wifiScanIntervalMs?: number;\n enableWifiRtt?: boolean;\n enableLogging?: boolean;\n enableForwardPrediction?: boolean;\n maxPredictionSeconds?: number;\n emitGnssStatus?: boolean;\n suppressMockedUpdates?: boolean;\n keepScreenOn?: boolean;\n}\n\nexport interface PermissionStatus {\n location: 'granted' | 'denied';\n wifi: 'granted' | 'denied';\n}\n\nexport interface DumonGeolocationPlugin {\n startPositioning(): Promise<void>;\n stopPositioning(): Promise<void>;\n getLatestPosition(): Promise<PositioningData>;\n checkAndRequestPermissions(): Promise<PermissionStatus>;\n setOptions(options: DumonGeoOptions): Promise<void>;\n getGnssStatus(): Promise<SatelliteStatus | null>;\n getLocationServicesStatus(): Promise<{ gpsEnabled: boolean; networkEnabled: boolean }>;\n\n configureEdgeToEdge(options: {\n bgColor: string;\n style: 'DARK' | 'LIGHT';\n overlay?: boolean;\n }): Promise<void>;\n\n setGpsMode(options: { mode: 'normal' | 'driving' }): Promise<void>;\n\n addListener(\n eventName: 'onPositionUpdate',\n listenerFunc: (data: PositioningData) => void\n ): PluginListenerHandle;\n\n addListener(\n eventName: 'onGnssStatus',\n listenerFunc: (data: SatelliteStatus) => void\n ): PluginListenerHandle;\n}\n"]}
|
||||
8
dist/esm/web.d.ts
vendored
8
dist/esm/web.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
import { WebPlugin } from '@capacitor/core';
|
||||
import type { PositioningData } from './definitions';
|
||||
import type { PositioningData, DumonGeoOptions, SatelliteStatus } from './definitions';
|
||||
export declare class DumonGeolocationWeb extends WebPlugin {
|
||||
startPositioning(): Promise<void>;
|
||||
stopPositioning(): Promise<void>;
|
||||
@ -13,4 +13,10 @@ export declare class DumonGeolocationWeb extends WebPlugin {
|
||||
style: 'DARK' | 'LIGHT';
|
||||
overlay?: boolean;
|
||||
}): Promise<void>;
|
||||
setOptions(_options: DumonGeoOptions): Promise<void>;
|
||||
getGnssStatus(): Promise<SatelliteStatus | null>;
|
||||
getLocationServicesStatus(): Promise<{
|
||||
gpsEnabled: boolean;
|
||||
networkEnabled: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
10
dist/esm/web.js
vendored
10
dist/esm/web.js
vendored
@ -31,5 +31,15 @@ export class DumonGeolocationWeb extends WebPlugin {
|
||||
console.info('[dumon-geolocation] configureEdgeToEdge called on web with:', options);
|
||||
// No-op
|
||||
}
|
||||
async setOptions(_options) {
|
||||
// No-op on web
|
||||
}
|
||||
async getGnssStatus() {
|
||||
return null;
|
||||
}
|
||||
async getLocationServicesStatus() {
|
||||
// Web stub; assume enabled
|
||||
return { gpsEnabled: true, networkEnabled: true };
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=web.js.map
|
||||
2
dist/esm/web.js.map
vendored
2
dist/esm/web.js.map
vendored
@ -1 +1 @@
|
||||
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,OAAO,mBAAoB,SAAQ,SAAS;IAChD,KAAK,CAAC,gBAAgB;QACpB,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;IACvE,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,OAAO,CAAC,GAAG,CAAC,wEAAwE,CAAC,CAAC;QACtF,OAAO;YACL,MAAM,EAAE,MAAM;YACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,QAAQ,EAAE,CAAC;YACX,SAAS,EAAE,CAAC;YACZ,QAAQ,EAAE,GAAG;YACb,KAAK,EAAE,CAAC;YACR,YAAY,EAAE,CAAC;YACf,YAAY,EAAE,CAAC;YACf,QAAQ,EAAE,KAAK;SAChB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,0BAA0B;QAI9B,OAAO,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;QAC/E,OAAO;YACL,QAAQ,EAAE,SAAS;YACnB,IAAI,EAAE,SAAS;SAChB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,OAIzB;QACC,OAAO,CAAC,IAAI,CAAC,6DAA6D,EAAE,OAAO,CAAC,CAAC;QACrF,QAAQ;IACV,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport type { PositioningData } from './definitions';\n\nexport class DumonGeolocationWeb extends WebPlugin {\n async startPositioning(): Promise<void> {\n console.log('DumonGeolocationWeb: startPositioning() called (no-op)');\n }\n\n async stopPositioning(): Promise<void> {\n console.log('DumonGeolocationWeb: stopPositioning() called (no-op)');\n }\n\n async getLatestPosition(): Promise<PositioningData> {\n console.log('DumonGeolocationWeb: getLatestPosition() called (returning dummy data)');\n return {\n source: 'GNSS',\n timestamp: Date.now(),\n latitude: 0,\n longitude: 0,\n accuracy: 999,\n speed: 0,\n acceleration: 0,\n directionRad: 0,\n isMocked: false,\n };\n }\n\n async checkAndRequestPermissions(): Promise<{\n location: 'granted' | 'denied';\n wifi: 'granted' | 'denied';\n }> {\n console.info('[dumon-geolocation] checkAndRequestPermissions mocked for web.');\n return {\n location: 'granted',\n wifi: 'granted',\n };\n }\n\n async configureEdgeToEdge(options: {\n bgColor: string;\n style: 'DARK' | 'LIGHT';\n overlay?: boolean;\n }): Promise<void> {\n console.info('[dumon-geolocation] configureEdgeToEdge called on web with:', options);\n // No-op\n }\n}"]}
|
||||
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,OAAO,mBAAoB,SAAQ,SAAS;IAChD,KAAK,CAAC,gBAAgB;QACpB,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;IACvE,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,OAAO,CAAC,GAAG,CAAC,wEAAwE,CAAC,CAAC;QACtF,OAAO;YACL,MAAM,EAAE,MAAM;YACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,QAAQ,EAAE,CAAC;YACX,SAAS,EAAE,CAAC;YACZ,QAAQ,EAAE,GAAG;YACb,KAAK,EAAE,CAAC;YACR,YAAY,EAAE,CAAC;YACf,YAAY,EAAE,CAAC;YACf,QAAQ,EAAE,KAAK;SAChB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,0BAA0B;QAI9B,OAAO,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;QAC/E,OAAO;YACL,QAAQ,EAAE,SAAS;YACnB,IAAI,EAAE,SAAS;SAChB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,OAIzB;QACC,OAAO,CAAC,IAAI,CAAC,6DAA6D,EAAE,OAAO,CAAC,CAAC;QACrF,QAAQ;IACV,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,QAAyB;QACxC,eAAe;IACjB,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,yBAAyB;QAC7B,2BAA2B;QAC3B,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;IACpD,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport type { PositioningData, DumonGeoOptions, SatelliteStatus } from './definitions';\n\nexport class DumonGeolocationWeb extends WebPlugin {\n async startPositioning(): Promise<void> {\n console.log('DumonGeolocationWeb: startPositioning() called (no-op)');\n }\n\n async stopPositioning(): Promise<void> {\n console.log('DumonGeolocationWeb: stopPositioning() called (no-op)');\n }\n\n async getLatestPosition(): Promise<PositioningData> {\n console.log('DumonGeolocationWeb: getLatestPosition() called (returning dummy data)');\n return {\n source: 'GNSS',\n timestamp: Date.now(),\n latitude: 0,\n longitude: 0,\n accuracy: 999,\n speed: 0,\n acceleration: 0,\n directionRad: 0,\n isMocked: false,\n };\n }\n\n async checkAndRequestPermissions(): Promise<{\n location: 'granted' | 'denied';\n wifi: 'granted' | 'denied';\n }> {\n console.info('[dumon-geolocation] checkAndRequestPermissions mocked for web.');\n return {\n location: 'granted',\n wifi: 'granted',\n };\n }\n\n async configureEdgeToEdge(options: {\n bgColor: string;\n style: 'DARK' | 'LIGHT';\n overlay?: boolean;\n }): Promise<void> {\n console.info('[dumon-geolocation] configureEdgeToEdge called on web with:', options);\n // No-op\n }\n\n async setOptions(_options: DumonGeoOptions): Promise<void> {\n // No-op on web\n }\n\n async getGnssStatus(): Promise<SatelliteStatus | null> {\n return null;\n }\n\n async getLocationServicesStatus(): Promise<{ gpsEnabled: boolean; networkEnabled: boolean }> {\n // Web stub; assume enabled\n return { gpsEnabled: true, networkEnabled: true };\n }\n}\n"]}
|
||||
10
dist/plugin.cjs.js
vendored
10
dist/plugin.cjs.js
vendored
@ -38,6 +38,16 @@ class DumonGeolocationWeb extends core.WebPlugin {
|
||||
console.info('[dumon-geolocation] configureEdgeToEdge called on web with:', options);
|
||||
// No-op
|
||||
}
|
||||
async setOptions(_options) {
|
||||
// No-op on web
|
||||
}
|
||||
async getGnssStatus() {
|
||||
return null;
|
||||
}
|
||||
async getLocationServicesStatus() {
|
||||
// Web stub; assume enabled
|
||||
return { gpsEnabled: true, networkEnabled: true };
|
||||
}
|
||||
}
|
||||
|
||||
var web = /*#__PURE__*/Object.freeze({
|
||||
|
||||
2
dist/plugin.cjs.js.map
vendored
2
dist/plugin.cjs.js.map
vendored
@ -1 +1 @@
|
||||
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst DumonGeolocation = registerPlugin('DumonGeolocation', {\n web: () => import('./web').then((m) => new m.DumonGeolocationWeb()),\n});\nexport * from './definitions';\nexport { DumonGeolocation };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class DumonGeolocationWeb extends WebPlugin {\n async startPositioning() {\n console.log('DumonGeolocationWeb: startPositioning() called (no-op)');\n }\n async stopPositioning() {\n console.log('DumonGeolocationWeb: stopPositioning() called (no-op)');\n }\n async getLatestPosition() {\n console.log('DumonGeolocationWeb: getLatestPosition() called (returning dummy data)');\n return {\n source: 'GNSS',\n timestamp: Date.now(),\n latitude: 0,\n longitude: 0,\n accuracy: 999,\n speed: 0,\n acceleration: 0,\n directionRad: 0,\n isMocked: false,\n };\n }\n async checkAndRequestPermissions() {\n console.info('[dumon-geolocation] checkAndRequestPermissions mocked for web.');\n return {\n location: 'granted',\n wifi: 'granted',\n };\n }\n async configureEdgeToEdge(options) {\n console.info('[dumon-geolocation] configureEdgeToEdge called on web with:', options);\n // No-op\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,gBAAgB,GAAGA,mBAAc,CAAC,kBAAkB,EAAE;AAC5D,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,mBAAmB,EAAE,CAAC;AACvE,CAAC;;ACFM,MAAM,mBAAmB,SAASC,cAAS,CAAC;AACnD,IAAI,MAAM,gBAAgB,GAAG;AAC7B,QAAQ,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC;AAC7E;AACA,IAAI,MAAM,eAAe,GAAG;AAC5B,QAAQ,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC;AAC5E;AACA,IAAI,MAAM,iBAAiB,GAAG;AAC9B,QAAQ,OAAO,CAAC,GAAG,CAAC,wEAAwE,CAAC;AAC7F,QAAQ,OAAO;AACf,YAAY,MAAM,EAAE,MAAM;AAC1B,YAAY,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;AACjC,YAAY,QAAQ,EAAE,CAAC;AACvB,YAAY,SAAS,EAAE,CAAC;AACxB,YAAY,QAAQ,EAAE,GAAG;AACzB,YAAY,KAAK,EAAE,CAAC;AACpB,YAAY,YAAY,EAAE,CAAC;AAC3B,YAAY,YAAY,EAAE,CAAC;AAC3B,YAAY,QAAQ,EAAE,KAAK;AAC3B,SAAS;AACT;AACA,IAAI,MAAM,0BAA0B,GAAG;AACvC,QAAQ,OAAO,CAAC,IAAI,CAAC,gEAAgE,CAAC;AACtF,QAAQ,OAAO;AACf,YAAY,QAAQ,EAAE,SAAS;AAC/B,YAAY,IAAI,EAAE,SAAS;AAC3B,SAAS;AACT;AACA,IAAI,MAAM,mBAAmB,CAAC,OAAO,EAAE;AACvC,QAAQ,OAAO,CAAC,IAAI,CAAC,6DAA6D,EAAE,OAAO,CAAC;AAC5F;AACA;AACA;;;;;;;;;"}
|
||||
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst DumonGeolocation = registerPlugin('DumonGeolocation', {\n web: () => import('./web').then((m) => new m.DumonGeolocationWeb()),\n});\nexport * from './definitions';\nexport { DumonGeolocation };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class DumonGeolocationWeb extends WebPlugin {\n async startPositioning() {\n console.log('DumonGeolocationWeb: startPositioning() called (no-op)');\n }\n async stopPositioning() {\n console.log('DumonGeolocationWeb: stopPositioning() called (no-op)');\n }\n async getLatestPosition() {\n console.log('DumonGeolocationWeb: getLatestPosition() called (returning dummy data)');\n return {\n source: 'GNSS',\n timestamp: Date.now(),\n latitude: 0,\n longitude: 0,\n accuracy: 999,\n speed: 0,\n acceleration: 0,\n directionRad: 0,\n isMocked: false,\n };\n }\n async checkAndRequestPermissions() {\n console.info('[dumon-geolocation] checkAndRequestPermissions mocked for web.');\n return {\n location: 'granted',\n wifi: 'granted',\n };\n }\n async configureEdgeToEdge(options) {\n console.info('[dumon-geolocation] configureEdgeToEdge called on web with:', options);\n // No-op\n }\n async setOptions(_options) {\n // No-op on web\n }\n async getGnssStatus() {\n return null;\n }\n async getLocationServicesStatus() {\n // Web stub; assume enabled\n return { gpsEnabled: true, networkEnabled: true };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,gBAAgB,GAAGA,mBAAc,CAAC,kBAAkB,EAAE;AAC5D,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,mBAAmB,EAAE,CAAC;AACvE,CAAC;;ACFM,MAAM,mBAAmB,SAASC,cAAS,CAAC;AACnD,IAAI,MAAM,gBAAgB,GAAG;AAC7B,QAAQ,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC;AAC7E;AACA,IAAI,MAAM,eAAe,GAAG;AAC5B,QAAQ,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC;AAC5E;AACA,IAAI,MAAM,iBAAiB,GAAG;AAC9B,QAAQ,OAAO,CAAC,GAAG,CAAC,wEAAwE,CAAC;AAC7F,QAAQ,OAAO;AACf,YAAY,MAAM,EAAE,MAAM;AAC1B,YAAY,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;AACjC,YAAY,QAAQ,EAAE,CAAC;AACvB,YAAY,SAAS,EAAE,CAAC;AACxB,YAAY,QAAQ,EAAE,GAAG;AACzB,YAAY,KAAK,EAAE,CAAC;AACpB,YAAY,YAAY,EAAE,CAAC;AAC3B,YAAY,YAAY,EAAE,CAAC;AAC3B,YAAY,QAAQ,EAAE,KAAK;AAC3B,SAAS;AACT;AACA,IAAI,MAAM,0BAA0B,GAAG;AACvC,QAAQ,OAAO,CAAC,IAAI,CAAC,gEAAgE,CAAC;AACtF,QAAQ,OAAO;AACf,YAAY,QAAQ,EAAE,SAAS;AAC/B,YAAY,IAAI,EAAE,SAAS;AAC3B,SAAS;AACT;AACA,IAAI,MAAM,mBAAmB,CAAC,OAAO,EAAE;AACvC,QAAQ,OAAO,CAAC,IAAI,CAAC,6DAA6D,EAAE,OAAO,CAAC;AAC5F;AACA;AACA,IAAI,MAAM,UAAU,CAAC,QAAQ,EAAE;AAC/B;AACA;AACA,IAAI,MAAM,aAAa,GAAG;AAC1B,QAAQ,OAAO,IAAI;AACnB;AACA,IAAI,MAAM,yBAAyB,GAAG;AACtC;AACA,QAAQ,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE;AACzD;AACA;;;;;;;;;"}
|
||||
10
dist/plugin.js
vendored
10
dist/plugin.js
vendored
@ -37,6 +37,16 @@ var capacitorDumonGeolocation = (function (exports, core) {
|
||||
console.info('[dumon-geolocation] configureEdgeToEdge called on web with:', options);
|
||||
// No-op
|
||||
}
|
||||
async setOptions(_options) {
|
||||
// No-op on web
|
||||
}
|
||||
async getGnssStatus() {
|
||||
return null;
|
||||
}
|
||||
async getLocationServicesStatus() {
|
||||
// Web stub; assume enabled
|
||||
return { gpsEnabled: true, networkEnabled: true };
|
||||
}
|
||||
}
|
||||
|
||||
var web = /*#__PURE__*/Object.freeze({
|
||||
|
||||
2
dist/plugin.js.map
vendored
2
dist/plugin.js.map
vendored
@ -1 +1 @@
|
||||
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst DumonGeolocation = registerPlugin('DumonGeolocation', {\n web: () => import('./web').then((m) => new m.DumonGeolocationWeb()),\n});\nexport * from './definitions';\nexport { DumonGeolocation };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class DumonGeolocationWeb extends WebPlugin {\n async startPositioning() {\n console.log('DumonGeolocationWeb: startPositioning() called (no-op)');\n }\n async stopPositioning() {\n console.log('DumonGeolocationWeb: stopPositioning() called (no-op)');\n }\n async getLatestPosition() {\n console.log('DumonGeolocationWeb: getLatestPosition() called (returning dummy data)');\n return {\n source: 'GNSS',\n timestamp: Date.now(),\n latitude: 0,\n longitude: 0,\n accuracy: 999,\n speed: 0,\n acceleration: 0,\n directionRad: 0,\n isMocked: false,\n };\n }\n async checkAndRequestPermissions() {\n console.info('[dumon-geolocation] checkAndRequestPermissions mocked for web.');\n return {\n location: 'granted',\n wifi: 'granted',\n };\n }\n async configureEdgeToEdge(options) {\n console.info('[dumon-geolocation] configureEdgeToEdge called on web with:', options);\n // No-op\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,gBAAgB,GAAGA,mBAAc,CAAC,kBAAkB,EAAE;IAC5D,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,mBAAmB,EAAE,CAAC;IACvE,CAAC;;ICFM,MAAM,mBAAmB,SAASC,cAAS,CAAC;IACnD,IAAI,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC;IAC7E;IACA,IAAI,MAAM,eAAe,GAAG;IAC5B,QAAQ,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC;IAC5E;IACA,IAAI,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,OAAO,CAAC,GAAG,CAAC,wEAAwE,CAAC;IAC7F,QAAQ,OAAO;IACf,YAAY,MAAM,EAAE,MAAM;IAC1B,YAAY,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;IACjC,YAAY,QAAQ,EAAE,CAAC;IACvB,YAAY,SAAS,EAAE,CAAC;IACxB,YAAY,QAAQ,EAAE,GAAG;IACzB,YAAY,KAAK,EAAE,CAAC;IACpB,YAAY,YAAY,EAAE,CAAC;IAC3B,YAAY,YAAY,EAAE,CAAC;IAC3B,YAAY,QAAQ,EAAE,KAAK;IAC3B,SAAS;IACT;IACA,IAAI,MAAM,0BAA0B,GAAG;IACvC,QAAQ,OAAO,CAAC,IAAI,CAAC,gEAAgE,CAAC;IACtF,QAAQ,OAAO;IACf,YAAY,QAAQ,EAAE,SAAS;IAC/B,YAAY,IAAI,EAAE,SAAS;IAC3B,SAAS;IACT;IACA,IAAI,MAAM,mBAAmB,CAAC,OAAO,EAAE;IACvC,QAAQ,OAAO,CAAC,IAAI,CAAC,6DAA6D,EAAE,OAAO,CAAC;IAC5F;IACA;IACA;;;;;;;;;;;;;;;"}
|
||||
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst DumonGeolocation = registerPlugin('DumonGeolocation', {\n web: () => import('./web').then((m) => new m.DumonGeolocationWeb()),\n});\nexport * from './definitions';\nexport { DumonGeolocation };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class DumonGeolocationWeb extends WebPlugin {\n async startPositioning() {\n console.log('DumonGeolocationWeb: startPositioning() called (no-op)');\n }\n async stopPositioning() {\n console.log('DumonGeolocationWeb: stopPositioning() called (no-op)');\n }\n async getLatestPosition() {\n console.log('DumonGeolocationWeb: getLatestPosition() called (returning dummy data)');\n return {\n source: 'GNSS',\n timestamp: Date.now(),\n latitude: 0,\n longitude: 0,\n accuracy: 999,\n speed: 0,\n acceleration: 0,\n directionRad: 0,\n isMocked: false,\n };\n }\n async checkAndRequestPermissions() {\n console.info('[dumon-geolocation] checkAndRequestPermissions mocked for web.');\n return {\n location: 'granted',\n wifi: 'granted',\n };\n }\n async configureEdgeToEdge(options) {\n console.info('[dumon-geolocation] configureEdgeToEdge called on web with:', options);\n // No-op\n }\n async setOptions(_options) {\n // No-op on web\n }\n async getGnssStatus() {\n return null;\n }\n async getLocationServicesStatus() {\n // Web stub; assume enabled\n return { gpsEnabled: true, networkEnabled: true };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,gBAAgB,GAAGA,mBAAc,CAAC,kBAAkB,EAAE;IAC5D,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,mBAAmB,EAAE,CAAC;IACvE,CAAC;;ICFM,MAAM,mBAAmB,SAASC,cAAS,CAAC;IACnD,IAAI,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC;IAC7E;IACA,IAAI,MAAM,eAAe,GAAG;IAC5B,QAAQ,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC;IAC5E;IACA,IAAI,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,OAAO,CAAC,GAAG,CAAC,wEAAwE,CAAC;IAC7F,QAAQ,OAAO;IACf,YAAY,MAAM,EAAE,MAAM;IAC1B,YAAY,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;IACjC,YAAY,QAAQ,EAAE,CAAC;IACvB,YAAY,SAAS,EAAE,CAAC;IACxB,YAAY,QAAQ,EAAE,GAAG;IACzB,YAAY,KAAK,EAAE,CAAC;IACpB,YAAY,YAAY,EAAE,CAAC;IAC3B,YAAY,YAAY,EAAE,CAAC;IAC3B,YAAY,QAAQ,EAAE,KAAK;IAC3B,SAAS;IACT;IACA,IAAI,MAAM,0BAA0B,GAAG;IACvC,QAAQ,OAAO,CAAC,IAAI,CAAC,gEAAgE,CAAC;IACtF,QAAQ,OAAO;IACf,YAAY,QAAQ,EAAE,SAAS;IAC/B,YAAY,IAAI,EAAE,SAAS;IAC3B,SAAS;IACT;IACA,IAAI,MAAM,mBAAmB,CAAC,OAAO,EAAE;IACvC,QAAQ,OAAO,CAAC,IAAI,CAAC,6DAA6D,EAAE,OAAO,CAAC;IAC5F;IACA;IACA,IAAI,MAAM,UAAU,CAAC,QAAQ,EAAE;IAC/B;IACA;IACA,IAAI,MAAM,aAAa,GAAG;IAC1B,QAAQ,OAAO,IAAI;IACnB;IACA,IAAI,MAAM,yBAAyB,GAAG;IACtC;IACA,QAAQ,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE;IACzD;IACA;;;;;;;;;;;;;;;"}
|
||||
2
example-app/package-lock.json
generated
2
example-app/package-lock.json
generated
@ -20,7 +20,7 @@
|
||||
}
|
||||
},
|
||||
"..": {
|
||||
"version": "0.0.1",
|
||||
"version": "1.0.2",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@capacitor/android": "^7.0.0",
|
||||
|
||||
@ -50,6 +50,40 @@
|
||||
<button id="startButton">Start Positioning</button>
|
||||
<button id="stopButton">Stop Positioning</button>
|
||||
<button id="getLatestButton">Get Latest Position</button>
|
||||
<button id="permButton">Request Permissions</button>
|
||||
<button id="clearLogButton" style="background-color:#999">Clear Log</button>
|
||||
|
||||
<h1>Modes</h1>
|
||||
<button id="drivingBtn">Driving Mode</button>
|
||||
<button id="normalBtn">Normal Mode</button>
|
||||
|
||||
<h1>Options</h1>
|
||||
<div style="display:flex;gap:1rem;flex-wrap:wrap">
|
||||
<div>
|
||||
<label><input type="checkbox" id="optEnableForwardPrediction" /> enableForwardPrediction</label><br />
|
||||
<label><input type="checkbox" id="optEmitGnssStatus" /> emitGnssStatus</label><br />
|
||||
<label><input type="checkbox" id="optEnableWifiRtt" checked /> enableWifiRtt</label><br />
|
||||
<label><input type="checkbox" id="optEnableLogging" checked /> enableLogging</label><br />
|
||||
<label><input type="checkbox" id="optSuppressMocked" /> suppressMockedUpdates</label><br />
|
||||
<label><input type="checkbox" id="optKeepScreenOn" /> keepScreenOn</label><br />
|
||||
</div>
|
||||
<div>
|
||||
<label>emitDebounceMs <input type="number" id="optEmitDebounceMs" placeholder="1000" /></label><br />
|
||||
<label>drivingEmitIntervalMs <input type="number" id="optDrivingEmitIntervalMs" placeholder="1600" /></label><br />
|
||||
<label>wifiScanIntervalMs <input type="number" id="optWifiScanIntervalMs" placeholder="3000" /></label><br />
|
||||
</div>
|
||||
<div>
|
||||
<label>distanceThresholdMeters <input type="number" step="0.1" id="optDistanceThreshold" placeholder="7.0" /></label><br />
|
||||
<label>speedChangeThreshold <input type="number" step="0.1" id="optSpeedThreshold" placeholder="0.5" /></label><br />
|
||||
<label>directionChangeThreshold <input type="number" step="0.01" id="optDirectionThreshold" placeholder="0.17" /></label><br />
|
||||
<label>maxPredictionSeconds <input type="number" step="0.1" id="optMaxPrediction" placeholder="1.0" /></label><br />
|
||||
</div>
|
||||
</div>
|
||||
<button id="applyOptionsButton" style="background-color:#2e7d32">Apply Options</button>
|
||||
|
||||
<h1>Diagnostics</h1>
|
||||
<button id="getGnssStatusButton">Get GNSS Status</button>
|
||||
<button id="getLocationServicesStatusButton">Get Location Services Status</button>
|
||||
|
||||
<pre id="logArea"></pre>
|
||||
|
||||
|
||||
@ -1,18 +1,99 @@
|
||||
import { DumonGeolocation } from 'dumon-geolocation';
|
||||
|
||||
const logArea = document.getElementById('logArea');
|
||||
let posListenerHandle = null;
|
||||
let gnssListenerHandle = null;
|
||||
|
||||
function appendLog(title, data) {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const formatted = `[${timestamp}] ${title}\n${JSON.stringify(data, null, 2)}\n\n`;
|
||||
logArea.textContent = formatted; // + logArea.textContent;
|
||||
logArea.textContent = formatted + logArea.textContent;
|
||||
}
|
||||
|
||||
function clearLog() {
|
||||
logArea.textContent = '';
|
||||
}
|
||||
|
||||
function readNumber(id) {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) return undefined;
|
||||
const v = el.value.trim();
|
||||
if (v === '') return undefined;
|
||||
const n = Number(v);
|
||||
return Number.isFinite(n) ? n : undefined;
|
||||
}
|
||||
|
||||
function readBool(id) {
|
||||
const el = document.getElementById(id);
|
||||
return !!el?.checked;
|
||||
}
|
||||
|
||||
async function applyOptionsFromForm() {
|
||||
const options = {};
|
||||
|
||||
// Booleans
|
||||
if (document.getElementById('optEnableForwardPrediction'))
|
||||
options.enableForwardPrediction = readBool('optEnableForwardPrediction');
|
||||
if (document.getElementById('optEmitGnssStatus'))
|
||||
options.emitGnssStatus = readBool('optEmitGnssStatus');
|
||||
if (document.getElementById('optEnableWifiRtt'))
|
||||
options.enableWifiRtt = readBool('optEnableWifiRtt');
|
||||
if (document.getElementById('optEnableLogging'))
|
||||
options.enableLogging = readBool('optEnableLogging');
|
||||
if (document.getElementById('optSuppressMocked'))
|
||||
options.suppressMockedUpdates = readBool('optSuppressMocked');
|
||||
if (document.getElementById('optKeepScreenOn'))
|
||||
options.keepScreenOn = readBool('optKeepScreenOn');
|
||||
|
||||
// Numbers
|
||||
const emitDebounceMs = readNumber('optEmitDebounceMs');
|
||||
const drivingEmitIntervalMs = readNumber('optDrivingEmitIntervalMs');
|
||||
const wifiScanIntervalMs = readNumber('optWifiScanIntervalMs');
|
||||
const distanceThresholdMeters = readNumber('optDistanceThreshold');
|
||||
const speedChangeThreshold = readNumber('optSpeedThreshold');
|
||||
const directionChangeThreshold = readNumber('optDirectionThreshold');
|
||||
const maxPredictionSeconds = readNumber('optMaxPrediction');
|
||||
|
||||
if (emitDebounceMs !== undefined) options.emitDebounceMs = emitDebounceMs;
|
||||
if (drivingEmitIntervalMs !== undefined) options.drivingEmitIntervalMs = drivingEmitIntervalMs;
|
||||
if (wifiScanIntervalMs !== undefined) options.wifiScanIntervalMs = wifiScanIntervalMs;
|
||||
if (distanceThresholdMeters !== undefined)
|
||||
options.distanceThresholdMeters = distanceThresholdMeters;
|
||||
if (speedChangeThreshold !== undefined) options.speedChangeThreshold = speedChangeThreshold;
|
||||
if (directionChangeThreshold !== undefined)
|
||||
options.directionChangeThreshold = directionChangeThreshold;
|
||||
if (maxPredictionSeconds !== undefined) options.maxPredictionSeconds = maxPredictionSeconds;
|
||||
|
||||
await DumonGeolocation.setOptions(options);
|
||||
appendLog('setOptions', options);
|
||||
|
||||
// Manage GNSS status listener dynamically
|
||||
const wantGnss = !!options.emitGnssStatus;
|
||||
if (wantGnss && !gnssListenerHandle) {
|
||||
gnssListenerHandle = DumonGeolocation.addListener('onGnssStatus', (data) => {
|
||||
appendLog('onGnssStatus', data);
|
||||
});
|
||||
} else if (!wantGnss && gnssListenerHandle) {
|
||||
await gnssListenerHandle.remove();
|
||||
gnssListenerHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function requestPermissions() {
|
||||
try {
|
||||
const perm = await DumonGeolocation.checkAndRequestPermissions();
|
||||
appendLog('permissions', perm);
|
||||
} catch (err) {
|
||||
appendLog('permissions', { error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
async function startGeolocation() {
|
||||
DumonGeolocation.addListener('onPositionUpdate', (data) => {
|
||||
if (!posListenerHandle) {
|
||||
posListenerHandle = DumonGeolocation.addListener('onPositionUpdate', (data) => {
|
||||
appendLog('onPositionUpdate', data);
|
||||
});
|
||||
|
||||
}
|
||||
try {
|
||||
await DumonGeolocation.startPositioning();
|
||||
appendLog('startPositioning', { success: true });
|
||||
@ -39,15 +120,58 @@ async function getLatestPosition() {
|
||||
}
|
||||
}
|
||||
|
||||
async function setDrivingMode() {
|
||||
try {
|
||||
await DumonGeolocation.setGpsMode({ mode: 'driving' });
|
||||
appendLog('setGpsMode', { mode: 'driving' });
|
||||
} catch (err) {
|
||||
appendLog('setGpsMode', { error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
async function setNormalMode() {
|
||||
try {
|
||||
await DumonGeolocation.setGpsMode({ mode: 'normal' });
|
||||
appendLog('setGpsMode', { mode: 'normal' });
|
||||
} catch (err) {
|
||||
appendLog('setGpsMode', { error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
async function getGnssStatus() {
|
||||
try {
|
||||
const data = await DumonGeolocation.getGnssStatus();
|
||||
appendLog('getGnssStatus', data);
|
||||
} catch (err) {
|
||||
appendLog('getGnssStatus', { error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
async function getLocationServicesStatus() {
|
||||
try {
|
||||
const data = await DumonGeolocation.getLocationServicesStatus();
|
||||
appendLog('getLocationServicesStatus', data);
|
||||
} catch (err) {
|
||||
appendLog('getLocationServicesStatus', { error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', async () => {
|
||||
document.getElementById('startButton').addEventListener('click', startGeolocation);
|
||||
document.getElementById('stopButton').addEventListener('click', stopGeolocation);
|
||||
document.getElementById('getLatestButton').addEventListener('click', getLatestPosition);
|
||||
document.getElementById('permButton').addEventListener('click', requestPermissions);
|
||||
document.getElementById('drivingBtn').addEventListener('click', setDrivingMode);
|
||||
document.getElementById('normalBtn').addEventListener('click', setNormalMode);
|
||||
document.getElementById('applyOptionsButton').addEventListener('click', applyOptionsFromForm);
|
||||
document.getElementById('getGnssStatusButton').addEventListener('click', getGnssStatus);
|
||||
document.getElementById('getLocationServicesStatusButton').addEventListener('click', getLocationServicesStatus);
|
||||
document.getElementById('clearLogButton').addEventListener('click', clearLog);
|
||||
|
||||
|
||||
// Apply a reasonable default UI config for E2E
|
||||
await DumonGeolocation.configureEdgeToEdge({
|
||||
bgColor: '#ffffff',
|
||||
style: 'DARK',
|
||||
overlay: false, // atau true jika kamu ingin konten masuk ke area statusbar/navbar
|
||||
overlay: false,
|
||||
});
|
||||
});
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dumon-geolocation",
|
||||
"version": "0.0.1",
|
||||
"version": "1.0.2",
|
||||
"description": "Implement manager GNSS, Wi‑Fi RTT, IMU, Kalman fusion, event emitter",
|
||||
"main": "dist/plugin.cjs.js",
|
||||
"module": "dist/esm/index.js",
|
||||
|
||||
@ -64,6 +64,28 @@ export interface PositioningData {
|
||||
predicted?: boolean;
|
||||
}
|
||||
|
||||
export interface SatelliteStatus {
|
||||
satellitesInView: number;
|
||||
usedInFix: number;
|
||||
constellationCounts: { [key: string]: number };
|
||||
}
|
||||
|
||||
export interface DumonGeoOptions {
|
||||
distanceThresholdMeters?: number;
|
||||
speedChangeThreshold?: number;
|
||||
directionChangeThreshold?: number;
|
||||
emitDebounceMs?: number;
|
||||
drivingEmitIntervalMs?: number;
|
||||
wifiScanIntervalMs?: number;
|
||||
enableWifiRtt?: boolean;
|
||||
enableLogging?: boolean;
|
||||
enableForwardPrediction?: boolean;
|
||||
maxPredictionSeconds?: number;
|
||||
emitGnssStatus?: boolean;
|
||||
suppressMockedUpdates?: boolean;
|
||||
keepScreenOn?: boolean;
|
||||
}
|
||||
|
||||
export interface PermissionStatus {
|
||||
location: 'granted' | 'denied';
|
||||
wifi: 'granted' | 'denied';
|
||||
@ -74,6 +96,9 @@ export interface DumonGeolocationPlugin {
|
||||
stopPositioning(): Promise<void>;
|
||||
getLatestPosition(): Promise<PositioningData>;
|
||||
checkAndRequestPermissions(): Promise<PermissionStatus>;
|
||||
setOptions(options: DumonGeoOptions): Promise<void>;
|
||||
getGnssStatus(): Promise<SatelliteStatus | null>;
|
||||
getLocationServicesStatus(): Promise<{ gpsEnabled: boolean; networkEnabled: boolean }>;
|
||||
|
||||
configureEdgeToEdge(options: {
|
||||
bgColor: string;
|
||||
@ -87,4 +112,9 @@ export interface DumonGeolocationPlugin {
|
||||
eventName: 'onPositionUpdate',
|
||||
listenerFunc: (data: PositioningData) => void
|
||||
): PluginListenerHandle;
|
||||
|
||||
addListener(
|
||||
eventName: 'onGnssStatus',
|
||||
listenerFunc: (data: SatelliteStatus) => void
|
||||
): PluginListenerHandle;
|
||||
}
|
||||
15
src/web.ts
15
src/web.ts
@ -1,5 +1,5 @@
|
||||
import { WebPlugin } from '@capacitor/core';
|
||||
import type { PositioningData } from './definitions';
|
||||
import type { PositioningData, DumonGeoOptions, SatelliteStatus } from './definitions';
|
||||
|
||||
export class DumonGeolocationWeb extends WebPlugin {
|
||||
async startPositioning(): Promise<void> {
|
||||
@ -44,4 +44,17 @@ export class DumonGeolocationWeb extends WebPlugin {
|
||||
console.info('[dumon-geolocation] configureEdgeToEdge called on web with:', options);
|
||||
// No-op
|
||||
}
|
||||
|
||||
async setOptions(_options: DumonGeoOptions): Promise<void> {
|
||||
// No-op on web
|
||||
}
|
||||
|
||||
async getGnssStatus(): Promise<SatelliteStatus | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async getLocationServicesStatus(): Promise<{ gpsEnabled: boolean; networkEnabled: boolean }> {
|
||||
// Web stub; assume enabled
|
||||
return { gpsEnabled: true, networkEnabled: true };
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user