@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.
@@ -76,6 +76,6 @@ repositories {
76
76
  }
77
77
 
78
78
  dependencies {
79
- api "ai.shen:shenai-sdk:3.1.10"
79
+ api "ai.shen:shenai-sdk:3.1.12"
80
80
  implementation "com.facebook.react:react-native:+"
81
81
  }
@@ -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) {
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shenai/react-native-sdk",
3
- "version": "3.1.10",
3
+ "version": "3.1.12",
4
4
  "private": false,
5
5
  "description": "Shen.AI SDK for React Native.",
6
6
  "main": "lib/module/index.js",
@@ -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.10"
16
+ s.dependency "ShenaiSDK", "3.1.12"
17
17
 
18
18
  if respond_to?(:install_modules_dependencies, true)
19
19
  install_modules_dependencies(s)