Browse Source

优化qq音乐服务代码,修复搜索不主动播放的问题

newdev_shunjiawei
liwei1dao 1 year ago
parent
commit
27e4b73202
  1. 120
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/QQMusicSystemFunctionHandler.kt
  2. 35
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicLoginManager.kt
  3. 106
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicManager.kt
  4. 438
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicSingleton.kt
  5. 16
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QqMusicPlugin.kt

120
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/QQMusicSystemFunctionHandler.kt

@ -48,41 +48,7 @@ class QQMusicSystemFunctionHandler(private val context: Context? = null) {
), ),
QQMusicSearchHandler(context) QQMusicSearchHandler(context)
) )
// QQ音乐播放歌曲
// client.registerLocalFunction(
// "qqmusic_play_songs",
// "播放QQ音乐歌曲列表",
// mapOf(
// "type" to "object",
// "properties" to mapOf(
// "songMids" to mapOf(
// "type" to "array",
// "description" to "歌曲ID列表",
// "items" to mapOf("type" to "string")
// ),
// "index" to mapOf(
// "type" to "integer",
// "description" to "开始播放的歌曲索引,默认为0"
// )
// ),
// "required" to listOf("songMids")
// ),
// QQMusicPlaySongsHandler(context)
// )
//QQ音乐播放控制
// client.registerLocalFunction(
// "qqmusic_play",
// "播放QQ音乐",
// mapOf(
// "type" to "object",
// "properties" to emptyMap<String, Any>(),
// "required" to emptyList<String>()
// ),
// QQMusicPlayHandler(context)
// )
client.registerLocalFunction( client.registerLocalFunction(
"qqmusic_pause", "qqmusic_pause",
"暂停QQ音乐", "暂停QQ音乐",
@ -137,40 +103,7 @@ class QQMusicSystemFunctionHandler(private val context: Context? = null) {
), ),
QQMusicPreviousHandler(context) QQMusicPreviousHandler(context)
) )
// QQ音乐状态查询
client.registerLocalFunction(
"qqmusic_get_playback_state",
"获取QQ音乐播放状态",
mapOf(
"type" to "object",
"properties" to emptyMap<String, Any>(),
"required" to emptyList<String>()
),
QQMusicGetPlaybackStateHandler(context)
)
client.registerLocalFunction(
"qqmusic_get_current_song",
"获取QQ音乐当前播放歌曲信息",
mapOf(
"type" to "object",
"properties" to emptyMap<String, Any>(),
"required" to emptyList<String>()
),
QQMusicGetCurrentSongHandler(context)
)
client.registerLocalFunction(
"qqmusic_get_play_time",
"获取QQ音乐播放时间信息",
mapOf(
"type" to "object",
"properties" to emptyMap<String, Any>(),
"required" to emptyList<String>()
),
QQMusicGetPlayTimeHandler(context)
)
Log.d(TAG, "QQ音乐所有功能注册完成") Log.d(TAG, "QQ音乐所有功能注册完成")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "注册QQ音乐功能失败: ${e.message}", e) Log.e(TAG, "注册QQ音乐功能失败: ${e.message}", e)
@ -179,31 +112,6 @@ class QQMusicSystemFunctionHandler(private val context: Context? = null) {
} }
private class QQMusicLoginHandler(private val context: Context?) : FunctionHandler {
override suspend fun handle(arguments: Map<String, Any>): String {
return try {
val qqMusicSingleton = QQMusicSingleton.getInstance()
var result = ""
qqMusicSingleton.login { loginResult ->
result = if (loginResult.isSuccess) {
val loginData = loginResult.getOrNull()
"{\"success\": true, \"message\": \"登录成功\", \"data\": {\"openId\": \"${loginData?.openId}\", \"openToken\": \"${loginData?.openToken}\", \"expireTime\": \"${loginData?.expireTime}\"}}"
} else {
val error = loginResult.exceptionOrNull()
"{\"success\": false, \"message\": \"登录失败: ${error?.message}\"}"
}
}
// 等待回调完成
delay(3000)
result.ifEmpty { "{\"success\": false, \"message\": \"登录超时\"}" }
} catch (e: Exception) {
Log.e("QQMusicLoginHandler", "QQ音乐登录失败", e)
"{\"success\": false, \"message\": \"登录异常: ${e.message}\"}"
}
}
}
private class QQMusicSearchHandler(private val context: Context?) : FunctionHandler { private class QQMusicSearchHandler(private val context: Context?) : FunctionHandler {
override suspend fun handle(arguments: Map<String, Any>): String { override suspend fun handle(arguments: Map<String, Any>): String {
return try { return try {
@ -537,27 +445,3 @@ private class QQMusicGetCurrentSongHandler(private val context: Context?) : Func
} }
} }
private class QQMusicGetPlayTimeHandler(private val context: Context?) : FunctionHandler {
override suspend fun handle(arguments: Map<String, Any>): String {
return try {
val qqMusicSingleton = QQMusicSingleton.getInstance()
var result = ""
qqMusicSingleton.getPlayTime { timeResult ->
result = if (timeResult.isSuccess) {
val timeInfo = timeResult.getOrNull()
"{\"success\": true, \"message\": \"获取播放时间成功\", \"data\": {\"totalTime\": ${timeInfo?.totalTime}, \"currentTime\": ${timeInfo?.currentTime}}}"
} else {
val error = timeResult.exceptionOrNull()
"{\"success\": false, \"message\": \"获取播放时间失败: ${error?.message}\"}"
}
}
delay(2000)
result.ifEmpty { "{\"success\": false, \"message\": \"获取播放时间超时\"}" }
} catch (e: Exception) {
Log.e("QQMusicLoginHandler", "获取QQ音乐播放时间失败", e)
"{\"success\": false, \"message\": \"获取播放时间异常: ${e.message}\"}"
}
}
}

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

@ -27,7 +27,7 @@ class QQMusicLoginManager(private val context: Context) {
// 定义错误码常量,替代 LoginExampleActivity 中的常量 // 定义错误码常量,替代 LoginExampleActivity 中的常量
const val LOGIN_FAILED = 1 const val LOGIN_FAILED = 1
} }
private var isBindQQMusicService = false
private lateinit var qPlayBindHelper: QPlayBindHelper private lateinit var qPlayBindHelper: QPlayBindHelper
private var qqMusicApi: IQQMusicApi? = null private var qqMusicApi: IQQMusicApi? = null
private lateinit var qqMusicApiWrapper: QQMusicApiWrapper private lateinit var qqMusicApiWrapper: QQMusicApiWrapper
@ -37,10 +37,15 @@ class QQMusicLoginManager(private val context: Context) {
* 登录结果回调接口 * 登录结果回调接口
*/ */
interface LoginCallback { interface LoginCallback {
fun onSuccess(openId: String, openToken: String, expireTime: String) fun onSuccess()
fun onError(errorCode: String, errorMessage: String) fun onError(errorCode: String, errorMessage: String)
} }
//是否连接
public val isLogined: Boolean
get() = isBindQQMusicService && qPlayBindHelper != null
/** /**
* 开始登录流程 * 开始登录流程
*/ */
@ -101,8 +106,8 @@ class QQMusicLoginManager(private val context: Context) {
ErrorCodes.ERROR_OK -> { ErrorCodes.ERROR_OK -> {
// 已授权,直接请求登录信息 // 已授权,直接请求登录信息
Log.d(TAG, "已授权,直接获取登录信息") Log.d(TAG, "已授权,直接获取登录信息")
callback.onSuccess("","","") isBindQQMusicService = true
// requestAuth(callback) callback.onSuccess()
} }
ErrorCodes.ERROR_API_NO_PERMISSION -> { ErrorCodes.ERROR_API_NO_PERMISSION -> {
// 需要授权 // 需要授权
@ -110,12 +115,14 @@ class QQMusicLoginManager(private val context: Context) {
startAuthProcess(callback) startAuthProcess(callback)
} }
else -> { else -> {
isBindQQMusicService = false
Log.e(TAG, "检查授权状态失败: $code") Log.e(TAG, "检查授权状态失败: $code")
callback.onError("AUTH_CHECK_FAILED", "检查授权状态失败") callback.onError("AUTH_CHECK_FAILED", "检查授权状态失败")
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "检查授权状态异常", e) Log.e(TAG, "检查授权状态异常", e)
isBindQQMusicService = false
callback.onError("AUTH_CHECK_ERROR", "检查授权状态异常: ${e.message}") callback.onError("AUTH_CHECK_ERROR", "检查授权状态异常: ${e.message}")
} }
} }
@ -172,7 +179,7 @@ class QQMusicLoginManager(private val context: Context) {
}, },
failed = { errorCode, errorMsg -> failed = { errorCode, errorMsg ->
Log.e(TAG, "授权失败: code=$errorCode, msg=$errorMsg") Log.e(TAG, "授权失败: code=$errorCode, msg=$errorMsg")
isBindQQMusicService = false
val errorMessage = when (errorCode) { val errorMessage = when (errorCode) {
LOGIN_FAILED -> "登录失败: $errorMsg" LOGIN_FAILED -> "登录失败: $errorMsg"
else -> "授权失败: $errorMsg" else -> "授权失败: $errorMsg"
@ -197,22 +204,23 @@ class QQMusicLoginManager(private val context: Context) {
// 检查签名 // 检查签名
if (OpenIDHelper.checkQMSign(sign, returnNonce)) { if (OpenIDHelper.checkQMSign(sign, returnNonce)) {
val openId = appDecryptJson.getString(Keys.API_RETURN_KEY_OPEN_ID) // val openId = appDecryptJson.getString(Keys.API_RETURN_KEY_OPEN_ID)
val openToken = appDecryptJson.getString(Keys.API_RETURN_KEY_OPEN_TOKEN) // val openToken = appDecryptJson.getString(Keys.API_RETURN_KEY_OPEN_TOKEN)
val expireTime = appDecryptJson.getString(Keys.API_PARAM_KEY_SDK_EXPIRETIME) // val expireTime = appDecryptJson.getString(Keys.API_PARAM_KEY_SDK_EXPIRETIME)
// Log.d(TAG, "登录成功: openId=$openId")
Log.d(TAG, "登录成功: openId=$openId") isBindQQMusicService = true
handler.post { handler.post {
callback.onSuccess(openId, openToken, expireTime) callback.onSuccess()
} }
} else { } else {
isBindQQMusicService = false
Log.e(TAG, "签名验证失败") Log.e(TAG, "签名验证失败")
handler.post { handler.post {
callback.onError("SIGN_ERROR", "签名验证失败") callback.onError("SIGN_ERROR", "签名验证失败")
} }
} }
} else { } else {
isBindQQMusicService = false
Log.e(TAG, "解密失败") Log.e(TAG, "解密失败")
handler.post { handler.post {
callback.onError("DECRYPT_ERROR", "解密失败") callback.onError("DECRYPT_ERROR", "解密失败")
@ -220,6 +228,7 @@ class QQMusicLoginManager(private val context: Context) {
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "解析登录结果失败", e) Log.e(TAG, "解析登录结果失败", e)
isBindQQMusicService = false
handler.post { handler.post {
callback.onError("PARSE_ERROR", "解析登录结果失败: ${e.message}") callback.onError("PARSE_ERROR", "解析登录结果失败: ${e.message}")
} }

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

@ -39,38 +39,27 @@ class QQMusicManager(private val context: Context) {
fun onSuccess(data: Map<String, Any?>) fun onSuccess(data: Map<String, Any?>)
fun onError(errorCode: Int, errorMessage: String) fun onError(errorCode: Int, errorMessage: String)
} }
private val serviceConnection = object : ServiceConnection { // 新增:绑定服务的回调接口
override fun onServiceConnected(name: ComponentName?, service: IBinder?) { interface BindCallback {
Log.d(TAG, "QQ音乐服务连接成功") fun onSuccess()
qqmusicApi = IQQMusicApi.Stub.asInterface(service) fun onError(errorCode: Int, errorMessage: String)
isBindQQMusicService = true
// 初始化SDK
CommonCmd.init(Config.BIND_PLATFORM)
// 延迟发送hi消息,确保Binder连接稳定
Handler(Looper.getMainLooper()).postDelayed({
sayHi()
}, 500) // 延迟500ms
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(TAG, "QQ音乐服务连接断开")
qqmusicApi = null
isBindQQMusicService = false
//重连绑定
bindQQMusicApiService();
}
} }
init { init {
bindQQMusicApiService() // bindQQMusicApiService()
} }
//是否连接
public val isConnected: Boolean
get() = isBindQQMusicService && qqmusicApi != null
private fun sayHi() { private fun sayHi() {
if (!isBindQQMusicService || qqmusicApi == null) { if (!isBindQQMusicService || qqmusicApi == null) {
Log.w(TAG, "QQ音乐服务未连接,无法发送hi消息") Log.w(TAG, "连接QQ音乐失败,可能是QQ音乐帐号状态异常,请打开QQ音乐检查帐号状态是否异常?")
return return
} }
@ -97,9 +86,9 @@ class QQMusicManager(private val context: Context) {
} }
} }
fun bindQQMusicApiService() { fun bindQQMusicApiService(callback: BindCallback? = null) {
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 {
@ -118,6 +107,7 @@ class QQMusicManager(private val context: Context) {
} }
else -> { else -> {
Log.e(TAG, "不支持的平台类型: ${Config.BIND_PLATFORM}") Log.e(TAG, "不支持的平台类型: ${Config.BIND_PLATFORM}")
callback?.onError(-1, "不支持的平台类型: ${Config.BIND_PLATFORM}")
return return
} }
} }
@ -125,22 +115,71 @@ class QQMusicManager(private val context: Context) {
Log.d(TAG, "绑定Intent: action=${intent.action}, package=${intent.`package`}") Log.d(TAG, "绑定Intent: action=${intent.action}, package=${intent.`package`}")
try { try {
val bindResult = context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) val bindResult = context.bindService(intent, object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.d(TAG, "QQ音乐服务连接成功")
qqmusicApi = IQQMusicApi.Stub.asInterface(service)
isBindQQMusicService = true
// 初始化SDK
CommonCmd.init(Config.BIND_PLATFORM)
// 延迟发送hi消息,确保Binder连接稳定
Handler(Looper.getMainLooper()).postDelayed({
sayHi()
// 绑定成功后调用回调
callback?.onSuccess()
}, 500) // 延迟500ms
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(TAG, "QQ音乐服务连接断开")
qqmusicApi = null
isBindQQMusicService = false
// 服务断开时通知错误
callback?.onError(-2, "QQ音乐服务连接断开")
//重连绑定
bindQQMusicApiService(callback)
}
}, Context.BIND_AUTO_CREATE)
Log.d(TAG, "bindService结果: $bindResult") Log.d(TAG, "bindService结果: $bindResult")
if (!bindResult) { if (!bindResult) {
Log.e(TAG, "绑定QQ音乐服务失败,尝试启动QQ音乐进程") Log.e(TAG, "绑定QQ音乐服务失败,尝试启动QQ音乐进程")
// 尝试启动QQ音乐进程 // 尝试启动QQ音乐进程
CommonCmd.startQQMusicProcess(context, context.packageName) CommonCmd.startQQMusicProcess(context, context.packageName)
// 延迟重试绑定 // 延迟重试绑定
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
Log.d(TAG, "重试绑定QQ音乐服务") val retryResult = context.bindService(intent, object : ServiceConnection {
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.d(TAG, "QQ音乐服务连接成功")
qqmusicApi = IQQMusicApi.Stub.asInterface(service)
isBindQQMusicService = true
// 初始化SDK
CommonCmd.init(Config.BIND_PLATFORM)
// 延迟发送hi消息,确保Binder连接稳定
Handler(Looper.getMainLooper()).postDelayed({
sayHi()
callback?.onSuccess()
}, 500) // 延迟500ms
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(TAG, "QQ音乐服务连接断开")
qqmusicApi = null
isBindQQMusicService = false
}
}, Context.BIND_AUTO_CREATE)
Log.d(TAG, "重试绑定QQ音乐服务 $retryResult")
if (!retryResult) {
callback?.onError(-3, "重试绑定QQ音乐服务失败")
}
}, 2000) }, 2000)
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "绑定QQ音乐服务异常", e) Log.e(TAG, "绑定QQ音乐服务异常", e)
callback?.onError(-4, "绑定QQ音乐服务异常: ${e.message}")
} }
} }
@ -659,7 +698,6 @@ class QQMusicManager(private val context: Context) {
fun cleanup() { fun cleanup() {
try { try {
if (isBindQQMusicService) { if (isBindQQMusicService) {
context.unbindService(serviceConnection)
isBindQQMusicService = false isBindQQMusicService = false
Log.d(TAG, "QQ音乐服务已解绑") Log.d(TAG, "QQ音乐服务已解绑")
} }

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

@ -8,8 +8,6 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import com.tencent.qqmusic.third.api.contract.* import com.tencent.qqmusic.third.api.contract.*
import java.text.SimpleDateFormat
import java.util.*
/** /**
* QQ音乐单例管理器 * QQ音乐单例管理器
@ -23,12 +21,7 @@ class QQMusicSingleton private constructor() {
private const val KEY_OPEN_ID = "open_id" private const val KEY_OPEN_ID = "open_id"
private const val KEY_OPEN_TOKEN = "open_token" private const val KEY_OPEN_TOKEN = "open_token"
private const val KEY_EXPIRE_TIME = "expire_time" private const val KEY_EXPIRE_TIME = "expire_time"
private const val LOGIN_WAIT_DELAY_MS = 30000L
// 添加搜索重试相关常量
private const val SEARCH_TIMEOUT_MS = 10000L
private const val MAX_RETRY_COUNT = 1
private const val RETRY_DELAY_MS = 1000L
@Volatile @Volatile
private var INSTANCE: QQMusicSingleton? = null private var INSTANCE: QQMusicSingleton? = null
@ -53,9 +46,9 @@ class QQMusicSingleton private constructor() {
*/ */
fun initialize(context: Context) { fun initialize(context: Context) {
this.context = context this.context = context
this.sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) // this.sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
loadSavedLoginInfo() // loadSavedLoginInfo()
Log.d(TAG, "QQ音乐单例初始化完成") // Log.d(TAG, "QQ音乐单例初始化完成")
} }
/** /**
* 检查QQ音乐App是否已安装 * 检查QQ音乐App是否已安装
@ -87,58 +80,54 @@ class QQMusicSingleton private constructor() {
* 检查是否已登录且未过期 * 检查是否已登录且未过期
*/ */
fun isLoggedIn(): Boolean { fun isLoggedIn(): Boolean {
if (currentLoginResult == null){ return loginManager != null && loginManager!!.isLogined
loadSavedLoginInfo()
if (currentLoginResult == null){
return false
}
return true
}else{
return true
}
} }
/**
* 获取当前登录信息
*/
fun getCurrentLoginInfo(): LoginResult? {
return if (isLoggedIn()) currentLoginResult else null
}
/** /**
* 登录接口 * 登录接口
*/ */
fun login(callback: (Result<LoginResult>) -> Unit) { fun login(callback: (Result<Unit>) -> Unit) {
if (!isQQMusicInstalled()){ if (!isQQMusicInstalled()){
callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件"))) callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件")))
return return
} }
val currentActivity = context val currentContext = context
// 先检查是否已经登录且未过期 // 先检查是否已经登录且未过期
if (isLoggedIn()) { if (isLoggedIn()) {
Log.d(TAG, "用户已登录且未过期,直接返回登录信息") Log.d(TAG, "用户已登录且未过期,直接返回登录信息")
ensureMusicManager(currentActivity!!) ensureMusicManager(currentContext!!,object: QQMusicManager. BindCallback{
callback(Result.success(currentLoginResult!!)) override fun onSuccess() {
callback(Result.success(Unit))
}
override fun onError(errorCode: Int, errorMessage: String) {
callback(Result.failure(Exception("no logined $errorMessage")))
}
})
return return
} }
if (currentActivity == null) { if (currentContext == null) {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return return
} }
Log.d(TAG, "开始新的登录流程") Log.d(TAG, "开始新的登录流程")
loginManager = QQMusicLoginManager(currentActivity) loginManager = QQMusicLoginManager(currentContext)
loginManager?.startLogin(object : QQMusicLoginManager.LoginCallback { loginManager?.startLogin(object : QQMusicLoginManager.LoginCallback {
override fun onSuccess(openId: String, openToken: String, expireTime: String) { override fun onSuccess() {
handler.post { handler.post {
ensureMusicManager(currentActivity!!) ensureMusicManager(currentContext,object: QQMusicManager. BindCallback{
val result = LoginResult(openId, openToken, expireTime) override fun onSuccess() {
currentLoginResult = result callback(Result.success(Unit))
saveLoginInfo(result) }
Log.d(TAG, "登录成功并已保存: $result")
callback(Result.success(result)) override fun onError(errorCode: Int, errorMessage: String) {
callback(Result.failure(Exception("no logined $errorMessage")))
}
})
} }
} }
@ -164,8 +153,16 @@ class QQMusicSingleton private constructor() {
// 先检查是否已经登录且未过期 // 先检查是否已经登录且未过期
if (isLoggedIn()) { if (isLoggedIn()) {
Log.d(TAG, "用户已登录且未过期,直接返回登录信息") Log.d(TAG, "用户已登录且未过期,直接返回登录信息")
ensureMusicManager(currentActivity!!) ensureMusicManager(currentActivity!!,object: QQMusicManager. BindCallback{
callback(Result.success(currentLoginResult!!)) override fun onSuccess() {
callback(Result.success(currentLoginResult!!))
}
override fun onError(errorCode: Int, errorMessage: String) {
callback(Result.failure(Exception("no logined $errorMessage")))
}
})
return return
}else{ }else{
callback(Result.failure(Exception("no logined"))) callback(Result.failure(Exception("no logined")))
@ -190,129 +187,12 @@ 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()){ executeSearchbackAction("搜索歌曲",0,2) { musicManager, searchCallback ->
callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件"))) musicManager.searchSongs(keyword,searchType,searchCallback)
return }(callback)
}
searchWithRetry(keyword, searchType, 1, callback)
}
/**
* 带重试的搜索实现
*/
private fun searchWithRetry(
keyword: String,
searchType: Int,
retryCount: Int,
callback: (Result<List<Map<String, Any?>>>) -> Unit
) {
val currentActivity = context
if (currentActivity == null) {
callback(Result.failure(Exception("Activity not available")))
return
}
// 检查登录状态
if (!isLoggedIn()) {
Log.d(TAG, "搜索前检测到未登录,开始自动登录")
login { loginResult ->
if (loginResult.isSuccess) {
Log.d(TAG, "自动登录成功,等待QQ音乐服务连接...")
handler.postDelayed({
Log.d(TAG, "等待完成,开始搜索")
performSearchWithTimeout(keyword, searchType, retryCount, callback)
}, LOGIN_WAIT_DELAY_MS)
} else {
callback(Result.failure(Exception("自动登录失败: ${loginResult.exceptionOrNull()?.message}")))
}
}
} else {
performSearchWithTimeout(keyword, searchType, retryCount, callback)
}
}
/**
* 执行带超时的搜索操作
*/
private fun performSearchWithTimeout(
keyword: String,
searchType: Int,
retryCount: Int,
callback: (Result<List<Map<String, Any?>>>) -> Unit
) {
val currentActivity = context ?: return
val searchKey = "${keyword}_${System.currentTimeMillis()}"
var completed = false
Log.d(TAG, "开始搜索: $keyword (第${retryCount + 1}次)")
// 设置超时
val timeoutRunnable = Runnable {
if (!completed) {
completed = true
searchTimeouts.remove(searchKey)
Log.w(TAG, "搜索超时: $keyword")
if (retryCount < MAX_RETRY_COUNT) {
Log.d(TAG, "准备重试: ${retryCount + 1}/$MAX_RETRY_COUNT")
handler.postDelayed({
searchWithRetry(keyword, searchType, retryCount + 1, callback)
}, RETRY_DELAY_MS)
} else {
callback(Result.failure(Exception("搜索超时,已重试${MAX_RETRY_COUNT}次")))
}
}
}
searchTimeouts[searchKey] = timeoutRunnable
handler.postDelayed(timeoutRunnable, SEARCH_TIMEOUT_MS)
ensureMusicManager(currentActivity)
musicManager?.searchSongs(keyword, searchType, object : QQMusicManager.SearchCallback {
override fun onSuccess(songs: List<Map<String, Any?>>) {
handler.post {
if (!completed) {
completed = true
searchTimeouts.remove(searchKey)?.let { handler.removeCallbacks(it) }
Log.d(TAG, "搜索成功: 找到 ${songs.size} 首歌曲")
callback(Result.success(songs))
}
}
}
override fun onError(errorCode: Int, errorMessage: String) {
handler.post {
if (!completed) {
completed = true
searchTimeouts.remove(searchKey)?.let { handler.removeCallbacks(it) }
Log.e(TAG, "搜索失败: code=$errorCode, message=$errorMessage")
// 如果是登录相关错误,清除本地登录信息
if (errorMessage.contains("登录") || errorMessage.contains("授权")) {
Log.d(TAG, "检测到登录相关错误,清除本地登录信息")
logout()
}
// 网络相关错误才重试
if (retryCount < MAX_RETRY_COUNT &&
(errorMessage.contains("网络") || errorMessage.contains("超时") ||
errorMessage.contains("连接") || errorCode == -1)) {
Log.d(TAG, "网络错误,准备重试: ${retryCount + 1}/$MAX_RETRY_COUNT")
if (errorCode == -1){
musicManager?.bindQQMusicApiService()
}
handler.postDelayed({
searchWithRetry(keyword, searchType, retryCount + 1, callback)
}, RETRY_DELAY_MS)
} else {
callback(Result.failure(Exception("搜索失败: $errorMessage")))
}
}
}
}
})
} }
/** /**
* 播放歌曲列表(带自动登录检查) * 播放歌曲列表(带自动登录检查)
*/ */
@ -321,8 +201,8 @@ class QQMusicSingleton private constructor() {
index: Int = 0, index: Int = 0,
callback: (Result<Unit>) -> Unit callback: (Result<Unit>) -> Unit
) { ) {
Log.e(TAG, "播放音乐列表: $songMids") executePlaybackAction("播放列表",0,2) { musicManager, playCallback ->
executePlaybackAction("播放列表") { musicManager, playCallback -> Log.d(TAG, "播放歌曲:${songMids[index]}")
musicManager.playSongs(songMids, index,playCallback) musicManager.playSongs(songMids, index,playCallback)
}(callback) }(callback)
} }
@ -331,7 +211,7 @@ class QQMusicSingleton private constructor() {
* 播放音乐 * 播放音乐
*/ */
fun playMusic(callback: (Result<Unit>) -> Unit) { fun playMusic(callback: (Result<Unit>) -> Unit) {
executePlaybackAction("播放") { musicManager, playCallback -> executePlaybackAction("播放",0,2) { musicManager, playCallback ->
musicManager.playMusic(playCallback) musicManager.playMusic(playCallback)
}(callback) }(callback)
} }
@ -386,11 +266,12 @@ class QQMusicSingleton private constructor() {
} }
}) })
} }
/** /**
* 恢复播放 * 恢复播放
*/ */
fun resumeMusic(callback: (Result<Unit>) -> Unit) { fun resumeMusic(callback: (Result<Unit>) -> Unit) {
executePlaybackAction("恢复播放") { musicManager, playCallback -> executePlaybackAction("恢复播放",0,0) { musicManager, playCallback ->
musicManager.resumeMusic(playCallback) musicManager.resumeMusic(playCallback)
}(callback) }(callback)
} }
@ -399,7 +280,7 @@ class QQMusicSingleton private constructor() {
* 停止音乐 * 停止音乐
*/ */
fun stopMusic(callback: (Result<Unit>) -> Unit) { fun stopMusic(callback: (Result<Unit>) -> Unit) {
executePlaybackAction("停止") { musicManager, playCallback -> executePlaybackAction("停止",0,0) { musicManager, playCallback ->
musicManager.stopMusic(playCallback) musicManager.stopMusic(playCallback)
}(callback) }(callback)
} }
@ -408,7 +289,7 @@ class QQMusicSingleton private constructor() {
* 下一首 * 下一首
*/ */
fun skipToNext(callback: (Result<Unit>) -> Unit) { fun skipToNext(callback: (Result<Unit>) -> Unit) {
executePlaybackAction("下一首") { musicManager, playCallback -> executePlaybackAction("下一首",0,0) { musicManager, playCallback ->
musicManager.skipToNext(playCallback) musicManager.skipToNext(playCallback)
}(callback) }(callback)
} }
@ -417,7 +298,7 @@ class QQMusicSingleton private constructor() {
* 上一首 * 上一首
*/ */
fun skipToPrevious(callback: (Result<Unit>) -> Unit) { fun skipToPrevious(callback: (Result<Unit>) -> Unit) {
executePlaybackAction("上一首") { musicManager, playCallback -> executePlaybackAction("上一首",0,0) { musicManager, playCallback ->
musicManager.skipToPrevious(playCallback) musicManager.skipToPrevious(playCallback)
}(callback) }(callback)
} }
@ -443,60 +324,20 @@ class QQMusicSingleton private constructor() {
/** /**
* 获取播放时间 * 获取播放时间
*/ */
fun getPlayTime(callback: (Result<PlayTimeInfo>) -> Unit) { fun getPlayTime(callback: (Result<Map<String, Any?>>) -> Unit) {
val currentActivity = context executeInfoAction("获取播放时间") { musicManager, infoCallback ->
if (currentActivity == null) { musicManager.getPlayTime(infoCallback)
callback(Result.failure(Exception("Activity not available"))) }(callback)
return
}
ensureMusicManager(currentActivity)
musicManager?.getPlayTime(object : QQMusicManager.InfoCallback {
override fun onSuccess(data: Map<String, Any?>) {
handler.post {
val totalTime = data["totalTime"] as? Long ?: 0L
val currentTime = data["currentTime"] as? Long ?: 0L
val playTimeInfo = PlayTimeInfo(totalTime, currentTime)
Log.d(TAG, "获取播放时间成功: $playTimeInfo")
callback(Result.success(playTimeInfo))
}
}
override fun onError(errorCode: Int, errorMessage: String) {
handler.post {
Log.e(TAG, "获取播放时间失败: $errorMessage")
callback(Result.failure(Exception("获取播放时间失败: $errorMessage")))
}
}
})
} }
/** /**
* 同步当前播放信息 - 统一接口 * 同步当前播放信息 - 统一接口
*/ */
fun syncCurrentPlayInfo(callback: (Result<Map<String, Any?>>) -> Unit) { fun syncCurrentPlayInfo(callback: (Result<Map<String, Any?>>) -> Unit) {
val currentcontext = context executeInfoAction("同步当前播放信息") { musicManager, infoCallback ->
if (currentcontext == null) { musicManager.syncCurrentPlayInfo(infoCallback)
callback(Result.failure(Exception("Activity not available"))) }(callback)
return
}
ensureMusicManager(currentcontext)
musicManager?.syncCurrentPlayInfo(object : QQMusicManager.InfoCallback {
override fun onSuccess(data: Map<String, Any?>) {
handler.post {
Log.d(TAG, "同步播放信息成功")
callback(Result.success(data))
}
}
override fun onError(errorCode: Int, errorMessage: String) {
handler.post {
Log.e(TAG, "同步播放信息失败: $errorMessage")
callback(Result.failure(Exception("同步播放信息失败: $errorMessage")))
}
}
})
} }
/** /**
@ -561,14 +402,81 @@ class QQMusicSingleton private constructor() {
} }
// 私有辅助方法 // 私有辅助方法
private fun ensureMusicManager(context: Context) { private fun ensureMusicManager(context: Context,callback: QQMusicManager. BindCallback) {
if (musicManager == null) { if (musicManager == null) {
musicManager = QQMusicManager(context) musicManager = QQMusicManager(context)
musicManager!!.bindQQMusicApiService(callback)
}else{
if(!musicManager!!.isConnected) { //未连接
musicManager?.bindQQMusicApiService(callback)
}else{
callback.onSuccess()
}
} }
} }
private fun executeSearchbackAction(
actionName: String,
curragain:Int,
allagain:Int,
action: (QQMusicManager, QQMusicManager.SearchCallback) -> Unit
): (callback: (Result<List<Map<String, Any?>>>) -> Unit) -> Unit {
return lambda@{ callback ->
if (!isQQMusicInstalled()){
callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件")))
return@lambda
}
val currentActivity = context
if (currentActivity == null) {
callback(Result.failure(Exception("Activity not available")))
return@lambda
}
Log.d(TAG, "开始 ${actionName}")
ensureMusicManager(currentActivity,object: QQMusicManager. BindCallback{
override fun onSuccess(){
action(musicManager!!, object : QQMusicManager.SearchCallback {
override fun onSuccess(songs: List<Map<String, Any?>>) {
Log.d(TAG, "${actionName} 成功")
handler.post {
callback(Result.success(songs))
}
}
override fun onError(errorCode: Int, errorMessage: String) {
Log.d(TAG, "失败 ${actionName} 成功")
if (allagain > 0 && curragain < allagain){ //重试机制
executeSearchbackAction(actionName,curragain+1,allagain,action)
}else{
handler.post {
callback(Result.failure(Exception("$actionName 失败: $errorMessage")))
}
}
}
})
}
override fun onError(errorCode: Int, errorMessage: String) {
if (allagain > 0 && curragain < allagain){ //重试机制
executeSearchbackAction(actionName,curragain+1,allagain,action)
}else{
handler.post {
callback(Result.failure(Exception("$actionName 失败: $errorMessage")))
}
}
}
})
}
}
private fun executePlaybackAction( private fun executePlaybackAction(
actionName: String, actionName: String,
curragain:Int,
allagain:Int,
action: (QQMusicManager, QQMusicManager.PlayCallback) -> Unit action: (QQMusicManager, QQMusicManager.PlayCallback) -> Unit
): (callback: (Result<Unit>) -> Unit) -> Unit { ): (callback: (Result<Unit>) -> Unit) -> Unit {
return lambda@{ callback -> return lambda@{ callback ->
@ -583,30 +491,42 @@ class QQMusicSingleton private constructor() {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return@lambda return@lambda
} }
Log.d(TAG, "开始 ${actionName}")
ensureMusicManager(currentActivity) ensureMusicManager(currentActivity,object: QQMusicManager. BindCallback{
action(musicManager!!, object : QQMusicManager.PlayCallback { override fun onSuccess(){
override fun onSuccess() { action(musicManager!!, object : QQMusicManager.PlayCallback {
handler.post { override fun onSuccess() {
Log.d(TAG, "${actionName}成功") Log.d(TAG, "${actionName}成功")
callback(Result.success(Unit)) handler.post {
} callback(Result.success(Unit))
} }
}
override fun onError(errorCode: Int, errorMessage: String) {
if (allagain > 0 && curragain < allagain){ //重试机制
executePlaybackAction(actionName,curragain+1,allagain,action)
}else{
handler.post {
Log.e(TAG, "${actionName}失败: $errorCode $errorMessage")
if (errorCode == -1){
musicManager?.bindQQMusicApiService()
callback(Result.failure(Exception("播放失败: $errorMessage")))
} else if (errorCode == 1030) {
callback(Result.success(Unit))
}else{
callback(Result.failure(Exception("播放失败: $errorMessage")))
}
}
}
override fun onError(errorCode: Int, errorMessage: String) {
handler.post {
Log.e(TAG, "播放失败: $errorCode $errorMessage")
if (errorCode == -1){
musicManager?.bindQQMusicApiService()
callback(Result.failure(Exception("播放失败: $errorMessage")))
} else if (errorCode == 1030) {
callback(Result.success(Unit))
}else{
callback(Result.failure(Exception("播放失败: $errorMessage")))
} }
} })
}
override fun onError(errorCode: Int, errorMessage: String) {
callback(Result.failure(Exception("$actionName 失败: $errorMessage")))
} }
}) })
} }
} }
@ -615,26 +535,36 @@ class QQMusicSingleton private constructor() {
action: (QQMusicManager, QQMusicManager.InfoCallback) -> Unit action: (QQMusicManager, QQMusicManager.InfoCallback) -> Unit
): (callback: (Result<Map<String, Any?>>) -> Unit) -> Unit { ): (callback: (Result<Map<String, Any?>>) -> Unit) -> Unit {
return lambda@{ callback -> return lambda@{ callback ->
if (!isQQMusicInstalled()){
callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件")))
return@lambda
}
val currentcontext = context val currentcontext = context
if (currentcontext == null) { if (currentcontext == null) {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return@lambda return@lambda
} }
ensureMusicManager(currentcontext) ensureMusicManager(currentcontext,object: QQMusicManager. BindCallback{
action(musicManager!!, object : QQMusicManager.InfoCallback { override fun onSuccess(){
override fun onSuccess(data: Map<String, Any?>) { action(musicManager!!, object : QQMusicManager.InfoCallback {
handler.post { override fun onSuccess(data: Map<String, Any?>) {
Log.d(TAG, "${actionName}成功") handler.post {
callback(Result.success(data)) Log.d(TAG, "${actionName}成功")
} callback(Result.success(data))
}
}
override fun onError(errorCode: Int, errorMessage: String) {
handler.post {
Log.e(TAG, "${actionName}失败: $errorMessage")
callback(Result.failure(Exception("${actionName}失败: $errorMessage")))
}
}
})
} }
override fun onError(errorCode: Int, errorMessage: String) { override fun onError(errorCode: Int, errorMessage: String) {
handler.post { callback(Result.failure(Exception("$actionName 失败: $errorMessage")))
Log.e(TAG, "${actionName}失败: $errorMessage")
callback(Result.failure(Exception("${actionName}失败: $errorMessage")))
}
} }
}) })
} }

16
local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QqMusicPlugin.kt

@ -203,22 +203,6 @@ class QqMusicPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
) )
} }
} }
"getPlayTime" -> {
qqMusicSingleton.getPlayTime { kotlinResult ->
kotlinResult.fold(
onSuccess = { playTimeInfo ->
result.success(mapOf(
"success" to true,
"totalTime" to playTimeInfo.totalTime,
"currentTime" to playTimeInfo.currentTime
))
},
onFailure = { error ->
result.error("GET_TIME_FAILED", error.message, null)
}
)
}
}
"syncCurrentPlayInfo" -> { "syncCurrentPlayInfo" -> {
qqMusicSingleton.syncCurrentPlayInfo { kotlinResult -> qqMusicSingleton.syncCurrentPlayInfo { kotlinResult ->
kotlinResult.fold( kotlinResult.fold(

Loading…
Cancel
Save