expo-beacon 0.10.6 → 0.10.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withBeaconAndroid.d.ts","sourceRoot":"","sources":["../src/withBeaconAndroid.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"withBeaconAndroid.d.ts","sourceRoot":"","sources":["../src/withBeaconAndroid.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,YAAY,EAGZ,MAAM,sBAAsB,CAAC;AAM9B;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,OAAO,GAAG,cAAc,CAAC;AAExE,MAAM,MAAM,wBAAwB,GAAG;IACtC;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE;QACb;2EACmE;QACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB;qCAC6B;QAC7B,QAAQ,CAAC,EAAE,mBAAmB,CAAC;KAC/B,CAAC;CACF,CAAC;AAIF,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA2KlE;AA8ND,QAAA,MAAM,iBAAiB,EAAE,YAAY,CAAC,wBAAwB,GAAG,IAAI,CA0BpE,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -7,39 +7,72 @@ const path = require("path");
|
|
|
7
7
|
// ─── Generated Kotlin file ────────────────────────────────────────────────────
|
|
8
8
|
function getAndroidPluginKotlin(packageName) {
|
|
9
9
|
return `\
|
|
10
|
-
package ${packageName}
|
|
10
|
+
package ${packageName}
|
|
11
|
+
|
|
12
|
+
import android.app.AlarmManager
|
|
13
|
+
import android.app.PendingIntent
|
|
14
|
+
import android.content.BroadcastReceiver
|
|
15
|
+
import android.content.Context
|
|
16
|
+
import android.content.Intent
|
|
17
|
+
import android.os.Handler
|
|
18
|
+
import android.os.Looper
|
|
19
|
+
import android.os.SystemClock
|
|
20
|
+
import com.transistorsoft.locationmanager.adapter.BackgroundGeolocation
|
|
21
|
+
import com.transistorsoft.locationmanager.adapter.callback.TSCallback
|
|
22
|
+
import com.transistorsoft.locationmanager.adapter.callback.TSSyncCallback
|
|
23
|
+
import com.transistorsoft.locationmanager.data.LocationModel
|
|
24
|
+
import expo.modules.beacon.BeaconEventPlugin
|
|
11
25
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class BeaconGeoPlugin(ctx: Context) : BeaconEventPlugin {
|
|
20
|
-
private val bgGeo = BackgroundGeolocation.getInstance(ctx, null)
|
|
21
|
-
private val noOp = object : TSCallback {
|
|
22
|
-
override fun onSuccess() {}
|
|
23
|
-
override fun onFailure(error: String) {}
|
|
26
|
+
class BeaconGeoPlugin(ctx: Context) : BeaconEventPlugin {
|
|
27
|
+
private val appContext = ctx.applicationContext
|
|
28
|
+
private val bgGeo = BackgroundGeolocation.getInstance(ctx, null)
|
|
29
|
+
private val noOp = object : TSCallback {
|
|
30
|
+
override fun onSuccess() {}
|
|
31
|
+
override fun onFailure(error: String) {}
|
|
24
32
|
}
|
|
25
33
|
private val noOpSync = object : TSSyncCallback {
|
|
26
|
-
override fun onSuccess(records: MutableList<LocationModel>) {}
|
|
27
|
-
override fun onFailure(error: String) {}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
bgGeo.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
override fun onSuccess(records: MutableList<LocationModel>) {}
|
|
35
|
+
override fun onFailure(error: String) {}
|
|
36
|
+
}
|
|
37
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
38
|
+
private var pendingCarPlayStop: Runnable? = null
|
|
39
|
+
private val carPlayStopDelayMs = 30_000L
|
|
40
|
+
|
|
41
|
+
private fun startTracking() {
|
|
42
|
+
cancelPendingCarPlayStop()
|
|
43
|
+
bgGeo.start(noOp)
|
|
44
|
+
bgGeo.changePace(true, noOp)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private fun stopTracking() {
|
|
48
|
+
cancelPendingCarPlayStop()
|
|
49
|
+
bgGeo.sync(noOpSync)
|
|
50
|
+
bgGeo.changePace(false, noOp)
|
|
51
|
+
bgGeo.stop(noOp)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private fun stopTrackingForCarPlay() {
|
|
55
|
+
bgGeo.sync(noOpSync)
|
|
56
|
+
bgGeo.changePace(false, noOp)
|
|
57
|
+
cancelPendingCarPlayStop()
|
|
58
|
+
val token = SystemClock.elapsedRealtimeNanos().toString()
|
|
59
|
+
val stop = Runnable {
|
|
60
|
+
pendingCarPlayStop = null
|
|
61
|
+
BeaconGeoCarPlayStop.performIfCurrent(appContext, token)
|
|
62
|
+
}
|
|
63
|
+
pendingCarPlayStop = stop
|
|
64
|
+
BeaconGeoCarPlayStop.schedule(appContext, token, carPlayStopDelayMs)
|
|
65
|
+
mainHandler.postDelayed(stop, carPlayStopDelayMs)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private fun cancelPendingCarPlayStop() {
|
|
69
|
+
pendingCarPlayStop?.let { mainHandler.removeCallbacks(it) }
|
|
70
|
+
pendingCarPlayStop = null
|
|
71
|
+
BeaconGeoCarPlayStop.cancel(appContext)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
override fun onBeaconEnter(identifier: String, uuid: String, major: Int, minor: Int, distance: Double) =
|
|
75
|
+
startTracking()
|
|
43
76
|
override fun onBeaconExit(identifier: String, uuid: String, major: Int, minor: Int, distance: Double) =
|
|
44
77
|
stopTracking()
|
|
45
78
|
override fun onBeaconTimeout(identifier: String, uuid: String, major: Int, minor: Int, distance: Double) =
|
|
@@ -50,14 +83,98 @@ class BeaconGeoPlugin(ctx: Context) : BeaconEventPlugin {
|
|
|
50
83
|
stopTracking()
|
|
51
84
|
override fun onEddystoneTimeout(identifier: String, namespace: String, instance: String, distance: Double) =
|
|
52
85
|
stopTracking()
|
|
53
|
-
// Start tracking when the device connects to Android Auto
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
86
|
+
// Start tracking when the device connects to Android Auto; stop after a
|
|
87
|
+
// grace period when it disconnects. The delayed stop is backed by
|
|
88
|
+
// AlarmManager so it can still fire if the process idles or is killed.
|
|
89
|
+
override fun onCarPlayConnected(transport: String) {
|
|
90
|
+
startTracking()
|
|
91
|
+
}
|
|
92
|
+
override fun onCarPlayDisconnected() {
|
|
93
|
+
stopTrackingForCarPlay()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private object BeaconGeoCarPlayStop {
|
|
98
|
+
private const val ACTION_STOP = "${packageName}.expo_beacon.CARPLAY_STOP_TRACKING"
|
|
99
|
+
private const val EXTRA_TOKEN = "token"
|
|
100
|
+
private const val PREFS = "expo_beacon_geo_plugin"
|
|
101
|
+
private const val KEY_PENDING_TOKEN = "pending_carplay_stop_token"
|
|
102
|
+
private const val REQUEST_CODE = 0xBEEA
|
|
103
|
+
|
|
104
|
+
fun schedule(context: Context, token: String, delayMs: Long) {
|
|
105
|
+
val appContext = context.applicationContext
|
|
106
|
+
appContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
|
107
|
+
.edit()
|
|
108
|
+
.putString(KEY_PENDING_TOKEN, token)
|
|
109
|
+
.apply()
|
|
110
|
+
val alarmManager = appContext.getSystemService(AlarmManager::class.java) ?: return
|
|
111
|
+
val triggerAt = SystemClock.elapsedRealtime() + delayMs
|
|
112
|
+
try {
|
|
113
|
+
alarmManager.setAndAllowWhileIdle(
|
|
114
|
+
AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
|
115
|
+
triggerAt,
|
|
116
|
+
pendingIntent(appContext, token, PendingIntent.FLAG_UPDATE_CURRENT),
|
|
117
|
+
)
|
|
118
|
+
} catch (_: SecurityException) {
|
|
119
|
+
alarmManager.set(
|
|
120
|
+
AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
|
121
|
+
triggerAt,
|
|
122
|
+
pendingIntent(appContext, token, PendingIntent.FLAG_UPDATE_CURRENT),
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fun cancel(context: Context) {
|
|
128
|
+
val appContext = context.applicationContext
|
|
129
|
+
appContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
|
130
|
+
.edit()
|
|
131
|
+
.remove(KEY_PENDING_TOKEN)
|
|
132
|
+
.apply()
|
|
133
|
+
val alarmManager = appContext.getSystemService(AlarmManager::class.java) ?: return
|
|
134
|
+
alarmManager.cancel(pendingIntent(appContext, null, PendingIntent.FLAG_UPDATE_CURRENT))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
fun performIfCurrent(context: Context, token: String?) {
|
|
138
|
+
if (token == null) return
|
|
139
|
+
val appContext = context.applicationContext
|
|
140
|
+
val prefs = appContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
|
141
|
+
if (prefs.getString(KEY_PENDING_TOKEN, null) != token) return
|
|
142
|
+
prefs.edit().remove(KEY_PENDING_TOKEN).apply()
|
|
143
|
+
val bgGeo = BackgroundGeolocation.getInstance(appContext, null)
|
|
144
|
+
val noOp = object : TSCallback {
|
|
145
|
+
override fun onSuccess() {}
|
|
146
|
+
override fun onFailure(error: String) {}
|
|
147
|
+
}
|
|
148
|
+
val noOpSync = object : TSSyncCallback {
|
|
149
|
+
override fun onSuccess(records: MutableList<LocationModel>) {}
|
|
150
|
+
override fun onFailure(error: String) {}
|
|
151
|
+
}
|
|
152
|
+
bgGeo.sync(noOpSync)
|
|
153
|
+
bgGeo.changePace(false, noOp)
|
|
154
|
+
bgGeo.stop(noOp)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fun tokenFrom(intent: Intent): String? = intent.getStringExtra(EXTRA_TOKEN)
|
|
158
|
+
|
|
159
|
+
private fun pendingIntent(context: Context, token: String?, flags: Int): PendingIntent {
|
|
160
|
+
val intent = Intent(context, BeaconGeoStopReceiver::class.java).apply {
|
|
161
|
+
action = ACTION_STOP
|
|
162
|
+
token?.let { putExtra(EXTRA_TOKEN, it) }
|
|
163
|
+
}
|
|
164
|
+
return PendingIntent.getBroadcast(
|
|
165
|
+
context,
|
|
166
|
+
REQUEST_CODE,
|
|
167
|
+
intent,
|
|
168
|
+
flags or PendingIntent.FLAG_IMMUTABLE,
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
class BeaconGeoStopReceiver : BroadcastReceiver() {
|
|
174
|
+
override fun onReceive(context: Context, intent: Intent) {
|
|
175
|
+
BeaconGeoCarPlayStop.performIfCurrent(context, BeaconGeoCarPlayStop.tokenFrom(intent))
|
|
176
|
+
}
|
|
177
|
+
}
|
|
61
178
|
`;
|
|
62
179
|
}
|
|
63
180
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
@@ -87,6 +204,7 @@ function modifyMainApplication(contents) {
|
|
|
87
204
|
const AA_META_DATA_NAME = "com.google.android.gms.car.application";
|
|
88
205
|
const AA_META_DATA_RESOURCE = "@xml/automotive_app_desc";
|
|
89
206
|
const AA_RES_FILE = "automotive_app_desc.xml";
|
|
207
|
+
const BEACON_GEO_STOP_RECEIVER = "BeaconGeoStopReceiver";
|
|
90
208
|
function getAutomotiveAppDescXml(usesName) {
|
|
91
209
|
return `<?xml version="1.0" encoding="utf-8"?>
|
|
92
210
|
<!--
|
|
@@ -150,6 +268,30 @@ function withAndroidAutoDescriptor(usesName) {
|
|
|
150
268
|
* the `backgroundGeolocation` plugin prop (default: enabled).
|
|
151
269
|
*/
|
|
152
270
|
const withBeaconGeoPlugin = (config) => {
|
|
271
|
+
config = (0, config_plugins_1.withAndroidManifest)(config, (config) => {
|
|
272
|
+
var _a, _b;
|
|
273
|
+
const pkgName = (_a = config.android) === null || _a === void 0 ? void 0 : _a.package;
|
|
274
|
+
if (!pkgName)
|
|
275
|
+
return config;
|
|
276
|
+
const app = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults);
|
|
277
|
+
const receiverName = `${pkgName}.${BEACON_GEO_STOP_RECEIVER}`;
|
|
278
|
+
const receivers = ((_b = app.receiver) !== null && _b !== void 0 ? _b : (app.receiver = []));
|
|
279
|
+
const exists = receivers.some((receiver) => {
|
|
280
|
+
var _a, _b;
|
|
281
|
+
return ((_a = receiver === null || receiver === void 0 ? void 0 : receiver.$) === null || _a === void 0 ? void 0 : _a["android:name"]) === receiverName ||
|
|
282
|
+
((_b = receiver === null || receiver === void 0 ? void 0 : receiver.$) === null || _b === void 0 ? void 0 : _b["android:name"]) === `.${BEACON_GEO_STOP_RECEIVER}`;
|
|
283
|
+
});
|
|
284
|
+
if (!exists) {
|
|
285
|
+
receivers.push({
|
|
286
|
+
$: {
|
|
287
|
+
"android:name": receiverName,
|
|
288
|
+
"android:enabled": "true",
|
|
289
|
+
"android:exported": "false",
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return config;
|
|
294
|
+
});
|
|
153
295
|
// Step 1 – write BeaconGeoPlugin.kt into the app source tree.
|
|
154
296
|
config = (0, config_plugins_1.withDangerousMod)(config, [
|
|
155
297
|
"android",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withBeaconIOS.d.ts","sourceRoot":"","sources":["../src/withBeaconIOS.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"withBeaconIOS.d.ts","sourceRoot":"","sources":["../src/withBeaconIOS.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,YAAY,EAKZ,MAAM,sBAAsB,CAAC;AAM9B,MAAM,MAAM,oBAAoB,GAAG;IAClC;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;;;;;;;;;OAcG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAIF,wBAAgB,iBAAiB,IAAI,MAAM,CAsE1C;AA2KD,QAAA,MAAM,aAAa,EAAE,YAAY,CAAC,oBAAoB,GAAG,IAAI,CAgC5D,CAAC;AA6CF,eAAe,aAAa,CAAC"}
|
|
@@ -11,6 +11,11 @@ import ExpoBeacon
|
|
|
11
11
|
import TSLocationManager
|
|
12
12
|
|
|
13
13
|
final class BeaconGeoPlugin: BeaconLifecycleDelegate {
|
|
14
|
+
// CarPlay disconnect uses a grace period: flush immediately, but defer the
|
|
15
|
+
// actual stop() so a quick reconnect (brief dropout) keeps tracking alive.
|
|
16
|
+
private var pendingCarPlayStop: DispatchWorkItem?
|
|
17
|
+
private let carPlayStopDelay: TimeInterval = 30
|
|
18
|
+
|
|
14
19
|
private func startTracking() {
|
|
15
20
|
BackgroundGeolocation.sharedInstance().start()
|
|
16
21
|
BackgroundGeolocation.sharedInstance().changePace(true)
|
|
@@ -22,6 +27,25 @@ final class BeaconGeoPlugin: BeaconLifecycleDelegate {
|
|
|
22
27
|
BackgroundGeolocation.sharedInstance().stop()
|
|
23
28
|
}
|
|
24
29
|
|
|
30
|
+
// Flush queued locations right away, then stop tracking after a grace
|
|
31
|
+
// period. Cancelled by cancelPendingCarPlayStop() if CarPlay reconnects.
|
|
32
|
+
private func stopTrackingForCarPlay() {
|
|
33
|
+
BackgroundGeolocation.sharedInstance().sync({ _ in }, failure: { _ in })
|
|
34
|
+
BackgroundGeolocation.sharedInstance().changePace(false)
|
|
35
|
+
cancelPendingCarPlayStop()
|
|
36
|
+
let work = DispatchWorkItem { [weak self] in
|
|
37
|
+
self?.pendingCarPlayStop = nil
|
|
38
|
+
BackgroundGeolocation.sharedInstance().stop()
|
|
39
|
+
}
|
|
40
|
+
pendingCarPlayStop = work
|
|
41
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + carPlayStopDelay, execute: work)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private func cancelPendingCarPlayStop() {
|
|
45
|
+
pendingCarPlayStop?.cancel()
|
|
46
|
+
pendingCarPlayStop = nil
|
|
47
|
+
}
|
|
48
|
+
|
|
25
49
|
func beaconDidEnter(identifier: String, uuid: String, major: Int, minor: Int, distance: Double) {
|
|
26
50
|
startTracking()
|
|
27
51
|
}
|
|
@@ -40,13 +64,14 @@ final class BeaconGeoPlugin: BeaconLifecycleDelegate {
|
|
|
40
64
|
func eddystoneDidTimeout(identifier: String, namespace: String, instance: String, distance: Double) {
|
|
41
65
|
stopTracking()
|
|
42
66
|
}
|
|
43
|
-
// Start tracking when the device connects to CarPlay (wired or wireless)
|
|
44
|
-
// stop when it disconnects.
|
|
67
|
+
// Start tracking when the device connects to CarPlay (wired or wireless);
|
|
68
|
+
// stop (after a grace period) when it disconnects.
|
|
45
69
|
func carPlayDidConnect(transport: String) {
|
|
70
|
+
cancelPendingCarPlayStop()
|
|
46
71
|
startTracking()
|
|
47
72
|
}
|
|
48
73
|
func carPlayDidDisconnect() {
|
|
49
|
-
|
|
74
|
+
stopTrackingForCarPlay()
|
|
50
75
|
}
|
|
51
76
|
}
|
|
52
77
|
`;
|