Files
client-sdk-flutter/example/lib/pages/connect.dart
T
CloudWebRTC 4f697ca867 fix bluetooth device enumerate. (#281)
* fix bluetooth device enumerate.

* update README.md

* dart format.

* revert pubspec.yaml.

* fix analyzer.
2023-05-11 17:29:33 +08:00

347 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:livekit_client/livekit_client.dart';
import 'package:livekit_example/widgets/text_field.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:permission_handler/permission_handler.dart';
import '../exts.dart';
import 'room.dart';
class ConnectPage extends StatefulWidget {
//
const ConnectPage({
Key? key,
}) : super(key: key);
@override
State<StatefulWidget> createState() => _ConnectPageState();
}
class _ConnectPageState extends State<ConnectPage> {
//
static const _storeKeyUri = 'uri';
static const _storeKeyToken = 'token';
static const _storeKeySimulcast = 'simulcast';
static const _storeKeyAdaptiveStream = 'adaptive-stream';
static const _storeKeyDynacast = 'dynacast';
static const _storeKeyFastConnect = 'fast-connect';
static const _storeKeyE2EE = 'e2ee';
static const _storeKeySharedKey = 'shared-key';
final _uriCtrl = TextEditingController();
final _tokenCtrl = TextEditingController();
final _sharedKeyCtrl = TextEditingController();
bool _simulcast = true;
bool _adaptiveStream = true;
bool _dynacast = true;
bool _busy = false;
bool _fastConnect = false;
bool _e2ee = false;
@override
void initState() {
super.initState();
_readPrefs();
if (lkPlatformIs(PlatformType.android)) {
_checkPremissions();
}
}
@override
void dispose() {
_uriCtrl.dispose();
_tokenCtrl.dispose();
super.dispose();
}
Future<void> _checkPremissions() async {
var status = await Permission.bluetooth.request();
if (status.isPermanentlyDenied) {
print('Bluetooth Permission disabled');
}
status = await Permission.bluetoothConnect.request();
if (status.isPermanentlyDenied) {
print('Bluetooth Connect Permission disabled');
}
status = await Permission.camera.request();
if (status.isPermanentlyDenied) {
print('Camera Permission disabled');
}
status = await Permission.microphone.request();
if (status.isPermanentlyDenied) {
print('Microphone Permission disabled');
}
}
// Read saved URL and Token
Future<void> _readPrefs() async {
final prefs = await SharedPreferences.getInstance();
_uriCtrl.text = const bool.hasEnvironment('URL')
? const String.fromEnvironment('URL')
: prefs.getString(_storeKeyUri) ?? '';
_tokenCtrl.text = const bool.hasEnvironment('TOKEN')
? const String.fromEnvironment('TOKEN')
: prefs.getString(_storeKeyToken) ?? '';
_sharedKeyCtrl.text = const bool.hasEnvironment('E2EEKEY')
? const String.fromEnvironment('E2EEKEY')
: prefs.getString(_storeKeySharedKey) ?? '';
setState(() {
_simulcast = prefs.getBool(_storeKeySimulcast) ?? true;
_adaptiveStream = prefs.getBool(_storeKeyAdaptiveStream) ?? true;
_dynacast = prefs.getBool(_storeKeyDynacast) ?? true;
_fastConnect = prefs.getBool(_storeKeyFastConnect) ?? false;
_e2ee = prefs.getBool(_storeKeyE2EE) ?? false;
});
}
// Save URL and Token
Future<void> _writePrefs() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_storeKeyUri, _uriCtrl.text);
await prefs.setString(_storeKeyToken, _tokenCtrl.text);
await prefs.setString(_storeKeySharedKey, _sharedKeyCtrl.text);
await prefs.setBool(_storeKeySimulcast, _simulcast);
await prefs.setBool(_storeKeyAdaptiveStream, _adaptiveStream);
await prefs.setBool(_storeKeyDynacast, _dynacast);
await prefs.setBool(_storeKeyFastConnect, _fastConnect);
await prefs.setBool(_storeKeyE2EE, _e2ee);
}
Future<void> _connect(BuildContext ctx) async {
//
try {
setState(() {
_busy = true;
});
// Save URL and Token for convenience
await _writePrefs();
print('Connecting with url: ${_uriCtrl.text}, '
'token: ${_tokenCtrl.text}...');
//create new room
final room = Room();
// Create a Listener before connecting
final listener = room.createListener();
E2EEOptions? e2eeOptions;
if (_e2ee) {
final keyProvider = await BaseKeyProvider.create();
e2eeOptions = E2EEOptions(keyProvider: keyProvider);
var sharedKey = _sharedKeyCtrl.text;
await keyProvider.setKey(sharedKey);
}
// Try to connect to the room
// This will throw an Exception if it fails for any reason.
await room.connect(
_uriCtrl.text,
_tokenCtrl.text,
roomOptions: RoomOptions(
adaptiveStream: _adaptiveStream,
dynacast: _dynacast,
defaultVideoPublishOptions: VideoPublishOptions(
simulcast: _simulcast,
),
defaultScreenShareCaptureOptions:
const ScreenShareCaptureOptions(useiOSBroadcastExtension: true),
e2eeOptions: e2eeOptions,
),
fastConnectOptions: _fastConnect
? FastConnectOptions(
microphone: const TrackOption(enabled: true),
camera: const TrackOption(enabled: true),
)
: null,
);
await Navigator.push<void>(
ctx,
MaterialPageRoute(builder: (_) => RoomPage(room, listener)),
);
} catch (error) {
print('Could not connect $error');
await ctx.showErrorDialog(error);
} finally {
setState(() {
_busy = false;
});
}
}
void _setSimulcast(bool? value) async {
if (value == null || _simulcast == value) return;
setState(() {
_simulcast = value;
});
}
void _setE2EE(bool? value) async {
if (value == null || _e2ee == value) return;
setState(() {
_e2ee = value;
});
}
void _setAdaptiveStream(bool? value) async {
if (value == null || _adaptiveStream == value) return;
setState(() {
_adaptiveStream = value;
});
}
void _setDynacast(bool? value) async {
if (value == null || _dynacast == value) return;
setState(() {
_dynacast = value;
});
}
void _setFastConnect(bool? value) async {
if (value == null || _fastConnect == value) return;
setState(() {
_fastConnect = value;
});
}
@override
Widget build(BuildContext context) => Scaffold(
body: Container(
alignment: Alignment.center,
child: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 20,
),
constraints: const BoxConstraints(maxWidth: 400),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 70),
child: SvgPicture.asset(
'images/logo-dark.svg',
),
),
Padding(
padding: const EdgeInsets.only(bottom: 25),
child: LKTextField(
label: 'Server URL',
ctrl: _uriCtrl,
),
),
Padding(
padding: const EdgeInsets.only(bottom: 25),
child: LKTextField(
label: 'Token',
ctrl: _tokenCtrl,
),
),
Padding(
padding: const EdgeInsets.only(bottom: 25),
child: LKTextField(
label: 'Shared Key',
ctrl: _sharedKeyCtrl,
),
),
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('E2EE'),
Switch(
value: _e2ee,
onChanged: (value) => _setE2EE(value),
),
],
),
),
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Simulcast'),
Switch(
value: _simulcast,
onChanged: (value) => _setSimulcast(value),
),
],
),
),
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Adaptive Stream'),
Switch(
value: _adaptiveStream,
onChanged: (value) => _setAdaptiveStream(value),
),
],
),
),
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Fast Connect'),
Switch(
value: _fastConnect,
onChanged: (value) => _setFastConnect(value),
),
],
),
),
Padding(
padding: const EdgeInsets.only(bottom: 25),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Dynacast'),
Switch(
value: _dynacast,
onChanged: (value) => _setDynacast(value),
),
],
),
),
ElevatedButton(
onPressed: _busy ? null : () => _connect(context),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (_busy)
const Padding(
padding: EdgeInsets.only(right: 10),
child: SizedBox(
height: 15,
width: 15,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
),
),
const Text('CONNECT'),
],
),
),
],
),
),
),
),
);
}