@shenai/react-native-sdk 3.1.10 → 3.1.12
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 +1 -1
- package/android/src/main/java/com/shenaisdk/ShenaiSdkModule.java +39 -2
- package/ios/ShenaiSdkNativeModule.m +60 -0
- package/lib/module/index.js +30 -0
- package/lib/typescript/src/index.d.ts +10 -0
- package/package.json +1 -1
- package/react-native-shenai-sdk.podspec +1 -1
package/android/build.gradle
CHANGED
|
@@ -18,9 +18,10 @@ import android.app.Activity;
|
|
|
18
18
|
import androidx.annotation.NonNull;
|
|
19
19
|
import androidx.annotation.Nullable;
|
|
20
20
|
import android.util.Base64;
|
|
21
|
-
import java.util.Optional;
|
|
22
|
-
import java.util.List;
|
|
23
21
|
import java.util.ArrayList;
|
|
22
|
+
import java.util.List;
|
|
23
|
+
import java.util.Locale;
|
|
24
|
+
import java.util.Optional;
|
|
24
25
|
|
|
25
26
|
public class ShenaiSdkModule extends ReactContextBaseJavaModule {
|
|
26
27
|
private static final ShenAIAndroidSDK shenai_sdk = new ShenAIAndroidSDK();
|
|
@@ -403,6 +404,42 @@ public class ShenaiSdkModule extends ReactContextBaseJavaModule {
|
|
|
403
404
|
promise.resolve(result.ordinal());
|
|
404
405
|
}
|
|
405
406
|
|
|
407
|
+
@ReactMethod
|
|
408
|
+
public void readBpMonitorPhoto(String base64Image, String contentType, Promise promise) {
|
|
409
|
+
byte[] image;
|
|
410
|
+
try {
|
|
411
|
+
image = Base64.decode(base64Image, Base64.DEFAULT);
|
|
412
|
+
} catch (IllegalArgumentException error) {
|
|
413
|
+
image = new byte[0];
|
|
414
|
+
}
|
|
415
|
+
shenai_sdk.readBpMonitorPhoto(image, contentType, (reading, issue) -> UiThreadUtil.runOnUiThread(() -> {
|
|
416
|
+
if (reading != null) {
|
|
417
|
+
WritableMap result = Arguments.createMap();
|
|
418
|
+
result.putInt("systolicMmhg", reading.systolicMmhg);
|
|
419
|
+
result.putInt("diastolicMmhg", reading.diastolicMmhg);
|
|
420
|
+
if (reading.pulseBpm != null) result.putInt("pulseBpm", reading.pulseBpm);
|
|
421
|
+
promise.resolve(result);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
String issueName = issue == null
|
|
425
|
+
? "analysis-failed"
|
|
426
|
+
: issue.name().toLowerCase(Locale.ROOT).replace('_', '-');
|
|
427
|
+
promise.resolve(issueName);
|
|
428
|
+
}));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
@ReactMethod
|
|
432
|
+
public void submitCalibrationReading(ReadableMap reading, Promise promise) {
|
|
433
|
+
Integer pulse = reading.hasKey("pulseBpm") && !reading.isNull("pulseBpm")
|
|
434
|
+
? reading.getInt("pulseBpm")
|
|
435
|
+
: null;
|
|
436
|
+
shenai_sdk.submitCalibrationReading(
|
|
437
|
+
reading.getInt("systolicMmhg"),
|
|
438
|
+
reading.getInt("diastolicMmhg"),
|
|
439
|
+
pulse,
|
|
440
|
+
accepted -> UiThreadUtil.runOnUiThread(() -> promise.resolve(accepted)));
|
|
441
|
+
}
|
|
442
|
+
|
|
406
443
|
@ReactMethod
|
|
407
444
|
public void setPrecisionMode(int precisionMode, Promise promise) {
|
|
408
445
|
shenai_sdk.setPrecisionMode(ShenAIAndroidSDK.PrecisionMode.values()[precisionMode]);
|
|
@@ -24,6 +24,30 @@ static id ShenaiMeasurementQualityMetricsToDictionary(MeasurementQualityMetrics
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
static NSString *ShenaiCuffReadingIssueName(CuffReadingIssue issue) {
|
|
28
|
+
switch (issue) {
|
|
29
|
+
case CuffReadingIssueInvalidImage:
|
|
30
|
+
return @"invalid-image";
|
|
31
|
+
case CuffReadingIssueServiceUnavailable:
|
|
32
|
+
return @"service-unavailable";
|
|
33
|
+
case CuffReadingIssueAnalysisFailed:
|
|
34
|
+
return @"analysis-failed";
|
|
35
|
+
case CuffReadingIssueValuesNotFound:
|
|
36
|
+
return @"values-not-found";
|
|
37
|
+
case CuffReadingIssueInvalidMonitorCount:
|
|
38
|
+
return @"invalid-monitor-count";
|
|
39
|
+
case CuffReadingIssueInvalidSystolic:
|
|
40
|
+
return @"invalid-systolic";
|
|
41
|
+
case CuffReadingIssueInvalidDiastolic:
|
|
42
|
+
return @"invalid-diastolic";
|
|
43
|
+
case CuffReadingIssueInvalidPulse:
|
|
44
|
+
return @"invalid-pulse";
|
|
45
|
+
case CuffReadingIssueInvalidPressureRelation:
|
|
46
|
+
return @"invalid-pressure-relation";
|
|
47
|
+
}
|
|
48
|
+
return @"analysis-failed";
|
|
49
|
+
}
|
|
50
|
+
|
|
27
51
|
@interface ShenaiSdkNativeModule : RCTEventEmitter <RCTBridgeModule>
|
|
28
52
|
|
|
29
53
|
@end
|
|
@@ -479,6 +503,42 @@ RCT_EXPORT_METHOD(getCalibrationState : (RCTPromiseResolveBlock)resolve rejecter
|
|
|
479
503
|
resolve(@(mode));
|
|
480
504
|
}
|
|
481
505
|
|
|
506
|
+
RCT_EXPORT_METHOD(readBpMonitorPhoto
|
|
507
|
+
: (NSString *)base64Image contentType
|
|
508
|
+
: (NSString *)contentType resolver
|
|
509
|
+
: (RCTPromiseResolveBlock)resolve rejecter
|
|
510
|
+
: (RCTPromiseRejectBlock)reject) {
|
|
511
|
+
NSData *image = [[NSData alloc] initWithBase64EncodedString:base64Image options:0];
|
|
512
|
+
[ShenaiSDK readBpMonitorPhoto:image ?: [NSData data]
|
|
513
|
+
contentType:contentType ?: @"image/jpeg"
|
|
514
|
+
completion:^(CuffReading *reading, NSNumber *issue) {
|
|
515
|
+
if (reading != nil) {
|
|
516
|
+
NSMutableDictionary *result = [@{
|
|
517
|
+
@"systolicMmhg" : @(reading.systolicMmhg),
|
|
518
|
+
@"diastolicMmhg" : @(reading.diastolicMmhg),
|
|
519
|
+
} mutableCopy];
|
|
520
|
+
if (reading.pulseBpm != nil) result[@"pulseBpm"] = reading.pulseBpm;
|
|
521
|
+
resolve(result);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
resolve(ShenaiCuffReadingIssueName((CuffReadingIssue)issue.integerValue));
|
|
525
|
+
}];
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
RCT_EXPORT_METHOD(submitCalibrationReading
|
|
529
|
+
: (NSDictionary *)reading resolver
|
|
530
|
+
: (RCTPromiseResolveBlock)resolve rejecter
|
|
531
|
+
: (RCTPromiseRejectBlock)reject) {
|
|
532
|
+
NSNumber *pulse = reading[@"pulseBpm"];
|
|
533
|
+
CuffReading *nativeReading = [[CuffReading alloc] initWithSystolicMmhg:[reading[@"systolicMmhg"] integerValue]
|
|
534
|
+
diastolicMmhg:[reading[@"diastolicMmhg"] integerValue]
|
|
535
|
+
pulseBpm:pulse];
|
|
536
|
+
[ShenaiSDK submitCalibrationReading:nativeReading
|
|
537
|
+
completion:^(BOOL accepted) {
|
|
538
|
+
resolve(@(accepted));
|
|
539
|
+
}];
|
|
540
|
+
}
|
|
541
|
+
|
|
482
542
|
RCT_EXPORT_METHOD(setPrecisionMode : (NSInteger)mode) { [ShenaiSDK setPrecisionMode:mode]; }
|
|
483
543
|
|
|
484
544
|
RCT_EXPORT_METHOD(getPrecisionMode : (RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
package/lib/module/index.js
CHANGED
|
@@ -211,6 +211,36 @@ export async function getCalibrationState() {
|
|
|
211
211
|
ensureNativeModuleAvailable();
|
|
212
212
|
return ShenaiSdkNativeModule.getCalibrationState();
|
|
213
213
|
}
|
|
214
|
+
function bpMonitorPhotoBlobToDataUri(image) {
|
|
215
|
+
return new Promise(resolve => {
|
|
216
|
+
const reader = new FileReader();
|
|
217
|
+
reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : "");
|
|
218
|
+
reader.onerror = () => resolve("");
|
|
219
|
+
reader.readAsDataURL(image);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function splitBpMonitorPhotoDataUri(value) {
|
|
223
|
+
const match = /^data:([^;,]+)?;base64,([\s\S]*)$/.exec(value.trim());
|
|
224
|
+
const base64 = (match?.[2] ?? value).replace(/\s/g, "").replace(/-/g, "+").replace(/_/g, "/");
|
|
225
|
+
const inferredContentType = base64.startsWith("iVBOR") ? "image/png" : "image/jpeg";
|
|
226
|
+
return {
|
|
227
|
+
base64,
|
|
228
|
+
contentType: match?.[1] ?? inferredContentType
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
export async function readBpMonitorPhoto(image) {
|
|
232
|
+
ensureNativeModuleAvailable();
|
|
233
|
+
const encoded = typeof image === "string" ? image : await bpMonitorPhotoBlobToDataUri(image);
|
|
234
|
+
const {
|
|
235
|
+
base64,
|
|
236
|
+
contentType
|
|
237
|
+
} = splitBpMonitorPhotoDataUri(encoded);
|
|
238
|
+
return ShenaiSdkNativeModule.readBpMonitorPhoto(base64, contentType);
|
|
239
|
+
}
|
|
240
|
+
export async function submitCalibrationReading(reading) {
|
|
241
|
+
ensureNativeModuleAvailable();
|
|
242
|
+
return ShenaiSdkNativeModule.submitCalibrationReading(reading);
|
|
243
|
+
}
|
|
214
244
|
export async function setPrecisionMode(precisionMode) {
|
|
215
245
|
ensureNativeModuleAvailable();
|
|
216
246
|
await ShenaiSdkNativeModule.setPrecisionMode(precisionMode);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { type ViewProps } from "react-native";
|
|
2
3
|
export declare const ShenaiSdkView: import("react-native").HostComponent<ViewProps> | (() => never);
|
|
3
4
|
export declare const enum InitializationResult {
|
|
@@ -16,6 +17,13 @@ export declare const enum CalibrationState {
|
|
|
16
17
|
NOT_CALIBRATED = 1,
|
|
17
18
|
OUTDATED = 2
|
|
18
19
|
}
|
|
20
|
+
export interface Reading {
|
|
21
|
+
systolicMmhg: number;
|
|
22
|
+
diastolicMmhg: number;
|
|
23
|
+
pulseBpm?: number;
|
|
24
|
+
}
|
|
25
|
+
export type ReadingIssue = "invalid-image" | "service-unavailable" | "analysis-failed" | "values-not-found" | "invalid-monitor-count" | "invalid-systolic" | "invalid-diastolic" | "invalid-pulse" | "invalid-pressure-relation";
|
|
26
|
+
export type ReadingResult = Reading | ReadingIssue;
|
|
19
27
|
export declare const enum PrecisionMode {
|
|
20
28
|
STRICT = 0,
|
|
21
29
|
RELAXED = 1
|
|
@@ -204,6 +212,8 @@ export declare function resetMeasurementSession(): Promise<void>;
|
|
|
204
212
|
export declare function isReadyToStartMeasurement(): Promise<boolean>;
|
|
205
213
|
export declare function areRequiredModelsDownloaded(): Promise<boolean>;
|
|
206
214
|
export declare function getCalibrationState(): Promise<CalibrationState>;
|
|
215
|
+
export declare function readBpMonitorPhoto(image: string | Blob): Promise<ReadingResult>;
|
|
216
|
+
export declare function submitCalibrationReading(reading: Reading): Promise<boolean>;
|
|
207
217
|
export declare function setPrecisionMode(precisionMode: PrecisionMode): Promise<void>;
|
|
208
218
|
export declare function getPrecisionMode(): Promise<PrecisionMode>;
|
|
209
219
|
export declare function setRelaxedModeConfidenceThreshold(threshold: number): Promise<void>;
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
|
|
|
13
13
|
s.platforms = { :ios => "14.0" }
|
|
14
14
|
s.source = { :path => "." }
|
|
15
15
|
s.source_files = "ios/**/*.{h,m,mm}"
|
|
16
|
-
s.dependency "ShenaiSDK", "3.1.
|
|
16
|
+
s.dependency "ShenaiSDK", "3.1.12"
|
|
17
17
|
|
|
18
18
|
if respond_to?(:install_modules_dependencies, true)
|
|
19
19
|
install_modules_dependencies(s)
|