From 29fa0d6a27e4e2de37312840b3a4fb5b0c2a9aee Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Sat, 10 May 2025 17:42:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E9=9F=B3=E4=B9=90=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=92=8C=E9=9F=B3=E4=B9=90=E5=88=97=E8=A1=A8=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/models/appconfig.dart | 4 +- lib/data/services/ble_manager.dart | 3 + lib/data/services/music_manager.dart | 21 ++++- .../agent/controllers/agent_controller.dart | 50 +++++++++-- lib/modules/agent/views/message_bubble.dart | 83 ++++++++++++++----- .../agent_service/AgentService.kt | 51 +++++++++++- .../agent_service/AgentServicePlugin.kt | 1 + .../agent_service/BleMediaService.kt | 3 + .../music_service/MusicService.kt | 42 ++++++++-- .../music_service/MusicServicePlugin.kt | 15 +++- .../music_service/MusicServiceStarter.kt | 11 +++ .../music_service/lib/music_service.dart | 7 ++ 12 files changed, 243 insertions(+), 48 deletions(-) diff --git a/lib/data/models/appconfig.dart b/lib/data/models/appconfig.dart index 1c04272a6..e80862486 100644 --- a/lib/data/models/appconfig.dart +++ b/lib/data/models/appconfig.dart @@ -21,8 +21,8 @@ class AppConfig { } //智能体配置 - static String agent(String agentId) { + static String? agentsystemPrompt(String agentId) { final foundAgent = config.agents.firstWhere((agent) => agent.id == agentId); - return jsonEncode((foundAgent)); + return foundAgent.systemPrompt; } } diff --git a/lib/data/services/ble_manager.dart b/lib/data/services/ble_manager.dart index ce8302c92..27ac8745c 100644 --- a/lib/data/services/ble_manager.dart +++ b/lib/data/services/ble_manager.dart @@ -404,6 +404,8 @@ class BleManager extends GetxService { final openaiBaseUrl = AppConfig.env('OPENAI_BASE_URL') ?? 'https://api.openai.com/v1'; final openaiModel = AppConfig.env('OPENAI_MODEL') ?? 'gpt-3.5-turbo'; + final systemPrompt = AppConfig.agentsystemPrompt("personal_assistant"); + // 从Flutter资源包中加载.mcp.json final jsonData = AppConfig.mcpConfig(); @@ -414,6 +416,7 @@ class BleManager extends GetxService { 'openaiApiKey': openaiApiKey, 'openaiBaseUrl': openaiBaseUrl, 'openaiModel': openaiModel, + 'systemPrompt': systemPrompt, 'mcpServer': jsonEncode(jsonData), }; diff --git a/lib/data/services/music_manager.dart b/lib/data/services/music_manager.dart index 5efabeaf1..0732f0f6b 100644 --- a/lib/data/services/music_manager.dart +++ b/lib/data/services/music_manager.dart @@ -22,6 +22,23 @@ class MusicManager extends GetxService { super.onClose(); } - //播放 音乐 - Future play(String url) async {} + //播放 + Future play(String url) async { + await _musicService.playMusic(url); + } + + //暂停 + Future pause() async { + await _musicService.pauseMusic(); + } + + //继续 + Future resume() async { + await _musicService.resumeMusic(); + } + + //状态 + Future> getStatus() async { + return await _musicService.getStatus(); + } } diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index 7e85a3fac..614266ebb 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:deep_voice/data/services/music_manager.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:agent_service/agent_service.dart'; @@ -40,6 +41,11 @@ class AgentController extends GetxController { final TextEditingController textController = TextEditingController(); final ScrollController scrollController = ScrollController(); + //音乐播放管理 + final _musiceManager = Get.find(); + var isPlaying = false.obs; + var currentUrl = "".obs; + // 状态标志 final isInitialized = false.obs; final isListening = false.obs; @@ -101,13 +107,17 @@ class AgentController extends GetxController { if (result.messages.isNotEmpty) { // 转换并添加消息到列表 - final historyMessages = result.messages - .map((msg) => Message( - isUser: msg.sender == 'user', - text: msg.message, - timestamp: DateTime.parse(msg.timestamp), - )) - .toList(); + final historyMessages = result.messages.map((msg) { + final meta = msg.metadata != null + ? jsonDecode(msg.metadata!) + : null; // 如果为空,使用空对象 + return Message( + isUser: msg.sender == 'user', + text: msg.message, + meta: meta, + timestamp: DateTime.parse(msg.timestamp), + ); + }).toList(); // 消息按时间排序,最新的消息在底部 historyMessages.sort((a, b) => a.timestamp.compareTo(b.timestamp)); @@ -607,4 +617,30 @@ class AgentController extends GetxController { void translateImageText() { useImagePrompt('请翻译这张图片中的文字'); } + + // 播放音乐 + void musicStatus() async { + var status = await _musiceManager.getStatus(); + logger.i('获取当前音乐播放状态 $status'); + isPlaying.value = status['isPlaying']; + currentUrl.value = status['url']; + } + + // 播放音乐 + void musicPlay(String url) { + _musiceManager.play(url); + isPlaying.value = true; + currentUrl.value = url; + } + + // 播放音乐 + void musicPause() { + _musiceManager.pause(); + isPlaying.value = false; + } + + void musicResume() { + _musiceManager.resume(); + isPlaying.value = true; + } } diff --git a/lib/modules/agent/views/message_bubble.dart b/lib/modules/agent/views/message_bubble.dart index cc539bc18..2bc3ea534 100644 --- a/lib/modules/agent/views/message_bubble.dart +++ b/lib/modules/agent/views/message_bubble.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'dart:io'; +import '../controllers/agent_controller.dart'; + /// 消息气泡组件 -class MessageBubble extends StatelessWidget { +class MessageBubble extends GetView { final bool isUser; final String message; final DateTime timestamp; @@ -199,8 +202,14 @@ class MessageBubble extends StatelessWidget { //音乐卡片 return _buildMusicItem(context, musicName, musicUrl, musicSinger); } else if (mate!.keys.contains('card_musiclist')) { + final rawList = mate!['card_musiclist']['musics'] as List; + final playlist = + rawList.map((e) => Map.from(e)).toList(); + // final playlist = + // mate!['card_musiclist']['musics'] as List>; + //音乐播放列表 - return _buildMusicContent(context); + return _buildMusicContent(context, playlist); } else if (mate!.keys.contains('card_weather')) { //天气 return _buildWeatherContent(context); @@ -250,11 +259,12 @@ class MessageBubble extends StatelessWidget { } /// 构建音乐内容卡片 - Widget _buildMusicContent(BuildContext context) { + Widget _buildMusicContent( + BuildContext context, List> playlist) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( + const Text( '以下是一些推荐的音乐:', style: TextStyle( color: Colors.black87, @@ -301,18 +311,22 @@ class MessageBubble extends StatelessWidget { ), const SizedBox(height: 10), - // 歌曲示例 - _buildMusicItem( - context, - '雨声与钢琴曲', - "", - '纯音乐', - ), - Divider(height: 1, thickness: 1, color: Colors.grey[100]), - _buildMusicItem(context, 'Rainy Day', "", 'Coldplay'), - Divider(height: 1, thickness: 1, color: Colors.grey[100]), - _buildMusicItem(context, '雨天的爵士咖啡馆', "", '爵士合集'), + // 动态生成音乐条目 + ...playlist.asMap().entries.map((entry) { + final index = entry.key; + final item = entry.value; + final name = item["name"] ?? "未知歌曲"; + final url = item["url"] ?? ""; + final singer = item["sgener"] ?? "未知歌手"; + return Column( + children: [ + _buildMusicItem(context, name, url, singer), + if (index != playlist.length - 1) + Divider(height: 1, thickness: 1, color: Colors.grey[100]), + ], + ); + }).toList(), const SizedBox(height: 8), Center( child: TextButton( @@ -507,6 +521,8 @@ class MessageBubble extends StatelessWidget { /// 构建音乐项目 Widget _buildMusicItem( BuildContext context, String name, String url, String singer) { + final controller = Get.find(); // 获取控制器 + controller.musicStatus(); //刷新状态 return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( @@ -550,14 +566,35 @@ class MessageBubble extends StatelessWidget { ], ), ), - IconButton( - onPressed: () {}, - icon: - Icon(Icons.play_circle, color: Theme.of(context).primaryColor), - padding: EdgeInsets.zero, - constraints: BoxConstraints(), - iconSize: 24, - ), + Obx(() { + final isThisPlaying = controller.isPlaying.value && + controller.currentUrl.value == url; + + return IconButton( + onPressed: () { + if (controller.isPlaying.value) { + if (controller.currentUrl.value == url) { + controller.musicPause(); + } else { + controller.musicPlay(url); + } + } else { + if (controller.currentUrl.value == url) { + controller.musicResume(); + } else { + controller.musicPlay(url); + } + } + }, + icon: Icon( + isThisPlaying ? Icons.pause_circle : Icons.play_circle, + color: Theme.of(context).primaryColor, + ), + padding: EdgeInsets.zero, + constraints: BoxConstraints(), + iconSize: 24, + ); + }), ], ), ); diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index 92a329613..14a92352a 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -132,6 +132,7 @@ object AgentService : CoroutineScope { // 自定义系统提示词 val customSystemPrompt = config["systemPrompt"] as? String + FileLogger.i(TAG, "初始化 系统提示词:$config") if (!customSystemPrompt.isNullOrBlank()) { systemPrompt = customSystemPrompt } else { @@ -559,6 +560,7 @@ object AgentService : CoroutineScope { // 使用历史记录作为上下文发送到OpenAI val responseBuilder = StringBuilder() + var metadata:String = "" // 添加系统提示词到历史消息的副本中 val messagesWithSystemPrompt = JSONArray() @@ -637,7 +639,7 @@ object AgentService : CoroutineScope { addToHistoryMessages(openAIService.createAssistantMessage(response)) // 保存聊天记录 - saveChatMessage(displayText, response) + saveChatMessage(displayText, response,metadata) } // 标记AI流式输出已完成 @@ -675,15 +677,38 @@ object AgentService : CoroutineScope { FileLogger.d(TAG, "mcp调用结果: $functionCallResult") val metaStr = functionCallResult.optString("meta") if (metaStr.isNotEmpty()) { + metadata = metaStr; val meta = JSONObject(metaStr) FileLogger.d(TAG, "mcp调用结果: $metaStr") - if (meta.has("card_music")) { + if (meta.has("card_music")) { //音乐卡片 val cardMusic = meta.getJSONObject("card_music") FileLogger.d(TAG, "检查到音乐卡片: $cardMusic") val musicUrl = cardMusic.optString("url", "") val name = cardMusic.optString("name", "") val sgener = cardMusic.optString("sgener", "") processMusicPlay(musicUrl, name, sgener) + }else if (meta.has("card_musiclist")) { //音乐列表 + val cardMusiclist = meta.getJSONObject("card_musiclist") + val musics = cardMusiclist.optJSONArray("musics") + if (musics != null) { + val playlist = mutableListOf>() + for (i in 0 until musics.length()) { + val item = musics.optJSONObject(i) ?: continue + val url = item.optString("url", "") + val name = item.optString("name", "") + val sgener = item.optString("sgener", "") + if (url.isNotEmpty()) { + playlist.add(mapOf("url" to url, "name" to name, "sgener" to sgener)) + } + } + if (playlist.isNotEmpty()) { + processMusicPlayList(playlist) + } else { + FileLogger.w(TAG, "card_musiclist 中没有有效的音乐条目") + } + } else { + FileLogger.w(TAG, "card_musiclist.musics 不是一个数组") + } } } } @@ -784,7 +809,7 @@ object AgentService : CoroutineScope { /** * 保存聊天消息 */ - private fun saveChatMessage(userMessage: String, assistantMessage: String) { + private fun saveChatMessage(userMessage: String, assistantMessage: String,metadata : String) { launch(Dispatchers.IO) { try { // 保存用户消息 @@ -799,7 +824,8 @@ object AgentService : CoroutineScope { val assistantMessageId = chatStorageHelper.saveMessage( sessionId = sessionId, message = assistantMessage, - sender = "assistant" + sender = "assistant", + metadata = metadata ) // if (assistantMessageId != -1L) { @@ -977,4 +1003,21 @@ object AgentService : CoroutineScope { FileLogger.i(TAG, "启动音乐服务 播放音乐 $musicUrl") MusicServiceStarter.startServiceWithCommand(context, "play", url = musicUrl) } + + /** + * 处理音乐播放列表 + * 将ai返回的音乐连接 在后台启动播放 + * + * @param musicUrl 图片文件路径 + * @param name 歌曲名称,默认为空 + * @param singer 歌手,默认为false + * @return 是否成功开始处理 + */ + fun processMusicPlayList(playlist:List>){ + // 在其他 Service、BroadcastReceiver 或 Application 中调用 + + val urls = playlist.mapNotNull { it["url"] }.distinct() + FileLogger.i(TAG, "启动音乐服务 播放音乐列表 $urls") + MusicServiceStarter.startServiceWithPlaylist(context, "playlist", urls = urls) + } } \ No newline at end of file diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt index 588e66ac5..1fc35fafa 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt @@ -57,6 +57,7 @@ class AgentServicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.Stream config["openaiApiKey"]?.toString()?.let { intent.putExtra("openaiApiKey", it) } config["openaiBaseUrl"]?.toString()?.let { intent.putExtra("openaiBaseUrl", it) } config["openaiModel"]?.toString()?.let { intent.putExtra("openaiModel", it) } + config["systemPrompt"]?.toString()?.let { intent.putExtra("systemPrompt", it) } config["mcpServer"]?.toString()?.let { intent.putExtra("mcpServer", it) } // 启动前台服务 diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt index 21c2cf5c4..fdf9535d5 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt @@ -54,6 +54,7 @@ class BleMediaService : Service(), CoroutineScope { private const val KEY_OPENAI_API_KEY = "openaiApiKey" private const val KEY_OPENAI_BASE_URL = "openaiBaseUrl" private const val KEY_OPENAI_MODEL = "openaiModel" + private const val KEY_SYSTEM_PROMPT = "systemPrompt" private const val KEY_MCP_SERVER = "mcpServer" // 用于跟踪服务是否正在运行 @@ -213,6 +214,7 @@ class BleMediaService : Service(), CoroutineScope { it.getStringExtra(KEY_OPENAI_API_KEY)?.let { value -> config[KEY_OPENAI_API_KEY] = value } it.getStringExtra(KEY_OPENAI_BASE_URL)?.let { value -> config[KEY_OPENAI_BASE_URL] = value } it.getStringExtra(KEY_OPENAI_MODEL)?.let { value -> config[KEY_OPENAI_MODEL] = value } + it.getStringExtra(KEY_SYSTEM_PROMPT)?.let { value -> config[KEY_SYSTEM_PROMPT] = value } it.getStringExtra(KEY_MCP_SERVER)?.let { value -> config[KEY_MCP_SERVER] = value } } FileLogger.i(TAG, "从Intent中提取到配置参数: $config") @@ -243,6 +245,7 @@ class BleMediaService : Service(), CoroutineScope { KEY_OPENAI_API_KEY to getConfigString(prefs, KEY_OPENAI_API_KEY, ""), KEY_OPENAI_BASE_URL to getConfigString(prefs, KEY_OPENAI_BASE_URL, "https://api.openai.com/v1"), KEY_OPENAI_MODEL to getConfigString(prefs, KEY_OPENAI_MODEL, "gpt-3.5-turbo"), + KEY_SYSTEM_PROMPT to getConfigString(prefs, KEY_SYSTEM_PROMPT, "gpt-3.5-turbo"), KEY_MCP_SERVER to getConfigString(prefs, KEY_MCP_SERVER, "") ) } diff --git a/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicService.kt b/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicService.kt index 038025181..0e3d34c3f 100644 --- a/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicService.kt +++ b/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicService.kt @@ -20,13 +20,18 @@ class MusicService : Service() { private const val NOTIFICATION_ID = 1002 private val isRunning = AtomicBoolean(false) + private var currentStatus: Boolean = false + private var currentUrlInternal: String? = null + + fun isServiceRunning(): Boolean = isRunning.get() + fun isPlaying(): Boolean = currentStatus + fun getCurrentUrl(): String? = currentUrlInternal } private var mediaPlayer: MediaPlayer? = null private val playlist = mutableListOf() // 可选:初始化为预置歌曲 private var currentIndex = 0 - private var isPlaying = false override fun onCreate() { super.onCreate() @@ -40,7 +45,7 @@ class MusicService : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val command = intent?.getStringExtra("command") ?: return START_NOT_STICKY - Log.i(TAG, "onStartCommand: $command") + Log.i(TAG, "onStartCommand: $command" ) when (command) { "play" -> { @@ -49,6 +54,18 @@ class MusicService : Service() { currentIndex = playlist.indexOf(url) play(url) } + "playlist" -> { + val urls = intent.getStringArrayListExtra("urls")?.filter { it.isNotEmpty() } ?: return START_NOT_STICKY + Log.i(TAG, "后台服务播放 playlist: $urls" ) + if (urls.isNotEmpty()) { + playlist.clear() + playlist.addAll(urls) + currentIndex = 0 + play(playlist[currentIndex]) + } else { + Log.w(TAG, "playlist 命令收到空列表") + } + } "pause" -> pause() "resume" -> resume() "stop" -> stopSelf() @@ -76,24 +93,31 @@ class MusicService : Service() { private fun play(url: String) { Log.i(TAG, "播放音乐: $url") releaseMediaPlayer() - + currentStatus = true; + currentUrlInternal = url; mediaPlayer = MediaPlayer().apply { setDataSource(url) setAudioStreamType(AudioManager.STREAM_MUSIC) setOnPreparedListener { start() - this@MusicService.isPlaying = true + currentStatus = true updateNotification("正在播放") Log.i(TAG, "开始播放") } setOnCompletionListener { Log.i(TAG, "播放完成") - this@MusicService.isPlaying = false - stopSelf() + // 播放下一首 + if (currentIndex + 1 < playlist.size) { + currentIndex++ + play(playlist[currentIndex]) // 播放下一首 + } else { + currentStatus = false + stopSelf() // 如果没有更多歌曲了,可以停止服务 + } } setOnErrorListener { _, what, extra -> Log.e(TAG, "播放错误: what=$what extra=$extra") - this@MusicService.isPlaying = false + currentStatus = false stopSelf() true } @@ -105,7 +129,7 @@ class MusicService : Service() { mediaPlayer?.let { if (it.isPlaying) { it.pause() - this@MusicService.isPlaying = false + currentStatus= false updateNotification("已暂停") Log.i(TAG, "暂停播放") } @@ -116,7 +140,7 @@ class MusicService : Service() { mediaPlayer?.let { if (!it.isPlaying) { it.start() - this@MusicService.isPlaying = true + currentStatus = true updateNotification("继续播放") Log.i(TAG, "继续播放") } diff --git a/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServicePlugin.kt b/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServicePlugin.kt index 6741e7937..f7686faf2 100644 --- a/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServicePlugin.kt +++ b/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServicePlugin.kt @@ -18,7 +18,7 @@ class MusicServicePlugin: FlutterPlugin, MethodCallHandler { override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { context = flutterPluginBinding.applicationContext - channel = MethodChannel(flutterPluginBinding.binaryMessenger, "music_service") + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.yunqiinnovation.music_service") channel.setMethodCallHandler(this) } @@ -30,6 +30,10 @@ class MusicServicePlugin: FlutterPlugin, MethodCallHandler { val url = call.argument("url") MusicServiceStarter.startServiceWithCommand(context, "play", url) } + "playlist" -> { + val urls = call.argument>("urls") + MusicServiceStarter.startServiceWithPlaylist(context, "playlist", urls) + } "pause" -> MusicServiceStarter.startServiceWithCommand(context, "pause") "resume" -> MusicServiceStarter.startServiceWithCommand(context, "resume") "stop" -> MusicServiceStarter.startServiceWithCommand(context, "stop") @@ -37,6 +41,15 @@ class MusicServicePlugin: FlutterPlugin, MethodCallHandler { val volume = call.argument("volume")?.toFloat() MusicServiceStarter.startServiceWithCommand(context, "setVolume", volume = volume) } + "getStatus" -> { + val isPlaying = MusicService.isPlaying() + val currentUrl = MusicService.getCurrentUrl() + val status = mapOf( + "isPlaying" to isPlaying, + "url" to currentUrl + ) + result.success(status) + } else -> result.notImplemented() } result.success(null) diff --git a/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServiceStarter.kt b/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServiceStarter.kt index dbcd05cca..9d8d290a1 100644 --- a/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServiceStarter.kt +++ b/local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServiceStarter.kt @@ -7,6 +7,7 @@ import androidx.core.content.ContextCompat object MusicServiceStarter { private val TAG = "MusicServiceStarter" + //播放单曲 fun startServiceWithCommand(context: Context, command: String, url: String? = null, volume: Float? = null) { Log.i(TAG, "startServiceWithCommand: $command $url") val intent = Intent(context, MusicService::class.java).apply { @@ -16,4 +17,14 @@ object MusicServiceStarter { } ContextCompat.startForegroundService(context, intent) } + + //启动播放列表 + fun startServiceWithPlaylist(context: Context,command: String, urls: List?,volume: Float? = null) { + Log.i(TAG, "startServiceWithPlaylist: $urls") + val intent = Intent(context, MusicService::class.java).apply { + putExtra("command", command) + putStringArrayListExtra("urls", ArrayList(urls!!)) // 注意类型是 ArrayList + } + ContextCompat.startForegroundService(context, intent) + } } \ No newline at end of file diff --git a/local_plugins/music_service/lib/music_service.dart b/local_plugins/music_service/lib/music_service.dart index cace4292f..ce3bb9e6d 100644 --- a/local_plugins/music_service/lib/music_service.dart +++ b/local_plugins/music_service/lib/music_service.dart @@ -52,4 +52,11 @@ class MusicService { 'setVolume', {'volume': volume}, ); // volume: 0.0~1.0 + Future> getStatus() async { + final result = await _methodChannel.invokeMethod('getStatus'); + return { + 'isPlaying': result?['isPlaying'] ?? false, + 'url': result?['url'] ?? "", + }; + } }