规范化代码结构

This commit is contained in:
2026-06-05 12:07:29 +08:00
parent 1e936bfc12
commit f6440ea8b7
17 changed files with 494 additions and 481 deletions

View File

@@ -0,0 +1,5 @@
abstract final class RecordingChannelNames {
static const packageName = 'com.qxy.dronex';
static const method = '$packageName/recording';
static const events = '$packageName/recording_events';
}

View File

@@ -0,0 +1,189 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:recording_tool/features/recording/platform/recording_channel_names.dart';
enum RecordingState {
idle,
previewing,
recording,
stopping,
error;
static RecordingState fromRaw(String? raw) {
return RecordingState.values.firstWhere(
(value) => value.name == raw,
orElse: () => RecordingState.idle,
);
}
}
class RecordingStatus {
const RecordingStatus({
required this.state,
this.outputPath,
this.elapsedMillis = 0,
this.message,
});
final RecordingState state;
final String? outputPath;
final int elapsedMillis;
final String? message;
factory RecordingStatus.fromMap(Map<dynamic, dynamic> map) {
return RecordingStatus(
state: RecordingState.fromRaw(map['state'] as String?),
outputPath: map['outputPath'] as String?,
elapsedMillis: (map['elapsedMillis'] as num?)?.toInt() ?? 0,
message: map['message'] as String?,
);
}
bool get isRecording => state == RecordingState.recording;
}
class RecordingPlatform {
RecordingPlatform._();
static const MethodChannel _channel = MethodChannel(
RecordingChannelNames.method,
);
static const EventChannel _events = EventChannel(
RecordingChannelNames.events,
);
static bool get isSupported =>
supportsHost(isAndroid: Platform.isAndroid, isIOS: Platform.isIOS);
static bool supportsHost({required bool isAndroid, required bool isIOS}) {
return isAndroid || isIOS;
}
static Stream<RecordingStatus>? _statusStream;
static Stream<RecordingStatus> statusStream() {
if (!isSupported) {
return const Stream.empty();
}
_statusStream ??= _events.receiveBroadcastStream().map(
(event) =>
RecordingStatus.fromMap(Map<dynamic, dynamic>.from(event as Map)),
);
return _statusStream!;
}
static Future<RecordingStatus> initializePreview() async {
final result = await _channel.invokeMapMethod<String, dynamic>(
'initializePreview',
);
return RecordingStatus.fromMap(result ?? const {});
}
static Future<RecordingStartResult> startRecording({
bool withAudio = true,
bool enableDoNotDisturb = true,
String? displayName,
}) async {
final args = <String, dynamic>{
'withAudio': withAudio,
'enableDoNotDisturb': enableDoNotDisturb,
};
if (displayName != null) {
args['displayName'] = displayName;
}
final result = await _channel.invokeMapMethod<String, dynamic>(
'startRecording',
args,
);
return RecordingStartResult(
outputPath: result?['outputPath'] as String?,
status: RecordingStatus.fromMap(
Map<dynamic, dynamic>.from(result?['status'] as Map? ?? const {}),
),
);
}
static Future<RecordingStopResult> stopRecording() async {
final result = await _channel.invokeMapMethod<String, dynamic>(
'stopRecording',
);
return RecordingStopResult.fromMap(result);
}
static Future<void> disposePreview() =>
_channel.invokeMethod('disposePreview');
static Future<bool> hasNotificationPolicyAccess() async {
return await _channel.invokeMethod<bool>('hasNotificationPolicyAccess') ??
false;
}
static Future<void> openNotificationPolicySettings() {
return _channel.invokeMethod('openNotificationPolicySettings');
}
static Future<bool> enableDoNotDisturb() async {
return await _channel.invokeMethod<bool>('enableDoNotDisturb') ?? false;
}
static Future<void> disableDoNotDisturb() {
return _channel.invokeMethod('disableDoNotDisturb');
}
static Future<bool> isIgnoringBatteryOptimizations() async {
return await _channel.invokeMethod<bool>(
'isIgnoringBatteryOptimizations',
) ??
true;
}
static Future<void> openBatteryOptimizationSettings() {
return _channel.invokeMethod('openBatteryOptimizationSettings');
}
static Future<void> setImmersiveMode({required bool enabled}) {
return _channel.invokeMethod('setImmersiveMode', <String, dynamic>{
'enabled': enabled,
});
}
static Future<RecordingStatus> getStatus() async {
final result = await _channel.invokeMapMethod<String, dynamic>('getStatus');
return RecordingStatus.fromMap(result ?? const {});
}
}
class RecordingStartResult {
const RecordingStartResult({this.outputPath, required this.status});
final String? outputPath;
final RecordingStatus status;
}
class RecordingStopResult {
const RecordingStopResult({
this.outputPath,
required this.status,
this.gallerySaved = true,
this.galleryErrorMessage,
});
final String? outputPath;
final RecordingStatus status;
final bool gallerySaved;
final String? galleryErrorMessage;
factory RecordingStopResult.fromMap(Map<String, dynamic>? result) {
return RecordingStopResult(
outputPath: result?['outputPath'] as String?,
status: RecordingStatus.fromMap(
Map<dynamic, dynamic>.from(result?['status'] as Map? ?? const {}),
),
gallerySaved: result?['gallerySaved'] as bool? ?? true,
galleryErrorMessage: result?['galleryErrorMessage'] as String?,
);
}
}