Browse Source

上传qq音乐服务代码

weicu
liwei1dao 1 year ago
parent
commit
495a5cfe76
  1. 4
      android/app/sign/DeepSound.properties
  2. 36
      lib/modules/qqmusic/controllers/qqmusic_playlist_controller.dart
  3. 2
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicLoginManager.kt
  4. 25
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicManager.kt
  5. 43
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicSingleton.kt
  6. 43
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/util/QPlayBindHelper.kt

4
android/app/sign/DeepSound.properties

@ -1,4 +0,0 @@
storePassword=13071376716
keyPassword=13071376716
keyAlias=DeepSound
storeFile=sign/DeepSound.keystore

36
lib/modules/qqmusic/controllers/qqmusic_playlist_controller.dart

@ -1,3 +1,5 @@
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../../data/models/song_model.dart'; import '../../../data/models/song_model.dart';
import '../../../data/services/qq_musoc.dart'; import '../../../data/services/qq_musoc.dart';
@ -22,6 +24,16 @@ class QQMusicPlaylistController extends GetxController {
void onInit() { void onInit() {
super.onInit(); super.onInit();
// 使用 WidgetsBinding.instance.addPostFrameCallback 延迟执行状态更新
WidgetsBinding.instance.addPostFrameCallback((_) {
_initializePlaylist();
});
print('🎵 [QQMusicPlaylistController] 控制器初始化完成');
}
/// 初始化播放列表 - 延迟执行避免构建期间状态更新
void _initializePlaylist() {
// 从路由参数获取播放列表 // 从路由参数获取播放列表
final arguments = Get.arguments; final arguments = Get.arguments;
if (arguments != null && arguments['playlist'] != null) { if (arguments != null && arguments['playlist'] != null) {
@ -76,11 +88,9 @@ class QQMusicPlaylistController extends GetxController {
// 界面初始化时同步播放状态和进度 // 界面初始化时同步播放状态和进度
_syncInitialState(); _syncInitialState();
print('🎵 [QQMusicPlaylistController] 控制器初始化完成');
} }
/// 同步初始播放状态 /// 同步初始播放状态 - 移除 update() 调用
Future<void> _syncInitialState() async { Future<void> _syncInitialState() async {
try { try {
Logger.d('QQMusicPlaylist', '开始同步初始播放状态'); Logger.d('QQMusicPlaylist', '开始同步初始播放状态');
@ -94,8 +104,8 @@ class QQMusicPlaylistController extends GetxController {
_qqMusicService.startPlaybackStateMonitor(); _qqMusicService.startPlaybackStateMonitor();
} }
// 触发UI更新 // 移除这行,避免在构建期间调用 update()
update(); // update();
Logger.d('QQMusicPlaylist', '初始状态同步完成'); Logger.d('QQMusicPlaylist', '初始状态同步完成');
} catch (e) { } catch (e) {
@ -103,6 +113,16 @@ class QQMusicPlaylistController extends GetxController {
} }
} }
/// 音乐状态变化回调 - 使用 SchedulerBinding 延迟更新
void _onMusicStateChanged() {
// 使用 SchedulerBinding 确保在构建完成后再更新
SchedulerBinding.instance.addPostFrameCallback((_) {
if (!isClosed) {
update();
}
});
}
@override @override
void onClose() { void onClose() {
// 移除状态监听器 // 移除状态监听器
@ -110,12 +130,6 @@ class QQMusicPlaylistController extends GetxController {
super.onClose(); super.onClose();
} }
/// 音乐状态变化回调
void _onMusicStateChanged() {
// 触发UI更新
update();
}
/// 播放歌曲 /// 播放歌曲
Future<void> playSong(SongModel song, {List<SongModel>? songList}) async { Future<void> playSong(SongModel song, {List<SongModel>? songList}) async {
try { try {

2
local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicLoginManager.kt

@ -76,7 +76,7 @@ class QQMusicLoginManager(private val activity: Activity) {
} else { } else {
Log.e(TAG, "QQ音乐服务绑定失败") Log.e(TAG, "QQ音乐服务绑定失败")
callback.onError("BIND_FAILED", "绑定QQ音乐服务失败,请确保已安装QQ音乐") callback.onError("APP_NOT_INSTALLED", "请先安装QQ音乐App才能使用音乐功能")
} }
} }
} }

25
local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicManager.kt

@ -5,6 +5,7 @@ import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.os.IBinder import android.os.IBinder
import android.util.Log import android.util.Log
@ -97,7 +98,7 @@ class QQMusicManager(private val activity: Activity) {
private fun bindQQMusicApiService() { private fun bindQQMusicApiService() {
Log.d(TAG, "开始绑定QQ音乐服务,平台类型: ${Config.BIND_PLATFORM}") Log.d(TAG, "开始绑定QQ音乐服务,平台类型: ${Config.BIND_PLATFORM}")
val intent = when (Config.BIND_PLATFORM) { val intent = when (Config.BIND_PLATFORM) {
CommonCmd.AIDL_PLATFORM_TYPE_PHONE -> { CommonCmd.AIDL_PLATFORM_TYPE_PHONE -> {
Intent("com.tencent.qqmusic.third.api.QQMusicApiService").apply { Intent("com.tencent.qqmusic.third.api.QQMusicApiService").apply {
@ -154,7 +155,7 @@ class QQMusicManager(private val activity: Activity) {
callback: SearchCallback callback: SearchCallback
) { ) {
Log.d(TAG, "searchSongs: keyword=$keyword, searchType=$searchType") Log.d(TAG, "searchSongs: keyword=$keyword, searchType=$searchType")
if (!isBindQQMusicService || qqmusicApi == null) { if (!isBindQQMusicService || qqmusicApi == null) {
Log.e(TAG, "QQ音乐服务未连接") Log.e(TAG, "QQ音乐服务未连接")
callback.onError(-1, "QQ音乐服务未连接") callback.onError(-1, "QQ音乐服务未连接")
@ -275,6 +276,7 @@ class QQMusicManager(private val activity: Activity) {
index: Int = 0, index: Int = 0,
callback: PlayCallback callback: PlayCallback
) { ) {
if (!isBindQQMusicService || qqmusicApi == null) { if (!isBindQQMusicService || qqmusicApi == null) {
callback.onError(-1, "QQ音乐服务未连接") callback.onError(-1, "QQ音乐服务未连接")
return return
@ -303,6 +305,7 @@ class QQMusicManager(private val activity: Activity) {
* 播放音乐 * 播放音乐
*/ */
fun playMusic(callback: PlayCallback) { fun playMusic(callback: PlayCallback) {
if (!isBindQQMusicService || qqmusicApi == null) { if (!isBindQQMusicService || qqmusicApi == null) {
callback.onError(-1, "QQ音乐服务未连接") callback.onError(-1, "QQ音乐服务未连接")
return return
@ -614,10 +617,24 @@ class QQMusicManager(private val activity: Activity) {
if (isBindQQMusicService) { if (isBindQQMusicService) {
activity.unbindService(serviceConnection) activity.unbindService(serviceConnection)
isBindQQMusicService = false isBindQQMusicService = false
Log.d(TAG, "QQ音乐服务已解绑")
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "清理资源异常", e) Log.e(TAG, "解绑QQ音乐服务失败", e)
}
}
/**
* 获取当前平台对应的QQ音乐包名
*/
private fun getQQMusicPackageName(): String {
return when (Config.BIND_PLATFORM) {
CommonCmd.AIDL_PLATFORM_TYPE_PHONE -> "com.tencent.qqmusic"
CommonCmd.AIDL_PLATFORM_TYPE_CAR -> "com.tencent.qqmusiccar"
CommonCmd.AIDL_PLATFORM_TYPE_TV -> "com.tencent.qqmusictv"
else -> "com.tencent.qqmusic"
} }
qqmusicApi = null
} }
} }

43
local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicSingleton.kt

@ -3,6 +3,7 @@ package com.yunqiinnovation.qq_music
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
@ -56,7 +57,32 @@ class QQMusicSingleton private constructor() {
loadSavedLoginInfo() loadSavedLoginInfo()
Log.d(TAG, "QQ音乐单例初始化完成") Log.d(TAG, "QQ音乐单例初始化完成")
} }
/**
* 检查QQ音乐App是否已安装
*/
private fun isQQMusicInstalled(): Boolean {
val packageName = when (Config.BIND_PLATFORM) {
CommonCmd.AIDL_PLATFORM_TYPE_PHONE -> "com.tencent.qqmusic"
CommonCmd.AIDL_PLATFORM_TYPE_CAR -> "com.tencent.qqmusiccar"
CommonCmd.AIDL_PLATFORM_TYPE_TV -> "com.tencent.qqmusictv"
else -> {
Log.e(TAG, "不支持的平台类型: ${Config.BIND_PLATFORM}")
return false
}
}
return try {
this.activity?.packageManager?.getPackageInfo(packageName, 0)
Log.d(TAG, "QQ音乐app已安装: $packageName")
true
} catch (e: PackageManager.NameNotFoundException) {
Log.w(TAG, "QQ音乐app未安装: $packageName")
false
} catch (e: Exception) {
Log.e(TAG, "检查QQ音乐app安装状态失败", e)
false
}
}
/** /**
* 检查是否已登录且未过期 * 检查是否已登录且未过期
*/ */
@ -88,6 +114,11 @@ class QQMusicSingleton private constructor() {
* 登录接口 * 登录接口
*/ */
fun login(callback: (Result<LoginResult>) -> Unit) { fun login(callback: (Result<LoginResult>) -> Unit) {
if (!isQQMusicInstalled()){
callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件")))
return
}
// 先检查是否已经登录且未过期 // 先检查是否已经登录且未过期
if (isLoggedIn()) { if (isLoggedIn()) {
Log.d(TAG, "用户已登录且未过期,直接返回登录信息") Log.d(TAG, "用户已登录且未过期,直接返回登录信息")
@ -140,6 +171,10 @@ class QQMusicSingleton private constructor() {
searchType: Int = Data.SearchType.SEARCH_TYPE_SONG, searchType: Int = Data.SearchType.SEARCH_TYPE_SONG,
callback: (Result<List<Map<String, Any?>>>) -> Unit callback: (Result<List<Map<String, Any?>>>) -> Unit
) { ) {
if (!isQQMusicInstalled()){
callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件")))
return
}
searchWithRetry(keyword, searchType, 0, callback) searchWithRetry(keyword, searchType, 0, callback)
} }
@ -264,12 +299,16 @@ class QQMusicSingleton private constructor() {
index: Int = 0, index: Int = 0,
callback: (Result<Unit>) -> Unit callback: (Result<Unit>) -> Unit
) { ) {
val currentActivity = activity val currentActivity = activity
if (currentActivity == null) { if (currentActivity == null) {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return return
} }
if (!isQQMusicInstalled()){
callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件")))
return
}
ensureMusicManager(currentActivity) ensureMusicManager(currentActivity)
musicManager?.playSongs(songMids, index, object : QQMusicManager.PlayCallback { musicManager?.playSongs(songMids, index, object : QQMusicManager.PlayCallback {
override fun onSuccess() { override fun onSuccess() {

43
local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/util/QPlayBindHelper.kt

@ -4,6 +4,7 @@ import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.os.Handler import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.os.Looper import android.os.Looper
@ -12,6 +13,7 @@ import android.widget.Toast
import com.tencent.qqmusic.third.api.contract.CommonCmd import com.tencent.qqmusic.third.api.contract.CommonCmd
import com.tencent.qqmusic.third.api.contract.CommonCmd.AIDL_PLATFORM_TYPE_PHONE import com.tencent.qqmusic.third.api.contract.CommonCmd.AIDL_PLATFORM_TYPE_PHONE
import com.tencent.qqmusic.third.api.contract.IQQMusicApi import com.tencent.qqmusic.third.api.contract.IQQMusicApi
import com.yunqiinnovation.qq_music.Config
class QPlayBindHelper(private val context: Context, private val bindPlatform: String = AIDL_PLATFORM_TYPE_PHONE) { class QPlayBindHelper(private val context: Context, private val bindPlatform: String = AIDL_PLATFORM_TYPE_PHONE) {
companion object { companion object {
@ -104,11 +106,50 @@ class QPlayBindHelper(private val context: Context, private val bindPlatform: St
bindQQMusic(callback) bindQQMusic(callback)
} }
/**
* 检查QQ音乐App是否已安装
*/
private fun isQQMusicInstalled(): Boolean {
return try {
val packageName = getQQMusicPackageName()
context.packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES)
true
} catch (e: PackageManager.NameNotFoundException) {
Log.w(TAG, "QQ音乐App未安装: ${e.message}")
false
}
}
/**
* 获取QQ音乐包名
*/
private fun getQQMusicPackageName(): String {
return when (Config.BIND_PLATFORM) {
CommonCmd.AIDL_PLATFORM_TYPE_PHONE -> "com.tencent.qqmusic"
CommonCmd.AIDL_PLATFORM_TYPE_CAR -> "com.tencent.qqmusiccar"
CommonCmd.AIDL_PLATFORM_TYPE_TV -> "com.tencent.qqmusictv"
else -> "com.tencent.qqmusic"
}
}
fun ensureQQMusicBindByStartProcess(isStartProcess: Boolean = true, callback: ((Boolean) -> Unit)? = null) { fun ensureQQMusicBindByStartProcess(isStartProcess: Boolean = true, callback: ((Boolean) -> Unit)? = null) {
// 首先检查QQ音乐是否已安装
if (!isQQMusicInstalled()) {
Log.e(TAG, "QQ音乐App未安装,无法绑定服务")
callback?.invoke(false)
return
}
bindQQMusic({ succeed -> bindQQMusic({ succeed ->
if (!succeed) { if (!succeed) {
if (isStartProcess) { if (isStartProcess) {
CommonCmd.startQQMusicProcess(context, context.packageName) try {
CommonCmd.startQQMusicProcess(context, context.packageName)
} catch (e: Exception) {
Log.e(TAG, "启动QQ音乐进程失败: ${e.message}")
callback?.invoke(false)
return@bindQQMusic
}
} }
handler.postDelayed({ handler.postDelayed({
ensureQQMusicBindByStartProcess(false, callback) ensureQQMusicBindByStartProcess(false, callback)

Loading…
Cancel
Save