鸿蒙端提交

This commit is contained in:
Developer
2026-06-06 06:54:22 +08:00
parent bc7cb075c5
commit e119c84868
32 changed files with 357 additions and 201 deletions

View File

@@ -10,6 +10,7 @@
| 日期 | 版本 | 变更内容 |
|---|---|---|
| 2026-06-06 | v8 | 新增 `app_tracking_transparency` 差异对照条目;新增 `nearby_connections` 鸿蒙端本地stub包说明新增 §2.10 nearby_connections鸿蒙适配说明 |
| 2026-06-02 | v7 | **重大变更**pubspec.yaml 拆分为双模板pubspec.ohos.yaml + pubspec.macos.yamlpubspec.yaml 不再提交到 Git新增三方库变更通知机制新增 setup_pubspec.ps1 脚本 |
| 2026-06-02 | v6 | 鸿蒙端 pubspec.yaml 同步 bitsdojo_window → window_manager 迁移;更新 file_picker 本地包版本注释(v8.3.7→v11.0.0-ohos.1);更新 speech_to_text(^7.0.0→^7.4.0)、live_activities(^2.0.0→^2.4.9) 远程版本号;补充 dependency_overrides 中 bitsdojo_window_windows 移除说明 |
| 2026-06-01 | v5 | 新增 §2.6 pub cache 补丁说明;标记 bitsdojo_window 迁移完成file_picker 升级到 12.x |
@@ -148,6 +149,7 @@ Error: The getter 'ohos' isn't defined for the class 'TargetPlatform'
| connectivity_plus | `path: packages/connectivity_plus` | `^7.1.1` |
| device_info_plus | `path: packages/device_info_plus` | `^13.1.0` |
| permission_handler | `path: packages/permission_handler` | `^12.0.1` |
| app_tracking_transparency | `^2.0.6` | `^2.0.6` |
| flutter_local_notifications | `path: packages/flutter_local_notifications` | `^21.0.0` |
| url_launcher | `path: packages/url_launcher` | `^6.3.2` |
| app_links | `path: packages/app_links` | `^7.0.0` |
@@ -174,6 +176,7 @@ Error: The getter 'ohos' isn't defined for the class 'TargetPlatform'
| mobile_scanner | `path: packages/mobile_scanner` | `^7.1.4` |
| wifi_iot | `path: packages/wifi_iot` | `^0.3.19` |
| nearby_service | `path: packages/nearby_service` | `^0.2.1` |
| nearby_connections | `path: packages/nearby_connections` (stub) | `^4.1.1` |
| sqflite | `path: packages/sqflite` | `^2.4.1` |
| workmanager | `path: packages/workmanager` | `^0.9.0` |
| flutter_tts | `path: packages/flutter_tts` | `^4.2.0` |
@@ -345,6 +348,36 @@ MacBook Pro 端使用 pub.dev 版本 `^0.9.1`,鸿蒙端的 `ohosName` 参数
home_widget: ^0.9.1
```
#### 2.8.7 nearby_connections鸿蒙端本地stub包
`nearby_connections` 仅支持 Android/iOS 平台Google Nearby Connections API不支持鸿蒙。
鸿蒙端使用本地 stub 包 `packages/nearby_connections/`,提供与 4.x API 一致的类型定义和方法签名,
但所有方法调用抛出 `UnsupportedError`。代码中通过 `isP2pSupported` 守卫,鸿蒙端不会实际调用 P2P 方法。
```yaml
# 鸿蒙端使用本地 stub 包
nearby_connections:
path: packages/nearby_connections
# MacBook Pro 端使用远程版本
nearby_connections: ^4.1.1
```
> **注意**:鸿蒙端 stub 包的 Dart API 与远程版本完全一致(枚举、类型、方法签名),
> 编译不会报错。运行时由 `NearbyServiceAdapter.isP2pSupported` 守卫,
> 鸿蒙端 P2P 功能不可用,仅 `nearby_service` 原生引擎可用。
#### 2.8.8 app_tracking_transparency两端均使用远程版本
`app_tracking_transparency` 是 iOS 专属权限库App Tracking Transparency
两端均使用远程版本 `^2.0.6`,无需本地适配。代码中通过 `Platform.isIOS` 条件守卫,
非 iOS 平台直接返回授权成功,不影响鸿蒙/Android/macOS 编译。
```yaml
# 两端配置相同
app_tracking_transparency: ^2.0.6
```
### 2.9 ⚠️ pub cache 补丁MacBook Pro 端必读)
> **关键问题**`dependency_overrides` 中 `win32: ^6.0.1` 导致部分依赖 `win32 ^5.x` 的三方包编译失败。

View File

@@ -114,16 +114,8 @@ enum AppPermission {
CupertinoIcons.antenna_radiowaves_left_right,
Color(0xFF64D2FF),
),
microphone(
Permission.microphone,
CupertinoIcons.mic_fill,
Color(0xFFFF3B30),
),
storage(
Permission.storage,
CupertinoIcons.folder_fill,
Color(0xFFFF9500),
),
microphone(Permission.microphone, CupertinoIcons.mic_fill, Color(0xFFFF3B30)),
storage(Permission.storage, CupertinoIcons.folder_fill, Color(0xFFFF9500)),
network(
Permission.notification,
CupertinoIcons.wifi,
@@ -537,37 +529,24 @@ class PermissionService {
}
/// 快捷方法: 请求相机权限
static Future<bool> requestCamera(BuildContext context) => requestPermission(
context,
AppPermission.camera,
);
static Future<bool> requestCamera(BuildContext context) =>
requestPermission(context, AppPermission.camera);
/// 快捷方法: 请求相册权限
static Future<bool> requestPhotos(BuildContext context) => requestPermission(
context,
AppPermission.photos,
);
static Future<bool> requestPhotos(BuildContext context) =>
requestPermission(context, AppPermission.photos);
/// 快捷方法: 请求通知权限
static Future<bool> requestNotification(BuildContext context) =>
requestPermission(
context,
AppPermission.notification,
);
requestPermission(context, AppPermission.notification);
/// 快捷方法: 请求位置权限
static Future<bool> requestLocation(BuildContext context) =>
requestPermission(
context,
AppPermission.location,
);
requestPermission(context, AppPermission.location);
/// 快捷方法: 请求麦克风权限
static Future<bool> requestMicrophone(BuildContext context) =>
requestPermission(
context,
AppPermission.microphone,
);
requestPermission(context, AppPermission.microphone);
/// 打开系统设置
static Future<bool> openSettings() => openAppSettings();
@@ -584,7 +563,8 @@ class PermissionService {
static Future<bool> requestTrackingPermission() async {
if (!Platform.isIOS) return true;
try {
final status = await AppTrackingTransparency.requestTrackingAuthorization();
final status =
await AppTrackingTransparency.requestTrackingAuthorization();
_log.i('ATT授权状态: $status');
return status == TrackingStatus.authorized;
} catch (e) {

View File

@@ -1,11 +1,11 @@
// ============================================================
// 闲言APP — NearbyService适配器
// 创建时间: 2026-05-13
// 更新时间: 2026-06-05
// 更新时间: 2026-06-06
// 作用: 封装nearby_service + nearby_connections三方库适配项目传输体系
// nearby_service: Android Wi-Fi Direct | iOS/macOS MultipeerConnectivity | 鸿蒙
// nearby_connections: Google Nearby Connections(蓝牙发现+Wi-Fi Direct传输, 仅Android/iOS)
// 上次更新: 集成nearby_connections实现P2P近场设备发现与文件传输
// 上次更新: 跨平台兼容修复鸿蒙端使用ohos属性替代android属性P2P引擎鸿蒙端跳过
// ============================================================
import 'dart:async';
@@ -38,10 +38,13 @@ class NearbyP2pDeviceInfo {
/// 端点ID
final String endpointId;
/// 端点名称
final String endpointName;
/// 服务ID
final String? serviceId;
/// 连接状态
final NearbyP2pConnectionState state;
@@ -160,10 +163,10 @@ class NearbyServiceAdapter {
bool get p2pIsDiscovering => _p2pIsDiscovering;
List<NearbyP2pDeviceInfo> get p2pDiscoveredDevices =>
_p2pDiscoveredDevices.values.toList();
List<NearbyP2pDeviceInfo> get p2pConnectedDevices =>
_p2pDiscoveredDevices.values
.where((d) => d.state == NearbyP2pConnectionState.connected)
.toList();
List<NearbyP2pDeviceInfo> get p2pConnectedDevices => _p2pDiscoveredDevices
.values
.where((d) => d.state == NearbyP2pConnectionState.connected)
.toList();
// ============================================================
// 公共流
@@ -578,9 +581,18 @@ class NearbyServiceAdapter {
}
Future<bool> requestPermissions() async {
if ((!Platform.isAndroid && !pu.isOhos) || _service == null) return true;
if (!Platform.isAndroid && !pu.isOhos) return true;
if (_service == null) return false;
try {
return await _service!.android!.requestPermissions();
// Android使用android属性鸿蒙使用ohos属性
if (Platform.isAndroid) {
return await _service!.android?.requestPermissions() ?? false;
}
if (pu.isOhos) {
// 鸿蒙端权限通过permission_handler统一请求nearby_service.ohos无独立权限方法
return true;
}
return true;
} catch (e) {
Log.e('NearbyService: Request permissions failed: $e');
return false;
@@ -588,9 +600,15 @@ class NearbyServiceAdapter {
}
Future<bool> checkWifiService() async {
if ((!Platform.isAndroid && !pu.isOhos) || _service == null) return true;
if (!Platform.isAndroid && !pu.isOhos) return true;
if (_service == null) return true;
try {
return await _service!.android!.checkWifiService();
// 仅Android端支持Wi-Fi服务检查
if (Platform.isAndroid) {
return await _service!.android?.checkWifiService() ?? false;
}
// 鸿蒙/iOS/macOS端无需检查Wi-Fi服务
return true;
} catch (e) {
Log.e('NearbyService: Check WiFi failed: $e');
return false;
@@ -729,12 +747,13 @@ class NearbyServiceAdapter {
'xianyan_discoverer',
Strategy.P2P_CLUSTER,
serviceId: _p2pServiceId,
onEndpointFound: (String endpointId, String endpointName, String serviceId) {
Log.i(
'NearbyP2p: 发现设备: $endpointName ($endpointId, service=$serviceId)',
);
_onP2pEndpointFound(endpointId, endpointName, serviceId);
},
onEndpointFound:
(String endpointId, String endpointName, String serviceId) {
Log.i(
'NearbyP2p: 发现设备: $endpointName ($endpointId, service=$serviceId)',
);
_onP2pEndpointFound(endpointId, endpointName, serviceId);
},
onEndpointLost: (String? endpointId) {
if (endpointId != null) {
Log.i('NearbyP2p: 设备丢失: $endpointId');
@@ -817,9 +836,10 @@ class NearbyServiceAdapter {
onPayLoadRecieved: (String endpointId, Payload payload) {
_onP2pPayloadReceived(endpointId, payload);
},
onPayloadTransferUpdate: (String endpointId, PayloadTransferUpdate payloadTransferUpdate) {
_onP2pPayloadTransferUpdate(endpointId, payloadTransferUpdate);
},
onPayloadTransferUpdate:
(String endpointId, PayloadTransferUpdate payloadTransferUpdate) {
_onP2pPayloadTransferUpdate(endpointId, payloadTransferUpdate);
},
);
Log.i('NearbyP2p: 接受连接: $endpointId');
} catch (e) {
@@ -883,10 +903,7 @@ class NearbyServiceAdapter {
}
// 通过nearby_connections发送文件payload
await Nearby().sendFilePayload(
endpointId,
filePath,
);
await Nearby().sendFilePayload(endpointId, filePath);
return true;
} catch (e) {
@@ -900,10 +917,7 @@ class NearbyServiceAdapter {
if (!isP2pSupported) return false;
try {
await Nearby().sendBytesPayload(
endpointId,
data,
);
await Nearby().sendBytesPayload(endpointId, data);
Log.i('NearbyP2p: 发送数据: ${data.length} bytes');
return true;
} catch (e) {
@@ -1041,8 +1055,7 @@ class NearbyServiceAdapter {
final filePath = payload.uri ?? payload.filePath;
if (filePath != null) {
final fileName = filePath.split('/').last;
final mimeType =
lookupMimeType(fileName) ?? 'application/octet-stream';
final mimeType = lookupMimeType(fileName) ?? 'application/octet-stream';
final isImage = mimeType.startsWith('image/');
final isVideo = mimeType.startsWith('video/');
@@ -1168,18 +1181,21 @@ class NearbyServiceAdapter {
/// 将P2P设备同步到nearby_service设备流统一设备列表
void _notifyP2pDeviceAsTransferDevice() {
final devices = _p2pDiscoveredDevices.values
.map((d) => TransferDevice(
id: d.endpointId,
alias: d.endpointName,
deviceType: DeviceType.mobile,
port: 0,
pairingMethod: PairingMethod.nearbyP2p,
preferredTransport: TransportType.wifiDirect,
lastSeen: DateTime.now(),
isOnline: d.state == NearbyP2pConnectionState.connected ||
d.state == NearbyP2pConnectionState.discovered,
isVerified: d.state == NearbyP2pConnectionState.connected,
))
.map(
(d) => TransferDevice(
id: d.endpointId,
alias: d.endpointName,
deviceType: DeviceType.mobile,
port: 0,
pairingMethod: PairingMethod.nearbyP2p,
preferredTransport: TransportType.wifiDirect,
lastSeen: DateTime.now(),
isOnline:
d.state == NearbyP2pConnectionState.connected ||
d.state == NearbyP2pConnectionState.discovered,
isVerified: d.state == NearbyP2pConnectionState.connected,
),
)
.toList();
if (!_devicesController.isClosed && devices.isNotEmpty) {

View File

@@ -24,6 +24,7 @@ import '../../../../shared/widgets/containers/glass_container.dart';
import '../../../../shared/widgets/adaptive/adaptive_back_button.dart';
import '../../../../shared/widgets/feedback/external_link_dialog.dart';
import '../../../../shared/widgets/feedback/app_toast.dart';
import 'about_shared_widgets.dart';
class AboutPage extends ConsumerWidget {
const AboutPage({super.key});
@@ -464,29 +465,19 @@ class _DeveloperSection extends ConsumerWidget {
subtitle: t.about.updateLogMenuDesc,
ext: ext,
onTap: () {
// 拼接共享更新日志数据
final logText = AppUpdateLog.entries
.map((entry) {
final items = entry.changes.map((c) => '$c').join('\n');
return '${entry.version} (${entry.date})\n$items';
})
.join('\n\n');
showCupertinoDialog<void>(
context: context,
builder: (ctx) => CupertinoAlertDialog(
title: Text(t.about.updateLog),
content: Text(
'v${AppVersion.version}\n'
'• 修复数据管理/图片缓存页面路由\n'
'• 修复返回主页面渲染异常\n'
'• 修复发现/灵感页面RenderSliver报错\n'
'• 修复工具中心溢出问题\n'
'• 优化下拉面板即时打开\n'
'• 增强句子列表交错动画\n'
'• 修复壁纸按钮颜色重合\n'
'• 修复倒计时+号无反应\n'
'• 绑定邮箱增加验证码流程\n'
'• 检测更新改为"无更新"\n'
'• 修复软件权限页面跳转\n'
'• 增加收集信息入口\n'
'• 日志查看器增加副标题\n'
'• 增加导出个人信息功能\n'
'• 备案信息迁移至软件信息页\n'
'• 统一按钮选中色动态主题',
),
content: Text(logText),
actions: [
CupertinoDialogAction(
isDefaultAction: true,

View File

@@ -1,9 +1,9 @@
/// ============================================================
/// 闲言APP — 关于页面共享组件
/// 创建时间: 2026-05-29
/// 更新时间: 2026-05-29
/// 作用: 软件信息页与了解我们页共用的通用组件
/// 上次更新: 从 app_info_page / learn_us_page 中提取共享组件
/// 更新时间: 2026-06-05
/// 作用: 软件信息页与了解我们页共用的通用组件及更新日志数据
/// 上次更新: 新增 AppUpdateLog 共享更新日志数据,确保两处展示一致
/// ============================================================
import 'package:flutter/cupertino.dart';
@@ -14,6 +14,61 @@ import '../../../../core/theme/app_spacing.dart';
import '../../../../core/theme/app_typography.dart';
import '../../../../core/theme/app_radius.dart';
// ============================================================
// 更新日志数据 — 软件信息页 & 关于页共用
// ============================================================
/// 单条更新日志
class UpdateLogEntry {
const UpdateLogEntry({
required this.version,
required this.date,
required this.changes,
});
/// 版本号
final String version;
/// 发布日期
final String date;
/// 更新内容列表(中文)
final List<String> changes;
}
/// 全局更新日志数据(中文)
class AppUpdateLog {
AppUpdateLog._();
static const List<UpdateLogEntry> entries = [
UpdateLogEntry(
version: 'v6.5.103',
date: '2026-06-05',
changes: [
'🆕 新增桌面快捷方式(主题个性化、通用设置)',
'🆕 引导页文件传输预览改为"移动设备/PC设备"',
'🔧 修复鸿蒙 module.json5 shortcuts 配置校验报错',
'🎨 液态玻璃风格适配优化',
],
),
UpdateLogEntry(
version: 'v6.5.1',
date: '2026-05-29',
changes: ['🔧 修复返回主页面渲染异常', '🎨 统一按钮选中色动态主题'],
),
UpdateLogEntry(
version: 'v6.5.0',
date: '2026-05-20',
changes: [
'🆕 增加收集信息入口',
'🔧 增强句子列表交错动画',
'🔧 修复软件权限页面跳转',
'🔧 日志查看器增加副标题',
],
),
];
}
class AboutSectionTitle extends StatelessWidget {
const AboutSectionTitle({
super.key,

View File

@@ -20,7 +20,18 @@ import '../../../../core/theme/app_typography.dart';
import '../../../../core/theme/app_radius.dart';
import '../../../../core/utils/platform/platform_utils.dart' as pu;
import '../../../../core/constants/app_constants.dart';
import '../../../../core/utils/platform/platform_utils.dart' show isOhos, isAndroid, isWindows, isIOS, isMacOS, isLinux, isWeb, isMobile, isDesktop, platformName;
import '../../../../core/utils/platform/platform_utils.dart'
show
isOhos,
isAndroid,
isWindows,
isIOS,
isMacOS,
isLinux,
isWeb,
isMobile,
isDesktop,
platformName;
import '../../../../shared/widgets/containers/glass_container.dart';
import '../../../../l10n/translations.dart';
import 'about_shared_widgets.dart';
@@ -606,15 +617,18 @@ class UpdateLogSection extends StatelessWidget {
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
child: UpdateItem(
version: '${t.about.version} ${AppVersion.version}',
date: '2026-05-29',
changes: [
t.about.updateLog1,
t.about.updateLog2,
t.about.updateLog3,
],
ext: ext,
child: Column(
children: AppUpdateLog.entries.map((entry) {
return Padding(
padding: const EdgeInsets.only(bottom: AppSpacing.sm),
child: UpdateItem(
version: entry.version,
date: entry.date,
changes: entry.changes,
ext: ext,
),
);
}).toList(),
),
),
const SizedBox(height: AppSpacing.md),
@@ -650,9 +664,7 @@ class IcpSection extends StatelessWidget {
icon: CupertinoIcons.doc_text,
title: t.about.icpInfo,
ext: ext,
trailing: isChinese
? null
: _IcpInfoIcon(ext: ext, t: t),
trailing: isChinese ? null : _IcpInfoIcon(ext: ext, t: t),
),
GestureDetector(
onTap: () {
@@ -808,11 +820,7 @@ class _IcpInfoIcon extends StatelessWidget {
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _showIcpInfoHintDialog(context),
child: Icon(
CupertinoIcons.info_circle,
size: 18,
color: ext.textHint,
),
child: Icon(CupertinoIcons.info_circle, size: 18, color: ext.textHint),
);
}

View File

@@ -11,7 +11,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:xianyan/core/router/app_nav_extension.dart';
import 'package:package_info_plus/package_info_plus.dart';
import '../../../../../../core/services/accessibility/accessibility_service.dart';
import '../../../../../../core/constants/app_constants.dart';
import '../../../../../../core/router/app_routes.dart';
import '../../../../../../core/services/device/app_lock_service.dart';
@@ -138,12 +137,6 @@ class _GeneralSettingsPageState extends ConsumerState<GeneralSettingsPage>
final t = ref.watch(translationsProvider);
final splitState = ref.watch(splitViewProvider);
// 获取系统无障碍状态
final mediaQuery = MediaQuery.of(context);
final systemHighContrast = mediaQuery.highContrast;
final systemReduceAnimations = mediaQuery.disableAnimations;
final systemBoldText = mediaQuery.boldText;
final allSections = buildGeneralSettingSections(
settings: settings,
notificationEnabled: notificationEnabled,
@@ -154,9 +147,6 @@ class _GeneralSettingsPageState extends ConsumerState<GeneralSettingsPage>
navBarPositionIndex: splitState.navBarPosition.index,
splitRatio: splitState.splitRatio,
splitViewEnabled: splitState.splitViewEnabled,
systemHighContrast: systemHighContrast,
systemReduceAnimations: systemReduceAnimations,
systemBoldText: systemBoldText,
);
return filterSettingSections(
allSections,
@@ -944,10 +934,6 @@ class _GeneralSettingsPageState extends ConsumerState<GeneralSettingsPage>
notifier.setNearbyDiscovery(value);
case 'split_view_enabled':
ref.read(splitViewProvider.notifier).setSplitViewEnabled(value);
case 'semantics_debug':
notifier.setSemanticsDebugEnabled(value);
// 同步到AccessibilityService
AccessibilityService.instance.setSemanticsDebug(value);
}
}

View File

@@ -26,9 +26,6 @@ List<SettingSection> buildGeneralSettingSections({
required int navBarPositionIndex,
required double splitRatio,
required bool splitViewEnabled,
bool systemHighContrast = false,
bool systemReduceAnimations = false,
bool systemBoldText = false,
}) {
return [
// ── 交互设置 ──
@@ -97,15 +94,15 @@ List<SettingSection> buildGeneralSettingSections({
),
if (settings.pageTransitionModeId == 'navigate')
SettingItem(
id: 'predictive_back',
icon: CupertinoIcons.arrow_turn_down_left,
iconColor: AppColors.iosLightBlue,
title: t.predictiveBack,
subtitle: t.predictiveBackSubtitle,
type: SettingType.toggle,
value: settings.predictiveBackEnabled,
isLocked: true,
),
id: 'predictive_back',
icon: CupertinoIcons.arrow_turn_down_left,
iconColor: AppColors.iosLightBlue,
title: t.predictiveBack,
subtitle: t.predictiveBackSubtitle,
type: SettingType.toggle,
value: settings.predictiveBackEnabled,
isLocked: true,
),
SettingItem(
id: 'long_press_preview',
icon: CupertinoIcons.hand_point_right_fill,
@@ -317,51 +314,6 @@ List<SettingSection> buildGeneralSettingSections({
],
),
// ── 无障碍设置 ──
SettingSection(
title: '♿ 无障碍',
icon: CupertinoIcons.person_circle_fill,
color: AppColors.iosBlue,
items: [
SettingItem(
id: 'semantics_debug',
icon: CupertinoIcons.eye_fill,
iconColor: AppColors.iosPurple,
title: '语义调试',
subtitle: '显示语义标注覆盖层,辅助开发无障碍适配',
type: SettingType.toggle,
value: settings.semanticsDebugEnabled,
),
SettingItem(
id: 'system_high_contrast',
icon: CupertinoIcons.circle_lefthalf_fill,
iconColor: AppColors.iosOrange,
title: '高对比度',
subtitle: '跟随系统设置',
type: SettingType.selection,
displayValue: systemHighContrast ? '已开启' : '未开启',
),
SettingItem(
id: 'system_reduce_animations',
icon: CupertinoIcons.wand_stars_inverse,
iconColor: AppColors.iosGray,
title: '减少动画',
subtitle: '跟随系统设置',
type: SettingType.selection,
displayValue: systemReduceAnimations ? '已开启' : '未开启',
),
SettingItem(
id: 'system_bold_text',
icon: CupertinoIcons.bold,
iconColor: AppColors.iosGreen,
title: '粗体文本',
subtitle: '跟随系统设置',
type: SettingType.selection,
displayValue: systemBoldText ? '已开启' : '未开启',
),
],
),
// ── 性能设置 ──
SettingSection(
title: t.performance,

View File

@@ -18,6 +18,7 @@ import '../../../../core/router/app_nav_extension.dart';
import '../../../../core/services/data/data_export_service.dart';
import '../../../../core/services/data/settings_export_service.dart';
import '../../../../core/services/device/haptic_service.dart';
import '../../../../core/services/accessibility/accessibility_service.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/theme/app_spacing.dart';
import '../../../../core/theme/app_typography.dart';
@@ -217,6 +218,20 @@ class _OtherSettingsPageState extends ConsumerState<OtherSettingsPage> {
ref.read(generalSettingsProvider.notifier).setBoldTextEnabled(v);
},
),
SettingsSwitchTile(
icon: CupertinoIcons.eye_fill,
iconColor: const Color(0xFFAF52DE),
title: '语义调试',
subtitle: '显示语义标注覆盖层,辅助开发无障碍适配',
value: settings.semanticsDebugEnabled,
onChanged: (v) {
HapticService.toggleSwitch();
ref
.read(generalSettingsProvider.notifier)
.setSemanticsDebugEnabled(v);
AccessibilityService.instance.setSemanticsDebug(v);
},
),
],
);
}
@@ -444,7 +459,11 @@ class _OtherSettingsPageState extends ConsumerState<OtherSettingsPage> {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(CupertinoIcons.square_arrow_up, size: 18, color: CupertinoColors.white),
const Icon(
CupertinoIcons.square_arrow_up,
size: 18,
color: CupertinoColors.white,
),
const SizedBox(width: AppSpacing.sm),
Text(
'导出设置',
@@ -475,11 +494,17 @@ class _OtherSettingsPageState extends ConsumerState<OtherSettingsPage> {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(CupertinoIcons.square_arrow_down, size: 18, color: ext.textPrimary),
Icon(
CupertinoIcons.square_arrow_down,
size: 18,
color: ext.textPrimary,
),
const SizedBox(width: AppSpacing.sm),
Text(
'从文件导入',
style: AppTypography.body.copyWith(color: ext.textPrimary),
style: AppTypography.body.copyWith(
color: ext.textPrimary,
),
),
],
),
@@ -498,11 +523,17 @@ class _OtherSettingsPageState extends ConsumerState<OtherSettingsPage> {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(CupertinoIcons.doc_on_clipboard, size: 18, color: ext.textPrimary),
Icon(
CupertinoIcons.doc_on_clipboard,
size: 18,
color: ext.textPrimary,
),
const SizedBox(width: AppSpacing.sm),
Text(
'从剪贴板导入',
style: AppTypography.body.copyWith(color: ext.textPrimary),
style: AppTypography.body.copyWith(
color: ext.textPrimary,
),
),
],
),
@@ -537,9 +568,7 @@ class _OtherSettingsPageState extends ConsumerState<OtherSettingsPage> {
allowedExtensions: ['json'],
);
} catch (_) {
result = await FilePicker.pickFiles(
);
result = await FilePicker.pickFiles();
}
if (result == null || result.files.isEmpty) return;
@@ -648,7 +677,9 @@ class _OtherSettingsPageState extends ConsumerState<OtherSettingsPage> {
child: const Text('导入'),
onPressed: () async {
Navigator.pop(ctx);
final success = await SettingsExportService.importFromJson(jsonStr);
final success = await SettingsExportService.importFromJson(
jsonStr,
);
if (mounted) {
_showMessage(success ? '✅ 导入成功,设置已更新' : '❌ 导入失败,请检查文件格式');
if (success) {

View File

@@ -518,7 +518,7 @@ class _WelcomePageState extends ConsumerState<WelcomePage> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildDeviceIcon(ext, '📱', 'iPhone'),
_buildDeviceIcon(ext, '📱', ob.mobileDevice),
Column(
children: [
SizedBox(
@@ -543,7 +543,7 @@ class _WelcomePageState extends ConsumerState<WelcomePage> {
),
],
),
_buildDeviceIcon(ext, '💻', 'MacBook'),
_buildDeviceIcon(ext, '💻', ob.pcDevice),
],
),
const SizedBox(height: AppSpacing.sm),

View File

@@ -1339,6 +1339,8 @@ const ar = T(
templateGlass: 'زجاجي',
transferring: 'جارٍ النقل 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: 'جهاز محمول',
pcDevice: 'جهاز كمبيوتر',
rssLabel: 'خلاصات RSS',
addRssSource: 'إضافة خلاصة',
welcomeNavLabel: 'ترحيب',

View File

@@ -1346,6 +1346,8 @@ const bn = T(
templateGlass: 'ফ্রস্টেড',
transferring: 'স্থানান্তরিত হচ্ছে 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: 'মোবাইল ডিভাইস',
pcDevice: 'PC ডিভাইস',
rssLabel: 'RSS ফিড',
addRssSource: 'ফিড যোগ করুন',
welcomeNavLabel: 'স্বাগতম',

View File

@@ -1362,6 +1362,8 @@ const de = T(
templateGlass: 'Milchglas',
transferring: 'Übertragung 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: 'Mobilgerät',
pcDevice: 'PC-Gerät',
rssLabel: 'RSS-Feeds',
addRssSource: 'Feed hinzufügen',
welcomeNavLabel: 'Willkommen',

View File

@@ -1356,6 +1356,8 @@ const en = T(
templateGlass: 'Frosted',
transferring: 'Transferring 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: 'Mobile Device',
pcDevice: 'PC Device',
rssLabel: 'RSS Feeds',
addRssSource: 'Add Feed',
welcomeNavLabel: 'Welcome',

View File

@@ -1374,6 +1374,8 @@ const es = T(
templateGlass: 'Esmerilado',
transferring: 'Transfiriendo 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: 'Dispositivo móvil',
pcDevice: 'Dispositivo PC',
rssLabel: 'Fuentes RSS',
addRssSource: 'Añadir fuente',
welcomeNavLabel: 'Bienvenida',

View File

@@ -1381,6 +1381,8 @@ const fr = T(
templateGlass: 'Givré',
transferring: 'Transfert 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: 'Appareil mobile',
pcDevice: 'Appareil PC',
rssLabel: 'Flux RSS',
addRssSource: 'Ajouter un flux',
welcomeNavLabel: 'Bienvenue',

View File

@@ -1341,6 +1341,8 @@ const hi = T(
templateGlass: 'फ्रॉस्टेड',
transferring: 'स्थानांतरित हो रहा है 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: 'मोबाइल डिवाइस',
pcDevice: 'PC डिवाइस',
rssLabel: 'RSS फ़ीड',
addRssSource: 'फ़ीड जोड़ें',
welcomeNavLabel: 'स्वागत',

View File

@@ -1371,6 +1371,8 @@ const it = T(
templateGlass: 'Satinato',
transferring: 'Trasferimento 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: 'Dispositivo mobile',
pcDevice: 'Dispositivo PC',
rssLabel: 'Feed RSS',
addRssSource: 'Aggiungi feed',
welcomeNavLabel: 'Benvenuto',

View File

@@ -1301,6 +1301,8 @@ const ja = T(
templateGlass: 'すりガラス',
transferring: '転送中 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: 'モバイルデバイス',
pcDevice: 'PCデバイス',
rssLabel: 'RSSフィード',
addRssSource: 'フィードを追加',
welcomeNavLabel: 'ようこそ',

View File

@@ -1302,6 +1302,8 @@ const ko = T(
templateGlass: '서리유리',
transferring: '전송 중 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: '모바일 기기',
pcDevice: 'PC 기기',
rssLabel: 'RSS 피드',
addRssSource: '피드 추가',
welcomeNavLabel: '환영',

View File

@@ -1365,6 +1365,8 @@ const pt = T(
templateGlass: 'Fosco',
transferring: 'Transferindo 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: 'Dispositivo móvel',
pcDevice: 'Dispositivo PC',
rssLabel: 'Feeds RSS',
addRssSource: 'Adicionar feed',
welcomeNavLabel: 'Bem-vindo',

View File

@@ -1363,6 +1363,8 @@ const ru = T(
templateGlass: 'Матовое стекло',
transferring: 'Передача 70%',
wifiDirect: 'WiFi Direct',
mobileDevice: 'Мобильное устройство',
pcDevice: 'ПК-устройство',
rssLabel: 'RSS-каналы',
addRssSource: 'Добавить канал',
welcomeNavLabel: 'Приветствие',

View File

@@ -1289,6 +1289,8 @@ const zhCN = T(
templateGlass: '毛玻璃',
transferring: '传输中 70%',
wifiDirect: 'WiFi直连',
mobileDevice: '移动设备',
pcDevice: 'PC设备',
rssLabel: 'RSS 订阅源',
addRssSource: '添加订阅源',
welcomeNavLabel: '欢迎与指引',

View File

@@ -1288,6 +1288,8 @@ const zhTW = T(
templateGlass: '毛玻璃',
transferring: '傳輸中 70%',
wifiDirect: 'WiFi直連',
mobileDevice: '行動裝置',
pcDevice: 'PC裝置',
rssLabel: 'RSS 訂閱源',
addRssSource: '添加訂閱源',
welcomeNavLabel: '歡迎與指引',

View File

@@ -36,6 +36,8 @@ class TOnboarding {
required this.templateGlass,
required this.transferring,
required this.wifiDirect,
required this.mobileDevice,
required this.pcDevice,
required this.rssLabel,
required this.addRssSource,
required this.welcomeNavLabel,
@@ -158,6 +160,12 @@ class TOnboarding {
/// WiFi直连
final String wifiDirect;
/// 移动设备
final String mobileDevice;
/// PC设备
final String pcDevice;
/// RSS 订阅源
final String rssLabel;
@@ -293,6 +301,8 @@ class TOnboarding {
'templateGlass': templateGlass,
'transferring': transferring,
'wifiDirect': wifiDirect,
'mobileDevice': mobileDevice,
'pcDevice': pcDevice,
'rssLabel': rssLabel,
'addRssSource': addRssSource,
'welcomeNavLabel': welcomeNavLabel,
@@ -329,8 +339,10 @@ class TOnboarding {
'completeSetup': completeSetup,
};
static TOnboarding fromMap(Map<String, String> map, {TOnboarding? fallback}) =>
TOnboarding(
static TOnboarding fromMap(
Map<String, String> map, {
TOnboarding? fallback,
}) => TOnboarding(
welcomeTitle: map['welcomeTitle']?.isNotEmpty == true
? map['welcomeTitle']!
: (fallback?.welcomeTitle ?? ''),
@@ -361,8 +373,8 @@ class TOnboarding {
featureFileTransferDesc: map['featureFileTransferDesc']?.isNotEmpty == true
? map['featureFileTransferDesc']!
: (fallback?.featureFileTransferDesc ?? ''),
featureFileTransferDetail: map['featureFileTransferDetail']?.isNotEmpty ==
true
featureFileTransferDetail:
map['featureFileTransferDetail']?.isNotEmpty == true
? map['featureFileTransferDetail']!
: (fallback?.featureFileTransferDetail ?? ''),
featureChatFlow: map['featureChatFlow']?.isNotEmpty == true
@@ -416,6 +428,12 @@ class TOnboarding {
wifiDirect: map['wifiDirect']?.isNotEmpty == true
? map['wifiDirect']!
: (fallback?.wifiDirect ?? ''),
mobileDevice: map['mobileDevice']?.isNotEmpty == true
? map['mobileDevice']!
: (fallback?.mobileDevice ?? ''),
pcDevice: map['pcDevice']?.isNotEmpty == true
? map['pcDevice']!
: (fallback?.pcDevice ?? ''),
rssLabel: map['rssLabel']?.isNotEmpty == true
? map['rssLabel']!
: (fallback?.rssLabel ?? ''),

View File

@@ -14,10 +14,11 @@ import device_info_plus
import file_picker
import file_selector_macos
import flutter_app_group_directory
import flutter_blue_plus_darwin
import flutter_image_compress_macos
import flutter_inappwebview_macos
import flutter_local_notifications
import flutter_secure_storage_darwin
import flutter_secure_storage_macos
import flutter_tts
import flutter_webrtc
import gal
@@ -52,10 +53,11 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterAppGroupDirectoryPlugin.register(with: registry.registrar(forPlugin: "FlutterAppGroupDirectoryPlugin"))
FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin"))
FlutterImageCompressMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterImageCompressMacosPlugin"))
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
FlutterTtsPlugin.register(with: registry.registrar(forPlugin: "FlutterTtsPlugin"))
FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))

View File

@@ -21,6 +21,12 @@
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"metadata": [
{
"name": "ohos.shortcut.config",
"resource": "$profile:shortcuts"
}
],
"skills": [
{
"entities": [

View File

@@ -0,0 +1,32 @@
{
"shortcuts": [
{
"shortcutId": "action_theme",
"label": "$string:shortcut_theme_label",
"icon": "$media:icon",
"wants": [
{
"bundleName": "apps.xy.xianyan",
"abilityName": "EntryAbility",
"parameters": {
"shortcutType": "action_theme"
}
}
]
},
{
"shortcutId": "action_general_settings",
"label": "$string:shortcut_general_settings_label",
"icon": "$media:icon",
"wants": [
{
"bundleName": "apps.xy.xianyan",
"abilityName": "EntryAbility",
"parameters": {
"shortcutType": "action_general_settings"
}
}
]
}
]
}

View File

@@ -31,6 +31,14 @@
{
"name": "permission_vibrate_reason",
"value": "Used for haptic feedback during interactions"
},
{
"name": "shortcut_theme_label",
"value": "Theme"
},
{
"name": "shortcut_general_settings_label",
"value": "Settings"
}
]
}

View File

@@ -85,6 +85,7 @@ dependencies:
# --- 权限 ---
permission_handler: # v12.0.1 | 运行时权限请求(本地化-鸿蒙适配)
path: packages/permission_handler
app_tracking_transparency: ^2.0.6 # iOS App Tracking Transparency授权(鸿蒙端不调用,仅保证编译通过)
# --- 本地通知 ---
flutter_local_notifications: # v21.0.0 | 本地推送通知(本地化-鸿蒙适配)
@@ -281,7 +282,8 @@ dependencies:
path: packages/wifi_iot
nearby_service: # v0.2.1 | 近场设备发现+通信(本地化-鸿蒙适配)
path: packages/nearby_service
nearby_connections: ^4.1.1 # Google Nearby Connections(蓝牙发现+Wi-Fi Direct传输,仅Android/iOS)
nearby_connections: # v4.1.1 | Google Nearby Connections(本地化-鸿蒙适配,仅Android/iOS)
path: packages/nearby_connections
flutter_localizations:
sdk: flutter # Flutter国际化支持
@@ -413,6 +415,10 @@ dependency_overrides:
path: packages/workmanager_ohos
home_widget:
path: packages/home_widget
nearby_service:
path: packages/nearby_service
nearby_connections:
path: packages/nearby_connections
# ============================================================
# Flutter 配置

View File

@@ -12,6 +12,7 @@
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <desktop_drop/desktop_drop_plugin.h>
#include <file_selector_windows/file_selector_windows.h>
#include <flutter_blue_plus_winrt/flutter_blue_plus_plugin.h>
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <flutter_tts/flutter_tts_plugin.h>
@@ -41,6 +42,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("DesktopDropPlugin"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterBluePlusPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterBluePlusPlugin"));
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(

View File

@@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
desktop_drop
file_selector_windows
flutter_blue_plus_winrt
flutter_inappwebview_windows
flutter_secure_storage_windows
flutter_tts