Skip to content

Commit c30ac80

Browse files
authored
Merge pull request #305 from cybex-dev/federated_plugin_structure
Migrates to Federated Plugin Architecture
2 parents d59b2ca + 94ff7fa commit c30ac80

File tree

12 files changed

+73
-67
lines changed

12 files changed

+73
-67
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
## Next Release
22

3+
* BREAKING CHANGES:
4+
* Feat: Completed migration to Federated Plugin structure. This requires one change:
5+
```dart
6+
/// old
7+
TwilioVoice.instance
8+
9+
// new
10+
TwilioVoicePlatform.instance
11+
```
312
* Feat: [Web] Add Twilio Device [DeviceState] accessor protecting un/registration.
413
* Feat: [Web] Add Twilio Device `updateToken(String)` function to allow updating of active device tokens.
514
* Fix: [Web] Twilio Device does not unregister on `unregister()` method call due to 'device.off' not visible in js object causing device event listeners to remain attached on unregistered device.

README.md

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# twilio_voice
22

3-
Provides an interface to Twilio's Programmable Voice SDK to allow voice-over-IP (VoIP) calling into
4-
your Flutter applications.
3+
Provides an interface to Twilio's Programmable Voice SDK to allow voice-over-IP (VoIP) calling into your Flutter applications.
54
~~This plugin was taken from the original `flutter_twilio_voice` as it seems that plugin is no longer maintained, this one is.~~ Project ownership & maintenance handed over by [diegogarcia](https://github.com/diegogarciar). For the foreseeable future, I'll be actively maintaining this project.
65

76
#### 🐞Bug? Issue? Something odd?
@@ -121,27 +120,27 @@ notifications:
121120
To register a Phone Account, request access to `READ_PHONE_NUMBERS` permission first:
122121

123122
```dart
124-
TwilioVoice.instance.requestReadPhoneNumbersPermission(); // Gives Android permissions to read Phone Accounts
123+
TwilioVoicePlatform.instance.requestReadPhoneNumbersPermission(); // Gives Android permissions to read Phone Accounts
125124
```
126125

127126
then, register the `PhoneAccount` with:
128127

129128
```dart
130-
TwilioVoice.instance.registerPhoneAccount();
129+
TwilioVoicePlatform.instance.registerPhoneAccount();
131130
```
132131

133132
#### Enable calling account
134133

135134
To open the `Call Account` settings, use the following code:
136135

137136
```dart
138-
TwilioVoice.instance.openPhoneAccountSettings();
137+
TwilioVoicePlatform.instance.openPhoneAccountSettings();
139138
```
140139

141140
Check if it's enabled with:
142141

143142
```dart
144-
TwilioVoice.instance.isPhoneAccountEnabled();
143+
TwilioVoicePlatform.instance.isPhoneAccountEnabled();
145144
```
146145

147146
#### Calling with ConnectionService
@@ -151,7 +150,7 @@ Placing a call with Telecom app via Connection Service requires a `PhoneAccount`
151150
Finally, to grant access to place calls, run:
152151

153152
```dart
154-
TwilioVoice.instance.requestCallPhonePermission(); // Gives Android permissions to place calls
153+
TwilioVoicePlatform.instance.requestCallPhonePermission(); // Gives Android permissions to place calls
155154
```
156155

157156
See [Customizing the Calling Account](#customizing-the-calling-account) for more information.
@@ -161,7 +160,7 @@ See [Customizing the Calling Account](#customizing-the-calling-account) for more
161160
To enable the `ConnectionService` and make/receive calls, run:
162161

163162
```dart
164-
TwilioVoice.instance.requestReadPhoneStatePermission(); // Gives Android permissions to read Phone State
163+
TwilioVoicePlatform.instance.requestReadPhoneStatePermission(); // Gives Android permissions to read Phone State
165164
```
166165

167166
Highly recommended to review the notes for **Android**. See [[Notes]](https://github.com/cybex-dev/twilio_voice/blob/master/NOTES.md#android) for more information.
@@ -272,7 +271,7 @@ import 'package:web_callkit/web_callkit.dart';
272271
273272
// Get call sid used as unique identifier
274273
void _notifyMissedCall() async {
275-
final callSid = await TwilioVoice.instance.call.getSid();
274+
final callSid = await TwilioVoicePlatform.instance.call.getSid();
276275
WebCallkit.instance.reportCallDisconnected(callSid!, response: CKDisconnectResponse.missed);
277276
}
278277
```
@@ -308,25 +307,25 @@ for more information on preparing for publishing your macOS app
308307

309308
### Usage
310309

311-
The plugin was separated into two classes, the `TwilioVoice.instance`
312-
and `TwilioVoice.instance.call`, the first one is in charge of general configuration and the second
310+
The plugin was separated into two classes, the `TwilioVoicePlatform.instance`
311+
and `TwilioVoicePlatform.instance.call`, the first one is in charge of general configuration and the second
313312
one is in charge of managing calls.
314313

315314
Register iOS capabilities
316315

317316
- Add Audio and Voice over IP in background modes
318317

319-
### TwilioVoice.instance
318+
### TwilioVoicePlatform.instance
320319

321320
#### Setting the tokens
322321

323-
call `TwilioVoice.instance.setTokens` as soon as your app starts.
322+
call `TwilioVoicePlatform.instance.setTokens` as soon as your app starts.
324323

325324
- `accessToken` provided from your server, you can see an example cloud
326325
function [here](https://github.com/cybex-dev/twilio_voice/blob/master/functions.js).
327326
- `deviceToken` is automatically handled on iOS, for android you need to pass a FCM token.
328327

329-
call `TwilioVoice.instance.unregister` to unregister from Twilio, if no access token is passed, it
328+
call `TwilioVoicePlatform.instance.unregister` to unregister from Twilio, if no access token is passed, it
330329
will use the token provided in `setTokens` at the same session.
331330

332331
### Call Identifier
@@ -339,13 +338,13 @@ register them so when they call, the call UI can display their names and not the
339338
#### Registering a client
340339

341340
```
342-
TwilioVoice.instance.registerClient(String clientId, String clientName)
341+
TwilioVoicePlatform.instance.registerClient(String clientId, String clientName)
343342
```
344343
345344
#### Unregistering a client
346345
347346
```
348-
TwilioVoice.instance.unregisterClient(String clientId)
347+
TwilioVoicePlatform.instance.unregisterClient(String clientId)
349348
```
350349
351350
#### Default caller
@@ -354,12 +353,12 @@ You can also set a default caller, such as "unknown number" or "chat friend" in
354353
from an unregistered client.
355354
356355
```
357-
TwilioVoice.instance.setDefaultCallerName(String callerName)
356+
TwilioVoicePlatform.instance.setDefaultCallerName(String callerName)
358357
```
359358
360359
### Call Events
361360
362-
use stream `TwilioVoice.instance.callEventsListener` to receive events from the TwilioSDK such as
361+
use stream `TwilioVoicePlatform.instance.callEventsListener` to receive events from the TwilioSDK such as
363362
call events and logs, it is a broadcast so you can listen to it on different parts of your app. Some
364363
events might be missed when the app has not launched, please check out the example project to find
365364
the workarounds.
@@ -449,7 +448,7 @@ to `false`.
449448
use `extraOptions` to pass additional variables to your server callback function.
450449
451450
```
452-
await TwilioVoice.instance.call.place(from:myId, to: clientId, extraOptions);
451+
await TwilioVoicePlatform.instance.call.place(from:myId, to: clientId, extraOptions);
453452

454453
```
455454
@@ -484,28 +483,28 @@ Receives calls via [ConnectionService](https://developer.android.com/reference/a
484483
#### Mute a Call
485484

486485
```
487-
TwilioVoice.instance.call.toggleMute(isMuted: true);
486+
TwilioVoicePlatform.instance.call.toggleMute(isMuted: true);
488487
489488
```
490489

491490
#### Toggle Speaker
492491

493492
```
494-
TwilioVoice.instance.call.toggleSpeaker(speakerIsOn: true);
493+
TwilioVoicePlatform.instance.call.toggleSpeaker(speakerIsOn: true);
495494
496495
```
497496

498497
#### Hang Up
499498

500499
```
501-
TwilioVoice.instance.call.hangUp();
500+
TwilioVoicePlatform.instance.call.hangUp();
502501
503502
```
504503

505504
#### Send Digits
506505

507506
```
508-
TwilioVoice.instance.call.sendDigits(String digits);
507+
TwilioVoicePlatform.instance.call.sendDigits(String digits);
509508
510509
```
511510

@@ -516,16 +515,16 @@ Receives calls via [ConnectionService](https://developer.android.com/reference/a
516515
To receive and place calls you need Microphone permissions, register the microphone permission in
517516
your info.plist for iOS.
518517

519-
You can use `TwilioVoice.instance.hasMicAccess` and `TwilioVoice.instance.requestMicAccess` to check
518+
You can use `TwilioVoicePlatform.instance.hasMicAccess` and `TwilioVoicePlatform.instance.requestMicAccess` to check
520519
and request the permission. Permissions is also automatically requested when receiving a call.
521520

522521
#### Background calls (Android only on some devices)
523522

524523
~~Xiaomi devices, and maybe others, need a special permission to receive background calls.
525-
use `TwilioVoice.instance.requiresBackgroundPermissions` to check if your device requires a special
524+
use `TwilioVoicePlatform.instance.requiresBackgroundPermissions` to check if your device requires a special
526525
permission, if it does, show a rationale explaining the user why you need the permission. Finally
527526
call
528-
`TwilioVoice.instance.requestBackgroundPermissions` which will take the user to the App Settings
527+
`TwilioVoicePlatform.instance.requestBackgroundPermissions` which will take the user to the App Settings
529528
page to enable the permission.~~
530529

531530
Deprecated in 0.10.0, as it is no longer needed. Custom UI has been replaced with native UI.
@@ -535,20 +534,20 @@ Deprecated in 0.10.0, as it is no longer needed. Custom UI has been replaced wit
535534
Similar to CallKit on iOS, Android implements their own via a [ConnectionService](https://developer.android.com/reference/android/telecom/ConnectionService) integration. To make use of this, you'll need to request `CALL_PHONE` permissions via:
536535

537536
```dart
538-
TwilioVoice.instance.requestCallPhonePermission(); // Gives Android permissions to place outgoing calls
539-
TwilioVoice.instance.requestReadPhoneStatePermission(); // Gives Android permissions to read Phone State including receiving calls
540-
TwilioVoice.instance.requestReadPhoneNumbersPermission(); // Gives Android permissions to read Phone Accounts
541-
TwilioVoice.instance.requestManageOwnCallsPermission(); // Gives Android permissions to manage calls, this isn't necessary to request as the permission is simply required in the Manifest, but added nontheless.
537+
TwilioVoicePlatform.instance.requestCallPhonePermission(); // Gives Android permissions to place outgoing calls
538+
TwilioVoicePlatform.instance.requestReadPhoneStatePermission(); // Gives Android permissions to read Phone State including receiving calls
539+
TwilioVoicePlatform.instance.requestReadPhoneNumbersPermission(); // Gives Android permissions to read Phone Accounts
540+
TwilioVoicePlatform.instance.requestManageOwnCallsPermission(); // Gives Android permissions to manage calls, this isn't necessary to request as the permission is simply required in the Manifest, but added nontheless.
542541
```
543542

544543
Following this, to register a Phone Account (required by all applications implementing a system-managed `ConnectionService`, run:
545544

546545
```dart
547-
TwilioVoice.instance.registerPhoneAccount(); // Registers the Phone Account
548-
TwilioVoice.instance.openPhoneAccountSettings(); // Opens the Phone Account settings
546+
TwilioVoicePlatform.instance.registerPhoneAccount(); // Registers the Phone Account
547+
TwilioVoicePlatform.instance.openPhoneAccountSettings(); // Opens the Phone Account settings
549548
550549
// After the account is enabled, you can check if it's enabled with:
551-
TwilioVoice.instance.isPhoneAccountEnabled(); // Checks if the Phone Account is enabled
550+
TwilioVoicePlatform.instance.isPhoneAccountEnabled(); // Checks if the Phone Account is enabled
552551
```
553552

554553
This last step can be considered the 'final check' to make/receive calls on Android.
@@ -558,8 +557,8 @@ This last step can be considered the 'final check' to make/receive calls on Andr
558557
Finally, a consideration for not all (`CALL_PHONE`) permissions granted on an Android device. The following feature is available on Android only:
559558

560559
```dart
561-
TwilioVoice.instance.rejectCallOnNoPermissions({Bool = false}); // Rejects incoming calls if permissions are not granted
562-
TwilioVoice.instance.isRejectingCallOnNoPermissions(); // Checks if the plugin is rejecting calls if permissions are not granted
560+
TwilioVoicePlatform.instance.rejectCallOnNoPermissions({Bool = false}); // Rejects incoming calls if permissions are not granted
561+
TwilioVoicePlatform.instance.isRejectingCallOnNoPermissions(); // Checks if the plugin is rejecting calls if permissions are not granted
563562
```
564563

565564
If the `CALL_PHONE` permissions group i.e. `READ_PHONE_STATE`, `READ_PHONE_NUMBERS`, `CALL_PHONE` aren't granted nor a Phone Account is registered and enabled, the plugin will either reject the incoming call (true) or not show the incoming call UI (false).

example/lib/dialogs/update_token_dialog.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class UpdateTokenDialogContent extends StatelessWidget {
77
Widget build(BuildContext context) {
88
final textController = TextEditingController();
99
return AlertDialog(
10-
title: Text('Paste your new token'),
10+
title: const Text('Paste your new token'),
1111
content: SingleChildScrollView(
1212
child: ListBody(
1313
children: <Widget>[

example/lib/main.dart

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class _AppState extends State<App> {
153153
androidToken = await FirebaseMessaging.instance.getToken();
154154
printDebug("androidToken is ${androidToken!}");
155155
}
156-
final result = await TwilioVoice.instance.setTokens(accessToken: accessToken, deviceToken: androidToken);
156+
final result = await TwilioVoicePlatform.instance.setTokens(accessToken: accessToken, deviceToken: androidToken);
157157
return result ?? false;
158158
}
159159

@@ -263,7 +263,7 @@ class _AppState extends State<App> {
263263
void initState() {
264264
super.initState();
265265

266-
TwilioVoice.instance.setOnDeviceTokenChanged((token) {
266+
TwilioVoicePlatform.instance.setOnDeviceTokenChanged((token) {
267267
printDebug("voip-device token changed");
268268
if (!kIsWeb) {
269269
register();
@@ -274,29 +274,29 @@ class _AppState extends State<App> {
274274
register();
275275

276276
const partnerId = "alicesId";
277-
TwilioVoice.instance.registerClient(partnerId, "Alice");
278-
// TwilioVoice.instance.requestReadPhoneStatePermission();
279-
// TwilioVoice.instance.requestMicAccess();
280-
// TwilioVoice.instance.requestCallPhonePermission();
277+
TwilioVoicePlatform.instance.registerClient(partnerId, "Alice");
278+
// TwilioVoicePlatform.instance.requestReadPhoneStatePermission();
279+
// TwilioVoicePlatform.instance.requestMicAccess();
280+
// TwilioVoicePlatform.instance.requestCallPhonePermission();
281281
}
282282

283283
/// Listen for call events
284284
void listenForEvents() {
285-
TwilioVoice.instance.callEventsListener.listen((event) {
285+
TwilioVoicePlatform.instance.callEventsListener.listen((event) {
286286
printDebug("voip-onCallStateChanged $event");
287287

288288
switch (event) {
289289
case CallEvent.incoming:
290290
// applies to web only
291291
if (kIsWeb || Platform.isAndroid) {
292-
final activeCall = TwilioVoice.instance.call.activeCall;
292+
final activeCall = TwilioVoicePlatform.instance.call.activeCall;
293293
if (activeCall != null && activeCall.callDirection == CallDirection.incoming) {
294294
_showWebIncomingCallDialog();
295295
}
296296
}
297297
break;
298298
case CallEvent.ringing:
299-
final activeCall = TwilioVoice.instance.call.activeCall;
299+
final activeCall = TwilioVoicePlatform.instance.call.activeCall;
300300
if (activeCall != null) {
301301
final customData = activeCall.customParams;
302302
if (customData != null) {
@@ -324,13 +324,13 @@ class _AppState extends State<App> {
324324

325325
/// Place a call to [clientIdentifier]
326326
Future<void> _onPerformCall(String clientIdentifier) async {
327-
if (!await (TwilioVoice.instance.hasMicAccess())) {
327+
if (!await (TwilioVoicePlatform.instance.hasMicAccess())) {
328328
printDebug("request mic access");
329-
TwilioVoice.instance.requestMicAccess();
329+
TwilioVoicePlatform.instance.requestMicAccess();
330330
return;
331331
}
332332
printDebug("starting call to $clientIdentifier");
333-
TwilioVoice.instance.call.place(to: clientIdentifier, from: userId, extraOptions: {"_TWI_SUBJECT": "Company Name"});
333+
TwilioVoicePlatform.instance.call.place(to: clientIdentifier, from: userId, extraOptions: {"_TWI_SUBJECT": "Company Name"});
334334
}
335335

336336
Future<void> _onRegisterWithToken(String token, [String? identity]) async {
@@ -402,14 +402,14 @@ class _AppState extends State<App> {
402402
/// Show incoming call dialog for web and Android
403403
void _showWebIncomingCallDialog() async {
404404
showingIncomingCallDialog = true;
405-
final activeCall = TwilioVoice.instance.call.activeCall!;
405+
final activeCall = TwilioVoicePlatform.instance.call.activeCall!;
406406
final action = await showIncomingCallScreen(context, activeCall);
407407
if (action == true) {
408408
printDebug("accepting call");
409-
TwilioVoice.instance.call.answer();
409+
TwilioVoicePlatform.instance.call.answer();
410410
} else if (action == false) {
411411
printDebug("rejecting call");
412-
TwilioVoice.instance.call.hangUp();
412+
TwilioVoicePlatform.instance.call.hangUp();
413413
} else {
414414
printDebug("no action");
415415
}
@@ -458,7 +458,7 @@ class _LogoutAction extends StatelessWidget {
458458
Widget build(BuildContext context) {
459459
return TextButton.icon(
460460
onPressed: () async {
461-
final result = await TwilioVoice.instance.unregister();
461+
final result = await TwilioVoicePlatform.instance.unregister();
462462
if (result == true) {
463463
onSuccess?.call();
464464
} else {
@@ -471,7 +471,7 @@ class _LogoutAction extends StatelessWidget {
471471
}
472472

473473
class _UpdateTokenAction extends StatelessWidget {
474-
const _UpdateTokenAction({super.key});
474+
const _UpdateTokenAction({Key? key}): super(key: key);
475475

476476
@override
477477
Widget build(BuildContext context) {
@@ -485,8 +485,9 @@ class _UpdateTokenAction extends StatelessWidget {
485485
if (token?.isEmpty ?? true) {
486486
return;
487487
}
488-
final result = await TwilioVoice.instance.setTokens(accessToken: token!);
488+
final result = await TwilioVoicePlatform.instance.setTokens(accessToken: token!);
489489
final message = (result ?? false) ? "Successfully updated token" : "Failed to update token";
490+
// ignore: use_build_context_synchronously
490491
ScaffoldMessenger.of(context).showSnackBar(
491492
SnackBar(content: Text(message)),
492493
);

example/lib/screens/ui_call_screen.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ class _RingSound extends StatefulWidget {
135135
}
136136

137137
class _RingSoundState extends State<_RingSound> {
138-
final _tv = TwilioVoice.instance;
138+
final _tv = TwilioVoicePlatform.instance;
139139
final TextEditingController _controller = TextEditingController();
140140

141141
@override

example/lib/screens/widgets/call_features.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class _CallControlsState extends State<CallControls> {
5353

5454
//#endregion
5555

56-
final _tv = TwilioVoice.instance;
56+
final _tv = TwilioVoicePlatform.instance;
5757
bool activeCall = false;
5858

5959
@override

0 commit comments

Comments
 (0)