diff --git a/apps/client/ios/Podfile b/apps/client/ios/Podfile index 3c25a837..af1e8479 100644 --- a/apps/client/ios/Podfile +++ b/apps/client/ios/Podfile @@ -63,20 +63,15 @@ post_install do |installer| end end - # ---- Auto patch: bump FlutterGeneratedPluginSwiftPackage iOS platform ---- - # Flutter 生成的中介包总是写死 .iOS("12.0"),与 Podfile 中的 - # platform :ios, '16.0' 不一致,会报 - # "requires minimum platform version 15.0/16.0 ... but this target supports 12.0"。 - # 这里自动同步为 16.0。 + # ---- Auto patch: bump generated SPM packages to the app deployment target ---- + # Flutter 生成的中介包写死 .iOS("13.0"),本地插件要求 16.0,会报 + # "requires minimum platform version 16.0 ... but this target supports 13.0/15.0"。 + # 与 Xcode scheme 里同一份脚本,pod install 时也对齐一次。 generated_pkg = File.expand_path( 'Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift', __dir__) - if File.exist?(generated_pkg) - content = File.read(generated_pkg) - new_content = content.gsub(/\.iOS\("\d+\.\d+"\)/, '.iOS("16.0")') - if new_content != content - File.write(generated_pkg, new_content) - puts '[Podfile patch] Bumped FlutterGeneratedPluginSwiftPackage iOS platform to 16.0' - end + sync_spm = File.expand_path('sync_spm_platform.sh', __dir__) + if File.exist?(generated_pkg) && File.exist?(sync_spm) + system(sync_spm) or raise '[Podfile patch] sync_spm_platform.sh 失败' end # ---- Auto patch: comment out illegal `` imports in all Pods sources ---- diff --git a/apps/client/ios/Runner.xcodeproj/project.pbxproj b/apps/client/ios/Runner.xcodeproj/project.pbxproj index 59f18b63..8d8cf0e6 100644 --- a/apps/client/ios/Runner.xcodeproj/project.pbxproj +++ b/apps/client/ios/Runner.xcodeproj/project.pbxproj @@ -431,7 +431,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "\"$SRCROOT/sync_spm_platform.sh\"\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; B6CA63E72817925CE1A152C3 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -649,7 +649,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 24; + CURRENT_PROJECT_VERSION = 26; DEVELOPMENT_TEAM = B8AJQ7ACU7; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -843,7 +843,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 24; + CURRENT_PROJECT_VERSION = 26; DEVELOPMENT_TEAM = B8AJQ7ACU7; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -877,7 +877,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 24; + CURRENT_PROJECT_VERSION = 26; DEVELOPMENT_TEAM = B8AJQ7ACU7; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; diff --git a/apps/client/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/apps/client/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 445444b0..353e06b5 100644 --- a/apps/client/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/apps/client/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -10,7 +10,7 @@ ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction"> + scriptText = ""$SRCROOT/sync_spm_platform.sh" /bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare "> "$tmp" < + + + + Label + ${label} + ProgramArguments + + ${IOS_DIR}/sync_spm_platform.sh + --from-watcher + + WatchPaths + + ${dir} + + ThrottleInterval + 1 + + +EOF + if [[ -f "$plist" ]] && cmp -s "$tmp" "$plist"; then + rm -f "$tmp" + else + mv "$tmp" "$plist" + launchctl bootout "gui/${uid}/${label}" >/dev/null 2>&1 || true + launchctl bootstrap "gui/${uid}" "$plist" >/dev/null 2>&1 || true + fi + # 已经在跑就别再 bootstrap;没加载过则补一次 + if ! launchctl print "gui/${uid}/${label}" >/dev/null 2>&1; then + launchctl bootstrap "gui/${uid}" "$plist" >/dev/null 2>&1 || true + fi +} + +# 监视器回调自己时不要再去装卸监视器,否则写文件会把自己叫醒。 +if [[ "${1:-}" != "--from-watcher" ]]; then + install_watcher || true +fi + red() { printf "\033[31m%s\033[0m\n" "$*"; } green() { printf "\033[32m%s\033[0m\n" "$*"; } blue() { printf "\033[34m%s\033[0m\n" "$*"; } @@ -95,6 +151,8 @@ if ! le "$FLOOR" "$CEILING"; then fi # ── 4) 对齐生成包 ─────────────────────────────────────────────────────────── +# 插件包必须存在。FlutterFramework 是可选的,在才改。 +MANIFEST="${MANIFESTS[0]}" if [[ ! -f "$MANIFEST" ]]; then red "✗ 生成包还不存在:${MANIFEST#"$CLIENT_DIR"/}" red " 先在 apps/client 下跑一次 \`flutter pub get\`(或 \`flutter build ios --config-only\`)再执行本脚本。" @@ -123,24 +181,34 @@ ensure_local_plugin_links() { } ensure_local_plugin_links -CURRENT_RAW="$(grep -o '\.iOS([^)]*)' "$MANIFEST" | head -1 | sed 's/^\.iOS(//; s/)$//')" -CURRENT="$(normalize "$CURRENT_RAW")" - -if [[ "$CURRENT" == "$CEILING" ]]; then - green "✓ 生成包已是 iOS ${CURRENT},无需改动" - exit 0 -fi - -if [[ $CHECK_ONLY -eq 1 ]]; then - red "✗ 生成包是 iOS ${CURRENT},应为 ${CEILING}(跑 ./ios/sync_spm_platform.sh 修)" - exit 1 -fi - -# 只替换 platforms 段里的第一个 .iOS(...),不碰文件其余内容 -/usr/bin/sed -i '' "s/\.iOS($(printf '%s' "$CURRENT_RAW" | sed 's/[.[\*^$\/]/\\&/g'))/.iOS(\"$CEILING\")/" "$MANIFEST" +align_one() { + local manifest="$1" + [[ -f "$manifest" ]] || return 0 + local current_raw current + current_raw="$(grep -o '\.iOS([^)]*)' "$manifest" | head -1 | sed 's/^\.iOS(//; s/)$//')" + current="$(normalize "$current_raw")" + local label="${manifest#"$CLIENT_DIR"/}" + if [[ "$current" == "$CEILING" ]]; then + green "✓ ${label} 已是 iOS ${current}" + return 0 + fi + if [[ $CHECK_ONLY -eq 1 ]]; then + red "✗ ${label} 是 iOS ${current},应为 ${CEILING}(跑 ./ios/sync_spm_platform.sh 修)" + return 1 + fi + # 只替换 platforms 段里的第一个 .iOS(...),不碰文件其余内容 + /usr/bin/sed -i '' "s/\.iOS($(printf '%s' "$current_raw" | sed 's/[.[\*^$\/]/\\&/g'))/.iOS(\"$CEILING\")/" "$manifest" + local new + new="$(normalize "$(grep -o '\.iOS([^)]*)' "$manifest" | head -1 | sed 's/^\.iOS(//; s/)$//')")" + if [[ "$new" != "$CEILING" ]]; then + red "✗ 改写失败 ${label},现在是 ${new}" + return 1 + fi + green "✓ ${label} 平台版本 ${current} → ${CEILING}" +} -NEW="$(normalize "$(grep -o '\.iOS([^)]*)' "$MANIFEST" | head -1 | sed 's/^\.iOS(//; s/)$//')")" -if [[ "$NEW" != "$CEILING" ]]; then - red "✗ 改写失败,现在是 ${NEW}"; exit 1 -fi -green "✓ 生成包平台版本 ${CURRENT} → ${CEILING},可以回 Xcode 构建了" +fail=0 +for m in "${MANIFESTS[@]}"; do + align_one "$m" || fail=1 +done +exit "$fail" diff --git a/apps/client/lib/data/services/bailian_multimodal_service.dart b/apps/client/lib/data/services/bailian_multimodal_service.dart index 92dc2e08..fa6e7fc2 100644 --- a/apps/client/lib/data/services/bailian_multimodal_service.dart +++ b/apps/client/lib/data/services/bailian_multimodal_service.dart @@ -202,10 +202,35 @@ class BailianMultimodalService extends GetxService { /// 服务是单例、两条链路轮流用,所以在 [start] 时定,不由调用方每次传。 String directiveSource = 'emai'; - /// 需要在 Start 时带上「位置 + 日期」的百炼应用 ID(由模块在 registerServices 里登记, - /// 目前只有车载香薰)。其它应用(EMAI 耳机)的 Start 报文一个字节不变。 + /// 额外需要在 Start 时带上「位置 + 日期」的百炼应用 ID。 + /// 车载助手在自己的模块里登记。EMAI 不进这个集合:它就是 [resolveAppId] 的默认应用 + /// (含后台覆盖后的 id),由 [wantsClientContext] 认出来。 static final Set clientContextAppIds = {}; + /// EMAI 与登记过的应用在 Start 时带位置、日期。其它应用一个字节不变。 + static bool wantsClientContext(String appId) { + if (clientContextAppIds.contains(appId)) return true; + return appId.isNotEmpty && appId == resolveAppId(); + } + + /// `${location}` / `${date}` 与 `client_info.location`。只读缓存,不等定位。 + /// 这个应用不需要时返回 null。没有缓存时地址是「未知」,日期照样给。 + static Map? promptContext(String appId) { + if (!wantsClientContext(appId)) return null; + DeviceLocationService? loc; + try { + loc = DeviceLocationService.maybe; + } catch (_) { + loc = null; + } + final clientLocation = loc?.clientLocation(); + return { + 'location': loc?.placeText() ?? '未知', + 'date': DeviceLocationService.dateText(), + if (clientLocation != null) 'clientLocation': clientLocation, + }; + } + Future start({ String mode = 'push2talk', String? voice, @@ -227,16 +252,15 @@ class BailianMultimodalService extends GetxService { directiveSource = source; _taskId = _uuid(); // 提示词「系统条件」里的 ${location} / ${date}:只读缓存,不等定位 - final withContext = clientContextAppIds.contains(_appId); - final loc = withContext ? DeviceLocationService.maybe : null; - final clientLocation = loc?.clientLocation(); - final promptParams = withContext - ? { - 'location': loc?.placeText() ?? '未知', - 'date': DeviceLocationService.dateText(), - } - : const {}; - if (withContext) { + final ctx = promptContext(_appId); + final clientLocation = ctx?['clientLocation'] as Map?; + final promptParams = ctx == null + ? const {} + : { + 'location': '${ctx['location']}', + 'date': '${ctx['date']}', + }; + if (ctx != null) { Logger.i( _tag, '会话上下文: $promptParams, client_info.location=$clientLocation'); } diff --git a/apps/client/lib/data/services/bailian_native_dialog.dart b/apps/client/lib/data/services/bailian_native_dialog.dart index 1333b8f2..28191e07 100644 --- a/apps/client/lib/data/services/bailian_native_dialog.dart +++ b/apps/client/lib/data/services/bailian_native_dialog.dart @@ -119,9 +119,22 @@ class BailianNativeDialog { }, ); try { + final appId = BailianMultimodalService.resolveAppId(_appIdOverride); + // 地址和日期的文案只在 Dart 算一份。原生负责写进 Start,不在这里等定位。 + final ctx = BailianMultimodalService.promptContext(appId); + final rawLoc = ctx?['clientLocation']; + final locMap = rawLoc is Map + ? rawLoc.map((k, v) => MapEntry('$k', '$v')) + : null; + if (ctx != null) { + Logger.i( + _tag, + '会话上下文: location=${ctx['location']}, date=${ctx['date']}, ' + 'client_info.location=${locMap == null ? '无' : '有'}'); + } final ok = await _channel.invokeMethod('start', { 'apiKey': BailianMultimodalService.resolveApiKey(), - 'appId': BailianMultimodalService.resolveAppId(_appIdOverride), + 'appId': appId, 'workspaceId': BailianMultimodalService.resolveWorkspaceId(_workspaceIdOverride), 'mode': mode, @@ -132,6 +145,11 @@ class BailianNativeDialog { if (mcpToken != null && mcpToken.trim().isNotEmpty) 'mcpToken': mcpToken, // 工具名只在 Dart 维护一份:服务端加工具时改那一处就够了 'mcpAuthTools': BailianMultimodalService.mcpAuthTools, + if (ctx != null) 'promptLocation': ctx['location'], + if (ctx != null) 'promptDate': ctx['date'], + if (locMap != null) 'latitude': locMap['latitude'], + if (locMap != null) 'longitude': locMap['longitude'], + if ((locMap?['city_name'] ?? '').isNotEmpty) 'cityName': locMap!['city_name'], }); _running = ok == true; if (!_running) await _cancelSub(); diff --git a/apps/client/lib/data/services/device_location_service.dart b/apps/client/lib/data/services/device_location_service.dart index 30c72577..e4fca8ef 100644 --- a/apps/client/lib/data/services/device_location_service.dart +++ b/apps/client/lib/data/services/device_location_service.dart @@ -10,18 +10,20 @@ import '../../core/utils/logger.dart'; import '../../devices/device_hub.dart'; import '../../devices/device_vendors.dart'; -/// 给百炼会话提供「用户在哪、现在几点」(2026-09-25,车载香薰)。 +/// 给百炼会话提供「用户在哪、现在几点」(2026-09-25 车载香薰,2026-10-09 起 EMAI 耳机共用)。 /// -/// 车载助手的提示词里有「系统条件」:`${location}`(用户当前位置)、`${date}`(今天的日期)。 +/// 车载助手和 EMAI 的提示词里有「系统条件」:`${location}`(用户当前位置)、`${date}`(今天的日期)。 /// 以前一个都没传,AI 问天气只能反问「你要查哪里」。现在两条通道一起给 /// (见 [BailianMultimodalService.start]): /// - `client_info.location` = {latitude, longitude, city_name}(lumi 也是这么传经纬度的); -/// - `biz_params.user_prompt_params` = {location: 省市区, date: 年月日 星期 时:分}。 +/// - `biz_params.user_prompt_params` = {location: 完整地址, date: 年月日 星期 时:分}。 +/// `${location}` 优先高德返回的详细地址(路名、门牌),没有再退回省市区加街道。 +/// 经纬度只进 `client_info.location`,不进这段要念出来的文案。 /// /// 节奏照 lumi 的 LocationService:启动 / 回到前台时刷新,10 分钟内不重复,结果落盘; /// **建会话时只读缓存,从不等定位**,所以不拖慢唤醒。 /// -/// 权限:车载香薰第一次连上(且 App 在前台)时申请一次「使用期间」定位权限, +/// 权限:恒玄耳机或车载香薰第一次连上(且 App 在前台)时申请一次「使用期间」定位权限, /// 用户拒绝后不再追问——没有权限就只是不带位置。 class DeviceLocationService extends GetxService with WidgetsBindingObserver { static const String _tag = 'DeviceLocation'; @@ -67,7 +69,8 @@ class DeviceLocationService extends GetxService with WidgetsBindingObserver { } void _checkSessions(List list) { - if (list.any((s) => s.vendor == DeviceVendors.eaicar)) { + if (list.any((s) => + s.vendor == DeviceVendors.eaicar || s.vendor == DeviceVendors.bes)) { unawaited(askPermissionOnce()); } } @@ -92,7 +95,7 @@ class DeviceLocationService extends GetxService with WidgetsBindingObserver { if (state == AppLifecycleState.resumed) refresh(); } - /// 车载香薰第一次连上时申请一次定位权限。只问一次,拒绝后不再打扰。 + /// 耳机或车载香薰第一次连上时申请一次定位权限。只问一次,拒绝后不再打扰。 Future askPermissionOnce() async { if (_storage.read(_askedKey) == true) return; // 权限框要在前台弹;在后台(被动回连)时先不问,下次前台连上再说 @@ -103,7 +106,7 @@ class DeviceLocationService extends GetxService with WidgetsBindingObserver { await _storage.write(_askedKey, true); try { final st = await Permission.locationWhenInUse.request(); - Logger.i(_tag, '车载香薰首次连接,申请定位权限 → $st'); + Logger.i(_tag, '设备首次连接,申请定位权限 → $st'); if (st.isGranted || st.isLimited) await refresh(force: true); } catch (e) { Logger.w(_tag, '申请定位权限失败: $e'); @@ -144,24 +147,20 @@ class DeviceLocationService extends GetxService with WidgetsBindingObserver { String _s(String k) => (_place?[k] ?? '').toString().trim(); - /// `${location}` 的取值:省市区(去掉重复的直辖市名),拿不到就是「未知」。 - String placeText() { + /// `${location}` 的取值。优先完整地址;没有再拼省市区加街道。不含经纬度。 + String placeText() => formatPlaceText(_place); + + /// 纯函数,方便单测。`place` 的键与 [LocationService.currentPlace] 一致。 + static String formatPlaceText(Map? place) { + String field(String k) => (place?[k] ?? '').toString().trim(); + final address = field('address'); + if (address.isNotEmpty) return address; final parts = []; - for (final k in ['province', 'city', 'district']) { - final v = _s(k); + for (final k in ['province', 'city', 'district', 'street']) { + final v = field(k); if (v.isNotEmpty && !parts.contains(v)) parts.add(v); } - if (parts.isEmpty) { - final addr = _s('address'); - if (addr.isNotEmpty) return addr; - // 反查不到地名时用坐标描述,模型/插件还能据此判断城市,比「未知」强 - final lat = _place?['latitude']; - final lng = _place?['longitude']; - if (lat is num && lng is num) { - return '经纬度 ${lat.toStringAsFixed(4)},${lng.toStringAsFixed(4)}'; - } - return '未知'; - } + if (parts.isEmpty) return '未知'; return parts.join(); } diff --git a/apps/client/lib/modules/eaicar/module.dart b/apps/client/lib/modules/eaicar/module.dart index babcbf31..13fecd63 100644 --- a/apps/client/lib/modules/eaicar/module.dart +++ b/apps/client/lib/modules/eaicar/module.dart @@ -35,8 +35,8 @@ final AgentModuleDescriptor eaicarModule = AgentModuleDescriptor( entryRoute: Routes.eaicarChat, // 设备按键唤醒时接的就是这个应用(不是 EMAI)——见 AgentModuleDescriptor.bailianAppId bailianAppId: kEaiCarBailianAppId, - // 车载助手提示词要 ${location} / ${date}:登记本应用,Start 时带上位置与日期; - // 位置服务也只为它常驻(EMAI 耳机的会话不带、不受影响)。 + // 车载助手提示词要 ${location} / ${date}:登记本应用,Start 时带上位置与日期。 + // 位置服务两边共用;EMAI 自己的应用 ID 不进这个集合,由 wantsClientContext 认。 registerServices: () { BailianMultimodalService.clientContextAppIds.add(kEaiCarBailianAppId); if (!Get.isRegistered()) { diff --git a/apps/client/lib/modules/emai/module.dart b/apps/client/lib/modules/emai/module.dart index 2243a314..cdf8615e 100644 --- a/apps/client/lib/modules/emai/module.dart +++ b/apps/client/lib/modules/emai/module.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../core/utils/ai_navigation.dart'; +import '../../data/services/device_location_service.dart'; import '../../routes/app_routes.dart'; import '../module_descriptor.dart'; import 'bindings/emai_binding.dart'; @@ -16,6 +17,13 @@ final AgentModuleDescriptor emaiModule = AgentModuleDescriptor( gradient: const [Color(0xFF5B8DEF), Color(0xFF7E57C2)], // 按 agentType 走移动精灵或 EMAI 页 onLaunch: navigateToAIAssistant, + // 位置缓存给手机页和耳机唤醒共用。手机页在 Dart Start 里读, + // 耳机按键唤醒由原生 Start 写入同一份缓存,这里只保证服务在。 + registerServices: () { + if (!Get.isRegistered()) { + Get.put(DeviceLocationService(), permanent: true); + } + }, pages: [ GetPage( name: Routes.emai, diff --git a/apps/client/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt b/apps/client/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt index 0ec7ce8b..b892242e 100644 --- a/apps/client/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt +++ b/apps/client/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt @@ -263,6 +263,11 @@ class AzureSpeechPlugin : BleService.Callback, FlutterPlugin, ActivityAware, downstreamRate = call.argument("downstreamRate") ?: 16000, mcpToken = call.argument("mcpToken"), mcpAuthTools = call.argument>("mcpAuthTools") ?: emptyList(), + promptLocation = call.argument("promptLocation"), + promptDate = call.argument("promptDate"), + latitude = call.argument("latitude"), + longitude = call.argument("longitude"), + cityName = call.argument("cityName"), ) val ok = client.start(cfg) if (ok) bridge.enable() diff --git a/apps/client/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/BailianDialogClient.kt b/apps/client/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/BailianDialogClient.kt index 949036f3..8c4390eb 100644 --- a/apps/client/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/BailianDialogClient.kt +++ b/apps/client/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/BailianDialogClient.kt @@ -73,6 +73,14 @@ class BailianDialogClient( * 服务端加工具时只改 Dart 那一处,漏改的表现是那个工具永远「身份校验失败」。 */ val mcpAuthTools: List, + /** `${location}`:完整地址。空则不写 user_prompt_params.location。 */ + val promptLocation: String? = null, + /** `${date}`:年月日 星期 时:分。空则不写。 */ + val promptDate: String? = null, + /** `client_info.location`。纬度或经度空着就整块不发。 */ + val latitude: String? = null, + val longitude: String? = null, + val cityName: String? = null, ) private val exec = Executors.newSingleThreadExecutor { r -> Thread(r, "azure_speech.bailian") } @@ -212,6 +220,15 @@ class BailianDialogClient( // 不传 voice:音色属于百炼应用的配置,硬编码只会跟应用实际用的 TTS 模型对不上 if (!c.voice.isNullOrBlank()) downstream.put("voice", c.voice) + val clientInfo = JSONObject().put("user_id", safeUserId(c.userId)) + if (!c.latitude.isNullOrBlank() && !c.longitude.isNullOrBlank()) { + val loc = JSONObject() + .put("latitude", c.latitude) + .put("longitude", c.longitude) + if (!c.cityName.isNullOrBlank()) loc.put("city_name", c.cityName) + clientInfo.put("location", loc) + } + val parameters = JSONObject() .put("upstream", JSONObject() .put("type", "AudioOnly") @@ -219,23 +236,36 @@ class BailianDialogClient( .put("audio_format", "pcm") .put("sample_rate", UPSTREAM_SAMPLE_RATE)) .put("downstream", downstream) - .put("client_info", JSONObject().put("user_id", safeUserId(c.userId))) + .put("client_info", clientInfo) + + val prompt = JSONObject() + if (!c.promptLocation.isNullOrBlank()) prompt.put("location", c.promptLocation) + if (!c.promptDate.isNullOrBlank()) prompt.put("date", c.promptDate) - // biz_params 是可选块,两件事都没有时整块不发——万一应用侧没开这些能力, + // biz_params 是可选块,三件事都没有时整块不发——万一应用侧没开这些能力, // 多带一个字段可能让 Start 直接被拒,那样整个功能都起不来 val hasToken = !c.mcpToken.isNullOrBlank() - if (c.enableWebSearch || hasToken) { - val userDefined = JSONObject() - if (hasToken) { - // ⚠️ **必须按工具名嵌套**,平铺写 auth_token 会被静默丢弃 - for (tool in c.mcpAuthTools) { - userDefined.put(tool, JSONObject().put("auth_token", c.mcpToken)) - } + if (c.enableWebSearch || hasToken || prompt.length() > 0) { + val biz = JSONObject() + if (prompt.length() > 0) { + biz.put("user_prompt_params", prompt) + Log.i(TAG, "会话上下文 location=${c.promptLocation} date=${c.promptDate} " + + "coords=${!c.latitude.isNullOrBlank()}") } - if (c.enableWebSearch) { - userDefined.put("extra_config", JSONObject().put("enable_web_search", true)) + if (c.enableWebSearch || hasToken) { + val userDefined = JSONObject() + if (hasToken) { + // ⚠️ **必须按工具名嵌套**,平铺写 auth_token 会被静默丢弃 + for (tool in c.mcpAuthTools) { + userDefined.put(tool, JSONObject().put("auth_token", c.mcpToken)) + } + } + if (c.enableWebSearch) { + userDefined.put("extra_config", JSONObject().put("enable_web_search", true)) + } + biz.put("user_defined_params", userDefined) } - parameters.put("biz_params", JSONObject().put("user_defined_params", userDefined)) + parameters.put("biz_params", biz) } return JSONObject() diff --git a/apps/client/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift b/apps/client/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift index bed18b14..0d808b80 100644 --- a/apps/client/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift +++ b/apps/client/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift @@ -497,7 +497,12 @@ internal func handleBailianDialogCall(_ call: FlutterMethodCall, result: @escapi enableWebSearch: (a["enableWebSearch"] as? Bool) ?? true, downstreamRate: (a["downstreamRate"] as? Int) ?? 16000, mcpToken: a["mcpToken"] as? String, - mcpAuthTools: (a["mcpAuthTools"] as? [String]) ?? [] + mcpAuthTools: (a["mcpAuthTools"] as? [String]) ?? [], + promptLocation: a["promptLocation"] as? String, + promptDate: a["promptDate"] as? String, + latitude: a["latitude"] as? String, + longitude: a["longitude"] as? String, + cityName: a["cityName"] as? String ) let ok = client.start(config: cfg) if ok { bridge.enable() } diff --git a/apps/client/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/BailianDialogClient.swift b/apps/client/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/BailianDialogClient.swift index df26cb3b..ade1be08 100644 --- a/apps/client/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/BailianDialogClient.swift +++ b/apps/client/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/BailianDialogClient.swift @@ -45,6 +45,14 @@ final class BailianDialogClient: NSObject { /// 需要用户身份的 MCP 工具名。**由 Dart 传下来**,不在原生再抄一份: /// 服务端加工具时只改 Dart 那一处,漏改的表现是那个工具永远「身份校验失败」。 let mcpAuthTools: [String] + /// `${location}`:完整地址。空则不写 user_prompt_params.location。 + let promptLocation: String? + /// `${date}`:年月日 星期 时:分。空则不写。 + let promptDate: String? + /// `client_info.location`。纬度或经度空着就整块不发。 + let latitude: String? + let longitude: String? + let cityName: String? } private static let endpoint = "wss://dashscope.aliyuncs.com/api-ws/v1/inference" @@ -212,6 +220,16 @@ final class BailianDialogClient: NSObject { downstream["voice"] = v } + var clientInfo: [String: Any] = ["user_id": Self.safeUserId(c.userId)] + let lat = c.latitude?.trimmingCharacters(in: .whitespaces) ?? "" + let lng = c.longitude?.trimmingCharacters(in: .whitespaces) ?? "" + if !lat.isEmpty && !lng.isEmpty { + var loc: [String: Any] = ["latitude": lat, "longitude": lng] + let city = c.cityName?.trimmingCharacters(in: .whitespaces) ?? "" + if !city.isEmpty { loc["city_name"] = city } + clientInfo["location"] = loc + } + var parameters: [String: Any] = [ "upstream": [ "type": "AudioOnly", @@ -220,24 +238,39 @@ final class BailianDialogClient: NSObject { "sample_rate": Self.upstreamSampleRate, ], "downstream": downstream, - "client_info": ["user_id": Self.safeUserId(c.userId)], + "client_info": clientInfo, ] - // biz_params 是可选块,两件事都没有时整块不发——万一应用侧没开这些能力, + var prompt: [String: String] = [:] + let spoken = c.promptLocation?.trimmingCharacters(in: .whitespaces) ?? "" + let day = c.promptDate?.trimmingCharacters(in: .whitespaces) ?? "" + if !spoken.isEmpty { prompt["location"] = spoken } + if !day.isEmpty { prompt["date"] = day } + + // biz_params 是可选块,三件事都没有时整块不发——万一应用侧没开这些能力, // 多带一个字段可能让 Start 直接被拒,那样整个功能都起不来 let hasToken = !(c.mcpToken ?? "").trimmingCharacters(in: .whitespaces).isEmpty - if c.enableWebSearch || hasToken { - var userDefined: [String: Any] = [:] - if hasToken, let tok = c.mcpToken { - // ⚠️ **必须按工具名嵌套**,平铺写 `auth_token` 会被静默丢弃 - for tool in c.mcpAuthTools { - userDefined[tool] = ["auth_token": tok] - } + if c.enableWebSearch || hasToken || !prompt.isEmpty { + var biz: [String: Any] = [:] + if !prompt.isEmpty { + biz["user_prompt_params"] = prompt + NSLog("[BailianDialog] 会话上下文 location=%@ date=%@ coords=%@", + spoken, day, lat.isEmpty ? "0" : "1") } - if c.enableWebSearch { - userDefined["extra_config"] = ["enable_web_search": true] + if c.enableWebSearch || hasToken { + var userDefined: [String: Any] = [:] + if hasToken, let tok = c.mcpToken { + // ⚠️ **必须按工具名嵌套**,平铺写 `auth_token` 会被静默丢弃 + for tool in c.mcpAuthTools { + userDefined[tool] = ["auth_token": tok] + } + } + if c.enableWebSearch { + userDefined["extra_config"] = ["enable_web_search": true] + } + biz["user_defined_params"] = userDefined } - parameters["biz_params"] = ["user_defined_params": userDefined] + parameters["biz_params"] = biz } return [ diff --git a/apps/client/local_plugins/location_service/android/build.gradle b/apps/client/local_plugins/location_service/android/build.gradle index 63c9f82d..3d0b95e4 100644 --- a/apps/client/local_plugins/location_service/android/build.gradle +++ b/apps/client/local_plugins/location_service/android/build.gradle @@ -24,6 +24,52 @@ allprojects { apply plugin: "com.android.library" apply plugin: "kotlin-android" +// 搜索 SDK 和导航 SDK 各带一份 AMapUtilCoreApi,导航那份是超集。 +// 直接 implementation search 会在打包时 Duplicate class。这里先剥掉那两个类,运行时用导航 SDK 的。 +configurations { + amapSearch +} + +dependencies { + amapSearch('com.amap.api:search:9.7.1') +} + +def stripAmapSearchJar = tasks.register("stripAmapSearchJar") { + def cfg = configurations.amapSearch + inputs.files(cfg) + def out = layout.buildDirectory.file("amap/search-stripped.jar") + outputs.file(out) + doLast { + def jar = cfg.singleFile + def dest = out.get().asFile + dest.parentFile.mkdirs() + def drop = [ + "com/amap/apis/utils/core/api/AMapUtilCoreApi.class", + "com/amap/apis/utils/core/api/NetProxy.class", + ] as Set + def zin = new java.util.zip.ZipFile(jar) + dest.withOutputStream { os -> + def zout = new java.util.zip.ZipOutputStream(os) + zin.entries().each { entry -> + if (drop.contains(entry.name)) return + zout.putNextEntry(new java.util.zip.ZipEntry(entry.name)) + if (!entry.isDirectory()) { + zin.getInputStream(entry).withCloseable { ins -> + byte[] buf = new byte[8192] + int n + while ((n = ins.read(buf)) > 0) { + zout.write(buf, 0, n) + } + } + } + zout.closeEntry() + } + zout.close() + } + zin.close() + } +} + android { namespace = "com.yunqiinnovation.location_service" @@ -50,6 +96,8 @@ android { // ✅ dependencies 应该写在这里(android {} 外面) dependencies { implementation('com.amap.api:navi-3dmap:latest.integration') + // 定位 SDK 在部分机型(华为无 GMS)只回坐标和市名,路名要单独走搜索 SDK 的逆地理。 + implementation(files(stripAmapSearchJar)) testImplementation("org.jetbrains.kotlin:kotlin-test") testImplementation("org.mockito:mockito-core:5.0.0") diff --git a/apps/client/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/PlaceLocator.kt b/apps/client/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/PlaceLocator.kt index 0d5addc3..0bcce442 100644 --- a/apps/client/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/PlaceLocator.kt +++ b/apps/client/local_plugins/location_service/android/src/main/kotlin/com/yunqiinnovation/location_service/PlaceLocator.kt @@ -9,42 +9,82 @@ import android.util.Log import androidx.core.content.ContextCompat import com.amap.api.location.AMapLocationClient import com.amap.api.location.AMapLocationClientOption +import com.amap.api.services.core.LatLonPoint +import com.amap.api.services.core.ServiceSettings +import com.amap.api.services.geocoder.GeocodeSearch +import com.amap.api.services.geocoder.RegeocodeQuery /** - * 取一次「当前位置 + 省市区」,给百炼会话的 `${location}` 用(2026-09-25,车载香薰)。 + * 取一次「当前位置 + 详细地址」,给百炼会话的 `${location}` 用(2026-09-25,车载香薰)。 * * 刻意不复用 [LocationService]:那套只收 50 米内的高精度结果、超时 60 秒,是给导航/签到用的; - * 这里只要知道「在哪个城市哪个区」,网络定位就够,要的是快。 + * 这里要路名和门牌,高德逆地理一次拿全,要的是快。 + * `address` 优先用高德格式化地址;它为空时用省市区、街道、门牌拼一条,供上层直接念。 * * - 没有定位权限直接回 `no_permission`,**不在这里弹权限框**(由 Dart 侧决定何时申请); - * - 高德定位自带逆地理(setNeedAddress),省/市/区一次拿全,华为无 GMS 的机器上也能用; - * - 坐标是高德的 GCJ-02,只用于城市级描述,偏移无关紧要。 + * - 定位回调经常只有市名(华为 ALN-AL80 上是「深圳市」)。没有路名时,用手里的经纬度 + * 再调一次高德逆地理,把格式化地址填进 `address`; + * - 坐标是高德的 GCJ-02,只放进 client_info,不放进要念出来的地址。 */ object PlaceLocator { - /** 系统 Geocoder 反查省市区;机器不支持 / 没网 / 出错都返回 null。在后台线程调用。 */ - private fun systemGeocode(context: Context, lat: Double, lng: Double): Map? { + /** 定位结果里已经有路名或门牌,就不必再打一次逆地理。 */ + private fun hasRoad(street: String?, streetNum: String?, address: String?): Boolean { + if (!street.isNullOrBlank() || !streetNum.isNullOrBlank()) return true + val a = address?.trim().orEmpty() + return a.contains('路') || a.contains('巷') || a.contains('号') || a.contains("大道") + } + + /** + * 用定位拿到的经纬度向高德要格式化地址。 + * 必须在后台线程调用:`getFromLocation` 是同步网络请求。 + * 失败返回 null,调用方保留定位回调里已有的市名。 + */ + private fun amapRegeocode(context: Context, lat: Double, lng: Double): Map? { return try { - if (!android.location.Geocoder.isPresent()) { - Log.w(TAG, "系统 Geocoder 不可用") - return null - } - @Suppress("DEPRECATION") - val list = android.location.Geocoder(context, java.util.Locale.CHINA).getFromLocation(lat, lng, 1) - val a = list?.firstOrNull() ?: return null - Log.i(TAG, "系统 Geocoder: 省=${a.adminArea} 市=${a.locality} 区=${a.subLocality} 地址=${a.getAddressLine(0)}") + ServiceSettings.updatePrivacyShow(context, true, true) + ServiceSettings.updatePrivacyAgree(context, true) + val search = GeocodeSearch(context) + val query = RegeocodeQuery(LatLonPoint(lat, lng), 200f, GeocodeSearch.AMAP) + val addr = search.getFromLocation(query) ?: return null + val sn = addr.streetNumber + val street = sn?.street?.trim().orEmpty() + val number = sn?.number?.trim().orEmpty() + val spoken = spokenAddress( + addr.formatAddress, addr.province, addr.city, addr.district, street, number, + ) + if (spoken.isEmpty()) return null + Log.i(TAG, "高德逆地理补全: $spoken") mapOf( - "province" to (a.adminArea ?: ""), - "city" to (a.locality ?: a.adminArea ?: ""), - "district" to (a.subLocality ?: ""), - "street" to (a.thoroughfare ?: ""), - "address" to (a.getAddressLine(0) ?: ""), + "province" to addr.province.orEmpty(), + "city" to addr.city.orEmpty().ifBlank { addr.province.orEmpty() }, + "district" to addr.district.orEmpty(), + "street" to listOf(street, number).filter { it.isNotEmpty() }.joinToString(""), + "address" to spoken, ) } catch (e: Exception) { - Log.w(TAG, "系统 Geocoder 失败: ${e.message}") + Log.w(TAG, "高德逆地理失败: ${e.message}") null } } + /** 高德格式化地址优先;为空时用省市区、街道、门牌拼一条。不含经纬度。 */ + private fun spokenAddress( + amapAddress: String?, + province: String?, + city: String?, + district: String?, + street: String?, + streetNum: String?, + ): String { + val formatted = amapAddress?.trim().orEmpty() + if (formatted.isNotEmpty()) return formatted + return listOf(province, city, district, street, streetNum) + .map { it?.trim().orEmpty() } + .filter { it.isNotEmpty() } + .distinct() + .joinToString("") + } + private const val TAG = "PlaceLocator" private val main = Handler(Looper.getMainLooper()) @@ -59,6 +99,8 @@ object PlaceLocator { main.post { var done = false var client: AMapLocationClient? = null + // 已经拿到坐标、正在补逆地理时,超时把这份粗地址交出去,而不是整次失败。 + var held: Map? = null fun finish(result: Map?, error: String?) { if (done) return done = true @@ -93,6 +135,9 @@ object PlaceLocator { } else { Log.i(TAG, "定位成功 type=${loc.locationType} lat=${loc.latitude} lng=${loc.longitude} " + "省=${loc.province} 市=${loc.city} 区=${loc.district} 地址=${loc.address}") + val spoken = spokenAddress( + loc.address, loc.province, loc.city, loc.district, loc.street, loc.streetNum, + ) val base = mapOf( "latitude" to loc.latitude, "longitude" to loc.longitude, @@ -100,21 +145,24 @@ object PlaceLocator { "city" to loc.city, "district" to loc.district, "street" to loc.street, - "address" to loc.address, + "address" to spoken, ) - if (!loc.city.isNullOrBlank() || !loc.address.isNullOrBlank()) { + if (hasRoad(loc.street, loc.streetNum, loc.address)) { finish(base, null) } else { - // 2026-09-25 华为 ALN-AL80 实测:高德只回了坐标(type=12)、省市区全空。 - // 退一步用系统 Geocoder 反查;再不行就只回坐标,由上层用坐标描述位置。 + // 市名有了但没有路名(华为上常见只回「深圳市」)。 + // 用同一对经纬度再向高德要一次格式化地址。 + held = base Thread { - val geo = systemGeocode(app, loc.latitude, loc.longitude) + val geo = amapRegeocode(app, loc.latitude, loc.longitude) main.post { finish(if (geo != null) base + geo else base, null) } }.start() } } } - main.postDelayed({ finish(null, "timeout") }, timeoutMs) + main.postDelayed({ + finish(held, if (held == null) "timeout" else null) + }, timeoutMs) c.startLocation() } catch (e: Exception) { Log.w(TAG, "定位异常: ${e.message}") diff --git a/apps/client/local_plugins/location_service/ios/location_service/Sources/location_service/PlaceLocator.swift b/apps/client/local_plugins/location_service/ios/location_service/Sources/location_service/PlaceLocator.swift index c73effa7..95d3ff41 100644 --- a/apps/client/local_plugins/location_service/ios/location_service/Sources/location_service/PlaceLocator.swift +++ b/apps/client/local_plugins/location_service/ios/location_service/Sources/location_service/PlaceLocator.swift @@ -1,14 +1,15 @@ import Foundation import CoreLocation -/// 取一次「当前位置 + 省市区」,给百炼会话的 `${location}` 用(2026-09-25,车载香薰)。 +/// 取一次「当前位置 + 详细地址」,给百炼会话的 `${location}` 用(2026-09-25,车载香薰)。 /// /// 刻意不复用 `LocationService`:那套带缓存/预热/权限请求,是给别的业务用的; -/// 这里只要城市级描述,要快、要可控。 +/// 这里要路名和门牌,要快、要可控。 /// /// - 没有定位权限直接回 `no_permission`,**不在这里弹权限框**(由 Dart 侧决定何时申请); -/// - 逆地理用系统 `CLGeocoder`(国内是高德数据),省/市/区取 administrativeArea/locality/subLocality; -/// 直辖市 locality 可能为空,退回 administrativeArea; +/// - 逆地理用系统 `CLGeocoder`(国内是高德数据)。`address` 拼成省市区 + 路名 + 门牌, +/// 与 Android 高德 `address` 同一口径,上层直接拿来念; +/// - 直辖市 locality 可能为空,退回 administrativeArea; /// - 逆地理失败时仍回经纬度(地名留空),经纬度本身对 client_info 也有用。 final class PlaceLocator: NSObject, CLLocationManagerDelegate { static let shared = PlaceLocator() @@ -60,11 +61,17 @@ final class PlaceLocator: NSObject, CLLocationManagerDelegate { DispatchQueue.main.async { if let p = marks?.first { let province = p.administrativeArea ?? "" + let city = (p.locality?.isEmpty == false) ? p.locality! : province + let district = p.subLocality ?? "" + let street = p.thoroughfare ?? "" + let number = p.subThoroughfare ?? "" base["province"] = province - base["city"] = (p.locality?.isEmpty == false) ? p.locality! : province - base["district"] = p.subLocality ?? "" - base["street"] = p.thoroughfare ?? "" - base["address"] = p.name ?? "" + base["city"] = city + base["district"] = district + base["street"] = street + base["address"] = Self.spokenAddress( + province: province, city: city, district: district, + street: street, number: number) } else if let err = err { NSLog("[PlaceLocator] 逆地理失败: \(err.localizedDescription)") } @@ -76,4 +83,17 @@ final class PlaceLocator: NSObject, CLLocationManagerDelegate { func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { finish(nil, "location_error: \(error.localizedDescription)") } + + /// 省市区 + 路名 + 门牌,去掉重复的直辖市名。不含经纬度。 + static func spokenAddress( + province: String, city: String, district: String, street: String, number: String + ) -> String { + var parts: [String] = [] + for raw in [province, city, district, street, number] { + let v = raw.trimmingCharacters(in: .whitespacesAndNewlines) + if v.isEmpty || parts.contains(v) { continue } + parts.append(v) + } + return parts.joined() + } } diff --git a/apps/client/local_plugins/location_service/lib/location_service.dart b/apps/client/local_plugins/location_service/lib/location_service.dart index 36d0461f..417d034a 100644 --- a/apps/client/local_plugins/location_service/lib/location_service.dart +++ b/apps/client/local_plugins/location_service/lib/location_service.dart @@ -55,11 +55,12 @@ class LocationService { } } - /// 取一次当前位置和省市区(2026-09-25 新增,给百炼 `${location}` 用)。 + /// 取一次当前位置和详细地址(2026-09-25 新增,给百炼 `${location}` 用)。 /// /// **不申请权限**:没权限、超时、定位失败都返回 `{'error': 原因}`,由调用方决定何时申请权限。 /// 成功返回键:latitude / longitude(double)、province / city / district / street / address - /// (String,可能为空)。 + /// (String,可能为空)。`address` 是路名加门牌的完整地址。Android 定位回调没有路名时, + /// 用经纬度再向高德逆地理要一次格式化地址;iOS 用省市区、路名、门牌拼成同一口径。 Future> currentPlace({int timeoutMs = 15000}) async { try { final r = await _methodChannel.invokeMethod( diff --git a/apps/client/pubspec.lock b/apps/client/pubspec.lock index 28c64c95..5f53fc61 100644 --- a/apps/client/pubspec.lock +++ b/apps/client/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: _fe_analyzer_shared sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "93.0.0" _flutterfire_internals: @@ -14,7 +14,7 @@ packages: description: name: _flutterfire_internals sha256: "6727cf2ced9b104abca9daa278380be2eca2b98ce33d4b46f11708e387dc6b4d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.76" agent_service: @@ -36,7 +36,7 @@ packages: description: name: analyzer sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "10.0.1" app_links: @@ -44,7 +44,7 @@ packages: description: name: app_links sha256: "3462d9defc61565fde4944858b59bec5be2b9d5b05f20aed190adb3ad08a7abc" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.0" app_links_linux: @@ -52,7 +52,7 @@ packages: description: name: app_links_linux sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.3" app_links_platform_interface: @@ -60,7 +60,7 @@ packages: description: name: app_links_platform_interface sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.2" app_links_web: @@ -68,7 +68,7 @@ packages: description: name: app_links_web sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.4" app_settings: @@ -76,7 +76,7 @@ packages: description: name: app_settings sha256: "476df1d85cec143c3d27dd1c7451629a59c0c5ccf70a0adcbfa92a0a2d928705" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.2.0" archive: @@ -84,7 +84,7 @@ packages: description: name: archive sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.0.9" args: @@ -92,7 +92,7 @@ packages: description: name: args sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.7.0" asn1lib: @@ -100,7 +100,7 @@ packages: description: name: asn1lib sha256: "9a8f69025044eb466b9b60ef3bc3ac99b4dc6c158ae9c56d25eeccf5bc56d024" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.6.5" async: @@ -108,7 +108,7 @@ packages: description: name: async sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.13.1" audio_session: @@ -116,7 +116,7 @@ packages: description: name: audio_session sha256: "2b7fff16a552486d078bfc09a8cde19f426dc6d6329262b684182597bec5b1ac" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.1.25" audio_waveforms: @@ -124,7 +124,7 @@ packages: description: name: audio_waveforms sha256: "658fef41bbab299184b65ba2fd749e8ec658c1f7d54a21f7cf97fa96b173b4ce" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.0" azure_speech: @@ -139,7 +139,7 @@ packages: description: name: barcode sha256: "7b6729c37e3b7f34233e2318d866e8c48ddb46c1f7ad01ff7bb2a8de1da2b9f4" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.9" bes_ota_manager: @@ -154,7 +154,7 @@ packages: description: name: bidi sha256: "77f475165e94b261745cf1032c751e2032b8ed92ccb2bf5716036db79320637d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.13" ble_service: @@ -176,7 +176,7 @@ packages: description: name: bluez sha256: "61a7204381925896a374301498f2f5399e59827c6498ae1e924aaa598751b545" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.8.3" boolean_selector: @@ -184,7 +184,7 @@ packages: description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.2" build: @@ -192,7 +192,7 @@ packages: description: name: build sha256: "45d14a0fb23e018d8287c32fc98d726ce466b231928ed9b9200f29bd3ccd39ae" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.0.7" build_config: @@ -200,7 +200,7 @@ packages: description: name: build_config sha256: d466ed2dc9c6cd1d169948879b84ee061eb5e22c64a7c6089879c6296d272a8d - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.3" build_daemon: @@ -208,7 +208,7 @@ packages: description: name: build_daemon sha256: e1d40ef3f7934986d5da2271b1ba07794921ce263e44d622fb6c406d76589e33 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.1.6" build_runner: @@ -216,23 +216,23 @@ packages: description: name: build_runner sha256: "5367e521935b102bdf1e735d2aab461e36b2edca6517662d088dd04cc39f8d16" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.15.1" built_collection: dependency: transitive description: name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.flutter-io.cn" + sha256: "44922ecebd8e3d0cef743dadd3b54df4e87e2a4c52a29465a1872aa9e192caa8" + url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.1.2" built_value: dependency: transitive description: name: built_value sha256: f87ea98192116f7093cb214551ce1929caae0681fdba282b3d8b4462adee7bb7 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "8.13.0" characters: @@ -240,7 +240,7 @@ packages: description: name: characters sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.1" chat_api: @@ -262,7 +262,7 @@ packages: description: name: checked_yaml sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.4" classic_bluetooth: @@ -277,7 +277,7 @@ packages: description: name: clock sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.2" code_assets: @@ -285,7 +285,7 @@ packages: description: name: code_assets sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.1" collection: @@ -293,7 +293,7 @@ packages: description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.19.1" common_utils: @@ -301,7 +301,7 @@ packages: description: name: common_utils sha256: c26884339b13ff99b0739e56f4b02090c84054ed9dd3a045435cd24e7b99c2c1 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.0" connectivity_plus: @@ -309,7 +309,7 @@ packages: description: name: connectivity_plus sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.1.5" connectivity_plus_platform_interface: @@ -317,7 +317,7 @@ packages: description: name: connectivity_plus_platform_interface sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.0" convert: @@ -325,7 +325,7 @@ packages: description: name: convert sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.2" cross_file: @@ -333,7 +333,7 @@ packages: description: name: cross_file sha256: f141ea4f277af142a0356955707f6556f37b03947d39d55585981a06ca437bd6 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.3.5+5" crypto: @@ -341,7 +341,7 @@ packages: description: name: crypto sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.7" csslib: @@ -349,7 +349,7 @@ packages: description: name: csslib sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.2" cupertino_icons: @@ -357,7 +357,7 @@ packages: description: name: cupertino_icons sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.9" dart_style: @@ -365,7 +365,7 @@ packages: description: name: dart_style sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.7" dbus: @@ -373,7 +373,7 @@ packages: description: name: dbus sha256: a48d5da28e89bd02196e80d81ed8d7954923d00a0f4a68cc20b575038f023383 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.15" decimal: @@ -381,7 +381,7 @@ packages: description: name: decimal sha256: "2c3c8b74f2948066d3f42585477aec9cfc48fefd7a723a4d4274a6c71a5c0df7" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.6" device_info_plus: @@ -389,7 +389,7 @@ packages: description: name: device_info_plus sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "11.5.0" device_info_plus_platform_interface: @@ -397,7 +397,7 @@ packages: description: name: device_info_plus_platform_interface sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.3" device_jieli: @@ -426,7 +426,7 @@ packages: description: name: diff_match_patch sha256: "2efc9e6e8f449d0abe15be240e2c2a3bcd977c8d126cfd70598aee60af35c0a4" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.4.1" dio: @@ -434,7 +434,7 @@ packages: description: name: dio sha256: "852ec3b48cc431ac04fff978413c541502b67ffc3e26921e74e3d994694192c1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.11.1" dio_web_adapter: @@ -442,7 +442,7 @@ packages: description: name: dio_web_adapter sha256: "3a1b2cd7be71086f38504956e3ebcd2837288d231ff454bafa78021244102bfc" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.2" echomeet_device_sdk: @@ -457,7 +457,7 @@ packages: description: name: encrypt sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.0.3" fake_async: @@ -465,7 +465,7 @@ packages: description: name: fake_async sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.3" ffi: @@ -473,7 +473,7 @@ packages: description: name: ffi sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" file: @@ -481,7 +481,7 @@ packages: description: name: file sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.1" file_picker: @@ -489,7 +489,7 @@ packages: description: name: file_picker sha256: "57d9a1dd5063f85fa3107fb42d1faffda52fdc948cefd5fe5ea85267a5fc7343" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "10.3.10" file_selector_linux: @@ -497,7 +497,7 @@ packages: description: name: file_selector_linux sha256: da76400e7872ce7637ffdce12749ec24169c25f6195c28372208e65a24bcd2ab - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.9.4+1" file_selector_macos: @@ -505,7 +505,7 @@ packages: description: name: file_selector_macos sha256: d57c62362766b5e7ae739448650b66c6aab7a68ba7ecc65e04018652645ae0f4 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.9.5+1" file_selector_platform_interface: @@ -513,7 +513,7 @@ packages: description: name: file_selector_platform_interface sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.7.0" file_selector_windows: @@ -521,7 +521,7 @@ packages: description: name: file_selector_windows sha256: fbefc5fb92c6d3cbe8d284a2cd971b593bb07d2cd6da8557b81a862250b4acec - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.9.3+6" firebase_auth: @@ -529,7 +529,7 @@ packages: description: name: firebase_auth sha256: "366b8edc0bbce7880a62dbf0f566fadd809ee69e6e345b7e7cae2080443acbfd" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.5.7" firebase_auth_platform_interface: @@ -537,7 +537,7 @@ packages: description: name: firebase_auth_platform_interface sha256: "314b7bd0bf8545cbd3e779bad294a811d09de5d25d5c88a00e6e48a6bded6368" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "9.0.6" firebase_auth_web: @@ -545,7 +545,7 @@ packages: description: name: firebase_auth_web sha256: "209f60c3e4bc44ce23b167b2361883bc15a012b027ffcb773ccd81fcaebc718b" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.2.6" firebase_core: @@ -553,7 +553,7 @@ packages: description: name: firebase_core sha256: "9478ca6700c02d315c6aba37e206e612317f98bb4f335bb1cbd6e0ce67dcf764" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.13.0" firebase_core_platform_interface: @@ -561,7 +561,7 @@ packages: description: name: firebase_core_platform_interface sha256: "9bfbc85faca09346471ac56fbcee00757ebcfd85887c6f602764eff0001902d8" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "8.1.1" firebase_core_web: @@ -569,7 +569,7 @@ packages: description: name: firebase_core_web sha256: "8ed84f5001dbd7a0390e2818d46bc858ceebb970edededdfc8d6552c76fc2f6e" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.12.0" fixnum: @@ -577,7 +577,7 @@ packages: description: name: fixnum sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" fleather: @@ -585,7 +585,7 @@ packages: description: name: fleather sha256: baa1cbba5de755adf8114eac3b8adf352f06a18e55d1d58636e9c10a0f86aed5 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.26.0" floating_ui_plugin: @@ -605,7 +605,7 @@ packages: description: name: flutter_blue_plus sha256: "69a8c87c11fc792e8cf0f997d275484fbdb5143ac9f0ac4d424429700cb4e0ed" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.36.8" flutter_blue_plus_android: @@ -613,7 +613,7 @@ packages: description: name: flutter_blue_plus_android sha256: "6f7fe7e69659c30af164a53730707edc16aa4d959e4c208f547b893d940f853d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.4" flutter_blue_plus_darwin: @@ -621,7 +621,7 @@ packages: description: name: flutter_blue_plus_darwin sha256: "682982862c1d964f4d54a3fb5fccc9e59a066422b93b7e22079aeecd9c0d38f8" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.3" flutter_blue_plus_linux: @@ -629,7 +629,7 @@ packages: description: name: flutter_blue_plus_linux sha256: "56b0c45edd0a2eec8f85bd97a26ac3cd09447e10d0094fed55587bf0592e3347" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.3" flutter_blue_plus_platform_interface: @@ -637,7 +637,7 @@ packages: description: name: flutter_blue_plus_platform_interface sha256: "84fbd180c50a40c92482f273a92069960805ce324e3673ad29c41d2faaa7c5c2" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.0" flutter_blue_plus_web: @@ -645,7 +645,7 @@ packages: description: name: flutter_blue_plus_web sha256: a1aceee753d171d24c0e0cdadb37895b5e9124862721f25f60bb758e20b72c99 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.2" flutter_dotenv: @@ -653,7 +653,7 @@ packages: description: name: flutter_dotenv sha256: b7c7be5cd9f6ef7a78429cabd2774d3c4af50e79cb2b7593e3d5d763ef95c61b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.2.1" flutter_easyloading: @@ -661,7 +661,7 @@ packages: description: name: flutter_easyloading sha256: ba21a3c883544e582f9cc455a4a0907556714e1e9cf0eababfcb600da191d17c - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.5" flutter_image_compress: @@ -669,7 +669,7 @@ packages: description: name: flutter_image_compress sha256: "98a48c05a7add6869c6838270e862124a4d571f9dd5d0cf209ed03a71b20ea84" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.5.1" flutter_image_compress_common: @@ -677,7 +677,7 @@ packages: description: name: flutter_image_compress_common sha256: "76869e4d5f3d65f3431e7edff0b2d8ad1eea68b49c6a37772fdb1ae6016da9ed" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" flutter_image_compress_macos: @@ -685,7 +685,7 @@ packages: description: name: flutter_image_compress_macos sha256: "0d2a842d2e544828fb32bda16dcfccb3149107df568898a4936d78232e486847" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" flutter_image_compress_ohos: @@ -693,7 +693,7 @@ packages: description: name: flutter_image_compress_ohos sha256: "1491bb7bcfdf59e3b127c263c116cfbf8ed3afade6e7fa691d9622e838ed7e48" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.0.3+1" flutter_image_compress_platform_interface: @@ -701,7 +701,7 @@ packages: description: name: flutter_image_compress_platform_interface sha256: bbefb7967bda565004fdabdb3300dcbfb40c7ef8402675d23206e5bddc358178 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" flutter_image_compress_web: @@ -709,7 +709,7 @@ packages: description: name: flutter_image_compress_web sha256: "91cc58e091a1e09c7683d216d579e2b964119a8527fc43b64dfdb00f1acc94ec" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.1.5+1" flutter_intro: @@ -717,7 +717,7 @@ packages: description: name: flutter_intro sha256: f8cee0d0d03d60f076c342073228a8a1f43df5e99eade30bcf895af6e045008c - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.4.0" flutter_lints: @@ -725,7 +725,7 @@ packages: description: name: flutter_lints sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.3" flutter_local_notifications: @@ -733,7 +733,7 @@ packages: description: name: flutter_local_notifications sha256: ef41ae901e7529e52934feba19ed82827b11baa67336829564aeab3129460610 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "18.0.1" flutter_local_notifications_linux: @@ -741,7 +741,7 @@ packages: description: name: flutter_local_notifications_linux sha256: "8f685642876742c941b29c32030f6f4f6dacd0e4eaecb3efbb187d6a3812ca01" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.0.0" flutter_local_notifications_platform_interface: @@ -749,7 +749,7 @@ packages: description: name: flutter_local_notifications_platform_interface sha256: "6c5b83c86bf819cdb177a9247a3722067dd8cc6313827ce7c77a4b238a26fd52" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "8.0.0" flutter_localizations: @@ -762,7 +762,7 @@ packages: description: name: flutter_markdown sha256: "08fb8315236099ff8e90cb87bb2b935e0a724a3af1623000a9cec930468e0f27" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.7+1" flutter_plugin_android_lifecycle: @@ -770,7 +770,7 @@ packages: description: name: flutter_plugin_android_lifecycle sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.35" flutter_screenutil: @@ -778,7 +778,7 @@ packages: description: name: flutter_screenutil sha256: "8239210dd68bee6b0577aa4a090890342d04a136ce1c81f98ee513fc0ce891de" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.9.3" flutter_spinkit: @@ -786,7 +786,7 @@ packages: description: name: flutter_spinkit sha256: "77850df57c00dc218bfe96071d576a8babec24cf58b2ed121c83cca4a2fdce7f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.2.2" flutter_staggered_grid_view: @@ -794,7 +794,7 @@ packages: description: name: flutter_staggered_grid_view sha256: "19e7abb550c96fbfeb546b23f3ff356ee7c59a019a651f8f102a4ba9b7349395" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.0" flutter_test: @@ -807,7 +807,7 @@ packages: description: name: flutter_timezone sha256: ea53c61c9152f271a5e30624a624184804947b6a733ff2b64186bb2579446892 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.1" flutter_web_plugins: @@ -820,7 +820,7 @@ packages: description: name: fluwx sha256: ae870e8ac0aaf13bd4fb0833e077a6294055f528c24890efa578817e93fbe674 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.7.7" font_awesome_flutter: @@ -828,7 +828,7 @@ packages: description: name: font_awesome_flutter sha256: b9011df3a1fa02993630b8fb83526368cf2206a711259830325bab2f1d2a4eb0 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "10.12.0" get: @@ -836,7 +836,7 @@ packages: description: name: get sha256: "5ed34a7925b85336e15d472cc4cfe7d9ebf4ab8e8b9f688585bf6b50f4c3d79a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.7.3" get_storage: @@ -844,7 +844,7 @@ packages: description: name: get_storage sha256: "39db1fffe779d0c22b3a744376e86febe4ade43bf65e06eab5af707dc84185a2" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.1" glob: @@ -852,7 +852,7 @@ packages: description: name: glob sha256: "218aeb56050c714f62a3182775320dfa04602b55074873e24e31bbd39bda96fb" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" google_identity_services_web: @@ -860,7 +860,7 @@ packages: description: name: google_identity_services_web sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.3.3+1" google_sign_in: @@ -868,7 +868,7 @@ packages: description: name: google_sign_in sha256: d0a2c3bcb06e607bb11e4daca48bd4b6120f0bbc4015ccebbe757d24ea60ed2a - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.3.0" google_sign_in_android: @@ -876,7 +876,7 @@ packages: description: name: google_sign_in_android sha256: d5e23c56a4b84b6427552f1cf3f98f716db3b1d1a647f16b96dbb5b93afa2805 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.2.1" google_sign_in_ios: @@ -884,7 +884,7 @@ packages: description: name: google_sign_in_ios sha256: "102005f498ce18442e7158f6791033bbc15ad2dcc0afa4cf4752e2722a516c96" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.9.0" google_sign_in_platform_interface: @@ -892,7 +892,7 @@ packages: description: name: google_sign_in_platform_interface sha256: "5f6f79cf139c197261adb6ac024577518ae48fdff8e53205c5373b5f6430a8aa" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.5.0" google_sign_in_web: @@ -900,7 +900,7 @@ packages: description: name: google_sign_in_web sha256: "460547beb4962b7623ac0fb8122d6b8268c951cf0b646dd150d60498430e4ded" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.12.4+4" graphs: @@ -908,7 +908,7 @@ packages: description: name: graphs sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.2" gtk: @@ -916,7 +916,7 @@ packages: description: name: gtk sha256: "4ff85b2a16724029dd9e5bbb5a94b6918f9973f74ba571c949d2002801879cf5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" haptic_feedback: @@ -924,7 +924,7 @@ packages: description: name: haptic_feedback sha256: "3422bf2a55c541c9e3f029197b9a94d262d2dda19191a022eb310e5dd922e9e0" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.5.1+2" hooks: @@ -932,7 +932,7 @@ packages: description: name: hooks sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.2" html: @@ -940,7 +940,7 @@ packages: description: name: html sha256: "43b67b8f43321ab066817dfac5619596c98bb1b61624e77203bb4351785f9699" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.15.7" htmltopdfwidgets: @@ -948,7 +948,7 @@ packages: description: name: htmltopdfwidgets sha256: e2c42dc784fa884eee0f29bbced6555a1a4cb0a3171c7d334cb5ca64a3854d77 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.3" http: @@ -956,7 +956,7 @@ packages: description: name: http sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.6.0" http_multi_server: @@ -964,7 +964,7 @@ packages: description: name: http_multi_server sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.2" http_parser: @@ -972,7 +972,7 @@ packages: description: name: http_parser sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.1.2" image: @@ -980,7 +980,7 @@ packages: description: name: image sha256: a1e7f4951e538a568e14b856702afc9ae1d2f4b202daced8d22c1b9cd211ce89 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.10.1" image_gallery_saver_plus: @@ -988,7 +988,7 @@ packages: description: name: image_gallery_saver_plus sha256: "199b9e24f8d85e98f11e3d35571ab68ae50626ad40e2bb85c84383f69a6950ad" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.0.1" image_picker: @@ -996,7 +996,7 @@ packages: description: name: image_picker sha256: d8402284df184bc05f4a2210c6c23983b0720f4cd87cbd05c5390a78af602667 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.3" image_picker_android: @@ -1004,7 +1004,7 @@ packages: description: name: image_picker_android sha256: d5b3e1774af29c9ab00103afb0d4614070f924d2e0057ac867ec98800114793f - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.8.13+17" image_picker_for_web: @@ -1012,7 +1012,7 @@ packages: description: name: image_picker_for_web sha256: "66257a3191ab360d23a55c8241c91a6e329d31e94efa7be9cf7a212e65850214" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.1" image_picker_ios: @@ -1020,7 +1020,7 @@ packages: description: name: image_picker_ios sha256: "852a7762962f3bb3ad54ef1d342669e41557f466033a676baa8876b367cf4050" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.8.13+8" image_picker_linux: @@ -1028,7 +1028,7 @@ packages: description: name: image_picker_linux sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.2" image_picker_macos: @@ -1036,7 +1036,7 @@ packages: description: name: image_picker_macos sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.2+1" image_picker_platform_interface: @@ -1044,7 +1044,7 @@ packages: description: name: image_picker_platform_interface sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.11.1" image_picker_windows: @@ -1052,7 +1052,7 @@ packages: description: name: image_picker_windows sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.2" in_app_purchase: @@ -1060,7 +1060,7 @@ packages: description: name: in_app_purchase sha256: d8317ec2ed6fe33199957d67ee67e08a4fd4871ef93b91d7611d8ee560892afc - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.3.1" in_app_purchase_android: @@ -1068,7 +1068,7 @@ packages: description: name: in_app_purchase_android sha256: b8af1b6f0e57076b5da5ede71e3af2d727017c0416ad2a41017c6ed2b102381d - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.5.0" in_app_purchase_platform_interface: @@ -1076,7 +1076,7 @@ packages: description: name: in_app_purchase_platform_interface sha256: "0b0076cac8ce4fa7048f01e76af8b123aeb6a7c4e0dea2a5206d6664454f3e36" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.1" in_app_purchase_storekit: @@ -1084,7 +1084,7 @@ packages: description: name: in_app_purchase_storekit sha256: bb87b9003fd77da6f23f82c09b1891b28f7bf2520cfa1881a266a9601e675bf0 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.4.13" intl: @@ -1092,7 +1092,7 @@ packages: description: name: intl sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.20.2" io: @@ -1100,7 +1100,7 @@ packages: description: name: io sha256: "2635216ca6a737e60de577ffa1a48a0bec76ca8a62917cfc1bb88c14c570646f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" jni: @@ -1108,7 +1108,7 @@ packages: description: name: jni sha256: f038e58b4dc2c9037f50e233175086337e0b305e356d28211bf55f21c504cbd3 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.3" jni_flutter: @@ -1116,7 +1116,7 @@ packages: description: name: jni_flutter sha256: b2310cdd4c18c65c081ab141a41efa94aa26c65431803703ece51996f174f351 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.3" jni_util: @@ -1124,7 +1124,7 @@ packages: description: name: jni_util sha256: "1ba86da04a5f2bf18fde2edb235587e70c5b0fc5bd4ba955f46b00942c3fc35f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.0" js: @@ -1132,7 +1132,7 @@ packages: description: name: js sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.2" json_annotation: @@ -1140,7 +1140,7 @@ packages: description: name: json_annotation sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.12.0" json_serializable: @@ -1148,7 +1148,7 @@ packages: description: name: json_serializable sha256: e45aefa0324f08c683caafbb94b72837aa6193c61822799c916e45f4a263113d - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.14.1" just_audio: @@ -1156,7 +1156,7 @@ packages: description: name: just_audio sha256: f978d5b4ccea08f267dae0232ec5405c1b05d3f3cd63f82097ea46c015d5c09e - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.9.46" just_audio_platform_interface: @@ -1164,7 +1164,7 @@ packages: description: name: just_audio_platform_interface sha256: "2532c8d6702528824445921c5ff10548b518b13f808c2e34c2fd54793b999a6a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.6.0" just_audio_web: @@ -1172,7 +1172,7 @@ packages: description: name: just_audio_web sha256: "6ba8a2a7e87d57d32f0f7b42856ade3d6a9fbe0f1a11fabae0a4f00bb73f0663" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.4.16" leak_tracker: @@ -1180,7 +1180,7 @@ packages: description: name: leak_tracker sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "11.0.2" leak_tracker_flutter_testing: @@ -1188,7 +1188,7 @@ packages: description: name: leak_tracker_flutter_testing sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.10" leak_tracker_testing: @@ -1196,7 +1196,7 @@ packages: description: name: leak_tracker_testing sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.2" lints: @@ -1204,7 +1204,7 @@ packages: description: name: lints sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.1" location_service: @@ -1219,7 +1219,7 @@ packages: description: name: logger sha256: "2a0dc097e7b01d942475bdd552356db2d0f768b05540bd4b2b53f1840f2239a7" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.8.0" logging: @@ -1227,7 +1227,7 @@ packages: description: name: logging sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.0" lottie: @@ -1235,7 +1235,7 @@ packages: description: name: lottie sha256: "0bd56a8c5c2944386313588408bf93a379e6fca69f736d585c538e35e72ccd5c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.6.1" lunar: @@ -1243,7 +1243,7 @@ packages: description: name: lunar sha256: "1d22682bb187b0090e6907b98227e3188bb64816efe1124e26385ccff4468ef1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.7.8" markdown: @@ -1251,7 +1251,7 @@ packages: description: name: markdown sha256: ee85086ad7698b42522c6ad42fe195f1b9898e4d974a1af4576c1a3a176cada9 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.3.1" matcher: @@ -1259,7 +1259,7 @@ packages: description: name: matcher sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.12.19" material_color_utilities: @@ -1267,7 +1267,7 @@ packages: description: name: material_color_utilities sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.13.0" meta: @@ -1275,7 +1275,7 @@ packages: description: name: meta sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.17.0" mime: @@ -1283,7 +1283,7 @@ packages: description: name: mime sha256: bd47de35f07e27267e69c8c8b22edf9473bfee170a60d60fcc93730c5144b7f6 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.0" music_service: @@ -1298,7 +1298,7 @@ packages: description: name: nm sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.5.0" objective_c: @@ -1306,7 +1306,7 @@ packages: description: name: objective_c sha256: b7fb95a6d9a4f009edd63dc5ac69f07420b23a16161c6dd8660290b59c602e8e - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "9.5.0" package_config: @@ -1314,7 +1314,7 @@ packages: description: name: package_config sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" package_info_plus: @@ -1322,7 +1322,7 @@ packages: description: name: package_info_plus sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "8.3.1" package_info_plus_platform_interface: @@ -1330,7 +1330,7 @@ packages: description: name: package_info_plus_platform_interface sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.1" parchment: @@ -1338,7 +1338,7 @@ packages: description: name: parchment sha256: "1af4dd4a39034452aecabac2baf4cb2a5248fc4467b6ba48cd5e11e533e00875" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.27.0" parchment_delta: @@ -1346,7 +1346,7 @@ packages: description: name: parchment_delta sha256: "8b360d0138eb591c7fe193670e2d05466e482792af9c5b4d3f568c5c61530f08" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.0" path: @@ -1354,7 +1354,7 @@ packages: description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.9.1" path_parsing: @@ -1362,7 +1362,7 @@ packages: description: name: path_parsing sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" path_provider: @@ -1370,7 +1370,7 @@ packages: description: name: path_provider sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.6" path_provider_android: @@ -1378,7 +1378,7 @@ packages: description: name: path_provider_android sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.1" path_provider_foundation: @@ -1386,7 +1386,7 @@ packages: description: name: path_provider_foundation sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.6.0" path_provider_linux: @@ -1394,7 +1394,7 @@ packages: description: name: path_provider_linux sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.2" path_provider_platform_interface: @@ -1402,7 +1402,7 @@ packages: description: name: path_provider_platform_interface sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.3" path_provider_windows: @@ -1410,7 +1410,7 @@ packages: description: name: path_provider_windows sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.0" pdf: @@ -1418,7 +1418,7 @@ packages: description: name: pdf sha256: e47a275b267873d5944ad5f5ff0dcc7ac2e36c02b3046a0ffac9b72fd362c44b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.12.0" permission_handler: @@ -1426,7 +1426,7 @@ packages: description: name: permission_handler sha256: fe54465bcc62a4564c6e4db337bbaded6c0c0fa6e10487414436d163114784f6 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "12.0.3" permission_handler_android: @@ -1434,23 +1434,23 @@ packages: description: name: permission_handler_android sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "13.0.1" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: f49cb15a064ea9d974fc7fbb302099353b7b170d07284e86e264561579e5bcf8 - url: "https://pub.flutter-io.cn" + sha256: "066bbbc60031b63b916a3871e7bb4b6a8c5e7cec8146dafec53d1da304eff0a5" + url: "https://pub.dev" source: hosted - version: "9.6.1" + version: "9.6.2" permission_handler_html: dependency: transitive description: name: permission_handler_html sha256: "6ea98b3f17f60d3b527f2647ed2ab4dc0f6bfe25b22cb1c363f5d8f62252f6ac" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.1.4+1" permission_handler_platform_interface: @@ -1458,7 +1458,7 @@ packages: description: name: permission_handler_platform_interface sha256: ed86a61c190258fdd65de395ea0632822e3415c1faec38eae0c31b479c28a531 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.4.1" permission_handler_windows: @@ -1466,7 +1466,7 @@ packages: description: name: permission_handler_windows sha256: caeae01858a0a7d2df67a445ac98e1ad95e55a0e77c73044f4e9b1c8c2289cbd - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.2.2" petitparser: @@ -1474,7 +1474,7 @@ packages: description: name: petitparser sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.2" photo_view: @@ -1482,7 +1482,7 @@ packages: description: name: photo_view sha256: "1fc3d970a91295fbd1364296575f854c9863f225505c28c46e0a03e48960c75e" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.15.0" platform: @@ -1490,7 +1490,7 @@ packages: description: name: platform sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.6" plugin_platform_interface: @@ -1498,7 +1498,7 @@ packages: description: name: plugin_platform_interface sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.8" pointycastle: @@ -1506,7 +1506,7 @@ packages: description: name: pointycastle sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.9.1" pool: @@ -1514,7 +1514,7 @@ packages: description: name: pool sha256: "4177f68c237ea2128d1bee66ac17b2ce05ba3dbaafcbdd54c5d40a39d0b6b11c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.5.3" posix: @@ -1522,7 +1522,7 @@ packages: description: name: posix sha256: bc1bad54ad2b735816e31f8d4600cfde6c7839975085ddfbca48b6c9f7c4044e - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.5.2" pub_semver: @@ -1530,7 +1530,7 @@ packages: description: name: pub_semver sha256: "261236774e8b1d69cfc6b9eabbc96c40f25e7a2d6b171f3385d4f65d5734fb24" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.1" pubspec_parse: @@ -1538,7 +1538,7 @@ packages: description: name: pubspec_parse sha256: c38b81cbf34450b67e0265d73433569d12e34782e30ed769c9cc99c9d5f2e796 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.6.0" qr: @@ -1546,7 +1546,7 @@ packages: description: name: qr sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.2" quiver: @@ -1554,7 +1554,7 @@ packages: description: name: quiver sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.2" rational: @@ -1562,7 +1562,7 @@ packages: description: name: rational sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.3" record: @@ -1570,7 +1570,7 @@ packages: description: name: record sha256: "10911465138fafacef459a780564e883e01bd48eabf87ab20543684884492870" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.2.1" record_android: @@ -1578,7 +1578,7 @@ packages: description: name: record_android sha256: eb1732e42d0d2a1895b8db86e4fc917287e6d8491b6ed59918aea8bed6c69de4 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.5.2" record_ios: @@ -1586,7 +1586,7 @@ packages: description: name: record_ios sha256: c051fb48edd7a0e265daafb9108730dc827c27b551728a3fdfb3ef69efd89c73 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.1" record_linux: @@ -1594,7 +1594,7 @@ packages: description: name: record_linux sha256: "31181787bf7eccb0e298835836b69b3cd0a903863b75d70e937de3dec71cd8f3" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.1" record_macos: @@ -1602,7 +1602,7 @@ packages: description: name: record_macos sha256: cfe1b61435e27db418bf513dc36820d10c9f7eb1843786c2c9a52e07e2f4f627 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.2" record_platform_interface: @@ -1610,7 +1610,7 @@ packages: description: name: record_platform_interface sha256: "8e56cbe06c6984137fb86132ff03459f29938d927496d9b2d0962e2d6345d488" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.6.0" record_use: @@ -1618,7 +1618,7 @@ packages: description: name: record_use sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.6.0" record_web: @@ -1626,7 +1626,7 @@ packages: description: name: record_web sha256: "7e9846981c1f2d111d86f0ae3309071f5bba8b624d1c977316706f08fc31d16d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.0" record_windows: @@ -1634,7 +1634,7 @@ packages: description: name: record_windows sha256: "223258060a1d25c62bae18282c16783f28581ec19401d17e56b5205b9f039d78" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.0.7" recorder_holder: @@ -1649,7 +1649,7 @@ packages: description: name: rxdart sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.28.0" share_plus: @@ -1657,7 +1657,7 @@ packages: description: name: share_plus sha256: "223873d106614442ea6f20db5a038685cc5b32a2fba81cdecaefbbae0523f7fa" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "12.0.2" share_plus_platform_interface: @@ -1665,7 +1665,7 @@ packages: description: name: share_plus_platform_interface sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.1.0" shelf: @@ -1673,7 +1673,7 @@ packages: description: name: shelf sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.2" shelf_web_socket: @@ -1681,7 +1681,7 @@ packages: description: name: shelf_web_socket sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.0" sign_in_with_apple: @@ -1689,7 +1689,7 @@ packages: description: name: sign_in_with_apple sha256: "8bd875c8e8748272749eb6d25b896f768e7e9d60988446d543fe85a37a2392b8" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "7.0.1" sign_in_with_apple_platform_interface: @@ -1697,7 +1697,7 @@ packages: description: name: sign_in_with_apple_platform_interface sha256: "981bca52cf3bb9c3ad7ef44aace2d543e5c468bb713fd8dda4275ff76dfa6659" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.0.0" sign_in_with_apple_web: @@ -1705,7 +1705,7 @@ packages: description: name: sign_in_with_apple_web sha256: f316400827f52cafcf50d00e1a2e8a0abc534ca1264e856a81c5f06bd5b10fed - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.0.0" sky_engine: @@ -1718,7 +1718,7 @@ packages: description: name: source_gen sha256: a603f1fb984a7391ae5978d1b92bfaaa08b350dca5c825256f925818f7943bf5 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.2.4" source_helper: @@ -1726,7 +1726,7 @@ packages: description: name: source_helper sha256: "5e6f216fdf6376c9f3852381ae037499797a3385377d388b011dac98d303c67c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.13" source_span: @@ -1734,7 +1734,7 @@ packages: description: name: source_span sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.10.2" speech: @@ -1749,7 +1749,7 @@ packages: description: name: sqflite sha256: "564cfed0746fe53140c23b70b308e045c3b31f17778f2f326ccb7d804ea0250a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.2+1" sqflite_android: @@ -1757,7 +1757,7 @@ packages: description: name: sqflite_android sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.2+3" sqflite_common: @@ -1765,7 +1765,7 @@ packages: description: name: sqflite_common sha256: "1581ffbf7a0e333b380d6a30737d78516b826cb35beb7fb0bf8a3ea0c678b465" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.5.8" sqflite_darwin: @@ -1773,7 +1773,7 @@ packages: description: name: sqflite_darwin sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.2" sqflite_platform_interface: @@ -1781,7 +1781,7 @@ packages: description: name: sqflite_platform_interface sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.0" stack_trace: @@ -1789,7 +1789,7 @@ packages: description: name: stack_trace sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.12.1" stream_channel: @@ -1797,7 +1797,7 @@ packages: description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.4" stream_transform: @@ -1805,7 +1805,7 @@ packages: description: name: stream_transform sha256: a00e5f18bffc764f923e7dec1038527f7fe7a1791361a7117f0358193f13d53a - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.2" string_scanner: @@ -1813,7 +1813,7 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.1" synchronized: @@ -1821,7 +1821,7 @@ packages: description: name: synchronized sha256: "63896c27e81b28f8cb4e69ead0d3e8f03f1d1e5fc531a3e579cabed6a2c7c9e5" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.4.0+1" term_glyph: @@ -1829,7 +1829,7 @@ packages: description: name: term_glyph sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.2" test_api: @@ -1837,7 +1837,7 @@ packages: description: name: test_api sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.7.10" timezone: @@ -1845,7 +1845,7 @@ packages: description: name: timezone sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.9.4" tobias: @@ -1853,7 +1853,7 @@ packages: description: name: tobias sha256: "1cfd203bf57f8d4daa3e734d24ef97568eb5265af62d1498c32da4e69905c70f" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.3.4" typed_data: @@ -1861,7 +1861,7 @@ packages: description: name: typed_data sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.4.0" url_launcher: @@ -1869,7 +1869,7 @@ packages: description: name: url_launcher sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.3.2" url_launcher_android: @@ -1877,7 +1877,7 @@ packages: description: name: url_launcher_android sha256: "17bc677f0b301615530dd1d67e0a9828cafa2d0b6b6eae4cd3679b7eac4a273c" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.3.30" url_launcher_ios: @@ -1885,7 +1885,7 @@ packages: description: name: url_launcher_ios sha256: "8faa1aab294f1ab4040b43660c887b0418d5fa4f0cffef76a484e6aa1092eb4a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.4.2" url_launcher_linux: @@ -1893,7 +1893,7 @@ packages: description: name: url_launcher_linux sha256: "10f86fef4c2c43563fa6c211ff9cf757adf4d3ab762c56bd430664a947d70cd0" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.3" url_launcher_macos: @@ -1901,7 +1901,7 @@ packages: description: name: url_launcher_macos sha256: "5e835a3b869c2d70325349c81c5a45c28e20791265b67b2669da6b08c5cd5201" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.2.6" url_launcher_platform_interface: @@ -1909,7 +1909,7 @@ packages: description: name: url_launcher_platform_interface sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.3.2" url_launcher_web: @@ -1917,7 +1917,7 @@ packages: description: name: url_launcher_web sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.3" url_launcher_windows: @@ -1925,7 +1925,7 @@ packages: description: name: url_launcher_windows sha256: "6c5ad3f22cd4c38e089b81963b3cd7bb83b111b2df5dce008bb066162f42e429" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.6" user_portrait: @@ -1940,7 +1940,7 @@ packages: description: name: uuid sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.6.0" vector_math: @@ -1948,7 +1948,7 @@ packages: description: name: vector_math sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.2.0" vm_service: @@ -1956,7 +1956,7 @@ packages: description: name: vm_service sha256: "5f37239c4851efcef929cea7824e76df7f2f0970aef85d66bbc430afa40e72f0" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "15.3.0" wakelock_plus: @@ -1964,7 +1964,7 @@ packages: description: name: wakelock_plus sha256: "61713aa82b7f85c21c9f4cd0a148abd75f38a74ec645fcb1e446f882c82fd09b" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.3.3" wakelock_plus_platform_interface: @@ -1972,7 +1972,7 @@ packages: description: name: wakelock_plus_platform_interface sha256: "0618d1799f0b28bcf98255b4ee8313e6fc4d38589dc4ee5fe5840d57d1aff6da" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.6.0" watcher: @@ -1980,7 +1980,7 @@ packages: description: name: watcher sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.2.1" web: @@ -1988,7 +1988,7 @@ packages: description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.1" web_socket_channel: @@ -1996,7 +1996,7 @@ packages: description: name: web_socket_channel sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.4.0" webview_flutter: @@ -2004,7 +2004,7 @@ packages: description: name: webview_flutter sha256: d53e1ccf5516f25017e3c9d44c39034db352d20fa34fe200674270242c2c5111 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.14.1" webview_flutter_android: @@ -2012,7 +2012,7 @@ packages: description: name: webview_flutter_android sha256: ad5182eff9a550925330cb9f0cb038eddfdd5712aba8b77aa0f0400e50f6e688 - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "4.12.0" webview_flutter_platform_interface: @@ -2020,7 +2020,7 @@ packages: description: name: webview_flutter_platform_interface sha256: "1221c1b12f5278791042f2ec2841743784cf25c5a644e23d6680e5d718824f04" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.15.1" webview_flutter_wkwebview: @@ -2028,7 +2028,7 @@ packages: description: name: webview_flutter_wkwebview sha256: "82648217f537573e1ca9ae9952d3eacedca6ab5aee69dc84445fc763766dcea2" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.25.1" wifi_iot: @@ -2036,7 +2036,7 @@ packages: description: name: wifi_iot sha256: "466aa682dfec9347f359ed552a35a2bc046597c39eb6005b36627b0bc8819d70" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "0.3.19+3" win32: @@ -2044,7 +2044,7 @@ packages: description: name: win32 sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "5.15.0" win32_registry: @@ -2052,7 +2052,7 @@ packages: description: name: win32_registry sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "2.1.0" window_size: @@ -2069,7 +2069,7 @@ packages: description: name: xdg_directories sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "1.1.0" xml: @@ -2077,7 +2077,7 @@ packages: description: name: xml sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "6.6.1" yaml: @@ -2085,7 +2085,7 @@ packages: description: name: yaml sha256: f67cdd8e07d3c6329146aaef1ba043542b3134c12489f553ca9a7435d1068aea - url: "https://pub.flutter-io.cn" + url: "https://pub.dev" source: hosted version: "3.1.4" sdks: diff --git a/apps/client/pubspec.yaml b/apps/client/pubspec.yaml index 82388bd8..aac1c5ee 100644 --- a/apps/client/pubspec.yaml +++ b/apps/client/pubspec.yaml @@ -1,7 +1,7 @@ name: eaimar description: "Eaimar - AI Voice Assistant." publish_to: "none" -version: 1.0.2+25 +version: 1.0.2+26 environment: sdk: ">=3.3.0 <4.0.0" diff --git a/apps/client/test/bailian_native_parity_test.dart b/apps/client/test/bailian_native_parity_test.dart index 7371dead..6ee9d53c 100644 --- a/apps/client/test/bailian_native_parity_test.dart +++ b/apps/client/test/bailian_native_parity_test.dart @@ -35,6 +35,30 @@ void main() { expect(BailianMultimodalService.resolveWorkspaceId('ws-x'), 'ws-x'); }); + test('EMAI 带位置和日期,未登记的应用不带', () { + final emai = BailianMultimodalService.resolveAppId(); + expect(BailianMultimodalService.wantsClientContext(emai), isTrue); + expect(BailianMultimodalService.wantsClientContext('not-an-app'), isFalse); + + final ctx = BailianMultimodalService.promptContext(emai); + expect(ctx, isNotNull); + expect(ctx!['location'], '未知'); + expect( + ctx['date'], + matches(RegExp(r'^\d{4}年\d{1,2}月\d{1,2}日 星期[一二三四五六日] \d{2}:\d{2}$')), + ); + expect(ctx.containsKey('clientLocation'), isFalse); + expect(BailianMultimodalService.promptContext('not-an-app'), isNull); + }); + + test('登记进 clientContextAppIds 的应用也带位置和日期', () { + const id = 'mm_test_context'; + BailianMultimodalService.clientContextAppIds.add(id); + addTearDown(() => BailianMultimodalService.clientContextAppIds.remove(id)); + expect(BailianMultimodalService.wantsClientContext(id), isTrue); + expect(BailianMultimodalService.promptContext(id)?['location'], '未知'); + }); + test('MCP 工具名只有一份,且非空', () { // 原生不再抄一份:服务端新增需要身份的工具时只改这里。 // 漏了那个工具永远「身份校验失败」,而且客户端不报任何错。 diff --git a/apps/client/test/device_location_date_test.dart b/apps/client/test/device_location_date_test.dart index ec89c097..165ee671 100644 --- a/apps/client/test/device_location_date_test.dart +++ b/apps/client/test/device_location_date_test.dart @@ -8,4 +8,37 @@ void main() { expect(DeviceLocationService.dateText(DateTime(2026, 9, 27, 9, 0)), '2026年9月27日 星期日 09:00'); }); + + test('百炼 \${location}:完整地址优先,否则省市区加街道,不带经纬度', () { + expect( + DeviceLocationService.formatPlaceText({ + 'province': '广东省', + 'city': '深圳市', + 'district': '福田区', + 'street': '华强北路', + 'address': '广东省深圳市福田区华强北路1002号', + 'latitude': 22.5431, + 'longitude': 114.0579, + }), + '广东省深圳市福田区华强北路1002号', + ); + expect( + DeviceLocationService.formatPlaceText({ + 'province': '北京市', + 'city': '北京市', + 'district': '东城区', + 'street': '王府井大街', + 'address': '', + }), + '北京市东城区王府井大街', + ); + expect( + DeviceLocationService.formatPlaceText({ + 'latitude': 22.54, + 'longitude': 114.05, + }), + '未知', + ); + expect(DeviceLocationService.formatPlaceText(null), '未知'); + }); }