Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,11 @@ jobs:
command: |
cd test/expo
yarn run expo prebuild --clean --platform=android
cd ../..
- run:
name: "Patch Android native prebuild files with Detox support"
command: |
cd test/expo
bash detox/scripts/patch-all.bash
cd ../..
- detox-build:
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ buildscript {
'java' : JavaVersion.VERSION_11,
'androidGradlePlugin': '7.4.2',
'googleServices' : '4.3.10',
'voiceAndroid' : '6.7.1',
'voiceAndroid' : '6.10.3',
'androidxCore' : '1.12.0',
'androidxLifecycle' : '2.2.0',
'audioSwitch' : '1.2.2',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ class Constants {
public static final String INCOMING_CALL_CONTACT_HANDLE_TEMPLATE_PREFERENCES_KEY = "incomingCallContactHandleTemplatePreferenceKey";
public static final String GLOBAL_ENV = "com.twilio.voice.env";
public static final String SDK_VERSION = "com.twilio.voice.env.sdk.version";
public static final String EXPO_VERSION = "com.twilio.voice.env.sdk.expo_version";
}
10 changes: 10 additions & 0 deletions android/src/main/java/com/twiliovoicereactnative/ExpoModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,16 @@ class ExpoModule : Module() {
[email protected](uuid, PromiseAdapter(promise))
}

AsyncFunction("voice_setExpoVersion") {
expoVersion: String?,
promise: Promise ->

[email protected](
expoVersion,
PromiseAdapter(promise)
)
}

AsyncFunction("voice_setIncomingCallContactHandleTemplate") {
template: String,
promise: Promise ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,11 @@ public void voice_selectAudioDevice(String uuid, Promise promise) {
this.moduleProxy.voice.selectAudioDevice(uuid, new PromiseAdapter(promise));
}

@ReactMethod
public void voice_setExpoVersion(String expoVersion, Promise promise) {
this.moduleProxy.voice.setExpoVersion(expoVersion, new PromiseAdapter(promise));
}

@ReactMethod
public void voice_setIncomingCallContactHandleTemplate(String template, Promise promise) {
this.moduleProxy.voice.setIncomingCallContactHandleTemplate(template, new PromiseAdapter(promise));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void connect(
if (iceOptions != null) {
builder.iceOptions(iceOptions);
}

ConnectOptions connectOptions = builder.build();
try {
final Call call = VoiceApplicationProxy.getVoiceServiceApi().connect(
Expand Down Expand Up @@ -303,6 +303,16 @@ public void runPreflight(PreflightOptions preflightOptions, ModuleProxy.Universa
});
}

public void setExpoVersion(String expoVersion, ModuleProxy.UniversalPromise promise) {
logger.debug(String.format(".setExpoVersion(%s)", expoVersion));
if (expoVersion == null || expoVersion.isEmpty()) {
System.clearProperty(Constants.EXPO_VERSION);
} else {
System.setProperty(Constants.EXPO_VERSION, expoVersion);
}
promise.resolve(null);
}

public void setIncomingCallContactHandleTemplate(String template, ModuleProxy.UniversalPromise promise) {
logger.debug(".setIncomingCallContactHandleTemplate()");
ConfigurationProperties.setIncomingCallContactHandleTemplate(this.reactApplicationContext, template);
Expand Down
2 changes: 1 addition & 1 deletion ios/TwilioVoiceReactNative+PreflightTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ - (void)sendPreflightEvent:(NSDictionary *)eventPayload {
}
}

- (void)preflight:(nonnull TVOPreflightTest *)preflightTest didCompleteWitReport:(nonnull TVOPreflightReport *)report {
- (void)preflight:(nonnull TVOPreflightTest *)preflightTest didCompleteWithReport:(nonnull TVOPreflightReport *)report {
[self sendPreflightEvent:@{
kTwilioVoiceReactNativePreflightTestEventKeyUuid: self.preflightTestUuid,
kTwilioVoiceReactNativePreflightTestEventKeyType: kTwilioVoiceReactNativePreflightTestEventTypeValueCompleted,
Expand Down
12 changes: 12 additions & 0 deletions ios/TwilioVoiceReactNative.m
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,18 @@ - (void)asyncPushRegistryInitialization:(dispatch_time_t)timeout
[self resolvePromise:resolver value:[NSNull null]];
}

RCT_EXPORT_METHOD(voice_setExpoVersion:(NSString *)expoVersion
resolver:(RCTPromiseResolveBlock)resolver
rejecter:(RCTPromiseRejectBlock)rejecter)
{
if ([expoVersion length] > 0) {
setenv("com.twilio.voice.env.sdk.expo_version", [expoVersion UTF8String], 1);
} else {
unsetenv("com.twilio.voice.env.sdk.expo_version");
}
[self resolvePromise:resolver value:[NSNull null]];
}

RCT_EXPORT_METHOD(voice_setIncomingCallContactHandleTemplate:(NSString *)template
resolver:(RCTPromiseResolveBlock)resolver
rejecter:(RCTPromiseRejectBlock)rejecter)
Expand Down
138 changes: 11 additions & 127 deletions src/PreflightTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,25 +596,6 @@ function parseTimeMeasurement(nativeTimeMeasurement: {
*/
function parseCallQuality(
nativeCallQuality: any
): PreflightTest.CallQuality | null {
switch (common.Platform.OS) {
case 'android': {
return parseCallQualityAndroid(nativeCallQuality);
}
case 'ios': {
return parseCallQualityIos(nativeCallQuality);
}
default: {
throw new InvalidStateError('Invalid platform.');
}
}
}

/**
* Parse call quality value for Android platform.
*/
function parseCallQualityAndroid(
nativeCallQuality: string | undefined | null
): PreflightTest.CallQuality | null {
if (typeof nativeCallQuality === 'undefined' || nativeCallQuality === null) {
return null;
Expand All @@ -626,7 +607,7 @@ function parseCallQualityAndroid(
);
}

const parsedCallQuality = callQualityMap.android.get(nativeCallQuality);
const parsedCallQuality = callQualityMap.get(nativeCallQuality);

if (typeof parsedCallQuality !== 'string') {
throw new InvalidStateError(
Expand All @@ -637,33 +618,6 @@ function parseCallQualityAndroid(
return parsedCallQuality;
}

/**
* Parse call quality for iOS platform.
*/
function parseCallQualityIos(
nativeCallQuality: number | undefined | null
): PreflightTest.CallQuality | null {
if (typeof nativeCallQuality === 'undefined' || nativeCallQuality === null) {
return null;
}

if (typeof nativeCallQuality !== 'number') {
throw new InvalidStateError(
`Call quality not of type "number". Found "${typeof nativeCallQuality}".`
);
}

const parsedCallQuality = callQualityMap.ios.get(nativeCallQuality);

if (typeof parsedCallQuality !== 'string') {
throw new InvalidStateError(
`Call quality invalid. Expected [0, 4], found "${nativeCallQuality}".`
);
}

return parsedCallQuality;
}

/**
* Parse native preflight test state value.
*/
Expand Down Expand Up @@ -727,25 +681,6 @@ function parseSample(
* Parse native "isTurnRequired" value.
*/
function parseIsTurnRequired(isTurnRequired: any): boolean | null {
switch (common.Platform.OS) {
case 'android': {
return parseIsTurnRequiredAndroid(isTurnRequired);
}
case 'ios': {
return parseIsTurnRequiredIos(isTurnRequired);
}
default: {
throw new InvalidStateError('Invalid platform.');
}
}
}

/**
* Parse native "isTurnRequired" value on Android.
*/
function parseIsTurnRequiredAndroid(
isTurnRequired: boolean | undefined | null
): boolean | null {
if (typeof isTurnRequired === 'undefined' || isTurnRequired === null) {
return null;
}
Expand All @@ -759,34 +694,6 @@ function parseIsTurnRequiredAndroid(
return isTurnRequired;
}

/**
* Parse native "isTurnRequired" value on iOS.
*/
function parseIsTurnRequiredIos(
isTurnRequired: string | undefined | null
): boolean | null {
if (typeof isTurnRequired === 'undefined' || isTurnRequired === null) {
return null;
}

if (typeof isTurnRequired !== 'string') {
throw new InvalidStateError(
'PreflightTest "isTurnRequired" not of type "string". ' +
`Found "${isTurnRequired}".`
);
}

const parsedValue = isTurnRequiredMap.ios.get(isTurnRequired);

if (typeof parsedValue !== 'boolean') {
throw new InvalidStateError(
`PreflightTest "isTurnRequired" not valid. Found "${isTurnRequired}".`
);
}

return parsedValue;
}

/**
* Parse native warnings array.
*/
Expand Down Expand Up @@ -833,8 +740,9 @@ function parseReport(rawReport: string): PreflightTest.Report {

const callSid: string = unprocessedReport.callSid;

// Note: Android returns enum values where the first letter is capitalized.
// The helper function normalizes this into all-lowercased values.
// Note: Native methods return enum values where the first letter is
// capitalized. The helper function normalizes this into all-lowercased
// values.
const callQuality: PreflightTest.CallQuality | null = parseCallQuality(
unprocessedReport.callQuality
);
Expand All @@ -845,7 +753,6 @@ function parseReport(rawReport: string): PreflightTest.Report {
const iceCandidateStats: PreflightTest.RTCIceCandidateStats[] =
unprocessedReport.iceCandidates;

// Note: iOS returns a string, Android returns a boolean
const isTurnRequired: boolean | null = parseIsTurnRequired(
unprocessedReport.isTurnRequired
);
Expand Down Expand Up @@ -886,14 +793,10 @@ function parseReport(rawReport: string): PreflightTest.Report {
const selectedIceCandidatePairStats: PreflightTest.RTCSelectedIceCandidatePairStats =
unprocessedReport.selectedIceCandidatePair;

// Note: iOS returns undefined where Android returns an empty array
// when there were no warnings
const warnings: PreflightTest.Warning[] = parseWarnings(
unprocessedReport.warnings
);

// Note: iOS returns undefined where Android returns an empty array
// when there were no warningsCleared
const warningsCleared: PreflightTest.WarningCleared[] = parseWarningsCleared(
unprocessedReport.warningsCleared
);
Expand Down Expand Up @@ -1442,32 +1345,13 @@ export namespace PreflightTest {
/**
* Map of call quality values from the native layer to the expected JS values.
*/
const callQualityMap = {
ios: new Map<number, PreflightTest.CallQuality>([
[0, PreflightTest.CallQuality.Excellent],
[1, PreflightTest.CallQuality.Great],
[2, PreflightTest.CallQuality.Good],
[3, PreflightTest.CallQuality.Fair],
[4, PreflightTest.CallQuality.Degraded],
]),
android: new Map<string, PreflightTest.CallQuality>([
['Excellent', PreflightTest.CallQuality.Excellent],
['Great', PreflightTest.CallQuality.Great],
['Good', PreflightTest.CallQuality.Good],
['Fair', PreflightTest.CallQuality.Fair],
['Degraded', PreflightTest.CallQuality.Degraded],
]),
};

/**
* Map of isTurnRequired values from the native layer to the expected JS values.
*/
const isTurnRequiredMap = {
ios: new Map<string, boolean>([
['true', true],
['false', false],
]),
};
const callQualityMap = new Map<string, PreflightTest.CallQuality>([
['Excellent', PreflightTest.CallQuality.Excellent],
['Great', PreflightTest.CallQuality.Great],
['Good', PreflightTest.CallQuality.Good],
['Fair', PreflightTest.CallQuality.Fair],
['Degraded', PreflightTest.CallQuality.Degraded],
]);

/**
* Map of state values from the native layers/common constants to the expected
Expand Down
9 changes: 8 additions & 1 deletion src/Voice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import { EventEmitter } from 'eventemitter3';
import { AudioDevice } from './AudioDevice';
import { Call } from './Call';
import { CallInvite } from './CallInvite';
import { NativeEventEmitter, NativeModule, Platform } from './common';
import {
getExpoVersion,
NativeEventEmitter,
NativeModule,
Platform,
} from './common';
import { Constants } from './constants';
import { InvalidArgumentError } from './error/InvalidArgumentError';
import type { TwilioError } from './error/TwilioError';
Expand Down Expand Up @@ -318,6 +323,8 @@ export class Voice extends EventEmitter {
Constants.ScopeVoice,
this._handleNativeEvent
);

NativeModule.voice_setExpoVersion(getExpoVersion());
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/__mocks__/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export const NativeModule = {
voice_register: createMockWithResolvedValue(undefined),
voice_selectAudioDevice: createMockWithResolvedValue(undefined),
voice_setCallKitConfiguration: createMockWithResolvedValue(undefined),
voice_setExpoVersion: createMockWithResolvedValue(undefined),
voice_showNativeAvRoutePicker: createMockWithResolvedValue(undefined),
voice_setIncomingCallContactHandleTemplate:
createMockWithResolvedValue(undefined),
Expand Down Expand Up @@ -163,3 +164,5 @@ class MockPlatform {
export const Platform = new MockPlatform();

export const setTimeout = jest.fn();

export const getExpoVersion = jest.fn().mockReturnValue('52.0.0');
Loading