also looking at this
feat(android): getCurrentPosition in Zig — ratchet 52 → 51
#188
6 files
+466
-3
| @@ -1,6 +1,14 @@ | ||
| 1 | 1 | package com.craft.runtime |
| 2 | 2 | |
| 3 | 3 | import android.app.Activity |
| 4 | import com.google.android.gms.location.Priority | |
| 5 | import com.google.android.gms.location.LocationServices | |
| 6 | import com.google.android.gms.location.LocationResult | |
| 7 | import com.google.android.gms.location.LocationRequest | |
| 8 | import com.google.android.gms.location.LocationCallback | |
| 9 | import com.google.android.gms.location.FusedLocationProviderClient | |
| 10 | import android.location.Location | |
| 11 | import android.annotation.SuppressLint | |
| 4 | 12 | import android.hardware.SensorManager |
| 5 | 13 | import android.hardware.SensorEventListener |
| 6 | 14 | import android.hardware.SensorEvent |
| @@ -169,6 +177,17 @@ object CraftNative { | ||
| 169 | 177 | private external fun nativeMotionSample(isAccelerometer: Boolean, x: Float, y: Float, z: Float) |
| 170 | 178 | private external fun nativeStartMotionUpdates(activity: Activity, intervalMs: Int): Boolean |
| 171 | 179 | private external fun nativeStopMotionUpdates(activity: Activity): Boolean |
| 180 | private external fun nativeLocationResult( | |
| 181 | latitude: Double, | |
| 182 | longitude: Double, | |
| 183 | accuracy: Double, | |
| 184 | altitude: Double, | |
| 185 | speed: Double, | |
| 186 | bearing: Double, | |
| 187 | time: Long | |
| 188 | ) | |
| 189 | private external fun nativeLocationFailed(message: String?) | |
| 190 | private external fun nativeGetCurrentPosition(activity: Activity): Boolean | |
| 172 | 191 | |
| 173 | 192 | /** |
| 174 | 193 | * `getDeviceInfo`, or null to use the Kotlin implementation. |
| @@ -1092,4 +1111,109 @@ object CraftNative { | ||
| 1092 | 1111 | false |
| 1093 | 1112 | } |
| 1094 | 1113 | } |
| 1114 | ||
| 1115 | // ==================== Current position ==================== | |
| 1116 | // | |
| 1117 | // The fifth listener shim, and the first with two Java interfaces in one | |
| 1118 | // flow: `OnSuccessListener` and `OnFailureListener` on the Play Services | |
| 1119 | // task, plus a `LocationCallback` for the fresh-location fallback. | |
| 1120 | // | |
| 1121 | // Every value crosses as a Double, including the three the framework types | |
| 1122 | // as Float. That is not a convenience: `CraftBridge` writes them with | |
| 1123 | // `put(name, value)`, and the overload Kotlin picks there is | |
| 1124 | // `put(String, double)` — primitive widening beats boxing — so they print | |
| 1125 | // as doubles. Passing them as Float and boxing them on the other side | |
| 1126 | // would print `0.1` where the shim prints `0.10000000149011612`. | |
| 1127 | ||
| 1128 | @JvmStatic | |
| 1129 | fun requestLocationPermissions(activity: Activity) { | |
| 1130 | ActivityCompat.requestPermissions( | |
| 1131 | activity, | |
| 1132 | arrayOf( | |
| 1133 | android.Manifest.permission.ACCESS_FINE_LOCATION, | |
| 1134 | android.Manifest.permission.ACCESS_COARSE_LOCATION | |
| 1135 | ), | |
| 1136 | REQUEST_LOCATION | |
| 1137 | ) | |
| 1138 | } | |
| 1139 | ||
| 1140 | /** `CraftBridge.REQUEST_LOCATION`, which this has to match. */ | |
| 1141 | private const val REQUEST_LOCATION = 1003 | |
| 1142 | ||
| 1143 | @JvmStatic | |
| 1144 | @SuppressLint("MissingPermission") | |
| 1145 | fun requestCurrentPosition(activity: Activity) { | |
| 1146 | val client = LocationServices.getFusedLocationProviderClient(activity) | |
| 1147 | client.lastLocation | |
| 1148 | .addOnSuccessListener { location: Location? -> | |
| 1149 | if (location != null) { | |
| 1150 | deliverLocation(location) | |
| 1151 | } else { | |
| 1152 | requestFreshPosition(activity, client) | |
| 1153 | } | |
| 1154 | } | |
| 1155 | .addOnFailureListener { e -> failLocation(e.message) } | |
| 1156 | } | |
| 1157 | ||
| 1158 | /** | |
| 1159 | * The fallback when there is no cached fix. | |
| 1160 | * | |
| 1161 | * One update, then the callback removes itself — as `CraftBridge`'s does. | |
| 1162 | * If it never fires, nothing settles the page's promise, here or there. | |
| 1163 | */ | |
| 1164 | @SuppressLint("MissingPermission") | |
| 1165 | private fun requestFreshPosition( | |
| 1166 | activity: Activity, | |
| 1167 | client: FusedLocationProviderClient | |
| 1168 | ) { | |
| 1169 | val request = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 1000) | |
| 1170 | .setWaitForAccurateLocation(false) | |
| 1171 | .setMinUpdateIntervalMillis(500) | |
| 1172 | .setMaxUpdateDelayMillis(1000) | |
| 1173 | .setMaxUpdates(1) | |
| 1174 | .build() | |
| 1175 | ||
| 1176 | client.requestLocationUpdates( | |
| 1177 | request, | |
| 1178 | object : LocationCallback() { | |
| 1179 | override fun onLocationResult(result: LocationResult) { | |
| 1180 | client.removeLocationUpdates(this) | |
| 1181 | result.lastLocation?.let { deliverLocation(it) } | |
| 1182 | } | |
| 1183 | }, | |
| 1184 | activity.mainLooper | |
| 1185 | ) | |
| 1186 | } | |
| 1187 | ||
| 1188 | private fun deliverLocation(location: Location) { | |
| 1189 | try { | |
| 1190 | nativeLocationResult( | |
| 1191 | location.latitude, | |
| 1192 | location.longitude, | |
| 1193 | location.accuracy.toDouble(), | |
| 1194 | location.altitude, | |
| 1195 | location.speed.toDouble(), | |
| 1196 | location.bearing.toDouble(), | |
| 1197 | location.time | |
| 1198 | ) | |
| 1199 | } catch (e: UnsatisfiedLinkError) { | |
| 1200 | // The library went away mid-flight. Nothing to answer to. | |
| 1201 | } | |
| 1202 | } | |
| 1203 | ||
| 1204 | private fun failLocation(message: String?) { | |
| 1205 | try { | |
| 1206 | nativeLocationFailed(message) | |
| 1207 | } catch (e: UnsatisfiedLinkError) { | |
| 1208 | } | |
| 1209 | } | |
| 1210 | ||
| 1211 | fun getCurrentPosition(activity: Activity): Boolean { | |
| 1212 | if (!isAvailable) return false | |
| 1213 | return try { | |
| 1214 | nativeGetCurrentPosition(activity) | |
| 1215 | } catch (e: UnsatisfiedLinkError) { | |
| 1216 | false | |
| 1217 | } | |
| 1218 | } | |
| 1095 | 1219 | } |