diff --git a/i18n/en.json b/i18n/en.json index 36b8501cb4..12be6263e1 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -655,6 +655,7 @@ "common_create_new_album": "Create new album", "common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.", "completed": "Completed", + "connected_device": "Connected device", "confirm": "Confirm", "confirm_admin_password": "Confirm Admin Password", "confirm_delete_face": "Are you sure you want to delete {name} face from the asset?", @@ -762,6 +763,7 @@ "disallow_edits": "Disallow edits", "discord": "Discord", "discover": "Discover", + "discovered_devices": "Discovered devices", "dismiss_all_errors": "Dismiss all errors", "dismiss_error": "Dismiss error", "display_options": "Display options", diff --git a/mobile/lib/providers/cast.provider.dart b/mobile/lib/providers/cast.provider.dart index d09045358c..a51694c0db 100644 --- a/mobile/lib/providers/cast.provider.dart +++ b/mobile/lib/providers/cast.provider.dart @@ -10,6 +10,8 @@ final castProvider = StateNotifierProvider( class CastNotifier extends StateNotifier { final GCastService _gCastService; + List<(String, CastDestinationType, dynamic)> discovered = List.empty(); + CastNotifier(this._gCastService) : super( CastManagerState( @@ -60,7 +62,11 @@ class CastNotifier extends StateNotifier { } Future> getDevices() async { - return _gCastService.getDevices(); + if (discovered.isEmpty) { + discovered = await _gCastService.getDevices(); + } + + return discovered; } void play() { diff --git a/mobile/lib/services/gcast.service.dart b/mobile/lib/services/gcast.service.dart index 446fd45165..ed1d20c081 100644 --- a/mobile/lib/services/gcast.service.dart +++ b/mobile/lib/services/gcast.service.dart @@ -190,6 +190,6 @@ class GCastService implements ICastDestinationService { device ), ) - .toList(growable: true); + .toList(growable: false); } } diff --git a/mobile/lib/widgets/asset_viewer/cast_dialog.dart b/mobile/lib/widgets/asset_viewer/cast_dialog.dart index b7c422385c..0c48f4837c 100644 --- a/mobile/lib/widgets/asset_viewer/cast_dialog.dart +++ b/mobile/lib/widgets/asset_viewer/cast_dialog.dart @@ -48,47 +48,77 @@ class CastDialog extends ConsumerWidget { ).tr(); } + final devices = snapshot.data!; + final connected = + devices.where((d) => isCurrentDevice(d.$1)).toList(); + final others = + devices.where((d) => !isCurrentDevice(d.$1)).toList(); + + final List sectionedList = []; + + if (connected.isNotEmpty) { + sectionedList.add("connected_device".tr()); + sectionedList.addAll(connected); + } + + if (others.isNotEmpty) { + sectionedList.add("discovered_devices".tr()); + sectionedList.addAll(others); + } + return ListView.builder( shrinkWrap: true, - itemCount: snapshot.data!.length, + itemCount: sectionedList.length, itemBuilder: (context, index) { - final found = snapshot.data![index]; - final deviceName = found.$1; - final type = found.$2; - final deviceObj = found.$3; + final item = sectionedList[index]; - return ListTile( - title: Text( - deviceName, - style: TextStyle( + if (item is String) { + // It's a section header + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + item, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ).tr(), + ); + } else { + final (deviceName, type, deviceObj) = + item as (String, CastDestinationType, dynamic); + + return ListTile( + title: Text( + deviceName, + style: TextStyle( + color: isCurrentDevice(deviceName) + ? context.colorScheme.primary + : null, + ), + ), + leading: Icon( + type == CastDestinationType.googleCast + ? Icons.cast + : Icons.cast_connected, color: isCurrentDevice(deviceName) ? context.colorScheme.primary : null, ), - ), - leading: Icon( - type == CastDestinationType.googleCast - ? Icons.cast - : Icons.cast_connected, - color: isCurrentDevice(deviceName) - ? context.colorScheme.primary - : null, - ), - trailing: isCurrentDevice(deviceName) - ? Icon(Icons.check, color: context.colorScheme.primary) - : isDeviceConnecting(deviceName) - ? const CircularProgressIndicator() - : null, - onTap: () { - // dont accept taps if the device is already connected or is connecting now - if (isDeviceConnecting(deviceName) || - castManager.isCasting) { - return; - } - - ref.read(castProvider.notifier).connect(type, deviceObj); - }, - ); + trailing: isCurrentDevice(deviceName) + ? Icon(Icons.check, color: context.colorScheme.primary) + : isDeviceConnecting(deviceName) + ? const CircularProgressIndicator() + : null, + onTap: () { + if (isDeviceConnecting(deviceName)) { + return; + } + ref.read(castProvider.notifier).disconnect(); + ref.read(castProvider.notifier).connect(type, deviceObj); + }, + ); + } }, ); },