Browse Source

Merge branch 'new_dev' of https://github.com/deepcloud2048/deep_voice into new_dev

weicu
fdp 1 year ago
parent
commit
4d977dec52
  1. 5
      ios/Podfile
  2. 111
      lib/core/widgets/intro_overlay_builder.dart
  3. 244
      lib/modules/agent/views/agent_view.dart
  4. 8
      lib/modules/home/views/home_view.dart
  5. 5
      lib/modules/permissions/controllers/permissions_controller.dart
  6. 5
      lib/modules/permissions/models/permission_group.dart
  7. 7
      lib/modules/permissions/views/permissions_view.dart
  8. 42
      lib/modules/settings/views/settings_view.dart
  9. 1
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  10. 16
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt
  11. 18
      local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift
  12. 4
      local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift
  13. 11
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicLoginManager.kt
  14. 10
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicManager.kt
  15. 44
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicSingleton.kt
  16. 2
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QqMusicPlugin.kt
  17. 6
      pubspec.yaml

5
ios/Podfile

@ -1,10 +1,11 @@
# Define a global platform for your project # Define a global platform for your project
platform :ios, '16.0' platform :ios, '16.0'
$FirebaseSDKVersion = '11.13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', { project 'Runner', {
'Debug' => :debug, 'Debug' => :debug,
'Profile' => :release, 'Profile' => :release,
@ -86,4 +87,4 @@ post_install do |installer|
end end
end end
end end
end end

111
lib/core/widgets/intro_overlay_builder.dart

@ -10,7 +10,7 @@ class IntroOverlayBuilder {
required Size size, required Size size,
}) { }) {
return OverlayPosition( return OverlayPosition(
top: ScreenUtil().statusBarHeight, top: 0,
width: screenSize.width, width: screenSize.width,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
); );
@ -26,6 +26,7 @@ class IntroOverlayBuilder {
children: [ children: [
Container( Container(
padding: EdgeInsetsGeometry.all(12.w), padding: EdgeInsetsGeometry.all(12.w),
margin: EdgeInsets.only(top: ScreenUtil().statusBarHeight),
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: _button( child: _button(
'跳过', '跳过',
@ -73,18 +74,22 @@ class IntroOverlayBuilder {
bool alignment = true, bool alignment = true,
Function? onNext, Function? onNext,
Function? onFinish, Function? onFinish,
Function? onTap,
}) { }) {
double height = double dy = params.offset?.dy ?? 0;
(params.offset?.dy ?? 0) - ScreenUtil().statusBarHeight - 20.w; double width = params.size.width;
double height = params.size.height;
double totalHeight = dy + height + 3;
double left = params.offset?.dx ?? 0; double left = params.offset?.dx ?? 0;
double right = 1.sw - left - params.size.width; double right = 1.sw - left - width;
return SizedBox( return SizedBox(
height: height, height: totalHeight,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Container( Container(
padding: EdgeInsetsGeometry.all(12.w), padding: EdgeInsetsGeometry.all(12.w),
margin: EdgeInsets.only(top: ScreenUtil().statusBarHeight),
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: _button( child: _button(
'跳过', '跳过',
@ -96,15 +101,15 @@ class IntroOverlayBuilder {
), ),
Padding( Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: alignment ? left : 12.w, left: alignment ? left - 3 : 12.w - 3,
right: alignment ? 12.w : right, right: alignment ? 12.w - 3 : right - 3,
), ),
child: Column( child: Column(
crossAxisAlignment: crossAxisAlignment:
alignment ? CrossAxisAlignment.start : CrossAxisAlignment.end, alignment ? CrossAxisAlignment.start : CrossAxisAlignment.end,
children: [ children: [
Padding( Padding(
padding: EdgeInsets.only(bottom: 10.w), padding: const EdgeInsets.symmetric(horizontal: 3),
child: Text( child: Text(
text, text,
style: TextStyle( style: TextStyle(
@ -117,23 +122,51 @@ class IntroOverlayBuilder {
Row( Row(
children: [ children: [
if (onNext != null) if (onNext != null)
_button( Padding(
buttonText, padding: EdgeInsets.only(top: 10.w, left: 3, right: 3),
() { child: _button(
params.onNext!(); buttonText,
onNext(); () {
}, params.onNext!();
onNext();
},
),
), ),
if (onFinish != null) if (onFinish != null)
_button( Padding(
buttonText, padding: EdgeInsets.only(top: 10.w, left: 3, right: 3),
() { child: _button(
params.onFinish(); buttonText,
onFinish(); () {
}, params.onFinish();
onFinish();
},
),
), ),
], ],
), ),
SizedBox(height: 20.w),
onTap != null
? _AnimatedBorderButton(
onTap: () {
onTap();
},
isButton: false,
child: Padding(
padding: const EdgeInsetsGeometry.all(3),
child: SizedBox(
width: width,
height: height,
),
),
)
: Padding(
padding: const EdgeInsetsGeometry.all(3),
child: SizedBox(
width: width,
height: height,
),
),
], ],
), ),
), ),
@ -162,8 +195,21 @@ class IntroOverlayBuilder {
), ),
) )
: _AnimatedBorderButton( : _AnimatedBorderButton(
text: text,
onTap: onTap, onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 5.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.r),
border: Border.all(color: Colors.transparent),
),
child: Text(
text,
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
),
),
),
); );
} }
} }
@ -228,12 +274,14 @@ class _AnimatedLottieWidgetState extends State<_AnimatedLottieWidget>
// 新增的带边框流动动画的按钮组件 // 新增的带边框流动动画的按钮组件
class _AnimatedBorderButton extends StatefulWidget { class _AnimatedBorderButton extends StatefulWidget {
final String text; final Widget child;
final bool isButton;
final Function() onTap; final Function() onTap;
const _AnimatedBorderButton({ const _AnimatedBorderButton({
required this.text, required this.child,
required this.onTap, required this.onTap,
this.isButton = true,
}); });
@override @override
@ -276,22 +324,9 @@ class _AnimatedBorderButtonState extends State<_AnimatedBorderButton>
return CustomPaint( return CustomPaint(
painter: _FlowingBorderPainter( painter: _FlowingBorderPainter(
progress: _animation.value, progress: _animation.value,
borderRadius: 20.r, borderRadius: widget.isButton ? 20.r : 0.r,
),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 5.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.r),
border: Border.all(color: Colors.transparent),
),
child: Text(
widget.text,
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
),
),
), ),
child: widget.child,
); );
}, },
), ),

244
lib/modules/agent/views/agent_view.dart

@ -742,151 +742,143 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
SizedBox(width: 8.w), SizedBox(width: 8.w),
// 动态按钮区域:展开按钮 + 发送按钮 或 图片选择按钮 // 动态按钮区域:展开按钮 + 发送按钮 或 图片选择按钮
Builder(builder: (context) { IntroStepBuilder(
StepWidgetParams? widgetParams; group: 'ai',
return IntroStepBuilder( order: 2,
group: 'ai', getOverlayPosition:
order: 2, IntroOverlayBuilder.overlayPosition,
onHighlightWidgetTap: () async { overlayBuilder: (params) {
await controller return IntroOverlayBuilder.aiBuilder(
.noviceGuideService.aiStepsList[ params,
controller.noviceGuideService.aiStep] '点击发送,${controller.noviceGuideService.aiStepsList[controller.noviceGuideService.aiStep]['send']}',
['method'](); alignment: false,
widgetParams!.onNext!(); onTap: () async {
controller.sendMessage(); await controller
}, .noviceGuideService.aiStepsList[
getOverlayPosition: controller.noviceGuideService
IntroOverlayBuilder.overlayPosition, .aiStep]['method']();
overlayBuilder: (params) { params.onNext!();
widgetParams = params; controller.sendMessage();
return IntroOverlayBuilder.aiBuilder( },
params, );
'点击发送,${controller.noviceGuideService.aiStepsList[controller.noviceGuideService.aiStep]['send']}', },
alignment: false, builder: (BuildContext context, GlobalKey key) =>
); Obx(
}, key: key,
builder: () {
(BuildContext context, GlobalKey key) => final hasText = controller.hasTextInput.value;
Obx( final hasImage =
key: key, controller.isImageInputActive.value;
() { final shouldShowSend = hasText || hasImage;
final hasText = final shouldShowExpand =
controller.hasTextInput.value; controller.shouldShowExpandButton.value;
final hasImage =
controller.isImageInputActive.value;
final shouldShowSend = hasText || hasImage;
final shouldShowExpand =
controller.shouldShowExpandButton.value;
if (shouldShowSend) { if (shouldShowSend) {
// 显示发送按钮(可能还有展开按钮) // 显示发送按钮(可能还有展开按钮)
return Column( return Column(
children: [ children: [
// 展开按钮(在发送按钮上方) // 展开按钮(在发送按钮上方)
if (shouldShowExpand && hasText) if (shouldShowExpand && hasText)
Padding( Padding(
padding: padding:
EdgeInsets.only(bottom: 40.h), EdgeInsets.only(bottom: 40.h),
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
// 打开底部弹出编辑模式 // 打开底部弹出编辑模式
controller.openFullScreenEdit();
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor:
Colors.transparent,
builder: (context) =>
const FullScreenEditor(),
).then((_) {
// 弹出页关闭时的回调
controller controller
.openFullScreenEdit(); .closeFullScreenEdit();
showModalBottomSheet( });
context: context, },
isScrollControlled: true, child: Container(
backgroundColor: width: 32.w,
Colors.transparent, height: 32.w,
builder: (context) => decoration: BoxDecoration(
const FullScreenEditor(), color: isDarkMode
).then((_) { ? Colors.grey[700]
// 弹出页关闭时的回调 : Colors.grey[300],
controller borderRadius:
.closeFullScreenEdit(); BorderRadius.circular(
}); 16.w),
}, ),
child: Container( child: Icon(
width: 32.w, Icons.expand_less,
height: 32.w, color: isDarkMode
decoration: BoxDecoration( ? Colors.grey[300]
color: isDarkMode : Colors.grey[700],
? Colors.grey[700] size: 20.sp,
: Colors.grey[300],
borderRadius:
BorderRadius.circular(
16.w),
),
child: Icon(
Icons.expand_less,
color: isDarkMode
? Colors.grey[300]
: Colors.grey[700],
size: 20.sp,
),
), ),
),
),
// 发送按钮
GestureDetector(
onTap: () =>
controller.sendMessage(),
child: Container(
width: 44.w,
height: 44.w,
margin: EdgeInsets.symmetric(
vertical: 2.w),
decoration: BoxDecoration(
color: primaryColor,
borderRadius:
BorderRadius.circular(22.w),
),
child: Icon(
Icons.send,
color: Colors.white,
size: 22.sp,
), ),
), ),
), ),
], // 发送按钮
); GestureDetector(
} else { onTap: () => controller.sendMessage(),
// 显示'+'选择按钮
return Obx(() {
final isExpanded = controller
.isPlusOptionsExpanded.value;
return GestureDetector(
onTap: () {
controller.togglePlusOptions();
},
child: Container( child: Container(
width: 44.w, width: 44.w,
height: 44.w, height: 44.w,
margin: EdgeInsets.symmetric( margin: EdgeInsets.symmetric(
vertical: 2.w), vertical: 2.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode color: primaryColor,
? Colors.grey[800]
: Colors.grey[200],
borderRadius: borderRadius:
BorderRadius.circular(22.w), BorderRadius.circular(22.w),
), ),
child: Icon( child: Icon(
isExpanded Icons.send,
? Icons.close color: Colors.white,
: Icons.add,
color: isDarkMode
? Colors.grey[300]
: Colors.grey[700],
size: 22.sp, size: 22.sp,
), ),
), ),
); ),
}); ],
} );
}, } else {
), // 显示'+'选择按钮
); return Obx(() {
}), final isExpanded = controller
.isPlusOptionsExpanded.value;
return GestureDetector(
onTap: () {
controller.togglePlusOptions();
},
child: Container(
width: 44.w,
height: 44.w,
margin: EdgeInsets.symmetric(
vertical: 2.w),
decoration: BoxDecoration(
color: isDarkMode
? Colors.grey[800]
: Colors.grey[200],
borderRadius:
BorderRadius.circular(22.w),
),
child: Icon(
isExpanded
? Icons.close
: Icons.add,
color: isDarkMode
? Colors.grey[300]
: Colors.grey[700],
size: 22.sp,
),
),
);
});
}
},
),
),
], ],
), ),

8
lib/modules/home/views/home_view.dart

@ -740,15 +740,15 @@ class HomeView extends GetView<HomeController> {
child: IntroStepBuilder( child: IntroStepBuilder(
group: 'home', group: 'home',
order: 3, order: 3,
onHighlightWidgetTap: () {
Intro.of(Get.context!).dispose();
controller.exploreAIFeatures();
},
getOverlayPosition: IntroOverlayBuilder.overlayPosition, getOverlayPosition: IntroOverlayBuilder.overlayPosition,
overlayBuilder: (params) { overlayBuilder: (params) {
return IntroOverlayBuilder.aiBuilder( return IntroOverlayBuilder.aiBuilder(
params, params,
'太棒了!你已成功唤醒小言,接下来试试让它帮你做件事吧', '太棒了!你已成功唤醒小言,接下来试试让它帮你做件事吧',
onTap: () {
params.onFinish();
controller.exploreAIFeatures();
},
); );
}, },
builder: (BuildContext context, GlobalKey key) => SizedBox( builder: (BuildContext context, GlobalKey key) => SizedBox(

5
lib/modules/permissions/controllers/permissions_controller.dart

@ -116,10 +116,7 @@ class PermissionsController extends GetxController {
print('获取Android版本信息失败: $e'); print('获取Android版本信息失败: $e');
return []; return [];
} }
} else if (Platform.isIOS) { }
// iOS使用基本蓝牙权限
return [Permission.bluetooth];
}
return []; return [];
} }

5
lib/modules/permissions/models/permission_group.dart

@ -35,10 +35,7 @@ class PermissionGroup {
print('获取Android版本信息失败: $e'); print('获取Android版本信息失败: $e');
return []; return [];
} }
} else if (Platform.isIOS) { }
// iOS使用基本蓝牙权限
return [Permission.bluetooth];
}
return []; return [];
} }

7
lib/modules/permissions/views/permissions_view.dart

@ -99,9 +99,12 @@ class PermissionsView extends GetView<PermissionsController> {
group.titleKey == 'alarmPermissions') { group.titleKey == 'alarmPermissions') {
return Platform.isAndroid; return Platform.isAndroid;
} }
// 蓝牙权限组:Android 12+或iOS才显示 // 蓝牙权限组:Android 12+才显示,iOS不显示
if (group.titleKey == 'bluetoothPermissions') { if (group.titleKey == 'bluetoothPermissions') {
return controller.shouldShowBluetoothPermissions(); if (Platform.isAndroid) {
return controller.shouldShowBluetoothPermissions();
}
return false;
} }
// 存储权限组:Android 10及以下版本或iOS才显示 // 存储权限组:Android 10及以下版本或iOS才显示
if (group.titleKey == 'storagePermissions') { if (group.titleKey == 'storagePermissions') {

42
lib/modules/settings/views/settings_view.dart

@ -515,27 +515,27 @@ class SettingsView extends GetView<SettingsController> {
// ? Colors.white.withOpacity(0.1) // ? Colors.white.withOpacity(0.1)
// : Colors.grey[200]), // : Colors.grey[200]),
//BLE测试 //BLE测试
_buildSimpleNavigationSetting( // _buildSimpleNavigationSetting(
title: 'opus解码测试', // title: 'opus解码测试',
subtitle: '测试opus解码', // subtitle: '测试opus解码',
icon: Icons.bluetooth_searching, // icon: Icons.bluetooth_searching,
iconBgColor: isDarkMode // iconBgColor: isDarkMode
? Colors.green[900]!.withOpacity(0.3) // ? Colors.green[900]!.withOpacity(0.3)
: Colors.green[100]!, // : Colors.green[100]!,
iconColor: // iconColor:
isDarkMode ? Colors.green[300]! : Colors.green[600]!, // isDarkMode ? Colors.green[300]! : Colors.green[600]!,
titleColor: isDarkMode ? Colors.white : null, // titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null, // subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () { // onTap: () {
Get.toNamed(Routes.opusTest); // Get.toNamed(Routes.opusTest);
}, // },
isDarkMode: isDarkMode, // isDarkMode: isDarkMode,
), // ),
Divider( // Divider(
height: 1, // height: 1,
color: isDarkMode // color: isDarkMode
? Colors.white.withOpacity(0.1) // ? Colors.white.withOpacity(0.1)
: Colors.grey[200]), // : Colors.grey[200]),
// BLE测试 // BLE测试
// _buildSimpleNavigationSetting( // _buildSimpleNavigationSetting(

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

@ -196,6 +196,7 @@ object AgentService : CoroutineScope {
UserPortrait.initialize(appContext); UserPortrait.initialize(appContext);
//初始化定位服务 //初始化定位服务
LocationService.initialize(appContext) LocationService.initialize(appContext)
QQMusicSingleton.getInstance().initialize(appContext)
// 设置系统提示词 // 设置系统提示词
val customSystemPrompt = config["systemPrompt"] as? String val customSystemPrompt = config["systemPrompt"] as? String
//保存配置 //保存配置

16
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt

@ -636,13 +636,15 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "onFunctionCallResult回调异常: ${e.message}", e) Log.e(TAG, "onFunctionCallResult回调异常: ${e.message}", e)
} }
// 将结果发送回OpenAI继续对话 if (functionName != "set_user_profile_field"){
sendFunctionCallResultInternal( // 将结果发送回OpenAI继续对话
sessionid= sessionid, sendFunctionCallResultInternal(
messages = currentMessages, sessionid= sessionid,
functionCall = functionCall, messages = currentMessages,
functionResult = gson.toJson(result) functionCall = functionCall,
) functionResult = gson.toJson(result)
)
}
} }
} }
} catch (e: Exception) { } catch (e: Exception) {

18
local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift

@ -524,14 +524,16 @@ getSessionCallback(sessionId)?.onFunctionCall(sessionId, jsonFunctionCall)
functionCall, functionCall,
result result
) )
if (firstToolCall.name != "set_user_profile_field"){
// 将结果发送回OpenAI继续对话 // 将结果发送回OpenAI继续对话
await self.sendFunctionCallResultInternal( await self.sendFunctionCallResultInternal(
sessionId: sessionId, sessionId: sessionId,
messages: self.currentMessages, messages: self.currentMessages,
functionCall: functionCall, functionCall: functionCall,
functionResult: self.jsonToString(result) ?? "{}" functionResult: self.jsonToString(result) ?? "{}"
) )
}
} }
} }
} catch { } catch {

4
local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift

@ -114,7 +114,7 @@ public class MCPSubClient {
} }
private func processTools(_ toolList: [Tool]) { private func processTools(_ toolList: [Tool]) {
tools = toolList tools.removeAll()
toolMaps.removeAll() toolMaps.removeAll()
for tool in toolList { for tool in toolList {
@ -128,7 +128,7 @@ public class MCPSubClient {
"description": tool.description ?? "", "description": tool.description ?? "",
"parameters": parameters "parameters": parameters
] ]
tools.append(tool)
toolMaps.append([ toolMaps.append([
"type": "function", "type": "function",
"function": functionMap "function": functionMap

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

@ -1,6 +1,7 @@
package com.yunqiinnovation.qq_music package com.yunqiinnovation.qq_music
import android.app.Activity import android.app.Activity
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@ -16,7 +17,7 @@ import org.json.JSONTokener
* QQ音乐登录管理器 * QQ音乐登录管理器
* 负责处理QQ音乐的登录授权流程 * 负责处理QQ音乐的登录授权流程
*/ */
class QQMusicLoginManager(private val activity: Activity) { class QQMusicLoginManager(private val context: Context) {
companion object { companion object {
private const val TAG = "QQMusicLoginManager" private const val TAG = "QQMusicLoginManager"
@ -53,7 +54,7 @@ class QQMusicLoginManager(private val activity: Activity) {
} }
// 初始化绑定助手 // 初始化绑定助手
qPlayBindHelper = QPlayBindHelper(activity) qPlayBindHelper = QPlayBindHelper(context)
// 开始绑定QQ音乐服务 // 开始绑定QQ音乐服务
bindQQMusicService(callback) bindQQMusicService(callback)
@ -126,7 +127,7 @@ class QQMusicLoginManager(private val activity: Activity) {
Log.d(TAG, "启动QQ音乐登录页面") Log.d(TAG, "启动QQ音乐登录页面")
// 启动QQ音乐登录 // 启动QQ音乐登录
CommonCmd.loginQQMusic(activity, URI_LOGIN) CommonCmd.loginQQMusic(context, URI_LOGIN)
// 等待用户登录完成后进行身份验证 // 等待用户登录完成后进行身份验证
handler.postDelayed({ handler.postDelayed({
@ -145,9 +146,9 @@ class QQMusicLoginManager(private val activity: Activity) {
val encryptString = OpenIDHelper.getEncryptString(nonce) val encryptString = OpenIDHelper.getEncryptString(nonce)
CommonCmd.verifyCallerIdentity( CommonCmd.verifyCallerIdentity(
activity, context,
Config.OPENID_APPID, Config.OPENID_APPID,
activity.packageName, context.packageName,
encryptString, encryptString,
BASE_SCHEME BASE_SCHEME
) )

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

@ -15,7 +15,7 @@ import com.tencent.qqmusic.third.api.contract.*
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
class QQMusicManager(private val activity: Activity) { class QQMusicManager(private val context: Context) {
companion object { companion object {
const val TAG = "QQMusicManager" const val TAG = "QQMusicManager"
} }
@ -123,18 +123,18 @@ class QQMusicManager(private val activity: Activity) {
Log.d(TAG, "绑定Intent: action=${intent.action}, package=${intent.`package`}") Log.d(TAG, "绑定Intent: action=${intent.action}, package=${intent.`package`}")
try { try {
val bindResult = activity.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) val bindResult = context.bindService(intent, serviceConnection, 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(activity, activity.packageName) CommonCmd.startQQMusicProcess(context, context.packageName)
// 延迟重试绑定 // 延迟重试绑定
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
Log.d(TAG, "重试绑定QQ音乐服务") Log.d(TAG, "重试绑定QQ音乐服务")
activity.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}, 2000) }, 2000)
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -657,7 +657,7 @@ class QQMusicManager(private val activity: Activity) {
fun cleanup() { fun cleanup() {
try { try {
if (isBindQQMusicService) { if (isBindQQMusicService) {
activity.unbindService(serviceConnection) context.unbindService(serviceConnection)
isBindQQMusicService = false isBindQQMusicService = false
Log.d(TAG, "QQ音乐服务已解绑") Log.d(TAG, "QQ音乐服务已解绑")
} }

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

@ -39,7 +39,7 @@ class QQMusicSingleton private constructor() {
} }
} }
private var activity: Activity? = null private var context: Context? = null
private var loginManager: QQMusicLoginManager? = null private var loginManager: QQMusicLoginManager? = null
private var musicManager: QQMusicManager? = null private var musicManager: QQMusicManager? = null
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
@ -51,9 +51,9 @@ class QQMusicSingleton private constructor() {
/** /**
* 初始化单例,设置Activity上下文 * 初始化单例,设置Activity上下文
*/ */
fun initialize(activity: Activity) { fun initialize(context: Context) {
this.activity = activity this.context = context
this.sharedPreferences = activity.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音乐单例初始化完成")
} }
@ -72,7 +72,7 @@ class QQMusicSingleton private constructor() {
} }
return try { return try {
this.activity?.packageManager?.getPackageInfo(packageName, 0) this.context?.packageManager?.getPackageInfo(packageName, 0)
Log.d(TAG, "QQ音乐app已安装: $packageName") Log.d(TAG, "QQ音乐app已安装: $packageName")
true true
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
@ -113,7 +113,7 @@ class QQMusicSingleton private constructor() {
callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件"))) callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件")))
return return
} }
val currentActivity = activity val currentActivity = context
// 先检查是否已经登录且未过期 // 先检查是否已经登录且未过期
if (isLoggedIn()) { if (isLoggedIn()) {
Log.d(TAG, "用户已登录且未过期,直接返回登录信息") Log.d(TAG, "用户已登录且未过期,直接返回登录信息")
@ -159,7 +159,7 @@ class QQMusicSingleton private constructor() {
callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件"))) callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件")))
return return
} }
val currentActivity = activity val currentActivity = context
// 先检查是否已经登录且未过期 // 先检查是否已经登录且未过期
if (isLoggedIn()) { if (isLoggedIn()) {
@ -206,7 +206,7 @@ class QQMusicSingleton private constructor() {
retryCount: Int, retryCount: Int,
callback: (Result<List<Map<String, Any?>>>) -> Unit callback: (Result<List<Map<String, Any?>>>) -> Unit
) { ) {
val currentActivity = activity val currentActivity = context
if (currentActivity == null) { if (currentActivity == null) {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return return
@ -240,7 +240,7 @@ class QQMusicSingleton private constructor() {
retryCount: Int, retryCount: Int,
callback: (Result<List<Map<String, Any?>>>) -> Unit callback: (Result<List<Map<String, Any?>>>) -> Unit
) { ) {
val currentActivity = activity ?: return val currentActivity = context ?: return
val searchKey = "${keyword}_${System.currentTimeMillis()}" val searchKey = "${keyword}_${System.currentTimeMillis()}"
var completed = false var completed = false
@ -336,7 +336,7 @@ class QQMusicSingleton private constructor() {
index: Int, index: Int,
callback: (Result<Unit>) -> Unit callback: (Result<Unit>) -> Unit
) { ) {
val currentActivity = activity val currentActivity = context
if (currentActivity == null) { if (currentActivity == null) {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return return
@ -369,7 +369,7 @@ class QQMusicSingleton private constructor() {
index: Int, index: Int,
callback: (Result<Unit>) -> Unit callback: (Result<Unit>) -> Unit
) { ) {
val currentActivity = activity ?: return val currentActivity = context ?: return
ensureMusicManager(currentActivity) ensureMusicManager(currentActivity)
musicManager?.playSongs(songMids, index, object : QQMusicManager.PlayCallback { musicManager?.playSongs(songMids, index, object : QQMusicManager.PlayCallback {
@ -481,7 +481,7 @@ class QQMusicSingleton private constructor() {
* 获取播放时间 * 获取播放时间
*/ */
fun getPlayTime(callback: (Result<PlayTimeInfo>) -> Unit) { fun getPlayTime(callback: (Result<PlayTimeInfo>) -> Unit) {
val currentActivity = activity val currentActivity = context
if (currentActivity == null) { if (currentActivity == null) {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return return
@ -512,13 +512,13 @@ class QQMusicSingleton private constructor() {
* 同步当前播放信息 - 统一接口 * 同步当前播放信息 - 统一接口
*/ */
fun syncCurrentPlayInfo(callback: (Result<Map<String, Any?>>) -> Unit) { fun syncCurrentPlayInfo(callback: (Result<Map<String, Any?>>) -> Unit) {
val currentActivity = activity val currentcontext = context
if (currentActivity == null) { if (currentcontext == null) {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return return
} }
ensureMusicManager(currentActivity) ensureMusicManager(currentcontext)
musicManager?.syncCurrentPlayInfo(object : QQMusicManager.InfoCallback { musicManager?.syncCurrentPlayInfo(object : QQMusicManager.InfoCallback {
override fun onSuccess(data: Map<String, Any?>) { override fun onSuccess(data: Map<String, Any?>) {
handler.post { handler.post {
@ -549,7 +549,7 @@ class QQMusicSingleton private constructor() {
loginManager = null loginManager = null
musicManager?.cleanup() musicManager?.cleanup()
musicManager = null musicManager = null
activity = null context = null
Log.d(TAG, "QQ音乐单例资源清理完成") Log.d(TAG, "QQ音乐单例资源清理完成")
} }
@ -598,9 +598,9 @@ class QQMusicSingleton private constructor() {
} }
// 私有辅助方法 // 私有辅助方法
private fun ensureMusicManager(activity: Activity) { private fun ensureMusicManager(context: Context) {
if (musicManager == null) { if (musicManager == null) {
musicManager = QQMusicManager(activity) musicManager = QQMusicManager(context)
} }
} }
@ -609,7 +609,7 @@ class QQMusicSingleton private constructor() {
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 ->
val currentActivity = activity val currentActivity = context
if (currentActivity == null) { if (currentActivity == null) {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return@lambda return@lambda
@ -639,13 +639,13 @@ 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 ->
val currentActivity = activity val currentcontext = context
if (currentActivity == null) { if (currentcontext == null) {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return@lambda return@lambda
} }
ensureMusicManager(currentActivity) ensureMusicManager(currentcontext)
action(musicManager!!, object : QQMusicManager.InfoCallback { action(musicManager!!, object : QQMusicManager.InfoCallback {
override fun onSuccess(data: Map<String, Any?>) { override fun onSuccess(data: Map<String, Any?>) {
handler.post { handler.post {

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

@ -42,7 +42,7 @@ class QqMusicPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
Log.d("QqMusicPlugin", "onAttachedToActivity called") Log.d("QqMusicPlugin", "onAttachedToActivity called")
activity = binding.activity activity = binding.activity
println("QQ音乐 onAttachedToActivity") println("QQ音乐 onAttachedToActivity")
qqMusicSingleton.initialize(activity!!) // qqMusicSingleton.initialize(activity!!)
Log.d("QqMusicPlugin", "qqMusicSingleton.initialize completed") Log.d("QqMusicPlugin", "qqMusicSingleton.initialize completed")
} }

6
pubspec.yaml

@ -1,7 +1,7 @@
name: voitrans name: voitrans
description: "Voitrans - AI Voice Assistant." description: "Voitrans - AI Voice Assistant."
publish_to: "none" publish_to: "none"
version: 1.0.8+20 version: 1.0.10+20
environment: environment:
sdk: ">=3.3.0 <4.0.0" sdk: ">=3.3.0 <4.0.0"
@ -62,8 +62,8 @@ dependencies:
app_settings: 5.2.0 app_settings: 5.2.0
fluwx: ^5.7.1 fluwx: ^5.7.1
url_launcher: ^6.3.1 url_launcher: ^6.3.1
firebase_core: ^3.12.1 firebase_core: ^4.0.0
firebase_auth: ^5.5.1 firebase_auth: ^6.0.1
google_sign_in: ^6.2.2 google_sign_in: ^6.2.2
flutter_facebook_auth: ^7.1.1 flutter_facebook_auth: ^7.1.1
sign_in_with_apple: ^7.0.1 sign_in_with_apple: ^7.0.1

Loading…
Cancel
Save