Browse Source

Merge commit '452c8c3e71197894caf0d2673e34b296b4ee737a' into new_dev

newdev_shunjiawei
lxm 1 year ago
parent
commit
cd74c0a4fd
  1. 2
      lib/data/services/music_manager.dart
  2. 53
      lib/modules/agent/controllers/agent_controller.dart
  3. 2
      lib/modules/agent/views/music_card.dart
  4. 58
      lib/modules/meeting/controllers/meeting_connect_controller.dart
  5. 15
      lib/modules/music/views/music_playlist_view.dart
  6. 8
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  7. 46
      local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicService.kt
  8. 7
      local_plugins/music_service/lib/music_service.dart
  9. 1
      pubspec.yaml

2
lib/data/services/music_manager.dart

@ -105,7 +105,7 @@ class MusicManager extends GetxService {
required Function() onPrevious,
required Function(int) onSeekTo,
required Function() onStop,
required Function(String, int, int, double) onStop1,
required Function(int, int, int, double) onStop1,
}) async {
try {
await _musicService.setMediaSessionCallbacks(

53
lib/modules/agent/controllers/agent_controller.dart

@ -5,7 +5,6 @@ import 'dart:io';
import 'package:amap_service/amap_service.dart';
import 'package:deep_voice/core/utils/upload_oss.dart';
import 'package:deep_voice/data/models/user_Info.dart';
import 'package:deep_voice/data/services/location_manager.dart';
import 'package:deep_voice/data/services/music_manager.dart';
import 'package:deep_voice/data/services/navigation_manager.dart';
import 'package:deep_voice/data/services/network/api.dart';
@ -126,11 +125,11 @@ class AgentController extends GetxController {
// 添加加载状态跟踪
final RxBool isLoadingMusic = false.obs;
final RxString loadingMusicId = ''.obs;
final RxInt loadingMusicId = 0.obs;
// 设置加载状态的方法
void setLoadingMusic(String id, bool loading) {
loadingMusicId.value = loading ? id : '';
void setLoadingMusic(int id, bool loading) {
loadingMusicId.value = loading ? id : 0;
isLoadingMusic.value = loading;
}
@ -1333,7 +1332,7 @@ class AgentController extends GetxController {
try {
var status = await _musiceManager.getStatus();
isPlaying.value = status['isPlaying'] ?? false;
currentId.value = status['id'] ?? 0;
currentId.value = status['id'] as int;
Logger.d('AgentController',
'音乐状态更新: playing=${isPlaying.value}, url=${currentId.value}');
print("音乐状态更新: playing=${isPlaying.value}, url=${currentId.value}");
@ -1351,16 +1350,14 @@ class AgentController extends GetxController {
isPlaying.value = true;
// 修复:确保currentId接收int类型
final songId = song['id'];
currentId.value = songId is int
? songId
: (int.tryParse(songId?.toString() ?? '0') ?? 0);
currentId.value = songId ?? 0;
// 重置进度状态
currentPosition.value = 0;
duration.value = 0;
progress.value = 0.0;
final id = song['id'] ?? "";
final id = song['id'] ?? 0;
final timestampMs = song['ts'] ?? 0;
// 检查URL是否需要刷新(只有当有时间戳时才检查)
@ -1382,9 +1379,7 @@ class AgentController extends GetxController {
song["url"] = urldata['url'] ?? '';
// 修复:确保currentId接收int类型
final newSongId = song['id'];
currentId.value = newSongId is int
? newSongId
: (int.tryParse(newSongId?.toString() ?? '0') ?? 0);
currentId.value = newSongId ?? 0;
Logger.d(TAG, '获取新URL成功');
} catch (e) {
Logger.e(TAG, '获取新URL失败: $e');
@ -1394,8 +1389,8 @@ class AgentController extends GetxController {
}
// 转换为MusicManager期望的格式
final musicData = <String, String>{
'id': song['id']?.toString() ?? '',
final musicData = <String, dynamic>{
'id': song['id'],
'url': song['url']?.toString() ?? '',
'title': song['title']?.toString() ?? song['name']?.toString() ?? '',
'artist':
@ -1521,10 +1516,10 @@ class AgentController extends GetxController {
}
// 获取播放进度
Future<void> updateMusicProgress1(String currentIdInternal, int newPosition,
Future<void> updateMusicProgress1(int currentIdInternal, int newPosition,
int newDuration, double newProgress) async {
try {
if (int.parse(currentIdInternal) != 0) {
if (currentIdInternal != 0) {
isPlaying.value = true;
// return; // 如果没有在播放,直接返回
}
@ -1533,11 +1528,11 @@ class AgentController extends GetxController {
// final newPosition = progressInfo['currentPosition'] ?? 0;
// final newDuration = progressInfo['duration'] ?? 0;
// final newProgress = progressInfo['progress'] ?? 0.0;
print(
"updateMusicProgress1:currentIdInternal:$currentIdInternal newPosition:$newPosition newDuration:$newDuration newProgress:$newProgress");
// print(
// "updateMusicProgress1:currentIdInternal:$currentIdInternal newPosition:$newPosition newDuration:$newDuration newProgress:$newProgress");
bool hasUpdate = false;
if (currentId.value != int.parse(currentIdInternal)) {
currentId.value = int.parse(currentIdInternal);
if (currentId.value != currentIdInternal) {
currentId.value = currentIdInternal;
hasUpdate = true;
}
// 只有当值发生变化时才更新,避免不必要的UI刷新
@ -1610,7 +1605,7 @@ class AgentController extends GetxController {
}
// 跳转到指定位置
Future<void> seekToPosition1(String currentIdInternal, int newPosition,
Future<void> seekToPosition1(int currentIdInternal, int newPosition,
int newDuration, double newProgress) async {
try {
//await _musiceManager.seekTo(positionMs);
@ -1676,7 +1671,7 @@ class AgentController extends GetxController {
// 设置加载状态
isLoadingMusic.value = true;
loadingMusicId.value = playlist[0]['id']?.toString() ?? '';
loadingMusicId.value = playlist[0]['id'];
// 重置播放状态
isPlaying.value = true;
@ -1687,7 +1682,7 @@ class AgentController extends GetxController {
// 格式化歌曲列表数据
final formattedPlaylist = playlist.map((song) {
return {
'id': song['id']?.toString() ?? '',
'id': song['id'],
'url': song['url']?.toString() ?? '',
'title': song['title']?.toString() ?? song['name']?.toString() ?? '',
'artist':
@ -1702,7 +1697,7 @@ class AgentController extends GetxController {
// 更新当前播放歌曲ID
if (formattedPlaylist.isNotEmpty) {
currentId.value = int.tryParse(formattedPlaylist[0]['id'] ?? '0') ?? 0;
currentId.value = formattedPlaylist[0]['id'] ?? 0;
}
// 启动进度定时器
@ -1726,7 +1721,7 @@ class AgentController extends GetxController {
} finally {
// 清除加载状态
isLoadingMusic.value = false;
loadingMusicId.value = '';
loadingMusicId.value = 0;
}
}
@ -1778,7 +1773,7 @@ class AgentController extends GetxController {
// 查找当前歌曲在列表中的索引
int index = -1;
for (int i = 0; i < playlist.length; i++) {
if (int.tryParse(playlist[i]['id']?.toString() ?? '0') == songId) {
if (playlist[i]['id'] == songId) {
index = i;
break;
}
@ -1837,10 +1832,10 @@ class AgentController extends GetxController {
}
/// 处理来自系统通知栏的跳转操作
Future<void> onMediaSessionSeekTo1(String currentIdInternal, int newPosition,
Future<void> onMediaSessionSeekTo1(int currentIdInternal, int newPosition,
int newDuration, double newProgress) async {
print(
"onMediaSessionSeekTo1 currentIdInternal :$currentIdInternal ,newPosition:$newPosition newDuration:$newDuration newProgress:$newProgress");
// print(
// "onMediaSessionSeekTo1 currentIdInternal :$currentIdInternal ,newPosition:$newPosition newDuration:$newDuration newProgress:$newProgress");
await seekToPosition1(
currentIdInternal, newPosition, newDuration, newProgress);
}

2
lib/modules/agent/views/music_card.dart

@ -200,7 +200,7 @@ Widget buildMusicContent(
...playlist.asMap().entries.take(4).map((entry) {
final index = entry.key;
final item = entry.value;
final id = item["id"] ?? 0;
final id = item["id"];
final name = item["name"] ?? "未知歌曲";
final url = item["url"] ?? "";
final singer = item["sgener"] ?? "未知歌手";

58
lib/modules/meeting/controllers/meeting_connect_controller.dart

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';
import 'package:wifi_iot/wifi_iot.dart';
class MeetingConnectController extends GetxController {
RxString stateStr = '搜索中...'.obs;
@ -20,6 +21,10 @@ class MeetingConnectController extends GetxController {
String _wifiSSID = '';
String _wifiPassword = '';
String _ftpAddress = '';
String _ftpPort = '';
String _ftpAccount = '';
String _ftpPassword = '';
@override
void onInit() {
@ -81,6 +86,7 @@ class MeetingConnectController extends GetxController {
}
Future<void> _disconnect() async {
await WiFiForIoTPlugin.disconnect();
await _device?.disconnect();
}
@ -139,27 +145,61 @@ class MeetingConnectController extends GetxController {
break;
case 0x87: // Wi-Fi 开启/关闭
if (value[3] == 0x01 && value[4] == 0x01) {
sendData([0x5a, 0x01, 0x0b]);
sendData([0x5a, 0x01, 0x0c]);
_getWifi();
}
break;
case 0x0b: // WiFi 热点 SSID
_wifiSSID = String.fromCharCodes(value.sublist(3, value.length - 2));
if (_wifiSSID.isNotEmpty && _wifiPassword.isNotEmpty) {
_connectWifi();
}
_connectWifi();
break;
case 0x0c: // WiFi 热点密码
_wifiPassword = _decodePassword(value);
if (_wifiSSID.isNotEmpty && _wifiPassword.isNotEmpty) {
_connectWifi();
}
_connectWifi();
break;
case 0x03: // FTP 地址
_ftpAddress = String.fromCharCodes(value.sublist(3, value.length - 2));
sendData([0x5a, 0x01, 0x04]);
break;
case 0x04: // FTP 端口
_ftpPort = String.fromCharCodes(value.sublist(3, value.length - 2));
sendData([0x5a, 0x01, 0x05]);
break;
case 0x05: // FTP 账号
_ftpAccount = String.fromCharCodes(value.sublist(3, value.length - 2));
sendData([0x5a, 0x01, 0x06]);
break;
case 0x06: // FTP 密码
_ftpPassword = _decodePassword(value);
_connectFtp();
break;
default:
}
}
void _connectWifi() {}
void _getWifi() async {
await sendData([0x5a, 0x01, 0x0b]);
await sendData([0x5a, 0x01, 0x0c]);
}
void _connectWifi() async {
if (_wifiSSID.isNotEmpty && _wifiPassword.isNotEmpty) {
stateStr.value = '正在连接 Wi-Fi...';
await WiFiForIoTPlugin.connect(
_wifiSSID,
password: _wifiPassword,
security: NetworkSecurity.WPA,
);
stateStr.value = 'Wi-Fi 连接成功';
sendData([0x5a, 0x01, 0x03]);
}
}
void _connectFtp() {
if (_ftpAddress.isNotEmpty &&
_ftpPort.isNotEmpty &&
_ftpAccount.isNotEmpty &&
_ftpPassword.isNotEmpty) {}
}
String _decodePassword(List<int> data) {
final passwordData = data.sublist(3, data.length - 2);

15
lib/modules/music/views/music_playlist_view.dart

@ -1,8 +1,6 @@
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter/services.dart';
import '../../../core/utils/logger.dart';
import '../../agent/controllers/agent_controller.dart';
import '../../agent/views/circular_progress_button.dart';
@ -80,12 +78,11 @@ class MusicPlaylistView extends StatelessWidget {
if (controller.currentId.value != 0) {
try {
currentSong = playlist.firstWhere(
(song) =>
int.tryParse(song['id']?.toString() ?? '0') ==
controller.currentId.value,
(song) => song['id'] == controller.currentId.value,
);
} catch (e) {
// 如果没找到,使用第一首歌曲
currentSong = playlist.isNotEmpty ? playlist.first : null;
}
} else {
@ -192,7 +189,7 @@ class MusicPlaylistView extends StatelessWidget {
itemCount: playlist.length,
itemBuilder: (context, index) {
final item = playlist[index];
final id = item["id"]?.toString() ?? "";
final id = item["id"] ?? 0;
final name = item["name"] ?? "未知歌曲";
final url = item["url"] ?? "";
final singer = item["singer"] ?? item["sgener"] ?? "未知歌手";
@ -215,7 +212,7 @@ class MusicPlaylistView extends StatelessWidget {
Widget _buildMusicItem(
BuildContext context,
AgentController controller,
String id,
int id,
String name,
String url,
String singer,
@ -323,7 +320,7 @@ class MusicPlaylistView extends StatelessWidget {
SizedBox(width: 14.w),
// 播放按钮
Obx(() {
final currentIdInt = int.tryParse(id) ?? 0;
final currentIdInt = id;
final isThisPlaying = controller.isPlaying.value &&
controller.currentId.value == currentIdInt;
final currentProgress =
@ -601,7 +598,7 @@ class MusicPlaylistView extends StatelessWidget {
// 播放选中的歌曲
void _playSelectedSong(AgentController controller, Map<String, dynamic> song,
List<Map<String, dynamic>> playlist) {
final songId = int.tryParse(song['id']?.toString() ?? '0') ?? 0;
final songId = song['id'] ?? 0;
controller.updateCurrentPlaylistContext(playlist, songId);
controller.musicPlay(song);
}

8
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt

@ -1447,12 +1447,12 @@ object AgentService : CoroutineScope {
// Log.d(TAG, "mcp调用结果: $metaStr")
if (meta.has("card_music")) { //音乐卡片
val cardMusic = meta.getJSONObject("card_music")
val id = cardMusic.optInt("id", 0)
val id = cardMusic.optLong("id", 0)
val url = cardMusic.optString("url", "")
val name = cardMusic.optString("name", "")
val sgener = cardMusic.optString("sgener", "")
val image = cardMusic.optString("image", "")
val ts = cardMusic.optInt("ts", 0)
val ts = cardMusic.optLong("ts", 0)
processMusicPlay(
mapOf(
"id" to id,
@ -1470,12 +1470,12 @@ object AgentService : CoroutineScope {
val playlist = mutableListOf<Map<String, Any>>()
for (i in 0 until musics.length()) {
val item = musics.optJSONObject(i) ?: continue
val id = item.optInt("id", 0)
val id = item.optLong("id", 0)
val url = item.optString("url", "")
val name = item.optString("name", "")
val sgener = item.optString("sgener", "")
val image = item.optString("image", "")
val ts = item.optInt("ts", 0)
val ts = item.optLong("ts", 0)
playlist.add(
mapOf(
"id" to id,

46
local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicService.kt

@ -38,7 +38,7 @@ class MusicService : MediaBrowserServiceCompat() {
private const val DELETE_ACTION = "com.yunqiinnovation.music_service.DELETE_NOTIFICATION"
private val isRunning = AtomicBoolean(false)
private var currentIdInternal: String? = null
private var currentIdInternal: Long? = null
private var currentStatus = false
private var playlist = mutableListOf<Map<String,Any>>()
private var currentIndex = 0
@ -48,7 +48,7 @@ class MusicService : MediaBrowserServiceCompat() {
fun isServiceRunning() = isRunning.get()
fun isPlaying() = currentStatus
fun getCurrentId(): String? = currentIdInternal
fun getCurrentId(): Long? = currentIdInternal
// // 获取播放进度信息
// fun getProgress(): Map<String, Any> {
@ -118,11 +118,11 @@ class MusicService : MediaBrowserServiceCompat() {
fun updateMediaSessionMetadata(song: Map<String, Any>) {
instance?.let { service ->
try {
val title = song["title"] as? String ?: song["name"] as? String ?: "未知标题"
val artist = song["artist"] as? String ?: song["sgener"] as? String ?: "未知歌手"
val coverUrl = song["coverUrl"] as? String ?: song["image"] as? String ?: ""
currentIdInternal = song["id"] as? String ?: song["id"] as? String ?: "0"
Log.i(TAG, "updateMediaSessionMetadata currentIdInternal: $currentIdInternal")
val title = song["title"] as? String ??: "未知标题"
val artist = song["artist"] as? String ??: "未知歌手"
val coverUrl = song["coverUrl"] as? String ??: ""
currentIdInternal = (song["id"] as? Number)?.toLong() ?: 0L
Log.i(TAG, "updateMediaSessionMetadata currentIdInternal: $currentIdInternal")
val metadata = android.support.v4.media.MediaMetadataCompat.Builder()
.putString(android.support.v4.media.MediaMetadataCompat.METADATA_KEY_TITLE, title)
.putString(android.support.v4.media.MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
@ -216,13 +216,12 @@ private fun updateProgress() {
// 更新媒体会话进度
// 通知Flutter层
flutterChannel?.invokeMethod("onMediaSessionSeekTo1",
mapOf(
flutterChannel?.invokeMethod("onMediaSessionSeekTo1", mapOf(
"currentIdInternal" to currentIdInternal,
"currentPosition" to currentPosition,
"duration" to duration,
"progress" to progress
))
"currentPosition" to currentPosition,
"duration" to duration,
"progress" to progress
))
}
}
}
@ -238,19 +237,20 @@ private fun updateProgress() {
when (command) {
"play" -> intent.getSerializableExtra("song")?.let { url ->
val songMap = intent.getSerializableExtra("song") as? Map<String, Any>
Log.i(TAG, "MusicePlay onStartCommand: $songMap")
songMap?.let { song ->
val songId = song["id"]as? String ?:"0"
Log.i(TAG, "command songId: $songId")
val songId = (song["id"] as? Number)?.toLong() ?: 0L
currentIdInternal = songId
val existingIndex = playlist.indexOfFirst { it["id"] == songId }
val existingIndex = playlist.indexOfFirst { it["id"] == songId}
if (existingIndex == -1) {
playlist.add(song)
currentIndex = playlist.size - 1
} else {
currentIndex = existingIndex
}
Log.i(TAG, "MusicePlay songId: $songId currentIndex:$currentIndex" )
play(song)
}
}
@ -546,8 +546,8 @@ private fun updateProgress() {
private fun play(song: Map<String,Any>) {
val url = song["url"] as? String ?: return
Log.i(TAG, "开始播放: $url")
val id = song["id"] as? String ?: "0"
Log.i(TAG, "开始播放play id: $id")
val id = (song["id"] as? Number)?.toLong() ?: 0L
Log.i(TAG, "开始播放play id: $id")
val title = song["title"] as? String ?: "未知标题"
val artist = song["artist"] as? String ?: "未知歌手"
val coverUrl = song["coverUrl"] as String
@ -586,8 +586,8 @@ private fun updateProgress() {
val title = song["title"] as? String ?: "未知标题"
val artist = song["artist"] as? String ?: "未知歌手"
val coverUrl = song["coverUrl"] as String
val id = song["id"] as? String ?: "0"
Log.i(TAG, "pause id: $id")
val id =(song["id"] as? Number)?.toLong() ?: 0L
Log.i(TAG, "pause id: $id")
currentIdInternal = id
updateNotification(title,artist,coverUrl)
setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
@ -607,8 +607,8 @@ private fun updateProgress() {
val title = song["title"] as? String ?: "未知标题"
val artist = song["artist"] as? String ?: "未知歌手"
val coverUrl = song["coverUrl"] as String
val id = song["id"] as? String ?: "0"
Log.i(TAG, "resume id: $id")
val id = (song["id"] as? Number)?.toLong() ?: 0L
Log.i(TAG, "resume id: $id")
currentIdInternal = id
updateNotification(title,artist,coverUrl)
setPlaybackState(PlaybackStateCompat.STATE_PLAYING);

7
local_plugins/music_service/lib/music_service.dart

@ -1,3 +1,5 @@
import 'dart:ffi';
import 'package:flutter/services.dart';
class MusicService {
@ -101,7 +103,7 @@ class MusicService {
required Function() onPrevious,
required Function(int) onSeekTo,
required Function() onStop,
required Function(String, int, int, double) onStop1,
required Function(int, int, int, double) onStop1,
}) async {
// 设置方法调用处理器来处理来自原生平台的回调
_methodChannel.setMethodCallHandler((call) async {
@ -126,8 +128,7 @@ class MusicService {
onStop();
break;
case 'onMediaSessionSeekTo1':
final currentIdInternal =
call.arguments['currentIdInternal'] as String;
final currentIdInternal = call.arguments['currentIdInternal'] as int;
final currentPosition = call.arguments['currentPosition'] as int;
final duration = call.arguments['duration'] as int;
final progress = call.arguments['progress'] as double;

1
pubspec.yaml

@ -27,6 +27,7 @@ dependencies:
git:
url: https://github.com/AppFlowy-IO/appflowy-editor.git
ref: main
wifi_iot: ^0.3.19+2
permission_handler: ^10.4.5
web_socket_channel: ^2.4.0
flutter_dotenv: ^5.1.0

Loading…
Cancel
Save