diff --git a/.circleci/config.yml b/.circleci/config.yml index 662223bc..bf2b8c8f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -377,10 +377,7 @@ jobs: - app-install-dependencies - app-save-google-services-json - android/install-ndk: - version: "21.4.7075529" - # for aarch64 - - android/install-ndk: - version: "24.0.8215888" + version: "25.2.9519653" - app-android-build app-detox-android: diff --git a/CHANGELOG.md b/CHANGELOG.md index 93b567ab..b03f2561 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ ### Changes * Refactored and simplified navigators. Now, movement between screens is more explicit and has finer control. -* Upgraded Twilio Voice SDK to `1.0.0-rc8`. +* Upgraded Twilio Voice SDK to `1.0.0-rc17`. +* Refactored Android implementation to use newly refactored Android Voice React Native SDK ### Fixes * iOS call timer, no longer shows negative numbers diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index f933e0f8..92ff696e 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -4,7 +4,6 @@ apply plugin: "com.android.application" apply plugin: 'com.google.gms.google-services' import com.android.build.OutputFile -import org.apache.tools.ant.taskdefs.condition.Os /** * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets @@ -262,15 +261,11 @@ android { dependencies { androidTestImplementation('com.wix:detox:+') - implementation 'androidx.appcompat:appcompat:1.1.0' implementation fileTree(dir: "libs", include: ["*.jar"]) - //noinspection GradleDynamicVersion - implementation "com.facebook.react:react-native:+" // From node_modules - + implementation "com.facebook.react:react-native:+" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" - implementation "com.microsoft.appcenter:appcenter-distribute:4.3.1" debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { @@ -339,7 +334,7 @@ def getLocalPropertyOrEnv(key) { return return_value; } -def stringifyNull(var) { +static def stringifyNull(var) { if (null == var) { return "\"null\"" } diff --git a/app/android/app/src/androidTest/java/com/twiliovoicereactnativereferenceapp/DetoxTest.java b/app/android/app/src/androidTest/java/com/twiliovoicereactnativereferenceapp/DetoxTest.java index f9c6a78a..e503a144 100644 --- a/app/android/app/src/androidTest/java/com/twiliovoicereactnativereferenceapp/DetoxTest.java +++ b/app/android/app/src/androidTest/java/com/twiliovoicereactnativereferenceapp/DetoxTest.java @@ -1,5 +1,8 @@ package com.twiliovoicereactnativereferenceapp; +import android.Manifest; +import android.os.Build; + import com.wix.detox.Detox; import com.wix.detox.config.DetoxConfig; @@ -10,12 +13,16 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import androidx.test.rule.ActivityTestRule; +import androidx.test.rule.GrantPermissionRule; @RunWith(AndroidJUnit4.class) @LargeTest public class DetoxTest { + private static final String[] permissionList; @Rule public ActivityTestRule mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false); + @Rule + public GrantPermissionRule permissionRule = GrantPermissionRule.grant(permissionList); @Test public void runDetoxTests() { @@ -26,4 +33,18 @@ public void runDetoxTests() { Detox.runTests(mActivityRule, detoxConfig); } -} \ No newline at end of file + + static { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) { + permissionList = new String[] { + Manifest.permission.RECORD_AUDIO, + Manifest.permission.BLUETOOTH_CONNECT, + Manifest.permission.POST_NOTIFICATIONS }; + } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { + permissionList = new String[] { + Manifest.permission.RECORD_AUDIO, Manifest.permission.BLUETOOTH_CONNECT }; + } else { + permissionList = new String[] { Manifest.permission.RECORD_AUDIO }; + } + } +} diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml index 24091c9c..11105b79 100644 --- a/app/android/app/src/main/AndroidManifest.xml +++ b/app/android/app/src/main/AndroidManifest.xml @@ -1,10 +1,6 @@ - - - - - + xmlns:tools="http://schemas.android.com/tools" + package="com.twiliovoicereactnativereferenceapp"> + android:networkSecurityConfig="@xml/network_security_config" + tools:targetApi="n" + tools:ignore="DataExtractionRules"> @@ -26,34 +23,5 @@ - - - - - - - - - - - - - - diff --git a/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/MainActivity.java b/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/MainActivity.java index 77ea09ee..4a49fe09 100644 --- a/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/MainActivity.java +++ b/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/MainActivity.java @@ -3,37 +3,58 @@ import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; import com.facebook.react.ReactRootView; +import com.twiliovoicereactnative.VoiceActivityProxy; import android.Manifest; -import android.content.pm.PackageManager; +import android.content.Intent; +import android.os.Build; import android.os.Bundle; -import android.util.Log; -import android.view.Window; -import android.view.WindowManager; - -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; +import android.widget.Toast; public class MainActivity extends ReactActivity { - private static final String TAG = "MainActivity"; - private static final int MIC_PERMISSION_REQUEST_CODE = 1; + public static class MainActivityDelegate extends ReactActivityDelegate { + public MainActivityDelegate(ReactActivity activity, String mainComponentName) { + super(activity, mainComponentName); + } - private boolean checkPermissionForMicrophone() { - int resultMic = ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO); - return resultMic == PackageManager.PERMISSION_GRANTED; - } + @Override + protected ReactRootView createRootView() { + ReactRootView reactRootView = new ReactRootView(getContext()); + // If you opted-in for the New Architecture, we enable the Fabric Renderer. + reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED); + return reactRootView; + } - private void requestPermissionForMicrophone() { - if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) { - Log.d(TAG, "Microphone permissions needed. Please allow in your application settings."); - } else { - ActivityCompat.requestPermissions( - this, - new String[]{Manifest.permission.RECORD_AUDIO}, - MIC_PERMISSION_REQUEST_CODE); + @Override + protected boolean isConcurrentRootEnabled() { + // If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18). + // More on this on https://reactjs.org/blog/2022/03/29/react-v18.html + return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; } } + private final VoiceActivityProxy activityProxy = new VoiceActivityProxy( + this, + permission -> { + if (Manifest.permission.RECORD_AUDIO.equals(permission)) { + Toast.makeText( + MainActivity.this, + "Microphone permissions needed. Please allow in your application settings.", + Toast.LENGTH_LONG).show(); + } else if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) && + Manifest.permission.BLUETOOTH_CONNECT.equals(permission)) { + Toast.makeText( + MainActivity.this, + "Bluetooth permissions needed. Please allow in your application settings.", + Toast.LENGTH_LONG).show(); + } else if ((Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) && + Manifest.permission.POST_NOTIFICATIONS.equals(permission)) { + Toast.makeText( + MainActivity.this, + "Notification permissions needed. Please allow in your application settings.", + Toast.LENGTH_LONG).show(); + } + }); /** * Returns the name of the main component registered from JavaScript. This is used to schedule * rendering of the component. @@ -56,30 +77,18 @@ protected ReactActivityDelegate createReactActivityDelegate() { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - if (!checkPermissionForMicrophone()) { - requestPermissionForMicrophone(); - } + activityProxy.onCreate(savedInstanceState); } - public static class MainActivityDelegate extends ReactActivityDelegate { - public MainActivityDelegate(ReactActivity activity, String mainComponentName) { - super(activity, mainComponentName); - } - - @Override - protected ReactRootView createRootView() { - ReactRootView reactRootView = new ReactRootView(getContext()); - // If you opted-in for the New Architecture, we enable the Fabric Renderer. - reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED); - return reactRootView; - } + @Override + public void onDestroy() { + activityProxy.onDestroy(); + super.onDestroy(); + } - @Override - protected boolean isConcurrentRootEnabled() { - // If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18). - // More on this on https://reactjs.org/blog/2022/03/29/react-v18.html - return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; - } + @Override + public void onNewIntent(Intent intent) { + super.onNewIntent(intent); + activityProxy.onNewIntent(intent); } } diff --git a/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/MainApplication.java b/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/MainApplication.java index 29c78b98..90821907 100644 --- a/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/MainApplication.java +++ b/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/MainApplication.java @@ -1,97 +1,81 @@ package com.twiliovoicereactnativereferenceapp; +import java.lang.reflect.InvocationTargetException; import android.app.Application; import android.content.Context; -import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; import com.facebook.react.ReactInstanceManager; -import com.facebook.react.ReactNativeHost; -import com.facebook.react.ReactPackage; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.soloader.SoLoader; -import com.twiliovoicereactnativereferenceapp.newarchitecture.MainApplicationReactNativeHost; -import java.lang.reflect.InvocationTargetException; -import java.util.List; +import com.twiliovoicereactnative.VoiceApplicationProxy; import com.microsoft.appcenter.AppCenter; import com.microsoft.appcenter.distribute.Distribute; +import com.twiliovoicereactnativereferenceapp.newarchitecture.MainApplicationReactNativeHost; public class MainApplication extends Application implements ReactApplication { + private final MainApplicationReactNativeHost mNewArchitectureNativeHost = + new MainApplicationReactNativeHost(this); + private final MainReactNativeHost mReactNativeHost; + private final VoiceApplicationProxy voiceApplicationProxy; - private final ReactNativeHost mReactNativeHost = - new ReactNativeHost(this) { - @Override - public boolean getUseDeveloperSupport() { - return BuildConfig.DEBUG; - } + public MainApplication() { + mReactNativeHost = new MainReactNativeHost(this); + voiceApplicationProxy = new VoiceApplicationProxy(getReactNativeHost()); + } - @Override - protected List getPackages() { - @SuppressWarnings("UnnecessaryLocalVariable") - List packages = new PackageList(this).getPackages(); - // Packages that cannot be autolinked yet can be added manually here, for example: - // packages.add(new MyReactNativePackage()); - return packages; + @Override + public VoiceApplicationProxy.VoiceReactNativeHost getReactNativeHost() { + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + return mNewArchitectureNativeHost; + } else { + return mReactNativeHost; } + } - @Override - protected String getJSMainModuleName() { - return "index"; + @Override + public void onCreate() { + super.onCreate(); + voiceApplicationProxy.onCreate(); + // for app center if available + if (!"null".equals(BuildConfig.APPCENTER_APP_KEY)) { + AppCenter.start(this, BuildConfig.APPCENTER_APP_KEY, Distribute.class); } - }; - - private final ReactNativeHost mNewArchitectureNativeHost = - new MainApplicationReactNativeHost(this); - - @Override - public ReactNativeHost getReactNativeHost() { - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - return mNewArchitectureNativeHost; - } else { - return mReactNativeHost; + // If you opted-in for the New Architecture, we enable the TurboModule system + ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; + SoLoader.init(this, /* native exopackage */ false); + initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); } - } - @Override - public void onCreate() { - super.onCreate(); - // for app center if available - if (!"null".equals(BuildConfig.APPCENTER_APP_KEY)) { - AppCenter.start(this, BuildConfig.APPCENTER_APP_KEY, Distribute.class); + @Override + public void onTerminate() { + // Note: this method is not called when running on device, devices just kill the process. + voiceApplicationProxy.onTerminate(); + super.onTerminate(); } - // If you opted-in for the New Architecture, we enable the TurboModule system - ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; - SoLoader.init(this, /* native exopackage */ false); - initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); - } - /** - * Loads Flipper in React Native templates. Call this in the onCreate method with something like - * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); - * - * @param context - * @param reactInstanceManager - */ - private static void initializeFlipper( - Context context, ReactInstanceManager reactInstanceManager) { - if (BuildConfig.DEBUG) { - try { + /** + * Loads Flipper in React Native templates. Call this in the onCreate method with something like + * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); + * + * @param context + * @param reactInstanceManager + */ + private static void initializeFlipper( + Context context, ReactInstanceManager reactInstanceManager) { + if (BuildConfig.DEBUG) { + try { /* We use reflection here to pick up the class that initializes Flipper, since Flipper library is not available in release mode */ - Class aClass = Class.forName("com.twiliovoicereactnativereferenceapp.ReactNativeFlipper"); - aClass - .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) - .invoke(null, context, reactInstanceManager); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } + Class aClass = Class.forName("com.twiliovoicereactnativereferenceapp.ReactNativeFlipper"); + aClass + .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) + .invoke(null, context, reactInstanceManager); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | + InvocationTargetException e) { + e.printStackTrace(); + } + } } - } } diff --git a/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/MainReactNativeHost.java b/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/MainReactNativeHost.java new file mode 100644 index 00000000..6f8168f2 --- /dev/null +++ b/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/MainReactNativeHost.java @@ -0,0 +1,30 @@ +package com.twiliovoicereactnativereferenceapp; + +import android.app.Application; + +import com.facebook.react.PackageList; +import com.facebook.react.ReactPackage; +import com.twiliovoicereactnative.VoiceApplicationProxy; + +import java.util.List; + +class MainReactNativeHost extends VoiceApplicationProxy.VoiceReactNativeHost { + public MainReactNativeHost(Application application) { + super(application); + } + @Override + public boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } + @Override + protected List getPackages() { + List packages = new PackageList(this).getPackages(); + // Packages that cannot be autolinked yet can be added manually here + // packages.add(new MyReactNativePackage()); + return packages; + } + @Override + protected String getJSMainModuleName() { + return "index"; + } +} diff --git a/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/newarchitecture/MainApplicationReactNativeHost.java b/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/newarchitecture/MainApplicationReactNativeHost.java index c757e2fe..f190f7a6 100644 --- a/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/newarchitecture/MainApplicationReactNativeHost.java +++ b/app/android/app/src/main/java/com/twiliovoicereactnativereferenceapp/newarchitecture/MainApplicationReactNativeHost.java @@ -11,14 +11,13 @@ import com.facebook.react.bridge.JSIModuleProvider; import com.facebook.react.bridge.JSIModuleSpec; import com.facebook.react.bridge.JSIModuleType; -import com.facebook.react.bridge.JavaScriptContextHolder; -import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.UIManager; import com.facebook.react.fabric.ComponentFactory; import com.facebook.react.fabric.CoreComponentsRegistry; import com.facebook.react.fabric.FabricJSIModuleProvider; import com.facebook.react.fabric.ReactNativeConfig; import com.facebook.react.uimanager.ViewManagerRegistry; +import com.twiliovoicereactnative.VoiceApplicationProxy; import com.twiliovoicereactnativereferenceapp.BuildConfig; import com.twiliovoicereactnativereferenceapp.newarchitecture.components.MainComponentsRegistry; import com.twiliovoicereactnativereferenceapp.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate; @@ -32,7 +31,7 @@ *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the * `newArchEnabled` property). Is ignored otherwise. */ -public class MainApplicationReactNativeHost extends ReactNativeHost { +public class MainApplicationReactNativeHost extends VoiceApplicationProxy.VoiceReactNativeHost { public MainApplicationReactNativeHost(Application application) { super(application); } @@ -62,7 +61,7 @@ protected String getJSMainModuleName() { @NonNull @Override protected ReactPackageTurboModuleManagerDelegate.Builder - getReactPackageTurboModuleManagerDelegateBuilder() { + getReactPackageTurboModuleManagerDelegateBuilder() { // Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary // for the new architecture and to use TurboModules correctly. return new MainApplicationTurboModuleManagerDelegate.Builder(); @@ -70,47 +69,42 @@ protected String getJSMainModuleName() { @Override protected JSIModulePackage getJSIModulePackage() { - return new JSIModulePackage() { - @Override - public List getJSIModules( - final ReactApplicationContext reactApplicationContext, - final JavaScriptContextHolder jsContext) { - final List specs = new ArrayList<>(); + return (reactApplicationContext, jsContext) -> { + final List specs = new ArrayList<>(); - // Here we provide a new JSIModuleSpec that will be responsible of providing the - // custom Fabric Components. - specs.add( - new JSIModuleSpec() { - @Override - public JSIModuleType getJSIModuleType() { - return JSIModuleType.UIManager; - } + // Here we provide a new JSIModuleSpec that will be responsible of providing the + // custom Fabric Components. + specs.add( + new JSIModuleSpec() { + @Override + public JSIModuleType getJSIModuleType() { + return JSIModuleType.UIManager; + } - @Override - public JSIModuleProvider getJSIModuleProvider() { - final ComponentFactory componentFactory = new ComponentFactory(); - CoreComponentsRegistry.register(componentFactory); + @Override + public JSIModuleProvider getJSIModuleProvider() { + final ComponentFactory componentFactory = new ComponentFactory(); + CoreComponentsRegistry.register(componentFactory); - // Here we register a Components Registry. - // The one that is generated with the template contains no components - // and just provides you the one from React Native core. - MainComponentsRegistry.register(componentFactory); + // Here we register a Components Registry. + // The one that is generated with the template contains no components + // and just provides you the one from React Native core. + MainComponentsRegistry.register(componentFactory); - final ReactInstanceManager reactInstanceManager = getReactInstanceManager(); + final ReactInstanceManager reactInstanceManager = getReactInstanceManager(); - ViewManagerRegistry viewManagerRegistry = - new ViewManagerRegistry( - reactInstanceManager.getOrCreateViewManagers(reactApplicationContext)); + ViewManagerRegistry viewManagerRegistry = + new ViewManagerRegistry( + reactInstanceManager.getOrCreateViewManagers(reactApplicationContext)); - return new FabricJSIModuleProvider( - reactApplicationContext, - componentFactory, - ReactNativeConfig.DEFAULT_CONFIG, - viewManagerRegistry); - } - }); - return specs; - } + return new FabricJSIModuleProvider( + reactApplicationContext, + componentFactory, + ReactNativeConfig.DEFAULT_CONFIG, + viewManagerRegistry); + } + }); + return specs; }; } } diff --git a/app/android/build.gradle b/app/android/build.gradle index a7ceb768..9c382a31 100644 --- a/app/android/build.gradle +++ b/app/android/build.gradle @@ -4,17 +4,11 @@ buildscript { ext { buildToolsVersion = "31.0.0" minSdkVersion = 21 - compileSdkVersion = 31 - targetSdkVersion = 31 + compileSdkVersion = 33 + targetSdkVersion = 33 kotlinVersion = "1.8.0" - - if (System.properties['os.arch'] == "aarch64") { - // For M1 Users we need to use the NDK 24 which added support for aarch64 - ndkVersion = "24.0.8215888" - } else { - // Otherwise we default to the side-by-side NDK version from AGP. - ndkVersion = "21.4.7075529" - } + // use the same NDK version as the Voice SDK + ndkVersion = "25.2.9519653" } repositories { google() @@ -61,7 +55,7 @@ subprojects { afterEvaluate { subproject -> // fix detox with auth0 error, reference: https://github.com/auth0/react-native-auth0/issues/387#issuecomment-918483390 if (subproject.name == 'react-native-auth0') { - def String taskRequests = getGradle().getStartParameter().getTaskRequests().toString() + String taskRequests = getGradle().getStartParameter().getTaskRequests().toString() if (taskRequests.contains("assembleAndroidTest")) { android { defaultConfig { diff --git a/app/package.json b/app/package.json index 9f93ae8b..0479f75a 100644 --- a/app/package.json +++ b/app/package.json @@ -18,7 +18,7 @@ "@react-navigation/native": "^6.1.2", "@react-navigation/native-stack": "^6.9.8", "@reduxjs/toolkit": "^1.9.1", - "@twilio/voice-react-native-sdk": "^1.0.0-rc8", + "@twilio/voice-react-native-sdk": "https://github.com/twilio/twilio-voice-react-native.git#1.0.0-rc17", "react": "18.1.0", "react-native": "0.70.9", "react-native-auth0": "^3.0.0", diff --git a/app/yarn.lock b/app/yarn.lock index 2e079803..6cc50dfc 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -1559,10 +1559,9 @@ resolved "https://registry.yarnpkg.com/@twilio/voice-errors/-/voice-errors-1.4.0.tgz#0ab954085a32a00442814e56c95936908871ff6e" integrity sha512-7BCg9MPz+KQ0JJ6Rl5W3Zw3E+i3Tt77VZw3/2i3Z+IPZITmCOQLu1nKx/0Nlj505Xtfr7eY9Mcern5PfIoBW0w== -"@twilio/voice-react-native-sdk@^1.0.0-rc8": - version "1.0.0-rc8" - resolved "https://registry.yarnpkg.com/@twilio/voice-react-native-sdk/-/voice-react-native-sdk-1.0.0-rc8.tgz#6bae15015794f732636229c2e88c4c028ecfda1d" - integrity sha512-BfjY1H7MDJiw0iu6xAebFYGKLD3er0Cgr+otPcbomRB5uoDyfBvwR0jQWdFpCHwQ9kHU9VL/bSd+4nrkE0lsVw== +"@twilio/voice-react-native-sdk@https://github.com/twilio/twilio-voice-react-native.git#1.0.0-rc17": + version "1.0.0-rc17" + resolved "https://github.com/twilio/twilio-voice-react-native.git#fe1f41fb3d85afe7cccc5c4f184d77f8800847e6" dependencies: "@twilio/voice-errors" "^1.2.2" eventemitter3 "^4.0.7"