You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
4.2 KiB
168 lines
4.2 KiB
import 'dart:async';
|
|
import 'package:audio_service/audio_service.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
/// 自定义 AudioHandler,用于捕获媒体按键事件(例如蓝牙耳机双击)
|
|
class MyAudioHandler extends BaseAudioHandler {
|
|
// 定义一个 StreamController 用于向 UI 发送自定义事件
|
|
final StreamController<dynamic> _customEventController = StreamController.broadcast();
|
|
bool _isDisposed = false;
|
|
|
|
Stream<dynamic> get customEventStream => _customEventController.stream;
|
|
|
|
MyAudioHandler() {
|
|
_initializeHandler();
|
|
}
|
|
|
|
void _initializeHandler() {
|
|
try {
|
|
// 设置初始播放状态
|
|
playbackState.add(PlaybackState(
|
|
controls: [
|
|
MediaControl.play,
|
|
MediaControl.pause,
|
|
MediaControl.stop,
|
|
MediaControl.skipToPrevious,
|
|
MediaControl.skipToNext,
|
|
],
|
|
systemActions: {
|
|
MediaAction.play,
|
|
MediaAction.pause,
|
|
MediaAction.stop,
|
|
MediaAction.skipToPrevious,
|
|
MediaAction.skipToNext,
|
|
MediaAction.seek,
|
|
},
|
|
androidCompactActionIndices: const [0, 1, 2],
|
|
processingState: AudioProcessingState.idle,
|
|
playing: false,
|
|
));
|
|
|
|
// 设置媒体元数据
|
|
final item = MediaItem(
|
|
id: 'deep_voice_audio',
|
|
title: 'Deep Voice Audio',
|
|
album: 'Deep Voice',
|
|
artist: 'Deep Voice',
|
|
genre: 'Speech',
|
|
duration: const Duration(seconds: 1),
|
|
artUri: null, // 移除图片URI
|
|
playable: true,
|
|
displayTitle: 'Deep Voice Audio',
|
|
displaySubtitle: 'Voice Assistant',
|
|
displayDescription: 'TTS Audio',
|
|
extras: const {'source': 'deep_voice'},
|
|
);
|
|
|
|
mediaItem.add(item);
|
|
} catch (e) {
|
|
print('初始化 AudioHandler 失败: $e');
|
|
}
|
|
}
|
|
|
|
// 激活应用
|
|
Future<void> _activateApp() async {
|
|
if (_isDisposed) return;
|
|
|
|
try {
|
|
print('收到激活应用事件');
|
|
_customEventController.add('activate_app');
|
|
|
|
// 尝试将应用带到前台
|
|
await SystemChannels.platform.invokeMethod('SystemNavigator.routeInformationUpdated');
|
|
} catch (e) {
|
|
print('激活应用失败: $e');
|
|
}
|
|
}
|
|
|
|
// 更新播放状态
|
|
void _updatePlaybackState({
|
|
required bool playing,
|
|
required AudioProcessingState processingState,
|
|
}) {
|
|
if (_isDisposed) return;
|
|
|
|
try {
|
|
playbackState.add(playbackState.value.copyWith(
|
|
playing: playing,
|
|
processingState: processingState,
|
|
updatePosition: Duration.zero,
|
|
));
|
|
} catch (e) {
|
|
print('更新播放状态失败: $e');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> play() async {
|
|
print('收到播放命令');
|
|
await _activateApp();
|
|
_updatePlaybackState(
|
|
playing: true,
|
|
processingState: AudioProcessingState.ready,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<void> pause() async {
|
|
print('收到暂停命令');
|
|
_updatePlaybackState(
|
|
playing: false,
|
|
processingState: AudioProcessingState.ready,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<void> stop() async {
|
|
print('停止 AudioHandler');
|
|
_isDisposed = true;
|
|
try {
|
|
await super.stop();
|
|
_customEventController.close();
|
|
_updatePlaybackState(
|
|
playing: false,
|
|
processingState: AudioProcessingState.idle,
|
|
);
|
|
} catch (e) {
|
|
print('停止 AudioHandler 时发生错误: $e');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> skipToPrevious() async {
|
|
print('收到上一曲命令');
|
|
await _activateApp();
|
|
}
|
|
|
|
@override
|
|
Future<void> skipToNext() async {
|
|
print('收到下一曲命令');
|
|
await _activateApp();
|
|
}
|
|
|
|
@override
|
|
Future<void> seek(Duration position) async {
|
|
print('收到跳转命令: $position');
|
|
await _activateApp();
|
|
}
|
|
|
|
@override
|
|
Future<dynamic> customAction(String name, [Map<String, dynamic>? extras]) async {
|
|
print('收到自定义命令: $name');
|
|
if (name == 'media_button') {
|
|
await _activateApp();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> onTaskRemoved() async {
|
|
print('服务被系统移除');
|
|
await stop();
|
|
}
|
|
|
|
@override
|
|
Future<void> onNotificationDeleted() async {
|
|
print('通知被用户移除');
|
|
await stop();
|
|
}
|
|
}
|