Browse Source

修改新手指引

newdev_shunjiawei
tanlongsheng 1 year ago
parent
commit
7bdda09d00
  1. 4
      lib/core/bindings/initial_binding.dart
  2. 34
      lib/data/services/novice_guide_service.dart
  3. 3
      lib/modules/agent/controllers/agent_controller.dart
  4. 181
      lib/modules/agent/views/agent_view.dart
  5. 40
      lib/modules/home/controllers/home_controller.dart
  6. 107
      lib/modules/home/views/home_view.dart

4
lib/core/bindings/initial_binding.dart

@ -6,6 +6,7 @@ import '../../../data/services/navigation_manager.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../data/services/meeting/meeting_task_service.dart'; import '../../data/services/meeting/meeting_task_service.dart';
import '../../data/services/meeting/meeting_upload_service.dart'; import '../../data/services/meeting/meeting_upload_service.dart';
import '../../data/services/novice_guide_service.dart';
import '../../data/services/speech_factory.dart'; import '../../data/services/speech_factory.dart';
import '../../data/services/volcano_translation_service.dart'; import '../../data/services/volcano_translation_service.dart';
import '../../data/services/volcano_ai_service.dart'; import '../../data/services/volcano_ai_service.dart';
@ -64,6 +65,9 @@ class InitialBinding extends Bindings {
// 会议助手音频上传 // 会议助手音频上传
Get.lazyPut<UserportraitManager>(() => UserportraitManager(), fenix: true); Get.lazyPut<UserportraitManager>(() => UserportraitManager(), fenix: true);
// 新手引导
Get.lazyPut<NoviceGuideService>(() => NoviceGuideService(), fenix: true);
// 杰理蓝牙服务 // 杰理蓝牙服务
// // 语言管理器(需要最先初始化) // // 语言管理器(需要最先初始化)

34
lib/data/services/novice_guide_service.dart

@ -0,0 +1,34 @@
import 'package:flutter_intro/flutter_intro.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
class NoviceGuideService extends GetxService {
final GetStorage _storage = GetStorage();
bool isHomeGuide = false;
bool isAIGuide = false;
@override
void onInit() {
super.onInit();
// isHomeGuide = _storage.read('home_guide') ?? false;
// isAIGuide = _storage.read('ai_guide') ?? false;
}
void startHomeGuide() {
if (!isHomeGuide) {
isHomeGuide = true;
_storage.write('home_guide', true);
Intro.of(Get.context!).start(group: 'home');
}
}
void startAIGuide() async {
if (!isAIGuide) {
await Future.delayed(const Duration(milliseconds: 100));
isAIGuide = true;
_storage.write('ai_guide', true);
Intro.of(Get.context!).start(group: 'ai');
}
}
}

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

@ -3,6 +3,7 @@ import 'dart:convert';
import 'dart:ffi'; import 'dart:ffi';
import 'dart:io'; import 'dart:io';
import 'package:amap_service/amap_service.dart'; import 'package:amap_service/amap_service.dart';
import '../../../data/services/novice_guide_service.dart';
import '/core/utils/upload_oss.dart'; import '/core/utils/upload_oss.dart';
import '/data/models/user_Info.dart'; import '/data/models/user_Info.dart';
import '/data/services/music_manager.dart'; import '/data/services/music_manager.dart';
@ -39,6 +40,8 @@ class AgentController extends GetxController {
final TextEditingController textController = TextEditingController(); final TextEditingController textController = TextEditingController();
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
final noviceGuideService = Get.find<NoviceGuideService>();
//音乐播放管理 //音乐播放管理
final _musiceManager = Get.find<MusicManager>(); final _musiceManager = Get.find<MusicManager>();

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

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_intro/flutter_intro.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../controllers/agent_controller.dart'; import '../controllers/agent_controller.dart';
import '../controllers/message_action_controller.dart'; import '../controllers/message_action_controller.dart';
@ -388,96 +389,110 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
if (controller.isTextInputMode.value) { if (controller.isTextInputMode.value) {
// 文本输入模式 - 限制最大高度并只向上扩展 // 文本输入模式 - 限制最大高度并只向上扩展
return Container( return IntroStepBuilder(
constraints: BoxConstraints( group: 'ai',
minHeight: 44.h, order: 1,
maxHeight: 120.h, text: '对小言说“今天天气怎么样”',
), onWidgetLoad: () {
padding: EdgeInsets.symmetric( controller.noviceGuideService
horizontal: 16.w), .startAIGuide();
decoration: BoxDecoration( },
color: isDarkMode builder: (BuildContext context,
? Colors.grey[800] GlobalKey key) =>
: Colors.grey[200], Container(
borderRadius: key: key,
BorderRadius.circular(22.w), constraints: BoxConstraints(
border: Border.all( minHeight: 44.h,
maxHeight: 120.h,
),
padding: EdgeInsets.symmetric(
horizontal: 16.w),
decoration: BoxDecoration(
color: isDarkMode color: isDarkMode
? Colors.grey[800]! ? Colors.grey[800]
: Colors.grey.withOpacity(0.1), : Colors.grey[200],
width: 1.w, borderRadius:
BorderRadius.circular(22.w),
border: Border.all(
color: isDarkMode
? Colors.grey[800]!
: Colors.grey.withOpacity(0.1),
width: 1.w,
),
), ),
), child: Row(
child: Row( crossAxisAlignment: CrossAxisAlignment
crossAxisAlignment: CrossAxisAlignment .end, // 对齐到底部,实现向上扩展
.end, // 对齐到底部,实现向上扩展 children: [
children: [ Expanded(
Expanded( child: Obx(() {
child: Obx(() { // 图片模式下使用更高的输入框
// 图片模式下使用更高的输入框 final isImageActive = controller
final isImageActive = controller .isImageInputActive.value;
.isImageInputActive.value;
return TextField( return TextField(
controller: controller:
controller.textController, controller.textController,
maxLines: isImageActive maxLines: isImageActive
? 3 ? 3
: 5, // 设置合理的最大行数 : 5, // 设置合理的最大行数
minLines: isImageActive ? 2 : 1, minLines:
keyboardType: TextInputType isImageActive ? 2 : 1,
.multiline, // 启用多行键盘 keyboardType: TextInputType
textInputAction: TextInputAction .multiline, // 启用多行键盘
.newline, // 支持回车换行 textInputAction:
onTap: () { TextInputAction
// 当点击输入框时,如果'+'选项已展开,则收缩 .newline, // 支持回车换行
if (controller onTap: () {
.isPlusOptionsExpanded // 当点击输入框时,如果'+'选项已展开,则收缩
.value) { if (controller
controller .isPlusOptionsExpanded
.collapsePlusOptions(); .value) {
} controller
}, .collapsePlusOptions();
style: TextStyle( }
fontSize: 15.sp, },
color: isDarkMode style: TextStyle(
? Colors.white
: Colors.grey[800],
),
decoration: InputDecoration(
hintText: isImageActive
? 'addQuestionAboutImage'
.tr // 添加对图片的问题或描述...
: 'inputMessage'
.tr, // 输入消息...
hintStyle: TextStyle(
color: Colors.grey[500],
fontSize: 15.sp, fontSize: 15.sp,
color: isDarkMode
? Colors.white
: Colors.grey[800],
), ),
filled: false, decoration: InputDecoration(
border: InputBorder.none, hintText: isImageActive
focusedBorder: ? 'addQuestionAboutImage'
OutlineInputBorder( .tr // 添加对图片的问题或描述...
borderSide: BorderSide( : 'inputMessage'
color: isDarkMode .tr, // 输入消息...
? Colors.grey[800]! hintStyle: TextStyle(
: Colors.grey[200]!, color: Colors.grey[500],
width: 1.w, fontSize: 15.sp,
),
filled: false,
border: InputBorder.none,
focusedBorder:
OutlineInputBorder(
borderSide: BorderSide(
color: isDarkMode
? Colors.grey[800]!
: Colors.grey[200]!,
width: 1.w,
),
), ),
contentPadding:
EdgeInsets.symmetric(
vertical:
isImageActive
? 12.h
: 8.h),
), ),
contentPadding: onSubmitted: (_) =>
EdgeInsets.symmetric( controller.sendMessage(),
vertical: );
isImageActive }),
? 12.h ),
: 8.h), ],
), ),
onSubmitted: (_) =>
controller.sendMessage(),
);
}),
),
],
), ),
); );
} else { } else {

40
lib/modules/home/controllers/home_controller.dart

@ -1,5 +1,6 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'dart:async'; import 'dart:async';
import '../../../data/services/novice_guide_service.dart';
import '../../../routes/app_routes.dart'; import '../../../routes/app_routes.dart';
import '../../../core/utils/logger.dart'; import '../../../core/utils/logger.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -24,7 +25,7 @@ class HomeController extends GetxController {
final _isFullyConnected = false.obs; final _isFullyConnected = false.obs;
String? _lastConnectedDeviceName; String? _lastConnectedDeviceName;
bool get isFullyConnected => _isFullyConnected.value; bool get isFullyConnected => _isFullyConnected.value;
RxBool get isFullyConnectedRx => _isFullyConnected; RxBool get isFullyConnectedRx => _isFullyConnected;
BluetoothDevice? get currentDevice => _bluetoothManager.currentDevice; BluetoothDevice? get currentDevice => _bluetoothManager.currentDevice;
@ -55,12 +56,15 @@ class HomeController extends GetxController {
final _bleManager = Get.find<BleManager>(); final _bleManager = Get.find<BleManager>();
final _storage = GetStorage(); final _storage = GetStorage();
final noviceGuideService = Get.find<NoviceGuideService>();
// 会话级别的版本检查标记(静态变量,整个应用会话期间保持) // 会话级别的版本检查标记(静态变量,整个应用会话期间保持)
static bool _hasCheckedVersionInSession = false; static bool _hasCheckedVersionInSession = false;
// 版本检查相关常量 // 版本检查相关常量
static const String _lastVersionCheckKey = 'last_version_check_timestamp'; static const String _lastVersionCheckKey = 'last_version_check_timestamp';
static const int _versionCheckIntervalHours = 24; // 24小时间隔 static const int _versionCheckIntervalHours = 24; // 24小时间隔
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@ -94,13 +98,14 @@ class HomeController extends GetxController {
update(); update();
}); });
_classicBluetoothWorker = ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) { _classicBluetoothWorker =
ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) {
if (device != null) { if (device != null) {
_lastConnectedDeviceName = device.name; _lastConnectedDeviceName = device.name;
} }
_checkConnectionStatus(); _checkConnectionStatus();
}); });
_bleConnectionWorker = ever(_bleManager.connectionStateRx, (int state) { _bleConnectionWorker = ever(_bleManager.connectionStateRx, (int state) {
_checkConnectionStatus(); _checkConnectionStatus();
}); });
@ -121,30 +126,30 @@ class HomeController extends GetxController {
void exploreAIFeatures() { void exploreAIFeatures() {
Get.toNamed(Routes.agent); Get.toNamed(Routes.agent);
} }
Future<void> _checkConnectionStatus() async { Future<void> _checkConnectionStatus() async {
try { try {
final currentBleState = await _bleManager.getConnectionState(); final currentBleState = await _bleManager.getConnectionState();
bool hasClassicBluetooth = currentDevice != null; bool hasClassicBluetooth = currentDevice != null;
bool hasBleConnection = currentBleState == 2; bool hasBleConnection = currentBleState == 2;
bool wasConnected = _isFullyConnected.value; bool wasConnected = _isFullyConnected.value;
bool isNowConnected = hasClassicBluetooth && hasBleConnection; bool isNowConnected = hasClassicBluetooth && hasBleConnection;
if (isNowConnected && currentDevice == null) { if (isNowConnected && currentDevice == null) {
isNowConnected = false; isNowConnected = false;
} }
if (_isFullyConnected.value != isNowConnected) { if (_isFullyConnected.value != isNowConnected) {
_isFullyConnected.value = isNowConnected; _isFullyConnected.value = isNowConnected;
_isFullyConnected.refresh(); _isFullyConnected.refresh();
update(); update();
if (!wasConnected && isNowConnected) { if (!wasConnected && isNowConnected) {
await _bleManager.fetchDeviceInfo(); await _bleManager.fetchDeviceInfo();
} }
} }
update(); update();
} catch (e) { } catch (e) {
Logger.e(_tag, '${'checkConnectionStatusFailed'.tr}: $e'); Logger.e(_tag, '${'checkConnectionStatusFailed'.tr}: $e');
@ -165,26 +170,28 @@ class HomeController extends GetxController {
} }
bool get shouldShowDeviceInfo => currentDevice != null; bool get shouldShowDeviceInfo => currentDevice != null;
String getDeviceName() { String getDeviceName() {
if (_isFullyConnected.value && currentDevice == null) { if (_isFullyConnected.value && currentDevice == null) {
_checkConnectionStatus(); _checkConnectionStatus();
} }
final deviceName = currentDevice?.name; final deviceName = currentDevice?.name;
if (deviceName != null && deviceName.isNotEmpty) { if (deviceName != null && deviceName.isNotEmpty) {
return deviceName; return deviceName;
} }
final bleDeviceName = deviceInfo.value?.name; final bleDeviceName = deviceInfo.value?.name;
if (bleDeviceName != null && bleDeviceName.isNotEmpty) { if (bleDeviceName != null && bleDeviceName.isNotEmpty) {
return bleDeviceName; return bleDeviceName;
} }
if (!_isFullyConnected.value && _lastConnectedDeviceName != null && _lastConnectedDeviceName!.isNotEmpty) { if (!_isFullyConnected.value &&
_lastConnectedDeviceName != null &&
_lastConnectedDeviceName!.isNotEmpty) {
return _lastConnectedDeviceName!; return _lastConnectedDeviceName!;
} }
return 'unknownEquipment'.tr; return 'unknownEquipment'.tr;
} }
@ -288,7 +295,8 @@ class HomeController extends GetxController {
} }
// 条件2:距离上次检查超过24小时 // 条件2:距离上次检查超过24小时
else if (lastCheckTimestamp != null) { else if (lastCheckTimestamp != null) {
final lastCheckTime = DateTime.fromMillisecondsSinceEpoch(lastCheckTimestamp); final lastCheckTime =
DateTime.fromMillisecondsSinceEpoch(lastCheckTimestamp);
final timeDifference = now.difference(lastCheckTime); final timeDifference = now.difference(lastCheckTime);
if (timeDifference.inHours >= _versionCheckIntervalHours) { if (timeDifference.inHours >= _versionCheckIntervalHours) {

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

@ -84,11 +84,30 @@ class HomeView extends GetView<HomeController> {
_buildAppBar(isDarkMode), _buildAppBar(isDarkMode),
// 顶部图片 // 顶部图片
_buildEarbudsImage(), IntroStepBuilder(
group: 'home',
order: 1,
text: '欢迎使用Voitrans!首先,让我们唤醒你的专属 AI—— 小言',
onWidgetLoad: () {
controller.noviceGuideService.startHomeGuide();
},
builder: (BuildContext context, GlobalKey key) => SizedBox(
key: key,
child: _buildEarbudsImage(),
),
),
SizedBox(height: 20.h), SizedBox(height: 20.h),
// 设备状态卡片 // 设备状态卡片
_buildDeviceStatusCard(isDarkMode), IntroStepBuilder(
group: 'home',
order: 2,
text: '对着麦克风说“小言小言”',
builder: (BuildContext context, GlobalKey key) => SizedBox(
key: key,
child: _buildDeviceStatusCard(isDarkMode),
),
),
SizedBox(height: 20.h), SizedBox(height: 20.h),
// 热门AI体验 // 热门AI体验
@ -624,8 +643,9 @@ class HomeView extends GetView<HomeController> {
Expanded( Expanded(
flex: 1, flex: 1,
child: IntroStepBuilder( child: IntroStepBuilder(
group: 'home',
order: 3, order: 3,
text: 'AI全能助手', text: '太棒了!你已成功唤醒小言,接下来试试让它帮你做件事吧',
onHighlightWidgetTap: () { onHighlightWidgetTap: () {
Intro.of(Get.context!).dispose(); Intro.of(Get.context!).dispose();
controller.exploreAIFeatures(); controller.exploreAIFeatures();
@ -660,62 +680,47 @@ class HomeView extends GetView<HomeController> {
child: Column( child: Column(
children: [ children: [
// 右上卡片 - 同声翻译 // 右上卡片 - 同声翻译
IntroStepBuilder( _buildFeatureCard(
order: 1, title: 'simultaneousInterpretation'.tr, // 同声传译
text: '同声传译', description:
// onWidgetLoad: () { 'simultaneousInterpretationInfo'.tr, // 80+语言实时翻译,出国无忧
// Intro.of(Get.context!).start(); icon: Icons.translate,
// }, gradient: LinearGradient(
builder: (BuildContext context, GlobalKey key) => begin: Alignment.topLeft,
_buildFeatureCard( end: Alignment.bottomRight,
key: key, colors: [
title: 'simultaneousInterpretation'.tr, // 同声传译 const Color(0xFF4CAF50).withOpacity(0.3),
description: const Color(0xFF009688).withOpacity(0.3),
'simultaneousInterpretationInfo'.tr, // 80+语言实时翻译,出国无忧 ],
icon: Icons.translate,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
const Color(0xFF4CAF50).withOpacity(0.3),
const Color(0xFF009688).withOpacity(0.3),
],
),
onTap: controller.openTranslation,
height: 83.h, // 增加高度
isDarkMode: isDarkMode,
iconColor: const Color(0xFF4CAF50),
), ),
onTap: controller.openTranslation,
height: 83.h, // 增加高度
isDarkMode: isDarkMode,
iconColor: const Color(0xFF4CAF50),
), ),
SizedBox(height: 5.h), // 减少间距 SizedBox(height: 5.h), // 减少间距
// 右下卡片 - // 右下卡片 -
// 右下卡片 - 会议助手 // 右下卡片 - 会议助手
IntroStepBuilder( _buildFeatureCard(
order: 2, title: 'conferenceAssistant'.tr, // 商务会议助手
text: '商务会议助手', description: 'conferenceAssistantInfo'.tr, // 实时记录会议纪要,多语言翻译
builder: (BuildContext context, GlobalKey key) => icon: Icons.people,
_buildFeatureCard( gradient: LinearGradient(
key: key, begin: Alignment.topLeft,
title: 'conferenceAssistant'.tr, // 商务会议助手 end: Alignment.bottomRight,
description: 'conferenceAssistantInfo'.tr, // 实时记录会议纪要,多语言翻译 colors: [
icon: Icons.people, Color(0xFFab47bc).withOpacity(0.3), // 薰衣草紫
gradient: LinearGradient( Color(0xFFec407a).withOpacity(0.3), // 品红
begin: Alignment.topLeft, ],
end: Alignment.bottomRight,
colors: [
Color(0xFFab47bc).withOpacity(0.3), // 薰衣草紫
Color(0xFFec407a).withOpacity(0.3), // 品红
],
),
onTap: () {
Get.toNamed(Routes.meeting);
},
height: 85.h, // 增加高度
isDarkMode: isDarkMode,
iconColor: const Color(0xFFd81b60), // 品红
), ),
onTap: () {
Get.toNamed(Routes.meeting);
},
height: 85.h, // 增加高度
isDarkMode: isDarkMode,
iconColor: const Color(0xFFd81b60), // 品红
), ),
], ],
), ),

Loading…
Cancel
Save