Skip to content

Commit 02ad891

Browse files
authored
[VBLOCKS-5601] feat: add module proxy (#625)
1 parent 7621183 commit 02ad891

12 files changed

+1149
-780
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.twiliovoicereactnative;
2+
3+
import android.os.Handler;
4+
import android.os.Looper;
5+
6+
import com.facebook.react.bridge.ReactApplicationContext;
7+
8+
import java.util.function.Consumer;
9+
10+
class CallInviteModuleProxy {
11+
private static final SDKLog logger = new SDKLog(CallInviteModuleProxy.class);
12+
13+
private final Handler mainHandler = new Handler(Looper.getMainLooper());
14+
15+
private final ReactApplicationContext reactApplicationContext;
16+
17+
public CallInviteModuleProxy(ReactApplicationContext reactApplicationContext) {
18+
this.reactApplicationContext = reactApplicationContext;
19+
}
20+
21+
private void getCallRecord(String uuid, ModuleProxy.UniversalPromise promise, Consumer<CallRecordDatabase.CallRecord> onSuccess) {
22+
logger.debug(String.format(".getCallRecord(%s)", uuid));
23+
24+
CallRecordDatabase.CallRecord callRecord = VoiceApplicationProxy
25+
.getCallRecordDatabase()
26+
.get(new CallRecordDatabase.CallRecord(uuid));
27+
28+
if (null == callRecord || null == callRecord.getCallInvite()) {
29+
final String warningMsg = this.reactApplicationContext
30+
.getString(R.string.missing_callinvite_uuid, uuid);
31+
promise.rejectWithName(CommonConstants.ErrorCodeInvalidArgumentError, warningMsg);
32+
return;
33+
}
34+
35+
mainHandler.post(() -> {
36+
logger.debug(String.format(".getCallRecord(%s) > runnable", uuid));
37+
onSuccess.accept(callRecord);
38+
});
39+
}
40+
41+
public void accept(String uuid, ModuleProxy.UniversalPromise promise) {
42+
logger.debug(String.format(".accept(%s)", uuid));
43+
44+
getCallRecord(uuid, promise, (callRecord) -> {
45+
logger.debug(String.format(".accept(%s) > runnable", uuid));
46+
47+
callRecord.setCallAcceptedPromise(promise);
48+
49+
try {
50+
VoiceApplicationProxy
51+
.getVoiceServiceApi()
52+
.acceptCall(callRecord);
53+
} catch (SecurityException e) {
54+
logger.error(e.toString());
55+
promise.rejectWithCode(31401, e.getMessage());
56+
}
57+
});
58+
}
59+
60+
public void reject(String uuid, ModuleProxy.UniversalPromise promise) {
61+
logger.debug(String.format(".reject(%s)", uuid));
62+
63+
getCallRecord(uuid, promise, (callRecord) -> {
64+
logger.debug(String.format(".reject(%s) > runnable", uuid));
65+
66+
// Store promise for callback
67+
callRecord.setCallRejectedPromise(promise);
68+
69+
// Send Event to service
70+
VoiceApplicationProxy
71+
.getVoiceServiceApi()
72+
.rejectCall(callRecord);
73+
});
74+
}
75+
}
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
package com.twiliovoicereactnative;
2+
3+
import android.os.Handler;
4+
import android.os.Looper;
5+
6+
import com.facebook.react.bridge.ReactApplicationContext;
7+
import com.twilio.voice.Call;
8+
import com.twilio.voice.CallMessage;
9+
10+
import java.util.Map;
11+
import java.util.function.Consumer;
12+
13+
class CallModuleProxy {
14+
/**
15+
* Map of common constant score strings to the Call.Score enum.
16+
*/
17+
public static final Map<String, Call.Score> scoreMap = Map.of(
18+
CommonConstants.CallFeedbackScoreNotReported, Call.Score.NOT_REPORTED,
19+
CommonConstants.CallFeedbackScoreOne, Call.Score.ONE,
20+
CommonConstants.CallFeedbackScoreTwo, Call.Score.TWO,
21+
CommonConstants.CallFeedbackScoreThree, Call.Score.THREE,
22+
CommonConstants.CallFeedbackScoreFour, Call.Score.FOUR,
23+
CommonConstants.CallFeedbackScoreFive, Call.Score.FIVE
24+
);
25+
26+
/**
27+
* Map of common constant issue strings to the Call.Issue enum.
28+
*/
29+
public static final Map<String, Call.Issue> issueMap = Map.of(
30+
CommonConstants.CallFeedbackIssueAudioLatency, Call.Issue.AUDIO_LATENCY,
31+
CommonConstants.CallFeedbackIssueChoppyAudio, Call.Issue.CHOPPY_AUDIO,
32+
CommonConstants.CallFeedbackIssueEcho, Call.Issue.ECHO,
33+
CommonConstants.CallFeedbackIssueDroppedCall, Call.Issue.DROPPED_CALL,
34+
CommonConstants.CallFeedbackIssueNoisyCall, Call.Issue.NOISY_CALL,
35+
CommonConstants.CallFeedbackIssueNotReported, Call.Issue.NOT_REPORTED,
36+
CommonConstants.CallFeedbackIssueOneWayAudio, Call.Issue.ONE_WAY_AUDIO
37+
);
38+
39+
/**
40+
* Use the score map to get a Call.Score value from a string.
41+
* @param score The score as a string passed from the JS layer.
42+
* @return a Call.Score enum value. If the passed string is not in the enum, defaults to
43+
* Call.Score.NOT_REPORTED.
44+
*/
45+
public static Call.Score getScoreFromString(String score) {
46+
return scoreMap.getOrDefault(score, Call.Score.NOT_REPORTED);
47+
}
48+
49+
/**
50+
* Use the issue map to get a Call.Issue value from a string.
51+
* @param issue The issue as a string passed from the JS layer.
52+
* @return a Call.Issue enum value. If the passed string is not in the enum, defaults to
53+
* Call.Issue.NOT_REPORTED.
54+
*/
55+
public static Call.Issue getIssueFromString(String issue) {
56+
return issueMap.getOrDefault(issue, Call.Issue.NOT_REPORTED);
57+
}
58+
59+
private final SDKLog logger = new SDKLog(CallModuleProxy.class);
60+
61+
private final Handler mainHandler = new Handler(Looper.getMainLooper());
62+
63+
private final ReactApplicationContext reactApplicationContext;
64+
65+
public CallModuleProxy(ReactApplicationContext reactApplicationContext) {
66+
this.reactApplicationContext = reactApplicationContext;
67+
}
68+
69+
private void getCallRecord(
70+
String uuid,
71+
ModuleProxy.UniversalPromise promise,
72+
Consumer<CallRecordDatabase.CallRecord> onSuccess
73+
) {
74+
logger.debug(".getCallRecord()");
75+
76+
final CallRecordDatabase.CallRecord callRecord = VoiceApplicationProxy
77+
.getCallRecordDatabase()
78+
.get(new CallRecordDatabase.CallRecord(uuid));
79+
80+
if (null == callRecord || null == callRecord.getVoiceCall()) {
81+
final String warningMsg = this.reactApplicationContext
82+
.getString(R.string.missing_call_uuid, uuid);
83+
promise.rejectWithName(CommonConstants.ErrorCodeInvalidArgumentError, warningMsg);
84+
return;
85+
}
86+
87+
mainHandler.post(() -> {
88+
logger.debug(".getCallRecord() > runnable");
89+
onSuccess.accept(callRecord);
90+
});
91+
}
92+
93+
public void getState(String uuid, ModuleProxy.UniversalPromise promise) {
94+
logger.debug(".getState()");
95+
96+
getCallRecord(uuid, promise, (callRecord) -> {
97+
final String state = callRecord
98+
.getVoiceCall()
99+
.getState()
100+
.toString()
101+
.toLowerCase();
102+
promise.resolve(state);
103+
});
104+
}
105+
106+
public void isMuted(String uuid, ModuleProxy.UniversalPromise promise) {
107+
logger.debug(".isMuted()");
108+
109+
getCallRecord(uuid, promise, (callRecord) -> {
110+
final boolean isMuted = callRecord
111+
.getVoiceCall()
112+
.isMuted();
113+
promise.resolve(isMuted);
114+
});
115+
}
116+
117+
public void isOnHold(String uuid, ModuleProxy.UniversalPromise promise) {
118+
logger.debug(".isOnHold()");
119+
120+
getCallRecord(uuid, promise, (callRecord) -> {
121+
final boolean isOnHold = callRecord
122+
.getVoiceCall()
123+
.isOnHold();
124+
promise.resolve(isOnHold);
125+
});
126+
}
127+
128+
public void disconnect(String uuid, ModuleProxy.UniversalPromise promise) {
129+
logger.debug(".disconnect()");
130+
131+
getCallRecord(uuid, promise, (callRecord) -> {
132+
callRecord
133+
.getVoiceCall()
134+
.disconnect();
135+
promise.resolve(null);
136+
});
137+
}
138+
139+
public void hold(String uuid, boolean hold, ModuleProxy.UniversalPromise promise) {
140+
logger.debug(".hold()");
141+
142+
getCallRecord(uuid, promise, (callRecord) -> {
143+
callRecord
144+
.getVoiceCall()
145+
.hold(hold);
146+
promise.resolve(null);
147+
});
148+
}
149+
150+
public void mute(String uuid, boolean mute, ModuleProxy.UniversalPromise promise) {
151+
logger.debug(".mute()");
152+
153+
getCallRecord(uuid, promise, (callRecord) -> {
154+
callRecord
155+
.getVoiceCall()
156+
.mute(mute);
157+
promise.resolve(null);
158+
});
159+
}
160+
161+
public void sendDigits(String uuid, String digits, ModuleProxy.UniversalPromise promise) {
162+
logger.debug(".sendDigits()");
163+
164+
getCallRecord(uuid, promise, (callRecord) -> {
165+
callRecord
166+
.getVoiceCall()
167+
.sendDigits(digits);
168+
promise.resolve(null);
169+
});
170+
}
171+
172+
public void postFeedback(String uuid, String score, String issue, ModuleProxy.UniversalPromise promise) {
173+
logger.debug(".postFeedback()");
174+
175+
getCallRecord(uuid, promise, (callRecord) -> {
176+
Call.Score parsedScore = CallModuleProxy.getScoreFromString(score);
177+
Call.Issue parsedIssue = CallModuleProxy.getIssueFromString(issue);
178+
179+
callRecord
180+
.getVoiceCall()
181+
.postFeedback(parsedScore, parsedIssue);
182+
183+
promise.resolve(null);
184+
});
185+
}
186+
187+
public void getStats(String uuid, ModuleProxy.UniversalPromise promise) {
188+
logger.debug(".getStats()");
189+
190+
getCallRecord(uuid, promise, (callRecord) -> {
191+
callRecord
192+
.getVoiceCall()
193+
.getStats(new StatsListenerProxy(promise));
194+
});
195+
}
196+
197+
public void sendMessage(
198+
String uuid,
199+
String content,
200+
String contentType,
201+
String messageType,
202+
ModuleProxy.UniversalPromise promise
203+
) {
204+
logger.debug(".sendMessage()");
205+
206+
getCallRecord(uuid, promise, (callRecord) -> {
207+
final CallMessage callMessage = new CallMessage.Builder(messageType)
208+
.contentType(contentType)
209+
.content(content)
210+
.build();
211+
212+
final String callMessageSid =
213+
CallRecordDatabase.CallRecord.CallInviteState.ACTIVE == callRecord.getCallInviteState()
214+
? callRecord.getCallInvite().sendMessage(callMessage)
215+
: callRecord.getVoiceCall().sendMessage(callMessage);
216+
217+
promise.resolve(callMessageSid);
218+
});
219+
}
220+
}

android/src/main/java/com/twiliovoicereactnative/CallRecordDatabase.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
import androidx.annotation.NonNull;
1515

16-
import com.facebook.react.bridge.Promise;
1716
import com.twilio.voice.Call;
1817
import com.twilio.voice.CallException;
1918
import com.twilio.voice.CallInvite;
@@ -32,8 +31,8 @@ public enum Direction { INCOMING, OUTGOING }
3231
private CallInvite callInvite = null;
3332
private CallInviteState callInviteState = NONE;
3433
private CancelledCallInvite cancelledCallInvite = null;
35-
private Promise callAcceptedPromise = null;
36-
private Promise callRejectedPromise = null;
34+
private ModuleProxy.UniversalPromise callAcceptedPromise = null;
35+
private ModuleProxy.UniversalPromise callRejectedPromise = null;
3736
private CallException callException = null;
3837
private Map<String, String> customParameters = null;
3938
private String notificationDisplayName = null;
@@ -103,16 +102,18 @@ public CallInviteState getCallInviteState() {
103102
public CancelledCallInvite getCancelledCallInvite() {
104103
return this.cancelledCallInvite;
105104
}
106-
public Promise getCallAcceptedPromise() {
105+
public ModuleProxy.UniversalPromise getCallAcceptedPromise() {
107106
return this.callAcceptedPromise;
108107
}
109-
public Promise getCallRejectedPromise() {
108+
public ModuleProxy.UniversalPromise getCallRejectedPromise() {
110109
return this.callRejectedPromise;
111110
}
112111
public CallException getCallException() {
113112
return this.callException;
114113
}
115-
public String getCallRecipient() { return this.callRecipient; }
114+
public String getCallRecipient() {
115+
return this.callRecipient;
116+
}
116117
public void setNotificationId(int notificationId) {
117118
this.notificationId = notificationId;
118119
}
@@ -124,18 +125,20 @@ public void setCall(@NonNull Call voiceCall) {
124125
this.voiceCall = voiceCall;
125126
}
126127
public void setCallInviteUsedState() {
127-
this.callInviteState = (this.callInviteState == ACTIVE) ? USED : this.callInviteState;
128+
this.callInviteState = this.callInviteState == ACTIVE
129+
? USED
130+
: this.callInviteState;
128131
}
129132
public void setCancelledCallInvite(@NonNull CancelledCallInvite cancelledCallInvite) {
130133
this.callSid = cancelledCallInvite.getCallSid();
131134
this.cancelledCallInvite = cancelledCallInvite;
132135
this.callInvite = null;
133136
this.callInviteState = NONE;
134137
}
135-
public void setCallAcceptedPromise(@NonNull Promise callAcceptedPromise) {
138+
public void setCallAcceptedPromise(@NonNull ModuleProxy.UniversalPromise callAcceptedPromise) {
136139
this.callAcceptedPromise = callAcceptedPromise;
137140
}
138-
public void setCallRejectedPromise(@NonNull Promise callRejectedPromise) {
141+
public void setCallRejectedPromise(@NonNull ModuleProxy.UniversalPromise callRejectedPromise) {
139142
this.callRejectedPromise = callRejectedPromise;
140143
}
141144
public void setCallException(CallException callException) {

android/src/main/java/com/twiliovoicereactnative/Constants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@ class Constants {
2020
public static final String JS_EVENT_KEY_CANCELLED_CALL_INVITE_INFO = "cancelledCallInvite";
2121
public static final String PREFERENCES_FILE = "com.twilio.twiliovoicereactnative.preferences";
2222
public static final String INCOMING_CALL_CONTACT_HANDLE_TEMPLATE_PREFERENCES_KEY = "incomingCallContactHandleTemplatePreferenceKey";
23+
public static final String GLOBAL_ENV = "com.twilio.voice.env";
24+
public static final String SDK_VERSION = "com.twilio.voice.env.sdk.version";
2325
}

0 commit comments

Comments
 (0)