Skip to content

Commit 57dc246

Browse files
authored
Merge pull request #280 from cybex-dev/feat_raw_connect
Adds raw connect functionality
2 parents 86c0062 + 13544f4 commit 57dc246

File tree

11 files changed

+143
-57
lines changed

11 files changed

+143
-57
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## 0.3.1
22

3+
* Feat: Add raw `Connect({Map<String, dynamic>?})` for all platforms
34
* Refactor: [Web] Removed unused `web_callkit` event listeners.
45
* Fix: [Web] Check if call SID is present when call is disconnected (this occurs if the call ends abruptly after starting, and `params` does not contain `CallSid`).
56
* Fix: [iOS] unregister removes device push token preventing new access token registration (i.e. user 1 logs out, user 2 and more won't receive any calls). Thanks to [@VinceDollo](https://github.com/VinceDollo) & [@Erchil66](https://github.com/Erchil66) [Issue #273](https://github.com/cybex-dev/twilio_voice/issues/273)

android/src/main/kotlin/com/twilio/twilio_voice/TwilioVoicePlugin.kt

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,57 @@ class TwilioVoicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamH
636636
}
637637
}
638638

639+
TVMethodChannels.CONNECT -> {
640+
val args = call.arguments as? Map<*, *> ?: run {
641+
result.error(
642+
FlutterErrorCodes.MALFORMED_ARGUMENTS,
643+
"Arguments should be a Map<*, *>",
644+
null
645+
)
646+
return@onMethodCall
647+
}
648+
649+
Log.d(TAG, "Making new call via connect")
650+
logEvent("Making new call via connect")
651+
val params = HashMap<String, String>()
652+
for ((key, value) in args) {
653+
when (key) {
654+
Constants.PARAM_TO, Constants.PARAM_FROM -> {}
655+
else -> {
656+
params[key.toString()] = value.toString()
657+
}
658+
}
659+
}
660+
// callOutgoing = true
661+
val from = call.argument<String>(Constants.PARAM_FROM) ?: run {
662+
logEvent("No 'from' provided or invalid type, ignoring.")
663+
""
664+
}
665+
666+
val to = call.argument<String>(Constants.PARAM_TO) ?: run {
667+
logEvent("No 'to' provided or invalid type, ignoring.")
668+
""
669+
}
670+
val paramsStringify = JSONObject(args).toString()
671+
Log.d(TAG, "calling with parameters: from: '$from' -> to: '$to', params: $paramsStringify")
672+
673+
accessToken?.let { token ->
674+
context?.let { ctx ->
675+
val success = placeCall(ctx, token, from, to, params, connect = true)
676+
result.success(success)
677+
} ?: run {
678+
Log.e(TAG, "Context is null, cannot place call")
679+
result.success(false)
680+
}
681+
} ?: run {
682+
result.error(
683+
FlutterErrorCodes.MALFORMED_ARGUMENTS,
684+
"No accessToken set, are you registered?",
685+
null
686+
)
687+
}
688+
}
689+
639690
TVMethodChannels.REGISTER_CLIENT -> {
640691

641692
val clientId = call.argument<String>("id") ?: run {
@@ -1040,13 +1091,14 @@ class TwilioVoicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamH
10401091
private fun placeCall(
10411092
ctx: Context,
10421093
accessToken: String,
1043-
from: String,
1044-
to: String,
1045-
params: Map<String, String>
1094+
from: String?,
1095+
to: String?,
1096+
params: Map<String, String>,
1097+
connect: Boolean = false
10461098
): Boolean {
10471099
assert(accessToken.isNotEmpty()) { "Twilio Access Token cannot be empty" }
1048-
assert(to.isNotEmpty()) { "To cannot be empty" }
1049-
assert(from.isNotEmpty()) { "From cannot be empty" }
1100+
assert(!connect && (to == null || to.isNotEmpty())) { "To cannot be empty" }
1101+
assert(!connect && (from == null || from.isNotEmpty())) { "From cannot be empty" }
10501102

10511103
telecomManager?.let { tm ->
10521104
if (!tm.hasCallCapableAccount(ctx, TVConnectionService::class.java.name)) {
@@ -1083,6 +1135,9 @@ class TwilioVoicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamH
10831135
Intent(ctx, TVConnectionService::class.java).apply {
10841136
action = TVConnectionService.ACTION_PLACE_OUTGOING_CALL
10851137
putExtra(TVConnectionService.EXTRA_TOKEN, accessToken)
1138+
if(connect) {
1139+
putExtra(TVConnectionService.EXTRA_CONNECT_RAW, true)
1140+
}
10861141
putExtra(TVConnectionService.EXTRA_TO, to)
10871142
putExtra(TVConnectionService.EXTRA_FROM, from)
10881143
putExtra(TVConnectionService.EXTRA_OUTGOING_PARAMS, Bundle().apply {

android/src/main/kotlin/com/twilio/twilio_voice/service/TVConnectionService.kt

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ class TVConnectionService : ConnectionService() {
138138
*/
139139
const val EXTRA_TOKEN: String = "EXTRA_TOKEN"
140140

141+
/**
142+
* Extra used with [ACTION_PLACE_OUTGOING_CALL] to place an outgoing call connection, denotes the call parameters treated as a Bundle.
143+
*/
144+
const val EXTRA_CONNECT_RAW: String = "EXTRA_CONNECT_RAW"
145+
141146
/**
142147
* Extra used with [ACTION_PLACE_OUTGOING_CALL] to place an outgoing call connection. Denotes the recipient's identity.
143148
*/
@@ -334,40 +339,38 @@ class TVConnectionService : ConnectionService() {
334339
}
335340

336341
ACTION_PLACE_OUTGOING_CALL -> {
337-
// check required EXTRA_TOKEN, EXTRA_TO, EXTRA_FROM
338-
val token = it.getStringExtra(EXTRA_TOKEN) ?: run {
339-
Log.e(TAG, "onStartCommand: ACTION_PLACE_OUTGOING_CALL is missing String EXTRA_TOKEN")
340-
return@let
341-
}
342-
val to = it.getStringExtra(EXTRA_TO) ?: run {
343-
Log.e(TAG, "onStartCommand: ACTION_PLACE_OUTGOING_CALL is missing String EXTRA_TO")
344-
return@let
345-
}
346-
val from = it.getStringExtra(EXTRA_FROM) ?: run {
347-
Log.e(TAG, "onStartCommand: ACTION_PLACE_OUTGOING_CALL is missing String EXTRA_FROM")
348-
return@let
349-
}
350342

351-
// Get all params from bundle
352-
val params = HashMap<String, String>()
353-
val outGoingParams = it.getParcelableExtraSafe<Bundle>(EXTRA_OUTGOING_PARAMS)
354-
outGoingParams?.keySet()?.forEach { key ->
355-
outGoingParams.getString(key)?.let { value ->
356-
params[key] = value
343+
val rawConnect = it.getBooleanExtra(EXTRA_CONNECT_RAW, false)
344+
345+
fun getRequiredString(key: String, allowNullIfRaw: Boolean = false): String? {
346+
val value = it.getStringExtra(key)
347+
if (value == null) {
348+
Log.e(TAG, "onStartCommand: ACTION_PLACE_OUTGOING_CALL is missing String $key")
349+
if (!rawConnect || !allowNullIfRaw) return null
357350
}
351+
return value
358352
}
359353

360-
// Add required params
361-
params[EXTRA_FROM] = from
362-
params[EXTRA_TO] = to
363-
params[EXTRA_TOKEN] = token
354+
val token = getRequiredString(EXTRA_TOKEN) ?: return@let
355+
val to = getRequiredString(EXTRA_TO, allowNullIfRaw = true)
356+
val from = getRequiredString(EXTRA_FROM, allowNullIfRaw = true)
357+
358+
val params = buildMap {
359+
it.getParcelableExtraSafe<Bundle>(EXTRA_OUTGOING_PARAMS)?.let { bundle ->
360+
for (key in bundle.keySet()) {
361+
bundle.getString(key)?.let { value -> put(key, value) }
362+
}
363+
}
364+
put(EXTRA_TOKEN, token)
365+
if (!rawConnect) {
366+
to?.let { v -> put(EXTRA_TO, v) }
367+
from?.let { v -> put(EXTRA_FROM, v) }
368+
}
369+
}
364370

365-
// Create Twilio Param bundles
366371
val myBundle = Bundle().apply {
367372
putBundle(EXTRA_OUTGOING_PARAMS, Bundle().apply {
368-
params.forEach { (key, value) ->
369-
putString(key, value)
370-
}
373+
params.forEach { (key, value) -> putString(key, value) }
371374
})
372375
}
373376

android/src/main/kotlin/com/twilio/twilio_voice/types/TVMethodChannels.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ enum class TVMethodChannels(val method: String) {
4747
IS_PHONE_ACCOUNT_ENABLED("isPhoneAccountEnabled"),
4848
REJECT_CALL_ON_NO_PERMISSIONS("rejectCallOnNoPermissions"),
4949
IS_REJECTING_CALL_ON_NO_PERMISSIONS("isRejectingCallOnNoPermissions"),
50-
UPDATE_CALLKIT_ICON("updateCallKitIcon");
50+
UPDATE_CALLKIT_ICON("updateCallKitIcon"),
51+
CONNECT("connect");
5152

5253
companion object {
5354
private val map = TVMethodChannels.values().associateBy(TVMethodChannels::method)

ios/Classes/SwiftTwilioVoicePlugin.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,21 @@ public class SwiftTwilioVoicePlugin: NSObject, FlutterPlugin, FlutterStreamHand
141141
self.callTo = callTo
142142
self.identity = callFrom
143143
makeCall(to: callTo)
144+
} else if flutterCall.method == "connect" {
145+
guard let callTo = arguments["To"] as? String? else {
146+
return
147+
}
148+
guard let callFrom = arguments["From"] as? String? else {
149+
return
150+
}
151+
self.callArgs = arguments
152+
self.callOutgoing = true
153+
if let accessToken = arguments["accessToken"] as? String{
154+
self.accessToken = accessToken
155+
}
156+
self.callTo = callTo ?? ""
157+
self.identity = callFrom ?? ""
158+
makeCall(to: self.callTo)
144159
}
145160
else if flutterCall.method == "toggleMute"
146161
{

lib/_internal/method_channel/twilio_call_method_channel.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,12 @@ class MethodChannelTwilioCall extends TwilioCallPlatform {
114114
return _channel.invokeMethod('isBluetoothOn', <String, dynamic>{});
115115
}
116116

117-
/// Only web supported for now.
118117
@override
119118
Future<bool?> connect({Map<String, dynamic>? extraOptions}) {
120-
return Future.value(false);
119+
_activeCall = ActiveCall(from: "", to: "", callDirection: CallDirection.outgoing);
120+
final options = {
121+
...?extraOptions,
122+
};
123+
return _channel.invokeMethod('connect', options);
121124
}
122125
}

lib/_internal/platform_interface/twilio_call_platform_interface.dart

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,9 @@ abstract class TwilioCallPlatform extends SharedPlatformInterface {
3131
/// [extraOptions] will be added to the callPayload sent to your server
3232
Future<bool?> place({required String from, required String to, Map<String, dynamic>? extraOptions});
3333

34-
/// Place outgoing call with raw parameters. Returns true if successful, false otherwise.
35-
/// Parameters send to Twilio's REST API endpoint 'makeCall' can be passed in [extraOptions];
36-
/// Parameters are reduced to this format
37-
/// <code>
38-
/// {
39-
/// ...extraOptions
40-
/// }
41-
/// </code>
42-
/// [extraOptions] will be added to the call payload sent to your server
34+
/// Places new call using raw parameters passed directly to Twilio's REST API endpoint 'makeCall'. Returns true if successful, false otherwise.
35+
///
36+
/// [extraOptions] will be added to the callPayload sent to your server
4337
Future<bool?> connect({Map<String, dynamic>? extraOptions});
4438

4539
/// Hangs up active call

lib/_internal/twilio_voice_web.dart

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -786,14 +786,9 @@ class Call extends MethodChannelTwilioCall {
786786
return true;
787787
}
788788

789-
/// Place outgoing call with raw parameters. Returns true if successful, false otherwise.
790-
/// Parameters send to Twilio's REST API endpoint 'makeCall' can be passed in [extraOptions];
791-
/// Parameters are reduced to this format
792-
/// <code>
793-
/// {
794-
/// ...extraOptions
795-
/// }
796-
/// </code>
789+
/// Places new call using raw parameters passed directly to Twilio's REST API endpoint 'makeCall'. Returns true if successful, false otherwise.
790+
///
791+
/// [extraOptions] will be added to the callPayload sent to your server
797792
/// See [twilio_js.Device.connect]
798793
@override
799794
Future<bool?> connect({Map<String, dynamic>? extraOptions}) async {

macos/Classes/JsInterop/Device/TVDeviceConnectOptions.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ public class TVDeviceConnectOptions: JSONArgumentSerializer {
66
// TODO(cybex-dev) - add region, edge information, etc.
77
var params: [String: String] = [:]
88

9-
init(to: String, from: String, customParameters: [String:Any]) {
10-
params[Constants.PARAM_TO] = to
11-
params[Constants.PARAM_FROM] = from
9+
init(to: String?, from: String?, customParameters: [String:Any]) {
10+
if(to != nil) params[Constants.PARAM_TO] = to
11+
if(from != nil) params[Constants.PARAM_FROM] = from
1212
let stringMap = customParameters.map({ (key, value) -> (String, String) in
1313
(key, String(describing: value))
1414
});

macos/Classes/TwilioVoiceChannelMethods.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Foundation
33
public enum TwilioVoiceChannelMethods: String {
44
case tokens = "tokens"
55
case makeCall = "makeCall"
6+
case connect = "connect"
67
case toggleMute = "toggleMute"
78
case isMuted = "isMuted"
89
case toggleSpeaker = "toggleSpeaker"

0 commit comments

Comments
 (0)