chore: 发布v6.36.1版本,鸿蒙适配+隐私合规收尾

### 变更详情
1.  **多语言国际化**:新增`rejectAndExit`翻译键,覆盖14种语言的拒绝退出文案
2.  **鸿蒙端适配**:
    - 修复USB事件监听报错,仅Android端启用原生USB监听
    - 调整小部件管理页交互,鸿蒙端跳过Beta弹窗并显示"敬请期待"提示
    - 适配通用设置页和功能检查的鸿蒙端toast替代弹窗逻辑
    - 调整句子详情页"编辑"按钮的鸿蒙端交互
3.  **隐私合规优化**:
    - 移除剪贴板定时轮询,改为被动触发模式,仅在用户主动进入稍后读页面时检查剪贴板
    - 移除Android端非核心权限,精简权限声明
    - 新增启动页SplashActivity作为协议守门人,从根本上阻止敏感插件在协议前初始化
    - 为Android端引导页新增双按钮布局,优化未勾选协议时的用户操作引导
4.  **依赖与代码清理**:
    - 修复macOS端flutter_secure_storage插件导入路径
    - 简化字体导入逻辑,移除冗余的readAsBytes降级逻辑
    - 更新CHANGELOG文档,记录版本变更细节
This commit is contained in:
Developer
2026-06-11 01:23:32 +08:00
parent 3a38c69521
commit b36b9bcf9e
38 changed files with 1422 additions and 542 deletions

View File

@@ -75,7 +75,8 @@ class UsbDiscoveryService {
_handleDeviceDetached(device);
});
if (Platform.isAndroid || pu.isOhos) {
// 仅Android端支持原生USB事件监听鸿蒙端原生插件未实现xianyan/usb_events
if (Platform.isAndroid) {
_startNativeUsbEventListening();
}

View File

@@ -29,6 +29,7 @@ import '../../../../core/providers/split_view_provider.dart';
import '../../../../shared/widgets/feedback/app_toast.dart';
import '../../../../shared/widgets/feedback/external_link_dialog.dart';
import '../../../../shared/widgets/feedback/share_sheet.dart';
import '../../../../core/utils/platform/platform_utils.dart' as pu;
import '../../../../shared/widgets/media/tts_player_bar.dart';
import '../../../../shared/widgets/interaction/animated_action_button.dart';
import '../../../home/models/feed_model.dart';
@@ -42,9 +43,14 @@ import 'sentence_detail_provider.dart';
// ============================================================
class SentenceDetailActions extends ConsumerStatefulWidget {
const SentenceDetailActions({required this.sentence, required this.cardKey, super.key});
const SentenceDetailActions({
required this.sentence,
required this.cardKey,
super.key,
});
final HomeSentence sentence;
/// 截图用GlobalKey由父组件SentenceDetailPanel传入确保每个面板实例独立
final GlobalKey cardKey;
@@ -138,7 +144,9 @@ class _SentenceDetailActionsState extends ConsumerState<SentenceDetailActions> {
children: [
Expanded(
child: AnimatedActionButton(
icon: sentence.isLiked ? CupertinoIcons.heart_fill : CupertinoIcons.hand_thumbsup,
icon: sentence.isLiked
? CupertinoIcons.heart_fill
: CupertinoIcons.hand_thumbsup,
label: sentence.isLiked ? '已点赞' : '点赞',
isActive: sentence.isLiked,
ext: ext,
@@ -290,12 +298,16 @@ class _SentenceDetailActionsState extends ConsumerState<SentenceDetailActions> {
.toggleTtsPlayer(sentence.text);
},
child: Row(
children: [
Icon(CupertinoIcons.speaker, size: 14, color: ext.textPrimary),
const SizedBox(width: 4),
Text('朗读', style: TextStyle(color: ext.textPrimary)),
],
),
children: [
Icon(
CupertinoIcons.speaker,
size: 14,
color: ext.textPrimary,
),
const SizedBox(width: 4),
Text('朗读', style: TextStyle(color: ext.textPrimary)),
],
),
),
),
],
@@ -311,6 +323,11 @@ class _SentenceDetailActionsState extends ConsumerState<SentenceDetailActions> {
color: ext.accent,
borderRadius: AppRadius.mdBorder,
onPressed: () {
// 鸿蒙端暂不支持编辑器弹出toast提示
if (pu.isOhos) {
AppToast.showInfo('敬请期待');
return;
}
context.appPush('/editor?text=${Uri.encodeComponent(sentence.text)}');
},
child: const Text(
@@ -358,7 +375,11 @@ class _SentenceDetailActionsState extends ConsumerState<SentenceDetailActions> {
},
child: Row(
children: [
Icon(CupertinoIcons.hand_thumbsdown, size: 12, color: ext.accent),
Icon(
CupertinoIcons.hand_thumbsdown,
size: 12,
color: ext.accent,
),
const SizedBox(width: 4),
Text('不感兴趣', style: TextStyle(fontSize: 12, color: ext.accent)),
],

View File

@@ -1,15 +1,16 @@
/// ============================================================
/// 闲言APP — 稍后读列表页面
/// 创建时间: 2026-04-28
/// 更新时间: 2026-06-09
/// 更新时间: 2026-06-10
/// 作用: 展示用户稍后读列表,支持搜索/筛选/排序/批量操作/富详情
/// 上次更新: 移除本地UI状态字段迁移至readLaterUiProvider
/// 上次更新: 添加剪贴板被动触发用户进入稍后读页面时检查URL
/// ============================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:desktop_drop/desktop_drop.dart';
import '../../../../core/services/clipboard_monitor_service.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/theme/app_typography.dart';
import '../../../../core/utils/platform/platform_helper.dart';
@@ -54,6 +55,8 @@ class _ReadLaterPageState extends ConsumerState<ReadLaterPage>
@override
void initState() {
super.initState();
// 被动触发用户主动进入稍后读页面时检查剪贴板URL
ClipboardMonitorService.instance.checkClipboardOnce();
}
@override

View File

@@ -6,14 +6,14 @@
/// 上次更新: ext参数内部化、Mixin抽取、下拉刷新、骨架屏、评分接入、退出逻辑迁移
/// ============================================================
import 'dart:io';
// import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:custom_refresh_indicator/custom_refresh_indicator.dart';
import 'package:url_launcher/url_launcher.dart';
// import 'package:url_launcher/url_launcher.dart';
import '../../../../core/theme/app_theme.dart';
import '../../../../core/theme/app_spacing.dart';
@@ -28,6 +28,7 @@ import '../../../../l10n/translations.dart';
import '../../../../shared/widgets/containers/glass_container.dart';
import '../../../../shared/widgets/adaptive/responsive_layout.dart';
import '../../../../shared/widgets/feedback/app_toast.dart';
import '../../../../core/utils/platform/platform_utils.dart' as pu;
import '../../../../shared/widgets/input/setting_row.dart';
import '../../settings/providers/theme_settings_provider.dart';
import '../../settings/providers/general_settings_provider.dart';
@@ -137,41 +138,37 @@ class _ProfilePageState extends ConsumerState<ProfilePage>
Future<void> _launchAppStore() async {
final t = ref.read(translationsProvider);
// 始终提示未找到应用商店
// TODO: 后续恢复应用商店跳转逻辑
AppToast.showInfo(t.profile.appStoreNotFound);
try {
// iOS App Store
if (Platform.isIOS) {
const appId = '6737492298';
final uri = Uri.parse('https://apps.apple.com/app/id$appId');
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
return;
}
}
// Android Google Play
if (Platform.isAndroid) {
final uri = Uri.parse(
'market://details?id=apps.xy.xianyan',
);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
return;
}
// 降级:打开网页版
final webUri = Uri.parse(
'https://play.google.com/store/apps/details?id=apps.xy.xianyan',
);
if (await canLaunchUrl(webUri)) {
await launchUrl(webUri, mode: LaunchMode.externalApplication);
return;
}
}
AppToast.showInfo(t.profile.appStoreNotFound);
} catch (e) {
Log.e('_launchAppStore', e);
AppToast.showInfo(t.profile.appStoreNotFound);
}
return;
// // iOS App Store
// if (Platform.isIOS) {
// const appId = '6737492298';
// final uri = Uri.parse('https://apps.apple.com/app/id$appId');
// if (await canLaunchUrl(uri)) {
// await launchUrl(uri, mode: LaunchMode.externalApplication);
// return;
// }
// }
// // Android Google Play
// if (Platform.isAndroid) {
// final uri = Uri.parse(
// 'market://details?id=apps.xy.xianyan',
// );
// if (await canLaunchUrl(uri)) {
// await launchUrl(uri, mode: LaunchMode.externalApplication);
// return;
// }
// // 降级:打开网页版
// final webUri = Uri.parse(
// 'https://play.google.com/store/apps/details?id=apps.xy.xianyan',
// );
// if (await canLaunchUrl(webUri)) {
// await launchUrl(webUri, mode: LaunchMode.externalApplication);
// return;
// }
// }
// AppToast.showInfo(t.profile.appStoreNotFound);
}
// ============================================================
@@ -237,15 +234,13 @@ class _ProfilePageState extends ConsumerState<ProfilePage>
_buildProfileHeader(authState),
// —— 数据统计栏(仅登录) ——
if (authState.isLoggedIn)
_buildUserStatsBar(authState),
if (authState.isLoggedIn) _buildUserStatsBar(authState),
// —— 收藏/历史/深色模式 ——
_buildQuickSettingsSection(t),
// —— 账号/数据/离线/缓存(仅登录) ——
if (authState.isLoggedIn)
_buildAccountSection(t),
if (authState.isLoggedIn) _buildAccountSection(t),
// —— 语言/主题/通用/小组件/来源 ——
_buildGeneralSection(t, settings),
@@ -271,376 +266,375 @@ class _ProfilePageState extends ConsumerState<ProfilePage>
/// 顶部导航栏
Widget _buildNavBar(AppThemeExtension ext, T t) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: Row(
children: [
Icon(
CupertinoIcons.person_fill,
size: 28,
color: ext.accent,
),
const SizedBox(width: AppSpacing.sm),
Text(
t.navProfile,
style: AppTypography.title1.copyWith(
color: ext.textPrimary,
),
),
const Spacer(),
// —— 搜索按钮 ——
Semantics(
label: '搜索',
button: true,
child: GestureDetector(
onTap: () => SpotlightSearchOverlay.show(context, ref),
child: Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: ext.accent.withValues(alpha: 0.12),
borderRadius: AppRadius.mdBorder,
),
child: Icon(
CupertinoIcons.search,
size: 18,
color: ext.accent,
),
child:
Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
),
),
const SizedBox(width: AppSpacing.xs),
// —— 快捷操作按钮 ——
Semantics(
label: '快捷操作',
button: true,
child: GestureDetector(
onTap: () => showQuickActions(context, ref),
child: Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: ext.bgSecondary,
borderRadius: AppRadius.mdBorder,
),
child: Icon(
CupertinoIcons.plus,
size: 18,
color: ext.accent,
),
child: Row(
children: [
Icon(
CupertinoIcons.person_fill,
size: 28,
color: ext.accent,
),
const SizedBox(width: AppSpacing.sm),
Text(
t.navProfile,
style: AppTypography.title1.copyWith(
color: ext.textPrimary,
),
),
const Spacer(),
// —— 搜索按钮 ——
Semantics(
label: '搜索',
button: true,
child: GestureDetector(
onTap: () => SpotlightSearchOverlay.show(context, ref),
child: Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: ext.accent.withValues(alpha: 0.12),
borderRadius: AppRadius.mdBorder,
),
child: Icon(
CupertinoIcons.search,
size: 18,
color: ext.accent,
),
),
),
),
const SizedBox(width: AppSpacing.xs),
// —— 快捷操作按钮 ——
Semantics(
label: '快捷操作',
button: true,
child: GestureDetector(
onTap: () => showQuickActions(context, ref),
child: Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: ext.bgSecondary,
borderRadius: AppRadius.mdBorder,
),
child: Icon(
CupertinoIcons.plus,
size: 18,
color: ext.accent,
),
),
),
),
],
),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms)
.slideY(
begin: 0.04,
end: 0,
duration: 300.ms,
curve: Curves.easeOutCubic,
),
),
],
),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms)
.slideY(
begin: 0.04,
end: 0,
duration: 300.ms,
curve: Curves.easeOutCubic,
),
);
}
/// 用户头像卡片
Widget _buildProfileHeader(AuthState authState) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
child: ProfileHeader(authState: authState),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 50.ms)
.slideY(
begin: 0.05,
end: 0,
duration: 300.ms,
delay: 50.ms,
curve: Curves.easeOutCubic,
),
child:
Padding(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
child: ProfileHeader(authState: authState),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 50.ms)
.slideY(
begin: 0.05,
end: 0,
duration: 300.ms,
delay: 50.ms,
curve: Curves.easeOutCubic,
),
);
}
/// 数据统计栏
Widget _buildUserStatsBar(AuthState authState) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: UserStatsBar(user: authState.user),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 80.ms)
.slideY(
begin: 0.04,
end: 0,
duration: 300.ms,
delay: 80.ms,
curve: Curves.easeOutCubic,
),
child:
Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: UserStatsBar(user: authState.user),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 80.ms)
.slideY(
begin: 0.04,
end: 0,
duration: 300.ms,
delay: 80.ms,
curve: Curves.easeOutCubic,
),
);
}
/// 收藏 / 历史 / 深色模式
Widget _buildQuickSettingsSection(T t) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: GlassContainer(
depth: GlassDepth.elevated,
padding: EdgeInsets.zero,
child: Column(
children: [
SettingRow(
icon: CupertinoIcons.heart_fill,
title: t.myFavorites,
onTap: () => navigateOrPanel(
AppRoutes.favorites,
'favorites',
child:
Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.book_fill,
title: t.readingHistory,
onTap: () => navigateOrPanel(
AppRoutes.history,
'history',
child: GlassContainer(
depth: GlassDepth.elevated,
padding: EdgeInsets.zero,
child: Column(
children: [
SettingRow(
icon: CupertinoIcons.heart_fill,
title: t.myFavorites,
onTap: () =>
navigateOrPanel(AppRoutes.favorites, 'favorites'),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.book_fill,
title: t.readingHistory,
onTap: () =>
navigateOrPanel(AppRoutes.history, 'history'),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.moon_fill,
title: t.darkMode,
trailing: const DarkModeSwitch(),
onTap: () {
ref.read(themeModeBounceProvider.notifier).trigger();
navigateOrPanel(
AppRoutes.themeSettings,
'theme_settings',
);
},
),
],
),
),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 100.ms)
.slideY(
begin: 0.05,
end: 0,
duration: 300.ms,
delay: 100.ms,
curve: Curves.easeOutCubic,
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.moon_fill,
title: t.darkMode,
trailing: const DarkModeSwitch(),
onTap: () {
ref.read(themeModeBounceProvider.notifier).trigger();
navigateOrPanel(
AppRoutes.themeSettings,
'theme_settings',
);
},
),
],
),
),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 100.ms)
.slideY(
begin: 0.05,
end: 0,
duration: 300.ms,
delay: 100.ms,
curve: Curves.easeOutCubic,
),
);
}
/// 账号设置 / 数据管理 / 离线模式 / 缓存管理
Widget _buildAccountSection(T t) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: GlassContainer(
depth: GlassDepth.elevated,
padding: EdgeInsets.zero,
child: Column(
children: [
SettingRow(
icon: CupertinoIcons.person_crop_circle,
title: t.profile.accountSettings,
onTap: () => navigateOrPanel(
AppRoutes.accountSettings,
'account_settings',
child:
Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.arrow_down_circle_fill,
title: t.profile.dataManagement,
onTap: () => navigateOrPanel(
AppRoutes.dataManagement,
'data_management',
child: GlassContainer(
depth: GlassDepth.elevated,
padding: EdgeInsets.zero,
child: Column(
children: [
SettingRow(
icon: CupertinoIcons.person_crop_circle,
title: t.profile.accountSettings,
onTap: () => navigateOrPanel(
AppRoutes.accountSettings,
'account_settings',
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.arrow_down_circle_fill,
title: t.profile.dataManagement,
onTap: () => navigateOrPanel(
AppRoutes.dataManagement,
'data_management',
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.antenna_radiowaves_left_right,
title: t.profile.offlineMode,
onTap: () =>
navigateOrPanel(AppRoutes.offline, 'offline_mode'),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.tray_full_fill,
title: t.cacheManagement,
onTap: () => navigateOrPanel(
AppRoutes.cacheManagement,
'cache_management',
),
),
],
),
),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 140.ms)
.slideY(
begin: 0.05,
end: 0,
duration: 300.ms,
delay: 140.ms,
curve: Curves.easeOutCubic,
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.antenna_radiowaves_left_right,
title: t.profile.offlineMode,
onTap: () => navigateOrPanel(
AppRoutes.offline,
'offline_mode',
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.tray_full_fill,
title: t.cacheManagement,
onTap: () => navigateOrPanel(
AppRoutes.cacheManagement,
'cache_management',
),
),
],
),
),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 140.ms)
.slideY(
begin: 0.05,
end: 0,
duration: 300.ms,
delay: 140.ms,
curve: Curves.easeOutCubic,
),
);
}
/// 语言 / 主题定制 / 通用设置 / 桌面小组件 / 句子来源
Widget _buildGeneralSection(T t, GeneralSettingsState settings) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: GlassContainer(
padding: EdgeInsets.zero,
child: Column(
children: [
SettingRow(
icon: CupertinoIcons.globe,
title: t.language,
trailing: Text(
AppLocale.fromId(settings.languageId).nativeName,
style: AppTypography.subhead.copyWith(
color: AppTheme.ext(context).textSecondary,
child:
Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: GlassContainer(
padding: EdgeInsets.zero,
child: Column(
children: [
SettingRow(
icon: CupertinoIcons.globe,
title: t.language,
trailing: Text(
AppLocale.fromId(settings.languageId).nativeName,
style: AppTypography.subhead.copyWith(
color: AppTheme.ext(context).textSecondary,
),
),
onTap: () {
ref.read(languageBounceProvider.notifier).trigger();
navigateOrPanel(
AppRoutes.languageSettings,
'language_settings',
);
},
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.paintbrush_fill,
title: t.themeCustomization,
onTap: () => navigateOrPanel(
AppRoutes.themeSettings,
'theme_settings',
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.gear_solid,
title: t.generalSettings,
onTap: () => navigateOrPanel(
AppRoutes.generalSettings,
'general_settings',
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.square_grid_2x2,
title: t.desktopWidgets,
onTap: () => navigateOrPanel(
AppRoutes.widgetManagement,
'widget_management',
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.book,
title: t.sentenceSource,
onTap: () =>
navigateOrPanel(AppRoutes.source, 'source'),
),
],
),
),
onTap: () {
ref.read(languageBounceProvider.notifier).trigger();
navigateOrPanel(
AppRoutes.languageSettings,
'language_settings',
);
},
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 180.ms)
.slideY(
begin: 0.05,
end: 0,
duration: 300.ms,
delay: 180.ms,
curve: Curves.easeOutCubic,
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.paintbrush_fill,
title: t.themeCustomization,
onTap: () => navigateOrPanel(
AppRoutes.themeSettings,
'theme_settings',
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.gear_solid,
title: t.generalSettings,
onTap: () => navigateOrPanel(
AppRoutes.generalSettings,
'general_settings',
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.square_grid_2x2,
title: t.desktopWidgets,
onTap: () => navigateOrPanel(
AppRoutes.widgetManagement,
'widget_management',
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.book,
title: t.sentenceSource,
onTap: () => navigateOrPanel(
AppRoutes.source,
'source',
),
),
],
),
),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 180.ms)
.slideY(
begin: 0.05,
end: 0,
duration: 300.ms,
delay: 180.ms,
curve: Curves.easeOutCubic,
),
);
}
/// 关于 / 评分 / 实验性功能
Widget _buildAboutSection(T t) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: GlassContainer(
padding: EdgeInsets.zero,
child: Column(
children: [
SettingRow(
icon: CupertinoIcons.info_circle,
title: t.aboutApp,
onTap: () => navigateOrPanel(
AppRoutes.about,
'about',
child:
Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.star_fill,
title: t.rateApp,
onTap: _launchAppStore,
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.lab_flask_solid,
title: t.profile.experimentalFeature,
onTap: () => navigateOrPanel(
AppRoutes.experimentalFeatures,
'experimental_features',
child: GlassContainer(
padding: EdgeInsets.zero,
child: Column(
children: [
SettingRow(
icon: CupertinoIcons.info_circle,
title: t.aboutApp,
onTap: () => navigateOrPanel(AppRoutes.about, 'about'),
),
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.star_fill,
title: t.rateApp,
onTap: _launchAppStore,
),
// 鸿蒙端不显示Beta/实验性功能入口
if (!pu.isOhos) ...[
const SettingsDivider(),
SettingRow(
icon: CupertinoIcons.lab_flask_solid,
title: t.profile.experimentalFeature,
onTap: () => navigateOrPanel(
AppRoutes.experimentalFeatures,
'experimental_features',
),
),
],
],
),
),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 220.ms)
.slideY(
begin: 0.05,
end: 0,
duration: 300.ms,
delay: 220.ms,
curve: Curves.easeOutCubic,
),
],
),
),
)
.animate(onPlay: (c) => c.forward())
.fadeIn(duration: 300.ms, delay: 220.ms)
.slideY(
begin: 0.05,
end: 0,
duration: 300.ms,
delay: 220.ms,
curve: Curves.easeOutCubic,
),
);
}
}

View File

@@ -685,25 +685,18 @@ class FontManagementNotifier extends Notifier<FontManagementState>
}
}
// 路径不可用时,使用 readAsBytes()file_picker 12.x 推荐
// 路径不可用时,使用 bytes 属性读取file_picker 11.x API
if (zipBytes == null) {
try {
zipBytes = await platformFile.readAsBytes();
} catch (e) {
Log.w('ZIP字体导入 readAsBytes 失败', e);
final rawBytes = platformFile.bytes;
if (rawBytes != null && rawBytes.isNotEmpty) {
zipBytes = rawBytes;
}
}
// 最终降级bytes 属性(兼容旧版)
if (zipBytes == null || zipBytes.isEmpty) {
// ignore: deprecated_member_use
final rawBytes = platformFile.bytes;
if (rawBytes == null || rawBytes.isEmpty) {
Log.w('ZIP字体导入跳过: 无可用文件路径或数据');
AppToast.showWarning('无法读取ZIP文件数据');
return;
}
zipBytes = rawBytes;
Log.w('ZIP字体导入跳过: 无可用文件路径或数据');
AppToast.showWarning('无法读取ZIP文件数据');
return;
}
final archive = ZipDecoder().decodeBytes(zipBytes);

View File

@@ -28,6 +28,7 @@ import '../../../../../../shared/widgets/adaptive/adaptive_back_button.dart';
import '../../../../onboarding/presentation/onboarding_page.dart';
import '../../../../../../core/utils/platform/platform_utils.dart' as pu;
import '../../../../../../shared/widgets/containers/glass_container.dart';
import '../../../../../../shared/widgets/feedback/app_toast.dart';
import '../../providers/general_settings_provider.dart';
import '../../providers/plugin_provider.dart';
import '../../../../../../core/providers/split_view_provider.dart';
@@ -566,6 +567,11 @@ class _GeneralSettingsPageState extends ConsumerState<GeneralSettingsPage>
HapticService.selection();
final message =
flag?.unsupportedMessage ?? t.common.featureNotSupported;
// 鸿蒙端不显示弹窗改为toast提示
if (pu.isOhos) {
AppToast.showInfo('敬请期待');
return;
}
showCupertinoDialog<void>(
context: context,
builder: (_) => CupertinoAlertDialog(

View File

@@ -335,24 +335,8 @@ class FontDownloadService {
}
}
// 路径不可用时,使用 readAsBytes() 读取file_picker 12.x 推荐
// 路径不可用时,使用 bytes 属性读取file_picker 11.x API
if (fontBytes == null) {
try {
fontBytes = await platformFile.readAsBytes();
if (fontBytes.isNotEmpty) {
if (await File(destPath).exists()) {
await File(destPath).delete();
}
await File(destPath).writeAsBytes(fontBytes);
}
} catch (e) {
Log.w('字体导入 readAsBytes 失败: $fileName', e);
}
}
// 最终降级:使用 bytes 属性(兼容旧版 file_picker
if (fontBytes == null || fontBytes.isEmpty) {
// ignore: deprecated_member_use
final rawBytes = platformFile.bytes;
if (rawBytes != null && rawBytes.isNotEmpty) {
fontBytes = rawBytes;

View File

@@ -1,15 +1,18 @@
// ============================================================
// 闲言APP — 软件协议页(引导页)
// 创建时间: 2026-05-21
// 更新时间: 2026-06-09
// 更新时间: 2026-06-10
// 作用: 隐私政策/用户协议/权限说明,勾选同意后继续
// 上次更新: 添加滚动进度条导航改用OnboardingNavScope语义索引适配安卓端页面顺序
// 上次更新: Android端协议页改用双按钮同意并继续+拒绝并退出),移除勾选框
// ============================================================
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'
show Colors, Scrollbar, LinearProgressIndicator;
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/constants/character_expression.dart';
@@ -201,9 +204,15 @@ class _AgreementPageState extends ConsumerState<AgreementPage> {
const SizedBox(height: AppSpacing.xs),
Expanded(child: _buildContent(ext, state, ob, languageId)),
const SizedBox(height: AppSpacing.xs),
_buildCheckboxes(ext, state, notifier, ob),
const SizedBox(height: AppSpacing.xs),
_buildContinueButton(ext, state, notifier, ob),
Platform.isAndroid
? _buildAndroidButtons(ext, state, notifier, ob)
: Column(
children: [
_buildCheckboxes(ext, state, notifier, ob),
const SizedBox(height: AppSpacing.xs),
_buildContinueButton(ext, state, notifier, ob),
],
),
SizedBox(height: isLandscape ? AppSpacing.xs : AppSpacing.sm),
],
),
@@ -924,6 +933,61 @@ class _AgreementPageState extends ConsumerState<AgreementPage> {
}
}
/// Android端两个按钮布局同意并继续 + 拒绝并退出),无勾选框
Widget _buildAndroidButtons(
AppThemeExtension ext,
OnboardingState state,
OnboardingNotifier notifier,
TOnboarding ob,
) {
return Column(
children: [
// 同意并继续
SizedBox(
width: double.infinity,
child: CupertinoButton.filled(
borderRadius: AppRadius.xlBorder,
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
onPressed: () {
HapticService.light();
// 自动勾选所有协议
if (!state.privacyAgreed) notifier.togglePrivacy();
if (!state.termsAgreed) notifier.toggleTerms();
if (!state.permissionRead) notifier.togglePermissionRead();
// 跳转到下一页
final nav = OnboardingNavScope.of(context);
nav.goToPage(nav.agreementIndex + 1);
},
child: Text(
ob.agreeAndContinue,
style: AppTypography.headline.copyWith(color: ext.textOnAccent),
),
),
),
const SizedBox(height: AppSpacing.sm),
// 拒绝并退出
SizedBox(
width: double.infinity,
child: CupertinoButton(
borderRadius: AppRadius.xlBorder,
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
color: const Color(0xFFFF3B30).withValues(alpha: 0.12),
onPressed: () {
SystemNavigator.pop();
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
},
child: Text(
ob.rejectAndExit,
style: AppTypography.headline.copyWith(
color: const Color(0xFFFF3B30),
),
),
),
),
],
);
}
Widget _buildCheckboxes(
AppThemeExtension ext,
OnboardingState state,
@@ -1049,24 +1113,47 @@ class _AgreementPageState extends ConsumerState<AgreementPage> {
OnboardingNotifier notifier,
TOnboarding ob,
) {
final enabled = state.canProceedAgreement;
final agreed = state.canProceedAgreement;
if (agreed) {
// 已勾选两个协议 → 显示"同意并继续"
return SizedBox(
width: double.infinity,
child: CupertinoButton.filled(
borderRadius: AppRadius.xlBorder,
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
onPressed: () {
HapticService.light();
// 跳转到协议页的下一页(安卓端是欢迎页,其他端是个性化页)
final nav = OnboardingNavScope.of(context);
nav.goToPage(nav.agreementIndex + 1);
},
child: Text(
ob.agreeAndContinue,
style: AppTypography.headline.copyWith(color: ext.textOnAccent),
),
),
);
}
// 未勾选协议 → 显示"拒绝并退出"(红色警告按钮)
return SizedBox(
width: double.infinity,
child: CupertinoButton.filled(
child: CupertinoButton(
borderRadius: AppRadius.xlBorder,
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
onPressed: enabled
? () {
HapticService.light();
// 跳转到协议页的下一页(安卓端是欢迎页,其他端是个性化页)
final nav = OnboardingNavScope.of(context);
nav.goToPage(nav.agreementIndex + 1);
}
: null,
color: const Color(0xFFFF3B30).withValues(alpha: 0.12),
onPressed: () {
// 退出应用
SystemNavigator.pop();
if (Platform.isAndroid) {
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
}
},
child: Text(
ob.agreeAndContinue,
ob.rejectAndExit,
style: AppTypography.headline.copyWith(
color: enabled ? ext.textOnAccent : ext.textDisabled,
color: const Color(0xFFFF3B30),
),
),
),

View File

@@ -1,11 +1,14 @@
// ============================================================
// 闲言APP — 引导页状态管理
// 创建时间: 2026-05-21
// 更新时间: 2026-06-09
// 更新时间: 2026-06-10
// 作用: 管理引导页流程状态(页面切换、协议勾选、个性化设置)
// 上次更新: 添加totalPages动态页数支持适配安卓端页面顺序调整
// 上次更新: 协议同意后通知原生端注册敏感插件(传感器/麦克风/位置等)
// ============================================================
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/services/post_agreement_initializer.dart';
@@ -158,6 +161,9 @@ class OnboardingNotifier extends Notifier<OnboardingState> {
await appBox?.flush();
} catch (_) {}
// 通知原生端协议已同意,注册之前跳过的敏感插件(传感器/麦克风/位置等)
await _notifyNativeAgreementAccepted();
Log.i('Onboarding: completeOnboarding ✓ 用户已同意协议,开始初始化权限敏感服务');
await PostAgreementInitializer.init();
Log.i('Onboarding: 权限敏感服务初始化完成 ✓');
@@ -167,6 +173,19 @@ class OnboardingNotifier extends Notifier<OnboardingState> {
state = state.copyWith(isCompleting: false);
}
}
/// 通知原生端协议已同意Android端补充注册敏感插件
static const _agreementChannel = MethodChannel('apps.xy.xianyan/agreement');
Future<void> _notifyNativeAgreementAccepted() async {
if (!Platform.isAndroid) return;
try {
await _agreementChannel.invokeMethod<bool>('markAgreementAccepted');
Log.i('Onboarding: 原生端协议同意通知成功 ✓');
} catch (e) {
Log.e('Onboarding: 原生端协议同意通知失败', e);
}
}
}
final onboardingProvider =

View File

@@ -49,7 +49,10 @@ class _WidgetManagementPageState extends ConsumerState<WidgetManagementPage> {
}
/// 开发中弹窗提示,支持"不再提醒"
/// 鸿蒙端不显示此弹窗
void _showDevDialogIfNeeded() {
// 鸿蒙端不显示Beta弹窗
if (PlatformHelper.isHarmonyOS) return;
final dismissed = KvStorage.getBool('widget_dev_dismissed') ?? false;
if (dismissed) return;
showCupertinoDialog<void>(
@@ -255,6 +258,11 @@ class _WidgetManagementPageState extends ConsumerState<WidgetManagementPage> {
};
void _handleAddWidget(WidgetType type) async {
// 鸿蒙端点击添加弹出toast提示
if (PlatformHelper.isHarmonyOS) {
AppToast.showInfo('敬请期待');
return;
}
if (_isAdding) return;
_isAdding = true;