mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-05-08 16:24:36 +00:00
feat: add ability to select current device's output speaker
This commit is contained in:
parent
83ac24ac02
commit
36d6a1d8a8
@ -120,4 +120,5 @@ abstract class SpotubeIcons {
|
||||
static const speaker = FeatherIcons.speaker;
|
||||
static const monitor = FeatherIcons.monitor;
|
||||
static const power = FeatherIcons.power;
|
||||
static const bluetooth = FeatherIcons.bluetooth;
|
||||
}
|
||||
|
||||
60
lib/components/connect/local_devices.dart
Normal file
60
lib/components/connect/local_devices.dart
Normal file
@ -0,0 +1,60 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
|
||||
class ConnectPageLocalDevices extends HookWidget {
|
||||
const ConnectPageLocalDevices({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData(:textTheme) = Theme.of(context);
|
||||
final devicesFuture = useFuture(audioPlayer.devices);
|
||||
final devicesStream = useStream(audioPlayer.devicesStream);
|
||||
final selectedDeviceFuture = useFuture(audioPlayer.selectedDevice);
|
||||
final selectedDeviceStream = useStream(audioPlayer.selectedDeviceStream);
|
||||
|
||||
final devices = devicesStream.data ?? devicesFuture.data;
|
||||
final selectedDevice =
|
||||
selectedDeviceStream.data ?? selectedDeviceFuture.data;
|
||||
|
||||
if (devices == null) {
|
||||
return const SliverToBoxAdapter(child: SizedBox.shrink());
|
||||
}
|
||||
|
||||
return SliverMainAxisGroup(
|
||||
slivers: [
|
||||
const SliverGap(10),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: Text(
|
||||
context.l10n.this_device,
|
||||
style: textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SliverGap(10),
|
||||
SliverList.separated(
|
||||
itemCount: devices.length,
|
||||
separatorBuilder: (context, index) => const Gap(10),
|
||||
itemBuilder: (context, index) {
|
||||
final device = devices[index];
|
||||
|
||||
return Card(
|
||||
child: ListTile(
|
||||
leading: const Icon(SpotubeIcons.speaker),
|
||||
title: Text(device.description),
|
||||
subtitle: Text(device.name),
|
||||
selected: selectedDevice == device,
|
||||
onTap: () => audioPlayer.setAudioDevice(device),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -318,5 +318,7 @@
|
||||
"enable_connect_description": "Control Spotube from other devices",
|
||||
"devices": "Devices",
|
||||
"select": "Select",
|
||||
"connect_client_alert": "You're being controlled by {client}"
|
||||
"connect_client_alert": "You're being controlled by {client}",
|
||||
"this_device": "This Device",
|
||||
"remote": "Remote"
|
||||
}
|
||||
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/connect/local_devices.dart';
|
||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/provider/connect/clients.dart';
|
||||
@ -12,7 +13,7 @@ class ConnectPage extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final ThemeData(:colorScheme) = Theme.of(context);
|
||||
final ThemeData(:colorScheme, :textTheme) = Theme.of(context);
|
||||
|
||||
final connectClients = ref.watch(connectClientsProvider);
|
||||
final connectClientsNotifier = ref.read(connectClientsProvider.notifier);
|
||||
@ -23,49 +24,69 @@ class ConnectPage extends HookConsumerWidget {
|
||||
automaticallyImplyLeading: true,
|
||||
title: Text(context.l10n.devices),
|
||||
),
|
||||
body: ListView.separated(
|
||||
padding: const EdgeInsets.all(10),
|
||||
itemCount: discoveredDevices?.length ?? 0,
|
||||
separatorBuilder: (context, index) => const Gap(10),
|
||||
itemBuilder: (context, index) {
|
||||
final device = discoveredDevices![index];
|
||||
final selected =
|
||||
connectClients.asData?.value.resolvedService?.name == device.name;
|
||||
return Card(
|
||||
child: ListTile(
|
||||
leading: const Icon(SpotubeIcons.monitor),
|
||||
title: Text(device.name),
|
||||
subtitle: selected
|
||||
? Text(
|
||||
"${connectClients.asData?.value.resolvedService?.host}"
|
||||
":${connectClients.asData?.value.resolvedService?.port}",
|
||||
)
|
||||
: null,
|
||||
selected: selected,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
body: ListTileTheme(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
selectedTileColor: colorScheme.secondary.withOpacity(0.1),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: Text(
|
||||
context.l10n.remote,
|
||||
style: textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
selectedTileColor: colorScheme.secondary.withOpacity(0.1),
|
||||
onTap: () {
|
||||
if (selected) {
|
||||
ServiceUtils.push(
|
||||
context,
|
||||
"/connect/control",
|
||||
const SliverGap(10),
|
||||
SliverList.separated(
|
||||
itemCount: discoveredDevices?.length ?? 0,
|
||||
separatorBuilder: (context, index) => const Gap(10),
|
||||
itemBuilder: (context, index) {
|
||||
final device = discoveredDevices![index];
|
||||
final selected =
|
||||
connectClients.asData?.value.resolvedService?.name ==
|
||||
device.name;
|
||||
return Card(
|
||||
child: ListTile(
|
||||
leading: const Icon(SpotubeIcons.monitor),
|
||||
title: Text(device.name),
|
||||
subtitle: selected
|
||||
? Text(
|
||||
"${connectClients.asData?.value.resolvedService?.host}"
|
||||
":${connectClients.asData?.value.resolvedService?.port}",
|
||||
)
|
||||
: null,
|
||||
selected: selected,
|
||||
onTap: () {
|
||||
if (selected) {
|
||||
ServiceUtils.push(
|
||||
context,
|
||||
"/connect/control",
|
||||
);
|
||||
} else {
|
||||
connectClientsNotifier.resolveService(device);
|
||||
}
|
||||
},
|
||||
trailing: selected
|
||||
? IconButton(
|
||||
icon: const Icon(SpotubeIcons.power),
|
||||
onPressed: () =>
|
||||
connectClientsNotifier.clearResolvedService(),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
connectClientsNotifier.resolveService(device);
|
||||
}
|
||||
},
|
||||
trailing: selected
|
||||
? IconButton(
|
||||
icon: const Icon(SpotubeIcons.power),
|
||||
onPressed: () =>
|
||||
connectClientsNotifier.clearResolvedService(),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
),
|
||||
const ConnectPageLocalDevices(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:catcher_2/catcher_2.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
import 'package:spotube/services/audio_player/mk_state_player.dart';
|
||||
// import 'package:just_audio/just_audio.dart' as ja;
|
||||
import 'dart:async';
|
||||
@ -14,7 +15,7 @@ part 'audio_player_impl.dart';
|
||||
|
||||
abstract class AudioPlayerInterface {
|
||||
final MkPlayerWithState _mkPlayer;
|
||||
// final ja.AudioPlayer? _justAudio;
|
||||
// final ja.AudioPlayer? _justAudxio;
|
||||
|
||||
AudioPlayerInterface()
|
||||
: _mkPlayer = MkPlayerWithState(
|
||||
@ -60,6 +61,14 @@ abstract class AudioPlayerInterface {
|
||||
}
|
||||
}
|
||||
|
||||
Future<AudioDevice> get selectedDevice async {
|
||||
return _mkPlayer.state.audioDevice;
|
||||
}
|
||||
|
||||
Future<List<AudioDevice>> get devices async {
|
||||
return _mkPlayer.state.audioDevices;
|
||||
}
|
||||
|
||||
bool get hasSource {
|
||||
return _mkPlayer.playlist.medias.isNotEmpty;
|
||||
// if (mkSupportedPlatform) {
|
||||
|
||||
@ -83,6 +83,10 @@ class SpotubeAudioPlayer extends AudioPlayerInterface
|
||||
// await _justAudio?.setSpeed(speed);
|
||||
}
|
||||
|
||||
Future<void> setAudioDevice(AudioDevice device) async {
|
||||
await _mkPlayer.setAudioDevice(device);
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _mkPlayer.dispose();
|
||||
// await _justAudio?.dispose();
|
||||
|
||||
@ -140,4 +140,10 @@ mixin SpotubeAudioPlayersStreams on AudioPlayerInterface {
|
||||
// .cast<String>();
|
||||
// }
|
||||
}
|
||||
|
||||
Stream<List<AudioDevice>> get devicesStream =>
|
||||
_mkPlayer.stream.audioDevices.asBroadcastStream();
|
||||
|
||||
Stream<AudioDevice> get selectedDeviceStream =>
|
||||
_mkPlayer.stream.audioDevice.asBroadcastStream();
|
||||
}
|
||||
|
||||
@ -4,7 +4,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"bn": [
|
||||
@ -12,7 +14,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"ca": [
|
||||
@ -20,7 +24,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"de": [
|
||||
@ -28,7 +34,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"es": [
|
||||
@ -36,7 +44,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"fa": [
|
||||
@ -44,7 +54,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
@ -52,7 +64,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"hi": [
|
||||
@ -60,7 +74,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"it": [
|
||||
@ -68,7 +84,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"ja": [
|
||||
@ -76,7 +94,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"ko": [
|
||||
@ -84,7 +104,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"ne": [
|
||||
@ -92,7 +114,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"nl": [
|
||||
@ -100,7 +124,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"pl": [
|
||||
@ -108,7 +134,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"pt": [
|
||||
@ -116,7 +144,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"ru": [
|
||||
@ -124,7 +154,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"tr": [
|
||||
@ -132,7 +164,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"uk": [
|
||||
@ -140,7 +174,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"vi": [
|
||||
@ -150,7 +186,9 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
],
|
||||
|
||||
"zh": [
|
||||
@ -158,6 +196,8 @@
|
||||
"enable_connect_description",
|
||||
"devices",
|
||||
"select",
|
||||
"connect_client_alert"
|
||||
"connect_client_alert",
|
||||
"this_device",
|
||||
"remote"
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user