packages = new PackageList(this).getPackages();
34 | packages.add(new NativeMobileSDKBridgePackage());
35 | return packages;
36 | }
37 |
38 | @Override
39 | protected String getJSMainModuleName() {
40 | return "index";
41 | }
42 | };
43 |
44 | @Override
45 | public ReactNativeHost getReactNativeHost() {
46 | return mReactNativeHost;
47 | }
48 |
49 | @Override
50 | public void onCreate() {
51 | System.loadLibrary("c++_shared");
52 | super.onCreate();
53 | SoLoader.init(this, /* native exopackage */ false);
54 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
55 | }
56 |
57 | /**
58 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like
59 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
60 | *
61 | * @param context
62 | * @param reactInstanceManager
63 | */
64 | private static void initializeFlipper(
65 | Context context, ReactInstanceManager reactInstanceManager) {
66 | if (BuildConfig.DEBUG) {
67 | try {
68 | /*
69 | We use reflection here to pick up the class that initializes Flipper,
70 | since Flipper library is not available in release mode
71 | */
72 | Class> aClass = Class.forName("com.amazonaws.services.chime.rndemo.ReactNativeFlipper");
73 | aClass
74 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
75 | .invoke(null, context, reactInstanceManager);
76 | } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException exception) {
77 | exception.printStackTrace();
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/android/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 Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto execute
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto execute
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *master* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
62 |
--------------------------------------------------------------------------------
/ios/RNDemo.xcodeproj/xcshareddata/xcschemes/RNDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/android/app/src/debug/java/com/amazonaws/services/chime/rndemo/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.amazonaws.services.chime.rndemo;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
22 | import com.facebook.react.ReactInstanceEventListener;
23 | import com.facebook.react.ReactInstanceManager;
24 | import com.facebook.react.bridge.ReactContext;
25 | import com.facebook.react.modules.network.NetworkingModule;
26 | import okhttp3.OkHttpClient;
27 |
28 | public class ReactNativeFlipper {
29 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
30 | if (FlipperUtils.shouldEnableFlipper(context)) {
31 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
32 |
33 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
34 | client.addPlugin(new ReactFlipperPlugin());
35 | client.addPlugin(new DatabasesFlipperPlugin(context));
36 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
37 | client.addPlugin(CrashReporterPlugin.getInstance());
38 |
39 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
40 | NetworkingModule.setCustomClientBuilder(
41 | new NetworkingModule.CustomClientBuilder() {
42 | @Override
43 | public void apply(OkHttpClient.Builder builder) {
44 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
45 | }
46 | });
47 | client.addPlugin(networkFlipperPlugin);
48 | client.start();
49 |
50 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
51 | // Hence we run if after all native modules have been initialized
52 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
53 | if (reactContext == null) {
54 | reactInstanceManager.addReactInstanceEventListener(
55 | new ReactInstanceEventListener() {
56 | @Override
57 | public void onReactContextInitialized(ReactContext reactContext) {
58 | reactInstanceManager.removeReactInstanceEventListener(this);
59 | reactContext.runOnNativeModulesQueueThread(
60 | new Runnable() {
61 | @Override
62 | public void run() {
63 | client.addPlugin(new FrescoFlipperPlugin());
64 | }
65 | });
66 | }
67 | });
68 | } else {
69 | client.addPlugin(new FrescoFlipperPlugin());
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/ios/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/ios/RNDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // SPDX-License-Identifier: MIT-0
4 | //
5 |
6 | #import "AppDelegate.h"
7 |
8 | #import
9 | #import
10 | #import
11 |
12 | #import
13 |
14 | #if RCT_NEW_ARCH_ENABLED
15 | #import
16 | #import
17 | #import
18 | #import
19 | #import
20 | #import
21 |
22 | #import
23 |
24 | static NSString *const kRNConcurrentRoot = @"concurrentRoot";
25 |
26 | @interface AppDelegate () {
27 | RCTTurboModuleManager *_turboModuleManager;
28 | RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
29 | std::shared_ptr _reactNativeConfig;
30 | facebook::react::ContextContainer::Shared _contextContainer;
31 | }
32 | @end
33 | #endif
34 |
35 | @implementation AppDelegate
36 |
37 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
38 | {
39 | RCTAppSetupPrepareApp(application);
40 |
41 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
42 |
43 | #if RCT_NEW_ARCH_ENABLED
44 | _contextContainer = std::make_shared();
45 | _reactNativeConfig = std::make_shared();
46 | _contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
47 | _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
48 | bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
49 | #endif
50 |
51 | NSDictionary *initProps = [self prepareInitialProps];
52 | UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"RNDemo", initProps);
53 |
54 | if (@available(iOS 13.0, *)) {
55 | rootView.backgroundColor = [UIColor systemBackgroundColor];
56 | } else {
57 | rootView.backgroundColor = [UIColor whiteColor];
58 | }
59 |
60 |
61 | // rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
62 |
63 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
64 | UIViewController *rootViewController = [UIViewController new];
65 | rootViewController.view = rootView;
66 | self.window.rootViewController = rootViewController;
67 | [self.window makeKeyAndVisible];
68 | return YES;
69 | }
70 |
71 | /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
72 | ///
73 | /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
74 | /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
75 | /// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
76 | - (BOOL)concurrentRootEnabled
77 | {
78 | // Switch this bool to turn on and off the concurrent root
79 | return true;
80 | }
81 |
82 | - (NSDictionary *)prepareInitialProps
83 | {
84 | NSMutableDictionary *initProps = [NSMutableDictionary new];
85 |
86 | #ifdef RCT_NEW_ARCH_ENABLED
87 | initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
88 | #endif
89 |
90 | return initProps;
91 | }
92 |
93 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
94 | {
95 | #if DEBUG
96 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
97 | #else
98 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
99 | #endif
100 | }
101 |
102 | #if RCT_NEW_ARCH_ENABLED
103 |
104 | #pragma mark - RCTCxxBridgeDelegate
105 |
106 | - (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge
107 | {
108 | _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
109 | delegate:self
110 | jsInvoker:bridge.jsCallInvoker];
111 | return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
112 | }
113 |
114 | #pragma mark RCTTurboModuleManagerDelegate
115 |
116 | - (Class)getModuleClassFromName:(const char *)name
117 | {
118 | return RCTCoreModulesClassProvider(name);
119 | }
120 |
121 | - (std::shared_ptr)getTurboModule:(const std::string &)name
122 | jsInvoker:(std::shared_ptr)jsInvoker
123 | {
124 | return nullptr;
125 | }
126 |
127 | - (std::shared_ptr)getTurboModule:(const std::string &)name
128 | initParams:
129 | (const facebook::react::ObjCTurboModule::InitParams &)params
130 | {
131 | return nullptr;
132 | }
133 |
134 | - (id)getModuleInstanceFromClass:(Class)moduleClass
135 | {
136 | return RCTAppSetupDefaultModuleFromClass(moduleClass);
137 | }
138 |
139 | #endif
140 |
141 | @end
142 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/amazonaws/services/chime/rndemo/RNEventEmitter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | * SPDX-License-Identifier: MIT-0
4 | */
5 |
6 | package com.amazonaws.services.chime.rndemo
7 |
8 | import com.amazonaws.services.chime.sdk.meetings.audiovideo.AttendeeInfo
9 | import com.amazonaws.services.chime.sdk.meetings.audiovideo.video.VideoTileState
10 | import com.amazonaws.services.chime.sdk.meetings.realtime.datamessage.DataMessage
11 | import com.facebook.react.bridge.ReactMethod
12 | import com.facebook.react.bridge.ReactApplicationContext
13 | import com.facebook.react.bridge.WritableMap
14 | import com.facebook.react.bridge.WritableNativeMap
15 | import com.facebook.react.modules.core.DeviceEventManagerModule
16 |
17 | class RNEventEmitter(private val reactContext: ReactApplicationContext) {
18 | companion object {
19 | // Event types
20 | const val RN_EVENT_ERROR = "OnError"
21 | const val RN_EVENT_MEETING_START = "OnMeetingStart"
22 | const val RN_EVENT_MEETING_END = "OnMeetingEnd"
23 | const val RN_EVENT_VIDEO_TILE_ADD = "OnAddVideoTile"
24 | const val RN_EVENT_VIDEO_TILE_REMOVE = "OnRemoveVideoTile"
25 | const val RN_EVENT_ATTENDEES_JOIN = "OnAttendeesJoin"
26 | const val RN_EVENT_ATTENDEES_LEAVE = "OnAttendeesLeave"
27 | const val RN_EVENT_ATTENDEES_MUTE = "OnAttendeesMute"
28 | const val RN_EVENT_ATTENDEES_UNMUTE = "OnAttendeesUnmute"
29 | const val RN_EVENT_DATA_MESSAGE_RECEIVE = "OnDataMessageReceive"
30 |
31 | // Additional data for attendee events
32 | private const val RN_EVENT_KEY_ATTENDEE_ID = "attendeeId"
33 | private const val RN_EVENT_KEY_EXTERNAL_USER_ID = "externalUserId"
34 |
35 | // Additional data for video tile events
36 | private const val RN_EVENT_KEY_VIDEO_TILE_ID = "tileId"
37 | private const val RN_EVENT_KEY_VIDEO_IS_LOCAL = "isLocal"
38 | private const val RN_EVENT_KEY_VIDEO_IS_SCREEN_SHARE = "isScreenShare"
39 | private const val RN_EVENT_KEY_VIDEO_ATTENDEE_ID = "attendeeId"
40 | private const val RN_EVENT_KEY_VIDEO_PAUSE_STATE = "pauseState"
41 | private const val RN_EVENT_KEY_VIDEO_VIDEO_STREAM_CONTENT_HEIGHT = "videoStreamContentHeight"
42 | private const val RN_EVENT_KEY_VIDEO_VIDEO_STREAM_CONTENT_WIDTH = "videoStreamContentWidth"
43 |
44 | // Additional data for data message
45 | private const val RN_EVENT_KEY_DATA_MESSAGE_DATA = "data"
46 | private const val RN_EVENT_KEY_DATA_MESSAGE_SENDER_ATTENDEE_ID = "senderAttendeeId"
47 | private const val RN_EVENT_KEY_DATA_MESSAGE_SENDER_EXTERNAL_USER_ID = "senderExternalUserId"
48 | private const val RN_EVENT_KEY_DATA_MESSAGE_THROTTLED = "throttled"
49 | private const val RN_EVENT_KEY_DATA_MESSAGE_TIMESTAMP_MS = "timestampMs"
50 | private const val RN_EVENT_KEY_DATA_MESSAGE_TOPIC= "topic"
51 | }
52 |
53 | // Used for events such as attendee join and attendee leave
54 | fun sendAttendeeUpdateEvent(eventName: String, attendeeInfo: AttendeeInfo) {
55 | val map: WritableMap = WritableNativeMap()
56 | map.putString(RN_EVENT_KEY_ATTENDEE_ID, attendeeInfo.attendeeId)
57 | map.putString(RN_EVENT_KEY_EXTERNAL_USER_ID, attendeeInfo.externalUserId)
58 |
59 | sendReactNativeEvent(eventName, map)
60 | }
61 |
62 | fun sendDataMessageEvent(eventName: String, dataMessage: DataMessage) {
63 | val map: WritableMap = WritableNativeMap()
64 | map.putString(RN_EVENT_KEY_DATA_MESSAGE_DATA, dataMessage.text())
65 | map.putString(RN_EVENT_KEY_DATA_MESSAGE_SENDER_ATTENDEE_ID, dataMessage.senderAttendeeId)
66 | map.putString(RN_EVENT_KEY_DATA_MESSAGE_SENDER_EXTERNAL_USER_ID, dataMessage.senderExternalUserId)
67 | map.putBoolean(RN_EVENT_KEY_DATA_MESSAGE_THROTTLED, dataMessage.throttled)
68 | map.putDouble(RN_EVENT_KEY_DATA_MESSAGE_TIMESTAMP_MS, dataMessage.timestampMs.toDouble())
69 | map.putString(RN_EVENT_KEY_DATA_MESSAGE_TOPIC, dataMessage.topic)
70 |
71 | sendReactNativeEvent(eventName, map)
72 | }
73 |
74 | // Used for events such as video tile added and video tile removed
75 | fun sendVideoTileEvent(eventName: String, tileState: VideoTileState) {
76 | val map: WritableMap = WritableNativeMap()
77 | map.putInt(RN_EVENT_KEY_VIDEO_TILE_ID, tileState.tileId)
78 | map.putBoolean(RN_EVENT_KEY_VIDEO_IS_LOCAL, tileState.isLocalTile)
79 | map.putBoolean(RN_EVENT_KEY_VIDEO_IS_SCREEN_SHARE, tileState.isContent)
80 | map.putString(RN_EVENT_KEY_VIDEO_ATTENDEE_ID, tileState.attendeeId)
81 | map.putInt(RN_EVENT_KEY_VIDEO_PAUSE_STATE, tileState.pauseState.ordinal)
82 | map.putInt(RN_EVENT_KEY_VIDEO_VIDEO_STREAM_CONTENT_HEIGHT, tileState.videoStreamContentHeight)
83 | map.putInt(RN_EVENT_KEY_VIDEO_VIDEO_STREAM_CONTENT_WIDTH, tileState.videoStreamContentWidth)
84 | sendReactNativeEvent(eventName, map)
85 | }
86 |
87 | // Used for sending events with non String data (Attendee info, video tile state)
88 | private fun sendReactNativeEvent(eventName: String, data: WritableMap) {
89 | reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
90 | .emit(eventName, data)
91 | }
92 |
93 | // Used for events such as meeting started, meeting ended, and for error messages
94 | fun sendReactNativeEvent(eventName: String, message: String?) {
95 | reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
96 | .emit(eventName, message)
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/ios/RNDemo/MeetingObservers.m:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // SPDX-License-Identifier: MIT-0
4 | //
5 |
6 | #import
7 | #import "MeetingObservers.h"
8 | #import "NativeMobileSDKBridge.h"
9 |
10 | @implementation MeetingObservers
11 | {
12 | NativeMobileSDKBridge* _bridge;
13 | ConsoleLogger * _logger;
14 | }
15 |
16 | - (id)initWithBridge: (NativeMobileSDKBridge *) bridge logger:(ConsoleLogger * )logger
17 | {
18 | _bridge = bridge;
19 | _logger = logger;
20 | return self;
21 | }
22 |
23 | - (void)attendeesDidJoinWithAttendeeInfo:(NSArray * _Nonnull)attendeeInfo
24 | {
25 | for (id currentAttendeeInfo in attendeeInfo)
26 | {
27 | [_bridge sendEventWithName:kEventOnAttendeesJoin body:@{@"attendeeId":[currentAttendeeInfo attendeeId], @"externalUserId":[currentAttendeeInfo externalUserId]}];
28 | [_logger infoWithMsg:[NSString stringWithFormat:@"Attendee %@ join", [currentAttendeeInfo externalUserId]]];
29 | }
30 | }
31 |
32 | - (void)attendeesDidLeaveWithAttendeeInfo:(NSArray * _Nonnull)attendeeInfo
33 | {
34 | for (id currentAttendeeInfo in attendeeInfo)
35 | {
36 | [_bridge sendEventWithName:kEventOnAttendeesLeave body:@{@"attendeeId":[currentAttendeeInfo attendeeId], @"externalUserId":[currentAttendeeInfo externalUserId]}];
37 | [_logger infoWithMsg:[NSString stringWithFormat:@"AttendeeQuit(leave) : %@ ", [currentAttendeeInfo externalUserId]]];
38 | }
39 | }
40 |
41 | - (void)attendeesDidMuteWithAttendeeInfo:(NSArray * _Nonnull)attendeeInfo
42 | {
43 | for (id currentAttendeeInfo in attendeeInfo)
44 | {
45 | [_bridge sendEventWithName:kEventOnAttendeesMute body:[currentAttendeeInfo attendeeId]];
46 | [_logger infoWithMsg:[NSString stringWithFormat:@"Attendee %@ mute", [currentAttendeeInfo externalUserId]]];
47 | }
48 | }
49 |
50 | - (void)attendeesDidUnmuteWithAttendeeInfo:(NSArray * _Nonnull)attendeeInfo
51 | {
52 | for (id currentAttendeeInfo in attendeeInfo)
53 | {
54 | [_bridge sendEventWithName:kEventOnAttendeesUnmute body:[currentAttendeeInfo attendeeId]];
55 | [_logger infoWithMsg:[NSString stringWithFormat:@"Attendee %@ unmute", [currentAttendeeInfo externalUserId]]];
56 | }
57 | }
58 |
59 | - (void)signalStrengthDidChangeWithSignalUpdates:(NSArray * _Nonnull)signalUpdates
60 | {
61 | for (id currentSignalUpdate in signalUpdates)
62 | {
63 | [_logger infoWithMsg:[NSString stringWithFormat:@"Attendee %@ signalStrength changed to %lu", [[currentSignalUpdate attendeeInfo] attendeeId], [currentSignalUpdate signalStrength]]];
64 | }
65 | }
66 |
67 | - (void)volumeDidChangeWithVolumeUpdates:(NSArray * _Nonnull)volumeUpdates
68 | {
69 | for (id currentVolumeUpdate in volumeUpdates)
70 | {
71 | [_logger infoWithMsg:[NSString stringWithFormat:@"Attendee %@ volumeLevel changed to %ld", [[currentVolumeUpdate attendeeInfo] attendeeId], [currentVolumeUpdate volumeLevel]]];
72 | }
73 | }
74 |
75 | - (void)attendeesDidDropWithAttendeeInfo:(NSArray * _Nonnull)attendeeInfo
76 | {
77 | for (id currentAttendeeInfo in attendeeInfo)
78 | {
79 | [_bridge sendEventWithName:kEventOnAttendeesLeave body:@{@"attendeeId":[currentAttendeeInfo attendeeId], @"externalUserId":[currentAttendeeInfo externalUserId]}];
80 | [_logger infoWithMsg:[NSString stringWithFormat:@"AttendeeQuit(drop) : %@ ", [currentAttendeeInfo externalUserId]]];
81 | }
82 | }
83 |
84 | - (void)videoTileDidAddWithTileState:(VideoTileState * _Nonnull)tileState
85 | {
86 | [_bridge sendEventWithName:kEventOnAddVideoTile body:@{@"tileId":[NSNumber numberWithInt: (int)tileState.tileId], @"isLocal":@(tileState.isLocalTile), @"isScreenShare":@(tileState.isContent), @"attendeeId":tileState.attendeeId, @"pauseState":[NSNumber numberWithInt: (int)tileState.pauseState], @"videoStreamContentHeight":[NSNumber numberWithInt: (int)tileState.videoStreamContentHeight], @"videoStreamContentWidth":[NSNumber numberWithInt: (int)tileState.videoStreamContentWidth]}];
87 | }
88 |
89 | - (void)videoTileDidPauseWithTileState:(VideoTileState * _Nonnull)tileState
90 | {
91 | // Not implemented for demo purposes
92 | }
93 |
94 | - (void)videoTileDidRemoveWithTileState:(VideoTileState * _Nonnull)tileState
95 | {
96 | [_bridge sendEventWithName:kEventOnRemoveVideoTile body:@{@"tileId":[NSNumber numberWithInt: (int)tileState.tileId], @"isLocal":@(tileState.isLocalTile), @"isScreenShare":@(tileState.isContent)}];
97 | }
98 |
99 | - (void)videoTileDidResumeWithTileState:(VideoTileState * _Nonnull)tileState
100 | {
101 | // Not implemented for demo purposes
102 | }
103 |
104 | - (void)videoTileSizeDidChangeWithTileState:(VideoTileState * _Nonnull)tileState {
105 | // Not implemented for demo purposes
106 | }
107 |
108 |
109 | - (void)audioSessionDidCancelReconnect
110 | {
111 | // Not implemented for demo purposes
112 | }
113 |
114 | - (void)audioSessionDidStartConnectingWithReconnecting:(BOOL)reconnecting
115 | {
116 | // Not implemented for demo purposes
117 | }
118 |
119 | - (void)audioSessionDidStartWithReconnecting:(BOOL)reconnecting
120 | {
121 | if (!reconnecting)
122 | {
123 | [_logger infoWithMsg:@"Meeting Started!"];
124 | [_bridge sendEventWithName:kEventOnMeetingStart body:nil];
125 | }
126 | }
127 |
128 | - (void)audioSessionDidStopWithStatusWithSessionStatus:(MeetingSessionStatus * _Nonnull)sessionStatus
129 | {
130 | // Not implemented for demo purposes
131 | }
132 |
133 | - (void)connectionDidBecomePoor
134 | {
135 | // Not implemented for demo purposes
136 | }
137 |
138 | - (void)connectionDidRecover
139 | {
140 | // Not implemented for demo purposes
141 | }
142 |
143 | - (void)videoSessionDidStartConnecting
144 | {
145 | // Not implemented for demo purposes
146 | }
147 |
148 | - (void)videoSessionDidStartWithStatusWithSessionStatus:(MeetingSessionStatus * _Nonnull)sessionStatus
149 | {
150 | if (sessionStatus.statusCode == sVideoAtCapacityViewOnly)
151 | {
152 | [_bridge sendEventWithName:kEventOnError body:kErrorEventOnMaximumConcurrentVideoReached];
153 | }
154 | }
155 |
156 | - (void)videoSessionDidStopWithStatusWithSessionStatus:(MeetingSessionStatus * _Nonnull)sessionStatus
157 | {
158 | // Not implemented for demo purposes
159 | }
160 |
161 | - (void)audioSessionDidDrop
162 | {
163 | // Not implemented for demo purposes
164 | }
165 |
166 | - (void)remoteVideoSourcesDidBecomeAvailableWithSources:(NSArray * _Nonnull)sources {
167 | // Not implemented for demo purposes
168 | }
169 |
170 | - (void)remoteVideoSourcesDidBecomeUnavailableWithSources:(NSArray * _Nonnull)sources {
171 | // Not implemented for demo purposes
172 | }
173 |
174 | - (void)cameraSendAvailabilityDidChangeWithAvailable:(BOOL)available {
175 | // Not implemented for demo purposes
176 | }
177 |
178 | - (void)dataMessageDidReceivedWithDataMessage:(DataMessage *)dataMessage {
179 | [_bridge sendEventWithName:kEventOnDataMessageReceive body:@{
180 | @"data":[dataMessage text],
181 | @"topic":[dataMessage topic],
182 | @"senderAttendeeId":[dataMessage senderAttendeeId],
183 | @"senderExternalUserId":[dataMessage senderExternalUserId],
184 | @"throttled":@(dataMessage.throttled),
185 | @"timestampMs":@(dataMessage.timestampMs)
186 | }];
187 | }
188 |
189 |
190 | @end
191 |
192 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/amazonaws/services/chime/rndemo/MeetingObservers.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | * SPDX-License-Identifier: MIT-0
4 | */
5 |
6 | package com.amazonaws.services.chime.rndemo
7 |
8 | import com.amazonaws.services.chime.rndemo.RNEventEmitter.Companion.RN_EVENT_ATTENDEES_JOIN
9 | import com.amazonaws.services.chime.rndemo.RNEventEmitter.Companion.RN_EVENT_ATTENDEES_LEAVE
10 | import com.amazonaws.services.chime.rndemo.RNEventEmitter.Companion.RN_EVENT_ATTENDEES_MUTE
11 | import com.amazonaws.services.chime.rndemo.RNEventEmitter.Companion.RN_EVENT_ATTENDEES_UNMUTE
12 | import com.amazonaws.services.chime.rndemo.RNEventEmitter.Companion.RN_EVENT_VIDEO_TILE_ADD
13 | import com.amazonaws.services.chime.rndemo.RNEventEmitter.Companion.RN_EVENT_VIDEO_TILE_REMOVE
14 | import com.amazonaws.services.chime.sdk.meetings.audiovideo.AttendeeInfo
15 | import com.amazonaws.services.chime.sdk.meetings.audiovideo.AudioVideoObserver
16 | import com.amazonaws.services.chime.sdk.meetings.audiovideo.SignalUpdate
17 | import com.amazonaws.services.chime.sdk.meetings.audiovideo.VolumeUpdate
18 | import com.amazonaws.services.chime.sdk.meetings.audiovideo.video.RemoteVideoSource
19 | import com.amazonaws.services.chime.sdk.meetings.audiovideo.video.VideoTileObserver
20 | import com.amazonaws.services.chime.sdk.meetings.audiovideo.video.VideoTileState
21 | import com.amazonaws.services.chime.sdk.meetings.realtime.RealtimeObserver
22 | import com.amazonaws.services.chime.sdk.meetings.realtime.datamessage.DataMessage
23 | import com.amazonaws.services.chime.sdk.meetings.realtime.datamessage.DataMessageObserver
24 | import com.amazonaws.services.chime.sdk.meetings.session.MeetingSessionStatus
25 | import com.amazonaws.services.chime.sdk.meetings.utils.logger.ConsoleLogger
26 | import com.amazonaws.services.chime.sdk.meetings.utils.logger.LogLevel
27 |
28 | class MeetingObservers(private val eventEmitter: RNEventEmitter) : RealtimeObserver, VideoTileObserver, AudioVideoObserver, DataMessageObserver {
29 | private val logger = ConsoleLogger(LogLevel.DEBUG)
30 | private var isAudioSessionStopped = true
31 | private var isVideoSessionStopped = true
32 | companion object {
33 | private const val TAG = "MeetingObservers"
34 | }
35 |
36 | override fun onAttendeesJoined(attendeeInfo: Array) {
37 | attendeeInfo.forEach {
38 | logger.info(TAG, "Received attendee join event for attendee: ${it.attendeeId} with externalUserId ${it.externalUserId}")
39 | eventEmitter.sendAttendeeUpdateEvent(RN_EVENT_ATTENDEES_JOIN, it)
40 | }
41 | }
42 |
43 | override fun onAttendeesLeft(attendeeInfo: Array) {
44 | attendeeInfo.forEach {
45 | logger.info(TAG, "Received attendee leave event for attendee ${it.attendeeId} with externalUserId ${it.externalUserId}")
46 | eventEmitter.sendAttendeeUpdateEvent(RN_EVENT_ATTENDEES_LEAVE, it)
47 | }
48 | }
49 |
50 | override fun onAttendeesDropped(attendeeInfo: Array) {
51 | attendeeInfo.forEach {
52 | logger.info(TAG, "Received attendee drop event for attendee ${it.attendeeId} with externalUserId ${it.externalUserId}")
53 | eventEmitter.sendAttendeeUpdateEvent(RN_EVENT_ATTENDEES_LEAVE, it)
54 | }
55 | }
56 |
57 | override fun onAttendeesMuted(attendeeInfo: Array) {
58 | attendeeInfo.forEach {
59 | logger.info(TAG, "Received attendee mute event for attendee ${it.attendeeId} with externalUserId ${it.externalUserId}")
60 | eventEmitter.sendReactNativeEvent(RN_EVENT_ATTENDEES_MUTE, it.attendeeId)
61 | }
62 | }
63 |
64 | override fun onAttendeesUnmuted(attendeeInfo: Array) {
65 | attendeeInfo.forEach {
66 | logger.info(TAG, "Received attendee unmute event for attendee ${it.attendeeId} with externalUserId ${it.externalUserId}")
67 | eventEmitter.sendReactNativeEvent(RN_EVENT_ATTENDEES_UNMUTE, it.attendeeId)
68 | }
69 | }
70 |
71 | override fun onSignalStrengthChanged(signalUpdates: Array) {
72 | // Not implemented for demo purposes
73 | }
74 |
75 | override fun onVolumeChanged(volumeUpdates: Array) {
76 | // Not implemented for demo purposes
77 | }
78 |
79 | override fun onVideoTileAdded(tileState: VideoTileState) {
80 | logger.info(TAG, "Received video tile add event for attendee: ${tileState.attendeeId}")
81 | eventEmitter.sendVideoTileEvent(RN_EVENT_VIDEO_TILE_ADD, tileState)
82 | }
83 |
84 | override fun onVideoTilePaused(tileState: VideoTileState) {
85 | // Not implemented for demo purposes
86 | }
87 |
88 | override fun onVideoTileRemoved(tileState: VideoTileState) {
89 | logger.info(TAG, "Received video tile remove event for attendee: ${tileState.attendeeId}")
90 | eventEmitter.sendVideoTileEvent(RN_EVENT_VIDEO_TILE_REMOVE, tileState)
91 | }
92 |
93 | override fun onVideoTileResumed(tileState: VideoTileState) {
94 | // Not implemented for demo purposes
95 | }
96 |
97 | override fun onVideoTileSizeChanged(tileState: VideoTileState) {
98 | // Not implemented for demo purposes
99 | }
100 |
101 | override fun onAudioSessionCancelledReconnect() {
102 | // Not implemented for demo purposes
103 | }
104 |
105 | override fun onAudioSessionDropped() {
106 | // Not implemented for demo purposes
107 | }
108 |
109 | override fun onAudioSessionStarted(reconnecting: Boolean) {
110 | logger.info(TAG, "Received event for audio session started. Reconnecting: $reconnecting")
111 |
112 | if (!reconnecting) {
113 | isAudioSessionStopped = false
114 | isVideoSessionStopped = false
115 | eventEmitter.sendReactNativeEvent(RNEventEmitter.RN_EVENT_MEETING_START, null)
116 | }
117 | }
118 |
119 | override fun onAudioSessionStartedConnecting(reconnecting: Boolean) {
120 | // Not implemented for demo purposes
121 | }
122 |
123 | override fun onAudioSessionStopped(sessionStatus: MeetingSessionStatus) {
124 | // Not implemented for demo purposes
125 | isAudioSessionStopped = true
126 | emitRnMeetingEndEventIfNeeded()
127 | }
128 |
129 | override fun onCameraSendAvailabilityUpdated(available: Boolean) {
130 | // Not implemented for demo purposes
131 | }
132 |
133 | override fun onConnectionBecamePoor() {
134 | // Not implemented for demo purposes
135 | }
136 |
137 | override fun onConnectionRecovered() {
138 | // Not implemented for demo purposes
139 | }
140 |
141 | override fun onVideoSessionStarted(sessionStatus: MeetingSessionStatus) {
142 | // Not implemented for demo purposes
143 | }
144 |
145 | override fun onVideoSessionStartedConnecting() {
146 | // Not implemented for demo purposes
147 | }
148 |
149 | override fun onVideoSessionStopped(sessionStatus: MeetingSessionStatus) {
150 | // Not implemented for demo purposes
151 | isVideoSessionStopped = true
152 | emitRnMeetingEndEventIfNeeded()
153 | }
154 |
155 | override fun onDataMessageReceived(dataMessage: DataMessage) {
156 | eventEmitter.sendDataMessageEvent(RNEventEmitter.RN_EVENT_DATA_MESSAGE_RECEIVE, dataMessage)
157 | }
158 |
159 | override fun onRemoteVideoSourceAvailable(sources: List) {
160 | // Not implemented for demo purposes
161 | }
162 |
163 | override fun onRemoteVideoSourceUnavailable(sources: List) {
164 | // Not implemented for demo purposes
165 | }
166 |
167 | private fun emitRnMeetingEndEventIfNeeded() {
168 | if(isAudioSessionStopped && isVideoSessionStopped) {
169 | eventEmitter.sendReactNativeEvent(RNEventEmitter.RN_EVENT_MEETING_END, null)
170 | }
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/src/containers/Meeting.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | * SPDX-License-Identifier: MIT-0
4 | */
5 |
6 | import React from 'react';
7 | import { View, Text, FlatList, Alert } from 'react-native';
8 | import styles from '../Style';
9 | import { NativeFunction, getSDKEventEmitter, MobileSDKEvent, MeetingError } from '../utils/Bridge';
10 | import { RNVideoRenderView } from '../components/RNVideoRenderView';
11 | import { MuteButton } from '../components/MuteButton';
12 | import { CameraButton } from '../components/CameraButton';
13 | import { HangOffButton } from '../components/HangOffButton';
14 | import { AttendeeItem } from '../components/AttendeeItem';
15 |
16 | // Maps attendee Id to attendee Name
17 | const attendeeNameMap = {};
18 |
19 |
20 | export class Meeting extends React.Component {
21 | constructor() {
22 | super();
23 | this.state = {
24 | attendees: [],
25 | videoTiles: [],
26 | mutedAttendee: [],
27 | selfVideoEnabled: false,
28 | meetingTitle: '',
29 | screenShareTile: null
30 | };
31 | }
32 |
33 | componentDidMount() {
34 | /**
35 | * Attendee Join and Leave handler
36 | */
37 | this.onAttendeesJoinSubscription = getSDKEventEmitter().addListener(MobileSDKEvent.OnAttendeesJoin, ({ attendeeId, externalUserId }) => {
38 | if (!(attendeeId in attendeeNameMap)) {
39 | // The externalUserId will be a format such as c19587e7#Alice
40 | attendeeNameMap[attendeeId] = externalUserId.split("#")[1];
41 | }
42 | this.setState((oldState) => ({
43 | ...oldState,
44 | attendees: oldState.attendees.concat([attendeeId])
45 | }));
46 | });
47 |
48 | this.onAttendeesLeaveSubscription = getSDKEventEmitter().addListener(MobileSDKEvent.OnAttendeesLeave, ({ attendeeId }) => {
49 | this.setState((oldState) => ({
50 | ...oldState,
51 | attendees: oldState.attendees.filter((attendeeToCompare => attendeeId != attendeeToCompare))
52 | }));
53 | });
54 |
55 | /**
56 | * Attendee Mute & Unmute handler
57 | */
58 | this.onAttendeesMuteSubscription = getSDKEventEmitter().addListener(MobileSDKEvent.OnAttendeesMute, attendeeId => {
59 | this.setState((oldState) => ({
60 | ...oldState,
61 | mutedAttendee: oldState.mutedAttendee.concat([attendeeId])
62 | }));
63 | });
64 |
65 | this.onAttendeesUnmuteSubscription = getSDKEventEmitter().addListener(MobileSDKEvent.OnAttendeesUnmute, attendeeId => {
66 | this.setState((oldState) => ({
67 | ...oldState,
68 | mutedAttendee: oldState.mutedAttendee.filter((attendeeToCompare => attendeeId != attendeeToCompare))
69 | }));
70 | });
71 |
72 | /**
73 | * Video tile Add & Remove Handler
74 | */
75 | this.onAddVideoTileSubscription = getSDKEventEmitter().addListener(MobileSDKEvent.OnAddVideoTile, (tileState) => {
76 | if (tileState.isScreenShare) {
77 | this.setState(oldState => ({
78 | ...oldState,
79 | screenShareTile: tileState.tileId
80 | }));
81 | } else {
82 | this.setState(oldState => ({
83 | ...oldState,
84 | videoTiles: [...oldState.videoTiles, tileState.tileId],
85 | selfVideoEnabled: tileState.isLocal ? true : oldState.selfVideoEnabled
86 | }));
87 | }
88 | });
89 |
90 | this.onRemoveVideoTileSubscription = getSDKEventEmitter().addListener(MobileSDKEvent.OnRemoveVideoTile, (tileState) => {
91 | if (tileState.isScreenShare) {
92 | this.setState(oldState => ({
93 | ...oldState,
94 | screenShareTile: null
95 | }));
96 | } else {
97 | this.setState(oldState => ({
98 | ...oldState,
99 | videoTiles: oldState.videoTiles.filter(tileIdToCompare => tileIdToCompare != tileState.tileId),
100 | selfVideoEnabled: tileState.isLocal ? false : oldState.selfVideoEnabled
101 | }));
102 | }
103 |
104 | });
105 |
106 | /**
107 | * Data message handler
108 | */
109 | this.onDataMessageReceivedSubscription = getSDKEventEmitter().addListener(MobileSDKEvent.OnDataMessageReceive, (dataMessage) => {
110 | const str = `Received Data message (topic: ${dataMessage.topic}) ${dataMessage.data} from ${dataMessage.senderAttendeeId}:${dataMessage.senderExternalUserId} at ${dataMessage.timestampMs} throttled: ${dataMessage.throttled}`;
111 | console.log(str);
112 | NativeFunction.sendDataMessage(dataMessage.topic, str, 1000);
113 | })
114 |
115 |
116 | /**
117 | * General Error handler
118 | */
119 | this.onErrorSubscription = getSDKEventEmitter().addListener(MobileSDKEvent.OnError, (errorType) => {
120 | switch(errorType) {
121 | case MeetingError.OnMaximumConcurrentVideoReached:
122 | Alert.alert("Failed to enable video", "maximum number of concurrent videos reached!");
123 | break;
124 | default:
125 | Alert.alert("Error", errorType);
126 | break;
127 | }
128 | });
129 | }
130 |
131 | componentWillUnmount() {
132 | if (this.onAttendeesJoinSubscription) {
133 | this.onAttendeesJoinSubscription.remove();
134 | }
135 | if (this.onAttendeesLeaveSubscription) {
136 | this.onAttendeesLeaveSubscription.remove();
137 | }
138 | if (this.onAttendeesMuteSubscription) {
139 | this.onAttendeesMuteSubscription.remove();
140 | }
141 | if (this.onAttendeesUnmuteSubscription) {
142 | this.onAttendeesUnmuteSubscription.remove();
143 | }
144 | if (this.onAddVideoTileSubscription) {
145 | this.onAddVideoTileSubscription.remove();
146 | }
147 | if (this.onRemoveVideoTileSubscription) {
148 | this.onRemoveVideoTileSubscription.remove();
149 | }
150 | if (this.onDataMessageReceivedSubscription) {
151 | this.onDataMessageReceivedSubscription.remove();
152 | }
153 | if (this.onErrorSubscription) {
154 | this.onErrorSubscription.remove();
155 | }
156 | }
157 |
158 | render() {
159 | const currentMuted = this.state.mutedAttendee.includes(this.props.selfAttendeeId);
160 | return (
161 |
162 | {this.props.meetingTitle}
163 |
164 | NativeFunction.setMute(!currentMuted) }/>
165 | NativeFunction.setCameraOn(!this.state.selfVideoEnabled)}/>
166 | NativeFunction.stopMeeting()} />
167 |
168 | Video
169 |
170 | {
171 | this.state.videoTiles.length > 0 ? this.state.videoTiles.map(tileId =>
172 |
173 | ) : No one is sharing video at this moment
174 | }
175 |
176 | {
177 | !!this.state.screenShareTile &&
178 |
179 | Screen Share
180 |
181 |
182 |
183 |
184 | }
185 | Attendee
186 | }
190 | keyExtractor={(item) => item}
191 | />
192 |
193 | );
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/ios/RNDemo/NativeMobileSDKBridge.m:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // SPDX-License-Identifier: MIT-0
4 | //
5 |
6 | #import "NativeMobileSDKBridge.h"
7 | #import
8 | #import "RNVideoViewManager.h"
9 | #import
10 | #import "MeetingObservers.h"
11 | #import
12 |
13 | @implementation NativeMobileSDKBridge
14 |
15 | static DefaultMeetingSession *meetingSession;
16 | static ConsoleLogger *logger;
17 |
18 | RCT_EXPORT_MODULE();
19 |
20 | - (NSArray *)supportedEvents
21 | {
22 | return
23 | @[
24 | kEventOnMeetingStart,
25 | kEventOnMeetingEnd,
26 | kEventOnAttendeesJoin,
27 | kEventOnAttendeesLeave,
28 | kEventOnAttendeesMute,
29 | kEventOnAttendeesUnmute,
30 | kEventOnAddVideoTile,
31 | kEventOnRemoveVideoTile,
32 | kEventOnDataMessageReceive,
33 | kEventOnError
34 | ];
35 | }
36 |
37 | # pragma mark: Native Function
38 | RCT_EXPORT_METHOD(startMeeting:(NSDictionary *)meetingInfoDict attendeeInfo:(NSDictionary *)attendeeInfoDict)
39 | {
40 | if (meetingSession != nil)
41 | {
42 | [meetingSession.audioVideo stop];
43 | meetingSession = nil;
44 | }
45 |
46 | logger = [[ConsoleLogger alloc] initWithName:@"NativeMobileSDKBridge" level:LogLevelDEFAULT];
47 | [logger infoWithMsg: [[NSString alloc] initWithFormat:@"Running Amazon Chime SDK (%@)", Versioning.sdkVersion]];
48 | // Parse meeting join data from payload
49 | NSDictionary *mediaPlacementDict = [meetingInfoDict objectForKey:kMediaPlacement];
50 |
51 | // Parse meeting info
52 | NSString *meetingId = [meetingInfoDict objectForKey:kMeetingId];
53 | NSString *externalMeetingId = [meetingInfoDict objectForKey:kExternalMeetingId];
54 | NSString *meetingRegion = [meetingInfoDict objectForKey:kMediaRegion];
55 |
56 | // Parse meeting join info
57 | NSString *audioFallbackUrl = [mediaPlacementDict objectForKey:kAudioFallbackUrl];
58 | NSString *audioHostUrl = [mediaPlacementDict objectForKey:kAudioHostUrl];
59 | NSString *turnControlUrl = [mediaPlacementDict objectForKey:kTurnControlUrl];
60 | NSString *signalingUrl = [mediaPlacementDict objectForKey:kSignalingUrl];
61 |
62 | // Parse attendee info
63 | NSString *attendeeId = [attendeeInfoDict objectForKey:kAttendeeId];
64 | NSString *externalUserId = [attendeeInfoDict objectForKey:kExternalUserId];
65 | NSString *joinToken = [attendeeInfoDict objectForKey:kJoinToken];
66 |
67 | // Initialize meeting session through AmazonChimeSDK
68 | MediaPlacement *mediaPlacement = [[MediaPlacement alloc] initWithAudioFallbackUrl:audioFallbackUrl
69 | audioHostUrl:audioHostUrl
70 | signalingUrl:signalingUrl
71 | turnControlUrl:turnControlUrl];
72 |
73 | Meeting *meeting = [[Meeting alloc] initWithExternalMeetingId:externalMeetingId
74 | mediaPlacement:mediaPlacement
75 | mediaRegion:meetingRegion
76 | meetingId:meetingId];
77 |
78 | CreateMeetingResponse *createMeetingResponse = [[CreateMeetingResponse alloc] initWithMeeting:meeting];
79 |
80 | Attendee *attendee = [[Attendee alloc] initWithAttendeeId:attendeeId
81 | externalUserId:externalUserId joinToken:joinToken];
82 |
83 | CreateAttendeeResponse *createAttendeeResponse = [[CreateAttendeeResponse alloc] initWithAttendee:attendee];
84 | MeetingSessionConfiguration *meetingSessionConfiguration = [[MeetingSessionConfiguration alloc] initWithCreateMeetingResponse:createMeetingResponse
85 | createAttendeeResponse:createAttendeeResponse];
86 |
87 | meetingSession = [[DefaultMeetingSession alloc] initWithConfiguration:meetingSessionConfiguration
88 | logger:logger];
89 | [self startAudioClient];
90 | }
91 |
92 | RCT_EXPORT_METHOD(stopMeeting)
93 | {
94 | [meetingSession.audioVideo stop];
95 | meetingSession = nil;
96 | [self sendEventWithName:kEventOnMeetingEnd body: nil];
97 | }
98 |
99 | RCT_EXPORT_METHOD(setMute:(BOOL)isMute)
100 | {
101 | BOOL success = true;
102 | if (isMute)
103 | {
104 | success = [meetingSession.audioVideo realtimeLocalMute];
105 | }
106 | else
107 | {
108 | success = [meetingSession.audioVideo realtimeLocalUnmute];
109 | }
110 |
111 | if (!success)
112 | {
113 | [self sendEventWithName:kEventOnError body:@"Failed to set mute state"];
114 | }
115 | }
116 |
117 | RCT_EXPORT_METHOD(setCameraOn:(BOOL)isOn)
118 | {
119 | if (isOn)
120 | {
121 | [self startVideo];
122 | }
123 | else
124 | {
125 | [meetingSession.audioVideo stopLocalVideo];
126 | }
127 |
128 | }
129 |
130 | RCT_EXPORT_METHOD(bindVideoView:(NSNumber * _Nonnull)viewIdentifier tileId:(NSNumber * _Nonnull)tileId)
131 | {
132 | dispatch_async(dispatch_get_main_queue(), ^{
133 | UIView* view = [self.bridge.uiManager viewForReactTag:viewIdentifier];
134 | if(view != nil) {
135 | [meetingSession.audioVideo bindVideoViewWithVideoView:(DefaultVideoRenderView*)view tileId:[tileId integerValue]];
136 | } else {
137 | [logger errorWithMsg:@"Failed to bind video view"];
138 | }
139 | });
140 | }
141 |
142 | RCT_EXPORT_METHOD(unbindVideoView:(NSNumber * _Nonnull)tileId)
143 | {
144 | dispatch_async(dispatch_get_main_queue(), ^{
145 | [meetingSession.audioVideo unbindVideoViewWithTileId:[tileId integerValue]];
146 | });
147 | }
148 |
149 | #pragma mark: Media Related Function
150 | -(void)startVideo
151 | {
152 | AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
153 | switch (status)
154 | {
155 | case AVAuthorizationStatusNotDetermined:
156 | {
157 | [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
158 | {
159 | if (granted)
160 | { // Access has been granted ..retry starting video
161 | [self startVideo];
162 | } else { // Access denied
163 | [self sendEventWithName:kEventOnError body:@"User denied camera permission"];
164 | }
165 | }];
166 | break;
167 | }
168 | case AVAuthorizationStatusAuthorized:
169 | {
170 | NSError* error;
171 | [meetingSession.audioVideo startLocalVideoAndReturnError:&error];
172 | if(error != nil)
173 | {
174 | [self sendEventWithName:kEventOnError body:@"Fail to start local video"];
175 | }
176 | break;
177 | }
178 | case AVAuthorizationStatusDenied:
179 | {
180 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
181 | break;
182 | }
183 | default:
184 | break;
185 | }
186 | }
187 |
188 | -(void)startAudioClient
189 | {
190 | if (meetingSession == nil)
191 | {
192 | [logger errorWithMsg:@"meetingSession is not initialized"];
193 | return;
194 | }
195 | MeetingObservers* observer = [[MeetingObservers alloc] initWithBridge:self logger:logger];
196 | [meetingSession.audioVideo addRealtimeObserverWithObserver:observer];
197 | [meetingSession.audioVideo addVideoTileObserverWithObserver:observer];
198 | [meetingSession.audioVideo addAudioVideoObserverWithObserver:observer];
199 | [meetingSession.audioVideo addRealtimeDataMessageObserverWithTopic:@"chat" observer:observer];
200 | [self startAudioVideo];
201 | }
202 |
203 | -(void)startAudioVideo
204 | {
205 | NSError* error = nil;
206 | BOOL started = [meetingSession.audioVideo startAndReturnError:&error];
207 | if (started && error == nil)
208 | {
209 | [logger infoWithMsg:@"RN meeting session was started successfully"];
210 |
211 | [meetingSession.audioVideo startRemoteVideo];
212 | }
213 | else
214 | {
215 | NSString *errorMsg = [NSString stringWithFormat:@"Failed to start meeting, error: %@", error.description];
216 | [logger errorWithMsg:errorMsg];
217 |
218 | // Handle missing permission error
219 | if ([error.domain isEqual:@"AmazonChimeSDK.PermissionError"])
220 | {
221 | AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission];
222 | if (permissionStatus == AVAudioSessionRecordPermissionUndetermined)
223 | {
224 | [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted)
225 | {
226 | if (granted)
227 | {
228 | [logger infoWithMsg:@"Audio permission granted"];
229 | // Retry after permission is granted
230 | [self startAudioVideo];
231 | }
232 | else
233 | {
234 | [logger infoWithMsg:@"Audio permission not granted"];
235 | [self sendEventWithName:kEventOnMeetingEnd body:nil];
236 | }
237 | }];
238 | }
239 | else if (permissionStatus == AVAudioSessionRecordPermissionDenied)
240 | {
241 | [logger errorWithMsg:@"User did not grant permission, should redirect to Settings"];
242 | [self sendEventWithName:kEventOnMeetingEnd body:nil];
243 | }
244 | }
245 | else
246 | {
247 | // Uncaught error
248 | [self sendEventWithName:kEventOnError body: errorMsg];
249 | [self sendEventWithName:kEventOnMeetingEnd body:nil];
250 | }
251 | }
252 | }
253 |
254 | RCT_EXPORT_METHOD(sendDataMessage:(NSString* _Nonnull)topic data:(NSString* _Nonnull)data lifetimeMs:(int)lifetimeMs)
255 | {
256 | if (meetingSession == nil) {
257 | return;
258 | }
259 |
260 | [meetingSession.audioVideo realtimeSendDataMessageWithTopic:topic data:data lifetimeMs:lifetimeMs error:nil];
261 | }
262 |
263 | @end
264 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/amazonaws/services/chime/rndemo/NativeMobileSDKBridge.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | * SPDX-License-Identifier: MIT-0
4 | */
5 |
6 | package com.amazonaws.services.chime.rndemo
7 |
8 | import android.Manifest
9 | import android.content.pm.PackageManager
10 | import androidx.core.content.ContextCompat
11 | import com.amazonaws.services.chime.rndemo.RNEventEmitter.Companion.RN_EVENT_ERROR
12 | import com.amazonaws.services.chime.rndemo.RNEventEmitter.Companion.RN_EVENT_MEETING_END
13 | import com.amazonaws.services.chime.sdk.meetings.session.DefaultMeetingSession
14 | import com.amazonaws.services.chime.sdk.meetings.session.MeetingSession
15 | import com.amazonaws.services.chime.sdk.meetings.session.MeetingSessionConfiguration
16 | import com.amazonaws.services.chime.sdk.meetings.session.MeetingSessionCredentials
17 | import com.amazonaws.services.chime.sdk.meetings.session.MeetingSessionURLs
18 | import com.amazonaws.services.chime.sdk.meetings.session.defaultUrlRewriter
19 | import com.amazonaws.services.chime.sdk.meetings.utils.logger.ConsoleLogger
20 | import com.amazonaws.services.chime.sdk.meetings.utils.logger.LogLevel
21 | import com.facebook.react.bridge.ReactApplicationContext
22 | import com.facebook.react.bridge.ReactContextBaseJavaModule
23 | import com.facebook.react.bridge.ReactMethod
24 | import com.facebook.react.bridge.ReadableMap
25 | import com.facebook.react.modules.core.PermissionAwareActivity
26 | import com.facebook.react.modules.core.PermissionListener
27 |
28 | class NativeMobileSDKBridge(
29 | reactContext: ReactApplicationContext,
30 | private val eventEmitter: RNEventEmitter,
31 | private val meetingObservers: MeetingObservers) : ReactContextBaseJavaModule(reactContext), PermissionListener {
32 |
33 | companion object {
34 | private const val WEBRTC_PERMISSION_REQUEST_CODE = 1
35 | private const val TAG = "ChimeReactNativeSDKDemoManager"
36 | private const val KEY_MEETING_ID = "MeetingId"
37 | private const val KEY_ATTENDEE_ID = "AttendeeId"
38 | private const val KEY_JOIN_TOKEN = "JoinToken"
39 | private const val KEY_EXTERNAL_ID = "ExternalUserId"
40 | private const val KEY_MEDIA_PLACEMENT = "MediaPlacement"
41 | private const val KEY_AUDIO_FALLBACK_URL = "AudioFallbackUrl"
42 | private const val KEY_AUDIO_HOST_URL = "AudioHostUrl"
43 | private const val KEY_TURN_CONTROL_URL = "TurnControlUrl"
44 | private const val KEY_SIGNALING_URL = "SignalingUrl"
45 | private const val TOPIC_CHAT = "chat"
46 |
47 | var meetingSession: MeetingSession? = null
48 | }
49 |
50 | private val logger = ConsoleLogger(LogLevel.DEBUG)
51 |
52 | private val webRtcPermissionPermission = arrayOf(
53 | Manifest.permission.MODIFY_AUDIO_SETTINGS,
54 | Manifest.permission.RECORD_AUDIO,
55 | Manifest.permission.CAMERA
56 | )
57 |
58 | override fun getName(): String {
59 | return "NativeMobileSDKBridge"
60 | }
61 |
62 | @ReactMethod
63 | fun startMeeting(meetingInfo: ReadableMap, attendeeInfo: ReadableMap) {
64 | logger.info(TAG, "Called startMeeting")
65 |
66 | currentActivity?.let { activity ->
67 | if (meetingSession != null) {
68 | meetingSession?.audioVideo?.stop()
69 | meetingSession = null
70 | }
71 |
72 | try {
73 | val sessionConfig = createSessionConfiguration(meetingInfo, attendeeInfo)
74 | val meetingSession = sessionConfig?.let {
75 | DefaultMeetingSession(
76 | it,
77 | logger,
78 | activity.applicationContext
79 | )
80 | }
81 |
82 | if (meetingSession != null) {
83 | NativeMobileSDKBridge.meetingSession = meetingSession
84 |
85 | if (!hasPermissionsAlready()) {
86 | val permissionAwareActivity = activity as PermissionAwareActivity
87 | permissionAwareActivity.requestPermissions(webRtcPermissionPermission, WEBRTC_PERMISSION_REQUEST_CODE, this)
88 | return
89 | }
90 |
91 | startAudioVideo()
92 | } else {
93 | logger.error(TAG, "Failed to create meeting session")
94 | eventEmitter.sendReactNativeEvent(RN_EVENT_ERROR, "Failed to create meeting session")
95 | }
96 | } catch (exception: Exception) {
97 | logger.error(TAG, "Error starting the meeting session: ${exception.localizedMessage}")
98 | eventEmitter.sendReactNativeEvent(RN_EVENT_ERROR, "Error starting the meeting session: ${exception.localizedMessage}")
99 | return
100 | }
101 | }
102 | }
103 |
104 | private fun hasPermissionsAlready(): Boolean {
105 | return currentActivity?.let { activity ->
106 | webRtcPermissionPermission.all {
107 | ContextCompat.checkSelfPermission(activity, it) == PackageManager.PERMISSION_GRANTED
108 | }
109 | } ?: false
110 | }
111 |
112 | private fun startAudioVideo() {
113 | meetingSession?.let {
114 | it.audioVideo.addRealtimeObserver(meetingObservers)
115 | it.audioVideo.addVideoTileObserver(meetingObservers)
116 | it.audioVideo.addAudioVideoObserver(meetingObservers)
117 | it.audioVideo.addRealtimeDataMessageObserver(TOPIC_CHAT, meetingObservers)
118 | it.audioVideo.start()
119 | it.audioVideo.startRemoteVideo()
120 | }
121 | }
122 |
123 | private fun createSessionConfiguration(meetingInfo: ReadableMap, attendeeInfo: ReadableMap): MeetingSessionConfiguration? {
124 | return try {
125 | val meetingId = meetingInfo.getString(KEY_MEETING_ID) ?: ""
126 | val attendeeId = attendeeInfo.getString(KEY_ATTENDEE_ID) ?: ""
127 | val joinToken = attendeeInfo.getString(KEY_JOIN_TOKEN) ?: ""
128 | val externalUserId = attendeeInfo.getString(KEY_EXTERNAL_ID) ?: ""
129 | var audioFallbackUrl = ""
130 | var audioHostUrl = ""
131 | var turnControlUrl = ""
132 | var signalingUrl = ""
133 |
134 | meetingInfo.getMap(KEY_MEDIA_PLACEMENT)?.let {
135 | logger.info(TAG, it.toString())
136 | audioFallbackUrl = it.getString(KEY_AUDIO_FALLBACK_URL) ?: ""
137 | audioHostUrl = it.getString(KEY_AUDIO_HOST_URL) ?: ""
138 | turnControlUrl = it.getString(KEY_TURN_CONTROL_URL) ?: ""
139 | signalingUrl = it.getString(KEY_SIGNALING_URL) ?: ""
140 | }
141 |
142 | MeetingSessionConfiguration(meetingId,
143 | MeetingSessionCredentials(attendeeId, externalUserId, joinToken),
144 | MeetingSessionURLs(audioFallbackUrl, audioHostUrl, turnControlUrl, signalingUrl, ::defaultUrlRewriter))
145 | } catch (exception: Exception) {
146 | logger.error(TAG, "Error creating session configuration: ${exception.localizedMessage}")
147 | eventEmitter.sendReactNativeEvent(RN_EVENT_ERROR, "Error creating session configuration: ${exception.localizedMessage}")
148 | null
149 | }
150 | }
151 |
152 | @ReactMethod
153 | fun stopMeeting() {
154 | logger.info(TAG, "Called stopMeeting")
155 |
156 | meetingSession?.audioVideo?.stop()
157 | }
158 |
159 | @ReactMethod
160 | fun setMute(isMute: Boolean) {
161 | logger.info(TAG, "Called setMute: $isMute")
162 |
163 | if (isMute) {
164 | meetingSession?.audioVideo?.realtimeLocalMute()
165 | } else {
166 | meetingSession?.audioVideo?.realtimeLocalUnmute()
167 | }
168 | }
169 |
170 | @ReactMethod
171 | fun setCameraOn(enabled: Boolean) {
172 | logger.info(TAG, "Called setCameraOn: $enabled")
173 |
174 | if (enabled) {
175 | meetingSession?.audioVideo?.startLocalVideo()
176 | } else {
177 | meetingSession?.audioVideo?.stopLocalVideo()
178 | }
179 | }
180 |
181 | @ReactMethod
182 | fun bindVideoView(viewIdentifier: Double, tileId: Int) {
183 | logger.info(TAG, "Called bindVideoView for tileId: $tileId with identifier: $viewIdentifier")
184 | }
185 |
186 | @ReactMethod
187 | fun unbindVideoView(tileId: Int) {
188 | logger.info(TAG, "Called unbindVideoView for tileId: $tileId")
189 |
190 | meetingSession?.run {
191 | audioVideo.unbindVideoView(tileId)
192 | }
193 | }
194 |
195 | @ReactMethod
196 | fun sendDataMessage(topic: String, message: String, lifetimeMs: Int) {
197 | meetingSession?.audioVideo?.realtimeSendDataMessage(topic, message, lifetimeMs)
198 | }
199 |
200 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array?, grantResults: IntArray?): Boolean {
201 | return when (requestCode) {
202 | WEBRTC_PERMISSION_REQUEST_CODE -> {
203 | val isMissingPermission: Boolean =
204 | grantResults?.isEmpty() ?: false || grantResults?.any { PackageManager.PERMISSION_GRANTED != it } ?: false
205 |
206 | if (isMissingPermission) {
207 | eventEmitter.sendReactNativeEvent(RN_EVENT_ERROR, "Unable to start meeting as permissions are not granted")
208 | false
209 | } else {
210 | startAudioVideo()
211 | true
212 | }
213 | }
214 | else -> false
215 | }
216 | }
217 |
218 | // Required for rn built in EventEmitter Calls.
219 | @ReactMethod
220 | fun addListener(eventName: String) {
221 |
222 | }
223 |
224 | @ReactMethod
225 | fun removeListeners(count: Int) {
226 |
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | * SPDX-License-Identifier: MIT-0
4 | */
5 |
6 | apply plugin: "com.android.application"
7 | apply plugin: 'kotlin-android'
8 | apply plugin: 'kotlin-android-extensions'
9 |
10 | import com.android.build.OutputFile
11 |
12 | /**
13 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
14 | * and bundleReleaseJsAndAssets).
15 | * These basically call `react-native bundle` with the correct arguments during the Android build
16 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
17 | * bundle directly from the development server. Below you can see all the possible configurations
18 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
19 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
20 | *
21 | * project.ext.react = [
22 | * // the name of the generated asset file containing your JS bundle
23 | * bundleAssetName: "index.android.bundle",
24 | *
25 | * // the entry file for bundle generation. If none specified and
26 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is
27 | * // default. Can be overridden with ENTRY_FILE environment variable.
28 | * entryFile: "index.android.js",
29 | *
30 | * // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
31 | * bundleCommand: "ram-bundle",
32 | *
33 | * // whether to bundle JS and assets in debug mode
34 | * bundleInDebug: false,
35 | *
36 | * // whether to bundle JS and assets in release mode
37 | * bundleInRelease: true,
38 | *
39 | * // whether to bundle JS and assets in another build variant (if configured).
40 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
41 | * // The configuration property can be in the following formats
42 | * // 'bundleIn${productFlavor}${buildType}'
43 | * // 'bundleIn${buildType}'
44 | * // bundleInFreeDebug: true,
45 | * // bundleInPaidRelease: true,
46 | * // bundleInBeta: true,
47 | *
48 | * // whether to disable dev mode in custom build variants (by default only disabled in release)
49 | * // for example: to disable dev mode in the staging build type (if configured)
50 | * devDisabledInStaging: true,
51 | * // The configuration property can be in the following formats
52 | * // 'devDisabledIn${productFlavor}${buildType}'
53 | * // 'devDisabledIn${buildType}'
54 | *
55 | * // the root of your project, i.e. where "package.json" lives
56 | * root: "../../",
57 | *
58 | * // where to put the JS bundle asset in debug mode
59 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
60 | *
61 | * // where to put the JS bundle asset in release mode
62 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
63 | *
64 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
65 | * // require('./image.png')), in debug mode
66 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
67 | *
68 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
69 | * // require('./image.png')), in release mode
70 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
71 | *
72 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
73 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
74 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
75 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
76 | * // for example, you might want to remove it from here.
77 | * inputExcludes: ["android/**", "ios/**"],
78 | *
79 | * // override which node gets called and with what additional arguments
80 | * nodeExecutableAndArgs: ["node"],
81 | *
82 | * // supply additional arguments to the packager
83 | * extraPackagerArgs: []
84 | * ]
85 | */
86 |
87 | project.ext.react = [
88 | enableHermes: true, // clean and rebuild if changing
89 | ]
90 |
91 | apply from: "../../node_modules/react-native/react.gradle"
92 |
93 | /**
94 | * Set this to true to create two separate APKs instead of one:
95 | * - An APK that only works on ARM devices
96 | * - An APK that only works on x86 devices
97 | * The advantage is the size of the APK is reduced by about 4MB.
98 | * Upload all the APKs to the Play Store and people will download
99 | * the correct one based on the CPU architecture of their device.
100 | */
101 | def enableSeparateBuildPerCPUArchitecture = false
102 |
103 | /**
104 | * Run Proguard to shrink the Java bytecode in release builds.
105 | */
106 | def enableProguardInReleaseBuilds = false
107 |
108 | /**
109 | * The preferred build flavor of JavaScriptCore.
110 | *
111 | * For example, to use the international variant, you can use:
112 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
113 | *
114 | * The international variant includes ICU i18n library and necessary data
115 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
116 | * give correct results when using with locales other than en-US. Note that
117 | * this variant is about 6MiB larger per architecture than default.
118 | */
119 | def jscFlavor = 'org.webkit:android-jsc:+'
120 |
121 | /**
122 | * Architectures to build native code for.
123 | */
124 | def reactNativeArchitectures() {
125 | def value = project.getProperties().get("reactNativeArchitectures")
126 | return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
127 | }
128 |
129 | /**
130 | * Whether to enable the Hermes VM.
131 | *
132 | * This should be set on project.ext.react and mirrored here. If it is not set
133 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
134 | * and the benefits of using Hermes will therefore be sharply reduced.
135 | */
136 | def enableHermes = project.ext.react.get("enableHermes", false);
137 |
138 | android {
139 | ndkVersion rootProject.ext.ndkVersion
140 | compileSdkVersion rootProject.ext.compileSdkVersion
141 |
142 | compileOptions {
143 | sourceCompatibility JavaVersion.VERSION_1_8
144 | targetCompatibility JavaVersion.VERSION_1_8
145 | }
146 |
147 | defaultConfig {
148 | applicationId "com.amazonaws.services.chime.rndemo"
149 | minSdkVersion rootProject.ext.minSdkVersion
150 | targetSdkVersion rootProject.ext.targetSdkVersion
151 | versionCode 1
152 | versionName "1.0"
153 | }
154 |
155 | splits {
156 | abi {
157 | reset()
158 | enable enableSeparateBuildPerCPUArchitecture
159 | universalApk false // If true, also generate a universal APK
160 | include (*reactNativeArchitectures())
161 | }
162 | }
163 | signingConfigs {
164 | debug {
165 | storeFile file('debug.keystore')
166 | storePassword 'android'
167 | keyAlias 'androiddebugkey'
168 | keyPassword 'android'
169 | }
170 | }
171 | packagingOptions {
172 | pickFirst "lib/armeabi-v7a/libc++_shared.so"
173 | pickFirst "lib/arm64-v8a/libc++_shared.so"
174 | pickFirst "lib/x86/libc++_shared.so"
175 | pickFirst "lib/x86_64/libc++_shared.so"
176 | }
177 | buildTypes {
178 | debug {
179 | signingConfig signingConfigs.debug
180 | }
181 | release {
182 | // Caution! In production, you need to generate your own keystore file.
183 | // see https://facebook.github.io/react-native/docs/signed-apk-android.
184 | signingConfig signingConfigs.debug
185 | minifyEnabled enableProguardInReleaseBuilds
186 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
187 | }
188 | }
189 |
190 | // applicationVariants are e.g. debug, release
191 | applicationVariants.all { variant ->
192 | variant.outputs.each { output ->
193 | // For each separate APK per architecture, set a unique version code as described here:
194 | // https://developer.android.com/studio/build/configure-apk-splits.html
195 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
196 | def abi = output.getFilter(OutputFile.ABI)
197 | if (abi != null) { // null for the universal-debug, universal-release variants
198 | output.versionCodeOverride =
199 | defaultConfig.versionCode * 1000 + versionCodes.get(abi)
200 | }
201 |
202 | }
203 | }
204 | }
205 |
206 | dependencies {
207 | implementation fileTree(dir: "libs", include: ["*.jar"])
208 | //noinspection GradleDynamicVersion
209 | implementation "com.facebook.react:react-native:+" // From node_modules
210 |
211 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
212 |
213 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
214 | exclude group:'com.facebook.fbjni'
215 | }
216 |
217 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
218 | exclude group:'com.facebook.flipper'
219 | exclude group:'com.squareup.okhttp3', module:'okhttp'
220 | }
221 |
222 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
223 | exclude group:'com.facebook.flipper'
224 | }
225 |
226 | if (enableHermes) {
227 | //noinspection GradleDynamicVersion
228 | implementation("com.facebook.react:hermes-engine:+") { // From node_modules
229 | exclude group:'com.facebook.fbjni'
230 | }
231 | } else {
232 | implementation jscFlavor
233 | }
234 |
235 | implementation 'software.aws.chimesdk:amazon-chime-sdk:0.17.10'
236 | implementation 'software.aws.chimesdk:amazon-chime-sdk-media:0.17.11'
237 | implementation 'com.google.code.gson:gson:2.9.0'
238 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
239 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
240 | implementation 'androidx.core:core-ktx:1.1.0'
241 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
242 | }
243 |
244 | // Run this once to be able to run the application with BUCK
245 | // puts all compile dependencies into folder libs for BUCK to use
246 | task copyDownloadableDepsToLibs(type: Copy) {
247 | from configurations.implementation
248 | into 'libs'
249 | }
250 |
251 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
252 | repositories {
253 | mavenCentral()
254 | }
255 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Amazon Chime SDK React Native Demo
2 | The Amazon Chime SDK is a set of real-time communications components that developers can use to quickly add audio calling, video calling, and screen sharing capabilities to their own applications. Developers can leverage the same communication infrastructure and services that power Amazon Chime, an online meetings service from AWS, to deliver engaging experiences in their applications. For instance, they can add video calling to a healthcare application so patients can consult remotely with doctors on health issues, or add audio calling to a company website so customers can quickly connect with sales. By using the Amazon Chime SDK, developers can eliminate the cost, complexity, and friction of creating and maintaining their own real-time communication infrastructure and services.
3 |
4 | This demo shows how to integrate the [Amazon Chime SDK](https://aws.amazon.com/blogs/business-productivity/amazon-chime-sdks-ios-android/) into your React Native (or RN) application.
5 |
6 | For more details about the SDK APIs, please refer to the **Getting Started** guide of the following SDK repositories:
7 | * [amazon-chime-sdk-android](https://github.com/aws/amazon-chime-sdk-android/blob/master/guides/01_Getting_Started.md)
8 | * [amazon-chime-sdk-ios](https://github.com/aws/amazon-chime-sdk-ios/blob/master/guides/01_Getting_Started.md)
9 |
10 | > *Note: Deploying the Amazon Chime SDK demo applications contained in this repository will cause your AWS Account to be billed for services, including the Amazon Chime SDK, used by the application.*
11 |
12 | # How to Run the React Native Demo Application
13 | ## 1. Clone the repository
14 | * Run `git clone` to download the source code
15 | * Run `npm install` inside the folder that was just created
16 |
17 | ## 2. Add SDK binaries to the project
18 | ### Android
19 | The Mobile SDKs for Android could be downloaded from the Maven Central repository, by integrated into your Android project's Gradle files, or you can be directly embedded via .aar files.
20 |
21 | #### From Maven
22 | We recommend obtaining the dependency from Maven. To obtain the dependencies from Maven, add the dependencies to `android/app/build.gradle`.
23 |
24 | ```
25 | dependencies {
26 | implementation 'software.aws.chimesdk:amazon-chime-sdk-media:$MEDIA_VERSION'
27 | implementation 'software.aws.chimesdk:amazon-chime-sdk:$SDK_VERSION'
28 | }
29 | ```
30 | The version numbers could be obtained from the [release](https://github.com/aws/amazon-chime-sdk-android/releases).
31 |
32 | #### From S3
33 | You can also download the Mobile SDKs from S3 following these steps:
34 |
35 | * Create a folder named `libs` under `android/app`
36 | * Download the Amazon Chime SDK binaries
37 | - [amazon-chime-sdk-0.17.10.tar.gz](https://amazon-chime-sdk-android.s3.amazonaws.com/sdk/0.17.10/amazon-chime-sdk-0.17.10.tar.gz)
38 | - [amazon-chime-sdk-media-0.17.11.tar.gz](https://amazon-chime-sdk-android.s3.amazonaws.com/media/0.17.11/amazon-chime-sdk-media-0.17.11.tar.gz)
39 | * Unzip, copy both `amazon-chime-sdk.aar` and `amazon-chime-sdk-media.aar` into the `android/app/libs` folder
40 |
41 | ### iOS
42 | #### From CocoaPods
43 | With `pod 'AmazonChimeSDK-Bitcode', '0.22.7'` declared in the `ios/Podfile`, the specified version of `AmazonChimeSDK-Bitcode` pod will be downloaded and integrated when you run `pod install` in the step 5 below.
44 |
45 | `AmazonChimeSDK-No-Bitcode` is available in [Cocoapods](https://cocoapods.org/) as well.
46 |
47 | The current version of Demo app works with AmazonChimeSDK up to `0.22.7`.
48 |
49 | ## 3. Deploy the serverless demo
50 | Follow the instructions in [amazon-chime-sdk-js](https://github.com/aws/amazon-chime-sdk-js/tree/master/demos/serverless) to deploy the serverless demo.
51 |
52 | ## 4. Update the server URLs
53 | Update `SERVER_URL` and `SERVER_REGION` in `src/utils/Api.js` with the server URL and region of the serverless demo you created.
54 |
55 | ## 5. Build and run
56 | Run `npm start` to start the React Native development server.
57 |
58 | ### Android
59 | * Connect a physical Android testing device (*we currently do not support x86 architecture/simulators*) to your computer
60 | * Run `npx react-native run-android` which builds and installs the demo onto the testing device
61 |
62 | * In order to run on single device run `npx react-native run-android --deviceId=`. Device id can be obtained from `adb devices`
63 |
64 | ### iOS
65 | * Run `pod install` in `ios` folder
66 | * Open `ios/RNDemo.xcworkspace` in Xcode
67 | * Select iOS simulator or a testing device, then build and run the demo application
68 |
69 |
70 | # How to Integrate the Amazon Chime SDK into Your Existing React Native Application
71 |
72 | ## 1. Configure your project
73 | Follow the steps below to add the Amazon Chime SDK into your React Native projects for both iOS and Android.
74 |
75 | * [amazon-chime-sdk-android](https://github.com/aws/amazon-chime-sdk-android/blob/master/README.md#setup)
76 | * [amazon-chime-sdk-ios](https://github.com/aws/amazon-chime-sdk-ios/blob/master/README.md#setup)
77 |
78 | ## 2. Request permission
79 | The following camera and microphone permissions need to be granted to enable audio and video functions.
80 |
81 | ### Android
82 | * `android.permission.CAMERA`
83 | * `android.permission.MODIFY_AUDIO_SETTINGS`
84 | * `android.permission.RECORD_AUDIO`
85 |
86 | ### iOS
87 | * `NSCameraUsageDescription`
88 | * `NSMicrophoneUsageDescription`
89 |
90 |
91 | More details on how to request permissions can be found in [Android](https://developer.android.com/training/permissions/requesting) and [iOS](https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_ios?language=objc) official documents.
92 |
93 | ## 3. Add libraries for SDK
94 |
95 | ### Android
96 | You'll need following libraries to be able to build the Android application.
97 | From `build.gradle`
98 | ```
99 | buildscript {
100 | ext {
101 | kotlin_version = '1.3.71'
102 | minSdkVersion = 21
103 | compileSdkVersion = 29
104 | targetSdkVersion = 29
105 | }
106 | dependencies {
107 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
108 | }
109 | allprojects {
110 | repositories {
111 | flatDir {
112 | dirs 'libs'
113 | }
114 | }
115 | }
116 | ```
117 |
118 | From `app/build.gradle`
119 | ```
120 | apply plugin: 'kotlin-android'
121 | apply plugin: 'kotlin-android-extensions'
122 |
123 | dependencies {
124 | implementation(name: 'amazon-chime-sdk', ext: 'aar')
125 | implementation(name: 'amazon-chime-sdk-media', ext: 'aar')
126 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
127 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
128 | implementation 'com.google.code.gson:gson:2.8.6'
129 | }
130 | ```
131 |
132 | ## 4. Connect React Native code to the Amazon Chime SDK
133 |
134 | You need [Native Module](https://reactnative.dev/docs/native-modules-ios) to proxy calls to the Amazon Chime SDK. A Native Module exposes native methods as JavaScript functions to be used in the React Native code. And Amazon Chime SDK callbacks are converted to React Native events that are handled by JavaScript listeners.
135 |
136 | [Android: Send Events to JavaScript](https://reactnative.dev/docs/native-modules-android#sending-events-to-javascript) and [iOS: Send Events to JavaScript](https://reactnative.dev/docs/native-modules-ios#sending-events-to-javascript) provide good examples on how to pass events from native to React Native. You can also look at the following files in our demo application for reference.
137 | * React Native: [Bridge.js](src/utils/Bridge.js)
138 | * iOS: [NativeMobileSDKBridge.h](ios/RNDemo/NativeMobileSDKBridge.h) and [NativeMobileSDKBridge.m](ios/RNDemo/NativeMobileSDKBridge.m)
139 | * Android: [NativeMobileSDKBridge.kt](android/app/src/main/java/com/amazonaws/services/chime/rndemo/NativeMobileSDKBridge.kt) and [RNEventEmitter.kt](android/app/src/main/java/com/amazonaws/services/chime/rndemo/RNEventEmitter.kt)
140 |
141 | To directly reuse code from this demo, here are the files you likely need and main functionality of each file.
142 |
143 | ### React Native
144 | * `src/utils/Bridge.js`: Native Module API calls and the event handler singleton.
145 | * `src/components/RNVideoRenderView.js`: Wrapper for the video tile UI component.
146 |
147 | ### iOS
148 | Add these files to you iOS project through **Xcode**
149 | * `ios/RNDemo/MeetingObserver.h` and `ios/RNDemo/MeetingObserver.m`: Event handlers to pass Amazon Chime SDK events into React Native.
150 | * `ios/RNDemo/NativeMobileSDKBridge.h` and `ios/RNDemo/NativeMobileSDKBridge.m`: Functions that will be available in React Native through Native Module.
151 | * `ios/RNDemo/RNVideoViewManager.h` and `ios/RNDemo/RNVideoViewManager.m`: UI component manager.
152 |
153 | ### Android
154 | The following files are all under `android/app/src/main/java/com/amazonaws/services/chime/rndemo`.
155 | If you are copy pasting the following files, make sure to adjust the package path and import path accordingly at the top of these files.
156 | * `NativeMobileSDKBridge.kt`:Functions that will be available in React Native through Native Module.
157 | * `RNEventEmitter.kt`: Utility class that provides helper to send events to React Native.
158 | * `MeetingObservers.kt`: Event handlers to pass Amazon Chime SDK events into React Native.
159 | * `RNVideoViewManager.kt`: UI component manager.
160 | * `NativeMobileSDKBridgePackage.kt`: Package definition to register the bridge in the Android application.
161 | * `MainApplication.java`: Because **React Native framework already generates this file for you**, you only need to add the following line in function `protected List getPackages()` to register the Native Module.
162 | ~~~kotlin
163 | packages.add(new NativeMobileSDKBridgePackage());
164 | ~~~
165 |
166 | # More Examples in the Demo Application
167 | ## How to start a meeting session
168 |
169 | 1. When the login button is pressed, `startMeeting()` in `Login.js` is called.
170 | 2. After `App.js` completes HTTP request, the meeting response object is passed into `NativeFuntion.startMeeting()`
171 | 3. As defined in `Bridge.js`, the function will call the corresponding native platform code, ochastrated by React Bridge.
172 | - For iOS
173 | ~~~objective-c
174 | // NativeMobileSDKBridge.m
175 | RCT_EXPORT_METHOD(startMeeting:(NSDictionary *)meetingInfoDict attendeeInfo:(NSDictionary *)attendeeInfoDict)
176 | ~~~
177 | - For Android
178 | ~~~kotlin
179 | // ChimeReactnativeSDKDemoManager.kt
180 | @ReactMethod
181 | fun startMeeting(meetingInfo: ReadableMap, attendeeInfo: ReadableMap)
182 | ~~~
183 | 4. In the native code, `startMeeting()` function will do the following.
184 | 1. Construct `MeetingSession`
185 | 2. Bind `AudioVideoObserver` which will listen to the Amazon Chime SDK events
186 | 3. Call `MeetingSession.AudioVideo.start()`
187 | 4. An `onAudioSessionStarted` event will be triggered and handled when the meeting is started
188 | - For iOS
189 | ~~~objective-c
190 | // MeetingObservers.m
191 | - (void)audioSessionDidStartWithReconnecting:(BOOL)reconnecting
192 | {
193 | if (!reconnecting)
194 | {
195 | [_logger infoWithMsg:@"Meeting Started!"];
196 | [_bridge sendEventWithName:kEventOnMeetingStart body:nil];
197 | }
198 | }
199 | ~~~
200 | - For Android
201 | ~~~kotlin
202 | // MeetingObservers.kt
203 | override fun onAudioSessionStarted(reconnecting: Boolean) {
204 | logger.info(TAG, "Received event for audio session started. Reconnecting: $reconnecting")
205 |
206 | if (!reconnecting) {
207 | eventEmitter.sendReactNativeEvent(RNEventEmitter.RN_EVENT_MEETING_START, null)
208 | }
209 | }
210 | ~~~
211 | 5. A [ReactNativeEvent](https://reactnative.dev/docs/native-components-ios#events) called `OnMeetingStart` will be dispatched to the React Native side.
212 | 6. In `App.js`, there is a handler registered for this event.
213 | ~~~javascript
214 | // App.js
215 | this.onMeetingStartSubscription = getSDKEventEmitter().addListener(MobileSDKEvent.OnMeetingStart, () => {
216 | this.setState({ isInMeeting: true });
217 | });
218 | ~~~
219 | 7. After state `isInMeeting` is set to be `true`, user will start to see the meeting view.
220 |
221 | ## About video tile
222 | A customized [Android Native UI Component](https://reactnative.dev/docs/native-components-android) and [iOS Native UI Component](https://reactnative.dev/docs/native-components-ios) is created to render the video tile from the React Native side.
223 |
224 | ### How to bind/unbind the video tile
225 | We need to find the reference of the native view to bind/unbind the video tile.
226 |
227 | #### iOS
228 | This can be done by `[uiManager viewForReactTag:];`. React Native provides a way to find this "tag" by calling `findNodeHandle`. We will pass the tagId as an integer from React Native to Native side.
229 | ~~~javascript
230 | // RNVideoRenderView.js
231 | componentDidMount() {
232 | setTimeout(() => {
233 | NativeFunction.bindVideoView(findNodeHandle(this), this.props.tileId);
234 | });
235 | }
236 | ~~~
237 | ~~~objective-c
238 | RCT_EXPORT_METHOD(bindVideoView:(NSNumber * _Nonnull)viewIdentifier tileId:(NSNumber * _Nonnull)tileId)
239 | {
240 | dispatch_async(dispatch_get_main_queue(), ^{
241 | UIView* view = [self.bridge.uiManager viewForReactTag:viewIdentifier];
242 | [meetingSession.audioVideo bindVideoViewWithVideoView:(DefaultVideoRenderView*)view tileId:[tileId integerValue]];
243 | });
244 | }
245 | ~~~
246 |
247 | #### Android
248 | There is no `viewForReactTag` function available in Android. Therefore, we use [View Prop Setter](https://reactnative.dev/docs/native-components-android#3-expose-view-property-setters-using-reactprop-or-reactpropgroup-annotation) to handle the binding.
249 | ~~~kotlin
250 | // RNVideoViewManager.kt
251 | @ReactProp(name = "tileId")
252 | fun setTileId(renderView: DefaultVideoRenderView, tileId: Int) {
253 | logger.info(TAG, "Setting tileId: $tileId")
254 |
255 | ChimeReactNativeSDKDemoManager.meetingSession?.let {
256 | it.audioVideo.bindVideoView(renderView, tileId)
257 | }
258 | }
259 | ~~~
260 |
261 | ### Potential race condition
262 | If you want to create a video tile and then bind the video immediately, you may run into a race condition. The `bind` function is called before the native UI component is fully initialized.
263 |
264 | #### iOS
265 | To mitigate this race condition, we need to make sure `bind` function is called after UI component is initialized.
266 | ~~~javascript
267 | setTimeout(() => {
268 | NativeFunction.bindVideoView(findNodeHandle(this), this.props.tileId);
269 | });
270 | ~~~
271 |
272 | #### Android
273 | In Android, we use `setTileId()` to bind the video tile. This will solve the problem because `setTileId()` is called after the UI component is initialized. You can read [Direct Manipulation](https://reactnative.dev/docs/direct-manipulation) for more details.
274 |
275 |
276 | # Cleanup
277 |
278 | If you no longer want to keep the demo active in your AWS account and want to avoid incurring AWS charges, the demo resources can be removed. Delete the two AWS CloudFormation (https://aws.amazon.com/cloudformation/) stacks created in the prerequisites that can be found in the AWS CloudFormation console (https://console.aws.amazon.com/cloudformation/home).
279 |
280 |
281 | **Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.**
282 |
--------------------------------------------------------------------------------
/THIRD-PARTY.txt:
--------------------------------------------------------------------------------
1 | ** AndroidKTX; version 1.1.0 -- https://developer.android.com/kotlin/ktx
2 | ** AndroidX Swiperefreshlayout; version 1.0.0 --
3 | https://developer.android.com/jetpack/androidx/releases/swiperefreshlayout
4 | ** Gson; version 2.8.6 -- https://github.com/google/gson
5 | ** Kotlin; version 1.3.71 -- https://github.com/JetBrains/kotlin
6 | ** KotlinStdlibJdk7; version 1.3.71 -- https://github.com/JetBrains/kotlin
7 | ** KotlinxCoroutinesAndroid; version 1.3.3 --
8 | https://github.com/Kotlin/kotlinx.coroutines
9 | ** KotlinxCoroutinesCore; version 1.3.3 --
10 | https://github.com/Kotlin/kotlinx.coroutines
11 |
12 | Apache License
13 |
14 | Version 2.0, January 2004
15 |
16 | http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND
17 | DISTRIBUTION
18 |
19 | 1. Definitions.
20 |
21 | "License" shall mean the terms and conditions for use, reproduction, and
22 | distribution as defined by Sections 1 through 9 of this document.
23 |
24 | "Licensor" shall mean the copyright owner or entity authorized by the
25 | copyright owner that is granting the License.
26 |
27 | "Legal Entity" shall mean the union of the acting entity and all other
28 | entities that control, are controlled by, or are under common control
29 | with that entity. For the purposes of this definition, "control" means
30 | (i) the power, direct or indirect, to cause the direction or management
31 | of such entity, whether by contract or otherwise, or (ii) ownership of
32 | fifty percent (50%) or more of the outstanding shares, or (iii)
33 | beneficial ownership of such entity.
34 |
35 | "You" (or "Your") shall mean an individual or Legal Entity exercising
36 | permissions granted by this License.
37 |
38 | "Source" form shall mean the preferred form for making modifications,
39 | including but not limited to software source code, documentation source,
40 | and configuration files.
41 |
42 | "Object" form shall mean any form resulting from mechanical
43 | transformation or translation of a Source form, including but not limited
44 | to compiled object code, generated documentation, and conversions to
45 | other media types.
46 |
47 | "Work" shall mean the work of authorship, whether in Source or Object
48 | form, made available under the License, as indicated by a copyright
49 | notice that is included in or attached to the work (an example is
50 | provided in the Appendix below).
51 |
52 | "Derivative Works" shall mean any work, whether in Source or Object form,
53 | that is based on (or derived from) the Work and for which the editorial
54 | revisions, annotations, elaborations, or other modifications represent,
55 | as a whole, an original work of authorship. For the purposes of this
56 | License, Derivative Works shall not include works that remain separable
57 | from, or merely link (or bind by name) to the interfaces of, the Work and
58 | Derivative Works thereof.
59 |
60 | "Contribution" shall mean any work of authorship, including the original
61 | version of the Work and any modifications or additions to that Work or
62 | Derivative Works thereof, that is intentionally submitted to Licensor for
63 | inclusion in the Work by the copyright owner or by an individual or Legal
64 | Entity authorized to submit on behalf of the copyright owner. For the
65 | purposes of this definition, "submitted" means any form of electronic,
66 | verbal, or written communication sent to the Licensor or its
67 | representatives, including but not limited to communication on electronic
68 | mailing lists, source code control systems, and issue tracking systems
69 | that are managed by, or on behalf of, the Licensor for the purpose of
70 | discussing and improving the Work, but excluding communication that is
71 | conspicuously marked or otherwise designated in writing by the copyright
72 | owner as "Not a Contribution."
73 |
74 | "Contributor" shall mean Licensor and any individual or Legal Entity on
75 | behalf of whom a Contribution has been received by Licensor and
76 | subsequently incorporated within the Work.
77 |
78 | 2. Grant of Copyright License. Subject to the terms and conditions of this
79 | License, each Contributor hereby grants to You a perpetual, worldwide,
80 | non-exclusive, no-charge, royalty-free, irrevocable copyright license to
81 | reproduce, prepare Derivative Works of, publicly display, publicly perform,
82 | sublicense, and distribute the Work and such Derivative Works in Source or
83 | Object form.
84 |
85 | 3. Grant of Patent License. Subject to the terms and conditions of this
86 | License, each Contributor hereby grants to You a perpetual, worldwide,
87 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in
88 | this section) patent license to make, have made, use, offer to sell, sell,
89 | import, and otherwise transfer the Work, where such license applies only to
90 | those patent claims licensable by such Contributor that are necessarily
91 | infringed by their Contribution(s) alone or by combination of their
92 | Contribution(s) with the Work to which such Contribution(s) was submitted.
93 | If You institute patent litigation against any entity (including a
94 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a
95 | Contribution incorporated within the Work constitutes direct or contributory
96 | patent infringement, then any patent licenses granted to You under this
97 | License for that Work shall terminate as of the date such litigation is
98 | filed.
99 |
100 | 4. Redistribution. You may reproduce and distribute copies of the Work or
101 | Derivative Works thereof in any medium, with or without modifications, and
102 | in Source or Object form, provided that You meet the following conditions:
103 |
104 | (a) You must give any other recipients of the Work or Derivative Works a
105 | copy of this License; and
106 |
107 | (b) You must cause any modified files to carry prominent notices stating
108 | that You changed the files; and
109 |
110 | (c) You must retain, in the Source form of any Derivative Works that You
111 | distribute, all copyright, patent, trademark, and attribution notices
112 | from the Source form of the Work, excluding those notices that do not
113 | pertain to any part of the Derivative Works; and
114 |
115 | (d) If the Work includes a "NOTICE" text file as part of its
116 | distribution, then any Derivative Works that You distribute must include
117 | a readable copy of the attribution notices contained within such NOTICE
118 | file, excluding those notices that do not pertain to any part of the
119 | Derivative Works, in at least one of the following places: within a
120 | NOTICE text file distributed as part of the Derivative Works; within the
121 | Source form or documentation, if provided along with the Derivative
122 | Works; or, within a display generated by the Derivative Works, if and
123 | wherever such third-party notices normally appear. The contents of the
124 | NOTICE file are for informational purposes only and do not modify the
125 | License. You may add Your own attribution notices within Derivative Works
126 | that You distribute, alongside or as an addendum to the NOTICE text from
127 | the Work, provided that such additional attribution notices cannot be
128 | construed as modifying the License.
129 |
130 | You may add Your own copyright statement to Your modifications and may
131 | provide additional or different license terms and conditions for use,
132 | reproduction, or distribution of Your modifications, or for any such
133 | Derivative Works as a whole, provided Your use, reproduction, and
134 | distribution of the Work otherwise complies with the conditions stated in
135 | this License.
136 |
137 | 5. Submission of Contributions. Unless You explicitly state otherwise, any
138 | Contribution intentionally submitted for inclusion in the Work by You to the
139 | Licensor shall be under the terms and conditions of this License, without
140 | any additional terms or conditions. Notwithstanding the above, nothing
141 | herein shall supersede or modify the terms of any separate license agreement
142 | you may have executed with Licensor regarding such Contributions.
143 |
144 | 6. Trademarks. This License does not grant permission to use the trade
145 | names, trademarks, service marks, or product names of the Licensor, except
146 | as required for reasonable and customary use in describing the origin of the
147 | Work and reproducing the content of the NOTICE file.
148 |
149 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
150 | writing, Licensor provides the Work (and each Contributor provides its
151 | Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
152 | KIND, either express or implied, including, without limitation, any
153 | warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or
154 | FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining
155 | the appropriateness of using or redistributing the Work and assume any risks
156 | associated with Your exercise of permissions under this License.
157 |
158 | 8. Limitation of Liability. In no event and under no legal theory, whether
159 | in tort (including negligence), contract, or otherwise, unless required by
160 | applicable law (such as deliberate and grossly negligent acts) or agreed to
161 | in writing, shall any Contributor be liable to You for damages, including
162 | any direct, indirect, special, incidental, or consequential damages of any
163 | character arising as a result of this License or out of the use or inability
164 | to use the Work (including but not limited to damages for loss of goodwill,
165 | work stoppage, computer failure or malfunction, or any and all other
166 | commercial damages or losses), even if such Contributor has been advised of
167 | the possibility of such damages.
168 |
169 | 9. Accepting Warranty or Additional Liability. While redistributing the Work
170 | or Derivative Works thereof, You may choose to offer, and charge a fee for,
171 | acceptance of support, warranty, indemnity, or other liability obligations
172 | and/or rights consistent with this License. However, in accepting such
173 | obligations, You may act only on Your own behalf and on Your sole
174 | responsibility, not on behalf of any other Contributor, and only if You
175 | agree to indemnify, defend, and hold each Contributor harmless for any
176 | liability incurred by, or claims asserted against, such Contributor by
177 | reason of your accepting any such warranty or additional liability. END OF
178 | TERMS AND CONDITIONS
179 |
180 | APPENDIX: How to apply the Apache License to your work.
181 |
182 | To apply the Apache License to your work, attach the following boilerplate
183 | notice, with the fields enclosed by brackets "[]" replaced with your own
184 | identifying information. (Don't include the brackets!) The text should be
185 | enclosed in the appropriate comment syntax for the file format. We also
186 | recommend that a file or class name and description of purpose be included on
187 | the same "printed page" as the copyright notice for easier identification
188 | within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 |
194 | you may not use this file except in compliance with the License.
195 |
196 | You may obtain a copy of the License at
197 |
198 | http://www.apache.org/licenses/LICENSE-2.0
199 |
200 | Unless required by applicable law or agreed to in writing, software
201 |
202 | distributed under the License is distributed on an "AS IS" BASIS,
203 |
204 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
205 |
206 | See the License for the specific language governing permissions and
207 |
208 | limitations under the License.
209 |
210 | * For AndroidKTX see also this required NOTICE:
211 | Copyright (C) 2018 The Android Open Source Project
212 | * For AndroidX Swiperefreshlayout see also this required NOTICE:
213 | Copyright 2018 The Android Open Source Project
214 | * For Gson see also this required NOTICE:
215 | Copyright 2008 Google Inc.
216 | * For Kotlin see also this required NOTICE:
217 | Copyright 2010-2020 JetBrains s.r.o and respective authors and developers
218 | * For KotlinStdlibJdk7 see also this required NOTICE:
219 | Copyright 2010-2020 JetBrains s.r.o and respective authors and developers
220 | * For KotlinxCoroutinesAndroid see also this required NOTICE:
221 | Copyright 2010-2020 JetBrains s.r.o and respective authors and developers
222 | * For KotlinxCoroutinesCore see also this required NOTICE:
223 | Copyright 2016-2019 JetBrains s.r.o and respective authors and developers
224 |
225 | ------
226 |
227 | ** @react-native-community/eslint-config; version 0.0.5 --
228 | https://github.com/facebook/react-native/tree/master/packages/eslint-config-react-native-community#readme
229 | Copyright (c) Facebook, Inc. and its affiliates.
230 | ** babel-jest; version 26.6.3 -- https://github.com/facebook/jest
231 | Copyright (c) Facebook, Inc. and its affiliates.
232 | ** Flipper; version 0.33.1 -- https://fbflipper.com/
233 | Copyright (c) Facebook, Inc. and its affiliates.
234 | ** Hermes; version 0.5.0 -- https://github.com/facebook/hermes
235 | Copyright (c) Facebook, Inc. and its affiliates.
236 | ** jest; version 26.6.3 -- https://jestjs.io/
237 | Copyright (c) Facebook, Inc. and its affiliates.
238 | ** metro-react-native-babel-preset; version 0.58.0 --
239 | https://github.com/facebook/metro
240 | Copyright (c) Facebook, Inc. and its affiliates.
241 | ** react; version 16.11.0 -- https://reactjs.org/
242 | Copyright (c) Facebook, Inc. and its affiliates.
243 | ** react-native; version 0.62.0 -- https://reactnative.dev/
244 | Copyright (c) Facebook, Inc. and its affiliates.
245 | ** react-test-renderer; version 16.11.0 -- https://reactjs.org/
246 | Copyright (c) Facebook, Inc. and its affiliates.
247 |
248 | MIT License
249 |
250 | Copyright (c) Facebook, Inc. and its affiliates.
251 |
252 | Permission is hereby granted, free of charge, to any person obtaining a copy
253 | of this software and associated documentation files (the "Software"), to deal
254 | in the Software without restriction, including without limitation the rights
255 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
256 | copies of the Software, and to permit persons to whom the Software is
257 | furnished to do so, subject to the following conditions:
258 |
259 | The above copyright notice and this permission notice shall be included in all
260 | copies or substantial portions of the Software.
261 |
262 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
263 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
264 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
265 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
266 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
267 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
268 | SOFTWARE.
269 |
270 | ------
271 |
272 | ** @babel/core; version 7.6.2 -- https://babeljs.io/docs/en/babel-core
273 | Copyright (c) 2014-present Sebastian McKenzie and other contributors
274 | ** @babel/runtime; version 7.6.2 --
275 | https://babeljs.io/docs/en/next/babel-runtime
276 | Copyright (c) 2014-present Sebastian McKenzie and other contributors
277 |
278 | MIT License
279 |
280 | Copyright (c) 2014-present Sebastian McKenzie and other contributors
281 |
282 | Permission is hereby granted, free of charge, to any person obtaining
283 | a copy of this software and associated documentation files (the
284 | "Software"), to deal in the Software without restriction, including
285 | without limitation the rights to use, copy, modify, merge, publish,
286 | distribute, sublicense, and/or sell copies of the Software, and to
287 | permit persons to whom the Software is furnished to do so, subject to
288 | the following conditions:
289 |
290 | The above copyright notice and this permission notice shall be
291 | included in all copies or substantial portions of the Software.
292 |
293 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
294 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
295 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
296 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
297 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
298 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
299 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
300 |
301 | ------
302 |
303 | ** eslint; version 6.5.1 -- https://eslint.org/
304 | Copyright JS Foundation and other contributors, https://js.foundation
305 |
306 | Copyright JS Foundation and other contributors, https://js.foundation
307 |
308 | Permission is hereby granted, free of charge, to any person obtaining a copy
309 | of this software and associated documentation files (the "Software"), to deal
310 | in the Software without restriction, including without limitation the rights
311 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
312 | copies of the Software, and to permit persons to whom the Software is
313 | furnished to do so, subject to the following conditions:
314 |
315 | The above copyright notice and this permission notice shall be included in
316 | all copies or substantial portions of the Software.
317 |
318 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
319 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
320 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
321 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
322 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
323 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
324 | THE SOFTWARE.
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - AmazonChimeSDK-Bitcode (0.22.7):
3 | - AmazonChimeSDKMedia-Bitcode (= 0.17.11)
4 | - AmazonChimeSDKMedia-Bitcode (0.17.11)
5 | - boost (1.76.0)
6 | - CocoaAsyncSocket (7.6.5)
7 | - DoubleConversion (1.1.6)
8 | - FBLazyVector (0.69.1)
9 | - FBReactNativeSpec (0.69.1):
10 | - RCT-Folly (= 2021.06.28.00-v2)
11 | - RCTRequired (= 0.69.1)
12 | - RCTTypeSafety (= 0.69.1)
13 | - React-Core (= 0.69.1)
14 | - React-jsi (= 0.69.1)
15 | - ReactCommon/turbomodule/core (= 0.69.1)
16 | - Flipper (0.125.0):
17 | - Flipper-Folly (~> 2.6)
18 | - Flipper-RSocket (~> 1.4)
19 | - Flipper-Boost-iOSX (1.76.0.1.11)
20 | - Flipper-DoubleConversion (3.2.0.1)
21 | - Flipper-Fmt (7.1.7)
22 | - Flipper-Folly (2.6.10):
23 | - Flipper-Boost-iOSX
24 | - Flipper-DoubleConversion
25 | - Flipper-Fmt (= 7.1.7)
26 | - Flipper-Glog
27 | - libevent (~> 2.1.12)
28 | - OpenSSL-Universal (= 1.1.1100)
29 | - Flipper-Glog (0.5.0.5)
30 | - Flipper-PeerTalk (0.0.4)
31 | - Flipper-RSocket (1.4.3):
32 | - Flipper-Folly (~> 2.6)
33 | - FlipperKit (0.125.0):
34 | - FlipperKit/Core (= 0.125.0)
35 | - FlipperKit/Core (0.125.0):
36 | - Flipper (~> 0.125.0)
37 | - FlipperKit/CppBridge
38 | - FlipperKit/FBCxxFollyDynamicConvert
39 | - FlipperKit/FBDefines
40 | - FlipperKit/FKPortForwarding
41 | - SocketRocket (~> 0.6.0)
42 | - FlipperKit/CppBridge (0.125.0):
43 | - Flipper (~> 0.125.0)
44 | - FlipperKit/FBCxxFollyDynamicConvert (0.125.0):
45 | - Flipper-Folly (~> 2.6)
46 | - FlipperKit/FBDefines (0.125.0)
47 | - FlipperKit/FKPortForwarding (0.125.0):
48 | - CocoaAsyncSocket (~> 7.6)
49 | - Flipper-PeerTalk (~> 0.0.4)
50 | - FlipperKit/FlipperKitHighlightOverlay (0.125.0)
51 | - FlipperKit/FlipperKitLayoutHelpers (0.125.0):
52 | - FlipperKit/Core
53 | - FlipperKit/FlipperKitHighlightOverlay
54 | - FlipperKit/FlipperKitLayoutTextSearchable
55 | - FlipperKit/FlipperKitLayoutIOSDescriptors (0.125.0):
56 | - FlipperKit/Core
57 | - FlipperKit/FlipperKitHighlightOverlay
58 | - FlipperKit/FlipperKitLayoutHelpers
59 | - YogaKit (~> 1.18)
60 | - FlipperKit/FlipperKitLayoutPlugin (0.125.0):
61 | - FlipperKit/Core
62 | - FlipperKit/FlipperKitHighlightOverlay
63 | - FlipperKit/FlipperKitLayoutHelpers
64 | - FlipperKit/FlipperKitLayoutIOSDescriptors
65 | - FlipperKit/FlipperKitLayoutTextSearchable
66 | - YogaKit (~> 1.18)
67 | - FlipperKit/FlipperKitLayoutTextSearchable (0.125.0)
68 | - FlipperKit/FlipperKitNetworkPlugin (0.125.0):
69 | - FlipperKit/Core
70 | - FlipperKit/FlipperKitReactPlugin (0.125.0):
71 | - FlipperKit/Core
72 | - FlipperKit/FlipperKitUserDefaultsPlugin (0.125.0):
73 | - FlipperKit/Core
74 | - FlipperKit/SKIOSNetworkPlugin (0.125.0):
75 | - FlipperKit/Core
76 | - FlipperKit/FlipperKitNetworkPlugin
77 | - fmt (6.2.1)
78 | - glog (0.3.5)
79 | - libevent (2.1.12)
80 | - OpenSSL-Universal (1.1.1100)
81 | - RCT-Folly (2021.06.28.00-v2):
82 | - boost
83 | - DoubleConversion
84 | - fmt (~> 6.2.1)
85 | - glog
86 | - RCT-Folly/Default (= 2021.06.28.00-v2)
87 | - RCT-Folly/Default (2021.06.28.00-v2):
88 | - boost
89 | - DoubleConversion
90 | - fmt (~> 6.2.1)
91 | - glog
92 | - RCTRequired (0.69.1)
93 | - RCTTypeSafety (0.69.1):
94 | - FBLazyVector (= 0.69.1)
95 | - RCTRequired (= 0.69.1)
96 | - React-Core (= 0.69.1)
97 | - React (0.69.1):
98 | - React-Core (= 0.69.1)
99 | - React-Core/DevSupport (= 0.69.1)
100 | - React-Core/RCTWebSocket (= 0.69.1)
101 | - React-RCTActionSheet (= 0.69.1)
102 | - React-RCTAnimation (= 0.69.1)
103 | - React-RCTBlob (= 0.69.1)
104 | - React-RCTImage (= 0.69.1)
105 | - React-RCTLinking (= 0.69.1)
106 | - React-RCTNetwork (= 0.69.1)
107 | - React-RCTSettings (= 0.69.1)
108 | - React-RCTText (= 0.69.1)
109 | - React-RCTVibration (= 0.69.1)
110 | - React-bridging (0.69.1):
111 | - RCT-Folly (= 2021.06.28.00-v2)
112 | - React-jsi (= 0.69.1)
113 | - React-callinvoker (0.69.1)
114 | - React-Codegen (0.69.1):
115 | - FBReactNativeSpec (= 0.69.1)
116 | - RCT-Folly (= 2021.06.28.00-v2)
117 | - RCTRequired (= 0.69.1)
118 | - RCTTypeSafety (= 0.69.1)
119 | - React-Core (= 0.69.1)
120 | - React-jsi (= 0.69.1)
121 | - React-jsiexecutor (= 0.69.1)
122 | - ReactCommon/turbomodule/core (= 0.69.1)
123 | - React-Core (0.69.1):
124 | - glog
125 | - RCT-Folly (= 2021.06.28.00-v2)
126 | - React-Core/Default (= 0.69.1)
127 | - React-cxxreact (= 0.69.1)
128 | - React-jsi (= 0.69.1)
129 | - React-jsiexecutor (= 0.69.1)
130 | - React-perflogger (= 0.69.1)
131 | - Yoga
132 | - React-Core/CoreModulesHeaders (0.69.1):
133 | - glog
134 | - RCT-Folly (= 2021.06.28.00-v2)
135 | - React-Core/Default
136 | - React-cxxreact (= 0.69.1)
137 | - React-jsi (= 0.69.1)
138 | - React-jsiexecutor (= 0.69.1)
139 | - React-perflogger (= 0.69.1)
140 | - Yoga
141 | - React-Core/Default (0.69.1):
142 | - glog
143 | - RCT-Folly (= 2021.06.28.00-v2)
144 | - React-cxxreact (= 0.69.1)
145 | - React-jsi (= 0.69.1)
146 | - React-jsiexecutor (= 0.69.1)
147 | - React-perflogger (= 0.69.1)
148 | - Yoga
149 | - React-Core/DevSupport (0.69.1):
150 | - glog
151 | - RCT-Folly (= 2021.06.28.00-v2)
152 | - React-Core/Default (= 0.69.1)
153 | - React-Core/RCTWebSocket (= 0.69.1)
154 | - React-cxxreact (= 0.69.1)
155 | - React-jsi (= 0.69.1)
156 | - React-jsiexecutor (= 0.69.1)
157 | - React-jsinspector (= 0.69.1)
158 | - React-perflogger (= 0.69.1)
159 | - Yoga
160 | - React-Core/RCTActionSheetHeaders (0.69.1):
161 | - glog
162 | - RCT-Folly (= 2021.06.28.00-v2)
163 | - React-Core/Default
164 | - React-cxxreact (= 0.69.1)
165 | - React-jsi (= 0.69.1)
166 | - React-jsiexecutor (= 0.69.1)
167 | - React-perflogger (= 0.69.1)
168 | - Yoga
169 | - React-Core/RCTAnimationHeaders (0.69.1):
170 | - glog
171 | - RCT-Folly (= 2021.06.28.00-v2)
172 | - React-Core/Default
173 | - React-cxxreact (= 0.69.1)
174 | - React-jsi (= 0.69.1)
175 | - React-jsiexecutor (= 0.69.1)
176 | - React-perflogger (= 0.69.1)
177 | - Yoga
178 | - React-Core/RCTBlobHeaders (0.69.1):
179 | - glog
180 | - RCT-Folly (= 2021.06.28.00-v2)
181 | - React-Core/Default
182 | - React-cxxreact (= 0.69.1)
183 | - React-jsi (= 0.69.1)
184 | - React-jsiexecutor (= 0.69.1)
185 | - React-perflogger (= 0.69.1)
186 | - Yoga
187 | - React-Core/RCTImageHeaders (0.69.1):
188 | - glog
189 | - RCT-Folly (= 2021.06.28.00-v2)
190 | - React-Core/Default
191 | - React-cxxreact (= 0.69.1)
192 | - React-jsi (= 0.69.1)
193 | - React-jsiexecutor (= 0.69.1)
194 | - React-perflogger (= 0.69.1)
195 | - Yoga
196 | - React-Core/RCTLinkingHeaders (0.69.1):
197 | - glog
198 | - RCT-Folly (= 2021.06.28.00-v2)
199 | - React-Core/Default
200 | - React-cxxreact (= 0.69.1)
201 | - React-jsi (= 0.69.1)
202 | - React-jsiexecutor (= 0.69.1)
203 | - React-perflogger (= 0.69.1)
204 | - Yoga
205 | - React-Core/RCTNetworkHeaders (0.69.1):
206 | - glog
207 | - RCT-Folly (= 2021.06.28.00-v2)
208 | - React-Core/Default
209 | - React-cxxreact (= 0.69.1)
210 | - React-jsi (= 0.69.1)
211 | - React-jsiexecutor (= 0.69.1)
212 | - React-perflogger (= 0.69.1)
213 | - Yoga
214 | - React-Core/RCTSettingsHeaders (0.69.1):
215 | - glog
216 | - RCT-Folly (= 2021.06.28.00-v2)
217 | - React-Core/Default
218 | - React-cxxreact (= 0.69.1)
219 | - React-jsi (= 0.69.1)
220 | - React-jsiexecutor (= 0.69.1)
221 | - React-perflogger (= 0.69.1)
222 | - Yoga
223 | - React-Core/RCTTextHeaders (0.69.1):
224 | - glog
225 | - RCT-Folly (= 2021.06.28.00-v2)
226 | - React-Core/Default
227 | - React-cxxreact (= 0.69.1)
228 | - React-jsi (= 0.69.1)
229 | - React-jsiexecutor (= 0.69.1)
230 | - React-perflogger (= 0.69.1)
231 | - Yoga
232 | - React-Core/RCTVibrationHeaders (0.69.1):
233 | - glog
234 | - RCT-Folly (= 2021.06.28.00-v2)
235 | - React-Core/Default
236 | - React-cxxreact (= 0.69.1)
237 | - React-jsi (= 0.69.1)
238 | - React-jsiexecutor (= 0.69.1)
239 | - React-perflogger (= 0.69.1)
240 | - Yoga
241 | - React-Core/RCTWebSocket (0.69.1):
242 | - glog
243 | - RCT-Folly (= 2021.06.28.00-v2)
244 | - React-Core/Default (= 0.69.1)
245 | - React-cxxreact (= 0.69.1)
246 | - React-jsi (= 0.69.1)
247 | - React-jsiexecutor (= 0.69.1)
248 | - React-perflogger (= 0.69.1)
249 | - Yoga
250 | - React-CoreModules (0.69.1):
251 | - RCT-Folly (= 2021.06.28.00-v2)
252 | - RCTTypeSafety (= 0.69.1)
253 | - React-Codegen (= 0.69.1)
254 | - React-Core/CoreModulesHeaders (= 0.69.1)
255 | - React-jsi (= 0.69.1)
256 | - React-RCTImage (= 0.69.1)
257 | - ReactCommon/turbomodule/core (= 0.69.1)
258 | - React-cxxreact (0.69.1):
259 | - boost (= 1.76.0)
260 | - DoubleConversion
261 | - glog
262 | - RCT-Folly (= 2021.06.28.00-v2)
263 | - React-callinvoker (= 0.69.1)
264 | - React-jsi (= 0.69.1)
265 | - React-jsinspector (= 0.69.1)
266 | - React-logger (= 0.69.1)
267 | - React-perflogger (= 0.69.1)
268 | - React-runtimeexecutor (= 0.69.1)
269 | - React-jsi (0.69.1):
270 | - boost (= 1.76.0)
271 | - DoubleConversion
272 | - glog
273 | - RCT-Folly (= 2021.06.28.00-v2)
274 | - React-jsi/Default (= 0.69.1)
275 | - React-jsi/Default (0.69.1):
276 | - boost (= 1.76.0)
277 | - DoubleConversion
278 | - glog
279 | - RCT-Folly (= 2021.06.28.00-v2)
280 | - React-jsiexecutor (0.69.1):
281 | - DoubleConversion
282 | - glog
283 | - RCT-Folly (= 2021.06.28.00-v2)
284 | - React-cxxreact (= 0.69.1)
285 | - React-jsi (= 0.69.1)
286 | - React-perflogger (= 0.69.1)
287 | - React-jsinspector (0.69.1)
288 | - React-logger (0.69.1):
289 | - glog
290 | - React-perflogger (0.69.1)
291 | - React-RCTActionSheet (0.69.1):
292 | - React-Core/RCTActionSheetHeaders (= 0.69.1)
293 | - React-RCTAnimation (0.69.1):
294 | - RCT-Folly (= 2021.06.28.00-v2)
295 | - RCTTypeSafety (= 0.69.1)
296 | - React-Codegen (= 0.69.1)
297 | - React-Core/RCTAnimationHeaders (= 0.69.1)
298 | - React-jsi (= 0.69.1)
299 | - ReactCommon/turbomodule/core (= 0.69.1)
300 | - React-RCTBlob (0.69.1):
301 | - RCT-Folly (= 2021.06.28.00-v2)
302 | - React-Codegen (= 0.69.1)
303 | - React-Core/RCTBlobHeaders (= 0.69.1)
304 | - React-Core/RCTWebSocket (= 0.69.1)
305 | - React-jsi (= 0.69.1)
306 | - React-RCTNetwork (= 0.69.1)
307 | - ReactCommon/turbomodule/core (= 0.69.1)
308 | - React-RCTImage (0.69.1):
309 | - RCT-Folly (= 2021.06.28.00-v2)
310 | - RCTTypeSafety (= 0.69.1)
311 | - React-Codegen (= 0.69.1)
312 | - React-Core/RCTImageHeaders (= 0.69.1)
313 | - React-jsi (= 0.69.1)
314 | - React-RCTNetwork (= 0.69.1)
315 | - ReactCommon/turbomodule/core (= 0.69.1)
316 | - React-RCTLinking (0.69.1):
317 | - React-Codegen (= 0.69.1)
318 | - React-Core/RCTLinkingHeaders (= 0.69.1)
319 | - React-jsi (= 0.69.1)
320 | - ReactCommon/turbomodule/core (= 0.69.1)
321 | - React-RCTNetwork (0.69.1):
322 | - RCT-Folly (= 2021.06.28.00-v2)
323 | - RCTTypeSafety (= 0.69.1)
324 | - React-Codegen (= 0.69.1)
325 | - React-Core/RCTNetworkHeaders (= 0.69.1)
326 | - React-jsi (= 0.69.1)
327 | - ReactCommon/turbomodule/core (= 0.69.1)
328 | - React-RCTSettings (0.69.1):
329 | - RCT-Folly (= 2021.06.28.00-v2)
330 | - RCTTypeSafety (= 0.69.1)
331 | - React-Codegen (= 0.69.1)
332 | - React-Core/RCTSettingsHeaders (= 0.69.1)
333 | - React-jsi (= 0.69.1)
334 | - ReactCommon/turbomodule/core (= 0.69.1)
335 | - React-RCTText (0.69.1):
336 | - React-Core/RCTTextHeaders (= 0.69.1)
337 | - React-RCTVibration (0.69.1):
338 | - RCT-Folly (= 2021.06.28.00-v2)
339 | - React-Codegen (= 0.69.1)
340 | - React-Core/RCTVibrationHeaders (= 0.69.1)
341 | - React-jsi (= 0.69.1)
342 | - ReactCommon/turbomodule/core (= 0.69.1)
343 | - React-runtimeexecutor (0.69.1):
344 | - React-jsi (= 0.69.1)
345 | - ReactCommon/turbomodule/core (0.69.1):
346 | - DoubleConversion
347 | - glog
348 | - RCT-Folly (= 2021.06.28.00-v2)
349 | - React-bridging (= 0.69.1)
350 | - React-callinvoker (= 0.69.1)
351 | - React-Core (= 0.69.1)
352 | - React-cxxreact (= 0.69.1)
353 | - React-jsi (= 0.69.1)
354 | - React-logger (= 0.69.1)
355 | - React-perflogger (= 0.69.1)
356 | - SocketRocket (0.6.0)
357 | - Yoga (1.14.0)
358 | - YogaKit (1.18.1):
359 | - Yoga (~> 1.14)
360 |
361 | DEPENDENCIES:
362 | - AmazonChimeSDK-Bitcode (= 0.22.7)
363 | - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
364 | - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
365 | - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
366 | - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
367 | - Flipper (= 0.125.0)
368 | - Flipper-Boost-iOSX (= 1.76.0.1.11)
369 | - Flipper-DoubleConversion (= 3.2.0.1)
370 | - Flipper-Fmt (= 7.1.7)
371 | - Flipper-Folly (= 2.6.10)
372 | - Flipper-Glog (= 0.5.0.5)
373 | - Flipper-PeerTalk (= 0.0.4)
374 | - Flipper-RSocket (= 1.4.3)
375 | - FlipperKit (= 0.125.0)
376 | - FlipperKit/Core (= 0.125.0)
377 | - FlipperKit/CppBridge (= 0.125.0)
378 | - FlipperKit/FBCxxFollyDynamicConvert (= 0.125.0)
379 | - FlipperKit/FBDefines (= 0.125.0)
380 | - FlipperKit/FKPortForwarding (= 0.125.0)
381 | - FlipperKit/FlipperKitHighlightOverlay (= 0.125.0)
382 | - FlipperKit/FlipperKitLayoutPlugin (= 0.125.0)
383 | - FlipperKit/FlipperKitLayoutTextSearchable (= 0.125.0)
384 | - FlipperKit/FlipperKitNetworkPlugin (= 0.125.0)
385 | - FlipperKit/FlipperKitReactPlugin (= 0.125.0)
386 | - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.125.0)
387 | - FlipperKit/SKIOSNetworkPlugin (= 0.125.0)
388 | - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
389 | - OpenSSL-Universal (= 1.1.1100)
390 | - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
391 | - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
392 | - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
393 | - React (from `../node_modules/react-native/`)
394 | - React-bridging (from `../node_modules/react-native/ReactCommon`)
395 | - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
396 | - React-Codegen (from `build/generated/ios`)
397 | - React-Core (from `../node_modules/react-native/`)
398 | - React-Core/DevSupport (from `../node_modules/react-native/`)
399 | - React-Core/RCTWebSocket (from `../node_modules/react-native/`)
400 | - React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
401 | - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
402 | - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
403 | - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
404 | - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
405 | - React-logger (from `../node_modules/react-native/ReactCommon/logger`)
406 | - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
407 | - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
408 | - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
409 | - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
410 | - React-RCTImage (from `../node_modules/react-native/Libraries/Image`)
411 | - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`)
412 | - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`)
413 | - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
414 | - React-RCTText (from `../node_modules/react-native/Libraries/Text`)
415 | - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
416 | - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
417 | - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
418 | - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
419 |
420 | SPEC REPOS:
421 | trunk:
422 | - AmazonChimeSDK-Bitcode
423 | - AmazonChimeSDKMedia-Bitcode
424 | - CocoaAsyncSocket
425 | - Flipper
426 | - Flipper-Boost-iOSX
427 | - Flipper-DoubleConversion
428 | - Flipper-Fmt
429 | - Flipper-Folly
430 | - Flipper-Glog
431 | - Flipper-PeerTalk
432 | - Flipper-RSocket
433 | - FlipperKit
434 | - fmt
435 | - libevent
436 | - OpenSSL-Universal
437 | - SocketRocket
438 | - YogaKit
439 |
440 | EXTERNAL SOURCES:
441 | boost:
442 | :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
443 | DoubleConversion:
444 | :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
445 | FBLazyVector:
446 | :path: "../node_modules/react-native/Libraries/FBLazyVector"
447 | FBReactNativeSpec:
448 | :path: "../node_modules/react-native/React/FBReactNativeSpec"
449 | glog:
450 | :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
451 | RCT-Folly:
452 | :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
453 | RCTRequired:
454 | :path: "../node_modules/react-native/Libraries/RCTRequired"
455 | RCTTypeSafety:
456 | :path: "../node_modules/react-native/Libraries/TypeSafety"
457 | React:
458 | :path: "../node_modules/react-native/"
459 | React-bridging:
460 | :path: "../node_modules/react-native/ReactCommon"
461 | React-callinvoker:
462 | :path: "../node_modules/react-native/ReactCommon/callinvoker"
463 | React-Codegen:
464 | :path: build/generated/ios
465 | React-Core:
466 | :path: "../node_modules/react-native/"
467 | React-CoreModules:
468 | :path: "../node_modules/react-native/React/CoreModules"
469 | React-cxxreact:
470 | :path: "../node_modules/react-native/ReactCommon/cxxreact"
471 | React-jsi:
472 | :path: "../node_modules/react-native/ReactCommon/jsi"
473 | React-jsiexecutor:
474 | :path: "../node_modules/react-native/ReactCommon/jsiexecutor"
475 | React-jsinspector:
476 | :path: "../node_modules/react-native/ReactCommon/jsinspector"
477 | React-logger:
478 | :path: "../node_modules/react-native/ReactCommon/logger"
479 | React-perflogger:
480 | :path: "../node_modules/react-native/ReactCommon/reactperflogger"
481 | React-RCTActionSheet:
482 | :path: "../node_modules/react-native/Libraries/ActionSheetIOS"
483 | React-RCTAnimation:
484 | :path: "../node_modules/react-native/Libraries/NativeAnimation"
485 | React-RCTBlob:
486 | :path: "../node_modules/react-native/Libraries/Blob"
487 | React-RCTImage:
488 | :path: "../node_modules/react-native/Libraries/Image"
489 | React-RCTLinking:
490 | :path: "../node_modules/react-native/Libraries/LinkingIOS"
491 | React-RCTNetwork:
492 | :path: "../node_modules/react-native/Libraries/Network"
493 | React-RCTSettings:
494 | :path: "../node_modules/react-native/Libraries/Settings"
495 | React-RCTText:
496 | :path: "../node_modules/react-native/Libraries/Text"
497 | React-RCTVibration:
498 | :path: "../node_modules/react-native/Libraries/Vibration"
499 | React-runtimeexecutor:
500 | :path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
501 | ReactCommon:
502 | :path: "../node_modules/react-native/ReactCommon"
503 | Yoga:
504 | :path: "../node_modules/react-native/ReactCommon/yoga"
505 |
506 | SPEC CHECKSUMS:
507 | AmazonChimeSDK-Bitcode: 0504362c4260b0229f2fac0cd9edf9b3c6191d22
508 | AmazonChimeSDKMedia-Bitcode: 8b5a340ecf4d4802a2875b872f0d06e6c6fab8c6
509 | boost: a7c83b31436843459a1961bfd74b96033dc77234
510 | CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
511 | DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
512 | FBLazyVector: 068141206af867f72854753423d0117c4bf53419
513 | FBReactNativeSpec: 546a637adc797fa436dd51d1c63c580f820de31c
514 | Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0
515 | Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
516 | Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30
517 | Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
518 | Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3
519 | Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446
520 | Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
521 | Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541
522 | FlipperKit: cbdee19bdd4e7f05472a66ce290f1b729ba3cb86
523 | fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
524 | glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a
525 | libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
526 | OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
527 | RCT-Folly: b9d9fe1fc70114b751c076104e52f3b1b5e5a95a
528 | RCTRequired: ae07282b2ec9c90d7eb98251603bc3f82403d239
529 | RCTTypeSafety: a04dc1339af2e1da759ccd093bf11c310dce1ef6
530 | React: dbd201f781b180eab148aa961683943c72f67dcf
531 | React-bridging: 10a863fdc0fc6f9c9f8527640936b293cd288bdc
532 | React-callinvoker: 6ad32eee2630dab9023de5df2a6a8cacbfc99a67
533 | React-Codegen: fe3423fa6f37d05e233ab0e85e34fe0b443a5654
534 | React-Core: 6177b1f2dd794fe202a5042d3678b2ddfcbfb7d4
535 | React-CoreModules: c74e6b155f9876b1947fc8a13f0cb437cc7f6dcd
536 | React-cxxreact: a07b7d90c4c71dd38c7383c7344b34d0a1336aee
537 | React-jsi: d762c410d10830b7579225c78f2fd881c29649ca
538 | React-jsiexecutor: 758e70947c232828a66b5ddc42d02b4d010fa26e
539 | React-jsinspector: 55605caf04e02f9b0e05842b786f1c12dde08f4b
540 | React-logger: ca970551cb7eea2fd814d0d5f6fc1a471eb53b76
541 | React-perflogger: c9161ff0f1c769993cd11d2751e4331ff4ceb7cd
542 | React-RCTActionSheet: 2d885b0bea76a5254ef852939273edd8de116180
543 | React-RCTAnimation: 353fa4fc3c19060068832dd32e555182ec07be45
544 | React-RCTBlob: 647da863bc7d4f169bb80463fbcdd59c4fc76e6a
545 | React-RCTImage: e77ee8d85f21ad5f4704e3ef67656903f45f9e76
546 | React-RCTLinking: 3dad213f5ef5798b9491037aebe84e8ad684d4c4
547 | React-RCTNetwork: ebbb9581d8fdc91596a4ee5e9f9ae37d5f1e13b9
548 | React-RCTSettings: a5e7f3f1d1b38be8bf9baa89228c5af98244f9ee
549 | React-RCTText: 209576913f7eccd84425ea3f3813772f1f66e1e4
550 | React-RCTVibration: e8b7dd6635cc95689b5db643b5a3848f1e05b30b
551 | React-runtimeexecutor: 27f468c5576eaf05ffb7a907528e44c75a3fcbae
552 | ReactCommon: e30ec17dfb1d4c4f3419eac254350d6abca6d5a2
553 | SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
554 | Yoga: 7ab6e3ee4ce47d7b789d1cb520163833e515f452
555 | YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
556 |
557 | PODFILE CHECKSUM: bf1e325fd31bff21823e860df489dc874ed9f84d
558 |
559 | COCOAPODS: 1.11.2
560 |
--------------------------------------------------------------------------------