├── .circleci └── config.yml ├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .gitmodules ├── .prettierrc.json ├── .sentiance ├── doc-pr-gen.sh └── doc-spec.json ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets └── ios-background-modes.png ├── babel.config.json ├── dev.md ├── docs ├── adding_new_transport_mode.md ├── images │ └── transport_mode_search_query.png └── react-native.md ├── images ├── create_new_module.png ├── initial_project_structure(2).png ├── initial_project_structure.png └── updated_project_structure.png ├── jest.config.js ├── jest ├── mockNativeModules.js └── test_util.js ├── package.json ├── packages ├── build.gradle ├── core │ ├── .npmignore │ ├── README.md │ ├── RNSentianceCore.podspec │ ├── __tests__ │ │ └── core.test.js │ ├── android │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── package-json-reader.gradle │ │ ├── sentiance-version-finder.gradle │ │ └── src │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── sentiance │ │ │ │ └── react │ │ │ │ └── bridge │ │ │ │ └── core │ │ │ │ ├── SentianceConverter.java │ │ │ │ ├── SentianceEmitter.java │ │ │ │ ├── SentianceHelper.java │ │ │ │ ├── SentianceModule.java │ │ │ │ ├── SentiancePackage.java │ │ │ │ ├── UserLinker.java │ │ │ │ ├── common │ │ │ │ ├── SentianceSubscriptionsManager.java │ │ │ │ ├── base │ │ │ │ │ ├── AbstractSentianceEmitter.java │ │ │ │ │ └── AbstractSentianceModule.java │ │ │ │ └── util │ │ │ │ │ ├── ErrorCodes.java │ │ │ │ │ └── SingleParamRunnable.java │ │ │ │ └── utils │ │ │ │ ├── SentianceUtils.java │ │ │ │ └── UserCreationCompletionHandler.java │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── sentiance │ │ │ └── react │ │ │ └── bridge │ │ │ └── core │ │ │ ├── SentianceConverterTest.java │ │ │ └── SentianceModuleTest.java │ ├── ios │ │ ├── RNSentianceCore+Converter.h │ │ ├── RNSentianceCore+Converter.m │ │ ├── RNSentianceCore.h │ │ ├── RNSentianceCore.m │ │ ├── RNSentianceCore.xcodeproj │ │ │ └── project.pbxproj │ │ ├── RNSentianceErrorCodes.h │ │ ├── RNSentianceErrorCodes.m │ │ ├── RNSentianceFoundation.h │ │ ├── RNSentianceHelper.h │ │ ├── RNSentianceHelper.m │ │ ├── RNSentianceNativeInitialization.h │ │ ├── RNSentianceNativeInitialization.m │ │ ├── RNSentianceSubscription.h │ │ ├── RNSentianceSubscription.m │ │ ├── RNSentianceSubscriptionDefinition.h │ │ ├── RNSentianceSubscriptionDefinition.m │ │ ├── RNSentianceSubscriptionsManager.h │ │ └── RNSentianceSubscriptionsManager.m │ ├── jest │ │ └── mockNativeModule.js │ ├── lib │ │ ├── core.js │ │ ├── generated │ │ │ ├── native-module.d.ts │ │ │ ├── native-module.js │ │ │ ├── require-native-module.d.ts │ │ │ ├── require-native-module.js │ │ │ ├── sentiance-event-emitter.d.ts │ │ │ ├── sentiance-event-emitter.js │ │ │ ├── subscription-id-gen.d.ts │ │ │ ├── subscription-id-gen.js │ │ │ ├── utils.d.ts │ │ │ └── utils.js │ │ ├── index.d.ts │ │ └── index.js │ ├── package.json │ ├── src │ │ ├── native-module.ts │ │ ├── require-native-module.ts │ │ ├── sentiance-event-emitter.ts │ │ ├── subscription-id-gen.ts │ │ └── utils.ts │ └── tsconfig.json ├── crash-detection │ ├── .npmignore │ ├── README.md │ ├── SentianceCrashDetection.podspec │ ├── android │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── com │ │ │ └── sentiance │ │ │ └── react │ │ │ └── bridge │ │ │ └── crashdetection │ │ │ ├── CrashDetectionEmitter.java │ │ │ ├── SentianceCrashDetectionConverter.java │ │ │ ├── SentianceCrashDetectionModule.java │ │ │ └── SentianceCrashDetectionPackage.java │ ├── ios │ │ ├── SentianceCrashDetection.xcodeproj │ │ │ └── project.pbxproj │ │ ├── SentianceCrashDetection │ │ │ ├── SentianceCrashDetection-Bridging-Header.h │ │ │ ├── SentianceCrashDetection.m │ │ │ └── SentianceCrashDetection.swift │ │ └── SentianceCrashDetectionTests │ │ │ └── SentianceCrashDetectionTests.swift │ ├── lib │ │ ├── crash-detection.js │ │ ├── index.d.ts │ │ └── index.js │ └── package.json ├── driving-insights │ ├── .npmignore │ ├── __tests__ │ │ ├── avg-overall-safety-score-api.test.js │ │ └── driving-insights.test.js │ ├── android │ │ ├── .gitignore │ │ ├── build.gradle │ │ └── src │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── sentiance │ │ │ │ └── react │ │ │ │ └── bridge │ │ │ │ └── drivinginsights │ │ │ │ ├── DrivingInsightsConverter.java │ │ │ │ ├── DrivingInsightsEmitter.java │ │ │ │ ├── DrivingInsightsModule.java │ │ │ │ └── SentianceDrivingInsightsPackage.java │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── sentiance │ │ │ └── react │ │ │ └── bridge │ │ │ └── drivinginsights │ │ │ ├── DrivingInsightsConverterTest.java │ │ │ ├── DrivingInsightsModuleTest.java │ │ │ └── util │ │ │ └── validators │ │ │ ├── CallWhileMovingEventBridgeValidator.java │ │ │ ├── DrivingEventBridgeValidator.java │ │ │ ├── DrivingInsightsBridgeValidator.java │ │ │ ├── HarshEventBridgeValidator.java │ │ │ ├── PhoneUsageEventBridgeValidator.java │ │ │ ├── SafetyScoreParametersValidator.java │ │ │ └── SpeedingEventBridgeValidator.java │ ├── jest │ │ └── mockNativeModule.js │ ├── lib │ │ ├── avg-overall-safety-score-api.js │ │ ├── driving-insights.js │ │ ├── index.d.ts │ │ └── index.js │ └── package.json ├── event-timeline │ ├── .npmignore │ ├── README.md │ ├── __tests__ │ │ ├── feedback │ │ │ └── feedback.test.js │ │ └── timeline │ │ │ └── event-timeline.test.js │ ├── android │ │ ├── .gitignore │ │ ├── build.gradle │ │ └── src │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── sentiance │ │ │ │ └── react │ │ │ │ └── bridge │ │ │ │ └── eventtimeline │ │ │ │ ├── ErrorCodes.java │ │ │ │ ├── EventTimelineEmitter.java │ │ │ │ ├── SentianceEventTimelineModule.java │ │ │ │ ├── SentianceEventTimelinePackage.java │ │ │ │ ├── SentianceFeedbackModule.java │ │ │ │ └── converters │ │ │ │ ├── OnDeviceTypesConverter.java │ │ │ │ └── TransportTagsConverter.java │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── sentiance │ │ │ └── react │ │ │ └── bridge │ │ │ └── eventtimeline │ │ │ ├── SentianceEventTimelineModuleTest.java │ │ │ ├── converters │ │ │ └── OnDeviceTypesConverterTest.java │ │ │ └── validators │ │ │ ├── EventBridgeValidator.java │ │ │ └── TransportTagsValidator.java │ ├── lib │ │ ├── errors.d.ts │ │ ├── errors.js │ │ ├── feedback │ │ │ ├── feedback-native-module.d.ts │ │ │ ├── feedback-native-module.js │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ ├── types.d.ts │ │ │ └── types.js │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── timeline │ │ │ ├── event-timeline-native-module.d.ts │ │ │ ├── event-timeline-native-module.js │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ ├── timeline-event-emitter.d.ts │ │ │ ├── timeline-event-emitter.js │ │ │ ├── types.d.ts │ │ │ └── types.js │ │ ├── types.d.ts │ │ └── types.js │ ├── package.json │ ├── src │ │ ├── errors.ts │ │ ├── feedback │ │ │ ├── feedback-native-module.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── index.ts │ │ ├── timeline │ │ │ ├── event-timeline-native-module.ts │ │ │ ├── index.ts │ │ │ ├── timeline-event-emitter.ts │ │ │ └── types.ts │ │ └── types.ts │ └── tsconfig.json ├── gradle-scripts │ └── common-test-config.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── legacy │ ├── .npmignore │ ├── README.md │ ├── android │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── com │ │ │ └── sentiance │ │ │ └── react │ │ │ └── bridge │ │ │ └── legacy │ │ │ ├── LegacySentianceModule.java │ │ │ ├── LegacySentiancePackage.java │ │ │ ├── RNSentianceHelper.java │ │ │ └── StartFinishedHandlerCreator.java │ ├── lib │ │ ├── index.d.ts │ │ ├── index.js │ │ └── legacy │ │ │ └── index.js │ └── package.json ├── settings.gradle ├── smart-geofences │ ├── README.md │ ├── __tests__ │ │ └── smart-geofences.test.js │ ├── android │ │ ├── .gitignore │ │ ├── build.gradle │ │ └── src │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── sentiance │ │ │ │ └── react │ │ │ │ └── bridge │ │ │ │ └── smartgeofences │ │ │ │ ├── SmartGeofenceEmitter.java │ │ │ │ ├── SmartGeofencesModule.java │ │ │ │ ├── SmartGeofencesPackage.java │ │ │ │ ├── converters │ │ │ │ └── SmartGeofencesConverter.java │ │ │ │ └── utils │ │ │ │ └── ErrorCodes.java │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── sentiance │ │ │ └── react │ │ │ └── bridge │ │ │ └── smartgeofences │ │ │ ├── SmartGeofencesModuleTest.java │ │ │ ├── converters │ │ │ └── SmartGeofencesConverterTest.java │ │ │ └── util │ │ │ └── validators │ │ │ ├── SmartGeofenceBridgeValidator.java │ │ │ ├── SmartGeofenceEventBridgeValidator.java │ │ │ └── SmartGeofenceRefreshErrorBridgeValidator.java │ ├── jest │ │ └── mockNativeModule.js │ ├── lib │ │ ├── index.d.ts │ │ └── index.js │ └── package.json ├── test-common │ └── android │ │ ├── build.gradle │ │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── sentiance │ │ └── react │ │ └── bridge │ │ └── test │ │ ├── ReactNativeModuleTest.java │ │ ├── ReactNativeTest.java │ │ └── validators │ │ ├── BridgeValidator.java │ │ ├── GeoLocationBridgeValidator.java │ │ ├── LocationBridgeValidator.java │ │ ├── VenueBridgeValidator.java │ │ ├── WaypointBridgeValidator.java │ │ └── WaypointsBridgeValidator.java └── user-context │ ├── .npmignore │ ├── README.md │ ├── __tests__ │ └── user-context.test.js │ ├── android │ ├── .gitignore │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── sentiance │ │ └── react │ │ └── bridge │ │ └── usercontext │ │ ├── SentianceUserContextConverter.java │ │ ├── SentianceUserContextEmitter.java │ │ ├── SentianceUserContextModule.java │ │ ├── SentianceUserContextPackage.java │ │ └── utils │ │ └── ErrorCodes.java │ ├── jest │ └── mockNativeModule.js │ ├── lib │ ├── index.d.ts │ ├── index.js │ └── user-context.js │ └── package.json ├── publishing.md ├── scripts ├── determine-version-tag.js ├── new-sdk-module │ ├── android-setup.sh │ ├── generate_build_gradle.sh │ ├── generate_java_code.sh │ ├── react-native-setup.sh │ ├── run.sh │ └── shared.sh ├── sdk_modules_map.json └── sync-package-versions.js ├── tsconfig.base.json ├── tsconfig.json └── types.md /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | defaults: &defaults 4 | working_directory: ~/repo 5 | docker: 6 | - image: circleci/node:16.10.0 7 | 8 | commands: 9 | generate_github_pr_for_docs: 10 | parameters: 11 | checkout: 12 | default: true 13 | description: Boolean value used for whether or not to checkout bitbucket repo as a first step. Default is true. 14 | type: boolean 15 | steps: 16 | - add_ssh_keys 17 | - when: 18 | condition: <> 19 | steps: 20 | - checkout 21 | - run: 22 | name: Docs PR Generating 23 | command: | 24 | . ./.sentiance/doc-pr-gen.sh 25 | 26 | jobs: 27 | test: 28 | <<: *defaults 29 | steps: 30 | - checkout 31 | - run: npm install 32 | - run: npm run lint 33 | - run: npm run tsc 34 | - run: npm run test 35 | android_test: 36 | docker: 37 | - image: cimg/android:2024.01.1-node 38 | environment: 39 | BUNDLE_PATH: vendor/bundle 40 | _JAVA_OPTIONS: -Xmx3g 41 | steps: 42 | - checkout 43 | - run: java -version 44 | - run: npm install 45 | - run: npm run androidTest 46 | deploy: 47 | <<: *defaults 48 | steps: 49 | - checkout 50 | - run: npm install 51 | - attach_workspace: 52 | at: ~/repo 53 | - run: 54 | name: Authenticate with registry 55 | command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc 56 | - run: 57 | name: Sync package versions for all modules 58 | command: npm run sync:package_versions 59 | - run: 60 | name: Compile Typescript code 61 | command: npm run tsc 62 | - run: 63 | name: Tag and publish all subpackages 64 | command: | 65 | NPMTAG=$(npm run fetch:npm_tag --silent) 66 | npm publish --workspaces --tag $NPMTAG 67 | publish_docs: 68 | environment: 69 | DOCKER_BUILDKIT: "1" 70 | machine: 71 | image: ubuntu-2004:202201-02 72 | steps: 73 | - generate_github_pr_for_docs 74 | 75 | workflows: 76 | version: 2 77 | workflow: 78 | jobs: 79 | - test: 80 | filters: 81 | tags: 82 | only: /^v.*/ 83 | - android_test: 84 | filters: 85 | tags: 86 | only: /^v.*/ 87 | - deploy: 88 | requires: [test, android_test] 89 | filters: 90 | tags: 91 | only: /^v.*/ 92 | branches: 93 | ignore: /.*/ 94 | - publish_docs: 95 | context: default 96 | requires: [test, android_test] 97 | filters: 98 | branches: 99 | only: 100 | - main 101 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parserOptions": { 4 | "ecmaVersion": 2020, 5 | "sourceType": "module" 6 | }, 7 | "plugins": [ 8 | "@typescript-eslint", 9 | "jest" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/no-var-requires": "off", 13 | "@typescript-eslint/no-empty-interface": "off" 14 | }, 15 | "extends": [ 16 | "eslint:recommended", 17 | "plugin:@typescript-eslint/recommended", 18 | "plugin:@typescript-eslint/eslint-recommended" 19 | ], 20 | "env": { 21 | "jest/globals": true, 22 | "node": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # OSX 3 | # 4 | .DS_Store 5 | 6 | # node.js 7 | # 8 | node_modules/ 9 | npm-debug.log 10 | yarn-error.log 11 | 12 | 13 | # Xcode 14 | # 15 | build/ 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | *.xccheckout 26 | *.moved-aside 27 | DerivedData 28 | *.hmap 29 | *.ipa 30 | *.xcuserstate 31 | project.xcworkspace 32 | 33 | 34 | # Android/IntelliJ 35 | # 36 | build/ 37 | .idea 38 | .gradle 39 | local.properties 40 | *.iml 41 | 42 | # BUCK 43 | buck-out/ 44 | \.buckd/ 45 | *.keystore 46 | 47 | .npmrc 48 | 49 | # TS 50 | *.tsbuildinfo 51 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "react-native-sentiance-private-resources"] 2 | path = other-resources 3 | url = git@bitbucket.org:getjini/react-native-sentiance-private-resources.git 4 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /.sentiance/doc-spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "mappings": [ 3 | { 4 | "fileName": "react-native.md", 5 | "repoDirPath": "docs/", 6 | "outDirPath": "sdk/api-reference" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true 4 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## Unreleased 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sentiance SDKs for React Native 2 | 3 | ## Demo Application 4 | 5 | https://github.com/sentiance/sample-apps-react-native 6 | 7 | ## Getting started 8 | 9 | To get started with the Sentiance React Native SDKs, please check the [getting started guide.](https://docs.sentiance.com/sdk/getting-started/react-native-quick-start) 10 | 11 | ## Usage 12 | 13 | To use the SDKs, please visit the [full index page](https://docs.sentiance.com/sdk/api-reference/react-native) of all of Sentiance's React Native SDKs' API references. 14 | -------------------------------------------------------------------------------- /assets/ios-background-modes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/assets/ios-background-modes.png -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["module:metro-react-native-babel-preset"] 3 | } 4 | -------------------------------------------------------------------------------- /dev.md: -------------------------------------------------------------------------------- 1 | # Creating new SDK modules 2 | 3 | Start by running `npm i` to download and install the project's dependencies. Then, run the following command: 4 | 5 | `npm run new-sdk-module` 6 | 7 | ## iOS specific configuration 8 | 9 | ### Project 10 | Use existing module as reference and update the following. 11 | 12 | - Module name via Xcode. 13 | - Podspec file. 14 | - Classes name for: native module, converter, error codes. 15 | 16 | ### Native Module 17 | In your native module class `NewModule.m` add these methods to prevent react-native runtime warnings. 18 | ```obj-c 19 | @property (assign) BOOL hasListeners; 20 | 21 | // Will be called when this module's first listener is added. 22 | - (void)startObserving { 23 | self.hasListeners = YES; 24 | // Set up any upstream listeners or background tasks as necessary 25 | } 26 | 27 | // Will be called when this module's last listener is removed, or on dealloc. 28 | - (void)stopObserving { 29 | self.hasListeners = NO; 30 | // Remove upstream listeners, stop unnecessary background tasks 31 | } 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /docs/adding_new_transport_mode.md: -------------------------------------------------------------------------------- 1 | To add project-wide support for a new transport mode, follow these steps: 2 | 3 | - Locate the `packages/driving-insights/lib/avg-overall-safety-score-api.js` and add the new transportMode to the `VALID_TRANSPORT_MODES` constant defined there. 4 | - You need to update all the Typescript code that references transport modes. One way to figure out which packages you need to look into is 5 | by doing a project-wide search for `TransportMode`: 6 | 7 | ![Transport mode search](./images/transport_mode_search_query.png) 8 | 9 | And from there you can tell that this is being used in the `core`, `event-timeline`, `user-context` and `driving-insights` packages. 10 | 11 | - Once you locate the packages in question, look for the `lib/index.d.ts` definitions files inside each package, then add the new transport mode to the 12 | `TransportMode` enum/union type. 13 | - Try to rebuild your native code, and you should get compilation errors in all affected packages. Then your IDE will help you locate the Java/Objective-C files 14 | where you need to make further changes. 15 | -------------------------------------------------------------------------------- /docs/images/transport_mode_search_query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/docs/images/transport_mode_search_query.png -------------------------------------------------------------------------------- /images/create_new_module.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/images/create_new_module.png -------------------------------------------------------------------------------- /images/initial_project_structure(2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/images/initial_project_structure(2).png -------------------------------------------------------------------------------- /images/initial_project_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/images/initial_project_structure.png -------------------------------------------------------------------------------- /images/updated_project_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/images/updated_project_structure.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | moduleDirectories: [ 4 | "./node_modules", // Prioritize top level node_modules over the package's. Makes sure the tests pick up the same top level defined RN. 5 | "/node_modules" 6 | ], 7 | preset: "react-native", 8 | transform: { 9 | "^.+\\.(js|jsx)$": 'babel-jest' 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /jest/mockNativeModules.js: -------------------------------------------------------------------------------- 1 | export function mockNativeModule(platform, nativeModule, module) { 2 | const mockAddListener = jest.fn(); 3 | const mockRemoveListeners = jest.fn(); 4 | const mockAddNativeListener = jest.fn(); 5 | const mockRemoveNativeListener = jest.fn(); 6 | const activeModuleName = platform === 'android' ? nativeModule.androidName : nativeModule.iosName; 7 | 8 | jest.doMock('react-native', () => { 9 | const RN = jest.requireActual('react-native'); 10 | RN.Platform.OS = platform; 11 | 12 | module = { 13 | ...module, 14 | addNativeListener: mockAddNativeListener, 15 | removeNativeListener: mockRemoveNativeListener, 16 | addListener: mockAddListener, 17 | removeListeners: mockRemoveListeners 18 | } 19 | 20 | RN.NativeModules[activeModuleName] = module; 21 | return RN; 22 | }); 23 | 24 | return { 25 | name: activeModuleName, 26 | addListener: mockAddListener, 27 | removeListeners: mockRemoveListeners, 28 | addNativeListener: mockAddNativeListener, 29 | removeNativeListener: mockRemoveNativeListener 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /jest/test_util.js: -------------------------------------------------------------------------------- 1 | export function runOnEachPlatform(callback) { 2 | it.each(['android', 'ios']) 3 | ("when running on %s", callback); 4 | } 5 | 6 | export function allEqual(arr) { 7 | return arr.every(val => val === arr[0]); 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sentiance-react-native", 3 | "version": "6.12.0", 4 | "description": "React Native libraries for the Sentiance SDK", 5 | "scripts": { 6 | "tsc": "tsc --build --verbose", 7 | "docs": "npx jsdoc2md index.d.ts > types.md", 8 | "test": "jest --verbose", 9 | "androidTest": "cd ./packages && ./gradlew test -PtestEnv=true --stacktrace", 10 | "fetch:npm_tag": "node ./scripts/determine-version-tag.js", 11 | "sync:package_versions": "node ./scripts/sync-package-versions.js", 12 | "lint": "npm run lint --workspaces", 13 | "prepublishLocal": "npm run tsc && npm run sync:package_versions", 14 | "publishLocal": "npm publish --workspaces --registry http://localhost:4873/ --tag $(npm run fetch:npm_tag --silent)", 15 | "prepublishLocal:overwrite": "npm run tsc", 16 | "publishLocal:overwrite": "npm unpublish --force --workspaces --registry http://localhost:4873/ && npm publish --workspaces --registry http://localhost:4873/ --tag $(npm run fetch:npm_tag --silent)", 17 | "new-sdk-module": "./scripts/new-sdk-module/run.sh" 18 | }, 19 | "workspaces": [ 20 | "packages/core", 21 | "packages/crash-detection", 22 | "packages/user-context", 23 | "packages/legacy", 24 | "packages/driving-insights", 25 | "packages/event-timeline", 26 | "packages/smart-geofences" 27 | ], 28 | "author": "", 29 | "license": "", 30 | "homepage": "https://github.com/sentiance/react-native-sentiance#readme", 31 | "bugs": "https://github.com/sentiance/react-native-sentiance/issues", 32 | "repository": "github:sentiance/react-native-sentiance", 33 | "devDependencies": { 34 | "@babel/core": "^7.25.8", 35 | "@babel/preset-env": "^7.22.4", 36 | "@babel/runtime": "^7.22.3", 37 | "@types/react-native": "^0.72.8", 38 | "@typescript-eslint/eslint-plugin": "^5.12.0", 39 | "@typescript-eslint/parser": "^5.12.0", 40 | "babel-jest": "^29.5.0", 41 | "eslint": "^8.56.0", 42 | "eslint-plugin-jest": "^27.6.3", 43 | "jest": "^29.5.0", 44 | "jsdoc": "^3.6.10", 45 | "jsdoc-to-markdown": "^7.1.1", 46 | "metro-react-native-babel-preset": "^0.76.6", 47 | "prettier": "^2.6.2", 48 | "prettier-plugin-jsdoc": "^0.3.38", 49 | "react-native": "0.61.5", 50 | "typescript": "^4.9.5" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | gradlePluginPortal() 5 | } 6 | 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:8.3.2" 9 | } 10 | } 11 | 12 | plugins { 13 | id 'com.android.library' version '8.3.2' apply false 14 | } 15 | 16 | task clean(type: Delete) { 17 | delete rootProject.buildDir 18 | } 19 | 20 | subprojects { 21 | // Check if the build was started by Android Studio 22 | def ideBuild = project.properties['android.injected.invoked.from.ide'] 23 | // Check if the build was started with -PtestEnv=true 24 | def isTestEnv = project.hasProperty('testEnv') && project.testEnv.toBoolean() 25 | 26 | if (ideBuild || isTestEnv) { 27 | apply from: rootProject.file('./gradle-scripts/common-test-config.gradle') 28 | } 29 | 30 | project.plugins.withId('com.android.library') { 31 | android { 32 | testOptions { 33 | unitTests.all { 34 | testLogging { 35 | events "passed", "skipped", "failed" 36 | showStandardStreams = true 37 | exceptionFormat "full" 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/core/.npmignore: -------------------------------------------------------------------------------- 1 | __tests__ 2 | /jest 3 | android/src/test 4 | tsconfig.json 5 | /src 6 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # Sentiance Core module for React Native 2 | 3 | ## Demo Application 4 | 5 | https://github.com/sentiance/sample-apps-react-native 6 | 7 | ## Usage 8 | 9 | To use the core SDK module, please visit the corresponding [API reference page.](https://docs.sentiance.com/sdk/api-reference/react-native/core) 10 | -------------------------------------------------------------------------------- /packages/core/RNSentianceCore.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | package = JSON.parse(File.read(File.join(__dir__, './package.json'))) 3 | sentiance_sdk_package_version = package['sdkVersions']['ios']['sentiance'] 4 | sentiance_sdk_env_var_version = ENV["SENTIANCE_RN_IOS_SDK_VERSION"] 5 | 6 | Pod::Spec.new do |s| 7 | s.name = "RNSentianceCore" 8 | s.version = "6.10.1" 9 | s.summary = "RNSentianceCore" 10 | s.description = <<-DESC 11 | RNSentianceCore 12 | DESC 13 | s.homepage = "https://developers.sentiance.com/docs" 14 | s.license = "MIT" 15 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 16 | s.author = { "author" => "sdk@sentiance.com" } 17 | s.platform = :ios, "9.0" 18 | s.source = { :path => '.' } 19 | s.source_files = "ios/**/*.{h,m,swift}" 20 | s.requires_arc = true 21 | s.xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => '${PODS_ROOT}/SENTSDK' } 22 | 23 | s.dependency "React" 24 | 25 | if sentiance_sdk_env_var_version.nil? 26 | s.dependency "SENTSDK", sentiance_sdk_package_version 27 | else 28 | s.dependency "SENTSDK", sentiance_sdk_env_var_version 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /packages/core/__tests__/core.test.js: -------------------------------------------------------------------------------- 1 | import { runOnEachPlatform } from "../../../jest/test_util"; 2 | import { mockNativeCoreModule } from "../jest/mockNativeModule"; 3 | 4 | describe("Core API tests", () => { 5 | beforeEach(() => jest.resetModules()); 6 | 7 | describe("Set is allowed to use mobile data", () => { 8 | 9 | runOnEachPlatform(platform => { 10 | const mockSetIsAllowedToUseMobileData = jest.fn(); 11 | mockNativeCoreModule(platform, { 12 | setIsAllowedToUseMobileData: mockSetIsAllowedToUseMobileData 13 | }); 14 | 15 | const coreApi = require("../lib"); 16 | 17 | coreApi.setIsAllowedToUseMobileData(false); 18 | coreApi.setIsAllowedToUseMobileData(true); 19 | coreApi.setIsAllowedToUseMobileData(false); 20 | 21 | expect(mockSetIsAllowedToUseMobileData).toHaveBeenNthCalledWith(1, false); 22 | expect(mockSetIsAllowedToUseMobileData).toHaveBeenNthCalledWith(2, true); 23 | expect(mockSetIsAllowedToUseMobileData).toHaveBeenNthCalledWith(3, false); 24 | }); 25 | }); 26 | 27 | describe("Is allowed to use mobile data", () => { 28 | runOnEachPlatform(platform => { 29 | const mockIsAllowedToUseMobileData = jest.fn(); 30 | mockNativeCoreModule(platform, { 31 | isAllowedToUseMobileData: mockIsAllowedToUseMobileData 32 | }); 33 | 34 | mockIsAllowedToUseMobileData.mockResolvedValueOnce(true); 35 | mockIsAllowedToUseMobileData.mockResolvedValueOnce(false); 36 | 37 | const coreApi = require("../lib"); 38 | expect(coreApi.isAllowedToUseMobileData()).resolves.toBe(true); 39 | expect(coreApi.isAllowedToUseMobileData()).resolves.toBe(false); 40 | }); 41 | }); 42 | }); 43 | 44 | 45 | -------------------------------------------------------------------------------- /packages/core/android/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .idea 3 | .gradle 4 | -------------------------------------------------------------------------------- /packages/core/android/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.library" 3 | } 4 | 5 | android { 6 | namespace "com.sentiance.react.bridge.core" 7 | 8 | compileOptions { 9 | sourceCompatibility JavaVersion.VERSION_1_8 10 | targetCompatibility JavaVersion.VERSION_1_8 11 | } 12 | } 13 | 14 | apply from: "$project.projectDir/package-json-reader.gradle" 15 | apply from: "$project.projectDir/sentiance-version-finder.gradle" 16 | 17 | def packageJson = PackageJson.of(project) 18 | applyAndroidVersionsFrom(packageJson) 19 | def sentianceSdkVersion = getSentianceSdkVersion() 20 | 21 | dependencies { 22 | implementation(platform("com.sentiance:sdk-bom:${sentianceSdkVersion}")) 23 | api("com.sentiance:sdk") { transitive = true } 24 | 25 | if (findProject(':test-common')) { 26 | testImplementation project(':test-common') 27 | } 28 | } 29 | 30 | applyReactNativeDependency() 31 | -------------------------------------------------------------------------------- /packages/core/android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | android.enableJetifier=true 3 | -------------------------------------------------------------------------------- /packages/core/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/packages/core/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /packages/core/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /packages/core/android/package-json-reader.gradle: -------------------------------------------------------------------------------- 1 | import groovy.json.JsonSlurper 2 | 3 | class PackageJsonReader { 4 | Object of(Project project) { 5 | File packageJson = null 6 | File parentDir = project.projectDir 7 | 8 | for (int i = 0; i <= 3; i++) { 9 | parentDir = parentDir.parentFile 10 | 11 | packageJson = new File( 12 | parentDir, 13 | 'package.json' 14 | ) 15 | 16 | if (packageJson.exists()) break 17 | } 18 | 19 | if (packageJson != null) { 20 | println ":${project.name} package.json found at ${packageJson.toString()}" 21 | Object json = null 22 | 23 | try { 24 | json = new JsonSlurper().parseText(packageJson.text) 25 | } catch (Exception ignored) { 26 | } 27 | 28 | if (json == null) { 29 | project.logger.warn ":${project.name} failed to parse package.json found at ${packageJson.toString()}" 30 | return json 31 | } 32 | 33 | return json 34 | } 35 | 36 | println ":${project.name} unable to locate a package.json file relative to this project" 37 | return null 38 | } 39 | } 40 | 41 | ext.PackageJson = new PackageJsonReader() 42 | ext.applyAndroidVersionsFrom = { packageJson -> 43 | def defaultMinSdkVersion = packageJson['sdkVersions']['android']['minSdk'] 44 | def defaultTargetSdkVersion = packageJson['sdkVersions']['android']['targetSdk'] 45 | def defaultCompileSdkVersion = packageJson['sdkVersions']['android']['compileSdk'] 46 | def defaultBuildToolsVersion = packageJson['sdkVersions']['android']['buildTools'] 47 | 48 | android { 49 | compileSdkVersion defaultCompileSdkVersion 50 | buildToolsVersion defaultBuildToolsVersion 51 | 52 | defaultConfig { 53 | minSdkVersion defaultMinSdkVersion 54 | targetSdkVersion defaultTargetSdkVersion 55 | } 56 | } 57 | } 58 | ext.applyReactNativeDependency = { 59 | dependencies { 60 | implementation 'com.facebook.react:react-native:+' 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/core/android/sentiance-version-finder.gradle: -------------------------------------------------------------------------------- 1 | def coreProj 2 | if (findProject(':core')) { 3 | coreProj = project(':core') 4 | } else if (findProject(':sentiance-react-native_core')) { 5 | coreProj = project(':sentiance-react-native_core') 6 | } else if (findProject(':@sentiance-react-native_core')) { 7 | coreProj = project(':@sentiance-react-native_core') 8 | } else { 9 | throw new GradleException('Could not find the @sentiance-react-native/core package, have you installed it?') 10 | } 11 | 12 | apply from: "$coreProj.projectDir/package-json-reader.gradle" 13 | 14 | def packageJson = PackageJson.of(coreProj) 15 | ext.getSentianceSdkVersion = { 16 | def envVarVersion = System.getenv("SENTIANCE_RN_ANDROID_SDK_VERSION") 17 | def packageVersion = packageJson['sdkVersions']['android']['sentiance'] 18 | 19 | return envVarVersion ?: packageVersion 20 | } 21 | -------------------------------------------------------------------------------- /packages/core/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /packages/core/android/src/main/java/com/sentiance/react/bridge/core/SentianceEmitter.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.core; 2 | 3 | import android.content.Context; 4 | 5 | import com.facebook.react.bridge.Arguments; 6 | import com.sentiance.react.bridge.core.common.base.AbstractSentianceEmitter; 7 | import com.sentiance.sdk.SdkStatus; 8 | import com.sentiance.sdk.detectionupdates.UserActivity; 9 | 10 | public class SentianceEmitter extends AbstractSentianceEmitter { 11 | private static final String USER_LINK = "SENTIANCE_USER_LINK_EVENT"; 12 | private static final String STATUS_UPDATE = "SENTIANCE_STATUS_UPDATE_EVENT"; 13 | private static final String USER_ACTIVITY_UPDATE = "SENTIANCE_USER_ACTIVITY_UPDATE_EVENT"; 14 | private static final String ON_TRIP_TIMED_OUT = "SENTIANCE_ON_TRIP_TIMED_OUT_EVENT"; 15 | private final SentianceConverter converter; 16 | 17 | public SentianceEmitter(Context context) { 18 | super(context); 19 | converter = new SentianceConverter(); 20 | } 21 | 22 | void sendUserLinkEvent(String installId) { 23 | sendEvent(USER_LINK, converter.convertInstallId(installId)); 24 | } 25 | 26 | public void sendStatusUpdateEvent(SdkStatus status) { 27 | sendEvent(STATUS_UPDATE, converter.convertSdkStatus(status)); 28 | } 29 | 30 | void sendUserActivityUpdate(UserActivity userActivity) { 31 | sendEvent(USER_ACTIVITY_UPDATE, converter.convertUserActivity(userActivity)); 32 | } 33 | 34 | void sendOnTripTimedOutEvent() { 35 | sendEvent(ON_TRIP_TIMED_OUT, Arguments.createMap()); 36 | } 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /packages/core/android/src/main/java/com/sentiance/react/bridge/core/SentiancePackage.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.core; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | import com.sentiance.react.bridge.core.common.SentianceSubscriptionsManager; 10 | import com.sentiance.sdk.Sentiance; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | public class SentiancePackage implements ReactPackage { 17 | 18 | @NonNull 19 | @Override 20 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 21 | List modules = new ArrayList<>(); 22 | 23 | SentianceModule sentianceModule = new SentianceModule( 24 | reactContext, 25 | Sentiance.getInstance(reactContext), 26 | new SentianceSubscriptionsManager(), 27 | SentianceHelper.getInstance(reactContext), 28 | new SentianceEmitter(reactContext), 29 | new SentianceConverter() 30 | ); 31 | modules.add(sentianceModule); 32 | return modules; 33 | } 34 | 35 | @NonNull 36 | @Override 37 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 38 | return Collections.emptyList(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/core/android/src/main/java/com/sentiance/react/bridge/core/UserLinker.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.core; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.util.concurrent.CountDownLatch; 6 | 7 | 8 | public class UserLinker implements com.sentiance.sdk.UserLinker { 9 | 10 | private final SentianceEmitter mSentianceEmitter; 11 | private boolean mUserLinkResult; 12 | private CountDownLatch mCountDownLatch; 13 | 14 | public UserLinker (SentianceEmitter sentianceEmitter) { 15 | mSentianceEmitter = sentianceEmitter; 16 | } 17 | 18 | @Override 19 | public boolean link(@NonNull String installId) { 20 | mCountDownLatch = new CountDownLatch(1); 21 | mSentianceEmitter.sendUserLinkEvent(installId); 22 | try { 23 | mCountDownLatch.await(); 24 | return mUserLinkResult; 25 | } catch (InterruptedException e) { 26 | return false; 27 | } 28 | } 29 | 30 | public void setUserLinkResult(boolean userLinkResult) { 31 | mUserLinkResult = userLinkResult; 32 | mCountDownLatch.countDown(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/core/android/src/main/java/com/sentiance/react/bridge/core/common/base/AbstractSentianceEmitter.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.core.common.base; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.os.Looper; 6 | 7 | import com.facebook.react.ReactApplication; 8 | import com.facebook.react.ReactNativeHost; 9 | import com.facebook.react.bridge.ReactContext; 10 | import com.facebook.react.bridge.WritableMap; 11 | import com.facebook.react.modules.core.DeviceEventManagerModule; 12 | 13 | public abstract class AbstractSentianceEmitter { 14 | 15 | protected final Handler mHandler = new Handler(Looper.getMainLooper()); 16 | protected ReactContext reactContext; 17 | protected ReactNativeHost reactNativeHost; 18 | 19 | protected AbstractSentianceEmitter(Context context) { 20 | ReactApplication reactApplication = ((ReactApplication) context.getApplicationContext()); 21 | reactNativeHost = reactApplication.getReactNativeHost(); 22 | reactContext = createReactContext(); 23 | } 24 | 25 | protected ReactContext createReactContext() { 26 | if (!reactNativeHost.getReactInstanceManager().hasStartedCreatingInitialContext()) 27 | reactNativeHost.getReactInstanceManager().createReactContextInBackground(); 28 | return reactNativeHost.getReactInstanceManager().getCurrentReactContext(); 29 | } 30 | 31 | protected void sendEvent(final String key, final WritableMap map) { 32 | if (reactContext != null && reactContext.hasActiveCatalystInstance()) { 33 | this.reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(key, map); 34 | } else { 35 | // add delay 36 | final Counter retry = new Counter(20); 37 | mHandler.postDelayed(new Runnable() { 38 | @Override 39 | public void run() { 40 | if (AbstractSentianceEmitter.this.reactContext == null) 41 | AbstractSentianceEmitter.this.reactContext = createReactContext(); 42 | if (AbstractSentianceEmitter.this.reactContext != null && AbstractSentianceEmitter.this.reactContext.hasActiveCatalystInstance()) { 43 | AbstractSentianceEmitter.this.reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(key, map); 44 | } else if (retry.count-- > 0) { 45 | mHandler.postDelayed(this, 500); 46 | } 47 | } 48 | }, 500); 49 | } 50 | } 51 | 52 | private static class Counter { 53 | int count; 54 | 55 | Counter(int count) { 56 | this.count = count; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages/core/android/src/main/java/com/sentiance/react/bridge/core/common/util/ErrorCodes.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.core.common.util; 2 | 3 | public class ErrorCodes { 4 | public static final String E_SDK_MISSING_PARAMS = "E_SDK_MISSING_PARAMS"; 5 | public static final String E_SDK_GET_TOKEN_ERROR = "E_SDK_GET_TOKEN_ERROR"; 6 | public static final String E_SDK_START_TRIP_ERROR = "E_SDK_START_TRIP_ERROR"; 7 | public static final String E_SDK_STOP_TRIP_ERROR = "E_SDK_STOP_TRIP_ERROR"; 8 | public static final String E_SDK_NOT_INITIALIZED = "E_SDK_NOT_INITIALIZED"; 9 | public static final String E_SDK_SUBMIT_DETECTIONS_ERROR = "E_SDK_SUBMIT_DETECTIONS_ERROR"; 10 | public static final String E_SDK_ENABLE_DETECTIONS_ERROR = "E_SDK_ENABLE_DETECTIONS_ERROR"; 11 | public static final String E_SDK_DISABLE_DETECTIONS_ERROR = "E_SDK_DISABLE_DETECTIONS_ERROR"; 12 | public static final String E_SDK_USER_LINK_ERROR = "E_SDK_USER_LINK_ERROR"; 13 | public static final String E_SDK_USER_LINK_AUTH_CODE_ERROR = "E_SDK_USER_LINK_AUTH_CODE_ERROR"; 14 | public static final String E_SDK_CREATE_USER_ERROR = "E_SDK_CREATE_USER_ERROR"; 15 | public static final String E_SDK_RESET_ERROR = "E_SDK_RESET_ERROR"; 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/android/src/main/java/com/sentiance/react/bridge/core/common/util/SingleParamRunnable.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.core.common.util; 2 | 3 | @FunctionalInterface 4 | public interface SingleParamRunnable { 5 | void run(T t); 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/android/src/main/java/com/sentiance/react/bridge/core/utils/UserCreationCompletionHandler.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.core.utils; 2 | 3 | import static com.sentiance.react.bridge.core.common.util.ErrorCodes.E_SDK_CREATE_USER_ERROR; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.facebook.react.bridge.Promise; 8 | import com.sentiance.react.bridge.core.SentianceConverter; 9 | import com.sentiance.sdk.pendingoperation.OnCompleteListener; 10 | import com.sentiance.sdk.pendingoperation.PendingOperation; 11 | import com.sentiance.sdk.usercreation.UserCreationError; 12 | import com.sentiance.sdk.usercreation.UserCreationResult; 13 | 14 | public class UserCreationCompletionHandler implements OnCompleteListener { 15 | 16 | private final Promise promise; 17 | private final SentianceConverter converter; 18 | 19 | public UserCreationCompletionHandler(Promise promise) { 20 | this.promise = promise; 21 | converter = new SentianceConverter(); 22 | } 23 | 24 | @Override 25 | public void onComplete(@NonNull PendingOperation pendingOperation) { 26 | if (pendingOperation.isSuccessful()) { 27 | promise.resolve(converter.convertUserCreationResult(pendingOperation.getResult())); 28 | } else { 29 | promise.reject(E_SDK_CREATE_USER_ERROR, 30 | converter.stringifyUserCreationError(pendingOperation.getError())); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/core/android/src/test/java/com/sentiance/react/bridge/core/SentianceConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.core; 2 | 3 | import android.location.Location; 4 | 5 | import androidx.annotation.Nullable; 6 | 7 | import com.facebook.react.bridge.JavaOnlyMap; 8 | import com.sentiance.react.bridge.test.ReactNativeTest; 9 | import com.sentiance.react.bridge.test.validators.LocationBridgeValidator; 10 | 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.robolectric.RobolectricTestRunner; 14 | 15 | import java.util.Arrays; 16 | 17 | // TODO: add unit tests 18 | @RunWith(RobolectricTestRunner.class) 19 | public class SentianceConverterTest extends ReactNativeTest { 20 | private SentianceConverter converter; 21 | 22 | @Override 23 | public void setUp() throws Exception { 24 | super.setUp(); 25 | converter = new SentianceConverter(); 26 | } 27 | 28 | @Test 29 | public void testConvertLocation() { 30 | Location location = newLocation(System.currentTimeMillis(), 0.65, 7.89, 10f, 8.5); 31 | Location locationWithoutAccuracy = newLocation(System.currentTimeMillis(), 0.65, 7.89, null, 8.5); 32 | Location locationWithoutAltitude = newLocation(System.currentTimeMillis(), 0.65, 7.89, 10f, null); 33 | 34 | for (Location loc : Arrays.asList(location, locationWithoutAccuracy, locationWithoutAltitude)) { 35 | JavaOnlyMap transformedLocation = (JavaOnlyMap) converter.convertLocation(loc); 36 | new LocationBridgeValidator().validate(loc, transformedLocation); 37 | } 38 | } 39 | 40 | private Location newLocation(long time, double latitude, double longitude, @Nullable Float accuracy, @Nullable Double altitude) { 41 | Location location = new Location("provider"); 42 | location.setTime(time); 43 | location.setLatitude(latitude); 44 | location.setLongitude(longitude); 45 | if (accuracy != null) { 46 | location.setAccuracy(accuracy); 47 | } 48 | if (altitude != null) { 49 | location.setAltitude(altitude); 50 | } 51 | return location; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/core/android/src/test/java/com/sentiance/react/bridge/core/SentianceModuleTest.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.core; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.Mockito.times; 6 | import static org.mockito.Mockito.verify; 7 | import static org.mockito.Mockito.when; 8 | 9 | import com.sentiance.react.bridge.test.ReactNativeModuleTest; 10 | 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.mockito.Mock; 14 | import org.robolectric.RobolectricTestRunner; 15 | 16 | import java.util.List; 17 | 18 | @RunWith(RobolectricTestRunner.class) 19 | public class SentianceModuleTest extends ReactNativeModuleTest { 20 | @Mock 21 | private SentianceHelper sentianceHelper; 22 | @Mock 23 | private SentianceEmitter sentianceEmitter; 24 | @Mock 25 | private SentianceConverter sentianceConverter; 26 | 27 | @Override 28 | protected SentianceModule initModule() { 29 | return new SentianceModule(mReactApplicationContext, mSentiance, 30 | mSentianceSubscriptionsManager, sentianceHelper, sentianceEmitter, sentianceConverter); 31 | } 32 | 33 | @Test 34 | public void testSetIsAllowedToUseMobileData() { 35 | mModule.setIsAllowedToUseMobileData(false, mPromise); 36 | verify(mSentiance).setIsAllowedToUseMobileData(false); 37 | verify(mPromise).resolve(null); 38 | } 39 | 40 | @Test 41 | public void testIsAllowedToUseMobileData() { 42 | when(mSentiance.isAllowedToUseMobileData()).thenReturn(true); 43 | mModule.isAllowedToUseMobileData(mPromise); 44 | 45 | when(mSentiance.isAllowedToUseMobileData()).thenReturn(false); 46 | mModule.isAllowedToUseMobileData(mPromise); 47 | 48 | verify(mPromise, times(2)).resolve(boolCaptor.capture()); 49 | 50 | List allValues = boolCaptor.getAllValues(); 51 | assertTrue(allValues.get(0)); 52 | assertFalse(allValues.get(1)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceCore.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #import 4 | #import 5 | #import 6 | 7 | static NSString * _Nonnull const UserLinkEvent = @"SENTIANCE_USER_LINK_EVENT"; 8 | static NSString * _Nonnull const SdkStatusUpdateEvent = @"SENTIANCE_STATUS_UPDATE_EVENT"; 9 | static NSString * _Nonnull const UserActivityUpdateEvent = @"SENTIANCE_USER_ACTIVITY_UPDATE_EVENT"; 10 | static NSString * _Nonnull const ON_DETECTIONS_ENABLED = @"SENTIANCE_ON_DETECTIONS_ENABLED_EVENT"; 11 | static NSString * _Nonnull const TripTimeoutEvent = @"SENTIANCE_ON_TRIP_TIMED_OUT_EVENT"; 12 | static NSString * _Nonnull const VehicleCrashEvent = @"SENTIANCE_VEHICLE_CRASH_EVENT"; 13 | static NSString * _Nonnull const VehicleCrashDiagnosticEvent = @"SENTIANCE_VEHICLE_CRASH_DIAGNOSTIC_EVENT"; 14 | static NSString * _Nonnull const UserContextUpdateEvent = @"SENTIANCE_USER_CONTEXT_UPDATE_EVENT"; 15 | static NSString * _Nonnull const DrivingInsightsReadyEvent = @"SENTIANCE_DRIVING_INSIGHTS_READY_EVENT"; 16 | static NSString * _Nonnull const TimelineUpdateEvent = @"SENTIANCE_TIMELINE_UPDATE_EVENT"; 17 | static NSString * _Nonnull const SmartGeofenceEvent = @"SENTIANCE_SMART_GEOFENCE_EVENT"; 18 | 19 | @interface RNSentianceCore : RCTEventEmitter 20 | typedef void (^SdkStatusHandler)(SENTSDKStatus * _Nonnull status); 21 | - (SENTUserLinker _Nonnull ) getUserLinker; 22 | - (SdkStatusHandler _Nonnull) getSdkStatusUpdateHandler; 23 | - (void) initSDK: (NSString * _Nonnull)appId secret:(NSString * _Nonnull)secret baseURL:(NSString * _Nullable)baseURL shouldStart:(BOOL)shouldStart resolver:(RCTPromiseResolveBlock _Nullable)resolve rejecter:(RCTPromiseRejectBlock _Nullable)reject; 24 | - (BOOL) initSDKIfUserLinkingCompleted: (NSString * _Nonnull)appId secret:(NSString * _Nonnull)secret baseURL:(NSString * _Nullable)baseURL shouldStart:(BOOL)shouldStart resolver:(RCTPromiseResolveBlock _Nonnull)resolve rejecter:(RCTPromiseRejectBlock _Nonnull)reject; 25 | - (BOOL) isThirdPartyLinked; 26 | - (NSString * _Nullable) getValueForKey: (NSString * _Nonnull)key value:(NSString * _Nullable)defaultValue; 27 | - (void) setValueForKey: (NSString * _Nonnull)key value:(NSString * _Nullable)value; 28 | - (void) startSDKWithStopEpochTimeMs: (nullable NSNumber*) stopEpochTimeMs resolver:(RCTPromiseResolveBlock _Nonnull )resolve rejecter:(RCTPromiseRejectBlock _Nonnull )reject; 29 | - (void) startSDK: (RCTPromiseResolveBlock _Nullable )resolve rejecter:(RCTPromiseRejectBlock _Nullable )reject; 30 | - (BOOL) isNativeInitializationEnabled; 31 | - (void) disableSDKNativeInitialization: (RCTPromiseResolveBlock _Nullable)resolve rejecter:(RCTPromiseRejectBlock _Nullable)reject; 32 | - (void) enableSDKNativeInitialization: (RCTPromiseResolveBlock _Nullable)resolve rejecter:(RCTPromiseRejectBlock _Nullable)reject; 33 | - (void) setTransmittableDataTypes: (NSArray *)types; 34 | - (NSArray*) getTransmittableDataTypes:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject; 35 | @end 36 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceErrorCodes.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNSentianceErrorCodes.h 3 | // RNSentianceCore 4 | // 5 | // Created by Hassan Shakeel on 05/05/2022. 6 | // 7 | 8 | extern NSString * const ESDKMissingParams; 9 | extern NSString * const ESDKGetTokenError; 10 | extern NSString * const ESDKStartTripError; 11 | extern NSString * const ESDKStopTripError; 12 | extern NSString * const ESDKNotInitialized; 13 | extern NSString * const ESDKSubmitDetectionsError; 14 | extern NSString * const ESDKEnableDetectionsError; 15 | extern NSString * const ESDKDisableDetectionsError; 16 | extern NSString * const ESDKUserLinkError; 17 | extern NSString * const ESDKUserLinkAuthCodeError; 18 | extern NSString * const ESDKCreateUserError; 19 | extern NSString * const ESDKResetError; 20 | extern NSString * const ESDKRequestUserContextError; 21 | extern NSString * const ESDKSmartGeofencesRefreshError; 22 | extern NSString * const ESDKTransportTaggingError; 23 | extern NSString * const ESDKFeedbackNotAvailableError; 24 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceErrorCodes.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNSentianceErrorCodes.m 3 | // RNSentianceCore 4 | // 5 | // Created by Hassan Shakeel on 05/05/2022. 6 | // 7 | 8 | #import "RNSentianceErrorCodes.h" 9 | 10 | NSString * const ESDKMissingParams = @"E_SDK_MISSING_PARAMS"; 11 | NSString * const ESDKGetTokenError = @"E_SDK_GET_TOKEN_ERROR"; 12 | NSString * const ESDKStartTripError = @"E_SDK_START_TRIP_ERROR"; 13 | NSString * const ESDKStopTripError = @"E_SDK_STOP_TRIP_ERROR"; 14 | NSString * const ESDKNotInitialized = @"E_SDK_NOT_INITIALIZED"; 15 | NSString * const ESDKSubmitDetectionsError = @"E_SDK_SUBMIT_DETECTIONS_ERROR"; 16 | NSString * const ESDKEnableDetectionsError = @"E_SDK_ENABLE_DETECTIONS_ERROR"; 17 | NSString * const ESDKDisableDetectionsError = @"E_SDK_DISABLE_DETECTIONS_ERROR"; 18 | NSString * const ESDKUserLinkError = @"E_SDK_USER_LINK_ERROR"; 19 | NSString * const ESDKUserLinkAuthCodeError = @"E_SDK_USER_LINK_AUTH_CODE_ERROR"; 20 | NSString * const ESDKCreateUserError = @"E_SDK_CREATE_USER_ERROR"; 21 | NSString * const ESDKResetError = @"E_SDK_RESET_ERROR"; 22 | NSString * const ESDKRequestUserContextError = @"E_SDK_REQUEST_USER_CONTEXT_ERROR"; 23 | NSString * const ESDKSmartGeofencesRefreshError = @"E_SDK_SMART_GEOFENCES_REFRESH_ERROR"; 24 | NSString * const ESDKTransportTaggingError = @"E_TRANSPORT_TAG_ERROR"; 25 | NSString * const ESDKFeedbackNotAvailableError = @"E_SDK_FEEDBACK_NOT_AVAILABLE"; 26 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceFoundation.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNSentianceFoundation.h 3 | // RNSentianceCore 4 | // 5 | // Created by David Chelidze on 05/04/2023. 6 | // Copyright © 2023 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^SentianceBlock)(void); 12 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNSentianceHelper.h 3 | // RNSentianceCore 4 | // 5 | // Created by Hassan Shakeel on 10/05/2022. 6 | // 7 | 8 | #import 9 | #import "RNSentianceCore.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import "RNSentianceCore+Converter.h" 15 | 16 | @interface RNSentianceHelper: NSObject 17 | 18 | - (SENTInitializationResult *)initializeSDKWithLaunchOptions:(nullable NSDictionary *)launchOptions 19 | NS_SWIFT_NAME(initializeSDK(launchOptions:)); 20 | 21 | - (SENTInitializationResult *)initializeSDKWithPlatformUrl:(NSString *)platformUrl 22 | launchOptions:(nullable NSDictionary *)launchOptions 23 | NS_SWIFT_NAME(initializeSDK(platformUrl:launchOptions:)); 24 | 25 | - (SENTInitializationResult *)initializeSDKWithPlatformUrl:(NSString *)platformUrl 26 | isAppSessionDataCollectionAllowed:(BOOL *)isAppSessionDataCollectionAllowed 27 | launchOptions:(nullable NSDictionary *)launchOptions 28 | NS_SWIFT_NAME(initializeSDK(platformUrl:isAppSessionDataCollectionAllowed:launchOptions:)); 29 | 30 | - (void)enableDetectionsIfUserExists; 31 | 32 | @end 33 | 34 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNSentianceHelper.m 3 | // RNSentianceCore 4 | // 5 | // Created by Hassan Shakeel on 10/05/2022. 6 | // 7 | 8 | #import 9 | #import "RNSentianceHelper.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import "RNSentianceCore+Converter.h" 15 | 16 | @implementation RNSentianceHelper 17 | 18 | - (SENTInitializationResult *)initializeSDKWithLaunchOptions:(nullable NSDictionary *)launchOptions { 19 | return [self initializeSDKWithPlatformUrl:nil isAppSessionDataCollectionAllowed:NO launchOptions:nil]; 20 | } 21 | 22 | - (SENTInitializationResult *)initializeSDKWithPlatformUrl:(NSString *)platformUrl 23 | launchOptions:(nullable NSDictionary *)launchOptions { 24 | return [self initializeSDKWithPlatformUrl:platformUrl isAppSessionDataCollectionAllowed:NO launchOptions:launchOptions]; 25 | } 26 | 27 | 28 | - (SENTInitializationResult *)initializeSDKWithPlatformUrl:(NSString *)platformUrl 29 | isAppSessionDataCollectionAllowed:(BOOL *)isAppSessionDataCollectionAllowed 30 | launchOptions:(nullable NSDictionary *)launchOptions { 31 | SENTOptions *options = [[SENTOptions alloc] initFor:SENTOptionsInitPurposeAppLaunch]; 32 | options.platformUrl = platformUrl; 33 | options.isAppSessionDataCollectionAllowed = isAppSessionDataCollectionAllowed; 34 | return [[Sentiance sharedInstance] initializeWithOptions:options launchOptions:launchOptions]; 35 | } 36 | 37 | - (void)enableDetectionsIfUserExists { 38 | if ([Sentiance sharedInstance].userExists) { 39 | [[Sentiance sharedInstance] enableDetectionsWithCompletionHandler:nil]; 40 | } 41 | } 42 | 43 | @end 44 | 45 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceNativeInitialization.h: -------------------------------------------------------------------------------- 1 | @interface RNSentianceNativeInitialization: NSObject 2 | + (RNSentianceNativeInitialization *)sharedObject; 3 | - (BOOL) isFlagFileExists; 4 | - (void) createFlagFile:(NSError **)error; 5 | - (void) removeFlagFile:(NSError **)error; 6 | @end 7 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceNativeInitialization.m: -------------------------------------------------------------------------------- 1 | #import "RNSentianceNativeInitialization.h" 2 | 3 | @implementation RNSentianceNativeInitialization 4 | 5 | + (RNSentianceNativeInitialization *)sharedObject { 6 | static RNSentianceNativeInitialization *rnSentianceNativeInitialization = nil; 7 | static dispatch_once_t onceToken; 8 | dispatch_once(&onceToken, ^{ 9 | rnSentianceNativeInitialization = [[self alloc] init]; 10 | }); 11 | return rnSentianceNativeInitialization; 12 | } 13 | 14 | - (NSString *) getRNSentianceDirectoryPath:(NSString *) docDir { 15 | return [docDir stringByAppendingPathComponent:@"RNSentiance"]; 16 | } 17 | 18 | - (NSString *) getNativeInitializationFilePath:(NSFileManager *)fileManager { 19 | NSString *docDir = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject].path; 20 | NSString *sentianceDir = [self getRNSentianceDirectoryPath:docDir]; 21 | NSString* path = [sentianceDir stringByAppendingPathComponent:@"rnsentiance_initialize_natively"]; 22 | return path; 23 | } 24 | 25 | - (BOOL)isFlagFileExists { 26 | NSFileManager *fileManager = [NSFileManager defaultManager]; 27 | NSString* path = [self getNativeInitializationFilePath:fileManager]; 28 | if([fileManager fileExistsAtPath:path]) { 29 | return YES; 30 | } else { 31 | return NO; 32 | } 33 | } 34 | 35 | - (void)createFlagFile:(NSError **)error { 36 | NSFileManager *fileManager = [NSFileManager defaultManager]; 37 | NSString *docDir = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject].path; 38 | NSString *sentianceDir = [self getRNSentianceDirectoryPath:docDir]; 39 | NSString* path = [self getNativeInitializationFilePath:fileManager]; 40 | 41 | [fileManager createDirectoryAtPath:sentianceDir withIntermediateDirectories:YES 42 | attributes:@{NSFileProtectionKey:NSFileProtectionNone} 43 | error:error]; 44 | 45 | if (*error == nil) { 46 | [fileManager createFileAtPath:path contents:nil attributes:@{NSFileProtectionKey:NSFileProtectionNone}]; 47 | } 48 | } 49 | 50 | - (void)removeFlagFile:(NSError **)error { 51 | NSFileManager *fileManager = [NSFileManager defaultManager]; 52 | NSString* path = [self getNativeInitializationFilePath:fileManager]; 53 | [fileManager removeItemAtPath:path error:error]; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceSubscription.h: -------------------------------------------------------------------------------- 1 | // 2 | // Subscription.h 3 | // Pods 4 | // 5 | // Created by Mohammed Aouf Zouag on 27/3/2023. 6 | // 7 | 8 | #import 9 | 10 | 11 | @interface RNSentianceSubscription : NSObject 12 | 13 | @property (nonatomic, readonly) NSInteger subscriptionId; 14 | @property (nonatomic, strong, readonly) NSString *eventType; 15 | 16 | - (instancetype)init NS_UNAVAILABLE; 17 | - (instancetype)initWithEventType:(NSString*)eventType subscriptionId:(NSInteger)subscriptionId NS_DESIGNATED_INITIALIZER; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceSubscription.m: -------------------------------------------------------------------------------- 1 | // 2 | // Subscription.m 3 | // DoubleConversion 4 | // 5 | // Created by Mohammed Aouf Zouag on 27/3/2023. 6 | // 7 | 8 | #import 9 | #import "RNSentianceSubscription.h" 10 | 11 | 12 | @interface RNSentianceSubscription () 13 | 14 | @property (nonatomic, readwrite) NSInteger subscriptionId; 15 | @property (nonatomic, strong, readwrite) NSString *eventType; 16 | 17 | @end 18 | 19 | @implementation RNSentianceSubscription 20 | 21 | - (instancetype)initWithEventType:(NSString *)eventType subscriptionId:(NSInteger)subscriptionId { 22 | self = [super init]; 23 | if (self) { 24 | self.eventType = eventType; 25 | self.subscriptionId = subscriptionId; 26 | } 27 | return self; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceSubscriptionDefinition.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNSentianceSubscriptionMapping.h 3 | // Pods 4 | // 5 | // Created by Mohammed Aouf Zouag on 27/3/2023. 6 | // 7 | 8 | #ifndef RNSentianceSubscriptionMapping_h 9 | #define RNSentianceSubscriptionMapping_h 10 | 11 | #import "RNSentianceFoundation.h" 12 | typedef NS_ENUM(NSUInteger, SENTSubscriptionType) { 13 | SENTSubscriptionTypeSingle, 14 | SENTSubscriptionTypeMultiple, 15 | }; 16 | 17 | @interface RNSentianceSubscriptionDefinition : NSObject 18 | 19 | @property (nonatomic, strong, readonly) NSString *eventType; 20 | @property (nonatomic, strong, readonly) SentianceBlock nativeSubscribeLogic; 21 | @property (nonatomic, strong, readonly) SentianceBlock nativeUnsubscribeLogic; 22 | @property (nonatomic, readonly) SENTSubscriptionType subscriptionType; 23 | 24 | - (instancetype)init NS_UNAVAILABLE; 25 | - (instancetype)initWithEventType:(NSString *)eventType 26 | nativeSubscribeLogic:(SentianceBlock)nativeSubscribeLogic 27 | nativeUnsubscribeLogic:(SentianceBlock)nativeUnsubscribeLogic 28 | subscriptionType:(SENTSubscriptionType)subscriptionType NS_DESIGNATED_INITIALIZER; 29 | 30 | @end 31 | 32 | #endif /* RNSentianceSubscriptionMapping_h */ 33 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceSubscriptionDefinition.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNSentianceSubscriptionMapping.m 3 | // DoubleConversion 4 | // 5 | // Created by Mohammed Aouf Zouag on 27/3/2023. 6 | // 7 | 8 | #import 9 | #import "RNSentianceSubscriptionDefinition.h" 10 | 11 | @interface RNSentianceSubscriptionDefinition () 12 | 13 | @property (nonatomic, strong, readwrite) NSString *eventType; 14 | @property (nonatomic, strong, readwrite) SentianceBlock nativeSubscribeLogic; 15 | @property (nonatomic, strong, readwrite) SentianceBlock nativeUnsubscribeLogic; 16 | @property (nonatomic, readwrite) SENTSubscriptionType subscriptionType; 17 | 18 | @end 19 | 20 | @implementation RNSentianceSubscriptionDefinition 21 | 22 | - (instancetype)initWithEventType:(NSString *)eventType 23 | nativeSubscribeLogic:(SentianceBlock)nativeSubscribeLogic 24 | nativeUnsubscribeLogic:(SentianceBlock)nativeUnsubscribeLogic 25 | subscriptionType:(SENTSubscriptionType)subscriptionType { 26 | self = [super init]; 27 | if (self) { 28 | self.eventType = eventType; 29 | self.nativeSubscribeLogic = nativeSubscribeLogic; 30 | self.nativeUnsubscribeLogic = nativeUnsubscribeLogic; 31 | self.subscriptionType = subscriptionType; 32 | } 33 | return self; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /packages/core/ios/RNSentianceSubscriptionsManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // SentianceSubscriptionsManager.h 3 | // Pods 4 | // 5 | // Created by Mohammed Aouf Zouag on 27/3/2023. 6 | // 7 | 8 | #ifndef SentianceSubscriptionsManager_h 9 | #define SentianceSubscriptionsManager_h 10 | 11 | #import "RNSentianceFoundation.h" 12 | #import "RNSentianceSubscriptionDefinition.h" 13 | #import 14 | 15 | @interface RNSentianceSubscriptionsManager : NSObject 16 | 17 | - (void)addSupportedSubscriptionForEventType:(NSString *)eventType 18 | nativeSubscribeLogic:(SentianceBlock)nativeSubscribeLogic 19 | nativeUnsubscribeLogic:(SentianceBlock)nativeUnsubscribeLogic 20 | subscriptionType:(SENTSubscriptionType)subscriptionType; 21 | - (void)addSubscriptionForEventType:(NSString *)eventType subscriptionId:(NSInteger)subscriptionId; 22 | - (void)removeSubscriptionForId:(NSInteger)subscriptionId eventType:(NSString *)eventType; 23 | 24 | @end 25 | 26 | #endif /* SentianceSubscriptionsManager_h */ 27 | -------------------------------------------------------------------------------- /packages/core/jest/mockNativeModule.js: -------------------------------------------------------------------------------- 1 | import {mockNativeModule} from "../../../jest/mockNativeModules"; 2 | 3 | export function mockNativeCoreModule(platform, module) { 4 | return mockNativeModule( 5 | platform, 6 | { 7 | androidName: 'SentianceCore', 8 | iosName: 'SentianceCore' 9 | }, 10 | module); 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/lib/core.js: -------------------------------------------------------------------------------- 1 | const {NativeModules, NativeEventEmitter} = require("react-native"); 2 | const {varToString} = require("./generated/utils"); 3 | const {SentianceCore} = NativeModules; 4 | 5 | const SDK_STATUS_UPDATE_EVENT = "SENTIANCE_STATUS_UPDATE_EVENT"; 6 | const SDK_USER_LINK_EVENT = "SENTIANCE_USER_LINK_EVENT"; 7 | const SDK_USER_ACTIVITY_UPDATE_EVENT = "SENTIANCE_USER_ACTIVITY_UPDATE_EVENT"; 8 | const SDK_ON_TRIP_TIMED_OUT_EVENT = "SENTIANCE_ON_TRIP_TIMED_OUT_EVENT"; 9 | 10 | let coreModule = {}; 11 | if (!SentianceCore) { 12 | const nativeModuleName = varToString({SentianceCore}); 13 | console.error(`Could not locate the native ${nativeModuleName} module. 14 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 15 | } else { 16 | const SENTIANCE_EMITTER = new NativeEventEmitter(SentianceCore); 17 | 18 | const _addSdkStatusUpdateListener = async (onSdkStatusUpdated) => { 19 | await SentianceCore.listenSdkStatusUpdates(); 20 | return SENTIANCE_EMITTER.addListener(SDK_STATUS_UPDATE_EVENT, (sdkStatus) => { 21 | onSdkStatusUpdated(sdkStatus); 22 | }); 23 | }; 24 | 25 | const _addUserLinkListener = async (linker) => { 26 | const subscription = SENTIANCE_EMITTER.addListener(SDK_USER_LINK_EVENT, async (data) => { 27 | const {installId} = data; 28 | const linkingResult = await linker(installId); 29 | if (typeof linkingResult != "boolean") { 30 | console.error('Expected linker result of type boolean, got: ' + typeof linkingResult); 31 | SentianceCore.userLinkCallback(false); 32 | } else { 33 | SentianceCore.userLinkCallback(linkingResult); 34 | } 35 | subscription.remove(); 36 | }); 37 | }; 38 | 39 | const _addSdkUserActivityUpdateListener = async (onUserActivityUpdated) => { 40 | await SentianceCore.listenUserActivityUpdates(); 41 | return SENTIANCE_EMITTER.addListener(SDK_USER_ACTIVITY_UPDATE_EVENT, (data) => { 42 | onUserActivityUpdated(data); 43 | }); 44 | }; 45 | 46 | const _addTripTimeoutListener = async (onTripTimedOut) => { 47 | await SentianceCore.listenTripTimeout(); 48 | return SENTIANCE_EMITTER.addListener(SDK_ON_TRIP_TIMED_OUT_EVENT, onTripTimedOut); 49 | }; 50 | 51 | SentianceCore._addSdkStatusUpdateListener = _addSdkStatusUpdateListener; 52 | SentianceCore._addUserLinkListener = _addUserLinkListener; 53 | SentianceCore._addSdkUserActivityUpdateListener = _addSdkUserActivityUpdateListener; 54 | SentianceCore._addTripTimeoutListener = _addTripTimeoutListener; 55 | coreModule = SentianceCore; 56 | } 57 | 58 | module.exports = coreModule; 59 | -------------------------------------------------------------------------------- /packages/core/lib/generated/native-module.d.ts: -------------------------------------------------------------------------------- 1 | export interface NativeModule { 2 | addListener: (eventType: string) => void; 3 | removeListeners: (count: number) => void; 4 | addNativeListener: (eventName: string, subscriptionKey: number, payload?: Map) => Promise; 5 | removeNativeListener: (eventName: string, subscriptionKey: number) => Promise; 6 | } 7 | export declare function isValidNativeModule(obj: Partial): obj is NativeModule; 8 | -------------------------------------------------------------------------------- /packages/core/lib/generated/native-module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.isValidNativeModule = void 0; 4 | function isValidNativeModule(obj) { 5 | return (typeof obj.addListener === "function" && 6 | typeof obj.removeListeners === "function" && 7 | typeof obj.addNativeListener === "function" && 8 | typeof obj.removeNativeListener === "function"); 9 | } 10 | exports.isValidNativeModule = isValidNativeModule; 11 | -------------------------------------------------------------------------------- /packages/core/lib/generated/require-native-module.d.ts: -------------------------------------------------------------------------------- 1 | import { type NativeModule } from "./native-module"; 2 | export default function (specs: { 3 | androidName: string; 4 | isModuleVerified: (obj: Partial) => obj is T; 5 | iosName?: string; 6 | }): T | undefined; 7 | -------------------------------------------------------------------------------- /packages/core/lib/generated/require-native-module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const react_native_1 = require("react-native"); 4 | const defaultNativeIosModuleName = "SentianceCore"; 5 | function default_1(specs) { 6 | const { androidName, iosName, isModuleVerified } = specs; 7 | const moduleName = react_native_1.Platform.OS === "android" ? androidName : iosName !== null && iosName !== void 0 ? iosName : defaultNativeIosModuleName; 8 | const nativeModule = react_native_1.NativeModules[moduleName]; 9 | if (!nativeModule) { 10 | console.error(`Could not locate the native ${moduleName} module. 11 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 12 | return undefined; 13 | } 14 | if (isModuleVerified(nativeModule)) { 15 | return nativeModule; 16 | } 17 | else { 18 | console.error(`The ${moduleName} module is missing 1 or more required bindings. 19 | Make sure that your native code is correctly exporting all expected bindings.`); 20 | return undefined; 21 | } 22 | } 23 | exports.default = default_1; 24 | -------------------------------------------------------------------------------- /packages/core/lib/generated/sentiance-event-emitter.d.ts: -------------------------------------------------------------------------------- 1 | import { type EmitterSubscription, NativeEventEmitter } from "react-native"; 2 | import { type NativeModule } from "./native-module"; 3 | export default class SentianceEventEmitter { 4 | protected nativeModule: NativeModule; 5 | protected reactNativeEventEmitter: NativeEventEmitter; 6 | constructor(nativeModule: NativeModule); 7 | /** 8 | * Registers a new listener with the React Native framework, in addition to our own 9 | * native modules via the `addNativeListener` binding. 10 | * 11 | * @param eventName the name of the event for which you are registering a listener 12 | * @param listener the listener to be registered 13 | * @param context additional context info related to the listener 14 | * @returns a promise that resolves to a subscription object which provides a `remove()` function, 15 | * allowing consumers to remove the associated listener. 16 | */ 17 | addListener(eventName: string, listener: (...args: T[]) => any, context?: any): Promise; 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/lib/generated/sentiance-event-emitter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __importDefault = (this && this.__importDefault) || function (mod) { 12 | return (mod && mod.__esModule) ? mod : { "default": mod }; 13 | }; 14 | Object.defineProperty(exports, "__esModule", { value: true }); 15 | const react_native_1 = require("react-native"); 16 | const subscription_id_gen_1 = __importDefault(require("./subscription-id-gen")); 17 | class SentianceEventEmitter { 18 | constructor(nativeModule) { 19 | this.nativeModule = nativeModule; 20 | this.reactNativeEventEmitter = new react_native_1.NativeEventEmitter(nativeModule); 21 | } 22 | /** 23 | * Registers a new listener with the React Native framework, in addition to our own 24 | * native modules via the `addNativeListener` binding. 25 | * 26 | * @param eventName the name of the event for which you are registering a listener 27 | * @param listener the listener to be registered 28 | * @param context additional context info related to the listener 29 | * @returns a promise that resolves to a subscription object which provides a `remove()` function, 30 | * allowing consumers to remove the associated listener. 31 | */ 32 | addListener(eventName, listener, context) { 33 | return __awaiter(this, void 0, void 0, function* () { 34 | // Register a new subscription with the React Native framework 35 | const subscription = this.reactNativeEventEmitter.addListener(eventName, listener, context); 36 | const rnRemove = subscription.remove.bind(subscription); 37 | // Upon removing a subscription, we want to notify the RN framework in addition to our own native modules. 38 | // so we upgrade the behavior of the subscriptions' remove() function 39 | subscription.key = subscription_id_gen_1.default.next(); 40 | subscription.eventType = eventName; 41 | subscription.remove = () => { 42 | rnRemove(); 43 | this.nativeModule.removeNativeListener(eventName, subscription.key); 44 | }; 45 | yield this.nativeModule.addNativeListener(eventName, subscription.key, context); 46 | return subscription; 47 | }); 48 | } 49 | } 50 | exports.default = SentianceEventEmitter; 51 | -------------------------------------------------------------------------------- /packages/core/lib/generated/subscription-id-gen.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Singleton to generate identifiers for callbacks. These identifiers are provided to 3 | * our native modules upon registering/unregistering a callback. 4 | */ 5 | declare class SubscriptionIdGenerator { 6 | private static instance; 7 | private id; 8 | private constructor(); 9 | static getInstance(): SubscriptionIdGenerator; 10 | next(): number; 11 | } 12 | declare const _default: SubscriptionIdGenerator; 13 | export default _default; 14 | -------------------------------------------------------------------------------- /packages/core/lib/generated/subscription-id-gen.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | /** 4 | * Singleton to generate identifiers for callbacks. These identifiers are provided to 5 | * our native modules upon registering/unregistering a callback. 6 | */ 7 | class SubscriptionIdGenerator { 8 | constructor() { 9 | this.id = 0; 10 | // Prevent external instantiation 11 | } 12 | static getInstance() { 13 | if (!SubscriptionIdGenerator.instance) { 14 | SubscriptionIdGenerator.instance = new SubscriptionIdGenerator(); 15 | } 16 | return SubscriptionIdGenerator.instance; 17 | } 18 | next() { 19 | return ++this.id; 20 | } 21 | } 22 | exports.default = SubscriptionIdGenerator.getInstance(); 23 | -------------------------------------------------------------------------------- /packages/core/lib/generated/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare function varToString(obj: { 2 | [key: string]: any; 3 | }): string; 4 | -------------------------------------------------------------------------------- /packages/core/lib/generated/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.varToString = void 0; 4 | function varToString(obj) { 5 | var _a; 6 | return (_a = Object.keys(obj)[0]) !== null && _a !== void 0 ? _a : ""; 7 | } 8 | exports.varToString = varToString; 9 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sentiance-react-native/core", 3 | "version": "6.10.1", 4 | "description": "The Sentiance Core library", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "test": "jest --verbose", 9 | "lint": "npx eslint lib/index.d.ts" 10 | }, 11 | "keywords": [ 12 | "react-native", 13 | "core", 14 | "sentiance" 15 | ], 16 | "peerDependencies": { 17 | "react-native": ">=0.60" 18 | }, 19 | "author": "", 20 | "license": "", 21 | "homepage": "https://github.com/sentiance/react-native-sentiance/tree/main/packages/core", 22 | "repository": "github:sentiance/react-native-sentiance", 23 | "publishConfig": { 24 | "access": "public" 25 | }, 26 | "sdkVersions": { 27 | "android": { 28 | "minSdk": 23, 29 | "targetSdk": 34, 30 | "compileSdk": 34, 31 | "buildTools": "34.0.0", 32 | "sentiance": "6.12.+" 33 | }, 34 | "ios": { 35 | "sentiance": "~> 6.12.0" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/src/native-module.ts: -------------------------------------------------------------------------------- 1 | export interface NativeModule { 2 | // Required by RN 3 | addListener: (eventType: string) => void; 4 | removeListeners: (count: number) => void; 5 | 6 | // Required by us 7 | addNativeListener: ( 8 | eventName: string, 9 | subscriptionKey: number, 10 | payload?: Map 11 | ) => Promise; 12 | removeNativeListener: ( 13 | eventName: string, 14 | subscriptionKey: number 15 | ) => Promise; 16 | } 17 | 18 | export function isValidNativeModule( 19 | obj: Partial 20 | ): obj is NativeModule { 21 | return ( 22 | typeof obj.addListener === "function" && 23 | typeof obj.removeListeners === "function" && 24 | typeof obj.addNativeListener === "function" && 25 | typeof obj.removeNativeListener === "function" 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/src/require-native-module.ts: -------------------------------------------------------------------------------- 1 | import { NativeModules, Platform } from "react-native"; 2 | import { type NativeModule } from "./native-module"; 3 | 4 | const defaultNativeIosModuleName = "SentianceCore"; 5 | 6 | export default function ( 7 | specs: { 8 | androidName: string, 9 | isModuleVerified: (obj: Partial) => obj is T, 10 | iosName?: string 11 | } 12 | ): T | undefined { 13 | const { androidName, iosName, isModuleVerified } = specs; 14 | const moduleName = Platform.OS === "android" ? androidName : iosName ?? defaultNativeIosModuleName; 15 | const nativeModule = NativeModules[moduleName] as Partial | undefined; 16 | 17 | if (!nativeModule) { 18 | console.error(`Could not locate the native ${moduleName} module. 19 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 20 | return undefined; 21 | } 22 | 23 | if (isModuleVerified(nativeModule)) { 24 | return nativeModule; 25 | } else { 26 | console.error(`The ${moduleName} module is missing 1 or more required bindings. 27 | Make sure that your native code is correctly exporting all expected bindings.`); 28 | return undefined; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/core/src/sentiance-event-emitter.ts: -------------------------------------------------------------------------------- 1 | import { type EmitterSubscription, NativeEventEmitter } from "react-native"; 2 | import { type NativeModule } from "./native-module"; 3 | import subscriptionIdGenerator from "./subscription-id-gen"; 4 | 5 | export default class SentianceEventEmitter { 6 | 7 | protected reactNativeEventEmitter: NativeEventEmitter; 8 | 9 | constructor(protected nativeModule: NativeModule) { 10 | this.reactNativeEventEmitter = new NativeEventEmitter(nativeModule); 11 | } 12 | 13 | /** 14 | * Registers a new listener with the React Native framework, in addition to our own 15 | * native modules via the `addNativeListener` binding. 16 | * 17 | * @param eventName the name of the event for which you are registering a listener 18 | * @param listener the listener to be registered 19 | * @param context additional context info related to the listener 20 | * @returns a promise that resolves to a subscription object which provides a `remove()` function, 21 | * allowing consumers to remove the associated listener. 22 | */ 23 | async addListener(eventName: string, listener: (...args: T[]) => any, context?: any): Promise { 24 | // Register a new subscription with the React Native framework 25 | const subscription = this.reactNativeEventEmitter.addListener(eventName, listener, context); 26 | const rnRemove = subscription.remove.bind(subscription); 27 | // Upon removing a subscription, we want to notify the RN framework in addition to our own native modules. 28 | // so we upgrade the behavior of the subscriptions' remove() function 29 | subscription.key = subscriptionIdGenerator.next(); 30 | subscription.eventType = eventName; 31 | subscription.remove = () => { 32 | rnRemove(); 33 | this.nativeModule.removeNativeListener(eventName, subscription.key); 34 | }; 35 | await this.nativeModule.addNativeListener(eventName, subscription.key, context); 36 | return subscription; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/src/subscription-id-gen.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Singleton to generate identifiers for callbacks. These identifiers are provided to 3 | * our native modules upon registering/unregistering a callback. 4 | */ 5 | class SubscriptionIdGenerator { 6 | private static instance: SubscriptionIdGenerator; 7 | private id = 0; 8 | 9 | private constructor() { 10 | // Prevent external instantiation 11 | } 12 | 13 | static getInstance(): SubscriptionIdGenerator { 14 | if (!SubscriptionIdGenerator.instance) { 15 | SubscriptionIdGenerator.instance = new SubscriptionIdGenerator(); 16 | } 17 | return SubscriptionIdGenerator.instance; 18 | } 19 | 20 | next(): number { 21 | return ++this.id; 22 | } 23 | } 24 | 25 | export default SubscriptionIdGenerator.getInstance(); 26 | -------------------------------------------------------------------------------- /packages/core/src/utils.ts: -------------------------------------------------------------------------------- 1 | export function varToString(obj: { [key: string]: any }): string { 2 | return Object.keys(obj)[0] ?? ""; 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "rootDir": "./src", 6 | "outDir": "./lib/generated", 7 | "declarationDir": "./lib/generated", 8 | "paths": { 9 | "react-native": [ 10 | "../../node_modules/react-native" 11 | ] 12 | } 13 | }, 14 | "include": [ 15 | "src/**/*" 16 | ], 17 | "exclude": [ 18 | "node_modules", 19 | "lib/generated" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/crash-detection/.npmignore: -------------------------------------------------------------------------------- 1 | __tests__ 2 | /jest 3 | android/src/test 4 | ios/*tests 5 | -------------------------------------------------------------------------------- /packages/crash-detection/README.md: -------------------------------------------------------------------------------- 1 | # Sentiance Crash Detection module for React Native 2 | 3 | ## Demo Application 4 | 5 | https://github.com/sentiance/sample-apps-react-native 6 | 7 | ## Usage 8 | 9 | To use the crash detection SDK module, please visit the corresponding [API reference page.](https://docs.sentiance.com/sdk/api-reference/react-native/crash-detection) 10 | -------------------------------------------------------------------------------- /packages/crash-detection/SentianceCrashDetection.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | corePackage = JSON.parse(File.read(File.join('..', 'core', 'package.json'))) 3 | sentiance_sdk_package_version = corePackage['sdkVersions']['ios']['sentiance'] 4 | sentiance_sdk_env_var_version = ENV["SENTIANCE_RN_IOS_SDK_VERSION"] 5 | 6 | Pod::Spec.new do |s| 7 | s.name = "SentianceCrashDetection" 8 | s.version = "6.12.0-alpha.8" 9 | s.summary = "SentianceCrashDetection" 10 | s.description = <<-DESC 11 | SentianceCrashDetection 12 | DESC 13 | s.homepage = "https://developers.sentiance.com/docs" 14 | s.license = "MIT" 15 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 16 | s.author = { "author" => "sdk@sentiance.com" } 17 | s.platform = :ios, "9.0" 18 | s.source = { :path => '.' } 19 | s.source_files = "ios/**/*.{h,m,swift}" 20 | s.requires_arc = true 21 | s.xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => '${PODS_ROOT}/SENTSDK' } 22 | s.swift_version = '5.0' 23 | s.dependency "React" 24 | 25 | if sentiance_sdk_env_var_version.nil? 26 | s.dependency "SENTSDK", sentiance_sdk_package_version 27 | else 28 | s.dependency "SENTSDK", sentiance_sdk_env_var_version 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /packages/crash-detection/android/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .idea 3 | .gradle 4 | -------------------------------------------------------------------------------- /packages/crash-detection/android/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.library" 3 | } 4 | 5 | def coreProj 6 | if (findProject(':core')) { 7 | coreProj = project(':core') 8 | } else if (findProject(':sentiance-react-native_core')) { 9 | // Starting from RN 0.61, the @ sign is stripped from project names 10 | coreProj = project(':sentiance-react-native_core') 11 | } else if (findProject(':@sentiance-react-native_core')) { 12 | // On RN 0.60, the @ sign is not stripped from project names 13 | coreProj = project(':@sentiance-react-native_core') 14 | } else { 15 | throw new GradleException('Could not find the @sentiance-react-native/core package, have you installed it?') 16 | } 17 | 18 | android { 19 | namespace "com.sentiance.react.bridge.crashdetection" 20 | 21 | compileOptions { 22 | sourceCompatibility JavaVersion.VERSION_1_8 23 | targetCompatibility JavaVersion.VERSION_1_8 24 | } 25 | } 26 | 27 | apply from: "$coreProj.projectDir/package-json-reader.gradle" 28 | apply from: "$coreProj.projectDir/sentiance-version-finder.gradle" 29 | 30 | def corePackageJson = PackageJson.of(coreProj) 31 | applyAndroidVersionsFrom(corePackageJson) 32 | def sentianceSdkVersion = getSentianceSdkVersion() 33 | 34 | dependencies { 35 | implementation(platform("com.sentiance:sdk-bom:${sentianceSdkVersion}")) 36 | api coreProj 37 | api("com.sentiance:sdk-crash-detection") { transitive = true } 38 | } 39 | 40 | applyReactNativeDependency() 41 | -------------------------------------------------------------------------------- /packages/crash-detection/android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | android.enableJetifier=true 3 | -------------------------------------------------------------------------------- /packages/crash-detection/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/packages/crash-detection/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /packages/crash-detection/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 31 10:45:16 CEST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /packages/crash-detection/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /packages/crash-detection/android/src/main/java/com/sentiance/react/bridge/crashdetection/CrashDetectionEmitter.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.crashdetection; 2 | 3 | import android.content.Context; 4 | 5 | import com.sentiance.react.bridge.core.common.base.AbstractSentianceEmitter; 6 | import com.sentiance.sdk.crashdetection.api.VehicleCrashEvent; 7 | import com.sentiance.sdk.crashdetection.api.VehicleCrashDiagnostic; 8 | 9 | class CrashDetectionEmitter extends AbstractSentianceEmitter { 10 | 11 | private static final String VEHICLE_CRASH_EVENT = "SENTIANCE_VEHICLE_CRASH_EVENT"; 12 | private static final String VEHICLE_CRASH_DIAGNOSTIC_EVENT = "SENTIANCE_VEHICLE_CRASH_DIAGNOSTIC_EVENT"; 13 | 14 | private final SentianceCrashDetectionConverter converter; 15 | 16 | public CrashDetectionEmitter(Context context) { 17 | super(context); 18 | converter = new SentianceCrashDetectionConverter(); 19 | } 20 | 21 | void sendVehicleCrashEvent(VehicleCrashEvent crashEvent) { 22 | sendEvent(VEHICLE_CRASH_EVENT, converter.convertVehicleCrashEvent(crashEvent)); 23 | } 24 | 25 | void sendVehicleCrashDiagnosticEvent(VehicleCrashDiagnostic vehicleCrashDiagnostic){ 26 | sendEvent(VEHICLE_CRASH_DIAGNOSTIC_EVENT, converter.convertVehicleCrashDiagnostic(vehicleCrashDiagnostic)); 27 | } 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /packages/crash-detection/android/src/main/java/com/sentiance/react/bridge/crashdetection/SentianceCrashDetectionConverter.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.crashdetection; 2 | 3 | import android.location.Location; 4 | 5 | import com.facebook.react.bridge.Arguments; 6 | import com.facebook.react.bridge.WritableArray; 7 | import com.facebook.react.bridge.WritableMap; 8 | import com.sentiance.react.bridge.core.SentianceConverter; 9 | import com.sentiance.sdk.crashdetection.api.VehicleCrashDiagnostic; 10 | import com.sentiance.sdk.crashdetection.api.VehicleCrashEvent; 11 | 12 | public class SentianceCrashDetectionConverter { 13 | 14 | public static final String JS_KEY_TIME = "time"; 15 | public static final String JS_KEY_DELTA_V = "deltaV"; 16 | public static final String JS_KEY_LOCATION = "location"; 17 | public static final String JS_KEY_MAGNITUDE = "magnitude"; 18 | public static final String JS_KEY_CONFIDENCE = "confidence"; 19 | public static final String JS_KEY_CRASH_SEVERITY = "severity"; 20 | public static final String JS_KEY_SPEED_AT_IMPACT = "speedAtImpact"; 21 | public static final String JS_KEY_PRECEDING_LOCATIONS = "precedingLocations"; 22 | public static final String JS_KEY_CRASH_DETECTION_STATE = "crashDetectionState"; 23 | public static final String JS_KEY_CRASH_DETECTION_STATE_DESC = "crashDetectionStateDescription"; 24 | 25 | private final SentianceConverter coreConverter; 26 | 27 | public SentianceCrashDetectionConverter() { 28 | coreConverter = new SentianceConverter(); 29 | } 30 | 31 | public WritableMap convertVehicleCrashEvent(VehicleCrashEvent crashEvent) { 32 | WritableMap map = Arguments.createMap(); 33 | 34 | map.putDouble(JS_KEY_TIME, (double) crashEvent.getTime()); 35 | 36 | WritableMap locationMap = coreConverter.convertLocation(crashEvent.getLocation()); 37 | map.putMap(JS_KEY_LOCATION, locationMap); 38 | 39 | map.putDouble(JS_KEY_MAGNITUDE, crashEvent.getMagnitude()); 40 | map.putDouble(JS_KEY_SPEED_AT_IMPACT, crashEvent.getSpeedAtImpact()); 41 | map.putDouble(JS_KEY_DELTA_V, crashEvent.getDeltaV()); 42 | map.putInt(JS_KEY_CONFIDENCE, crashEvent.getConfidence()); 43 | 44 | WritableArray precedingLocationsArray = Arguments.createArray(); 45 | for (Location location : crashEvent.getPrecedingLocations()) { 46 | precedingLocationsArray.pushMap(coreConverter.convertLocation(location)); 47 | } 48 | map.putArray(JS_KEY_PRECEDING_LOCATIONS, precedingLocationsArray); 49 | map.putString(JS_KEY_CRASH_SEVERITY, crashEvent.getSeverity().name()); 50 | 51 | return map; 52 | } 53 | 54 | public WritableMap convertVehicleCrashDiagnostic(VehicleCrashDiagnostic vehicleCrashDiagnostic) { 55 | WritableMap map = Arguments.createMap(); 56 | 57 | map.putString(JS_KEY_CRASH_DETECTION_STATE, vehicleCrashDiagnostic.getCrashDetectionState().name()); 58 | map.putString(JS_KEY_CRASH_DETECTION_STATE_DESC, vehicleCrashDiagnostic.getCrashDetectionStateDescription()); 59 | 60 | return map; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/crash-detection/android/src/main/java/com/sentiance/react/bridge/crashdetection/SentianceCrashDetectionPackage.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.crashdetection; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | public class SentianceCrashDetectionPackage implements ReactPackage { 15 | 16 | @NonNull 17 | @Override 18 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 19 | List modules = new ArrayList<>(); 20 | SentianceCrashDetectionModule crashDetectionModule = new SentianceCrashDetectionModule(reactContext); 21 | modules.add(crashDetectionModule); 22 | return modules; 23 | } 24 | 25 | @NonNull 26 | @Override 27 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 28 | return Collections.emptyList(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/crash-detection/ios/SentianceCrashDetection/SentianceCrashDetection-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | -------------------------------------------------------------------------------- /packages/crash-detection/ios/SentianceCrashDetection/SentianceCrashDetection.m: -------------------------------------------------------------------------------- 1 | // 2 | // SentianceCrashDetection.h 3 | // SentianceCrashDetection 4 | // 5 | // Created by Mohammed Aouf Zouag on 18/03/2025. 6 | // 7 | 8 | #import 9 | 10 | @interface RCT_EXTERN_MODULE(SentianceCrashDetection, NSObject) 11 | 12 | RCT_EXTERN_METHOD(isVehicleCrashDetectionSupported:(RCTPromiseResolveBlock)resolve 13 | rejecter:(RCTPromiseRejectBlock)reject) 14 | 15 | RCT_EXTERN_METHOD(invokeDummyVehicleCrash:(RCTPromiseResolveBlock)resolve 16 | rejecter:(RCTPromiseRejectBlock)reject) 17 | 18 | + (BOOL)requiresMainQueueSetup 19 | { 20 | return NO; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /packages/crash-detection/ios/SentianceCrashDetection/SentianceCrashDetection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SentianceCrashDetection.swift 3 | // SentianceCrashDetection 4 | // 5 | // Created by Mohammed Aouf Zouag on 18/03/2025. 6 | // 7 | 8 | import Foundation 9 | import SENTSDK 10 | 11 | @objc(SentianceCrashDetection) 12 | class SentianceCrashDetection: NSObject { 13 | 14 | private let sdkNotInitializedError = (code: ESDKNotInitialized, message: "SDK is not initialized") 15 | private let sentiance = Sentiance.shared 16 | 17 | @objc 18 | func isVehicleCrashDetectionSupported(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) { 19 | guard ensureSDKInitialized(reject) else { return } 20 | resolve(sentiance.isVehicleCrashDetectionSupported) 21 | } 22 | 23 | @objc 24 | func invokeDummyVehicleCrash(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) { 25 | guard ensureSDKInitialized(reject) else { return } 26 | sentiance.invokeDummyVehicleCrash() 27 | resolve(true) 28 | } 29 | 30 | private func ensureSDKInitialized(_ reject: RCTPromiseRejectBlock) -> Bool { 31 | guard sentiance.initState == SENTSDKInitState.initialized else { 32 | reject(sdkNotInitializedError.code, sdkNotInitializedError.message, nil) 33 | return false 34 | } 35 | return true 36 | } 37 | 38 | @objc 39 | static func requiresMainQueueSetup() -> Bool { 40 | return false 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/crash-detection/ios/SentianceCrashDetectionTests/SentianceCrashDetectionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SentianceCrashDetectionTests.swift 3 | // SentianceCrashDetectionTests 4 | // 5 | // Created by Mohammed Aouf Zouag on 18/03/2025. 6 | // 7 | 8 | final class SentianceCrashDetectionTests { 9 | } 10 | -------------------------------------------------------------------------------- /packages/crash-detection/lib/crash-detection.js: -------------------------------------------------------------------------------- 1 | const {NativeModules, NativeEventEmitter, Platform} = require("react-native"); 2 | const {varToString} = require("@sentiance-react-native/core/lib/generated/utils"); 3 | const {SentianceCrashDetection, SentianceCore} = NativeModules; 4 | const SDK_VEHICLE_CRASH_EVENT = "SENTIANCE_VEHICLE_CRASH_EVENT"; 5 | const SDK_VEHICLE_CRASH_DIAGNOSTIC_EVENT = "SENTIANCE_VEHICLE_CRASH_DIAGNOSTIC_EVENT"; 6 | 7 | let didLocateNativeModule = true; 8 | let crashDetectionModule = {}; 9 | if (Platform.OS === 'android') { 10 | if (!SentianceCrashDetection) { 11 | didLocateNativeModule = false; 12 | const nativeModuleName = varToString({SentianceCrashDetection}); 13 | console.error(`Could not locate the native ${nativeModuleName} module. 14 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 15 | } else { 16 | crashDetectionModule = SentianceCrashDetection 17 | } 18 | } else { 19 | if (!SentianceCore) { 20 | didLocateNativeModule = false; 21 | const nativeModuleName = varToString({SentianceCore}); 22 | console.error(`Could not locate the native ${nativeModuleName} module. 23 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 24 | } else if (!SentianceCrashDetection) { 25 | didLocateNativeModule = false; 26 | const nativeModuleName = varToString({SentianceCrashDetection}); 27 | console.error(`Could not locate the native ${nativeModuleName} module. 28 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 29 | } else { 30 | crashDetectionModule = { 31 | ...SentianceCore, 32 | isVehicleCrashDetectionSupported: SentianceCrashDetection.isVehicleCrashDetectionSupported, 33 | invokeDummyVehicleCrash: SentianceCrashDetection.invokeDummyVehicleCrash, 34 | } 35 | } 36 | } 37 | 38 | if (didLocateNativeModule) { 39 | const SENTIANCE_EMITTER = new NativeEventEmitter(crashDetectionModule); 40 | 41 | crashDetectionModule._addVehicleCrashEventListener = async (onVehicleCrash) => { 42 | await crashDetectionModule.listenVehicleCrashEvents(); 43 | return SENTIANCE_EMITTER.addListener(SDK_VEHICLE_CRASH_EVENT, (data) => { 44 | onVehicleCrash(data); 45 | }); 46 | }; 47 | 48 | crashDetectionModule._addVehicleCrashDiagnosticListener = async (onVehicleCrashDiagnostic) => { 49 | await crashDetectionModule.listenVehicleCrashDiagnostic(); 50 | return SENTIANCE_EMITTER.addListener(SDK_VEHICLE_CRASH_DIAGNOSTIC_EVENT, (data) => { 51 | onVehicleCrashDiagnostic(data); 52 | }); 53 | }; 54 | } 55 | 56 | module.exports = crashDetectionModule; 57 | -------------------------------------------------------------------------------- /packages/crash-detection/lib/index.js: -------------------------------------------------------------------------------- 1 | const crashDetection = require('./crash-detection'); 2 | 3 | const listenVehicleCrashEvents = () => crashDetection.listenVehicleCrashEvents(); 4 | const invokeDummyVehicleCrash = () => crashDetection.invokeDummyVehicleCrash(); 5 | const isVehicleCrashDetectionSupported = () => crashDetection.isVehicleCrashDetectionSupported(); 6 | const addVehicleCrashEventListener = crashDetection._addVehicleCrashEventListener; 7 | const addVehicleCrashDiagnosticListener = crashDetection._addVehicleCrashDiagnosticListener; 8 | 9 | module.exports = { 10 | listenVehicleCrashEvents, 11 | invokeDummyVehicleCrash, 12 | isVehicleCrashDetectionSupported, 13 | addVehicleCrashEventListener, 14 | addVehicleCrashDiagnosticListener, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/crash-detection/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sentiance-react-native/crash-detection", 3 | "version": "6.10.1", 4 | "description": "The Sentiance Crash Detection library", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "lint": "npx eslint lib/index.d.ts" 10 | }, 11 | "keywords": [ 12 | "react-native", 13 | "crash-detection", 14 | "sentiance" 15 | ], 16 | "peerDependencies": { 17 | "@sentiance-react-native/core": "6.10.1" 18 | }, 19 | "author": "", 20 | "license": "", 21 | "homepage": "https://github.com/sentiance/react-native-sentiance/tree/main/packages/crash-detection", 22 | "repository": "github:sentiance/react-native-sentiance", 23 | "publishConfig": { 24 | "access": "public" 25 | } 26 | } -------------------------------------------------------------------------------- /packages/driving-insights/.npmignore: -------------------------------------------------------------------------------- 1 | __tests__ 2 | /jest 3 | android/src/test 4 | -------------------------------------------------------------------------------- /packages/driving-insights/android/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .idea 3 | .gradle 4 | -------------------------------------------------------------------------------- /packages/driving-insights/android/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.library" 3 | } 4 | 5 | def coreProj 6 | if (findProject(':core')) { 7 | coreProj = project(':core') 8 | } else if (findProject(':sentiance-react-native_core')) { 9 | // Starting from RN 0.61, the @ sign is stripped from project names 10 | coreProj = project(':sentiance-react-native_core') 11 | } else if (findProject(':@sentiance-react-native_core')) { 12 | // On RN 0.60, the @ sign is not stripped from project names 13 | coreProj = project(':@sentiance-react-native_core') 14 | } else { 15 | throw new GradleException('Could not find the @sentiance-react-native/core package, have you installed it?') 16 | } 17 | 18 | def eventTimelineProj 19 | if (findProject(':event-timeline')) { 20 | eventTimelineProj = project(':event-timeline') 21 | } else if (findProject(':sentiance-react-native_event-timeline')) { 22 | eventTimelineProj = project(':sentiance-react-native_event-timeline') 23 | } else if (findProject(':@sentiance-react-native_event-timeline')) { 24 | eventTimelineProj = project(':@sentiance-react-native_event-timeline') 25 | } else { 26 | throw new GradleException('Could not find the @sentiance-react-native/event-timeline package, have you installed it?') 27 | } 28 | 29 | android { 30 | namespace "com.sentiance.react.bridge.drivinginsights" 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | } 37 | 38 | apply from: "$coreProj.projectDir/package-json-reader.gradle" 39 | apply from: "$coreProj.projectDir/sentiance-version-finder.gradle" 40 | 41 | def corePackageJson = PackageJson.of(coreProj) 42 | applyAndroidVersionsFrom(corePackageJson) 43 | def sentianceSdkVersion = getSentianceSdkVersion() 44 | 45 | dependencies { 46 | implementation(platform("com.sentiance:sdk-bom:${sentianceSdkVersion}")) 47 | api("com.sentiance:sdk-driving-insights") { transitive = true } 48 | 49 | implementation coreProj 50 | implementation eventTimelineProj 51 | 52 | if (findProject(':test-common')) { 53 | testImplementation project(':test-common') 54 | } 55 | } 56 | 57 | applyReactNativeDependency() 58 | -------------------------------------------------------------------------------- /packages/driving-insights/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/driving-insights/android/src/main/java/com/sentiance/react/bridge/drivinginsights/DrivingInsightsEmitter.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.drivinginsights; 2 | 3 | import android.content.Context; 4 | 5 | import com.sentiance.react.bridge.core.common.base.AbstractSentianceEmitter; 6 | import com.sentiance.sdk.drivinginsights.api.DrivingInsights; 7 | 8 | public class DrivingInsightsEmitter extends AbstractSentianceEmitter { 9 | 10 | public static final String DRIVING_INSIGHTS_READY_EVENT = "SENTIANCE_DRIVING_INSIGHTS_READY_EVENT"; 11 | private final DrivingInsightsConverter converter; 12 | 13 | public DrivingInsightsEmitter(Context context) { 14 | super(context); 15 | converter = new DrivingInsightsConverter(); 16 | } 17 | 18 | public void sendDrivingInsightsReadyEvent(DrivingInsights drivingInsights) { 19 | sendEvent(DRIVING_INSIGHTS_READY_EVENT, converter.convertDrivingInsights(drivingInsights)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/driving-insights/android/src/main/java/com/sentiance/react/bridge/drivinginsights/SentianceDrivingInsightsPackage.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.drivinginsights; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | import com.sentiance.react.bridge.core.common.SentianceSubscriptionsManager; 10 | import com.sentiance.sdk.Sentiance; 11 | import com.sentiance.sdk.drivinginsights.api.DrivingInsightsApi; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | public class SentianceDrivingInsightsPackage implements ReactPackage { 18 | @NonNull 19 | @Override 20 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 21 | List modules = new ArrayList<>(); 22 | DrivingInsightsModule drivingInsightsModule = new DrivingInsightsModule( 23 | reactContext, 24 | Sentiance.getInstance(reactContext), 25 | DrivingInsightsApi.getInstance(reactContext), 26 | new DrivingInsightsEmitter(reactContext), 27 | new SentianceSubscriptionsManager(), 28 | new DrivingInsightsConverter()); 29 | modules.add(drivingInsightsModule); 30 | return modules; 31 | } 32 | 33 | @NonNull 34 | @Override 35 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 36 | return Collections.emptyList(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/driving-insights/android/src/test/java/com/sentiance/react/bridge/drivinginsights/util/validators/CallWhileMovingEventBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.drivinginsights.util.validators; 2 | 3 | import static com.sentiance.react.bridge.drivinginsights.DrivingInsightsConverter.JS_KEY_MAX_TRAVELLED_SPEED_MPS; 4 | import static com.sentiance.react.bridge.drivinginsights.DrivingInsightsConverter.JS_KEY_MIN_TRAVELLED_SPEED_MPS; 5 | import static org.junit.Assert.assertEquals; 6 | import static org.junit.Assert.assertFalse; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import com.facebook.react.bridge.JavaOnlyMap; 11 | import com.sentiance.sdk.drivinginsights.api.CallWhileMovingEvent; 12 | 13 | public class CallWhileMovingEventBridgeValidator extends DrivingEventBridgeValidator { 14 | 15 | @Override 16 | public void validate(@NonNull CallWhileMovingEvent expected, @NonNull JavaOnlyMap actual) { 17 | super.validate(expected, actual); 18 | 19 | if (expected.getMaxTraveledSpeedInMps() == null) { 20 | assertFalse(actual.hasKey(JS_KEY_MAX_TRAVELLED_SPEED_MPS)); 21 | } else { 22 | assertEquals(expected.getMaxTraveledSpeedInMps(), actual.getDouble(JS_KEY_MAX_TRAVELLED_SPEED_MPS), 0.0); 23 | } 24 | 25 | if (expected.getMinTraveledSpeedInMps() == null) { 26 | assertFalse(actual.hasKey(JS_KEY_MIN_TRAVELLED_SPEED_MPS)); 27 | } else { 28 | assertEquals(expected.getMinTraveledSpeedInMps(), actual.getDouble(JS_KEY_MIN_TRAVELLED_SPEED_MPS), 0.0); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/driving-insights/android/src/test/java/com/sentiance/react/bridge/drivinginsights/util/validators/DrivingEventBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.drivinginsights.util.validators; 2 | 3 | import static com.sentiance.react.bridge.eventtimeline.converters.OnDeviceTypesConverter.JS_KEY_END_TIME; 4 | import static com.sentiance.react.bridge.eventtimeline.converters.OnDeviceTypesConverter.JS_KEY_END_TIME_EPOCH; 5 | import static com.sentiance.react.bridge.eventtimeline.converters.OnDeviceTypesConverter.JS_KEY_START_TIME; 6 | import static com.sentiance.react.bridge.eventtimeline.converters.OnDeviceTypesConverter.JS_KEY_START_TIME_EPOCH; 7 | import static org.junit.Assert.assertEquals; 8 | 9 | import androidx.annotation.NonNull; 10 | 11 | import com.facebook.react.bridge.JavaOnlyMap; 12 | import com.sentiance.react.bridge.test.validators.BridgeValidator; 13 | import com.sentiance.react.bridge.test.validators.WaypointsBridgeValidator; 14 | import com.sentiance.sdk.drivinginsights.api.DrivingEvent; 15 | 16 | public class DrivingEventBridgeValidator implements BridgeValidator { 17 | 18 | private final WaypointsBridgeValidator waypointsValidator; 19 | 20 | public DrivingEventBridgeValidator() { 21 | waypointsValidator = new WaypointsBridgeValidator(); 22 | } 23 | 24 | @Override 25 | public void validate(@NonNull T expected, @NonNull JavaOnlyMap actual) { 26 | assertEquals( 27 | expected.getStartTime().toString(), 28 | actual.getString(JS_KEY_START_TIME)); 29 | assertEquals( 30 | expected.getStartTime().getEpochTime(), 31 | actual.getDouble(JS_KEY_START_TIME_EPOCH), 0.00000001); 32 | assertEquals( 33 | expected.getEndTime().toString(), 34 | actual.getString(JS_KEY_END_TIME)); 35 | assertEquals( 36 | expected.getEndTime().getEpochTime(), 37 | actual.getDouble(JS_KEY_END_TIME_EPOCH), 0.00000001); 38 | 39 | waypointsValidator.validate(expected.getWaypoints(), actual); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/driving-insights/android/src/test/java/com/sentiance/react/bridge/drivinginsights/util/validators/HarshEventBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.drivinginsights.util.validators; 2 | 3 | import static com.sentiance.react.bridge.drivinginsights.DrivingInsightsConverter.JS_KEY_CONFIDENCE; 4 | import static com.sentiance.react.bridge.drivinginsights.DrivingInsightsConverter.JS_KEY_MAGNITUDE; 5 | import static com.sentiance.react.bridge.drivinginsights.DrivingInsightsConverter.JS_KEY_TYPE; 6 | import static org.junit.Assert.assertEquals; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import com.facebook.react.bridge.JavaOnlyMap; 11 | import com.sentiance.sdk.drivinginsights.api.HarshDrivingEvent; 12 | 13 | public class HarshEventBridgeValidator extends DrivingEventBridgeValidator { 14 | 15 | @Override 16 | public void validate(@NonNull HarshDrivingEvent expected, @NonNull JavaOnlyMap actual) { 17 | super.validate(expected, actual); 18 | assertEquals( 19 | expected.getMagnitude(), 20 | actual.getDouble(JS_KEY_MAGNITUDE), 0.001); 21 | assertEquals(expected.getConfidence(), actual.getInt(JS_KEY_CONFIDENCE)); 22 | assertEquals(expected.getType().name(), actual.getString(JS_KEY_TYPE)); 23 | } 24 | 25 | @Override 26 | public void validate(@NonNull JavaOnlyMap expected, @NonNull HarshDrivingEvent actual) { 27 | validate(actual, expected); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/driving-insights/android/src/test/java/com/sentiance/react/bridge/drivinginsights/util/validators/PhoneUsageEventBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.drivinginsights.util.validators; 2 | 3 | import com.sentiance.sdk.drivinginsights.api.PhoneUsageEvent; 4 | 5 | public class PhoneUsageEventBridgeValidator extends DrivingEventBridgeValidator { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /packages/driving-insights/android/src/test/java/com/sentiance/react/bridge/drivinginsights/util/validators/SpeedingEventBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.drivinginsights.util.validators; 2 | 3 | import com.sentiance.sdk.drivinginsights.api.SpeedingEvent; 4 | 5 | public class SpeedingEventBridgeValidator extends DrivingEventBridgeValidator { 6 | } 7 | -------------------------------------------------------------------------------- /packages/driving-insights/jest/mockNativeModule.js: -------------------------------------------------------------------------------- 1 | import {mockNativeModule} from "../../../jest/mockNativeModules"; 2 | 3 | export function mockNativeDrivingInsightsModule(platform, module) { 4 | return mockNativeModule( 5 | platform, 6 | { 7 | androidName: 'SentianceDrivingInsights', 8 | iosName: 'SentianceCore' 9 | }, 10 | module); 11 | } 12 | -------------------------------------------------------------------------------- /packages/driving-insights/lib/driving-insights.js: -------------------------------------------------------------------------------- 1 | const { NativeModules, Platform } = require("react-native"); 2 | const { varToString } = require("@sentiance-react-native/core/lib/generated/utils"); 3 | const SentianceEventEmitter = require("@sentiance-react-native/core/lib/generated/sentiance-event-emitter").default; 4 | const { SentianceDrivingInsights, SentianceCore } = NativeModules; 5 | const DRIVING_INSIGHTS_READY_EVENT = "SENTIANCE_DRIVING_INSIGHTS_READY_EVENT"; 6 | 7 | let didLocateNativeModule = true; 8 | let drivingInsightsModule = {}; 9 | if (Platform.OS === "android") { 10 | if (!SentianceDrivingInsights) { 11 | didLocateNativeModule = false; 12 | const nativeModuleName = varToString({ SentianceDrivingInsights }); 13 | console.error(`Could not locate the native ${nativeModuleName} module. 14 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 15 | } else { 16 | drivingInsightsModule = SentianceDrivingInsights; 17 | } 18 | } else { 19 | if (!SentianceCore) { 20 | didLocateNativeModule = false; 21 | const nativeModuleName = varToString({ SentianceCore }); 22 | console.error(`Could not locate the native ${nativeModuleName} module. 23 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 24 | } else { 25 | drivingInsightsModule = SentianceCore; 26 | } 27 | } 28 | 29 | if (didLocateNativeModule) { 30 | const emitter = new SentianceEventEmitter(drivingInsightsModule); 31 | 32 | drivingInsightsModule._addDrivingInsightsReadyListener = (onDrivingInsightsReady) => 33 | emitter.addListener(DRIVING_INSIGHTS_READY_EVENT, onDrivingInsightsReady); 34 | } 35 | 36 | module.exports = drivingInsightsModule; 37 | module.exports.events = { 38 | DRIVING_INSIGHTS_READY_EVENT 39 | }; 40 | -------------------------------------------------------------------------------- /packages/driving-insights/lib/index.js: -------------------------------------------------------------------------------- 1 | const drivingInsights = require("./driving-insights"); 2 | import avgOverallSafetyScoreApi from "./avg-overall-safety-score-api"; 3 | 4 | const getDrivingInsights = (transportId) => drivingInsights.getDrivingInsights(transportId); 5 | const getHarshDrivingEvents = (transportId) => drivingInsights.getHarshDrivingEvents(transportId); 6 | const getPhoneUsageEvents = (transportId) => drivingInsights.getPhoneUsageEvents(transportId); 7 | const getCallWhileMovingEvents = (transportId) => drivingInsights.getCallWhileMovingEvents(transportId); 8 | const getSpeedingEvents = (transportId) => drivingInsights.getSpeedingEvents(transportId); 9 | const addDrivingInsightsReadyListener = drivingInsights._addDrivingInsightsReadyListener; 10 | const events = drivingInsights.events; 11 | 12 | const getAverageOverallSafetyScore = (params) => avgOverallSafetyScoreApi(drivingInsights, params); 13 | 14 | module.exports = { 15 | getDrivingInsights, 16 | getHarshDrivingEvents, 17 | getPhoneUsageEvents, 18 | getCallWhileMovingEvents, 19 | getSpeedingEvents, 20 | addDrivingInsightsReadyListener, 21 | getAverageOverallSafetyScore, 22 | events 23 | }; 24 | -------------------------------------------------------------------------------- /packages/driving-insights/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sentiance-react-native/driving-insights", 3 | "version": "6.10.1", 4 | "description": "The Sentiance Driving Insights library", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "test": "jest --verbose", 9 | "lint": "npx eslint lib/index.d.ts" 10 | }, 11 | "keywords": [ 12 | "react-native", 13 | "driving-insights", 14 | "sentiance" 15 | ], 16 | "peerDependencies": { 17 | "@sentiance-react-native/core": "6.10.1", 18 | "react-native": "*" 19 | }, 20 | "author": "", 21 | "license": "", 22 | "homepage": "https://github.com/sentiance/react-native-sentiance/tree/main/packages/driving-insights", 23 | "repository": "github:sentiance/react-native-sentiance", 24 | "publishConfig": { 25 | "access": "public" 26 | } 27 | } -------------------------------------------------------------------------------- /packages/event-timeline/.npmignore: -------------------------------------------------------------------------------- 1 | __tests__ 2 | /jest 3 | android/src/test 4 | tsconfig.json 5 | /src 6 | -------------------------------------------------------------------------------- /packages/event-timeline/README.md: -------------------------------------------------------------------------------- 1 | # Sentiance Event Timeline module for React Native 2 | 3 | ## Demo Application 4 | 5 | https://github.com/sentiance/sample-apps-react-native 6 | 7 | ## Usage 8 | 9 | To use the Event Timeline SDK module, please visit the corresponding [API reference page.](https://docs.sentiance.com/sdk/api-reference/react-native/event-timeline) 10 | -------------------------------------------------------------------------------- /packages/event-timeline/android/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .idea 3 | .gradle 4 | -------------------------------------------------------------------------------- /packages/event-timeline/android/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.library" 3 | } 4 | 5 | def coreProj 6 | if (findProject(':core')) { 7 | coreProj = project(':core') 8 | } else if (findProject(':sentiance-react-native_core')) { 9 | // Starting from RN 0.61, the @ sign is stripped from project names 10 | coreProj = project(':sentiance-react-native_core') 11 | } else if (findProject(':@sentiance-react-native_core')) { 12 | // On RN 0.60, the @ sign is not stripped from project names 13 | coreProj = project(':@sentiance-react-native_core') 14 | } else { 15 | throw new GradleException('Could not find the @sentiance-react-native/core package, have you installed it?') 16 | } 17 | 18 | android { 19 | namespace "com.sentiance.react.bridge.eventtimeline" 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_1_8 22 | targetCompatibility JavaVersion.VERSION_1_8 23 | } 24 | } 25 | 26 | apply from: "$coreProj.projectDir/package-json-reader.gradle" 27 | apply from: "$coreProj.projectDir/sentiance-version-finder.gradle" 28 | 29 | def corePackageJson = PackageJson.of(coreProj) 30 | applyAndroidVersionsFrom(corePackageJson) 31 | def sentianceSdkVersion = getSentianceSdkVersion() 32 | 33 | dependencies { 34 | implementation(platform("com.sentiance:sdk-bom:${sentianceSdkVersion}")) 35 | api("com.sentiance:sdk-event-timeline") { transitive = true } 36 | api coreProj 37 | 38 | if (findProject(':test-common')) { 39 | testImplementation project(':test-common') 40 | } 41 | } 42 | 43 | applyReactNativeDependency() 44 | -------------------------------------------------------------------------------- /packages/event-timeline/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/event-timeline/android/src/main/java/com/sentiance/react/bridge/eventtimeline/ErrorCodes.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.eventtimeline; 2 | 3 | public class ErrorCodes { 4 | public static final String E_TRANSPORT_TAG_ERROR = "E_TRANSPORT_TAG_ERROR"; 5 | public static final String E_INVALID_FEEDBACK_TYPE = "E_INVALID_FEEDBACK_TYPE"; 6 | public static final String E_OCCUPANT_ROLE_FEEDBACK_SUBMISSION = "E_OCCUPANT_ROLE_FEEDBACK_SUBMISSION"; 7 | } 8 | -------------------------------------------------------------------------------- /packages/event-timeline/android/src/main/java/com/sentiance/react/bridge/eventtimeline/EventTimelineEmitter.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.eventtimeline; 2 | 3 | import android.content.Context; 4 | 5 | import com.sentiance.react.bridge.core.common.base.AbstractSentianceEmitter; 6 | import com.sentiance.react.bridge.eventtimeline.converters.OnDeviceTypesConverter; 7 | import com.sentiance.sdk.ondevice.api.event.Event; 8 | 9 | public class EventTimelineEmitter extends AbstractSentianceEmitter { 10 | 11 | public static final String TIMELINE_UPDATE_EVENT = "SENTIANCE_TIMELINE_UPDATE_EVENT"; 12 | private final OnDeviceTypesConverter onDeviceTypesConverter; 13 | 14 | public EventTimelineEmitter(Context context) { 15 | super(context); 16 | onDeviceTypesConverter = new OnDeviceTypesConverter(); 17 | } 18 | 19 | public void sendTimelineUpdateEvent(Event event) { 20 | sendEvent(TIMELINE_UPDATE_EVENT, onDeviceTypesConverter.convertEvent(event)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/event-timeline/android/src/main/java/com/sentiance/react/bridge/eventtimeline/SentianceEventTimelinePackage.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.eventtimeline; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | import com.sentiance.react.bridge.core.common.SentianceSubscriptionsManager; 10 | import com.sentiance.react.bridge.eventtimeline.converters.OnDeviceTypesConverter; 11 | import com.sentiance.sdk.Sentiance; 12 | import com.sentiance.sdk.eventtimeline.api.EventTimelineApi; 13 | import com.sentiance.sdk.feedback.api.FeedbackApi; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.Collections; 18 | import java.util.List; 19 | 20 | public class SentianceEventTimelinePackage implements ReactPackage { 21 | @NonNull 22 | @Override 23 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 24 | SentianceEventTimelineModule eventTimelineModule = new SentianceEventTimelineModule( 25 | reactContext, 26 | Sentiance.getInstance(reactContext), 27 | new SentianceSubscriptionsManager(), 28 | EventTimelineApi.getInstance(reactContext), 29 | new EventTimelineEmitter(reactContext), 30 | new OnDeviceTypesConverter() 31 | ); 32 | 33 | SentianceFeedbackModule feedbackModule = new SentianceFeedbackModule( 34 | reactContext, 35 | Sentiance.getInstance(reactContext), 36 | FeedbackApi.getInstance(), 37 | new SentianceSubscriptionsManager(), 38 | new OnDeviceTypesConverter() 39 | ); 40 | 41 | return Arrays.asList(eventTimelineModule, feedbackModule); 42 | } 43 | 44 | @NonNull 45 | @Override 46 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 47 | return Collections.emptyList(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/event-timeline/android/src/main/java/com/sentiance/react/bridge/eventtimeline/converters/TransportTagsConverter.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.eventtimeline.converters; 2 | 3 | import com.facebook.react.bridge.Arguments; 4 | import com.facebook.react.bridge.ReadableMap; 5 | import com.facebook.react.bridge.ReadableMapKeySetIterator; 6 | import com.facebook.react.bridge.WritableMap; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | /** 13 | * Utility converter to handle conversions from/to React Native maps and standard Java hashmaps. 14 | */ 15 | public class TransportTagsConverter { 16 | public Map convertFrom(ReadableMap tags) { 17 | Map map = new HashMap<>(); 18 | ReadableMapKeySetIterator iterator = tags.keySetIterator(); 19 | while (iterator.hasNextKey()) { 20 | String key = iterator.nextKey(); 21 | String value = tags.getString(key); 22 | 23 | if (value == null) { 24 | throw new IllegalArgumentException("Cannot get string value for key: " + key); 25 | } 26 | 27 | map.put(key, value); 28 | } 29 | return map; 30 | } 31 | 32 | public WritableMap convertFrom(Map tags) { 33 | WritableMap writableMap = Arguments.createMap(); 34 | Set keys = tags.keySet(); 35 | 36 | for (String key : keys) { 37 | writableMap.putString(key, tags.get(key)); 38 | } 39 | 40 | return writableMap; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/event-timeline/android/src/test/java/com/sentiance/react/bridge/eventtimeline/validators/TransportTagsValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.eventtimeline.validators; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.facebook.react.bridge.JavaOnlyMap; 9 | import com.sentiance.react.bridge.test.validators.BridgeValidator; 10 | 11 | import java.util.Map; 12 | 13 | public class TransportTagsValidator implements BridgeValidator> { 14 | @Override 15 | public void validate(@NonNull JavaOnlyMap expected, @NonNull Map actualTags) { 16 | Map expectedTags = expected.toHashMap(); 17 | assertEquals(expectedTags.size(), actualTags.size()); 18 | 19 | expectedTags.forEach((key, value) -> { 20 | assertTrue(actualTags.containsKey(key)); 21 | assertEquals(expectedTags.get(key), actualTags.get(key)); 22 | }); 23 | } 24 | 25 | @Override 26 | public void validate(@NonNull Map expected, @NonNull JavaOnlyMap actual) { 27 | validate(actual, expected); 28 | } 29 | } -------------------------------------------------------------------------------- /packages/event-timeline/lib/errors.d.ts: -------------------------------------------------------------------------------- 1 | export declare const E_TRANSPORT_TAG_ERROR = "E_TRANSPORT_TAG_ERROR"; 2 | export declare class TransportTaggingError extends Error { 3 | constructor(message: string); 4 | } 5 | export declare function isErrorWithCodeAndMsg(e: unknown): e is { 6 | code: string; 7 | message: string; 8 | }; 9 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/errors.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.isErrorWithCodeAndMsg = exports.TransportTaggingError = exports.E_TRANSPORT_TAG_ERROR = void 0; 4 | exports.E_TRANSPORT_TAG_ERROR = "E_TRANSPORT_TAG_ERROR"; 5 | class TransportTaggingError extends Error { 6 | constructor(message) { 7 | super(message); 8 | this.name = this.constructor.name; 9 | Object.setPrototypeOf(this, TransportTaggingError.prototype); 10 | } 11 | } 12 | exports.TransportTaggingError = TransportTaggingError; 13 | function isErrorWithCodeAndMsg(e) { 14 | return (typeof e === "object" && 15 | e !== null && 16 | typeof e.code === "string" && 17 | typeof e.message === "string"); 18 | } 19 | exports.isErrorWithCodeAndMsg = isErrorWithCodeAndMsg; 20 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/feedback/feedback-native-module.d.ts: -------------------------------------------------------------------------------- 1 | import { type NativeModule } from "@sentiance-react-native/core/lib/generated/native-module"; 2 | import { type OccupantRoleFeedback, type OccupantRoleFeedbackResult } from "./types"; 3 | export declare const NATIVE_MODULE_NAME = "SentianceFeedback"; 4 | export interface FeedbackModule extends NativeModule { 5 | submitOccupantRoleFeedback(transportId: string, occupantRoleFeedback: OccupantRoleFeedback): Promise; 6 | } 7 | export default function (): FeedbackModule; 8 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/feedback/feedback-native-module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.NATIVE_MODULE_NAME = void 0; 7 | const native_module_1 = require("@sentiance-react-native/core/lib/generated/native-module"); 8 | const require_native_module_1 = __importDefault(require("@sentiance-react-native/core/lib/generated/require-native-module")); 9 | exports.NATIVE_MODULE_NAME = "SentianceFeedback"; 10 | function default_1() { 11 | const module = (0, require_native_module_1.default)({ 12 | androidName: exports.NATIVE_MODULE_NAME, 13 | isModuleVerified: function (unverifiedModule) { 14 | return (typeof unverifiedModule.submitOccupantRoleFeedback === "function") && (0, native_module_1.isValidNativeModule)(unverifiedModule); 15 | } 16 | }); 17 | if (!module) { 18 | throw new Error("Could not locate the feedback native module."); 19 | } 20 | return module; 21 | } 22 | exports.default = default_1; 23 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/feedback/index.d.ts: -------------------------------------------------------------------------------- 1 | import { type OccupantRoleFeedback, type OccupantRoleFeedbackResult } from "./types"; 2 | export declare const submitOccupantRoleFeedback: (transportId: string, occupantRoleFeedback: OccupantRoleFeedback) => Promise; 3 | declare const _default: { 4 | submitOccupantRoleFeedback: (transportId: string, occupantRoleFeedback: "DRIVER" | "PASSENGER") => Promise; 5 | }; 6 | export default _default; 7 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/feedback/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.submitOccupantRoleFeedback = void 0; 7 | const feedback_native_module_1 = __importDefault(require("./feedback-native-module")); 8 | const types_1 = require("./types"); 9 | const nativeModule = (0, feedback_native_module_1.default)(); 10 | const submitOccupantRoleFeedback = (transportId, occupantRoleFeedback) => { 11 | if (!(0, types_1.isValidOccupantRoleFeedback)(occupantRoleFeedback)) { 12 | throw new Error("Invalid feedback type: " + occupantRoleFeedback); 13 | } 14 | return nativeModule.submitOccupantRoleFeedback(transportId, occupantRoleFeedback); 15 | }; 16 | exports.submitOccupantRoleFeedback = submitOccupantRoleFeedback; 17 | exports.default = { 18 | submitOccupantRoleFeedback: exports.submitOccupantRoleFeedback 19 | }; 20 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/feedback/types.d.ts: -------------------------------------------------------------------------------- 1 | export type OccupantRoleFeedbackResult = "ACCEPTED" | "TRANSPORT_IS_PROVISIONAL" | "TRANSPORT_TYPE_NOT_SUPPORTED" | "TRANSPORT_NOT_FOUND" | "TRANSPORT_NOT_YET_COMPLETE" | "FEEDBACK_ALREADY_PROVIDED" | "UNEXPECTED_ERROR"; 2 | /** 3 | * @internal 4 | */ 5 | declare const OCCUPANT_ROLE_FEEDBACK_VALUES: ("DRIVER" | "PASSENGER")[]; 6 | export type OccupantRoleFeedback = (typeof OCCUPANT_ROLE_FEEDBACK_VALUES)[number]; 7 | export declare function isValidOccupantRoleFeedback(value: unknown): value is OccupantRoleFeedback; 8 | export interface SentianceFeedback { 9 | submitOccupantRoleFeedback(transportId: string, occupantRoleFeedback: OccupantRoleFeedback): Promise; 10 | } 11 | export {}; 12 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/feedback/types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.isValidOccupantRoleFeedback = void 0; 4 | const types_1 = require("../timeline/types"); 5 | /** 6 | * @internal 7 | */ 8 | const OCCUPANT_ROLE_FEEDBACK_VALUES = types_1.OCCUPANT_ROLE_VALUES.filter((role) => role !== "UNAVAILABLE"); 9 | /** 10 | * @internal 11 | */ 12 | const validOccupantRoleFeedbackValues = new Set(OCCUPANT_ROLE_FEEDBACK_VALUES); 13 | function isValidOccupantRoleFeedback(value) { 14 | return validOccupantRoleFeedbackValues.has(value); 15 | } 16 | exports.isValidOccupantRoleFeedback = isValidOccupantRoleFeedback; 17 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/index.d.ts: -------------------------------------------------------------------------------- 1 | import { SentianceEventTimeline } from "./timeline/types"; 2 | export * from "./types"; 3 | export * from "./timeline"; 4 | export * from "./feedback"; 5 | declare const module: SentianceEventTimeline; 6 | export default module; 7 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __exportStar = (this && this.__exportStar) || function(m, exports) { 14 | for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); 15 | }; 16 | var __importDefault = (this && this.__importDefault) || function (mod) { 17 | return (mod && mod.__esModule) ? mod : { "default": mod }; 18 | }; 19 | Object.defineProperty(exports, "__esModule", { value: true }); 20 | const feedback_1 = __importDefault(require("./feedback")); 21 | const timeline_1 = require("./timeline"); 22 | __exportStar(require("./types"), exports); 23 | __exportStar(require("./timeline"), exports); 24 | __exportStar(require("./feedback"), exports); 25 | const module = { 26 | getTimelineUpdates: timeline_1.getTimelineUpdates, 27 | getTimelineEvents: timeline_1.getTimelineEvents, 28 | getTimelineEvent: timeline_1.getTimelineEvent, 29 | setTransportTags: timeline_1.setTransportTags, 30 | addTimelineUpdateListener: timeline_1.addTimelineUpdateListener, 31 | sentianceFeedback: feedback_1.default 32 | }; 33 | exports.default = module; 34 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/timeline/event-timeline-native-module.d.ts: -------------------------------------------------------------------------------- 1 | import { type NativeModule } from "@sentiance-react-native/core/lib/generated/native-module"; 2 | import type { Event, TransportTags } from "./types"; 3 | export declare const NATIVE_MODULE_NAME = "SentianceEventTimeline"; 4 | export interface EventTimelineModule extends NativeModule { 5 | getTimelineUpdates(afterEpochTimeMs: number, includeProvisionalEvents: boolean): Promise; 6 | getTimelineEvents(fromEpochTimeMs: number, toEpochTimeMs: number, includeProvisionalEvents: boolean): Promise; 7 | getTimelineEvent(eventId: string): Promise; 8 | setTransportTags(tags: TransportTags): Promise; 9 | } 10 | export default function (): EventTimelineModule; 11 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/timeline/event-timeline-native-module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.NATIVE_MODULE_NAME = void 0; 7 | const native_module_1 = require("@sentiance-react-native/core/lib/generated/native-module"); 8 | const require_native_module_1 = __importDefault(require("@sentiance-react-native/core/lib/generated/require-native-module")); 9 | exports.NATIVE_MODULE_NAME = "SentianceEventTimeline"; 10 | function default_1() { 11 | const module = (0, require_native_module_1.default)({ 12 | androidName: exports.NATIVE_MODULE_NAME, 13 | isModuleVerified: function (unverifiedModule) { 14 | return (typeof unverifiedModule.getTimelineUpdates === "function" && 15 | typeof unverifiedModule.getTimelineEvents === "function" && 16 | typeof unverifiedModule.getTimelineEvent === "function" && 17 | typeof unverifiedModule.setTransportTags === "function") && (0, native_module_1.isValidNativeModule)(unverifiedModule); 18 | } 19 | }); 20 | if (!module) { 21 | throw new Error("Could not locate the event timeline native module."); 22 | } 23 | return module; 24 | } 25 | exports.default = default_1; 26 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/timeline/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { Event, TransportTags } from "./types"; 2 | import { type TimelineUpdateListener } from "./timeline-event-emitter"; 3 | import { type EmitterSubscription } from "react-native"; 4 | export declare const getTimelineUpdates: (afterEpochTimeMs: number, includeProvisionalEvents?: boolean) => Promise; 5 | export declare const getTimelineEvents: (fromEpochTimeMs: number, toEpochTimeMs: number, includeProvisionalEvents?: boolean) => Promise; 6 | export declare const getTimelineEvent: (eventId: string) => Promise; 7 | export declare const setTransportTags: (tags: TransportTags) => Promise; 8 | export declare const addTimelineUpdateListener: (listener: TimelineUpdateListener, includeProvisionalEvents?: boolean) => Promise; 9 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/timeline/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __importDefault = (this && this.__importDefault) || function (mod) { 12 | return (mod && mod.__esModule) ? mod : { "default": mod }; 13 | }; 14 | Object.defineProperty(exports, "__esModule", { value: true }); 15 | exports.addTimelineUpdateListener = exports.setTransportTags = exports.getTimelineEvent = exports.getTimelineEvents = exports.getTimelineUpdates = void 0; 16 | const event_timeline_native_module_1 = __importDefault(require("./event-timeline-native-module")); 17 | const timeline_event_emitter_1 = __importDefault(require("./timeline-event-emitter")); 18 | const errors_1 = require("../errors"); 19 | const nativeModule = (0, event_timeline_native_module_1.default)(); 20 | const emitter = new timeline_event_emitter_1.default(nativeModule); 21 | const getTimelineUpdates = (afterEpochTimeMs, includeProvisionalEvents = false) => nativeModule.getTimelineUpdates(afterEpochTimeMs, includeProvisionalEvents); 22 | exports.getTimelineUpdates = getTimelineUpdates; 23 | const getTimelineEvents = (fromEpochTimeMs, toEpochTimeMs, includeProvisionalEvents = false) => nativeModule.getTimelineEvents(fromEpochTimeMs, toEpochTimeMs, includeProvisionalEvents); 24 | exports.getTimelineEvents = getTimelineEvents; 25 | const getTimelineEvent = (eventId) => nativeModule.getTimelineEvent(eventId); 26 | exports.getTimelineEvent = getTimelineEvent; 27 | const setTransportTags = (tags) => __awaiter(void 0, void 0, void 0, function* () { 28 | try { 29 | return yield nativeModule.setTransportTags(tags); 30 | } 31 | catch (e) { 32 | if ((0, errors_1.isErrorWithCodeAndMsg)(e) && e.code == errors_1.E_TRANSPORT_TAG_ERROR) { 33 | throw new errors_1.TransportTaggingError(e.message); 34 | } 35 | throw e; 36 | } 37 | }); 38 | exports.setTransportTags = setTransportTags; 39 | const addTimelineUpdateListener = (listener, includeProvisionalEvents = false) => emitter.addEventTimelineListener(listener, includeProvisionalEvents); 40 | exports.addTimelineUpdateListener = addTimelineUpdateListener; 41 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/timeline/timeline-event-emitter.d.ts: -------------------------------------------------------------------------------- 1 | import { type EventTimelineModule } from "./event-timeline-native-module"; 2 | import SentianceEventEmitter from "@sentiance-react-native/core/lib/generated/sentiance-event-emitter"; 3 | import { type EmitterSubscription } from "react-native"; 4 | import { type Event } from "./types"; 5 | export type TimelineUpdateListener = (event: Event) => void; 6 | export declare const TIMELINE_UPDATE_EVENT = "SENTIANCE_TIMELINE_UPDATE_EVENT"; 7 | export default class EventTimelineEventEmitter extends SentianceEventEmitter { 8 | constructor(nativeModule: EventTimelineModule); 9 | addEventTimelineListener(listener: TimelineUpdateListener, includeProvisionalEvents: boolean): Promise; 10 | } 11 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/timeline/timeline-event-emitter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.TIMELINE_UPDATE_EVENT = void 0; 7 | const sentiance_event_emitter_1 = __importDefault(require("@sentiance-react-native/core/lib/generated/sentiance-event-emitter")); 8 | exports.TIMELINE_UPDATE_EVENT = "SENTIANCE_TIMELINE_UPDATE_EVENT"; 9 | class EventTimelineEventEmitter extends sentiance_event_emitter_1.default { 10 | constructor(nativeModule) { 11 | super(nativeModule); 12 | } 13 | addEventTimelineListener(listener, includeProvisionalEvents) { 14 | // This instructs our native code to always register a listener that gets notified with all 15 | // sorts of events (provisional or not), so that we could then filter out the received events 16 | // and re-dispatch them to the appropriate JS callbacks. 17 | const context = { 18 | includeProvisionalEvents: true 19 | }; 20 | return this.addListener(exports.TIMELINE_UPDATE_EVENT, (event) => { 21 | if (includeProvisionalEvents || !event.isProvisional) { 22 | listener(event); 23 | } 24 | }, context); 25 | } 26 | } 27 | exports.default = EventTimelineEventEmitter; 28 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/timeline/types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.OCCUPANT_ROLE_VALUES = void 0; 4 | /** 5 | * @internal 6 | */ 7 | exports.OCCUPANT_ROLE_VALUES = [ 8 | "DRIVER", 9 | "PASSENGER", 10 | "UNAVAILABLE" 11 | ]; 12 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/types.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./feedback/types"; 2 | export * from "./timeline/types"; 3 | -------------------------------------------------------------------------------- /packages/event-timeline/lib/types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __exportStar = (this && this.__exportStar) || function(m, exports) { 14 | for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); 15 | }; 16 | Object.defineProperty(exports, "__esModule", { value: true }); 17 | __exportStar(require("./feedback/types"), exports); 18 | __exportStar(require("./timeline/types"), exports); 19 | -------------------------------------------------------------------------------- /packages/event-timeline/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sentiance-react-native/event-timeline", 3 | "version": "6.10.1", 4 | "description": "The Sentiance Event Timeline library", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "test": "jest --verbose", 9 | "lint": "npx eslint lib/index.d.ts" 10 | }, 11 | "keywords": [ 12 | "react-native", 13 | "event-timeline", 14 | "sentiance" 15 | ], 16 | "peerDependencies": { 17 | "@sentiance-react-native/core": "6.10.1" 18 | }, 19 | "author": "", 20 | "license": "", 21 | "homepage": "https://github.com/sentiance/react-native-sentiance/tree/main/packages/event-timeline", 22 | "repository": "github:sentiance/react-native-sentiance", 23 | "publishConfig": { 24 | "access": "public" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/event-timeline/src/errors.ts: -------------------------------------------------------------------------------- 1 | export const E_TRANSPORT_TAG_ERROR = "E_TRANSPORT_TAG_ERROR"; 2 | 3 | export class TransportTaggingError extends Error { 4 | constructor(message: string) { 5 | super(message); 6 | this.name = this.constructor.name; 7 | Object.setPrototypeOf(this, TransportTaggingError.prototype); 8 | } 9 | } 10 | 11 | export function isErrorWithCodeAndMsg(e: unknown): e is { code: string; message: string } { 12 | return ( 13 | typeof e === "object" && 14 | e !== null && 15 | typeof (e as { code?: unknown }).code === "string" && 16 | typeof (e as { message?: unknown }).message === "string" 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/event-timeline/src/feedback/feedback-native-module.ts: -------------------------------------------------------------------------------- 1 | import { isValidNativeModule, type NativeModule } from "@sentiance-react-native/core/lib/generated/native-module"; 2 | import requireNativeModule from "@sentiance-react-native/core/lib/generated/require-native-module"; 3 | import { type OccupantRoleFeedback, type OccupantRoleFeedbackResult } from "./types"; 4 | 5 | export const NATIVE_MODULE_NAME = "SentianceFeedback"; 6 | 7 | export interface FeedbackModule extends NativeModule { 8 | submitOccupantRoleFeedback( 9 | transportId: string, 10 | occupantRoleFeedback: OccupantRoleFeedback 11 | ): Promise; 12 | } 13 | 14 | export default function(): FeedbackModule { 15 | const module = requireNativeModule({ 16 | androidName: NATIVE_MODULE_NAME, 17 | isModuleVerified: function(unverifiedModule): unverifiedModule is FeedbackModule { 18 | return ( 19 | typeof unverifiedModule.submitOccupantRoleFeedback === "function" 20 | ) && isValidNativeModule(unverifiedModule); 21 | } 22 | }); 23 | 24 | if (!module) { 25 | throw new Error("Could not locate the feedback native module."); 26 | } 27 | 28 | return module; 29 | } 30 | -------------------------------------------------------------------------------- /packages/event-timeline/src/feedback/index.ts: -------------------------------------------------------------------------------- 1 | import feedbackNativeModule from "./feedback-native-module"; 2 | import { isValidOccupantRoleFeedback, type OccupantRoleFeedback, type OccupantRoleFeedbackResult } from "./types"; 3 | 4 | const nativeModule = feedbackNativeModule(); 5 | 6 | export const submitOccupantRoleFeedback = ( 7 | transportId: string, 8 | occupantRoleFeedback: OccupantRoleFeedback 9 | ): Promise => { 10 | if (!isValidOccupantRoleFeedback(occupantRoleFeedback)) { 11 | throw new Error("Invalid feedback type: " + occupantRoleFeedback); 12 | } 13 | return nativeModule.submitOccupantRoleFeedback(transportId, occupantRoleFeedback) 14 | }; 15 | 16 | export default { 17 | submitOccupantRoleFeedback 18 | }; 19 | -------------------------------------------------------------------------------- /packages/event-timeline/src/feedback/types.ts: -------------------------------------------------------------------------------- 1 | import { OCCUPANT_ROLE_VALUES, type OccupantRole } from "../timeline/types"; 2 | 3 | export type OccupantRoleFeedbackResult = 4 | | "ACCEPTED" 5 | | "TRANSPORT_IS_PROVISIONAL" 6 | | "TRANSPORT_TYPE_NOT_SUPPORTED" 7 | | "TRANSPORT_NOT_FOUND" 8 | | "TRANSPORT_NOT_YET_COMPLETE" 9 | | "FEEDBACK_ALREADY_PROVIDED" 10 | | "UNEXPECTED_ERROR"; 11 | 12 | /** 13 | * @internal 14 | */ 15 | const OCCUPANT_ROLE_FEEDBACK_VALUES = OCCUPANT_ROLE_VALUES.filter( 16 | (role): role is Exclude => role !== "UNAVAILABLE" 17 | ); 18 | /** 19 | * @internal 20 | */ 21 | const validOccupantRoleFeedbackValues = new Set(OCCUPANT_ROLE_FEEDBACK_VALUES); 22 | export type OccupantRoleFeedback = (typeof OCCUPANT_ROLE_FEEDBACK_VALUES)[number]; 23 | 24 | export function isValidOccupantRoleFeedback( 25 | value: unknown 26 | ): value is OccupantRoleFeedback { 27 | return validOccupantRoleFeedbackValues.has(value as OccupantRoleFeedback); 28 | } 29 | 30 | export interface SentianceFeedback { 31 | submitOccupantRoleFeedback(transportId: string, occupantRoleFeedback: OccupantRoleFeedback): Promise; 32 | } 33 | -------------------------------------------------------------------------------- /packages/event-timeline/src/index.ts: -------------------------------------------------------------------------------- 1 | import { SentianceEventTimeline } from "./timeline/types"; 2 | import feedbackApi from "./feedback"; 3 | import { 4 | addTimelineUpdateListener, 5 | getTimelineEvent, 6 | getTimelineEvents, 7 | getTimelineUpdates, 8 | setTransportTags 9 | } from "./timeline"; 10 | 11 | export * from "./types"; 12 | export * from "./timeline"; 13 | export * from "./feedback"; 14 | 15 | const module: SentianceEventTimeline = { 16 | getTimelineUpdates, 17 | getTimelineEvents, 18 | getTimelineEvent, 19 | setTransportTags, 20 | addTimelineUpdateListener, 21 | sentianceFeedback: feedbackApi 22 | }; 23 | 24 | export default module; 25 | -------------------------------------------------------------------------------- /packages/event-timeline/src/timeline/event-timeline-native-module.ts: -------------------------------------------------------------------------------- 1 | import { isValidNativeModule, type NativeModule } from "@sentiance-react-native/core/lib/generated/native-module"; 2 | import requireNativeModule from "@sentiance-react-native/core/lib/generated/require-native-module"; 3 | import type { Event, TransportTags } from "./types"; 4 | 5 | export const NATIVE_MODULE_NAME = "SentianceEventTimeline"; 6 | 7 | export interface EventTimelineModule extends NativeModule { 8 | getTimelineUpdates(afterEpochTimeMs: number, includeProvisionalEvents: boolean): Promise; 9 | 10 | getTimelineEvents(fromEpochTimeMs: number, toEpochTimeMs: number, includeProvisionalEvents: boolean): Promise; 11 | 12 | getTimelineEvent(eventId: string): Promise; 13 | 14 | setTransportTags(tags: TransportTags): Promise; 15 | } 16 | 17 | export default function(): EventTimelineModule { 18 | const module = requireNativeModule({ 19 | androidName: NATIVE_MODULE_NAME, 20 | isModuleVerified: function(unverifiedModule): unverifiedModule is EventTimelineModule { 21 | return ( 22 | typeof unverifiedModule.getTimelineUpdates === "function" && 23 | typeof unverifiedModule.getTimelineEvents === "function" && 24 | typeof unverifiedModule.getTimelineEvent === "function" && 25 | typeof unverifiedModule.setTransportTags === "function" 26 | ) && isValidNativeModule(unverifiedModule); 27 | } 28 | }); 29 | 30 | if (!module) { 31 | throw new Error("Could not locate the event timeline native module."); 32 | } 33 | 34 | return module; 35 | } 36 | -------------------------------------------------------------------------------- /packages/event-timeline/src/timeline/index.ts: -------------------------------------------------------------------------------- 1 | import eventTimelineNativeModule from "./event-timeline-native-module"; 2 | import type { Event, TransportTags } from "./types"; 3 | import EventTimelineEventEmitter, { type TimelineUpdateListener } from "./timeline-event-emitter"; 4 | import { type EmitterSubscription } from "react-native"; 5 | import { E_TRANSPORT_TAG_ERROR, isErrorWithCodeAndMsg, TransportTaggingError } from "../errors"; 6 | 7 | const nativeModule = eventTimelineNativeModule(); 8 | const emitter = new EventTimelineEventEmitter(nativeModule); 9 | 10 | export const getTimelineUpdates = ( 11 | afterEpochTimeMs: number, 12 | includeProvisionalEvents = false 13 | ): Promise => nativeModule.getTimelineUpdates(afterEpochTimeMs, includeProvisionalEvents); 14 | 15 | export const getTimelineEvents = ( 16 | fromEpochTimeMs: number, 17 | toEpochTimeMs: number, 18 | includeProvisionalEvents = false 19 | ): Promise => nativeModule.getTimelineEvents(fromEpochTimeMs, toEpochTimeMs, includeProvisionalEvents); 20 | 21 | export const getTimelineEvent = (eventId: string): Promise => nativeModule.getTimelineEvent(eventId); 22 | 23 | export const setTransportTags = async (tags: TransportTags): Promise => { 24 | try { 25 | return await nativeModule.setTransportTags(tags); 26 | } catch (e: unknown) { 27 | if (isErrorWithCodeAndMsg(e) && e.code == E_TRANSPORT_TAG_ERROR) { 28 | throw new TransportTaggingError(e.message); 29 | } 30 | throw e; 31 | } 32 | }; 33 | 34 | export const addTimelineUpdateListener = ( 35 | listener: TimelineUpdateListener, 36 | includeProvisionalEvents: boolean = false 37 | ): Promise => emitter.addEventTimelineListener(listener, includeProvisionalEvents); 38 | -------------------------------------------------------------------------------- /packages/event-timeline/src/timeline/timeline-event-emitter.ts: -------------------------------------------------------------------------------- 1 | import { type EventTimelineModule } from "./event-timeline-native-module"; 2 | import SentianceEventEmitter from "@sentiance-react-native/core/lib/generated/sentiance-event-emitter"; 3 | import { type EmitterSubscription } from "react-native"; 4 | import { type Event } from "./types"; 5 | 6 | export type TimelineUpdateListener = (event: Event) => void; 7 | export const TIMELINE_UPDATE_EVENT = "SENTIANCE_TIMELINE_UPDATE_EVENT"; 8 | 9 | export default class EventTimelineEventEmitter extends SentianceEventEmitter { 10 | 11 | constructor(nativeModule: EventTimelineModule) { 12 | super(nativeModule); 13 | } 14 | 15 | addEventTimelineListener(listener: TimelineUpdateListener, includeProvisionalEvents: boolean): Promise { 16 | // This instructs our native code to always register a listener that gets notified with all 17 | // sorts of events (provisional or not), so that we could then filter out the received events 18 | // and re-dispatch them to the appropriate JS callbacks. 19 | const context = { 20 | includeProvisionalEvents: true 21 | }; 22 | 23 | return this.addListener( 24 | TIMELINE_UPDATE_EVENT, 25 | (event: Event) => { 26 | if (includeProvisionalEvents || !event.isProvisional) { 27 | listener(event); 28 | } 29 | }, 30 | context 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/event-timeline/src/types.ts: -------------------------------------------------------------------------------- 1 | export * from "./feedback/types"; 2 | export * from "./timeline/types"; 3 | -------------------------------------------------------------------------------- /packages/event-timeline/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "rootDir": "./src", 6 | "outDir": "./lib", 7 | "declarationDir": "./lib", 8 | "paths": { 9 | "react-native": [ 10 | "../../node_modules/react-native" 11 | ] 12 | } 13 | }, 14 | "references": [ 15 | { 16 | "path": "../core" 17 | } 18 | ], 19 | "include": ["src/**/*"], 20 | "exclude": ["node_modules", "lib"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/gradle-scripts/common-test-config.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | afterEvaluate { project -> 3 | if (project.plugins.hasPlugin('com.android.library')) { 4 | android { 5 | testOptions { 6 | unitTests.all { 7 | // Add JVM options to open specific packages 8 | allJvmArgs += [ 9 | '--add-opens=java.base/java.lang=ALL-UNNAMED', 10 | '--add-opens=java.base/java.util=ALL-UNNAMED', 11 | '--add-opens=java.base/java.text=ALL-UNNAMED', 12 | '--add-opens=java.base/java.net=ALL-UNNAMED', 13 | '--add-opens=java.base/java.io=ALL-UNNAMED', 14 | '--add-opens=java.base/java.security=ALL-UNNAMED', 15 | '--add-opens=java.base/java.lang.reflect=ALL-UNNAMED', 16 | '--add-opens=java.base/java.util.stream=ALL-UNNAMED', 17 | '--add-opens=java.desktop/java.awt.font=ALL-UNNAMED', 18 | ] 19 | } 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | android.enableJetifier=true 3 | android.jetifier.ignorelist=bcprov-jdk15on 4 | -------------------------------------------------------------------------------- /packages/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/packages/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /packages/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /packages/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /packages/legacy/.npmignore: -------------------------------------------------------------------------------- 1 | __tests__ 2 | /jest 3 | android/src/test 4 | -------------------------------------------------------------------------------- /packages/legacy/README.md: -------------------------------------------------------------------------------- 1 | # Sentiance Legacy module for React Native 2 | 3 | ## Demo Application 4 | 5 | https://github.com/sentiance/sample-apps-react-native 6 | 7 | ## Usage 8 | 9 | To use the legacy SDK module, please visit the corresponding [API reference page.](https://docs.sentiance.com/sdk/api-reference/react-native/legacy) 10 | -------------------------------------------------------------------------------- /packages/legacy/android/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .idea 3 | .gradle 4 | -------------------------------------------------------------------------------- /packages/legacy/android/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.library" 3 | } 4 | 5 | def coreProj 6 | if (findProject(':core')) { 7 | coreProj = project(':core') 8 | } else if (findProject(':sentiance-react-native_core')) { 9 | // Starting from RN 0.61, the @ sign is stripped from project names 10 | coreProj = project(':sentiance-react-native_core') 11 | } else if (findProject(':@sentiance-react-native_core')) { 12 | // On RN 0.60, the @ sign is not stripped from project names 13 | coreProj = project(':@sentiance-react-native_core') 14 | } else { 15 | throw new GradleException('Could not find the @sentiance-react-native/core package, have you installed it?') 16 | } 17 | 18 | def crashDetectionProj 19 | if (findProject(':crash-detection')) { 20 | crashDetectionProj = project(':crash-detection') 21 | } else if (findProject(':sentiance-react-native_crash-detection')) { 22 | // Starting from RN 0.61, the @ sign is stripped from project names 23 | crashDetectionProj = project(':sentiance-react-native_crash-detection') 24 | } else if (findProject(':@sentiance-react-native_crash-detection')) { 25 | // On RN 0.60, the @ sign is not stripped from project names 26 | crashDetectionProj = project(':@sentiance-react-native_crash-detection') 27 | } else { 28 | throw new GradleException('Could not find the @sentiance-react-native/crash-detection package, have you installed it?') 29 | } 30 | 31 | android { 32 | namespace "com.sentiance.react.bridge.legacy" 33 | 34 | compileOptions { 35 | sourceCompatibility JavaVersion.VERSION_1_8 36 | targetCompatibility JavaVersion.VERSION_1_8 37 | } 38 | } 39 | 40 | apply from: "$coreProj.projectDir/package-json-reader.gradle" 41 | 42 | def corePackageJson = PackageJson.of(coreProj) 43 | applyAndroidVersionsFrom(corePackageJson) 44 | 45 | dependencies { 46 | implementation crashDetectionProj 47 | } 48 | 49 | applyReactNativeDependency() 50 | -------------------------------------------------------------------------------- /packages/legacy/android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | android.enableJetifier=true 3 | -------------------------------------------------------------------------------- /packages/legacy/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/packages/legacy/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /packages/legacy/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /packages/legacy/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /packages/legacy/android/src/main/java/com/sentiance/react/bridge/legacy/LegacySentiancePackage.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.legacy; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | public class LegacySentiancePackage implements ReactPackage { 15 | 16 | @NonNull 17 | @Override 18 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 19 | List modules = new ArrayList<>(); 20 | LegacySentianceModule legacySentianceModule = new LegacySentianceModule(reactContext); 21 | modules.add(legacySentianceModule); 22 | return modules; 23 | } 24 | 25 | @Override 26 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 27 | return Collections.emptyList(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/legacy/android/src/main/java/com/sentiance/react/bridge/legacy/StartFinishedHandlerCreator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.legacy; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.bridge.Promise; 6 | import com.sentiance.react.bridge.core.SentianceConverter; 7 | import com.sentiance.sdk.OnStartFinishedHandler; 8 | import com.sentiance.sdk.SdkStatus; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class StartFinishedHandlerCreator { 14 | 15 | private final List startFinishedHandlers; 16 | private final SentianceConverter converter; 17 | 18 | StartFinishedHandlerCreator() { 19 | this.startFinishedHandlers = new ArrayList<>(); 20 | converter = new SentianceConverter(); 21 | } 22 | 23 | OnStartFinishedHandler createNewStartFinishedHandler(final Promise promise) { 24 | final OnStartFinishedHandler startFinishedHandler = new OnStartFinishedHandler() { 25 | @Override 26 | public void onStartFinished(@NonNull SdkStatus sdkStatus) { 27 | promise.resolve(converter.convertSdkStatus(sdkStatus)); 28 | removeStartFinishHandler(this); 29 | } 30 | }; 31 | // hold strong reference 32 | addStartFinishHandler(startFinishedHandler); 33 | return startFinishedHandler; 34 | } 35 | 36 | private synchronized void addStartFinishHandler(OnStartFinishedHandler handler) { 37 | startFinishedHandlers.add(handler); 38 | } 39 | 40 | private synchronized void removeStartFinishHandler(OnStartFinishedHandler handler) { 41 | startFinishedHandlers.remove(handler); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/legacy/lib/legacy/index.js: -------------------------------------------------------------------------------- 1 | const {NativeModules, Platform} = require('react-native'); 2 | const {varToString} = require("@sentiance-react-native/core/lib/generated/utils"); 3 | 4 | const {RNSentiance, SentianceCore} = NativeModules; 5 | 6 | let legacyModule = {}; 7 | if (Platform.OS === 'ios') { 8 | if (!SentianceCore) { 9 | const nativeModuleName = varToString({SentianceCore}); 10 | console.error(`Could not locate the native ${nativeModuleName} module. 11 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 12 | } else { 13 | legacyModule = SentianceCore 14 | } 15 | } else { 16 | if (!RNSentiance) { 17 | const nativeModuleName = varToString({RNSentiance}); 18 | console.error(`Could not locate the native ${nativeModuleName} module. 19 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 20 | } else { 21 | legacyModule = RNSentiance 22 | } 23 | } 24 | 25 | module.exports = legacyModule; 26 | -------------------------------------------------------------------------------- /packages/legacy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sentiance-react-native/legacy", 3 | "version": "6.10.1", 4 | "description": "The Sentiance Legacy library", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "lint": "npx eslint lib/index.d.ts" 10 | }, 11 | "keywords": [ 12 | "react-native", 13 | "legacy", 14 | "sentiance" 15 | ], 16 | "peerDependencies": { 17 | "@sentiance-react-native/crash-detection": "6.10.1" 18 | }, 19 | "author": "", 20 | "license": "", 21 | "homepage": "https://github.com/sentiance/react-native-sentiance/tree/main/packages/legacy", 22 | "repository": "github:sentiance/react-native-sentiance", 23 | "publishConfig": { 24 | "access": "public" 25 | } 26 | } -------------------------------------------------------------------------------- /packages/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | 9 | rootProject.name = 'RNSentiance' 10 | 11 | dependencyResolutionManagement { 12 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 13 | repositories { 14 | google() 15 | mavenLocal() 16 | maven { 17 | url("$rootDir/../node_modules/react-native/android") 18 | } 19 | mavenCentral { 20 | // We don't want to fetch react-native from Maven Central as there are 21 | // older versions over there. 22 | content { 23 | excludeGroup "com.facebook.react" 24 | } 25 | } 26 | maven { 27 | url "https://repository.sentiance.com" 28 | } 29 | } 30 | } 31 | 32 | [ 33 | 'event-timeline', 34 | 'test-common', 35 | 'core', 36 | 'crash-detection', 37 | 'driving-insights', 38 | 'user-context', 39 | 'legacy', 40 | 'smart-geofences', 41 | ].each { projectName -> 42 | def module = ":$projectName" 43 | include(module) 44 | project(module).projectDir = new File(rootProject.projectDir, "./$projectName/android") 45 | } 46 | -------------------------------------------------------------------------------- /packages/smart-geofences/README.md: -------------------------------------------------------------------------------- 1 | # Sentiance Smart Geofences module for React Native 2 | 3 | ## Demo Application 4 | 5 | https://github.com/sentiance/sample-apps-react-native 6 | 7 | ## Usage 8 | 9 | To use the Smart Geofences SDK module, please visit the corresponding [API reference page.](https://docs.sentiance.com/sdk/api-reference/react-native/smart-geofences) 10 | -------------------------------------------------------------------------------- /packages/smart-geofences/android/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /packages/smart-geofences/android/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.library" 3 | } 4 | 5 | def coreProj 6 | if (findProject(':core')) { 7 | coreProj = project(':core') 8 | } else if (findProject(':sentiance-react-native_core')) { 9 | // Starting from RN 0.61, the @ sign is stripped from project names 10 | coreProj = project(':sentiance-react-native_core') 11 | } else if (findProject(':@sentiance-react-native_core')) { 12 | // On RN 0.60, the @ sign is not stripped from project names 13 | coreProj = project(':@sentiance-react-native_core') 14 | } else { 15 | throw new GradleException('Could not find the @sentiance-react-native/core package, have you installed it?') 16 | } 17 | 18 | android { 19 | namespace "com.sentiance.react.bridge.smartgeofences" 20 | 21 | compileOptions { 22 | sourceCompatibility JavaVersion.VERSION_1_8 23 | targetCompatibility JavaVersion.VERSION_1_8 24 | } 25 | } 26 | 27 | apply from: "$coreProj.projectDir/package-json-reader.gradle" 28 | apply from: "$coreProj.projectDir/sentiance-version-finder.gradle" 29 | 30 | def corePackageJson = PackageJson.of(coreProj) 31 | applyAndroidVersionsFrom(corePackageJson) 32 | def sentianceSdkVersion = getSentianceSdkVersion() 33 | 34 | dependencies { 35 | implementation(platform("com.sentiance:sdk-bom:${sentianceSdkVersion}")) 36 | api("com.sentiance:sdk-smart-geofences") { transitive = true } 37 | api coreProj 38 | 39 | if (findProject(':test-common')) { 40 | testImplementation project(':test-common') 41 | } 42 | } 43 | 44 | applyReactNativeDependency() 45 | -------------------------------------------------------------------------------- /packages/smart-geofences/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/smart-geofences/android/src/main/java/com/sentiance/react/bridge/smartgeofences/SmartGeofenceEmitter.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.smartgeofences; 2 | 3 | import android.content.Context; 4 | 5 | import com.sentiance.react.bridge.core.common.base.AbstractSentianceEmitter; 6 | import com.sentiance.react.bridge.smartgeofences.converters.SmartGeofencesConverter; 7 | import com.sentiance.sdk.smartgeofences.api.SmartGeofenceEvent; 8 | 9 | public class SmartGeofenceEmitter extends AbstractSentianceEmitter { 10 | public static final String SMART_GEOFENCE_EVENT = "SENTIANCE_SMART_GEOFENCE_EVENT"; 11 | private final SmartGeofencesConverter smartGeofencesConverter; 12 | 13 | protected SmartGeofenceEmitter(Context context, SmartGeofencesConverter smartGeofencesConverter) { 14 | super(context); 15 | this.smartGeofencesConverter = smartGeofencesConverter; 16 | } 17 | 18 | public void sendSmartGeofencesEvent(SmartGeofenceEvent event) { 19 | sendEvent(SMART_GEOFENCE_EVENT, smartGeofencesConverter.convertSmartGeofenceEvent(event)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/smart-geofences/android/src/main/java/com/sentiance/react/bridge/smartgeofences/SmartGeofencesPackage.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.smartgeofences; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | import com.sentiance.react.bridge.core.SentianceConverter; 10 | import com.sentiance.react.bridge.core.common.SentianceSubscriptionsManager; 11 | import com.sentiance.react.bridge.smartgeofences.converters.SmartGeofencesConverter; 12 | import com.sentiance.sdk.Sentiance; 13 | import com.sentiance.sdk.smartgeofences.api.SmartGeofenceApi; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Collections; 17 | import java.util.List; 18 | 19 | public class SmartGeofencesPackage implements ReactPackage { 20 | @NonNull 21 | @Override 22 | public List createNativeModules(@NonNull ReactApplicationContext reactApplicationContext) { 23 | List modules = new ArrayList<>(); 24 | SmartGeofencesConverter smartGeofencesConverter = new SmartGeofencesConverter(new SentianceConverter()); 25 | 26 | SmartGeofencesModule eventTimelineModule = new SmartGeofencesModule( 27 | reactApplicationContext, 28 | Sentiance.getInstance(reactApplicationContext), 29 | new SentianceSubscriptionsManager(), 30 | SmartGeofenceApi.getInstance(reactApplicationContext), 31 | new SmartGeofenceEmitter(reactApplicationContext, smartGeofencesConverter), 32 | smartGeofencesConverter 33 | ); 34 | modules.add(eventTimelineModule); 35 | return modules; 36 | } 37 | 38 | @NonNull 39 | @Override 40 | public List createViewManagers(@NonNull ReactApplicationContext reactApplicationContext) { 41 | return Collections.emptyList(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/smart-geofences/android/src/main/java/com/sentiance/react/bridge/smartgeofences/utils/ErrorCodes.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.smartgeofences.utils; 2 | 3 | public class ErrorCodes { 4 | public static final String E_SMART_GEOFENCES_REFRESH_ERROR = "E_SDK_SMART_GEOFENCES_REFRESH_ERROR"; 5 | } -------------------------------------------------------------------------------- /packages/smart-geofences/android/src/test/java/com/sentiance/react/bridge/smartgeofences/util/validators/SmartGeofenceBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.smartgeofences.util.validators; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.facebook.react.bridge.JavaOnlyMap; 8 | import com.sentiance.react.bridge.core.SentianceConverter; 9 | import com.sentiance.react.bridge.smartgeofences.converters.SmartGeofencesConverter; 10 | import com.sentiance.react.bridge.test.validators.BridgeValidator; 11 | import com.sentiance.sdk.smartgeofences.api.SmartGeofence; 12 | 13 | public class SmartGeofenceBridgeValidator implements BridgeValidator { 14 | @Override 15 | public void validate(@NonNull SmartGeofence expected, @NonNull JavaOnlyMap actual) { 16 | assertEquals(expected.getSentianceId(), actual.getString(SmartGeofencesConverter.JS_KEY_SENTIANCE_ID)); 17 | assertEquals(expected.getLatitude(), actual.getDouble(SentianceConverter.JS_KEY_LATITUDE), 0.000001); 18 | assertEquals(expected.getLongitude(), actual.getDouble(SentianceConverter.JS_KEY_LONGITUDE), 0.000001); 19 | assertEquals(expected.getRadius(), actual.getInt(SmartGeofencesConverter.JS_KEY_RADIUS)); 20 | assertEquals(expected.getExternalId(), actual.getString(SmartGeofencesConverter.JS_KEY_EXTERNAL_ID)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/smart-geofences/android/src/test/java/com/sentiance/react/bridge/smartgeofences/util/validators/SmartGeofenceEventBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.smartgeofences.util.validators; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.facebook.react.bridge.JavaOnlyMap; 9 | import com.facebook.react.bridge.ReadableArray; 10 | import com.facebook.react.bridge.ReadableMap; 11 | import com.sentiance.react.bridge.smartgeofences.converters.SmartGeofencesConverter; 12 | import com.sentiance.react.bridge.test.validators.BridgeValidator; 13 | import com.sentiance.react.bridge.test.validators.LocationBridgeValidator; 14 | import com.sentiance.sdk.smartgeofences.api.SmartGeofence; 15 | import com.sentiance.sdk.smartgeofences.api.SmartGeofenceEvent; 16 | 17 | import java.util.List; 18 | 19 | public class SmartGeofenceEventBridgeValidator implements BridgeValidator { 20 | 21 | private final SmartGeofenceBridgeValidator smartGeofenceBridgeValidator; 22 | private final LocationBridgeValidator locationBridgeValidator; 23 | 24 | public SmartGeofenceEventBridgeValidator() { 25 | smartGeofenceBridgeValidator = new SmartGeofenceBridgeValidator(); 26 | locationBridgeValidator = new LocationBridgeValidator(); 27 | } 28 | 29 | @Override 30 | public void validate(@NonNull SmartGeofenceEvent expected, @NonNull JavaOnlyMap actual) { 31 | assertTrue(actual.hasKey(SmartGeofencesConverter.JS_KEY_GEOFENCES)); 32 | validateSmartGeofences(expected.getGeofences(), actual.getArray(SmartGeofencesConverter.JS_KEY_GEOFENCES)); 33 | 34 | assertEquals(expected.getEventType().name(), actual.getString(SmartGeofencesConverter.JS_KEY_EVENT_TYPE)); 35 | 36 | locationBridgeValidator.validate(expected.getTriggeringLocation(), 37 | (JavaOnlyMap) actual.getMap(SmartGeofencesConverter.JS_KEY_TRIGGERING_LOCATION)); 38 | } 39 | 40 | private void validateSmartGeofences(List expectedGeofences, ReadableArray actualGeofences) { 41 | for (int i = 0; i < expectedGeofences.size(); i++) { 42 | SmartGeofence expectedGeofence = expectedGeofences.get(i); 43 | ReadableMap actualGeofence = actualGeofences.getMap(i); 44 | 45 | smartGeofenceBridgeValidator.validate(expectedGeofence, (JavaOnlyMap) actualGeofence); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/smart-geofences/android/src/test/java/com/sentiance/react/bridge/smartgeofences/util/validators/SmartGeofenceRefreshErrorBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.smartgeofences.util.validators; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.facebook.react.bridge.JavaOnlyMap; 9 | import com.sentiance.react.bridge.smartgeofences.converters.SmartGeofencesConverter; 10 | import com.sentiance.react.bridge.test.validators.BridgeValidator; 11 | import com.sentiance.sdk.smartgeofences.api.SmartGeofencesRefreshError; 12 | 13 | public class SmartGeofenceRefreshErrorBridgeValidator implements BridgeValidator { 14 | 15 | @Override 16 | public void validate(@NonNull SmartGeofencesRefreshError expected, @NonNull JavaOnlyMap actual) { 17 | assertEquals(expected.getReason().name(), actual.getString(SmartGeofencesConverter.JS_KEY_REASON)); 18 | 19 | String details = expected.getDetails(); 20 | if (details == null) { 21 | assertFalse(actual.hasKey(SmartGeofencesConverter.JS_KEY_DETAILS)); 22 | } else { 23 | assertEquals(details, actual.getString(SmartGeofencesConverter.JS_KEY_DETAILS)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/smart-geofences/jest/mockNativeModule.js: -------------------------------------------------------------------------------- 1 | import {mockNativeModule} from "../../../jest/mockNativeModules"; 2 | 3 | export function mockNativeSmartGeofencesModule(platform, module) { 4 | return mockNativeModule( 5 | platform, 6 | { 7 | androidName: 'SentianceSmartGeofences', 8 | iosName: 'SentianceCore' 9 | }, 10 | module); 11 | } 12 | -------------------------------------------------------------------------------- /packages/smart-geofences/lib/index.js: -------------------------------------------------------------------------------- 1 | const { NativeModules, Platform } = require("react-native"); 2 | const { varToString } = require("@sentiance-react-native/core/lib/generated/utils"); 3 | const SentianceEventEmitter = require("@sentiance-react-native/core/lib/generated/sentiance-event-emitter").default; 4 | const { SentianceSmartGeofences, SentianceCore } = NativeModules; 5 | 6 | const SMART_GEOFENCE_EVENT = "SENTIANCE_SMART_GEOFENCE_EVENT"; 7 | 8 | let didLocateNativeModule = true; 9 | let smartGeofencesModule = {}; 10 | if (Platform.OS === "android") { 11 | if (!SentianceSmartGeofences) { 12 | didLocateNativeModule = false; 13 | const nativeModuleName = varToString({ SentianceSmartGeofences }); 14 | console.error(`Could not locate the native ${nativeModuleName} module. 15 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 16 | } else { 17 | smartGeofencesModule = SentianceSmartGeofences; 18 | } 19 | } else { 20 | if (!SentianceCore) { 21 | didLocateNativeModule = false; 22 | const nativeModuleName = varToString({ SentianceCore }); 23 | console.error(`Could not locate the native ${nativeModuleName} module. 24 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 25 | } else { 26 | smartGeofencesModule = SentianceCore; 27 | } 28 | } 29 | 30 | if (didLocateNativeModule) { 31 | const emitter = new SentianceEventEmitter(smartGeofencesModule); 32 | 33 | smartGeofencesModule._addSmartGeofenceEventListener = (onSmartGeofenceEvent) => 34 | emitter.addListener(SMART_GEOFENCE_EVENT, onSmartGeofenceEvent); 35 | } 36 | 37 | const refreshGeofences = () => smartGeofencesModule.refreshGeofences(); 38 | 39 | const getDetectionMode = () => smartGeofencesModule.getDetectionMode(); 40 | 41 | const addSmartGeofenceEventListener = smartGeofencesModule._addSmartGeofenceEventListener; 42 | 43 | module.exports = { 44 | refreshGeofences, 45 | getDetectionMode, 46 | addSmartGeofenceEventListener 47 | }; 48 | module.exports.events = { 49 | SMART_GEOFENCE_EVENT 50 | }; 51 | -------------------------------------------------------------------------------- /packages/smart-geofences/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sentiance-react-native/smart-geofences", 3 | "version": "6.10.1", 4 | "description": "New module description goes here", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "test": "jest --verbose", 9 | "lint": "npx eslint lib/index.d.ts" 10 | }, 11 | "keywords": [ 12 | "react-native", 13 | "smart-geofences", 14 | "sentiance" 15 | ], 16 | "peerDependencies": { 17 | "@sentiance-react-native/core": "6.10.1" 18 | }, 19 | "homepage": "https://github.com/sentiance/react-native-sentiance/packages/smart-geofences#readme", 20 | "repository": "github:sentiance/react-native-sentiance", 21 | "publishConfig": { 22 | "access": "public" 23 | } 24 | } -------------------------------------------------------------------------------- /packages/test-common/android/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.library" 3 | } 4 | 5 | def coreProj 6 | if (findProject(':core')) { 7 | coreProj = project(':core') 8 | } else { 9 | throw new GradleException('Could not find the @sentiance-react-native/core package, have you installed it?') 10 | } 11 | 12 | def eventTimelineProj 13 | if (findProject(':event-timeline')) { 14 | eventTimelineProj = project(':event-timeline') 15 | } else { 16 | throw new GradleException('Could not find the @sentiance-react-native/event-timeline package, have you installed it?') 17 | } 18 | 19 | android { 20 | namespace "com.sentiance.react.bridge.test" 21 | 22 | compileOptions { 23 | sourceCompatibility JavaVersion.VERSION_1_8 24 | targetCompatibility JavaVersion.VERSION_1_8 25 | } 26 | } 27 | 28 | apply from: "$coreProj.projectDir/package-json-reader.gradle" 29 | apply from: "$coreProj.projectDir/sentiance-version-finder.gradle" 30 | 31 | def corePackageJson = PackageJson.of(coreProj) 32 | applyAndroidVersionsFrom(corePackageJson) 33 | def sentianceSdkVersion = getSentianceSdkVersion() 34 | 35 | dependencies { 36 | implementation(platform("com.sentiance:sdk-bom:${sentianceSdkVersion}")) 37 | implementation coreProj 38 | implementation eventTimelineProj 39 | 40 | api "junit:junit:4.13.2" 41 | api 'org.robolectric:robolectric:4.9' 42 | api "org.powermock:powermock-module-junit4:1.6.6" 43 | api "org.powermock:powermock-module-junit4-rule:1.6.6" 44 | api "org.powermock:powermock-api-mockito:1.6.6" 45 | api "org.powermock:powermock-classloading-xstream:1.6.6" 46 | } 47 | 48 | applyReactNativeDependency() 49 | -------------------------------------------------------------------------------- /packages/test-common/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/test-common/android/src/main/java/com/sentiance/react/bridge/test/ReactNativeModuleTest.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.test; 2 | 3 | import com.facebook.react.bridge.Promise; 4 | import com.facebook.react.bridge.ReactApplicationContext; 5 | import com.facebook.react.bridge.WritableArray; 6 | import com.facebook.react.bridge.WritableMap; 7 | import com.sentiance.react.bridge.core.common.SentianceSubscriptionsManager; 8 | import com.sentiance.react.bridge.core.common.base.AbstractSentianceModule; 9 | import com.sentiance.sdk.InitState; 10 | import com.sentiance.sdk.Sentiance; 11 | 12 | import org.junit.After; 13 | import org.junit.Before; 14 | import org.mockito.ArgumentCaptor; 15 | import org.mockito.Captor; 16 | import org.mockito.Mock; 17 | import org.mockito.Mockito; 18 | import org.mockito.MockitoAnnotations; 19 | 20 | public abstract class ReactNativeModuleTest extends ReactNativeTest { 21 | 22 | @Mock 23 | protected ReactApplicationContext mReactApplicationContext; 24 | 25 | @Mock 26 | protected WritableArray mockWritableArray; 27 | @Mock 28 | protected WritableMap mockWritableMap; 29 | @Captor 30 | protected ArgumentCaptor writableArrayCaptor; 31 | @Captor 32 | protected ArgumentCaptor writableMapCaptor; 33 | @Captor 34 | protected ArgumentCaptor stringCaptor; 35 | @Captor 36 | protected ArgumentCaptor intCaptor; 37 | @Captor 38 | protected ArgumentCaptor boolCaptor; 39 | 40 | @Mock 41 | protected Sentiance mSentiance; 42 | @Mock 43 | protected Promise mPromise; 44 | @Mock 45 | protected SentianceSubscriptionsManager mSentianceSubscriptionsManager; 46 | 47 | protected T mModule; 48 | 49 | @Before 50 | public void setUp() throws Exception { 51 | super.setUp(); 52 | MockitoAnnotations.initMocks(this); 53 | ensureSdkIsInitialized(); 54 | mModule = initModule(); 55 | } 56 | 57 | @After 58 | public void tearDown() { 59 | Mockito.validateMockitoUsage(); 60 | } 61 | 62 | protected abstract T initModule(); 63 | 64 | private void ensureSdkIsInitialized() { 65 | Mockito.when(mSentiance.getInitState()).thenReturn(InitState.INITIALIZED); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/test-common/android/src/main/java/com/sentiance/react/bridge/test/ReactNativeTest.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.test; 2 | 3 | import com.facebook.react.bridge.Arguments; 4 | import com.facebook.react.bridge.JavaOnlyArray; 5 | import com.facebook.react.bridge.JavaOnlyMap; 6 | import com.facebook.react.bridge.WritableArray; 7 | import com.facebook.react.bridge.WritableMap; 8 | 9 | import org.junit.Before; 10 | import org.junit.Rule; 11 | import org.mockito.stubbing.Answer; 12 | import org.powermock.api.mockito.PowerMockito; 13 | import org.powermock.core.classloader.annotations.PowerMockIgnore; 14 | import org.powermock.core.classloader.annotations.PrepareForTest; 15 | import org.powermock.modules.junit4.rule.PowerMockRule; 16 | import org.robolectric.annotation.Config; 17 | 18 | /** 19 | * A base class that statically mocks the {@link Arguments} class. 20 | *
21 | * Any test class that interacts with a component that makes use of the {@link Arguments} class internally 22 | * has to extend this class. 23 | */ 24 | @Config(manifest = Config.NONE) 25 | @PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "jdk.internal.reflect.*"}) 26 | @PrepareForTest(Arguments.class) 27 | public abstract class ReactNativeTest { 28 | 29 | @Rule 30 | public PowerMockRule rule = new PowerMockRule(); 31 | 32 | @Before 33 | public void setUp() throws Exception { 34 | PowerMockito.mockStatic(Arguments.class); 35 | PowerMockito.when(Arguments.createArray()).thenAnswer((Answer) invocation -> new JavaOnlyArray()); 36 | PowerMockito.when(Arguments.createMap()).thenAnswer((Answer) invocation -> new JavaOnlyMap()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/test-common/android/src/main/java/com/sentiance/react/bridge/test/validators/BridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.test.validators; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.bridge.JavaOnlyMap; 6 | 7 | public interface BridgeValidator { 8 | default void validate(@NonNull T expected, @NonNull JavaOnlyMap actual) { 9 | throw new UnsupportedOperationException("Not yet implemented."); 10 | } 11 | 12 | default void validate(@NonNull JavaOnlyMap expected, @NonNull T actual) { 13 | throw new UnsupportedOperationException("Not yet implemented."); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/test-common/android/src/main/java/com/sentiance/react/bridge/test/validators/GeoLocationBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.test.validators; 2 | 3 | import static com.sentiance.react.bridge.core.SentianceConverter.JS_KEY_ACCURACY; 4 | import static com.sentiance.react.bridge.core.SentianceConverter.JS_KEY_LATITUDE; 5 | import static com.sentiance.react.bridge.core.SentianceConverter.JS_KEY_LONGITUDE; 6 | import static org.junit.Assert.assertEquals; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import com.facebook.react.bridge.JavaOnlyMap; 11 | import com.sentiance.sdk.ondevice.api.GeoLocation; 12 | 13 | public class GeoLocationBridgeValidator implements BridgeValidator { 14 | @Override 15 | public void validate(@NonNull GeoLocation expected, @NonNull JavaOnlyMap actual) { 16 | assertEquals(expected.getLatitude(), actual.getDouble(JS_KEY_LATITUDE), 0.000001); 17 | assertEquals(expected.getLongitude(), actual.getDouble(JS_KEY_LONGITUDE), 0.000001); 18 | assertEquals(expected.getAccuracyInMeters(), actual.getInt(JS_KEY_ACCURACY)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/test-common/android/src/main/java/com/sentiance/react/bridge/test/validators/LocationBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.test.validators; 2 | 3 | import static com.sentiance.react.bridge.core.SentianceConverter.JS_KEY_ACCURACY; 4 | import static com.sentiance.react.bridge.core.SentianceConverter.JS_KEY_ALTITUDE; 5 | import static com.sentiance.react.bridge.core.SentianceConverter.JS_KEY_LATITUDE; 6 | import static com.sentiance.react.bridge.core.SentianceConverter.JS_KEY_LONGITUDE; 7 | import static com.sentiance.react.bridge.core.SentianceConverter.JS_KEY_PROVIDER; 8 | import static com.sentiance.react.bridge.core.SentianceConverter.JS_KEY_TIMESTAMP; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertFalse; 12 | 13 | import android.location.Location; 14 | 15 | import androidx.annotation.NonNull; 16 | 17 | import com.facebook.react.bridge.JavaOnlyMap; 18 | 19 | public class LocationBridgeValidator implements BridgeValidator { 20 | @Override 21 | public void validate(@NonNull Location expected, @NonNull JavaOnlyMap actual) { 22 | assertEquals(expected.getTime(), actual.getDouble(JS_KEY_TIMESTAMP), 0.001); 23 | assertEquals(expected.getLatitude(), actual.getDouble(JS_KEY_LATITUDE), 0.001); 24 | assertEquals(expected.getLongitude(), actual.getDouble(JS_KEY_LONGITUDE), 0.001); 25 | assertEquals(expected.getProvider(), actual.getString(JS_KEY_PROVIDER)); 26 | if (expected.hasAccuracy()) { 27 | assertEquals(expected.getAccuracy(), actual.getDouble(JS_KEY_ACCURACY), 0.0); 28 | } else { 29 | assertFalse(actual.hasKey(JS_KEY_ACCURACY)); 30 | } 31 | if (expected.hasAltitude()) { 32 | assertEquals(expected.getAltitude(), actual.getDouble(JS_KEY_ALTITUDE), 0.0); 33 | } else { 34 | assertFalse(actual.hasKey(JS_KEY_ALTITUDE)); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/test-common/android/src/main/java/com/sentiance/react/bridge/test/validators/VenueBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.test.validators; 2 | 3 | import static com.sentiance.react.bridge.eventtimeline.converters.OnDeviceTypesConverter.JS_KEY_LOCATION; 4 | import static com.sentiance.react.bridge.eventtimeline.converters.OnDeviceTypesConverter.JS_KEY_SIGNIFICANCE; 5 | import static com.sentiance.react.bridge.eventtimeline.converters.OnDeviceTypesConverter.JS_KEY_TYPE; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertFalse; 8 | 9 | import androidx.annotation.NonNull; 10 | 11 | import com.facebook.react.bridge.JavaOnlyMap; 12 | import com.sentiance.sdk.ondevice.api.GeoLocation; 13 | import com.sentiance.sdk.ondevice.api.venue.Venue; 14 | 15 | public class VenueBridgeValidator implements BridgeValidator { 16 | 17 | private final GeoLocationBridgeValidator geoLocationBridgeValidator; 18 | 19 | public VenueBridgeValidator() { 20 | this.geoLocationBridgeValidator = new GeoLocationBridgeValidator(); 21 | } 22 | 23 | @Override 24 | public void validate(@NonNull Venue expected, @NonNull JavaOnlyMap actual) { 25 | GeoLocation location = expected.getLocation(); 26 | if (location == null) { 27 | assertFalse(actual.hasKey(JS_KEY_LOCATION)); 28 | } else { 29 | geoLocationBridgeValidator.validate(location, (JavaOnlyMap) actual.getMap(JS_KEY_LOCATION)); 30 | } 31 | 32 | assertEquals(expected.getSignificance().name(), actual.getString(JS_KEY_SIGNIFICANCE)); 33 | assertEquals(expected.getType().name(), actual.getString(JS_KEY_TYPE)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/test-common/android/src/main/java/com/sentiance/react/bridge/test/validators/WaypointsBridgeValidator.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.test.validators; 2 | 3 | import static com.sentiance.react.bridge.eventtimeline.converters.OnDeviceTypesConverter.JS_KEY_WAYPOINTS; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.facebook.react.bridge.JavaOnlyMap; 9 | import com.facebook.react.bridge.ReadableArray; 10 | import com.facebook.react.bridge.ReadableMap; 11 | import com.sentiance.sdk.ondevice.api.Waypoint; 12 | 13 | import java.util.List; 14 | 15 | public class WaypointsBridgeValidator implements BridgeValidator> { 16 | 17 | private final WaypointBridgeValidator waypointBridgeValidator; 18 | 19 | public WaypointsBridgeValidator() { 20 | waypointBridgeValidator = new WaypointBridgeValidator(); 21 | } 22 | 23 | @Override 24 | public void validate(@NonNull List expected, @NonNull JavaOnlyMap actual) { 25 | assertTrue(actual.hasKey(JS_KEY_WAYPOINTS)); 26 | ReadableArray transformedWaypoints = actual.getArray(JS_KEY_WAYPOINTS); 27 | for (int i = 0; i < expected.size(); i++) { 28 | Waypoint waypoint = expected.get(i); 29 | ReadableMap transformedWaypoint = transformedWaypoints.getMap(i); 30 | if (transformedWaypoint != null) { 31 | waypointBridgeValidator.validate(waypoint, (JavaOnlyMap) transformedWaypoint); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/user-context/.npmignore: -------------------------------------------------------------------------------- 1 | /__tests__ 2 | /jest 3 | android/src/test 4 | -------------------------------------------------------------------------------- /packages/user-context/README.md: -------------------------------------------------------------------------------- 1 | # Sentiance User Context module for React Native 2 | 3 | ## Demo Application 4 | 5 | https://github.com/sentiance/sample-apps-react-native 6 | 7 | ## Usage 8 | 9 | To use the user context SDK module, please visit the corresponding [API reference page.](https://docs.sentiance.com/sdk/api-reference/react-native/user-context) 10 | -------------------------------------------------------------------------------- /packages/user-context/__tests__/user-context.test.js: -------------------------------------------------------------------------------- 1 | import { runOnEachPlatform } from "../../../jest/test_util.js"; 2 | import { mockNativeUserContextModule } from "../jest/mockNativeModule"; 3 | 4 | describe("User context API tests", () => { 5 | 6 | beforeEach(() => jest.resetModules()); 7 | 8 | describe("Request user context", () => { 9 | 10 | runOnEachPlatform(async platform => { 11 | const mockNativeRequestUserContext = jest.fn(); 12 | mockNativeUserContextModule(platform, { 13 | requestUserContext: mockNativeRequestUserContext 14 | }); 15 | 16 | const userContextApi = require("../lib"); 17 | 18 | await userContextApi.requestUserContext(); 19 | expect(mockNativeRequestUserContext).toHaveBeenLastCalledWith(false); 20 | 21 | await userContextApi.requestUserContext(true); 22 | expect(mockNativeRequestUserContext).toHaveBeenLastCalledWith(true); 23 | 24 | await userContextApi.requestUserContext(false); 25 | expect(mockNativeRequestUserContext).toHaveBeenLastCalledWith(false); 26 | }); 27 | }); 28 | 29 | describe("Test add/remove user context updates listener", () => { 30 | runOnEachPlatform(async platform => { 31 | // TODO: Ideally, we would mock the SentianceEventEmitter and avoid tapping into its implementation details, 32 | // such as subscription ID assignments etc... 33 | 34 | const activeModule = mockNativeUserContextModule(platform, {}); 35 | const addNativeListener = activeModule.addNativeListener; 36 | const removeNativeListener = activeModule.removeNativeListener; 37 | 38 | const userContextApi = require("../lib"); 39 | const { USER_CONTEXT_UPDATE_EVENT } = userContextApi.events; 40 | 41 | const listener = () => undefined; 42 | 43 | // addNativeListener should always instruct the native code to provide 44 | // all sorts of events (provisional and non-provisional) 45 | const subscription1 = await userContextApi.addUserContextUpdateListener(listener); 46 | expect(addNativeListener).toHaveBeenLastCalledWith(USER_CONTEXT_UPDATE_EVENT, 1, { includeProvisionalEvents: true }); 47 | const subscription2 = await userContextApi.addUserContextUpdateListener(listener, false); 48 | expect(addNativeListener).toHaveBeenLastCalledWith(USER_CONTEXT_UPDATE_EVENT, 2, { includeProvisionalEvents: true }); 49 | const subscription3 = await userContextApi.addUserContextUpdateListener(listener, true); 50 | expect(addNativeListener).toHaveBeenLastCalledWith(USER_CONTEXT_UPDATE_EVENT, 3, { includeProvisionalEvents: true }); 51 | 52 | await subscription1.remove(); 53 | expect(removeNativeListener).toHaveBeenLastCalledWith(USER_CONTEXT_UPDATE_EVENT, 1); 54 | await subscription2.remove(); 55 | expect(removeNativeListener).toHaveBeenLastCalledWith(USER_CONTEXT_UPDATE_EVENT, 2); 56 | await subscription3.remove(); 57 | expect(removeNativeListener).toHaveBeenLastCalledWith(USER_CONTEXT_UPDATE_EVENT, 3); 58 | }); 59 | }); 60 | }); 61 | 62 | 63 | -------------------------------------------------------------------------------- /packages/user-context/android/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .idea 3 | .gradle 4 | -------------------------------------------------------------------------------- /packages/user-context/android/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.library" 3 | } 4 | 5 | def coreProj 6 | if (findProject(':core')) { 7 | coreProj = project(':core') 8 | } else if (findProject(':sentiance-react-native_core')) { 9 | // Starting from RN 0.61, the @ sign is stripped from project names 10 | coreProj = project(':sentiance-react-native_core') 11 | } else if (findProject(':@sentiance-react-native_core')) { 12 | // On RN 0.60, the @ sign is not stripped from project names 13 | coreProj = project(':@sentiance-react-native_core') 14 | } else { 15 | throw new GradleException('Could not find the @sentiance-react-native/core package, have you installed it?') 16 | } 17 | 18 | def eventTimelineProj 19 | if (findProject(':event-timeline')) { 20 | eventTimelineProj = project(':event-timeline') 21 | } else if (findProject(':sentiance-react-native_event-timeline')) { 22 | eventTimelineProj = project(':sentiance-react-native_event-timeline') 23 | } else if (findProject(':@sentiance-react-native_event-timeline')) { 24 | eventTimelineProj = project(':@sentiance-react-native_event-timeline') 25 | } else { 26 | throw new GradleException('Could not find the @sentiance-react-native/event-timeline package, have you installed it?') 27 | } 28 | 29 | android { 30 | namespace "com.sentiance.react.bridge.usercontext" 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | } 37 | 38 | apply from: "$coreProj.projectDir/package-json-reader.gradle" 39 | apply from: "$coreProj.projectDir/sentiance-version-finder.gradle" 40 | 41 | def corePackageJson = PackageJson.of(coreProj) 42 | applyAndroidVersionsFrom(corePackageJson) 43 | def sentianceSdkVersion = getSentianceSdkVersion() 44 | 45 | dependencies { 46 | implementation(platform("com.sentiance:sdk-bom:${sentianceSdkVersion}")) 47 | api("com.sentiance:sdk-user-context") { transitive = true } 48 | api("com.sentiance:sdk-lifestyle") { transitive = true } 49 | implementation coreProj 50 | implementation eventTimelineProj 51 | } 52 | 53 | applyReactNativeDependency() 54 | -------------------------------------------------------------------------------- /packages/user-context/android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | android.enableJetifier=true 3 | -------------------------------------------------------------------------------- /packages/user-context/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/packages/user-context/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /packages/user-context/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /packages/user-context/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /packages/user-context/android/src/main/java/com/sentiance/react/bridge/usercontext/SentianceUserContextEmitter.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.usercontext; 2 | 3 | import android.content.Context; 4 | 5 | import com.facebook.react.bridge.Arguments; 6 | import com.facebook.react.bridge.WritableMap; 7 | import com.sentiance.react.bridge.core.common.base.AbstractSentianceEmitter; 8 | import com.sentiance.sdk.usercontext.api.UserContext; 9 | import com.sentiance.sdk.usercontext.api.UserContextUpdateCriteria; 10 | 11 | import java.util.List; 12 | 13 | public class SentianceUserContextEmitter extends AbstractSentianceEmitter { 14 | 15 | static final String USER_CONTEXT_EVENT = "SENTIANCE_USER_CONTEXT_UPDATE_EVENT"; 16 | private final SentianceUserContextConverter converter; 17 | 18 | public SentianceUserContextEmitter(Context context) { 19 | super(context); 20 | converter = new SentianceUserContextConverter(); 21 | } 22 | 23 | void sendUserContext(List criteria, UserContext userContext) { 24 | WritableMap map = Arguments.createMap(); 25 | map.putMap("userContext", converter.convertUserContext(userContext)); 26 | map.putArray("criteria", converter.convertCriteriaList(criteria)); 27 | 28 | sendEvent(USER_CONTEXT_EVENT, map); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/user-context/android/src/main/java/com/sentiance/react/bridge/usercontext/SentianceUserContextPackage.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.usercontext; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | import com.sentiance.react.bridge.core.common.SentianceSubscriptionsManager; 10 | import com.sentiance.sdk.Sentiance; 11 | import com.sentiance.sdk.usercontext.api.UserContextApi; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | public class SentianceUserContextPackage implements ReactPackage { 18 | 19 | @NonNull 20 | @Override 21 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 22 | List modules = new ArrayList<>(); 23 | SentianceUserContextModule module = new SentianceUserContextModule( 24 | reactContext, 25 | Sentiance.getInstance(reactContext), 26 | new SentianceSubscriptionsManager(), 27 | new SentianceUserContextEmitter(reactContext), 28 | new SentianceUserContextConverter(), 29 | UserContextApi.getInstance(reactContext) 30 | ); 31 | modules.add(module); 32 | return modules; 33 | } 34 | 35 | @NonNull 36 | @Override 37 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 38 | return Collections.emptyList(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/user-context/android/src/main/java/com/sentiance/react/bridge/usercontext/utils/ErrorCodes.java: -------------------------------------------------------------------------------- 1 | package com.sentiance.react.bridge.usercontext.utils; 2 | 3 | public class ErrorCodes { 4 | public static final String E_SDK_REQUEST_USER_CONTEXT_ERROR = "E_SDK_REQUEST_USER_CONTEXT_ERROR"; 5 | } 6 | -------------------------------------------------------------------------------- /packages/user-context/jest/mockNativeModule.js: -------------------------------------------------------------------------------- 1 | import {mockNativeModule} from "../../../jest/mockNativeModules"; 2 | 3 | export function mockNativeUserContextModule(platform, module) { 4 | return mockNativeModule( 5 | platform, 6 | { 7 | androidName: 'SentianceUserContext', 8 | iosName: 'SentianceCore' 9 | }, 10 | module); 11 | } 12 | -------------------------------------------------------------------------------- /packages/user-context/lib/index.js: -------------------------------------------------------------------------------- 1 | const userContext = require("./user-context"); 2 | 3 | const requestUserContext = (includeProvisionalEvents) => { 4 | // See: https://github.com/facebook/react-native/issues/24250 5 | return userContext.requestUserContext(!!includeProvisionalEvents); 6 | }; 7 | const addUserContextUpdateListener = (listener, includeProvisionalEvents) => 8 | userContext._addUserContextUpdateListener(listener, !!includeProvisionalEvents); 9 | 10 | module.exports = { 11 | requestUserContext, 12 | addUserContextUpdateListener 13 | }; 14 | module.exports.events = { 15 | USER_CONTEXT_UPDATE_EVENT: userContext.events.USER_CONTEXT_UPDATE_EVENT 16 | }; 17 | -------------------------------------------------------------------------------- /packages/user-context/lib/user-context.js: -------------------------------------------------------------------------------- 1 | const { NativeModules, Platform } = require("react-native"); 2 | const { varToString } = require("@sentiance-react-native/core/lib/generated/utils"); 3 | const SentianceEventEmitter = require("@sentiance-react-native/core/lib/generated/sentiance-event-emitter").default; 4 | const { SentianceUserContext, SentianceCore } = NativeModules; 5 | 6 | const allUserContextCriteria = ["CURRENT_EVENT", "ACTIVE_SEGMENTS", "VISITED_VENUES"]; 7 | const SDK_USER_CONTEXT_UPDATE_EVENT = "SENTIANCE_USER_CONTEXT_UPDATE_EVENT"; 8 | 9 | let didLocateNativeModule = true; 10 | let userContextModule = {}; 11 | if (Platform.OS === "android") { 12 | if (!SentianceUserContext) { 13 | didLocateNativeModule = false; 14 | const nativeModuleName = varToString({ SentianceUserContext }); 15 | console.error(`Could not locate the native ${nativeModuleName} module. 16 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 17 | } else { 18 | userContextModule = SentianceUserContext; 19 | } 20 | } else { 21 | if (!SentianceCore) { 22 | didLocateNativeModule = false; 23 | const nativeModuleName = varToString({ SentianceCore }); 24 | console.error(`Could not locate the native ${nativeModuleName} module. 25 | Make sure that your native code is properly linked, and that the module name you specified is correct.`); 26 | } else { 27 | userContextModule = SentianceCore; 28 | } 29 | } 30 | 31 | if (didLocateNativeModule) { 32 | const emitter = new SentianceEventEmitter(userContextModule); 33 | 34 | userContextModule._addUserContextUpdateListener = async (onUserContextUpdated, includeProvisionalEvents) => { 35 | const payload = { 36 | includeProvisionalEvents: true 37 | }; 38 | return emitter.addListener( 39 | SDK_USER_CONTEXT_UPDATE_EVENT, 40 | async function(update) { 41 | if (includeProvisionalEvents) { 42 | // If the JS listener is interested in provisional events, we deliver the update as is 43 | onUserContextUpdated(update); 44 | } else { 45 | // Otherwise, we query for a fresh user context without provisional events 46 | const userContext = await userContextModule.requestUserContext(false); 47 | onUserContextUpdated({ 48 | userContext, 49 | criteria: allUserContextCriteria 50 | }); 51 | } 52 | }, 53 | payload 54 | ); 55 | }; 56 | } 57 | 58 | module.exports = userContextModule; 59 | module.exports.events = { 60 | USER_CONTEXT_UPDATE_EVENT: SDK_USER_CONTEXT_UPDATE_EVENT 61 | }; 62 | -------------------------------------------------------------------------------- /packages/user-context/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sentiance-react-native/user-context", 3 | "version": "6.10.1", 4 | "description": "The Sentiance User Context library", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "lint": "npx eslint lib/index.d.ts" 10 | }, 11 | "keywords": [ 12 | "react-native", 13 | "user-context", 14 | "sentiance" 15 | ], 16 | "peerDependencies": { 17 | "@sentiance-react-native/core": "6.10.1" 18 | }, 19 | "author": "", 20 | "license": "", 21 | "homepage": "https://github.com/sentiance/react-native-sentiance/tree/main/packages/user-context", 22 | "repository": "github:sentiance/react-native-sentiance", 23 | "publishConfig": { 24 | "access": "public" 25 | } 26 | } -------------------------------------------------------------------------------- /publishing.md: -------------------------------------------------------------------------------- 1 | # Publishing the library modules to a local NPM registry 2 | 3 | ## Getting started 4 | 5 | We use [verdaccio](https://verdaccio.org/) as a local private NPM registry where 6 | we can publish the different library modules to be tested at a later time. 7 | 8 | #### Install verdaccio 9 | 10 | Start by installing verdaccio globally: 11 | 12 | ```bash 13 | npm i -g verdaccio 14 | ``` 15 | 16 | #### Registering a user 17 | 18 | Next, we configure the user which is going to publish packages to the private registry: 19 | 20 | ```bash 21 | npm adduser --registry http://localhost:4873 22 | ``` 23 | 24 | Then follow the prompts on the command line. 25 | 26 | #### Run verdaccio 27 | 28 | To start verdaccio, run the following command: 29 | 30 | ```bash 31 | verdaccio 32 | ``` 33 | 34 | That's it. You are now ready to publish your packages to verdaccio. 35 | 36 | ## Publishing the library packages 37 | 38 | On the root folder of the project, run the `publishLocal` npm script: 39 | 40 | ```bash 41 | npm run publishLocal 42 | ``` 43 | 44 | This publishes all library packages to your local verdaccio repository. 45 | -------------------------------------------------------------------------------- /scripts/determine-version-tag.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This script attempts to figure out what the tag for the version could be. 3 | * This tag would ideally be used in the "npm publish" of the package. 4 | * 5 | * 1.1.1 tag = stable 6 | * 1.1.1-beta.0 = beta 7 | * etc 8 | */ 9 | 10 | const json = require('../package.json') 11 | 12 | const match = json.version.match(/^(\d*\.\d*\.\d*)\-([a-z]*)\.(\d*)$/) 13 | const tag = (match && match[2]) ?? 'latest' 14 | 15 | /** Logs the tag for it to be consumed by a different script as an input */ 16 | console.log(tag) -------------------------------------------------------------------------------- /scripts/new-sdk-module/generate_build_gradle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTS_FOLDER="scripts" 4 | source "$SCRIPTS_FOLDER/new-sdk-module/shared.sh" 5 | 6 | output_file="$1" 7 | fully_qualified_module_name="$2" 8 | 9 | cat << EOF > "$output_file" 10 | plugins { 11 | id "com.android.library" 12 | } 13 | 14 | def coreProj 15 | if (findProject(':core')) { 16 | coreProj = project(':core') 17 | } else if (findProject(':sentiance-react-native_core')) { 18 | // Starting from RN 0.61, the @ sign is stripped from project names 19 | coreProj = project(':sentiance-react-native_core') 20 | } else if (findProject(':@sentiance-react-native_core')) { 21 | // On RN 0.60, the @ sign is not stripped from project names 22 | coreProj = project(':@sentiance-react-native_core') 23 | } else { 24 | throw new GradleException('Could not find the @sentiance-react-native/core package, have you installed it?') 25 | } 26 | 27 | android { 28 | namespace "$fully_qualified_module_name" 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | } 35 | 36 | apply from: "\$coreProj.projectDir/package-json-reader.gradle" 37 | apply from: "\$coreProj.projectDir/sentiance-version-finder.gradle" 38 | 39 | def corePackageJson = PackageJson.of(coreProj) 40 | applyAndroidVersionsFrom(corePackageJson) 41 | def sentianceSdkVersion = getSentianceSdkVersion() 42 | 43 | dependencies { 44 | implementation(platform("com.sentiance:sdk-bom:\${sentianceSdkVersion}")) 45 | api coreProj 46 | 47 | if (findProject(':test-common')) { 48 | testImplementation project(':test-common') 49 | } 50 | 51 | // Add here any other dependencies that the new module requires 52 | } 53 | 54 | applyReactNativeDependency() 55 | EOF 56 | -------------------------------------------------------------------------------- /scripts/new-sdk-module/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script provides a step-by-step configuration for setting up a new SDK module. 4 | 5 | SCRIPTS_FOLDER="scripts" 6 | source "$SCRIPTS_FOLDER/new-sdk-module/shared.sh" 7 | 8 | # Helper setup scripts 9 | REACT_NATIVE_SETUP="$SCRIPTS_FOLDER/new-sdk-module/react-native-setup.sh" 10 | ANDROID_SETUP="$SCRIPTS_FOLDER/new-sdk-module/android-setup.sh" 11 | 12 | # Check if the packages folder exists in the current directory 13 | if [ ! -d "$PACKAGES_FOLDER" ]; then 14 | echo-red "You must run this script from the root of the project." 15 | exit 1 16 | fi 17 | 18 | echo "" 19 | echo "" 20 | echo "" 21 | echo_red "███╗ ██╗███████╗██╗ ██╗ ███████╗██████╗ ██╗ ██╗ ███╗ ███╗ ██████╗ ██████╗ ██╗ ██╗██╗ ███████╗" 22 | echo_red "████╗ ██║██╔════╝██║ ██║ ██╔════╝██╔══██╗██║ ██╔╝ ████╗ ████║██╔═══██╗██╔══██╗██║ ██║██║ ██╔════╝" 23 | echo_red "██╔██╗ ██║█████╗ ██║ █╗ ██║ ███████╗██║ ██║█████╔╝ ██╔████╔██║██║ ██║██║ ██║██║ ██║██║ █████╗ " 24 | echo_red "██║╚██╗██║██╔══╝ ██║███╗██║ ╚════██║██║ ██║██╔═██╗ ██║╚██╔╝██║██║ ██║██║ ██║██║ ██║██║ ██╔══╝ " 25 | echo_red "██║ ╚████║███████╗╚███╔███╔╝ ███████║██████╔╝██║ ██╗ ██║ ╚═╝ ██║╚██████╔╝██████╔╝╚██████╔╝███████╗███████╗" 26 | echo_red "╚═╝ ╚═══╝╚══════╝ ╚══╝╚══╝ ╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝" 27 | echo "" 28 | echo "" 29 | echo_gray "" 30 | 31 | read -p "Enter the name of the new SDK module: " module_name 32 | 33 | # Check if the packages folder already has a package with the provided module name 34 | if [ -d "$PACKAGES_FOLDER/$module_name" ]; then 35 | echo_red "A package with the same name already exists, aborting." 36 | exit 2 37 | fi 38 | 39 | new_module_dir="$PACKAGES_FOLDER/$module_name" 40 | mkdir "$new_module_dir" 41 | res=$? 42 | if [ $res -ne $SUCCESS ]; then 43 | echo_red "Failed to create directory: $new_module_dir" 44 | exit $ERR_CREATE_NEW_MODULE_FOLDER 45 | fi 46 | 47 | $REACT_NATIVE_SETUP $new_module_dir $module_name 48 | res=$? 49 | if [ $res -eq $SUCCESS ]; then 50 | echo_green "React Native and JS related module set up. ✅ " 51 | else 52 | echo_red "React Native setup failed with code: $res" 53 | exit $res 54 | fi 55 | 56 | $ANDROID_SETUP $new_module_dir $module_name 57 | res=$? 58 | if [ $res -eq $SUCCESS ]; then 59 | echo_green "Android module set up. ✅ " 60 | else 61 | echo_red "Android module setup failed with code: $res" 62 | exit $res 63 | fi 64 | 65 | echo_magenta "$module_name has been successfully set up !" 66 | -------------------------------------------------------------------------------- /scripts/new-sdk-module/shared.sh: -------------------------------------------------------------------------------- 1 | PACKAGES_FOLDER="packages" 2 | ORG_NAME="@sentiance-react-native" 3 | SDK_MODULES_MAP_FILE_PATH="./$SCRIPTS_FOLDER/sdk_modules_map.json" 4 | GRADLE_SETTINGS_FILE_PATH="$PACKAGES_FOLDER/settings.gradle" 5 | ANDROID_BASE_PACKAGE_NAME="com.sentiance.react.bridge" 6 | 7 | # Exit codes 8 | SUCCESS=0 9 | ERR_PACKAGE_JSON=1 10 | ERR_ADD_NPM_WORKSPACE=2 11 | ERR_UPDATE_SDK_MODULES_MAP=3 12 | ERR_CREATE_ANDROID_MAIN_FOLDER=4 13 | ERR_CREATE_ANDROID_TEST_FOLDER=5 14 | ERR_SETTINGS_GRADLE=6 15 | ERR_ANDROID_MANIFEST=7 16 | ERR_MODULE_BUILD_GRADLE=8 17 | ERR_GENERATE_JAVA_CODE=9 18 | ERR_CREATE_JS_TESTS_FOLDER=10 19 | ERR_CREATE_NEW_MODULE_FOLDER=11 20 | ERR_CREATE_JS_LIBS_FOLDER=12 21 | ERR_CREATE_README=13 22 | ERR_CREATE_TYPESCRIPT_DEFINITION=14 23 | ERR_CREATE_JS_ENTRY_POINT=15 24 | 25 | # Function to write a line to the output file and check for errors 26 | append_to_file() { 27 | echo "$2" >> "$1" 28 | if [ $? -ne 0 ]; then 29 | exit 1 30 | fi 31 | } 32 | 33 | # Strip dashes from a string 34 | function stripDashes() { 35 | echo "$1" | sed 's/-//g' 36 | } 37 | 38 | # Function to replace dashes with spaces and capitalize each word 39 | replace_dashes_with_spaces_and_capitalize() { 40 | local input_string="$1" 41 | # Replace dashes with spaces 42 | local modified_string="${input_string//-/ }" 43 | # Capitalize the first letter of each word 44 | local capitalized_string=$(echo "$modified_string" | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2)); print}') 45 | echo "$capitalized_string" 46 | } 47 | 48 | # Function to remove dashes with spaces and capitalize each word 49 | remove_dashes_and_capitalize() { 50 | # Convert to pascal case using perl 51 | local dash_less_pascal_case=$(echo "$1" | perl -pe 's/(^|-)([a-z])/\U$2/gi') 52 | echo $dash_less_pascal_case 53 | } 54 | 55 | # Color variables 56 | RED="\033[0;31m" 57 | CYAN="\033[0;36m" 58 | YELLOW="\033[0;33m" 59 | MAGENTA="\033[0;35m" 60 | GREEN="\033[0;32m" 61 | GRAY="\033[0;37m" 62 | 63 | function echo_red() { 64 | echo -e "${RED}$1${RESET}" 65 | } 66 | 67 | function echo_cyan() { 68 | echo -e "${CYAN}$1${RESET}" 69 | } 70 | 71 | function echo_yellow() { 72 | echo -e "${YELLOW}$1${RESET}" 73 | } 74 | 75 | function echo_magenta() { 76 | echo -e "${MAGENTA}$1${RESET}" 77 | } 78 | 79 | function echo_green() { 80 | echo -e "${GREEN}$1${RESET}" 81 | } 82 | 83 | function echo_gray() { 84 | echo -e "${GRAY}$1${RESET}" 85 | } 86 | -------------------------------------------------------------------------------- /scripts/sdk_modules_map.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "core", 4 | "iosModuleName": "RNSentianceCore" 5 | }, 6 | { 7 | "name": "crash-detection", 8 | "iosModuleName": "SentianceCrashDetection" 9 | }, 10 | { 11 | "name": "user-context" 12 | }, 13 | { 14 | "name": "legacy" 15 | }, 16 | { 17 | "name": "driving-insights" 18 | }, 19 | { 20 | "name": "event-timeline" 21 | }, 22 | { 23 | "name": "smart-geofences" 24 | } 25 | ] 26 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 4 | "module": "commonjs", /* Specify what module code is generated. */ 5 | "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 6 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 7 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 8 | "strict": true, /* Enable all strict type-checking options. */ 9 | "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 10 | "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 11 | "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 12 | "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 13 | "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 14 | "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 15 | "skipLibCheck": true, /* Skip type checking all .d.ts files. */ 16 | "typeRoots": ["./node_modules/@types"] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./packages/core" }, 5 | { "path": "./packages/event-timeline" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /types.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentiance/react-native-sentiance/4ffd2f4daa39e69ed80ee4dc2a4f94b8c6b59e90/types.md --------------------------------------------------------------------------------