diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index d690627..d844e11 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,13 +1,17 @@ + + - + + + @@ -40,6 +44,11 @@ + { } void _shareScreen() async { - // final lp = widget.room.localParticipant; for (final track in lp.videoTracks) { @@ -122,6 +122,17 @@ class _ControlsWidgetState extends State { } try { + // Required for android screenshare. + const androidConfig = FlutterBackgroundAndroidConfig( + notificationTitle: 'Screen Sharing', + notificationText: 'LiveKit Example is sharing the screen.', + notificationImportance: AndroidNotificationImportance.Default, + notificationIcon: + AndroidResource(name: 'livekit_ic_launcher', defType: 'mipmap'), + ); + await FlutterBackground.initialize(androidConfig: androidConfig); + await FlutterBackground.enableBackgroundExecution(); + final screenTrack = await LocalVideoTrack.createScreenTrack(); // Defaults to camera await widget.room.localParticipant.publishVideoTrack( @@ -132,6 +143,17 @@ class _ControlsWidgetState extends State { } } + void _unshareScreen() async { + final lp = widget.room.localParticipant; + + try { + await lp.setScreenShareEnabled(false); + await FlutterBackground.disableBackgroundExecution(); + } catch (e) { + print('error disabling screen share: $e'); + } + } + void _onTapDisconnect() async { final result = await context.showDisconnectDialog(); if (result == true) await widget.room.disconnect(); @@ -156,8 +178,12 @@ class _ControlsWidgetState extends State { // mute audio final canMute = participant.hasAudio && !participant.isMuted; - final videoPub = participant.videoTracks.firstOrNull; + final videoPub = + participant.getTrackPublicationBySource(TrackSource.camera); final videoEnabled = videoPub != null && !videoPub.muted; + final screenSharePub = + participant.getTrackPublicationBySource(TrackSource.screenShareVideo); + final screenShareEnabled = screenSharePub != null && !screenSharePub.muted; return Padding( padding: const EdgeInsets.symmetric( @@ -205,11 +231,18 @@ class _ControlsWidgetState extends State { onPressed: () => _toggleCamera(), tooltip: 'toggle camera', ), - IconButton( - icon: const Icon(EvaIcons.monitor), - onPressed: () => _shareScreen(), - tooltip: 'share screen (experimental)', - ), + if (screenShareEnabled) + IconButton( + icon: const Icon(EvaIcons.monitorOutline), + onPressed: () => _unshareScreen(), + tooltip: 'unshare screen (experimental)', + ) + else + IconButton( + icon: const Icon(EvaIcons.monitor), + onPressed: () => _shareScreen(), + tooltip: 'share screen (experimental)', + ), IconButton( onPressed: _onTapDisconnect, icon: const Icon(EvaIcons.closeCircle), diff --git a/example/pubspec.lock b/example/pubspec.lock index 632de6e..afc2925 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -90,6 +90,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_background: + dependency: "direct main" + description: + name: flutter_background + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2+2" flutter_lints: dependency: "direct dev" description: @@ -218,6 +225,20 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted + version: "2.0.7" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted version: "2.0.6" path_provider_linux: dependency: transitive @@ -225,7 +246,7 @@ packages: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" path_provider_macos: dependency: transitive description: @@ -246,7 +267,7 @@ packages: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.0.4" petitparser: dependency: transitive description: @@ -302,7 +323,7 @@ packages: name: shared_preferences_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" shared_preferences_macos: dependency: transitive description: @@ -330,7 +351,7 @@ packages: name: shared_preferences_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" sky_engine: dependency: transitive description: flutter @@ -419,7 +440,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.2.10" + version: "2.3.0" xdg_directories: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index b3ed3d3..6667ef4 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: flutter: sdk: flutter + flutter_background: 1.0.2+2 provider: ^6.0.1 logging: ^1.0.1 shared_preferences: ^2.0.7 diff --git a/lib/src/participant/local_participant.dart b/lib/src/participant/local_participant.dart index 3d57341..d78a9b2 100644 --- a/lib/src/participant/local_participant.dart +++ b/lib/src/participant/local_participant.dart @@ -242,6 +242,10 @@ extension LocalParticipantTrackSourceExt on LocalParticipant { return setSourceEnabled(TrackSource.microphone, enabled); } + Future setScreenShareEnabled(bool enabled) async { + return setSourceEnabled(TrackSource.screenShareVideo, enabled); + } + Future setSourceEnabled(TrackSource source, bool enabled) async { final pub = getTrackPublicationBySource(source); if (pub != null) { @@ -261,8 +265,10 @@ extension LocalParticipantTrackSourceExt on LocalParticipant { } else if (source == TrackSource.microphone) { final track = await LocalAudioTrack.create(); await publishAudioTrack(track); + } else if (source == TrackSource.screenShareVideo) { + final track = await LocalVideoTrack.createScreenTrack(); + await publishVideoTrack(track); } - // TODO: Screen share } } } diff --git a/lib/src/track/local_video_track.dart b/lib/src/track/local_video_track.dart index 37c7b08..89fcba8 100644 --- a/lib/src/track/local_video_track.dart +++ b/lib/src/track/local_video_track.dart @@ -71,6 +71,10 @@ class LocalVideoTrack extends VideoTrack { ); } + /// Creates a LocalVideoTrack from the display. + /// + /// Note: Android requires a foreground service to be started prior to + /// creating a screen track. Refer to the example app for an implementation. static Future createScreenTrack([ ScreenTrackOptions? options, ]) async {