@tryvital/vital-devices-react-native 0.2.5

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/LICENSE ADDED
@@ -0,0 +1 @@
1
+ MIT
package/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # vital-devices-react-native
2
+
3
+ This library provides helper methods to BLE Devices for Vital. This allows developers to easily make requests and interact with Vital's API.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install vital-devices-react-native
9
+ cd ios && pod install
10
+
11
+ ```
12
+ ## Getting Started
13
+
14
+ Please refer to our guide to get started - https://docs.tryvital.io/wearables/sdks/react_native
@@ -0,0 +1,150 @@
1
+ buildscript {
2
+ // Buildscript is evaluated before everything else so we can't use getExtOrDefault
3
+ def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['VitalDevicesReactNative_kotlinVersion']
4
+
5
+ repositories {
6
+ google()
7
+ mavenCentral()
8
+ }
9
+
10
+ dependencies {
11
+ classpath 'com.android.tools.build:gradle:3.5.3'
12
+ // noinspection DifferentKotlinGradleVersion
13
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
+ }
15
+ }
16
+
17
+ def isNewArchitectureEnabled() {
18
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
19
+ }
20
+
21
+ apply plugin: 'com.android.library'
22
+ apply plugin: 'kotlin-android'
23
+
24
+ if (isNewArchitectureEnabled()) {
25
+ apply plugin: 'com.facebook.react'
26
+ }
27
+
28
+ def getExtOrDefault(name) {
29
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['VitalDevicesReactNative_' + name]
30
+ }
31
+
32
+ def getExtOrIntegerDefault(name) {
33
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['VitalDevicesReactNative_' + name]).toInteger()
34
+ }
35
+
36
+ android {
37
+ compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
38
+
39
+ defaultConfig {
40
+ minSdkVersion getExtOrIntegerDefault('minSdkVersion')
41
+ targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
42
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
43
+ }
44
+ buildTypes {
45
+ release {
46
+ minifyEnabled false
47
+ }
48
+ }
49
+
50
+ lintOptions {
51
+ disable 'GradleCompatible'
52
+ }
53
+
54
+ compileOptions {
55
+ sourceCompatibility JavaVersion.VERSION_1_8
56
+ targetCompatibility JavaVersion.VERSION_1_8
57
+ }
58
+
59
+ }
60
+
61
+ repositories {
62
+ mavenCentral()
63
+ google()
64
+ maven { url 'https://jitpack.io' }
65
+
66
+ def found = false
67
+ def defaultDir = null
68
+ def androidSourcesName = 'React Native sources'
69
+
70
+ if (rootProject.ext.has('reactNativeAndroidRoot')) {
71
+ defaultDir = rootProject.ext.get('reactNativeAndroidRoot')
72
+ } else {
73
+ defaultDir = new File(
74
+ projectDir,
75
+ '/../../../node_modules/react-native/android'
76
+ )
77
+ }
78
+
79
+ if (defaultDir.exists()) {
80
+ maven {
81
+ url defaultDir.toString()
82
+ name androidSourcesName
83
+ }
84
+
85
+ logger.info(":${project.name}:reactNativeAndroidRoot ${defaultDir.canonicalPath}")
86
+ found = true
87
+ } else {
88
+ def parentDir = rootProject.projectDir
89
+
90
+ 1.upto(5, {
91
+ if (found) return true
92
+ parentDir = parentDir.parentFile
93
+
94
+ def androidSourcesDir = new File(
95
+ parentDir,
96
+ 'node_modules/react-native'
97
+ )
98
+
99
+ def androidPrebuiltBinaryDir = new File(
100
+ parentDir,
101
+ 'node_modules/react-native/android'
102
+ )
103
+
104
+ if (androidPrebuiltBinaryDir.exists()) {
105
+ maven {
106
+ url androidPrebuiltBinaryDir.toString()
107
+ name androidSourcesName
108
+ }
109
+
110
+ logger.info(":${project.name}:reactNativeAndroidRoot ${androidPrebuiltBinaryDir.canonicalPath}")
111
+ found = true
112
+ } else if (androidSourcesDir.exists()) {
113
+ maven {
114
+ url androidSourcesDir.toString()
115
+ name androidSourcesName
116
+ }
117
+
118
+ logger.info(":${project.name}:reactNativeAndroidRoot ${androidSourcesDir.canonicalPath}")
119
+ found = true
120
+ }
121
+ })
122
+ }
123
+
124
+ if (!found) {
125
+ throw new GradleException(
126
+ "${project.name}: unable to locate React Native android sources. " +
127
+ "Ensure you have you installed React Native as a dependency in your project and try again."
128
+ )
129
+ }
130
+ }
131
+
132
+ def kotlin_version = getExtOrDefault('kotlinVersion')
133
+
134
+ dependencies {
135
+ //noinspection GradleDynamicVersion
136
+ implementation "com.facebook.react:react-native:+"
137
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
138
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
139
+ implementation 'com.github.tryVital.vital-android:VitalClient:v1.0.0-alpha.7'
140
+ implementation 'com.github.tryVital.vital-android:VitalDevices:v1.0.0-alpha.7'
141
+ // From node_modules
142
+ }
143
+
144
+ if (isNewArchitectureEnabled()) {
145
+ react {
146
+ jsRootDir = file("../src/")
147
+ libraryName = "VitalDevicesReactNative"
148
+ codegenJavaPackageName = "com.vitaldevicesreactnative"
149
+ }
150
+ }
@@ -0,0 +1,5 @@
1
+ VitalDevicesReactNative_kotlinVersion=1.7.0
2
+ VitalDevicesReactNative_minSdkVersion=26
3
+ VitalDevicesReactNative_targetSdkVersion=33
4
+ VitalDevicesReactNative_compileSdkVersion=33
5
+ VitalDevicesReactNative_ndkversion=21.4.7075529
@@ -0,0 +1,4 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.vitaldevicesreactnative">
3
+
4
+ </manifest>
@@ -0,0 +1,24 @@
1
+ package com.vitaldevicesreactnative
2
+
3
+ sealed class VitalDevicesEvent(val value: String) {
4
+ object ScanEvent : VitalDevicesEvent("ScanEvent")
5
+ object PairEvent : VitalDevicesEvent("PairEvent")
6
+ object GlucoseMeterReadEvent : VitalDevicesEvent("GlucoseMeterReadEvent")
7
+ object BloodPressureReadEvent : VitalDevicesEvent("BloodPressureReadEvent")
8
+
9
+ companion object {
10
+ fun values(): Array<VitalDevicesEvent> {
11
+ return arrayOf(ScanEvent, PairEvent, GlucoseMeterReadEvent, BloodPressureReadEvent)
12
+ }
13
+
14
+ fun valueOf(value: String): VitalDevicesEvent {
15
+ return when (value) {
16
+ "ScanEvent" -> ScanEvent
17
+ "PairEvent" -> PairEvent
18
+ "GlucoseMeterReadEvent" -> GlucoseMeterReadEvent
19
+ "BloodPressureReadEvent" -> BloodPressureReadEvent
20
+ else -> throw IllegalArgumentException("Invalid VitalDevicesEvent value: $value")
21
+ }
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,272 @@
1
+ package com.vitaldevicesreactnative
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.bridge.*
5
+ import com.facebook.react.modules.core.DeviceEventManagerModule
6
+ import io.tryvital.client.services.data.QuantitySample
7
+ import io.tryvital.vitaldevices.*
8
+ import kotlinx.coroutines.*
9
+ import kotlinx.coroutines.flow.flowOn
10
+
11
+
12
+ class VitalDevicesReactNativeModule(reactContext: ReactApplicationContext) :
13
+ ReactContextBaseJavaModule(reactContext) {
14
+
15
+ private val vitalDeviceManager = VitalDeviceManager(reactContext)
16
+ private val scannedDevices: MutableList<ScannedDevice> = mutableListOf()
17
+ private var mainScope: CoroutineScope? = null
18
+
19
+ override fun getName(): String {
20
+ return NAME
21
+ }
22
+
23
+ @ReactMethod
24
+ private fun startScanForDevice(
25
+ id: String,
26
+ name: String,
27
+ brand: String,
28
+ kind: String,
29
+ promise: Promise
30
+ ) {
31
+ val deviceModel = DeviceModel(
32
+ id = id,
33
+ name = name,
34
+ brand = stringToBrand(brand),
35
+ kind = stringToKind(kind),
36
+ )
37
+ mainScope?.cancel()
38
+ mainScope = MainScope()
39
+ mainScope?.launch {
40
+ try {
41
+ vitalDeviceManager.search(deviceModel).flowOn(Dispatchers.IO).collect {
42
+ withContext(Dispatchers.Main) {
43
+ scannedDevices.add(it)
44
+ sendEvent(VitalDevicesEvent.ScanEvent, WritableNativeMap().apply {
45
+ putString("id", it.address)
46
+ putString("name", it.name)
47
+ putString("id", it.address)
48
+ putMap("deviceModel", WritableNativeMap().apply {
49
+ putString("name", it.deviceModel.name)
50
+ putString("brand", brandToString(it.deviceModel.brand))
51
+ putString("kind", kindToString(it.deviceModel.kind))
52
+ })
53
+ }
54
+ )
55
+ }
56
+ }
57
+ } catch (e: Exception) {
58
+ withContext(Dispatchers.Main) {
59
+ sendEvent(VitalDevicesEvent.ScanEvent, WritableNativeMap().apply {
60
+ putString("error", e.message)
61
+ })
62
+ }
63
+ }
64
+ }
65
+ promise.resolve(null)
66
+ }
67
+
68
+ @ReactMethod
69
+ fun stopScanForDevice(promise: Promise) {
70
+ mainScope?.cancel()
71
+ promise.resolve(null)
72
+ }
73
+
74
+ @ReactMethod
75
+ fun pair(scannedDeviceId: String, promise: Promise) {
76
+ val scannedDevice = scannedDevices.firstOrNull { it.address == scannedDeviceId }
77
+
78
+ if (scannedDevice == null) {
79
+ sendEvent(VitalDevicesEvent.PairEvent, WritableNativeMap().apply {
80
+ putString("error", "Device not found")
81
+ })
82
+ } else {
83
+ mainScope?.cancel()
84
+ mainScope = MainScope()
85
+ mainScope?.launch {
86
+ try {
87
+ withContext(Dispatchers.Default) {
88
+ vitalDeviceManager.pair(scannedDevice).collect {
89
+ withContext(Dispatchers.Main) {
90
+ sendEvent(VitalDevicesEvent.PairEvent, it)
91
+ }
92
+ }
93
+ }
94
+ } catch (e: Exception) {
95
+ withContext(Dispatchers.Main) {
96
+ sendEvent(VitalDevicesEvent.PairEvent, WritableNativeMap().apply {
97
+ putString("error", e.message)
98
+ })
99
+ }
100
+ }
101
+ }
102
+ promise.resolve(null)
103
+ }
104
+ }
105
+
106
+ @ReactMethod
107
+ private fun startReadingGlucoseMeter(scannedDeviceId: String, promise: Promise) {
108
+ val scannedDevice = scannedDevices.firstOrNull { it.address == scannedDeviceId }
109
+
110
+ if (scannedDevice == null) {
111
+ sendEvent(VitalDevicesEvent.GlucoseMeterReadEvent, WritableNativeMap().apply {
112
+ putString("error", "Device not found")
113
+ })
114
+ } else {
115
+ mainScope?.cancel()
116
+ mainScope = MainScope()
117
+ mainScope?.launch {
118
+ try {
119
+ withContext(Dispatchers.Default) {
120
+ vitalDeviceManager.glucoseMeter(reactApplicationContext, scannedDevice)
121
+ .flowOn(Dispatchers.IO)
122
+ .collect { samples ->
123
+ withContext(Dispatchers.Main) {
124
+ sendEvent(VitalDevicesEvent.GlucoseMeterReadEvent, WritableNativeMap().apply {
125
+ putArray("samples", WritableNativeArray().apply {
126
+ samples.forEach {
127
+ pushMap(WritableNativeMap().apply {
128
+ mapSample(it)
129
+ })
130
+ }
131
+ })
132
+ })
133
+ }
134
+ }
135
+ }
136
+ } catch (e: Exception) {
137
+ withContext(Dispatchers.Main) {
138
+ sendEvent(VitalDevicesEvent.GlucoseMeterReadEvent, WritableNativeMap().apply {
139
+ putString("error", e.message)
140
+ })
141
+ }
142
+ }
143
+ }
144
+ promise.resolve(null)
145
+ }
146
+ }
147
+
148
+ @ReactMethod
149
+ private fun startReadingBloodPressure(scannedDeviceId: String, promise: Promise) {
150
+ val scannedDevice = scannedDevices.firstOrNull { it.address == scannedDeviceId }
151
+
152
+ if (scannedDevice == null) {
153
+ sendEvent(VitalDevicesEvent.BloodPressureReadEvent, WritableNativeMap().apply {
154
+ putString("error", "Device not found")
155
+ })
156
+ } else {
157
+ mainScope?.cancel()
158
+ mainScope = MainScope()
159
+ mainScope?.launch {
160
+ try {
161
+ withContext(Dispatchers.Default) {
162
+ vitalDeviceManager.bloodPressure(reactApplicationContext, scannedDevice)
163
+ .flowOn(Dispatchers.IO)
164
+ .collect { samples ->
165
+ withContext(Dispatchers.Main) {
166
+ sendEvent(VitalDevicesEvent.BloodPressureReadEvent, WritableNativeMap().apply {
167
+ putArray("samples", WritableNativeArray().apply {
168
+ samples.forEach {
169
+ pushMap(WritableNativeMap().apply {
170
+ putMap("systolic", WritableNativeMap().apply {
171
+ mapSample(it.systolic)
172
+ })
173
+ putMap("diastolic", WritableNativeMap().apply {
174
+ mapSample(it.diastolic)
175
+ })
176
+ putMap("pulse", WritableNativeMap().apply {
177
+ mapSample(it.pulse)
178
+ })
179
+ })
180
+ }
181
+ })
182
+ })
183
+ }
184
+ }
185
+ }
186
+ } catch (e: Exception) {
187
+ withContext(Dispatchers.Main) {
188
+ sendEvent(VitalDevicesEvent.BloodPressureReadEvent, WritableNativeMap().apply {
189
+ putString("error", e.message)
190
+ })
191
+ }
192
+
193
+ }
194
+ }
195
+
196
+ promise.resolve(null)
197
+ }
198
+ }
199
+
200
+ private fun WritableNativeMap.mapSample(it: QuantitySample) {
201
+ putString("id", it.id)
202
+ putString("value", it.value)
203
+ putString("unit", it.unit)
204
+ putString("startDate", it.startDate.time.toString())
205
+ putString("endDate", it.endDate.time.toString())
206
+ putString("type", it.type)
207
+ }
208
+
209
+ @ReactMethod
210
+ fun addListener(eventName: String?) {
211
+ // Keep: Required for RN built in Event Emitter Calls.
212
+ }
213
+
214
+ @ReactMethod
215
+ fun removeListeners(count: Int) {
216
+ // Keep: Required for RN built in Event Emitter Calls.
217
+ }
218
+
219
+ private fun sendEvent(event: VitalDevicesEvent, params: Any) {
220
+ try {
221
+ reactApplicationContext
222
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
223
+ .emit(event.value, params)
224
+ } catch (e: Exception) {
225
+ Log.e("VitalDevices", "sendEvent: $e")
226
+ }
227
+ }
228
+
229
+ override fun getConstants(): MutableMap<String, Any> {
230
+ return VitalDevicesEvent.values().associate { it.value to it.value }.toMutableMap()
231
+ }
232
+
233
+ private fun kindToString(kind: Kind): String {
234
+ return when (kind) {
235
+ Kind.GlucoseMeter -> "glucoseMeter"
236
+ Kind.BloodPressure -> "bloodPressure"
237
+ }
238
+ }
239
+
240
+ private fun brandToString(brand: Brand): String {
241
+ return when (brand) {
242
+ Brand.Omron -> "omron"
243
+ Brand.AccuChek -> "accuChek"
244
+ Brand.Contour -> "contour"
245
+ Brand.Beurer -> "beurer"
246
+ Brand.Libre -> "libre"
247
+ }
248
+ }
249
+
250
+ private fun stringToBrand(string: String): Brand {
251
+ return when (string) {
252
+ "omron" -> Brand.Omron
253
+ "accuChek" -> Brand.AccuChek
254
+ "contour" -> Brand.Contour
255
+ "beurer" -> Brand.Beurer
256
+ "libre" -> Brand.Libre
257
+ else -> throw Exception("Unsupported brand $string")
258
+ }
259
+ }
260
+
261
+ private fun stringToKind(string: String): Kind {
262
+ return when (string) {
263
+ "bloodPressure" -> Kind.BloodPressure
264
+ "glucoseMeter" -> Kind.GlucoseMeter
265
+ else -> throw Exception("Unsupported kind $string")
266
+ }
267
+ }
268
+
269
+ companion object {
270
+ const val NAME = "VitalDevicesReactNative"
271
+ }
272
+ }
@@ -0,0 +1,16 @@
1
+ package com.vitaldevicesreactnative
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class VitalDevicesReactNativePackage : ReactPackage {
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
+ return listOf(VitalDevicesReactNativeModule(reactContext))
11
+ }
12
+
13
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
14
+ return emptyList()
15
+ }
16
+ }
@@ -0,0 +1,2 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTViewManager.h>
@@ -0,0 +1,14 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface RCT_EXTERN_MODULE(VitalDevicesReactNative, NSObject)
4
+
5
+ RCT_EXTERN_METHOD(multiply:(float)a withB:(float)b
6
+ withResolver:(RCTPromiseResolveBlock)resolve
7
+ withRejecter:(RCTPromiseRejectBlock)reject)
8
+
9
+ + (BOOL)requiresMainQueueSetup
10
+ {
11
+ return NO;
12
+ }
13
+
14
+ @end
@@ -0,0 +1,8 @@
1
+ @objc(VitalDevicesReactNative)
2
+ class VitalDevicesReactNative: NSObject {
3
+
4
+ @objc(multiply:withB:withResolver:withRejecter:)
5
+ func multiply(a: Float, b: Float, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Void {
6
+ resolve(a*b)
7
+ }
8
+ }