Refined gradle config

This commit is contained in:
wengki81 2025-06-15 00:04:11 +08:00
parent e2054b9841
commit 8ecd890bc9
15 changed files with 890 additions and 56 deletions

View File

@ -0,0 +1,67 @@
ext {
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1'
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1'
}
buildscript {
ext {
kotlin_version = '1.9.24'
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.7.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'com.android.library'
apply plugin: 'org.jetbrains.kotlin.android'
android {
namespace "com.dumon.plugin.geolocation"
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
defaultConfig {
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
}
repositories {
google()
mavenCentral()
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':capacitor-android')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation 'androidx.core:core-ktx:1.16.0'
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
}

View File

@ -0,0 +1,67 @@
ext {
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1'
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1'
}
buildscript {
ext {
kotlin_version = '1.9.24'
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.7.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'com.android.library'
apply plugin: 'org.jetbrains.kotlin.android'
android {
namespace "com.dumon.plugin.geolocation"
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
defaultConfig {
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
}
repositories {
google()
mavenCentral()
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':capacitor-android')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation 'androidx.core:core-ktx:1.16.0'
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
}

View File

@ -0,0 +1,183 @@
package com.dumon.plugin.geolocation
import android.Manifest
import com.getcapacitor.*
import com.getcapacitor.annotation.CapacitorPlugin
import com.getcapacitor.annotation.Permission
import com.dumon.plugin.geolocation.gps.GpsStatusManager
import com.dumon.plugin.geolocation.gps.SatelliteStatus
import com.dumon.plugin.geolocation.imu.ImuData
import com.dumon.plugin.geolocation.imu.ImuSensorManager
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 org.json.JSONArray
import org.json.JSONObject
@CapacitorPlugin(
name = "DumonGeolocation",
permissions = [
Permission(strings = [
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_WIFI_STATE,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.NEARBY_WIFI_DEVICES
])
]
)
class DumonGeolocation : Plugin() {
private var gpsManager: GpsStatusManager? = null
private var imuManager: ImuSensorManager? = null
private var wifiManager: WifiPositioningManager? = null
private var fusionManager: SensorFusionManager? = null
private var latestLatitude = 0.0
private var latestLongitude = 0.0
private var latestAccuracy = 999.0
private var latestSource = "GNSS"
private var latestTimestamp: Long = 0L
private var latestImu: ImuData? = null
private var satelliteStatus: SatelliteStatus? = null
private var wifiScanResult: WifiScanResult? = null
private var isMockedLocation = false
private var lastEmitTimestamp: Long = 0L
private val emitIntervalMs: Long = 500L
override fun load() {
gpsManager = GpsStatusManager(
context,
onSatelliteStatusUpdate = { satelliteStatus = it },
onLocationUpdate = { location, isMocked ->
latestLatitude = location.latitude
latestLongitude = location.longitude
latestAccuracy = location.accuracy.toDouble()
latestSource = if (isMocked) "MOCK" else "GNSS"
isMockedLocation = isMocked
latestTimestamp = location.time
fusionManager?.updateGpsPosition(latestLatitude, latestLongitude)
emitPositionUpdate()
}
)
imuManager = ImuSensorManager(
context,
onImuUpdate = {
latestImu = it
emitPositionUpdate()
}
)
wifiManager = WifiPositioningManager(
context,
onWifiPositioningUpdate = {
wifiScanResult = it
emitPositionUpdate()
}
)
fusionManager = SensorFusionManager { lat, lon ->
latestLatitude = lat
latestLongitude = lon
latestAccuracy = 3.0
latestSource = "FUSED"
latestTimestamp = System.currentTimeMillis()
emitPositionUpdate()
}
}
@PluginMethod
fun startPositioning(call: PluginCall) {
if (!PermissionUtils.hasLocationAndWifiPermissions(context)) {
call.reject("Required permissions not granted")
return
}
gpsManager?.start()
imuManager?.start()
wifiManager?.startPeriodicScan(3000L)
call.resolve()
}
@PluginMethod
fun stopPositioning(call: PluginCall) {
gpsManager?.stop()
imuManager?.stop()
wifiManager?.stopPeriodicScan()
call.resolve()
}
@PluginMethod
fun getLatestPosition(call: PluginCall) {
call.resolve(buildPositionData())
}
private fun emitPositionUpdate() {
val now = System.currentTimeMillis()
if (now - lastEmitTimestamp < emitIntervalMs) return
lastEmitTimestamp = now
notifyListeners("onPositionUpdate", buildPositionData())
}
private fun buildPositionData(): JSObject {
val obj = JSObject()
obj.put("source", latestSource)
obj.put("timestamp", if (latestTimestamp > 0) latestTimestamp else System.currentTimeMillis())
obj.put("latitude", latestLatitude)
obj.put("longitude", latestLongitude)
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)
}
// === Full Detail (commented out for future use) ===
/*
satelliteStatus?.let {
val gnss = JSObject()
gnss.put("satellitesInView", it.satellitesInView)
gnss.put("usedInFix", it.usedInFix)
val constellations = JSObject()
it.constellationCounts.forEach { (k, v) -> constellations.put(k, v) }
gnss.put("constellationCounts", constellations)
obj.put("gnssData", gnss)
}
latestImu?.let {
val imu = JSObject()
imu.put("accelX", it.accelX)
imu.put("accelY", it.accelY)
imu.put("accelZ", it.accelZ)
imu.put("gyroX", it.gyroX)
imu.put("gyroY", it.gyroY)
imu.put("gyroZ", it.gyroZ)
imu.put("speed", it.speed)
imu.put("acceleration", it.acceleration)
imu.put("directionRad", it.directionRad)
obj.put("imuData", imu)
}
wifiScanResult?.let {
val wifi = JSArray()
it.aps.forEach { ap ->
val a = JSObject()
a.put("ssid", ap.ssid)
a.put("bssid", ap.bssid)
a.put("rssi", ap.rssi)
ap.distance?.let { d -> a.put("distance", d) }
wifi.put(a)
}
obj.put("wifiData", wifi)
}
*/
return obj
}
}

View File

@ -0,0 +1,183 @@
package com.dumon.plugin.geolocation
import android.Manifest
import com.getcapacitor.*
import com.getcapacitor.annotation.CapacitorPlugin
import com.getcapacitor.annotation.Permission
import com.dumon.plugin.geolocation.gps.GpsStatusManager
import com.dumon.plugin.geolocation.gps.SatelliteStatus
import com.dumon.plugin.geolocation.imu.ImuData
import com.dumon.plugin.geolocation.imu.ImuSensorManager
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 org.json.JSONArray
import org.json.JSONObject
@CapacitorPlugin(
name = "DumonGeolocation",
permissions = [
Permission(strings = [
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_WIFI_STATE,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.NEARBY_WIFI_DEVICES
])
]
)
class DumonGeolocation : Plugin() {
private var gpsManager: GpsStatusManager? = null
private var imuManager: ImuSensorManager? = null
private var wifiManager: WifiPositioningManager? = null
private var fusionManager: SensorFusionManager? = null
private var latestLatitude = 0.0
private var latestLongitude = 0.0
private var latestAccuracy = 999.0
private var latestSource = "GNSS"
private var latestTimestamp: Long = 0L
private var latestImu: ImuData? = null
private var satelliteStatus: SatelliteStatus? = null
private var wifiScanResult: WifiScanResult? = null
private var isMockedLocation = false
private var lastEmitTimestamp: Long = 0L
private val emitIntervalMs: Long = 500L
override fun load() {
gpsManager = GpsStatusManager(
context,
onSatelliteStatusUpdate = { satelliteStatus = it },
onLocationUpdate = { location, isMocked ->
latestLatitude = location.latitude
latestLongitude = location.longitude
latestAccuracy = location.accuracy.toDouble()
latestSource = if (isMocked) "MOCK" else "GNSS"
isMockedLocation = isMocked
latestTimestamp = location.time
fusionManager?.updateGpsPosition(latestLatitude, latestLongitude)
emitPositionUpdate()
}
)
imuManager = ImuSensorManager(
context,
onImuUpdate = {
latestImu = it
emitPositionUpdate()
}
)
wifiManager = WifiPositioningManager(
context,
onWifiPositioningUpdate = {
wifiScanResult = it
emitPositionUpdate()
}
)
fusionManager = SensorFusionManager { lat, lon ->
latestLatitude = lat
latestLongitude = lon
latestAccuracy = 3.0
latestSource = "FUSED"
latestTimestamp = System.currentTimeMillis()
emitPositionUpdate()
}
}
@PluginMethod
fun startPositioning(call: PluginCall) {
if (!PermissionUtils.hasLocationAndWifiPermissions(context)) {
call.reject("Required permissions not granted")
return
}
gpsManager?.start()
imuManager?.start()
wifiManager?.startPeriodicScan(3000L)
call.resolve()
}
@PluginMethod
fun stopPositioning(call: PluginCall) {
gpsManager?.stop()
imuManager?.stop()
wifiManager?.stopPeriodicScan()
call.resolve()
}
@PluginMethod
fun getLatestPosition(call: PluginCall) {
call.resolve(buildPositionData())
}
private fun emitPositionUpdate() {
val now = System.currentTimeMillis()
if (now - lastEmitTimestamp < emitIntervalMs) return
lastEmitTimestamp = now
notifyListeners("onPositionUpdate", buildPositionData())
}
private fun buildPositionData(): JSObject {
val obj = JSObject()
obj.put("source", latestSource)
obj.put("timestamp", if (latestTimestamp > 0) latestTimestamp else System.currentTimeMillis())
obj.put("latitude", latestLatitude)
obj.put("longitude", latestLongitude)
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)
}
// === Full Detail (commented out for future use) ===
/*
satelliteStatus?.let {
val gnss = JSObject()
gnss.put("satellitesInView", it.satellitesInView)
gnss.put("usedInFix", it.usedInFix)
val constellations = JSObject()
it.constellationCounts.forEach { (k, v) -> constellations.put(k, v) }
gnss.put("constellationCounts", constellations)
obj.put("gnssData", gnss)
}
latestImu?.let {
val imu = JSObject()
imu.put("accelX", it.accelX)
imu.put("accelY", it.accelY)
imu.put("accelZ", it.accelZ)
imu.put("gyroX", it.gyroX)
imu.put("gyroY", it.gyroY)
imu.put("gyroZ", it.gyroZ)
imu.put("speed", it.speed)
imu.put("acceleration", it.acceleration)
imu.put("directionRad", it.directionRad)
obj.put("imuData", imu)
}
wifiScanResult?.let {
val wifi = JSArray()
it.aps.forEach { ap ->
val a = JSObject()
a.put("ssid", ap.ssid)
a.put("bssid", ap.bssid)
a.put("rssi", ap.rssi)
ap.distance?.let { d -> a.put("distance", d) }
wifi.put(a)
}
obj.put("wifiData", wifi)
}
*/
return obj
}
}

View File

@ -0,0 +1,23 @@
package com.dumon.plugin.geolocation.utils
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.ActivityCompat
object PermissionUtils {
fun hasLocationAndWifiPermissions(context: Context): Boolean {
val fineLocation = ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
val coarseLocation = ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
val nearbyWifi = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.checkSelfPermission(context, Manifest.permission.NEARBY_WIFI_DEVICES)
} else {
PackageManager.PERMISSION_GRANTED
}
return fineLocation == PackageManager.PERMISSION_GRANTED &&
coarseLocation == PackageManager.PERMISSION_GRANTED &&
nearbyWifi == PackageManager.PERMISSION_GRANTED
}
}

View File

@ -0,0 +1,75 @@
import type { PluginListenerHandle } from '@capacitor/core';
// export interface SatelliteStatus {
// satellitesInView: number;
// usedInFix: number;
// constellationCounts: { [key: string]: number };
// }
// export interface WifiAp {
// ssid: string;
// bssid: string;
// rssi: number;
// distance?: number;
// }
// export interface WifiScanResult {
// apCount: number;
// aps: WifiAp[];
// }
// export interface ImuData {
// accelX: number;
// accelY: number;
// accelZ: number;
// gyroX: number;
// gyroY: number;
// gyroZ: number;
// speed?: number;
// acceleration?: number;
// directionRad?: number;
// }
// export interface GpsData {
// latitude: number;
// longitude: number;
// accuracy: number;
// satellitesInView?: number;
// usedInFix?: number;
// constellationCounts?: { [key: string]: number };
// }
// export interface PositioningData {
// source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';
// timestamp: number;
// latitude: number;
// longitude: number;
// accuracy: number;
// gnssData?: SatelliteStatus;
// wifiData?: WifiAp[];
// imuData?: ImuData;
// }
export interface PositioningData {
source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';
timestamp: number;
latitude: number;
longitude: number;
accuracy: number;
speed: number;
acceleration: number;
directionRad: number;
isMocked: boolean;
}
export interface DumonGeolocationPlugin {
startPositioning(): Promise<void>;
stopPositioning(): Promise<void>;
getLatestPosition(): Promise<PositioningData>;
addListener(
eventName: 'onPositionUpdate',
listenerFunc: (data: PositioningData) => void
): PluginListenerHandle;
}

View File

@ -0,0 +1,81 @@
import type { PluginListenerHandle } from '@capacitor/core';
// export interface SatelliteStatus {
// satellitesInView: number;
// usedInFix: number;
// constellationCounts: { [key: string]: number };
// }
// export interface WifiAp {
// ssid: string;
// bssid: string;
// rssi: number;
// distance?: number;
// }
// export interface WifiScanResult {
// apCount: number;
// aps: WifiAp[];
// }
// export interface ImuData {
// accelX: number;
// accelY: number;
// accelZ: number;
// gyroX: number;
// gyroY: number;
// gyroZ: number;
// speed?: number;
// acceleration?: number;
// directionRad?: number;
// }
// export interface GpsData {
// latitude: number;
// longitude: number;
// accuracy: number;
// satellitesInView?: number;
// usedInFix?: number;
// constellationCounts?: { [key: string]: number };
// }
// export interface PositioningData {
// source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';
// timestamp: number;
// latitude: number;
// longitude: number;
// accuracy: number;
// gnssData?: SatelliteStatus;
// wifiData?: WifiAp[];
// imuData?: ImuData;
// }
export interface PositioningData {
source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';
timestamp: number;
latitude: number;
longitude: number;
accuracy: number;
speed: number;
acceleration: number;
directionRad: number;
isMocked: boolean;
}
export interface PermissionStatus {
location: 'granted' | 'denied' | 'prompt';
wifi: 'granted' | 'denied' | 'prompt';
}
export interface DumonGeolocationPlugin {
startPositioning(): Promise<void>;
stopPositioning(): Promise<void>;
getLatestPosition(): Promise<PositioningData>;
checkPermissions(): Promise<PermissionStatus>;
addListener(
eventName: 'onPositionUpdate',
listenerFunc: (data: PositioningData) => void
): PluginListenerHandle;
}

View File

@ -0,0 +1,81 @@
import type { PluginListenerHandle } from '@capacitor/core';
// export interface SatelliteStatus {
// satellitesInView: number;
// usedInFix: number;
// constellationCounts: { [key: string]: number };
// }
// export interface WifiAp {
// ssid: string;
// bssid: string;
// rssi: number;
// distance?: number;
// }
// export interface WifiScanResult {
// apCount: number;
// aps: WifiAp[];
// }
// export interface ImuData {
// accelX: number;
// accelY: number;
// accelZ: number;
// gyroX: number;
// gyroY: number;
// gyroZ: number;
// speed?: number;
// acceleration?: number;
// directionRad?: number;
// }
// export interface GpsData {
// latitude: number;
// longitude: number;
// accuracy: number;
// satellitesInView?: number;
// usedInFix?: number;
// constellationCounts?: { [key: string]: number };
// }
// export interface PositioningData {
// source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';
// timestamp: number;
// latitude: number;
// longitude: number;
// accuracy: number;
// gnssData?: SatelliteStatus;
// wifiData?: WifiAp[];
// imuData?: ImuData;
// }
export interface PositioningData {
source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';
timestamp: number;
latitude: number;
longitude: number;
accuracy: number;
speed: number;
acceleration: number;
directionRad: number;
isMocked: boolean;
}
export interface PermissionStatus {
location: 'granted' | 'denied' | 'prompt';
wifi: 'granted' | 'denied' | 'prompt';
}
export interface DumonGeolocationPlugin {
startPositioning(): Promise<void>;
stopPositioning(): Promise<void>;
getLatestPosition(): Promise<PositioningData>;
checkAndRequestPermissions(): Promise<PermissionStatus>;
addListener(
eventName: 'onPositionUpdate',
listenerFunc: (data: PositioningData) => void
): PluginListenerHandle;
}

View File

@ -0,0 +1,27 @@
import { WebPlugin } from '@capacitor/core';
import type { PositioningData } from './definitions';
export class DumonGeolocationWeb extends WebPlugin {
async startPositioning(): Promise<void> {
console.log('DumonGeolocationWeb: startPositioning() called (no-op)');
}
async stopPositioning(): Promise<void> {
console.log('DumonGeolocationWeb: stopPositioning() called (no-op)');
}
async getLatestPosition(): Promise<PositioningData> {
console.log('DumonGeolocationWeb: getLatestPosition() called (returning dummy data)');
return {
source: 'GNSS',
timestamp: Date.now(),
latitude: 0,
longitude: 0,
accuracy: 999,
speed: 0,
acceleration: 0,
directionRad: 0,
isMocked: false,
};
}
}

View File

@ -24,10 +24,10 @@ apply plugin: 'org.jetbrains.kotlin.android'
android { android {
namespace "com.dumon.plugin.geolocation" namespace "com.dumon.plugin.geolocation"
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35 compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
defaultConfig { defaultConfig {
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24 minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 35 targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@ -11,6 +11,7 @@ import com.dumon.plugin.geolocation.imu.ImuSensorManager
import com.dumon.plugin.geolocation.wifi.WifiPositioningManager import com.dumon.plugin.geolocation.wifi.WifiPositioningManager
import com.dumon.plugin.geolocation.wifi.WifiScanResult import com.dumon.plugin.geolocation.wifi.WifiScanResult
import com.dumon.plugin.geolocation.fusion.SensorFusionManager import com.dumon.plugin.geolocation.fusion.SensorFusionManager
import com.dumon.plugin.geolocation.utils.PermissionUtils
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
@ -91,6 +92,11 @@ class DumonGeolocation : Plugin() {
@PluginMethod @PluginMethod
fun startPositioning(call: PluginCall) { fun startPositioning(call: PluginCall) {
if (!PermissionUtils.hasLocationAndWifiPermissions(context)) {
call.reject("Required permissions not granted")
return
}
gpsManager?.start() gpsManager?.start()
imuManager?.start() imuManager?.start()
wifiManager?.startPeriodicScan(3000L) wifiManager?.startPeriodicScan(3000L)
@ -110,6 +116,19 @@ class DumonGeolocation : Plugin() {
call.resolve(buildPositionData()) call.resolve(buildPositionData())
} }
@PluginMethod
fun checkAndRequestPermissions(call: PluginCall) {
if (PermissionUtils.hasLocationAndWifiPermissions(context)) {
val result = JSObject().apply {
put("location", "granted")
put("wifi", "granted")
}
call.resolve(result)
} else {
requestAllPermissions(call, "checkAndRequestPermissions")
}
}
private fun emitPositionUpdate() { private fun emitPositionUpdate() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
if (now - lastEmitTimestamp < emitIntervalMs) return if (now - lastEmitTimestamp < emitIntervalMs) return

View File

@ -0,0 +1,23 @@
package com.dumon.plugin.geolocation.utils
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.ActivityCompat
object PermissionUtils {
fun hasLocationAndWifiPermissions(context: Context): Boolean {
val fineLocation = ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
val coarseLocation = ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
val nearbyWifi = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.checkSelfPermission(context, Manifest.permission.NEARBY_WIFI_DEVICES)
} else {
PackageManager.PERMISSION_GRANTED
}
return fineLocation == PackageManager.PERMISSION_GRANTED &&
coarseLocation == PackageManager.PERMISSION_GRANTED &&
nearbyWifi == PackageManager.PERMISSION_GRANTED
}
}

View File

@ -1,43 +1,55 @@
import type { PluginListenerHandle } from '@capacitor/core'; import type { PluginListenerHandle } from '@capacitor/core';
export interface SatelliteStatus { // export interface SatelliteStatus {
satellitesInView: number; // satellitesInView: number;
usedInFix: number; // usedInFix: number;
constellationCounts: { [key: string]: number }; // constellationCounts: { [key: string]: number };
} // }
export interface WifiAp { // export interface WifiAp {
ssid: string; // ssid: string;
bssid: string; // bssid: string;
rssi: number; // rssi: number;
distance?: number; // distance?: number;
} // }
export interface WifiScanResult { // export interface WifiScanResult {
apCount: number; // apCount: number;
aps: WifiAp[]; // aps: WifiAp[];
} // }
export interface ImuData { // export interface ImuData {
accelX: number; // accelX: number;
accelY: number; // accelY: number;
accelZ: number; // accelZ: number;
gyroX: number; // gyroX: number;
gyroY: number; // gyroY: number;
gyroZ: number; // gyroZ: number;
speed?: number; // speed?: number;
acceleration?: number; // acceleration?: number;
directionRad?: number; // directionRad?: number;
} // }
export interface GpsData { // export interface GpsData {
latitude: number; // latitude: number;
longitude: number; // longitude: number;
accuracy: number; // accuracy: number;
satellitesInView?: number; // satellitesInView?: number;
usedInFix?: number; // usedInFix?: number;
constellationCounts?: { [key: string]: number }; // constellationCounts?: { [key: string]: number };
} // }
// export interface PositioningData {
// source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';
// timestamp: number;
// latitude: number;
// longitude: number;
// accuracy: number;
// gnssData?: SatelliteStatus;
// wifiData?: WifiAp[];
// imuData?: ImuData;
// }
export interface PositioningData { export interface PositioningData {
source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK'; source: 'GNSS' | 'WIFI' | 'FUSED' | 'MOCK';
@ -45,16 +57,22 @@ export interface PositioningData {
latitude: number; latitude: number;
longitude: number; longitude: number;
accuracy: number; accuracy: number;
speed: number;
acceleration: number;
directionRad: number;
isMocked: boolean;
}
gnssData?: SatelliteStatus; export interface PermissionStatus {
wifiData?: WifiAp[]; location: 'granted' | 'denied' | 'prompt';
imuData?: ImuData; wifi: 'granted' | 'denied' | 'prompt';
} }
export interface DumonGeolocationPlugin { export interface DumonGeolocationPlugin {
startPositioning(): Promise<void>; startPositioning(): Promise<void>;
stopPositioning(): Promise<void>; stopPositioning(): Promise<void>;
getLatestPosition(): Promise<PositioningData>; getLatestPosition(): Promise<PositioningData>;
checkAndRequestPermissions(): Promise<PermissionStatus>;
addListener( addListener(
eventName: 'onPositionUpdate', eventName: 'onPositionUpdate',

View File

@ -18,23 +18,10 @@ export class DumonGeolocationWeb extends WebPlugin {
latitude: 0, latitude: 0,
longitude: 0, longitude: 0,
accuracy: 999, accuracy: 999,
gnssData: {
satellitesInView: 0,
usedInFix: 0,
constellationCounts: {}
},
wifiData: [],
imuData: {
accelX: 0,
accelY: 0,
accelZ: 0,
gyroX: 0,
gyroY: 0,
gyroZ: 0,
speed: 0, speed: 0,
acceleration: 0, acceleration: 0,
directionRad: 0 directionRad: 0,
} isMocked: false,
}; };
} }
} }