1.开始录制、结束录制增加
2. 增加电量检测、内存检查,是否低于 10%
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:recording_tool/core/platform/device_health_snapshot.dart';
|
||||
|
||||
class AppPackageInfo {
|
||||
const AppPackageInfo({
|
||||
@@ -75,4 +76,11 @@ class AppPlatformInfo {
|
||||
);
|
||||
return AppDeviceInfo.fromMap(result ?? const <Object?, Object?>{});
|
||||
}
|
||||
|
||||
static Future<DeviceHealthSnapshot> deviceHealth() async {
|
||||
final result = await _channel.invokeMapMethod<Object?, Object?>(
|
||||
'deviceHealth',
|
||||
);
|
||||
return DeviceHealthSnapshot.fromMap(result ?? const <Object?, Object?>{});
|
||||
}
|
||||
}
|
||||
|
||||
25
lib/core/platform/device_health_checker.dart
Normal file
25
lib/core/platform/device_health_checker.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:recording_tool/core/platform/device_health_snapshot.dart';
|
||||
|
||||
class DeviceHealthChecker {
|
||||
DeviceHealthChecker._();
|
||||
|
||||
static const int thresholdPercent = 10;
|
||||
|
||||
static const String lowBatteryMessage = '电量低于10%,请充电';
|
||||
static const String lowStorageMessage = '内存低于10%,请清理内存';
|
||||
|
||||
static List<String> warningLines(DeviceHealthSnapshot snapshot) {
|
||||
final lines = <String>[];
|
||||
|
||||
final battery = snapshot.batteryLevelPercent;
|
||||
if (battery != null && battery < thresholdPercent) {
|
||||
lines.add(lowBatteryMessage);
|
||||
}
|
||||
|
||||
if (snapshot.storageAvailablePercent < thresholdPercent) {
|
||||
lines.add(lowStorageMessage);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
30
lib/core/platform/device_health_snapshot.dart
Normal file
30
lib/core/platform/device_health_snapshot.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
class DeviceHealthSnapshot {
|
||||
const DeviceHealthSnapshot({
|
||||
this.batteryLevelPercent,
|
||||
required this.storageAvailablePercent,
|
||||
});
|
||||
|
||||
factory DeviceHealthSnapshot.fromMap(Map<Object?, Object?> map) {
|
||||
final batteryRaw = map['batteryLevelPercent'];
|
||||
int? batteryLevelPercent;
|
||||
if (batteryRaw is int) {
|
||||
batteryLevelPercent = batteryRaw;
|
||||
} else if (batteryRaw is num) {
|
||||
batteryLevelPercent = batteryRaw.round();
|
||||
}
|
||||
|
||||
final storageRaw = map['storageAvailablePercent'];
|
||||
final storageAvailablePercent = switch (storageRaw) {
|
||||
final num value => value.toDouble(),
|
||||
_ => 100.0,
|
||||
};
|
||||
|
||||
return DeviceHealthSnapshot(
|
||||
batteryLevelPercent: batteryLevelPercent,
|
||||
storageAvailablePercent: storageAvailablePercent,
|
||||
);
|
||||
}
|
||||
|
||||
final int? batteryLevelPercent;
|
||||
final double storageAvailablePercent;
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:recording_tool/core/platform/app_platform_info.dart';
|
||||
import 'package:recording_tool/core/platform/device_health_checker.dart';
|
||||
import 'package:recording_tool/core/utils/date_time_formatter.dart';
|
||||
import 'package:recording_tool/features/recording/model/model_recording.dart';
|
||||
import 'package:recording_tool/features/recording/recording_display_name.dart';
|
||||
@@ -33,7 +35,20 @@ class _RecordingPageState extends ConsumerState<RecordingPage> {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _bootstrap());
|
||||
}
|
||||
|
||||
Future<void> _checkAndShowDeviceHealthAlerts() async {
|
||||
final snapshot = await AppPlatformInfo.deviceHealth();
|
||||
if (!mounted) return;
|
||||
|
||||
final lines = DeviceHealthChecker.warningLines(snapshot);
|
||||
if (lines.isEmpty) return;
|
||||
|
||||
await AppDialog.deviceHealthAlert(context, lines: lines);
|
||||
}
|
||||
|
||||
Future<void> _bootstrap() async {
|
||||
await _checkAndShowDeviceHealthAlerts();
|
||||
if (!mounted) return;
|
||||
|
||||
final clipboardResult = await ref
|
||||
.read(recordingViewModelProvider.notifier)
|
||||
.getClipboardContent();
|
||||
@@ -95,6 +110,8 @@ class _RecordingPageState extends ConsumerState<RecordingPage> {
|
||||
await _showNoPlayerInfoDialog();
|
||||
return;
|
||||
}
|
||||
await _checkAndShowDeviceHealthAlerts();
|
||||
if (!mounted) return;
|
||||
await ref.read(recordingSessionControllerProvider.notifier).startRecording();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'dart:io';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:recording_tool/core/permission/permission_service.dart';
|
||||
import 'package:recording_tool/core/utils/rate_limiter.dart';
|
||||
import 'package:recording_tool/features/recording/recording_display_name.dart';
|
||||
import 'package:recording_tool/features/recording/recording_platform.dart';
|
||||
import 'package:recording_tool/features/recording/view-model/view_model_recording.dart';
|
||||
@@ -95,7 +96,12 @@ final recordingSessionControllerProvider =
|
||||
);
|
||||
|
||||
class RecordingSessionController extends Notifier<RecordingSessionState> {
|
||||
static const Duration _recordingActionInterval = Duration(milliseconds: 300);
|
||||
static const Object _startRecordingThrottleKey = 'recording.session.start';
|
||||
static const Object _stopRecordingThrottleKey = 'recording.session.stop';
|
||||
|
||||
StreamSubscription<RecordingStatus>? _statusSubscription;
|
||||
final _rateLimit = RateLimitHub();
|
||||
|
||||
@override
|
||||
RecordingSessionState build() {
|
||||
@@ -207,7 +213,21 @@ class RecordingSessionController extends Notifier<RecordingSessionState> {
|
||||
return _galleryPermissions().isEmpty;
|
||||
}
|
||||
|
||||
bool _tryAcquireRecordingAction(Object key) {
|
||||
var executed = false;
|
||||
_rateLimit.throttle<void>(
|
||||
key: key,
|
||||
value: null,
|
||||
duration: _recordingActionInterval,
|
||||
options: const ThrottleOptions(leading: true, trailing: false),
|
||||
onCallback: (_) => executed = true,
|
||||
);
|
||||
return executed;
|
||||
}
|
||||
|
||||
Future<void> startRecording({bool enableDoNotDisturb = true}) async {
|
||||
if (!_tryAcquireRecordingAction(_startRecordingThrottleKey)) return;
|
||||
|
||||
if (!state.isPreviewReady ||
|
||||
state.isRecording ||
|
||||
state.isStartingRecording) {
|
||||
@@ -239,6 +259,8 @@ class RecordingSessionController extends Notifier<RecordingSessionState> {
|
||||
}
|
||||
|
||||
Future<void> stopRecording() async {
|
||||
if (!_tryAcquireRecordingAction(_stopRecordingThrottleKey)) return;
|
||||
|
||||
if (!state.isRecording) return;
|
||||
|
||||
try {
|
||||
@@ -302,6 +324,7 @@ class RecordingSessionController extends Notifier<RecordingSessionState> {
|
||||
}
|
||||
|
||||
Future<void> _dispose() async {
|
||||
_rateLimit.clear();
|
||||
await _statusSubscription?.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,4 +30,24 @@ class AppDialog {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> deviceHealthAlert(
|
||||
BuildContext context, {
|
||||
required List<String> lines,
|
||||
}) {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertDialog(
|
||||
content: Text(lines.join('\n')),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(),
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user