Files
photo-live/apps/mobile/lib/core/notification_permission.dart
T
浅念 955f9b13c1 feat(mobile): upload queue ui and continuous capture page
Task 9 (P3 plan): queue tab replaces the stub, continuous capture page
enqueues per shot, album detail gains an upload FAB, and the upload
engine lifecycle is now bound to the login session.

- Engine scope: session account drives UploadEngine start/stop; wired
  from app boot, login success, logout, and 401 eviction. Tasks stay in
  the store across logout (accountId hard isolation).
- Queue tab: album x state x time filter bar, task rows with thumbnail,
  state chip, localized error copy, attempt count, per-row progress via
  a broadcast progress hub, state-dependent actions (re-pick retry,
  retryNow, cancel, delete), batch retry bar, 200ms-debounced live
  refresh from drift table updates.
- Capture page: camera preview via injected CaptureSource, one enqueue
  per shutter press, persistent album/category/badges overlay with a
  switch-only-affects-new-shots hint, gallery multi-pick, exit confirm
  while uploads are pending, permission-denied and init-failure views.
- POST_NOTIFICATIONS runtime permission via a small MethodChannel
  (photolive/permissions) + PermissionPlugin.kt: single runtime
  permission need, avoids permission_handler dependency; requested once
  on first enqueue, silent degradation when denied or unavailable.
- Album detail FAB: capture-and-upload / pick-from-gallery sheet.
- l10n: ~50 new keys in zh/en arbs; tests: +45 (filter matrix, engine
  scope lifecycle, permission gate, queue tab, capture page, FAB entry);
  existing page tests updated to inject session/task-store fakes.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-19 04:47:31 +08:00

58 lines
2.4 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// POST_NOTIFICATIONS 运行时权限门(P3 计划 Task 9)。
///
/// Android 13+(API 33)起,上传前台服务通知需运行时授权;未授权时
/// 传输继续、仅通知不显示——**静默降级,永不阻断入队**(技术方案 §6.3)。
///
/// 实现取舍(有意不引入 permission_handler):本 App 全部运行时权限需求
/// 仅此一项(camera/image_picker 插件自管权限),为单个权限引入
/// permission_handler 全家桶(AndroidX 传递依赖 + iOS Podfile 宏配置)
/// 不成比例——复用既有原生桥接模式(Task 7 UploadPlugin 同款),自建
/// MethodChannel `photolive/permissions`(MainActivity 内 PermissionPlugin
/// 约 60 行 Kotlin,SDK<33 直接返回 granted)。后续再增权限需求时评估
/// 插件化。
///
/// 进程内单次语义:首次入队动作(连拍/相册选择)时请求一次,之后不再
/// 打扰;拒绝或通道失败(iOS/桌面无此通道)一律静默。
library;
import 'dart:io';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
/// 通知权限门(注入 [channel]/[isAndroid] 供测试)。
class NotificationPermission {
NotificationPermission({
MethodChannel? channel,
bool? isAndroid,
}) : _channel = channel ?? const MethodChannel(kChannelName),
_isAndroid = isAndroid ?? (!kIsWeb && Platform.isAndroid);
/// 平台通道名(与 Kotlin `PermissionPlugin.CHANNEL` 一致)。
static const String kChannelName = 'photolive/permissions';
final MethodChannel _channel;
final bool _isAndroid;
bool _requested = false;
/// 确保已请求一次(首次入队动作调用;幂等)。
///
/// 非 Android 平台直接跳过(iOS 后台传输不依赖通知权限);通道调用
/// 失败/拒绝静默吞掉——上传链路不因通知权限降级而中断。
Future<void> ensureRequested() async {
if (_requested) return;
_requested = true;
if (!_isAndroid) return;
try {
await _channel.invokeMethod<String>('requestPostNotifications');
} on Object {
// 通道缺失(iOS/桌面)或请求异常:静默降级。
}
}
}
/// 生产 Provider(队列/连拍页经 ref.read 一次性调用)。
final notificationPermissionProvider =
Provider<NotificationPermission>((ref) => NotificationPermission());