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 speaker = FeatherIcons.speaker;
|
||||||
static const monitor = FeatherIcons.monitor;
|
static const monitor = FeatherIcons.monitor;
|
||||||
static const power = FeatherIcons.power;
|
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",
|
"enable_connect_description": "Control Spotube from other devices",
|
||||||
"devices": "Devices",
|
"devices": "Devices",
|
||||||
"select": "Select",
|
"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:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.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/components/shared/page_window_title_bar.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/provider/connect/clients.dart';
|
import 'package:spotube/provider/connect/clients.dart';
|
||||||
@ -12,7 +13,7 @@ class ConnectPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final ThemeData(:colorScheme) = Theme.of(context);
|
final ThemeData(:colorScheme, :textTheme) = Theme.of(context);
|
||||||
|
|
||||||
final connectClients = ref.watch(connectClientsProvider);
|
final connectClients = ref.watch(connectClientsProvider);
|
||||||
final connectClientsNotifier = ref.read(connectClientsProvider.notifier);
|
final connectClientsNotifier = ref.read(connectClientsProvider.notifier);
|
||||||
@ -23,14 +24,33 @@ class ConnectPage extends HookConsumerWidget {
|
|||||||
automaticallyImplyLeading: true,
|
automaticallyImplyLeading: true,
|
||||||
title: Text(context.l10n.devices),
|
title: Text(context.l10n.devices),
|
||||||
),
|
),
|
||||||
body: ListView.separated(
|
body: ListTileTheme(
|
||||||
padding: const EdgeInsets.all(10),
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SliverGap(10),
|
||||||
|
SliverList.separated(
|
||||||
itemCount: discoveredDevices?.length ?? 0,
|
itemCount: discoveredDevices?.length ?? 0,
|
||||||
separatorBuilder: (context, index) => const Gap(10),
|
separatorBuilder: (context, index) => const Gap(10),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final device = discoveredDevices![index];
|
final device = discoveredDevices![index];
|
||||||
final selected =
|
final selected =
|
||||||
connectClients.asData?.value.resolvedService?.name == device.name;
|
connectClients.asData?.value.resolvedService?.name ==
|
||||||
|
device.name;
|
||||||
return Card(
|
return Card(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(SpotubeIcons.monitor),
|
leading: const Icon(SpotubeIcons.monitor),
|
||||||
@ -42,10 +62,6 @@ class ConnectPage extends HookConsumerWidget {
|
|||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
selectedTileColor: colorScheme.secondary.withOpacity(0.1),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
ServiceUtils.push(
|
ServiceUtils.push(
|
||||||
@ -67,6 +83,11 @@ class ConnectPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
const ConnectPageLocalDevices(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:catcher_2/catcher_2.dart';
|
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:spotube/services/audio_player/mk_state_player.dart';
|
||||||
// import 'package:just_audio/just_audio.dart' as ja;
|
// import 'package:just_audio/just_audio.dart' as ja;
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
@ -14,7 +15,7 @@ part 'audio_player_impl.dart';
|
|||||||
|
|
||||||
abstract class AudioPlayerInterface {
|
abstract class AudioPlayerInterface {
|
||||||
final MkPlayerWithState _mkPlayer;
|
final MkPlayerWithState _mkPlayer;
|
||||||
// final ja.AudioPlayer? _justAudio;
|
// final ja.AudioPlayer? _justAudxio;
|
||||||
|
|
||||||
AudioPlayerInterface()
|
AudioPlayerInterface()
|
||||||
: _mkPlayer = MkPlayerWithState(
|
: _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 {
|
bool get hasSource {
|
||||||
return _mkPlayer.playlist.medias.isNotEmpty;
|
return _mkPlayer.playlist.medias.isNotEmpty;
|
||||||
// if (mkSupportedPlatform) {
|
// if (mkSupportedPlatform) {
|
||||||
|
|||||||
@ -83,6 +83,10 @@ class SpotubeAudioPlayer extends AudioPlayerInterface
|
|||||||
// await _justAudio?.setSpeed(speed);
|
// await _justAudio?.setSpeed(speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> setAudioDevice(AudioDevice device) async {
|
||||||
|
await _mkPlayer.setAudioDevice(device);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
await _mkPlayer.dispose();
|
await _mkPlayer.dispose();
|
||||||
// await _justAudio?.dispose();
|
// await _justAudio?.dispose();
|
||||||
|
|||||||
@ -140,4 +140,10 @@ mixin SpotubeAudioPlayersStreams on AudioPlayerInterface {
|
|||||||
// .cast<String>();
|
// .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",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"bn": [
|
"bn": [
|
||||||
@ -12,7 +14,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ca": [
|
"ca": [
|
||||||
@ -20,7 +24,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"de": [
|
"de": [
|
||||||
@ -28,7 +34,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"es": [
|
"es": [
|
||||||
@ -36,7 +44,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"fa": [
|
"fa": [
|
||||||
@ -44,7 +54,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"fr": [
|
"fr": [
|
||||||
@ -52,7 +64,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"hi": [
|
"hi": [
|
||||||
@ -60,7 +74,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"it": [
|
"it": [
|
||||||
@ -68,7 +84,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ja": [
|
"ja": [
|
||||||
@ -76,7 +94,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ko": [
|
"ko": [
|
||||||
@ -84,7 +104,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ne": [
|
"ne": [
|
||||||
@ -92,7 +114,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"nl": [
|
"nl": [
|
||||||
@ -100,7 +124,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"pl": [
|
"pl": [
|
||||||
@ -108,7 +134,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"pt": [
|
"pt": [
|
||||||
@ -116,7 +144,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ru": [
|
"ru": [
|
||||||
@ -124,7 +154,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"tr": [
|
"tr": [
|
||||||
@ -132,7 +164,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"uk": [
|
"uk": [
|
||||||
@ -140,7 +174,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"vi": [
|
"vi": [
|
||||||
@ -150,7 +186,9 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
],
|
],
|
||||||
|
|
||||||
"zh": [
|
"zh": [
|
||||||
@ -158,6 +196,8 @@
|
|||||||
"enable_connect_description",
|
"enable_connect_description",
|
||||||
"devices",
|
"devices",
|
||||||
"select",
|
"select",
|
||||||
"connect_client_alert"
|
"connect_client_alert",
|
||||||
|
"this_device",
|
||||||
|
"remote"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user