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:
@@ -1,9 +1,9 @@
|
||||
/// ============================================================
|
||||
/// 闲言APP — 剪贴板链接监控服务
|
||||
/// 创建时间: 2026-05-15
|
||||
/// 更新时间: 2026-05-15
|
||||
/// 作用: 自动检测剪贴板中的URL链接并提示保存到稍后读
|
||||
/// 上次更新: E10 初始创建,支持3秒轮询剪贴板+URL检测+隐私保护
|
||||
/// 更新时间: 2026-06-10
|
||||
/// 作用: 被动检测剪贴板中的URL链接并提示保存到稍后读
|
||||
/// 上次更新: 移除定时轮询,改为被动触发(隐私合规),仅用户主动操作时检查
|
||||
/// ============================================================
|
||||
|
||||
import 'package:xianyan/core/utils/data/pattern_utils.dart';
|
||||
@@ -23,50 +23,44 @@ class ClipboardMonitorService {
|
||||
|
||||
static const _keyEnabled = 'clipboard_monitor_enabled';
|
||||
static const _readlaterConvId = 'readlater';
|
||||
static const _monitorInterval = Duration(seconds: 3);
|
||||
|
||||
String? _lastClipboardText;
|
||||
Timer? _monitorTimer;
|
||||
bool _enabled = false;
|
||||
DateTime? _lastCheckTime;
|
||||
|
||||
bool get isEnabled => _enabled;
|
||||
|
||||
// ============================================================
|
||||
// 启动监控
|
||||
// 启用/禁用监控(仅设置标志,不启动定时器)
|
||||
// ============================================================
|
||||
|
||||
void startMonitor() {
|
||||
if (_enabled) return;
|
||||
|
||||
_enabled = true;
|
||||
_monitorTimer?.cancel();
|
||||
_monitorTimer = Timer.periodic(_monitorInterval, (_) {
|
||||
_checkClipboard();
|
||||
});
|
||||
|
||||
Log.i('ClipboardMonitor: 剪贴板监控已启动 (每3秒检查)');
|
||||
Log.i('ClipboardMonitor: 剪贴板监控已启用(被动模式,不主动轮询)');
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 停止监控
|
||||
// ============================================================
|
||||
|
||||
void stopMonitor() {
|
||||
_monitorTimer?.cancel();
|
||||
_monitorTimer = null;
|
||||
_enabled = false;
|
||||
_lastClipboardText = null;
|
||||
|
||||
Log.i('ClipboardMonitor: 剪贴板监控已停止');
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 检查剪贴板
|
||||
// 被动检查剪贴板 — 仅在用户主动操作时调用
|
||||
// 如:打开稍后读页面、从其他App返回时用户在首页
|
||||
// ============================================================
|
||||
|
||||
Future<void> _checkClipboard() async {
|
||||
Future<void> checkClipboardOnce() async {
|
||||
if (!_enabled) return;
|
||||
|
||||
// 防抖:5秒内不重复检查
|
||||
final now = DateTime.now();
|
||||
if (_lastCheckTime != null &&
|
||||
now.difference(_lastCheckTime!).inSeconds < 5) {
|
||||
return;
|
||||
}
|
||||
_lastCheckTime = now;
|
||||
|
||||
try {
|
||||
final clipboardData = await Clipboard.getData(Clipboard.kTextPlain);
|
||||
final text = clipboardData?.text;
|
||||
@@ -141,13 +135,14 @@ class ClipboardMonitorService {
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 初始化 — 根据存储的开关状态决定是否启动
|
||||
// 初始化 — 根据存储的开关状态设置标志(不启动定时器)
|
||||
// ============================================================
|
||||
|
||||
Future<void> initFromStore() async {
|
||||
final stored = KvStorage.getBool(_keyEnabled) ?? false;
|
||||
if (stored) {
|
||||
startMonitor();
|
||||
_enabled = true;
|
||||
Log.i('ClipboardMonitor: 已从存储恢复启用状态(被动模式)');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import 'package:hive_flutter/hive_flutter.dart';
|
||||
import '../../network/api_client.dart';
|
||||
import '../../storage/kv_storage.dart';
|
||||
import '../../utils/logger.dart';
|
||||
import '../../utils/platform/platform_utils.dart' as pu;
|
||||
import '../../../shared/widgets/feedback/app_toast.dart';
|
||||
|
||||
// ============================================================
|
||||
// 本地功能标志枚举(原有,保持向后兼容)
|
||||
@@ -159,8 +161,8 @@ class FeatureFlagNotifier extends Notifier<FeatureFlagState> {
|
||||
|
||||
final featureFlagProvider =
|
||||
NotifierProvider<FeatureFlagNotifier, FeatureFlagState>(
|
||||
FeatureFlagNotifier.new,
|
||||
);
|
||||
FeatureFlagNotifier.new,
|
||||
);
|
||||
|
||||
// ============================================================
|
||||
// 远程功能标志状态枚举
|
||||
@@ -226,9 +228,12 @@ class FeatureFlagItem {
|
||||
name: json['name'] as String? ?? '',
|
||||
description: json['description'] as String? ?? '',
|
||||
enabled: json['enabled'] as bool? ?? false,
|
||||
status: FeatureFlagStatus.fromId(json['status'] as String? ?? 'developing'),
|
||||
status: FeatureFlagStatus.fromId(
|
||||
json['status'] as String? ?? 'developing',
|
||||
),
|
||||
progress: (json['progress'] as num?)?.toDouble() ?? 0.0,
|
||||
rolloutPercentage: (json['rollout_percentage'] as num?)?.toDouble() ?? 1.0,
|
||||
rolloutPercentage:
|
||||
(json['rollout_percentage'] as num?)?.toDouble() ?? 1.0,
|
||||
targetGroup: json['target_group'] as String?,
|
||||
expiresAt: json['expires_at'] != null
|
||||
? DateTime.tryParse(json['expires_at'] as String)
|
||||
@@ -281,7 +286,8 @@ class FeatureFlagItem {
|
||||
class RemoteFeatureFlagService {
|
||||
RemoteFeatureFlagService._();
|
||||
|
||||
static final RemoteFeatureFlagService _instance = RemoteFeatureFlagService._();
|
||||
static final RemoteFeatureFlagService _instance =
|
||||
RemoteFeatureFlagService._();
|
||||
static RemoteFeatureFlagService get instance => _instance;
|
||||
|
||||
static const String _cacheKey = 'remote_flags';
|
||||
@@ -441,6 +447,12 @@ class FeatureFlagService {
|
||||
static bool check(BuildContext context, FeatureFlag flag) {
|
||||
if (flag.isEnabled) return true;
|
||||
|
||||
// 鸿蒙端不显示弹窗,改为toast提示
|
||||
if (pu.isOhos) {
|
||||
AppToast.showInfo('敬请期待');
|
||||
return false;
|
||||
}
|
||||
|
||||
showCupertinoDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => CupertinoAlertDialog(
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
// 创建时间: 2026-05-22
|
||||
// 更新时间: 2026-06-10
|
||||
// 作用: 统一管理前后台切换时暂停/恢复后台服务和动画
|
||||
// 上次更新: 移除ShakeDetector相关代码
|
||||
// 上次更新: 移除剪贴板监控自动恢复(改为被动触发)
|
||||
// ============================================================
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:xianyan/core/services/performance/performance_orchestrator.dart';
|
||||
import 'package:xianyan/core/services/clipboard_monitor_service.dart';
|
||||
import 'package:xianyan/core/services/device/battery_info_service.dart';
|
||||
import 'package:xianyan/core/utils/logger.dart';
|
||||
|
||||
@@ -43,7 +42,6 @@ mixin AppLifecycleGate on WidgetsBindingObserver {
|
||||
Log.i('AppLifecycleGate: 进入后台 — 暂停服务和动画');
|
||||
PerformanceOrchestrator.instance.onAppPaused();
|
||||
|
||||
ClipboardMonitorService.instance.stopMonitor();
|
||||
BatteryInfoService.instance.pausePolling();
|
||||
}
|
||||
|
||||
@@ -51,13 +49,7 @@ mixin AppLifecycleGate on WidgetsBindingObserver {
|
||||
Log.i('AppLifecycleGate: 回到前台 — 恢复服务和动画');
|
||||
PerformanceOrchestrator.instance.onAppResumed();
|
||||
|
||||
try {
|
||||
if (ClipboardMonitorService.instance.isEnabled) {
|
||||
ClipboardMonitorService.instance.startMonitor();
|
||||
}
|
||||
} catch (e) {
|
||||
Log.e('AppLifecycleGate: 恢复剪贴板监控失败', e);
|
||||
}
|
||||
// 剪贴板监控已改为被动触发,不再自动恢复轮询
|
||||
try {
|
||||
BatteryInfoService.instance.resumePolling();
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// ============================================================
|
||||
// 闲言APP — 协议同意后初始化器
|
||||
// 创建时间: 2026-05-30
|
||||
// 更新时间: 2026-06-06
|
||||
// 更新时间: 2026-06-10
|
||||
// 作用: 将权限敏感的服务初始化延迟到用户同意协议后执行
|
||||
// 上次更新: 新增iOS ATT(App Tracking Transparency)授权请求
|
||||
// 上次更新: 移除ClipboardMonitorService(改为被动触发,不在此初始化)
|
||||
// ============================================================
|
||||
|
||||
import 'dart:io';
|
||||
@@ -16,7 +16,6 @@ import '../utils/platform/platform_utils.dart' as pu;
|
||||
import '../router/app_router.dart' show rootNavigatorKey;
|
||||
import 'auth/permission_service.dart';
|
||||
import 'network/connectivity_service.dart';
|
||||
import 'clipboard_monitor_service.dart';
|
||||
import 'background/background_task_service.dart';
|
||||
import 'notification/local_notification_service.dart';
|
||||
import 'device/screen_wake_service.dart';
|
||||
@@ -43,9 +42,11 @@ class PostAgreementInitializer {
|
||||
// 先检查当前授权状态,仅在未决定时请求,避免Info.plist缺少描述时原生崩溃
|
||||
if (Platform.isIOS) {
|
||||
try {
|
||||
final status = await AppTrackingTransparency.trackingAuthorizationStatus;
|
||||
final status =
|
||||
await AppTrackingTransparency.trackingAuthorizationStatus;
|
||||
if (status == TrackingStatus.notDetermined) {
|
||||
final authorized = await PermissionService.requestTrackingPermission();
|
||||
final authorized =
|
||||
await PermissionService.requestTrackingPermission();
|
||||
Log.i('iOS ATT授权结果: ${authorized ? "已授权" : "未授权"}');
|
||||
} else {
|
||||
Log.i('iOS ATT已授权状态: $status,跳过请求');
|
||||
@@ -117,14 +118,8 @@ class PostAgreementInitializer {
|
||||
}
|
||||
}
|
||||
|
||||
if (!pu.isWeb) {
|
||||
try {
|
||||
await ClipboardMonitorService.instance.initFromStore();
|
||||
Log.i('剪贴板监控服务初始化完成');
|
||||
} catch (e, st) {
|
||||
Log.e('剪贴板监控服务初始化失败', e, st);
|
||||
}
|
||||
}
|
||||
// 剪贴板监控服务已改为被动触发模式,不在此初始化
|
||||
// 仅在用户主动操作时(如打开稍后读页面)才检查剪贴板
|
||||
|
||||
if (!pu.isWeb) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user