react-native-gizwits-sdk-v5 1.7.7-beta → 1.7.9-beta
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/android/build.gradle +7 -4
- package/android/src/main/java/com/gizwits/reactnativegizwitssdkv5/DeviceListEventCoordinator.kt +72 -0
- package/android/src/main/java/com/gizwits/reactnativegizwitssdkv5/RNGizSDKManagerModule.kt +133 -143
- package/android/src/test/java/com/gizwits/reactnativegizwitssdkv5/DeviceListEventCoordinatorTest.kt +110 -0
- package/package.json +1 -1
- package/react-native-gizwits-sdk-v5.podspec +1 -1
package/android/build.gradle
CHANGED
|
@@ -67,10 +67,10 @@ dependencies {
|
|
|
67
67
|
// ESPTouch
|
|
68
68
|
implementation 'com.github.EspressifApp:lib-esptouch-android:1.1.1'
|
|
69
69
|
|
|
70
|
-
implementation("io.github.gizwits:sdk:1.4.
|
|
71
|
-
implementation("io.github.gizwits:sdk-bluetooth:1.4.
|
|
72
|
-
implementation("io.github.gizwits:sdk-lan:1.4.
|
|
73
|
-
implementation("io.github.gizwits:sdk-mqtt:1.4.
|
|
70
|
+
implementation("io.github.gizwits:sdk:1.4.11")
|
|
71
|
+
implementation("io.github.gizwits:sdk-bluetooth:1.4.11")
|
|
72
|
+
implementation("io.github.gizwits:sdk-lan:1.4.11")
|
|
73
|
+
implementation("io.github.gizwits:sdk-mqtt:1.4.11")
|
|
74
74
|
// implementation files('libs/sdk-release.aar', 'libs/sdk-bluetooth-release.aar', 'libs/sdk-lan-release.aar', 'libs/sdk-mqtt-release.aar')
|
|
75
75
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutine_version")
|
|
76
76
|
// retrofit
|
|
@@ -82,4 +82,7 @@ dependencies {
|
|
|
82
82
|
//ktor
|
|
83
83
|
implementation "io.ktor:ktor-network:$ktor_network_version"
|
|
84
84
|
|
|
85
|
+
testImplementation 'junit:junit:4.13.2'
|
|
86
|
+
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlin_coroutine_version"
|
|
87
|
+
|
|
85
88
|
}
|
package/android/src/main/java/com/gizwits/reactnativegizwitssdkv5/DeviceListEventCoordinator.kt
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
package com.gizwits.reactnativegizwitssdkv5
|
|
2
|
+
|
|
3
|
+
import kotlinx.coroutines.CoroutineScope
|
|
4
|
+
import kotlinx.coroutines.Job
|
|
5
|
+
import kotlinx.coroutines.channels.Channel
|
|
6
|
+
import kotlinx.coroutines.delay
|
|
7
|
+
import kotlinx.coroutines.launch
|
|
8
|
+
|
|
9
|
+
internal class ConflatedEventDispatcher(
|
|
10
|
+
scope: CoroutineScope,
|
|
11
|
+
private val windowMillis: Long,
|
|
12
|
+
private val dispatch: suspend () -> Unit,
|
|
13
|
+
) {
|
|
14
|
+
private val requests = Channel<Unit>(Channel.CONFLATED)
|
|
15
|
+
private val worker = scope.launch {
|
|
16
|
+
for (request in requests) {
|
|
17
|
+
if (windowMillis > 0) {
|
|
18
|
+
delay(windowMillis)
|
|
19
|
+
}
|
|
20
|
+
while (requests.tryReceive().isSuccess) {
|
|
21
|
+
// Keep only the latest-state notification for this window.
|
|
22
|
+
}
|
|
23
|
+
dispatch()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
init {
|
|
28
|
+
require(windowMillis >= 0) { "windowMillis must not be negative" }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
internal fun request(): Boolean = requests.trySend(Unit).isSuccess
|
|
32
|
+
|
|
33
|
+
internal fun cancel() {
|
|
34
|
+
requests.close()
|
|
35
|
+
worker.cancel()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
internal class KeyedSubscriptionRegistry<K, V>(
|
|
40
|
+
private val keySelector: (V) -> K,
|
|
41
|
+
private val subscribe: (V) -> Job,
|
|
42
|
+
) {
|
|
43
|
+
private data class Subscription<V>(val value: V, val job: Job)
|
|
44
|
+
|
|
45
|
+
private val subscriptions = linkedMapOf<K, Subscription<V>>()
|
|
46
|
+
|
|
47
|
+
@Synchronized
|
|
48
|
+
internal fun update(values: Collection<V>) {
|
|
49
|
+
val valuesByKey = values.associateBy(keySelector)
|
|
50
|
+
|
|
51
|
+
(subscriptions.keys - valuesByKey.keys).forEach { key ->
|
|
52
|
+
subscriptions.remove(key)?.job?.cancel()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
valuesByKey.forEach { (key, value) ->
|
|
56
|
+
val existing = subscriptions[key]
|
|
57
|
+
if (existing == null || !existing.job.isActive || existing.value !== value) {
|
|
58
|
+
existing?.job?.cancel()
|
|
59
|
+
subscriptions[key] = Subscription(value, subscribe(value))
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@Synchronized
|
|
65
|
+
internal fun cancelAll() {
|
|
66
|
+
subscriptions.values.forEach { it.job.cancel() }
|
|
67
|
+
subscriptions.clear()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@Synchronized
|
|
71
|
+
internal fun keys(): Set<K> = subscriptions.keys.toSet()
|
|
72
|
+
}
|
|
@@ -28,20 +28,18 @@ import com.google.gson.annotations.SerializedName
|
|
|
28
28
|
import kotlinx.coroutines.CoroutineScope
|
|
29
29
|
import kotlinx.coroutines.Dispatchers
|
|
30
30
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
31
|
-
import kotlinx.coroutines.
|
|
32
|
-
import kotlinx.coroutines.
|
|
31
|
+
import kotlinx.coroutines.SupervisorJob
|
|
32
|
+
import kotlinx.coroutines.cancel
|
|
33
33
|
import kotlinx.coroutines.flow.Flow
|
|
34
|
-
import kotlinx.coroutines.flow.
|
|
34
|
+
import kotlinx.coroutines.flow.collect
|
|
35
35
|
import kotlinx.coroutines.flow.combine
|
|
36
|
-
import kotlinx.coroutines.flow.
|
|
37
|
-
import kotlinx.coroutines.flow.
|
|
38
|
-
import kotlinx.coroutines.flow.
|
|
39
|
-
import kotlinx.coroutines.flow.flatMapConcat
|
|
40
|
-
import kotlinx.coroutines.flow.last
|
|
41
|
-
import kotlinx.coroutines.flow.launchIn
|
|
36
|
+
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
37
|
+
import kotlinx.coroutines.flow.drop
|
|
38
|
+
import kotlinx.coroutines.flow.filterNotNull
|
|
42
39
|
import kotlinx.coroutines.flow.mapLatest
|
|
43
40
|
import kotlinx.coroutines.flow.take
|
|
44
41
|
import kotlinx.coroutines.launch
|
|
42
|
+
import kotlinx.coroutines.supervisorScope
|
|
45
43
|
import org.json.JSONArray
|
|
46
44
|
import org.json.JSONObject
|
|
47
45
|
|
|
@@ -62,15 +60,24 @@ data class GizFeedback(
|
|
|
62
60
|
|
|
63
61
|
class RNGizSDKManagerModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
|
64
62
|
|
|
63
|
+
private val moduleJob = SupervisorJob()
|
|
64
|
+
private val moduleScope = CoroutineScope(moduleJob + Dispatchers.IO)
|
|
65
|
+
@Volatile
|
|
66
|
+
private var latestDeviceList = emptyList<GizDevice>()
|
|
65
67
|
private var mReactContext: ReactContext? = null
|
|
68
|
+
private val deviceListEventDispatcher = ConflatedEventDispatcher(
|
|
69
|
+
scope = moduleScope,
|
|
70
|
+
windowMillis = 500,
|
|
71
|
+
dispatch = ::emitDeviceListChange,
|
|
72
|
+
)
|
|
73
|
+
private val deviceSubscriptions = KeyedSubscriptionRegistry<String, GizDevice>(
|
|
74
|
+
keySelector = GizDevice::id,
|
|
75
|
+
subscribe = ::subscribeToDevice,
|
|
76
|
+
)
|
|
66
77
|
|
|
67
|
-
private var isSubscriptDevices: List<String> = listOf();
|
|
68
78
|
init {
|
|
69
79
|
mReactContext = reactContext
|
|
70
80
|
}
|
|
71
|
-
companion object {
|
|
72
|
-
internal var devices = listOf<GizDevice>()
|
|
73
|
-
}
|
|
74
81
|
enum class EventName(val value: String) {
|
|
75
82
|
DeviceDataListener("DeviceDataListener"),
|
|
76
83
|
DeviceListListener("DeviceListListener"),
|
|
@@ -120,7 +127,7 @@ class RNGizSDKManagerModule(reactContext: ReactApplicationContext) : ReactContex
|
|
|
120
127
|
|
|
121
128
|
override fun initialize() {
|
|
122
129
|
super.initialize()
|
|
123
|
-
|
|
130
|
+
moduleScope.launch {
|
|
124
131
|
GizSDKManager.subscribeBindEvent().collect {
|
|
125
132
|
val data = JSONObject()
|
|
126
133
|
data.put("did", it.first)
|
|
@@ -129,159 +136,141 @@ class RNGizSDKManagerModule(reactContext: ReactApplicationContext) : ReactContex
|
|
|
129
136
|
}
|
|
130
137
|
}
|
|
131
138
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (!isSubscriptDevices.contains(dev.mac)) {
|
|
139
|
-
CoroutineScope(Dispatchers.IO).launch {
|
|
140
|
-
async {
|
|
141
|
-
dev.bleCapability.subscribeModuleProfile().debounce(500).collect {
|
|
142
|
-
if (it != null) {
|
|
143
|
-
emitDeviceListChange()
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
async {
|
|
148
|
-
combine(
|
|
149
|
-
dev.bleCapability.subscribeStatus(),
|
|
150
|
-
dev.bleCapability.subscribeIsLogin(),
|
|
151
|
-
) { status, isLogin ->
|
|
152
|
-
Pair(status, isLogin)
|
|
153
|
-
}.collect {
|
|
154
|
-
val data = JSONObject()
|
|
155
|
-
data.put("state", it.first.toInt())
|
|
156
|
-
data.put("isLogin", it.second)
|
|
157
|
-
data.put("type", "BLE")
|
|
158
|
-
data.put("device", dev.toJsonObject())
|
|
159
|
-
sendEvent(EventName.DeviceStateListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
async {
|
|
163
|
-
dev.bleCapability.subscribeDp().collect{
|
|
164
|
-
val data = JSONObject()
|
|
165
|
-
data.put("data", JSONObject(it.toString()))
|
|
166
|
-
data.put("type", "BLE")
|
|
167
|
-
data.put("device", dev.toJsonObject())
|
|
168
|
-
sendEvent(EventName.DeviceDataListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
async {
|
|
172
|
-
dev.lanCapability.subscribeModuleProfile().debounce(500).collectLatest{
|
|
173
|
-
if (it != null) {
|
|
174
|
-
emitDeviceListChange()
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async {
|
|
180
|
-
combineLatest(
|
|
181
|
-
dev.lanCapability.subscribeStatus(),
|
|
182
|
-
dev.lanCapability.subscribeIsLogin(),
|
|
183
|
-
) { status, isLogin ->
|
|
184
|
-
Pair(status, isLogin)
|
|
185
|
-
}.collectLatest {
|
|
186
|
-
val data = JSONObject()
|
|
187
|
-
data.put("state", it.first.toInt())
|
|
188
|
-
data.put("isLogin", it.second)
|
|
189
|
-
data.put("type", "LAN")
|
|
190
|
-
data.put("device", dev.toJsonObject())
|
|
191
|
-
sendEvent(EventName.DeviceStateListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async {
|
|
196
|
-
dev.lanCapability.subscribeDp().collect{
|
|
197
|
-
val data = JSONObject()
|
|
198
|
-
data.put("data", JSONObject(it.toString()))
|
|
199
|
-
data.put("type", "LAN")
|
|
200
|
-
data.put("device", dev.toJsonObject())
|
|
201
|
-
sendEvent(EventName.DeviceDataListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async {
|
|
206
|
-
combineLatest(
|
|
207
|
-
dev.mqttCapability.subscribeStatus(),
|
|
208
|
-
dev.mqttCapability.subscribeIsLogin(),
|
|
209
|
-
) { status, isLogin ->
|
|
210
|
-
Pair(status, isLogin)
|
|
211
|
-
}.collectLatest {
|
|
212
|
-
val data = JSONObject()
|
|
213
|
-
data.put("state", it.first.toInt())
|
|
214
|
-
data.put("isLogin", it.second)
|
|
215
|
-
data.put("type", "MQTT")
|
|
216
|
-
data.put("device", dev.toJsonObject())
|
|
217
|
-
sendEvent(EventName.DeviceStateListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// 不会主动发生改表
|
|
222
|
-
// async {
|
|
223
|
-
// dev.mqttCapability.subscribeModuleProfile().debounce(500).collectLatest{
|
|
224
|
-
// if (it != null) {
|
|
225
|
-
// emitDeviceListChange()
|
|
226
|
-
// }
|
|
227
|
-
// }
|
|
228
|
-
// }
|
|
229
|
-
|
|
230
|
-
async {
|
|
231
|
-
dev.mqttCapability.subscribeDp().collect{
|
|
232
|
-
val data = JSONObject()
|
|
233
|
-
data.put("data", JSONObject(it.toString()))
|
|
234
|
-
data.put("type", "MQTT")
|
|
235
|
-
data.put("device", dev.toJsonObject())
|
|
236
|
-
sendEvent(EventName.DeviceDataListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
139
|
+
moduleScope.launch {
|
|
140
|
+
deviceListState
|
|
141
|
+
.distinctUntilChanged { previous, current ->
|
|
142
|
+
previous.size == current.size && previous.indices.all { index ->
|
|
143
|
+
previous[index].id == current[index].id && previous[index] === current[index]
|
|
240
144
|
}
|
|
241
145
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
*/
|
|
247
|
-
isSubscriptDevices = deviceList.map{
|
|
248
|
-
it.mac
|
|
146
|
+
.collect { deviceList ->
|
|
147
|
+
latestDeviceList = deviceList
|
|
148
|
+
deviceSubscriptions.update(deviceList)
|
|
149
|
+
requestDeviceListChange()
|
|
249
150
|
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
250
153
|
|
|
251
|
-
|
|
154
|
+
private fun subscribeToDevice(device: GizDevice) = moduleScope.launch {
|
|
155
|
+
supervisorScope {
|
|
156
|
+
launch {
|
|
157
|
+
device.bleCapability.subscribeModuleProfile()
|
|
158
|
+
.drop(1)
|
|
159
|
+
.filterNotNull()
|
|
160
|
+
.collect { requestDeviceListChange() }
|
|
252
161
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
162
|
+
launch {
|
|
163
|
+
combine(
|
|
164
|
+
device.bleCapability.subscribeStatus(),
|
|
165
|
+
device.bleCapability.subscribeIsLogin(),
|
|
166
|
+
) { status, isLogin -> Pair(status, isLogin) }
|
|
167
|
+
.collect { (status, isLogin) ->
|
|
168
|
+
val data = JSONObject()
|
|
169
|
+
data.put("state", status.toInt())
|
|
170
|
+
data.put("isLogin", isLogin)
|
|
171
|
+
data.put("type", "BLE")
|
|
172
|
+
data.put("device", device.toJsonObject())
|
|
173
|
+
sendEvent(EventName.DeviceStateListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
launch {
|
|
177
|
+
device.bleCapability.subscribeDp().collect { payload ->
|
|
178
|
+
val data = JSONObject()
|
|
179
|
+
data.put("data", JSONObject(payload.toString()))
|
|
180
|
+
data.put("type", "BLE")
|
|
181
|
+
data.put("device", device.toJsonObject())
|
|
182
|
+
sendEvent(EventName.DeviceDataListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
launch {
|
|
186
|
+
device.lanCapability.subscribeModuleProfile()
|
|
187
|
+
.drop(1)
|
|
188
|
+
.filterNotNull()
|
|
189
|
+
.collect { requestDeviceListChange() }
|
|
190
|
+
}
|
|
191
|
+
launch {
|
|
192
|
+
combineLatest(
|
|
193
|
+
device.lanCapability.subscribeStatus(),
|
|
194
|
+
device.lanCapability.subscribeIsLogin(),
|
|
195
|
+
) { status, isLogin -> Pair(status, isLogin) }
|
|
196
|
+
.collect { (status, isLogin) ->
|
|
197
|
+
val data = JSONObject()
|
|
198
|
+
data.put("state", status.toInt())
|
|
199
|
+
data.put("isLogin", isLogin)
|
|
200
|
+
data.put("type", "LAN")
|
|
201
|
+
data.put("device", device.toJsonObject())
|
|
202
|
+
sendEvent(EventName.DeviceStateListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
launch {
|
|
206
|
+
device.lanCapability.subscribeDp().collect { payload ->
|
|
207
|
+
val data = JSONObject()
|
|
208
|
+
data.put("data", JSONObject(payload.toString()))
|
|
209
|
+
data.put("type", "LAN")
|
|
210
|
+
data.put("device", device.toJsonObject())
|
|
211
|
+
sendEvent(EventName.DeviceDataListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
launch {
|
|
215
|
+
combineLatest(
|
|
216
|
+
device.mqttCapability.subscribeStatus(),
|
|
217
|
+
device.mqttCapability.subscribeIsLogin(),
|
|
218
|
+
) { status, isLogin -> Pair(status, isLogin) }
|
|
219
|
+
.collect { (status, isLogin) ->
|
|
220
|
+
val data = JSONObject()
|
|
221
|
+
data.put("state", status.toInt())
|
|
222
|
+
data.put("isLogin", isLogin)
|
|
223
|
+
data.put("type", "MQTT")
|
|
224
|
+
data.put("device", device.toJsonObject())
|
|
225
|
+
sendEvent(EventName.DeviceStateListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
launch {
|
|
229
|
+
device.mqttCapability.subscribeDp().collect { payload ->
|
|
230
|
+
val data = JSONObject()
|
|
231
|
+
data.put("data", JSONObject(payload.toString()))
|
|
232
|
+
data.put("type", "MQTT")
|
|
233
|
+
data.put("device", device.toJsonObject())
|
|
234
|
+
sendEvent(EventName.DeviceDataListener.name, GizRNCallbackManager.jsonObject2WriteableMap(data))
|
|
235
|
+
}
|
|
260
236
|
}
|
|
261
237
|
}
|
|
262
238
|
}
|
|
263
239
|
|
|
240
|
+
private fun requestDeviceListChange() {
|
|
241
|
+
deviceListEventDispatcher.request()
|
|
242
|
+
}
|
|
243
|
+
|
|
264
244
|
/**
|
|
265
245
|
* RN的场景下
|
|
266
246
|
* module profile 变更 相当于设备列表变更
|
|
267
247
|
*/
|
|
268
|
-
suspend fun emitDeviceListChange() {
|
|
269
|
-
val deviceList = deviceListState.first()
|
|
248
|
+
private suspend fun emitDeviceListChange() {
|
|
270
249
|
// 缓存变更
|
|
271
250
|
val deviceListJson = JSONArray()
|
|
272
|
-
|
|
251
|
+
latestDeviceList.forEach{ item ->
|
|
273
252
|
deviceListJson.put(item.toJsonObject())
|
|
274
253
|
}
|
|
275
254
|
sendEvent(EventName.DeviceListListener.name, GizRNCallbackManager.jsonArray2WriteableArray(deviceListJson))
|
|
276
255
|
}
|
|
277
256
|
|
|
257
|
+
override fun invalidate() {
|
|
258
|
+
deviceSubscriptions.cancelAll()
|
|
259
|
+
deviceListEventDispatcher.cancel()
|
|
260
|
+
moduleScope.cancel()
|
|
261
|
+
mReactContext = null
|
|
262
|
+
super.invalidate()
|
|
263
|
+
}
|
|
264
|
+
|
|
278
265
|
fun sendEvent(name:String, data: WritableArray?) {
|
|
279
|
-
mReactContext
|
|
266
|
+
val reactContext = mReactContext ?: return
|
|
267
|
+
reactContext
|
|
280
268
|
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
281
269
|
.emit(name, data)
|
|
282
270
|
}
|
|
283
271
|
fun sendEvent(name:String, data: WritableMap?) {
|
|
284
|
-
mReactContext
|
|
272
|
+
val reactContext = mReactContext ?: return
|
|
273
|
+
reactContext
|
|
285
274
|
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
286
275
|
.emit(name, data)
|
|
287
276
|
}
|
|
@@ -326,6 +315,7 @@ class RNGizSDKManagerModule(reactContext: ReactApplicationContext) : ReactContex
|
|
|
326
315
|
// 查询 绑定设备 返回设备列表
|
|
327
316
|
GizSDKManager.queryBoundDevices()
|
|
328
317
|
deviceListState.take(1).collect {
|
|
318
|
+
latestDeviceList = it
|
|
329
319
|
val jsonData = JSONArray()
|
|
330
320
|
it.forEach{device ->
|
|
331
321
|
jsonData.put(device.toJsonObject())
|
|
@@ -337,7 +327,7 @@ class RNGizSDKManagerModule(reactContext: ReactApplicationContext) : ReactContex
|
|
|
337
327
|
writableMap.putInt("error", 0)
|
|
338
328
|
writableMap.putString("message", "")
|
|
339
329
|
result.invoke(null, writableMap)
|
|
340
|
-
|
|
330
|
+
requestDeviceListChange()
|
|
341
331
|
}
|
|
342
332
|
}
|
|
343
333
|
}
|
package/android/src/test/java/com/gizwits/reactnativegizwitssdkv5/DeviceListEventCoordinatorTest.kt
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
package com.gizwits.reactnativegizwitssdkv5
|
|
2
|
+
|
|
3
|
+
import kotlinx.coroutines.Job
|
|
4
|
+
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
5
|
+
import kotlinx.coroutines.test.advanceTimeBy
|
|
6
|
+
import kotlinx.coroutines.test.runCurrent
|
|
7
|
+
import kotlinx.coroutines.test.runTest
|
|
8
|
+
import org.junit.Assert.assertEquals
|
|
9
|
+
import org.junit.Assert.assertFalse
|
|
10
|
+
import org.junit.Assert.assertTrue
|
|
11
|
+
import org.junit.Test
|
|
12
|
+
|
|
13
|
+
@OptIn(ExperimentalCoroutinesApi::class)
|
|
14
|
+
class DeviceListEventCoordinatorTest {
|
|
15
|
+
@Test
|
|
16
|
+
fun requestBeforeWorkerStartsIsDelivered() = runTest {
|
|
17
|
+
var dispatchCount = 0
|
|
18
|
+
val dispatcher = ConflatedEventDispatcher(this, 500) { dispatchCount++ }
|
|
19
|
+
|
|
20
|
+
assertTrue(dispatcher.request())
|
|
21
|
+
advanceTimeBy(500)
|
|
22
|
+
runCurrent()
|
|
23
|
+
|
|
24
|
+
assertEquals(1, dispatchCount)
|
|
25
|
+
dispatcher.cancel()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@Test
|
|
29
|
+
fun burstRequestsDeliverOnlyTheLatestSnapshot() = runTest {
|
|
30
|
+
var latestSnapshot = 0
|
|
31
|
+
val deliveredSnapshots = mutableListOf<Int>()
|
|
32
|
+
val dispatcher = ConflatedEventDispatcher(this, 500) {
|
|
33
|
+
deliveredSnapshots += latestSnapshot
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
dispatcher.request()
|
|
37
|
+
runCurrent()
|
|
38
|
+
repeat(5) { value ->
|
|
39
|
+
latestSnapshot = value + 1
|
|
40
|
+
advanceTimeBy(75)
|
|
41
|
+
dispatcher.request()
|
|
42
|
+
}
|
|
43
|
+
advanceTimeBy(125)
|
|
44
|
+
runCurrent()
|
|
45
|
+
|
|
46
|
+
assertEquals(listOf(5), deliveredSnapshots)
|
|
47
|
+
dispatcher.cancel()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Test
|
|
51
|
+
fun continuousRequestsStillDispatchAtEveryWindow() = runTest {
|
|
52
|
+
var dispatchCount = 0
|
|
53
|
+
val dispatcher = ConflatedEventDispatcher(this, 500) { dispatchCount++ }
|
|
54
|
+
|
|
55
|
+
dispatcher.request()
|
|
56
|
+
runCurrent()
|
|
57
|
+
repeat(15) {
|
|
58
|
+
advanceTimeBy(100)
|
|
59
|
+
dispatcher.request()
|
|
60
|
+
runCurrent()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
assertTrue(dispatchCount >= 2)
|
|
64
|
+
dispatcher.cancel()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@Test
|
|
68
|
+
fun registryCancelsRemovedDevicesWithoutDuplicatingRetainedSubscriptions() {
|
|
69
|
+
val createdJobs = mutableMapOf<String, MutableList<Job>>()
|
|
70
|
+
val registry = KeyedSubscriptionRegistry<String, String>(
|
|
71
|
+
keySelector = { it },
|
|
72
|
+
subscribe = { key -> Job().also { createdJobs.getOrPut(key) { mutableListOf() }.add(it) } },
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
registry.update(listOf("a", "b"))
|
|
76
|
+
val firstAJob = createdJobs.getValue("a").single()
|
|
77
|
+
val firstBJob = createdJobs.getValue("b").single()
|
|
78
|
+
registry.update(listOf("b"))
|
|
79
|
+
registry.update(listOf("a", "b"))
|
|
80
|
+
|
|
81
|
+
assertFalse(firstAJob.isActive)
|
|
82
|
+
assertTrue(firstBJob.isActive)
|
|
83
|
+
assertEquals(2, createdJobs.getValue("a").size)
|
|
84
|
+
assertEquals(1, createdJobs.getValue("b").size)
|
|
85
|
+
assertEquals(setOf("a", "b"), registry.keys())
|
|
86
|
+
|
|
87
|
+
registry.cancelAll()
|
|
88
|
+
assertTrue(createdJobs.values.flatten().none { it.isActive })
|
|
89
|
+
assertTrue(registry.keys().isEmpty())
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@Test
|
|
93
|
+
fun registryReplacesSubscriptionWhenValueInstanceChangesForTheSameKey() {
|
|
94
|
+
data class Device(val id: String)
|
|
95
|
+
|
|
96
|
+
val jobs = mutableListOf<Job>()
|
|
97
|
+
val registry = KeyedSubscriptionRegistry<String, Device>(
|
|
98
|
+
keySelector = Device::id,
|
|
99
|
+
subscribe = { Job().also(jobs::add) },
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
registry.update(listOf(Device("a")))
|
|
103
|
+
registry.update(listOf(Device("a")))
|
|
104
|
+
|
|
105
|
+
assertEquals(2, jobs.size)
|
|
106
|
+
assertFalse(jobs.first().isActive)
|
|
107
|
+
assertTrue(jobs.last().isActive)
|
|
108
|
+
registry.cancelAll()
|
|
109
|
+
}
|
|
110
|
+
}
|
package/package.json
CHANGED