Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## Next Release

* Feat: [Web] Add Twilio Device [DeviceState] accessor protecting un/registration.
* Feat: [Web] Add Twilio Device `updateToken(String)` function to allow updating of active device tokens.
* Feat: update example.
* Docs: update CHANGELOG

## 0.3.2+2
Expand Down
44 changes: 44 additions & 0 deletions example/lib/dialogs/update_token_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';

class UpdateTokenDialogContent extends StatelessWidget {
const UpdateTokenDialogContent({super.key});

@override
Widget build(BuildContext context) {
final textController = TextEditingController();
return AlertDialog(
title: Text('Paste your new token'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
const Text('Paste your new token to continue making calls, you\'ll still receive calls to the current device.'),
const SizedBox(height: 16),
TextField(
controller: textController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'New Token',
alignLabelWithHint: true,
),
maxLines: 3,
),
],
),
),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop(null); // User chose not to update the token
},
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(textController.text); // User chose to update the token
},
child: const Text('Update Token'),
),
],
);
}
}
83 changes: 58 additions & 25 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:twilio_voice/twilio_voice.dart';
import 'package:twilio_voice_example/dialogs/update_token_dialog.dart';
import 'package:twilio_voice_example/screens/ui_call_screen.dart';
import 'package:twilio_voice_example/screens/ui_registration_screen.dart';

Expand Down Expand Up @@ -103,25 +104,25 @@ class _AppState extends State<App> {

// Use for locally provided token generator e.g. Twilio's quickstarter project: https://github.com/twilio/voice-quickstart-server-node
// if (!kIsWeb) {
bool success = false;
// if not web, we use the requested registration method
switch (widget.registrationMethod) {
case RegistrationMethod.env:
success = await _registerFromEnvironment();
break;
case RegistrationMethod.url:
success = await _registerUrl();
break;
case RegistrationMethod.firebase:
success = await _registerFirebase();
break;
}
bool success = false;
// if not web, we use the requested registration method
switch (widget.registrationMethod) {
case RegistrationMethod.env:
success = await _registerFromEnvironment();
break;
case RegistrationMethod.url:
success = await _registerUrl();
break;
case RegistrationMethod.firebase:
success = await _registerFirebase();
break;
}

if (success) {
setState(() {
twilioInit = true;
});
}
if (success) {
setState(() {
twilioInit = true;
});
}
// } else {
// // for web, we always show the initialisation screen unless we specified an
// if (widget.registrationMethod == RegistrationMethod.env) {
Expand Down Expand Up @@ -286,7 +287,7 @@ class _AppState extends State<App> {

switch (event) {
case CallEvent.incoming:
// applies to web only
// applies to web only
if (kIsWeb || Platform.isAndroid) {
final activeCall = TwilioVoice.instance.call.activeCall;
if (activeCall != null && activeCall.callDirection == CallDirection.incoming) {
Expand Down Expand Up @@ -338,7 +339,8 @@ class _AppState extends State<App> {
showDialog(
// ignore: use_build_context_synchronously
context: context,
builder: (context) => const AlertDialog(
builder: (context) =>
const AlertDialog(
title: Text("Error"),
content: Text("Failed to register for calls"),
),
Expand All @@ -357,6 +359,10 @@ class _AppState extends State<App> {
appBar: AppBar(
title: const Text("Plugin example app"),
actions: [
if(twilioInit) ...[
const SizedBox(width: 8),
const _UpdateTokenAction(),
],
_LogoutAction(
onSuccess: () {
setState(() {
Expand All @@ -381,12 +387,12 @@ class _AppState extends State<App> {
padding: const EdgeInsets.symmetric(horizontal: 8),
child: twilioInit
? UICallScreen(
userId: userId,
onPerformCall: _onPerformCall,
)
userId: userId,
onPerformCall: _onPerformCall,
)
: UIRegistrationScreen(
onRegister: _onRegisterWithToken,
),
onRegister: _onRegisterWithToken,
),
),
),
),
Expand Down Expand Up @@ -463,3 +469,30 @@ class _LogoutAction extends StatelessWidget {
icon: const Icon(Icons.logout, color: Colors.white));
}
}

class _UpdateTokenAction extends StatelessWidget {
const _UpdateTokenAction({super.key});

@override
Widget build(BuildContext context) {
return TextButton.icon(
onPressed: () async {
// show dialog to enter new token
final token = await showDialog<String?>(
context: context,
builder: (context) => const UpdateTokenDialogContent(),
);
if (token?.isEmpty ?? true) {
return;
}
final result = await TwilioVoice.instance.setTokens(accessToken: token!);
final message = (result ?? false) ? "Successfully updated token" : "Failed to update token";
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
},
label: const Text("Update Token", style: TextStyle(color: Colors.white)),
icon: const Icon(Icons.refresh, color: Colors.white),
);
}
}
5 changes: 5 additions & 0 deletions lib/_internal/js/device/device.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ class Device extends Twilio {
/// Documentation: https://www.twilio.com/docs/voice/sdks/javascript/twiliodevice#devicestate
@JS("status")
external String state();

/// Update the device's access token.
/// Documentation: https://www.twilio.com/docs/voice/sdks/javascript/twiliodevice#deviceupdatetokentoken
@JS("updateToken")
external void updateToken(String token);
}

/// Device options
Expand Down
5 changes: 5 additions & 0 deletions lib/_internal/twilio_voice_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,10 @@ class TwilioVoiceWeb extends MethodChannelTwilioVoice {
// }
// }
try {
final shouldUpdate = device != null && getDeviceState(device!) == DeviceState.registered;
if (shouldUpdate) {
device!.updateToken(accessToken);
} else {
/// opus set as primary code
/// https://www.twilio.com/blog/client-javascript-sdk-1-7-ga
List<String> codecs = ["opus", "pcmu"];
Expand All @@ -378,6 +382,7 @@ class TwilioVoiceWeb extends MethodChannelTwilioVoice {

// Register device to accept notifications
device!.register();
}

return true;
} catch (e) {
Expand Down
Loading