Realtime gps optimized for efficiency
This commit is contained in:
parent
10c64ce5d1
commit
39bfc59817
@ -15,6 +15,7 @@ import com.getcapacitor.*
|
|||||||
import com.getcapacitor.annotation.CapacitorPlugin
|
import com.getcapacitor.annotation.CapacitorPlugin
|
||||||
import com.getcapacitor.annotation.Permission
|
import com.getcapacitor.annotation.Permission
|
||||||
import com.dumon.plugin.geolocation.gps.GpsStatusManager
|
import com.dumon.plugin.geolocation.gps.GpsStatusManager
|
||||||
|
import com.dumon.plugin.geolocation.gps.GpsTrackingMode
|
||||||
import com.dumon.plugin.geolocation.gps.SatelliteStatus
|
import com.dumon.plugin.geolocation.gps.SatelliteStatus
|
||||||
import com.dumon.plugin.geolocation.imu.ImuData
|
import com.dumon.plugin.geolocation.imu.ImuData
|
||||||
import com.dumon.plugin.geolocation.imu.ImuSensorManager
|
import com.dumon.plugin.geolocation.imu.ImuSensorManager
|
||||||
@ -73,6 +74,8 @@ class DumonGeolocation : Plugin() {
|
|||||||
|
|
||||||
private var motionState: String = "idle" // 'idle', 'driving', 'mocked'
|
private var motionState: String = "idle" // 'idle', 'driving', 'mocked'
|
||||||
|
|
||||||
|
private var currentTrackingMode = GpsTrackingMode.NORMAL
|
||||||
|
|
||||||
override fun load() {
|
override fun load() {
|
||||||
gpsManager = GpsStatusManager(
|
gpsManager = GpsStatusManager(
|
||||||
context,
|
context,
|
||||||
@ -221,6 +224,19 @@ class DumonGeolocation : Plugin() {
|
|||||||
call.resolve()
|
call.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PluginMethod
|
||||||
|
fun setGpsMode(call: PluginCall) {
|
||||||
|
val mode = call.getString("mode") ?: "normal"
|
||||||
|
if (mode == "driving") {
|
||||||
|
gpsManager?.startContinuousMode()
|
||||||
|
Log.d("DUMON_GEOLOCATION", "Switched to driving mode (continuous GPS)")
|
||||||
|
} else {
|
||||||
|
gpsManager?.startPollingMode()
|
||||||
|
Log.d("DUMON_GEOLOCATION", "Switched to normal mode (polling GPS)")
|
||||||
|
}
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
@PermissionCallback
|
@PermissionCallback
|
||||||
private fun onPermissionResult(call: PluginCall) {
|
private fun onPermissionResult(call: PluginCall) {
|
||||||
val locationStatus = PermissionUtils.getPermissionStatus(
|
val locationStatus = PermissionUtils.getPermissionStatus(
|
||||||
@ -275,17 +291,13 @@ class DumonGeolocation : Plugin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun adjustIntervalAndSensorRate(speed: Float) {
|
private fun adjustIntervalAndSensorRate(speed: Float) {
|
||||||
val targetInterval = when {
|
val targetMode = if (speed > 3.0f) GpsTrackingMode.DRIVING else GpsTrackingMode.NORMAL
|
||||||
speed > 5f -> 1000L
|
|
||||||
speed > 1.5f -> 5000L
|
|
||||||
speed > 0.3f -> 15000L
|
|
||||||
else -> 30000L
|
|
||||||
}
|
|
||||||
|
|
||||||
if (emitIntervalMs != targetInterval) {
|
if (currentTrackingMode != targetMode) {
|
||||||
emitIntervalMs = targetInterval
|
currentTrackingMode = targetMode
|
||||||
gpsManager?.setPollingInterval(targetInterval)
|
gpsManager?.stop()
|
||||||
Log.d("DUMON_GEOLOCATION", "Auto-set emitIntervalMs = $emitIntervalMs ms")
|
gpsManager?.start(currentTrackingMode)
|
||||||
|
Log.d("DUMON_GEOLOCATION", "Switched GPS mode to $currentTrackingMode")
|
||||||
}
|
}
|
||||||
|
|
||||||
imuManager?.setSensorDelayBySpeed(speed)
|
imuManager?.setSensorDelayBySpeed(speed)
|
||||||
|
|||||||
@ -15,6 +15,11 @@ import android.text.format.DateFormat
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
|
|
||||||
|
enum class GpsTrackingMode {
|
||||||
|
NORMAL,
|
||||||
|
DRIVING
|
||||||
|
}
|
||||||
|
|
||||||
class GpsStatusManager(
|
class GpsStatusManager(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val onSatelliteStatusUpdate: (SatelliteStatus) -> Unit = {},
|
private val onSatelliteStatusUpdate: (SatelliteStatus) -> Unit = {},
|
||||||
@ -23,8 +28,11 @@ class GpsStatusManager(
|
|||||||
|
|
||||||
private val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
private val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
private var pollingIntervalMs: Long = 1000L // default 1 second
|
|
||||||
|
private var pollingIntervalMs: Long = 1000L // Default NORMAL mode
|
||||||
private var isPolling = false
|
private var isPolling = false
|
||||||
|
private var currentMode = GpsTrackingMode.NORMAL
|
||||||
|
private var continuousListener: LocationListener? = null
|
||||||
|
|
||||||
private val gnssStatusCallback = object : GnssStatus.Callback() {
|
private val gnssStatusCallback = object : GnssStatus.Callback() {
|
||||||
override fun onSatelliteStatusChanged(status: GnssStatus) {
|
override fun onSatelliteStatusChanged(status: GnssStatus) {
|
||||||
@ -59,16 +67,15 @@ class GpsStatusManager(
|
|||||||
|
|
||||||
val timestamp = DateFormat.format("HH:mm:ss", System.currentTimeMillis())
|
val timestamp = DateFormat.format("HH:mm:ss", System.currentTimeMillis())
|
||||||
val isMocked = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
val isMocked = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
location.isMock // API 31+
|
location.isMock
|
||||||
} else {
|
} else {
|
||||||
location.isFromMockProvider // Fallback
|
location.isFromMockProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
val info = "$providerTag Lat: ${location.latitude}, Lon: ${location.longitude}, Acc: ${location.accuracy} m @ $timestamp | Mock=$isMocked"
|
val info = "$providerTag Lat: ${location.latitude}, Lon: ${location.longitude}, Acc: ${location.accuracy} m @ $timestamp | Mock=$isMocked"
|
||||||
Log.d("GPS_LOCATION", info)
|
Log.d("GPS_LOCATION", info)
|
||||||
|
|
||||||
onLocationUpdate(location, isMocked)
|
onLocationUpdate(location, isMocked)
|
||||||
|
|
||||||
locationManager.removeUpdates(this)
|
locationManager.removeUpdates(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,47 +109,86 @@ class GpsStatusManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
fun start() {
|
fun start(mode: GpsTrackingMode = GpsTrackingMode.NORMAL) {
|
||||||
try {
|
stop() // Reset dulu
|
||||||
if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
currentMode = mode
|
||||||
Log.e("GPS_STATUS", "Missing location permissions")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||||
Log.w("GPS_STATUS", "GPS Provider not enabled")
|
Log.e("GPS_STATUS", "Missing location permissions")
|
||||||
}
|
return
|
||||||
if (!locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
|
}
|
||||||
Log.w("GPS_STATUS", "Network Provider not enabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
locationManager.registerGnssStatusCallback(gnssStatusCallback, null)
|
locationManager.registerGnssStatusCallback(gnssStatusCallback, null)
|
||||||
|
|
||||||
|
if (mode == GpsTrackingMode.DRIVING) {
|
||||||
|
startContinuousUpdates()
|
||||||
|
} else {
|
||||||
isPolling = true
|
isPolling = true
|
||||||
handler.post(pollingRunnable)
|
handler.post(pollingRunnable)
|
||||||
|
}
|
||||||
|
|
||||||
// Fallback lokasi terakhir
|
// Fallback lokasi terakhir
|
||||||
val lastKnown = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
|
val lastKnown = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
|
||||||
?: locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
|
?: locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
|
||||||
|
|
||||||
lastKnown?.let { location ->
|
lastKnown?.let { location ->
|
||||||
if (location.latitude != 0.0 && location.longitude != 0.0) {
|
if (location.latitude != 0.0 && location.longitude != 0.0) {
|
||||||
val isMocked = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) location.isMock else location.isFromMockProvider
|
val isMocked = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) location.isMock else location.isFromMockProvider
|
||||||
Log.d("GPS_STATUS", "Using last known location as fallback")
|
Log.d("GPS_STATUS", "Using last known location as fallback")
|
||||||
onLocationUpdate(location, isMocked)
|
onLocationUpdate(location, isMocked)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.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")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
continuousListener = object : LocationListener {
|
||||||
|
override fun onLocationChanged(location: Location) {
|
||||||
|
val isMocked = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) location.isMock else location.isFromMockProvider
|
||||||
|
onLocationUpdate(location, isMocked)
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("GPS_STATUS", "One-shot GPS polling started")
|
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
|
||||||
} catch (e: SecurityException) {
|
override fun onProviderEnabled(provider: String) {}
|
||||||
Log.e("GPS_STATUS", "SecurityException", e)
|
override fun onProviderDisabled(provider: String) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
locationManager.requestLocationUpdates(
|
||||||
|
LocationManager.GPS_PROVIDER,
|
||||||
|
0L,
|
||||||
|
0f,
|
||||||
|
continuousListener!!
|
||||||
|
)
|
||||||
|
|
||||||
|
Log.d("GPS_STATUS", "Started continuous location updates")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startContinuousMode() {
|
||||||
|
stop()
|
||||||
|
startContinuousUpdates()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startPollingMode() {
|
||||||
|
stop()
|
||||||
|
isPolling = true
|
||||||
|
handler.post(pollingRunnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
isPolling = false
|
isPolling = false
|
||||||
handler.removeCallbacks(pollingRunnable)
|
handler.removeCallbacks(pollingRunnable)
|
||||||
locationManager.unregisterGnssStatusCallback(gnssStatusCallback)
|
locationManager.unregisterGnssStatusCallback(gnssStatusCallback)
|
||||||
Log.d("GPS_STATUS", "GPS polling stopped")
|
continuousListener?.let {
|
||||||
|
locationManager.removeUpdates(it)
|
||||||
|
continuousListener = null
|
||||||
|
}
|
||||||
|
Log.d("GPS_STATUS", "GPS stopped for mode: $currentMode")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getConstellationName(type: Int): String {
|
private fun getConstellationName(type: Int): String {
|
||||||
|
|||||||
16
dist/docs.json
vendored
16
dist/docs.json
vendored
@ -65,6 +65,22 @@
|
|||||||
"complexTypes": [],
|
"complexTypes": [],
|
||||||
"slug": "configureedgetoedge"
|
"slug": "configureedgetoedge"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setGpsMode",
|
||||||
|
"signature": "(options: { mode: 'normal' | 'driving'; }) => Promise<void>",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "options",
|
||||||
|
"docs": "",
|
||||||
|
"type": "{ mode: 'normal' | 'driving'; }"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"returns": "Promise<void>",
|
||||||
|
"tags": [],
|
||||||
|
"docs": "",
|
||||||
|
"complexTypes": [],
|
||||||
|
"slug": "setgpsmode"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "addListener",
|
"name": "addListener",
|
||||||
"signature": "(eventName: 'onPositionUpdate', listenerFunc: (data: PositioningData) => void) => PluginListenerHandle",
|
"signature": "(eventName: 'onPositionUpdate', listenerFunc: (data: PositioningData) => void) => PluginListenerHandle",
|
||||||
|
|||||||
3
dist/esm/definitions.d.ts
vendored
3
dist/esm/definitions.d.ts
vendored
@ -25,5 +25,8 @@ export interface DumonGeolocationPlugin {
|
|||||||
style: 'DARK' | 'LIGHT';
|
style: 'DARK' | 'LIGHT';
|
||||||
overlay?: boolean;
|
overlay?: boolean;
|
||||||
}): Promise<void>;
|
}): Promise<void>;
|
||||||
|
setGpsMode(options: {
|
||||||
|
mode: 'normal' | 'driving';
|
||||||
|
}): Promise<void>;
|
||||||
addListener(eventName: 'onPositionUpdate', listenerFunc: (data: PositioningData) => void): PluginListenerHandle;
|
addListener(eventName: 'onPositionUpdate', listenerFunc: (data: PositioningData) => 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 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 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}"]}
|
||||||
@ -81,6 +81,8 @@ export interface DumonGeolocationPlugin {
|
|||||||
overlay?: boolean;
|
overlay?: boolean;
|
||||||
}): Promise<void>;
|
}): Promise<void>;
|
||||||
|
|
||||||
|
setGpsMode(options: { mode: 'normal' | 'driving' }): Promise<void>;
|
||||||
|
|
||||||
addListener(
|
addListener(
|
||||||
eventName: 'onPositionUpdate',
|
eventName: 'onPositionUpdate',
|
||||||
listenerFunc: (data: PositioningData) => void
|
listenerFunc: (data: PositioningData) => void
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user