Browse Source

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

weicu
lxm 1 year ago
parent
commit
09dc013c56
  1. 10
      android/app/src/main/AndroidManifest.xml
  2. 3
      lib/data/services/ble_manager.dart
  3. 133
      lib/modules/agent/controllers/agent_controller.dart
  4. 25
      lib/modules/agent/views/message_bubble.dart
  5. 9
      lib/modules/agent/views/navigation_card.dart
  6. 562
      lib/modules/agent/views/weather_baidu.dart
  7. 26
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  8. 1
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt
  9. 5
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt

10
android/app/src/main/AndroidManifest.xml

@ -132,4 +132,14 @@
<data android:mimeType="text/plain"/>
</intent>
</queries>
<queries>
<!-- 声明百度地图包名 -->
<package android:name="com.baidu.BaiduMap" />
<!-- 声明要处理的URL Scheme -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="baidumap" />
</intent>
</queries>
</manifest>

3
lib/data/services/ble_manager.dart

@ -663,6 +663,8 @@ class BleManager extends GetxService {
AppConfig.env('OPENAI_AGENT_ID') ?? 'personal_assistant';
final systemPrompt = AppConfig.agentsystemPrompt(openaiagentid);
final musicServiceType = AppConfig.env('APP_MUSICSERVICE_TYPE') ?? 'AUTO';
final navigationMode =
AppConfig.env('APP_NAVIGATION_MODE') ?? 'external_auto';
// 从Flutter资源包中加载.mcp.json
final jsonData = AppConfig.mcpConfig();
Logger.info('启动BLE服务,musicServiceType: ${musicServiceType}');
@ -734,6 +736,7 @@ class BleManager extends GetxService {
'usertoken': jsonString,
'supportedLanguages': supportedLanguages,
'musicServiceType': musicServiceType,
"navigationMode": navigationMode
};
// 记录日志(不记录敏感信息)

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

@ -4,7 +4,9 @@ import 'dart:ffi';
import 'dart:io';
import 'package:amap_service/amap_service.dart';
import 'package:http/http.dart' as http;
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:voitrans/data/models/appconfig.dart';
import 'package:voitrans/data/models/song_model.dart';
import 'package:voitrans/data/services/ble_manager.dart';
import 'package:voitrans/data/services/bluetooth_manager.dart';
@ -172,7 +174,6 @@ class AgentController extends GetxController with WidgetsBindingObserver {
_setupTextInputListener(); //设置文本输入监听器
startVoiceInput(); //开启语音输入
// 监听应用生命周期:当应用切到后台/失焦时兜底结束按住说话,防止 onPointerUp 未触发
WidgetsBinding.instance.addObserver(this);
@ -812,13 +813,22 @@ class AgentController extends GetxController with WidgetsBindingObserver {
if (card_navigation is Map<String, dynamic>) {
//导航
final start = card_navigation["start"] as String;
final end = card_navigation["end"] as String;
final destination =
card_navigation["destination"] as String;
final end_longitude =
card_navigation["end_longitude"] as String;
final end_latitude =
card_navigation["end_latitude"] as String;
// End_longitude string `json:"end_longitude"` //目标点经度
// End_latitude string `json:"end_latitude"` //目标点纬度
final items = card_navigation['items'];
if (items != null) {
Future.delayed(Duration(seconds: 2), () {
// Logger.i(TAG, 'liwei-------- 打开导航页面: $start $end ');
// 2秒后执行的代码
openNavigation(start, end, "");
openNavigation(start, destination, end_latitude,
end_longitude, "");
});
}
}
@ -1855,8 +1865,19 @@ class AgentController extends GetxController with WidgetsBindingObserver {
}
//去导航
void openNavigation(String start, String end, String ntype) {
_navigationManager.open(start, end, ntype);
void openNavigation(String start, String destination, String endlatitude,
String endlongitude, String mode) {
final navigationMode =
AppConfig.env('APP_NAVIGATION_MODE') ?? 'external_auto';
if (navigationMode == 'internal_gaode') {
_navigationManager.open(start, "$endlongitude,$endlatitude", mode);
} else if (navigationMode == 'external_baidu') {
openBaiduMap(destination, mode);
} else if (navigationMode == 'external_gaode') {
openGaodeMap(destination, endlatitude, destination, mode);
} else if (navigationMode == 'external_google') {
openGoogleMap(destination, mode);
}
}
// 打开图片查看器
@ -2189,4 +2210,106 @@ class AgentController extends GetxController with WidgetsBindingObserver {
Get.snackbar('错误', '打开地图时发生错误');
}
}
/// 打开高德地图进行导航
Future<void> openGaodeMap(
String destination,
String lat,
String lon,
String mode,
) async {
try {
// 获取应用包名
final packageInfo = await PackageInfo.fromPlatform();
// 构建src参数(带平台前缀)
final String src = Platform.isAndroid
? "android.${packageInfo.packageName}"
: "ios.${packageInfo.packageName}";
// 构建高德地图导航链接
final Uri uri = Uri(
scheme: 'androidamap',
host: 'navi',
queryParameters: {
'poiname': destination, // 目的地地址
'lat': lat,
'lon': lon,
'mode': mode.isNotEmpty ? mode : 'driving', // 导航模式,默认驾车
'sourceApplication': src, // 来源标识
'callnative': '1', // 优先调用原生应用
},
);
print('高德导航链接: ${uri.toString()}');
// final Uri testMapsUri = Uri.parse(
// 'androidamap://navi?sourceApplication=android.com.saitong.voitrans&lat=22.615274&lon=114.035576&poiname=深圳北站&dev=0&mode=driving');
// // 尝试打开高德地图应用
if (await canLaunchUrl(uri)) {
final bool launched = await launchUrl(
uri,
mode: LaunchMode.externalApplication,
);
if (!launched) {
print('高德地图已安装,但启动失败');
Get.snackbar('提示', '高德地图启动失败');
}
} else {
print('高德地图未安装');
Get.snackbar('提示', '请先安装高德地图应用');
}
} catch (e) {
print('打开地图失败: $e');
Get.snackbar('错误', '打开地图时发生错误');
}
}
/// 打开Baidu地图进行导航
Future<void> openBaiduMap(
String destination,
String mode,
) async {
try {
// 地址和城市(已编码的字符串也可以直接使用)
// String destination = "深圳北站";
// String city = "深圳";
// 构建正确的导航链接
final Uri appUri = Uri(
scheme: 'baidumap',
host: 'map',
path: 'direction', // 使用direction端点而非navi
queryParameters: {
'destination': destination,
// 'city': '深圳',
'mode': mode, // 明确导航模式
'src': 'com.saitong.voitrans', // 带平台前缀的src参数
},
);
print('生成的链接: ${appUri.toString()}');
if (await canLaunchUrl(appUri)) {
await launchUrl(appUri, mode: LaunchMode.externalApplication);
} else {
print('无法打开百度地图,请检查是否已安装');
}
// final packageInfo = await PackageInfo.fromPlatform();
// String packageName = packageInfo.packageName;
// // 方案1: 尝试使用Google Maps应用的URL scheme
// // final Uri googleMapsUri = Uri.parse(url);
// // 简化版链接(仅包含必要参数)
// final Uri testUri = Uri.parse(
// 'baidumap://map/navi?location=$end_latitude,$end_longitude&coord_type=bd09ll&src$packageName');
// if (await canLaunchUrl(testUri)) {
// await launchUrl(testUri, mode: LaunchMode.externalApplication);
// } else {
// print('打开地图失败');
// }
} catch (e) {
print('打开地图失败: $e');
Get.snackbar('错误', '打开地图时发生错误');
}
}
}

25
lib/modules/agent/views/message_bubble.dart

@ -2,6 +2,7 @@ import 'package:voitrans/core/utils/logger.dart';
import 'package:voitrans/modules/agent/views/google_navigation_card.dart';
import 'package:voitrans/modules/agent/views/music_card.dart';
import 'package:voitrans/modules/agent/views/qqmusic_card.dart';
import 'package:voitrans/modules/agent/views/weather_baidu.dart';
import '../../../data/models/song_model.dart';
import '../../../modules/agent/views/navigation_card.dart';
@ -258,6 +259,7 @@ class MessageBubble extends GetView<AgentController> {
return buildQQMusicContent(context, songList);
}
} else if (meta!.keys.contains('maps_weather')) {
//高德的
final maps_weather = meta!['maps_weather'] as Map<String, dynamic>;
final city = maps_weather['city'];
if (city != null) {
@ -273,6 +275,25 @@ class MessageBubble extends GetView<AgentController> {
daylist,
);
}
} else if (meta!.keys.contains('map_weather')) {
//百度的
final map_weather = meta!['map_weather'] as Map<String, dynamic>;
final status = map_weather['status'];
if (status == 0) {
final result = map_weather['result'] as Map<String, dynamic>;
final city = result["location"]["city"] as String? ?? '';
final rawList = result['forecasts'] as List? ?? [];
final daylist =
rawList.map((e) => Map<String, dynamic>.from(e)).toList();
final now = daylist.isNotEmpty ? daylist[0] : <String, dynamic>{};
//天气
return buildWeatherBaiduContent(
context,
city,
now,
daylist,
);
}
} else if (meta!.keys.contains('card_travel')) {
final rawList = meta!['card_travel']['items'] as List? ?? [];
final travellist =
@ -286,6 +307,8 @@ class MessageBubble extends GetView<AgentController> {
maps_route_navigation["card_navigation"] as Map<String, dynamic>;
final start = card_navigation['start'] as String? ?? '';
final end = card_navigation['end'] as String? ?? '';
final end_longitude = card_navigation["end_longitude"] as String;
final end_latitude = card_navigation["end_latitude"] as String;
final destination = card_navigation['destination'] as String? ?? '';
final rawList = card_navigation['items'] as List? ?? null;
if (rawList != null) {
@ -299,6 +322,8 @@ class MessageBubble extends GetView<AgentController> {
return navigationCard(context,
start: start,
end: end,
endlatitude: end_latitude,
endlongitude: end_longitude,
destination: destination,
modes: list,
navigation: navigation);

9
lib/modules/agent/views/navigation_card.dart

@ -45,6 +45,8 @@ Widget navigationCard(
BuildContext context, {
required String start,
required String end,
required String endlatitude,
required String endlongitude,
required String destination,
required List<Map<String, dynamic>> modes,
Map<String, dynamic>? navigation,
@ -413,7 +415,12 @@ Widget navigationCard(
child: InkWell(
borderRadius: BorderRadius.circular(12.r),
onTap: () {
controller.openNavigation(start, end, "");
controller.openNavigation(
start,
destination,
endlatitude,
endlongitude,
modes[currentIndex]["ntype"] as String);
},
child: Container(
padding: EdgeInsets.symmetric(

562
lib/modules/agent/views/weather_baidu.dart

@ -0,0 +1,562 @@
import 'package:common_utils/common_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
/// 构建天气内容卡片
Widget buildWeatherBaiduContent(BuildContext context, String city,
Map<String, dynamic> now, List<Map<String, dynamic>> days) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'weatherQuery'.tr, // 翻译键代表的中文文本内容:查询天气情况:
style: TextStyle(
color:
isDarkMode ? Colors.white.withValues(alpha: 0.9) : Colors.black87,
fontSize: 14.sp,
height: 1.4,
fontWeight: FontWeight.w400,
),
),
SizedBox(height: 12.h),
Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
gradient: _getWeatherGradient(now['text_day'] ?? '', isDarkMode),
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: isDarkMode ? 0.3 : 0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${city} ${now["date"]}",
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
SizedBox(height: 4.w),
Text(
_getWeatherDescriptionTranslation(now["text_day"]),
style: TextStyle(
fontSize: 14.sp,
color: Colors.white.withValues(alpha: 0.9),
),
),
SizedBox(height: 8.h),
Row(
children: [
Text(
"${now["high"]}°",
style: TextStyle(
fontSize: 36.sp,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(width: 8.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${'highest'.tr}: ${now["high"]}°', // 翻译键代表的中文文本内容:最高
style: TextStyle(
fontSize: 12.sp,
color: Colors.white.withValues(alpha: 0.9),
),
),
Text(
'${'lowest'.tr}: ${now["low"]}°', // 翻译键代表的中文文本内容:最低
style: TextStyle(
fontSize: 12.sp,
color: Colors.white.withValues(alpha: 0.9),
),
),
],
),
],
),
],
),
Icon(
getWeatherIcon(now['text_day']),
size: 48.sp,
color: Colors.white,
),
],
),
SizedBox(height: 16.h),
Container(
height: 1,
color: Colors.white.withValues(alpha: isDarkMode ? 0.15 : 0.2),
),
SizedBox(height: 16.h),
Text(
'futureForecast'.tr, // 翻译键代表的中文文本内容:未来预报
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
SizedBox(height: 8.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(
days.length,
(index) {
String dateStr = days[index]['date'];
DateTime? date = DateUtil.getDateTime(dateStr);
IconData weatherIcon =
getWeatherIcon(days[index]['text_day']);
// 获取星期几的翻译键
String weekdayKey = _getWeekdayTranslationKey(date);
String weekday = weekdayKey.tr; // 翻译键代表的中文文本内容:对应的星期几
String temp = "${days[index]['high']}°";
return _buildForecastDay(
weekday,
weatherIcon,
temp,
);
},
),
),
SizedBox(height: 16.h),
_buildWeatherTip(context, now, isDarkMode),
],
),
),
],
);
}
/// 获取天气描述的翻译
String _getWeatherDescriptionTranslation(String weatherDescription) {
final description = weatherDescription.trim();
// 天气描述映射表
final Map<String, String> weatherMap = {
// 晴天类
'晴': 'sunny'.tr, // 翻译键代表的中文文本内容:晴
'少云': 'partlyCloudy'.tr, // 翻译键代表的中文文本内容:少云
'晴间多云': 'partlySunny'.tr, // 翻译键代表的中文文本内容:晴间多云
// 多云类
'多云': 'cloudy'.tr, // 翻译键代表的中文文本内容:多云
'阴': 'overcast'.tr, // 翻译键代表的中文文本内容:阴
// 雨天类
'阵雨': 'shower'.tr, // 翻译键代表的中文文本内容:阵雨
'雷阵雨': 'thundershower'.tr, // 翻译键代表的中文文本内容:雷阵雨
'小雨': 'lightRain'.tr, // 翻译键代表的中文文本内容:小雨
'中雨': 'moderateRain'.tr, // 翻译键代表的中文文本内容:中雨
'大雨': 'heavyRain'.tr, // 翻译键代表的中文文本内容:大雨
'暴雨': 'rainstorm'.tr, // 翻译键代表的中文文本内容:暴雨
// 雪天类
'阵雪': 'snowShower'.tr, // 翻译键代表的中文文本内容:阵雪
'小雪': 'lightSnow'.tr, // 翻译键代表的中文文本内容:小雪
'中雪': 'moderateSnow'.tr, // 翻译键代表的中文文本内容:中雪
'大雪': 'heavySnow'.tr, // 翻译键代表的中文文本内容:大雪
'暴雪': 'blizzard'.tr, // 翻译键代表的中文文本内容:暴雪
// 雾霾类
'雾': 'fog'.tr, // 翻译键代表的中文文本内容:雾
'霾': 'haze'.tr, // 翻译键代表的中文文本内容:霾
// 风类
'大风': 'strongWind'.tr, // 翻译键代表的中文文本内容:大风
};
// 返回翻译后的描述,如果没有找到对应翻译则返回原文
return weatherMap[description] ?? description;
}
/// 获取星期几的翻译键
String _getWeekdayTranslationKey(DateTime? date) {
if (date == null) return 'unknown';
switch (date.weekday) {
case 1:
return 'monday'; // 翻译键代表的中文文本内容:周一
case 2:
return 'tuesday'; // 翻译键代表的中文文本内容:周二
case 3:
return 'wednesday'; // 翻译键代表的中文文本内容:周三
case 4:
return 'thursday'; // 翻译键代表的中文文本内容:周四
case 5:
return 'friday'; // 翻译键代表的中文文本内容:周五
case 6:
return 'saturday'; // 翻译键代表的中文文本内容:周六
case 7:
return 'sunday'; // 翻译键代表的中文文本内容:周日
default:
return 'unknown';
}
}
/// 构建智能天气提示
Widget _buildWeatherTip(
BuildContext context, Map<String, dynamic> now, bool isDarkMode) {
final weatherTip = _generateWeatherTip(now);
return Container(
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 12.w),
decoration: BoxDecoration(
color: isDarkMode
? Colors.white.withValues(alpha: 0.15)
: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(8.r),
),
child: Row(
children: [
Icon(
weatherTip.icon,
size: 16.sp,
color: Colors.white,
),
SizedBox(width: 8.w),
Flexible(
child: Text(
weatherTip.message,
style: TextStyle(
fontSize: 12.sp,
color: Colors.white,
),
),
),
],
),
);
}
/// 天气提示数据类
class WeatherTip {
final IconData icon;
final String message;
const WeatherTip({required this.icon, required this.message});
}
/// 根据天气数据生成智能提示
WeatherTip _generateWeatherTip(Map<String, dynamic> now) {
final dayWeather = (now['dayweather'] as String? ?? '').trim();
final dayTemp = int.tryParse(now['daytemp']?.toString() ?? '0') ?? 0;
final nightTemp = int.tryParse(now['nighttemp']?.toString() ?? '0') ?? 0;
// 高温提示 (>= 35°C)
if (dayTemp >= 35) {
return WeatherTip(
icon: Icons.local_fire_department,
message: 'hotWeatherTip'.tr, // 翻译键代表的中文文本内容:今日高温炎热,请注意防暑降温,多喝水避免中暑
);
}
// 低温提示 (<= 5°C)
if (dayTemp <= 5) {
return WeatherTip(
icon: Icons.ac_unit,
message: 'coldWeatherTip'.tr, // 翻译键代表的中文文本内容:今日气温较低,外出请注意保暖,小心感冒
);
}
// 雨天提示
if (dayWeather.contains('雨') ||
['阵雨', '雷阵雨', '小雨', '中雨', '大雨', '暴雨']
.any((rain) => dayWeather.contains(rain))) {
return WeatherTip(
icon: Icons.umbrella,
message: 'rainyWeatherTip'.tr, // 翻译键代表的中文文本内容:今日有雨,外出请携带雨具,注意出行安全
);
}
// 雪天提示
if (dayWeather.contains('雪') ||
['阵雪', '小雪', '中雪', '大雪', '暴雪'].any((snow) => dayWeather.contains(snow))) {
return WeatherTip(
icon: Icons.ac_unit,
message: 'snowyWeatherTip'.tr, // 翻译键代表的中文文本内容:今日有雪,路面湿滑请注意安全,做好保暖措施
);
}
// 雾霾提示
if (dayWeather.contains('雾') ||
dayWeather.contains('霾') ||
['雾', '浓雾', '霾', '中度霾', '重度霾'].any((fog) => dayWeather.contains(fog))) {
return WeatherTip(
icon: Icons.blur_on,
message: 'foggyWeatherTip'.tr, // 翻译键代表的中文文本内容:今日有雾霾,能见度较低,外出请佩戴口罩
);
}
// 大风提示
if (dayWeather.contains('风') ||
['大风', '强风', '疾风', '烈风'].any((wind) => dayWeather.contains(wind))) {
return WeatherTip(
icon: Icons.air,
message: 'windyWeatherTip'.tr, // 翻译键代表的中文文本内容:今日风力较大,外出请注意安全,固定好物品
);
}
// 晴天紫外线提示 (温度 >= 25°C)
if (['晴', '少云', '晴间多云'].any((sunny) => dayWeather.contains(sunny)) &&
dayTemp >= 25) {
return WeatherTip(
icon: Icons.wb_sunny,
message: 'sunnyWeatherTip'.tr, // 翻译键代表的中文文本内容:今日阳光充足紫外线较强,外出请做好防晒
);
}
// 温差大提示 (>= 10°C)
if ((dayTemp - nightTemp).abs() >= 10) {
return WeatherTip(
icon: Icons.thermostat,
message: 'temperatureDifferenceTip'.tr, // 翻译键代表的中文文本内容:今日昼夜温差较大,请适时增减衣物
);
}
// 默认舒适天气提示
return WeatherTip(
icon: Icons.wb_sunny_outlined,
message: 'comfortableWeatherTip'.tr, // 翻译键代表的中文文本内容:今日天气适宜,是外出活动的好时光
);
}
/// 构建单日天气预报
Widget _buildForecastDay(String day, IconData icon, String temp) {
return Column(
children: [
Text(
day,
style: TextStyle(
fontSize: 12.sp,
color: Colors.white.withValues(alpha: 0.9),
),
),
SizedBox(height: 4.h),
Icon(
icon,
size: 20.sp,
color: Colors.white,
),
SizedBox(height: 4.h),
Text(
temp,
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
);
}
/// 获取天气图标
IconData getWeatherIcon(String description) {
description = description.trim();
if (["晴", "少云", "晴间多云"].contains(description)) {
return Icons.wb_sunny;
}
if (["多云", "阴"].contains(description)) {
return Icons.cloud;
}
if (description.contains("雾") ||
["雾", "浓雾", "强浓雾", "轻雾", "大雾", "特强浓雾", "霾", "中度霾", "重度霾", "严重霾"]
.contains(description)) {
return Icons.blur_on;
}
if (description.contains("风") ||
[
"有风",
"平静",
"微风",
"和风",
"清风",
"强风/劲风",
"疾风",
"大风",
"烈风",
"风暴",
"狂爆风",
"飓风",
"热带风暴"
].contains(description)) {
return Icons.air;
}
if (description.contains("雨") ||
[
"阵雨",
"雷阵雨",
"雷阵雨并伴有冰雹",
"小雨",
"中雨",
"大雨",
"暴雨",
"大暴雨",
"特大暴雨",
"强阵雨",
"强雷阵雨",
"极端降雨",
"毛毛雨/细雨",
"雨",
"小雨-中雨",
"中雨-大雨",
"大雨-暴雨",
"暴雨-大暴雨",
"大暴雨-特大暴雨"
].contains(description)) {
return Icons.grain; // 或使用 community icon 包:weather_icons 中的 rain
}
if (description.contains("雪") ||
[
"雨雪天气",
"雨夹雪",
"阵雨夹雪",
"冻雨",
"雪",
"阵雪",
"小雪",
"中雪",
"大雪",
"暴雪",
"小雪-中雪",
"中雪-大雪",
"大雪-暴雪"
].contains(description)) {
return Icons.ac_unit;
}
if (["浮尘", "扬沙", "沙尘暴", "强沙尘暴"].contains(description)) {
return Icons.filter_drama;
}
if (["龙卷风"].contains(description)) {
return Icons.waves;
}
if (["热"].contains(description)) {
return Icons.local_fire_department;
}
if (["冷"].contains(description)) {
return Icons.ac_unit;
}
// 默认或未知天气
return Icons.help_outline;
}
/// 根据天气类型获取背景渐变颜色
LinearGradient _getWeatherGradient(String weather, bool isDarkMode) {
final weatherType = weather.trim();
// 晴天 - 温暖的橙黄色
if (['晴', '少云', '晴间多云'].any((sunny) => weatherType.contains(sunny))) {
return LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: isDarkMode
? [Color(0xFFFF8A00), Color(0xFFE65100)] // 深色模式:深橙色
: [Color(0xFFFFB74D), Color(0xFFFF9800)], // 浅色模式:亮橙色
);
}
// 多云/阴天 - 灰蓝色
if (['多云', '阴'].any((cloudy) => weatherType.contains(cloudy))) {
return LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: isDarkMode
? [Color(0xFF455A64), Color(0xFF37474F)] // 深色模式:深灰蓝
: [Color(0xFF78909C), Color(0xFF607D8B)], // 浅色模式:灰蓝色
);
}
// 雨天 - 深蓝色
if (weatherType.contains('雨') ||
['阵雨', '雷阵雨', '小雨', '中雨', '大雨', '暴雨']
.any((rain) => weatherType.contains(rain))) {
return LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: isDarkMode
? [Color(0xFF1565C0), Color(0xFF0D47A1)] // 深色模式:深蓝色
: [Color(0xFF42A5F5), Color(0xFF1976D2)], // 浅色模式:亮蓝色
);
}
// 雪天 - 冷色调蓝白色
if (weatherType.contains('雪') ||
['阵雪', '小雪', '中雪', '大雪', '暴雪']
.any((snow) => weatherType.contains(snow))) {
return LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: isDarkMode
? [Color(0xFF0277BD), Color(0xFF01579B)] // 深色模式:深冷蓝
: [Color(0xFF81D4FA), Color(0xFF29B6F6)], // 浅色模式:浅蓝色
);
}
// 雾霾天 - 暗黄灰色
if (weatherType.contains('雾') ||
weatherType.contains('霾') ||
['雾', '浓雾', '霾', '中度霾', '重度霾'].any((fog) => weatherType.contains(fog))) {
return LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: isDarkMode
? [Color(0xFF5D4037), Color(0xFF3E2723)] // 深色模式:深棕色
: [Color(0xFF8D6E63), Color(0xFF6D4C41)], // 浅色模式:棕灰色
);
}
// 大风天 - 青绿色
if (weatherType.contains('风') ||
['大风', '强风', '疾风', '烈风'].any((wind) => weatherType.contains(wind))) {
return LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: isDarkMode
? [Color(0xFF00695C), Color(0xFF004D40)] // 深色模式:深青色
: [Color(0xFF26A69A), Color(0xFF00796B)], // 浅色模式:青绿色
);
}
// 默认 - 原来的蓝色
return LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: isDarkMode
? [Color(0xFF1E40AF), Color(0xFF1E3A8A)]
: [Color(0xFF60A5FA), Color(0xFF3B82F6)],
);
}

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

@ -138,7 +138,7 @@ object AgentService : CoroutineScope {
private val INTERRUPT_DEBOUNCE_MS = 300L
var audioSourceType = AzureAsrHelper.AudioSourceType.MICROPHONE
var navigationMode = ""
// 语气词过滤列表(中英文)
private val FILLER_WORDS = setOf(
// 中文语气词
@ -238,7 +238,7 @@ object AgentService : CoroutineScope {
// 初始化TTS服务
initializeTtsEngine(config)
navigationMode = config["navigationMode"]?.toString() ?: ""
// 初始化ChatAPI服务
chatApiService.initialize(
config["openaiApiKey"]?.toString() ?: "",
@ -2058,16 +2058,18 @@ object AgentService : CoroutineScope {
* @return 是否成功开始处理
*/
fun processNavigation(start: String, end: String) {
// 如果 playlist 中有有效的歌曲,开始播放
if (!start.isNullOrEmpty() && !end.isNullOrEmpty()) {
NavigationServiceHelper.startNavigation(
context,
"start",
startpos = start,
endpos = end
)
} else {
Log.i(TAG, "启动导航服务失败")
if (navigationMode == "internal_gaode"){
// 如果 playlist 中有有效的歌曲,开始播放
if (!start.isNullOrEmpty() && !end.isNullOrEmpty()) {
NavigationServiceHelper.startNavigation(
context,
"start",
startpos = start,
endpos = end
)
} else {
Log.i(TAG, "启动导航服务失败")
}
}
}

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

@ -76,6 +76,7 @@ class AgentServicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.Stream
config["serverurl"]?.toString()?.let { intent.putExtra("serverurl", it) }
config["usertoken"]?.toString()?.let { intent.putExtra("usertoken", it) }
config["musicServiceType"]?.toString()?.let { intent.putExtra("musicServiceType", it) }
config["navigationMode"]?.toString()?.let { intent.putExtra("navigationMode", it) }
// 添加 supportedLanguages 参数处理
config["supportedLanguages"]?.let { languages ->

5
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt

@ -53,7 +53,7 @@ class BleMediaService : Service(), CoroutineScope {
private const val KEY_USER_TOKEN = "usertoken"
private const val KEY_SUPPORTED_LANGUAGES = "supportedLanguages"
private const val KEY_MUSICSERVICE_TYPE = "musicServiceType"
private const val APP_NAVIGATION_MODE = "navigationMode"
// 用于跟踪服务是否正在运行
private val isRunning = AtomicBoolean(false)
@ -182,7 +182,7 @@ class BleMediaService : Service(), CoroutineScope {
it.getStringExtra(KEY_SERVER_URL)?.let { value -> config[KEY_SERVER_URL] = value }
it.getStringExtra(KEY_USER_TOKEN)?.let { value -> config[KEY_USER_TOKEN] = value }
it.getStringExtra(KEY_MUSICSERVICE_TYPE)?.let { value -> config[KEY_MUSICSERVICE_TYPE] = value }
it.getStringExtra(APP_NAVIGATION_MODE)?.let { value -> config[APP_NAVIGATION_MODE] = value }
// 提取 supportedLanguages 参数
it.getStringArrayListExtra(KEY_SUPPORTED_LANGUAGES)?.let { languages ->
if (languages.isNotEmpty()) {
@ -242,6 +242,7 @@ class BleMediaService : Service(), CoroutineScope {
config[KEY_SERVER_URL] = getConfigString(prefs, KEY_SERVER_URL, "")
config[KEY_USER_TOKEN] = getConfigString(prefs, KEY_USER_TOKEN, "")
config[KEY_MUSICSERVICE_TYPE] = getConfigString(prefs, KEY_MUSICSERVICE_TYPE, "")
config[APP_NAVIGATION_MODE] = getConfigString(prefs, APP_NAVIGATION_MODE, "")
// 添加 supportedLanguages 参数处理
val supportedLanguagesStr = getConfigString(prefs, KEY_SUPPORTED_LANGUAGES, "")

Loading…
Cancel
Save