From 1b404525d2c5df1a096179a3c3f2bb960637cb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E9=94=8B?= <2535831261@qq.com> Date: Thu, 4 Jun 2026 16:58:34 +0800 Subject: [PATCH] =?UTF-8?q?1.=E6=9B=B4=E6=8D=A2=E5=8C=85=E5=90=8D=202.?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=BD=95=E5=88=B6=E9=A1=B5=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E4=B8=8B=E6=96=B9=E6=97=B6=E9=97=B4=E4=B8=BA=E5=BD=93=E5=89=8D?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E8=AF=BB=E7=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 2 +- .../{gdfw/fxjk => qxy/dronex}/AppConstants.kt | 0 .../{gdfw/fxjk => qxy/dronex}/MainActivity.kt | 0 .../recording/BatteryOptimizationHelper.kt | 0 .../dronex}/recording/DoNotDisturbHelper.kt | 0 .../recording/RecordingCameraController.kt | 0 .../recording/RecordingForegroundService.kt | 0 .../recording/RecordingOutputFactory.kt | 2 +- .../recording/RecordingPlatformHandler.kt | 0 .../recording/RecordingPreviewFactory.kt | 0 .../dronex}/recording/RecordingSession.kt | 0 .../dronex}/recording/RecordingState.kt | 0 ios/Podfile.lock | 8 +- ios/Runner.xcodeproj/project.pbxproj | 38 +++----- ios/Runner/Info.plist | 4 +- lib/app/config/app_config.dart | 2 +- lib/features/recording/recording_page.dart | 89 ++++++++++++------- 17 files changed, 77 insertions(+), 68 deletions(-) rename android/app/src/main/kotlin/com/{gdfw/fxjk => qxy/dronex}/AppConstants.kt (100%) rename android/app/src/main/kotlin/com/{gdfw/fxjk => qxy/dronex}/MainActivity.kt (100%) rename android/app/src/main/kotlin/com/{gdfw/fxjk => qxy/dronex}/recording/BatteryOptimizationHelper.kt (100%) rename android/app/src/main/kotlin/com/{gdfw/fxjk => qxy/dronex}/recording/DoNotDisturbHelper.kt (100%) rename android/app/src/main/kotlin/com/{gdfw/fxjk => qxy/dronex}/recording/RecordingCameraController.kt (100%) rename android/app/src/main/kotlin/com/{gdfw/fxjk => qxy/dronex}/recording/RecordingForegroundService.kt (100%) rename android/app/src/main/kotlin/com/{gdfw/fxjk => qxy/dronex}/recording/RecordingOutputFactory.kt (95%) rename android/app/src/main/kotlin/com/{gdfw/fxjk => qxy/dronex}/recording/RecordingPlatformHandler.kt (100%) rename android/app/src/main/kotlin/com/{gdfw/fxjk => qxy/dronex}/recording/RecordingPreviewFactory.kt (100%) rename android/app/src/main/kotlin/com/{gdfw/fxjk => qxy/dronex}/recording/RecordingSession.kt (100%) rename android/app/src/main/kotlin/com/{gdfw/fxjk => qxy/dronex}/recording/RecordingState.kt (100%) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 267c67d..6f3eebb 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -19,7 +19,7 @@ android:required="true" /> /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 62D3774184B6C76B8E34C8C2 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -392,6 +377,7 @@ files = ( 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 8D5D9D9E2C69000100F10001 /* RecordingPlugin.swift in Sources */, + 8E5D9D9E2C69000100F10002 /* PlatformInfoPlugin.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -492,7 +478,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterTemplate; + PRODUCT_BUNDLE_IDENTIFIER = com.qxy.dronex; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -509,7 +495,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterTemplate.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.qxy.dronex.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -527,7 +513,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterTemplate.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.qxy.dronex.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -543,7 +529,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterTemplate.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.qxy.dronex.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -675,7 +661,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterTemplate; + PRODUCT_BUNDLE_IDENTIFIER = com.qxy.dronex; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -698,7 +684,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterTemplate; + PRODUCT_BUNDLE_IDENTIFIER = com.qxy.dronex; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index f54ad50..e691476 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -7,7 +7,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - 飞行极控 + 飞行极控录像工作台 CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -15,7 +15,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - 飞行极控 + 飞行极控录像工作台 CFBundlePackageType APPL CFBundleShortVersionString diff --git a/lib/app/config/app_config.dart b/lib/app/config/app_config.dart index 91cc51e..72fcb97 100644 --- a/lib/app/config/app_config.dart +++ b/lib/app/config/app_config.dart @@ -21,7 +21,7 @@ class AppConfig { static late EnvironmentValues current; static AppPackageInfo? packageInfo; - static const appName = '飞行极控'; + static const appName = '飞行极控录像工作台'; static const designSize = Size(375, 812); static void configure({ diff --git a/lib/features/recording/recording_page.dart b/lib/features/recording/recording_page.dart index 2c949a1..d8c76a5 100644 --- a/lib/features/recording/recording_page.dart +++ b/lib/features/recording/recording_page.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; @@ -56,25 +57,6 @@ class _RecordingPageState extends ConsumerState { _immersiveApplied = true; } - String _clipboardHintLabel(RecordingModel recordingInfo) { - if (!recordingInfo.hasValidClipboardInfo) return ''; - final clip = recordingInfo.clipboardRecordingModel; - final lines = []; - final address = clip.address.trim(); - if (address.isNotEmpty) { - lines.add(address); - } - if (clip.startTimestamp > 0) { - final startTime = DateTime.fromMillisecondsSinceEpoch( - clip.startTimestamp * 1000, - ).toLocal(); - lines.add( - DateTimeFormatter.format(startTime, pattern: 'yyyy-M-d-H:mm:ss'), - ); - } - return lines.join('\n'); - } - String _savedDialogSessionTitle( RecordingModel recordingInfo, String? savedName, @@ -178,7 +160,8 @@ class _RecordingPageState extends ConsumerState { state: state, eventTitle: showClipboardInfo ? clipboard.title : null, eventAddress: showClipboardInfo ? clipboard.address : null, - clipboardHintLabel: _clipboardHintLabel(recordingInfo), + showClipboardHint: showClipboardInfo, + clipboardAddress: clipboard.address.trim(), onPasteEventInfo: () async { final result = await ref .read(recordingViewModelProvider.notifier) @@ -257,7 +240,8 @@ class _RecordingHud extends StatelessWidget { required this.state, this.eventTitle, this.eventAddress, - this.clipboardHintLabel, + this.showClipboardHint = false, + this.clipboardAddress = '', required this.onPasteEventInfo, required this.onStart, required this.onStop, @@ -269,7 +253,8 @@ class _RecordingHud extends StatelessWidget { final RecordingSessionState state; final String? eventTitle; final String? eventAddress; - final String? clipboardHintLabel; + final bool showClipboardHint; + final String clipboardAddress; final Future Function() onPasteEventInfo; final VoidCallback onStart; final VoidCallback onStop; @@ -330,7 +315,8 @@ class _RecordingHud extends StatelessWidget { hasDndAccess: state.hasDndAccess, isBatteryIgnored: state.isBatteryOptimizedIgnored, notificationsGranted: state.notificationsGranted, - clipboardHintLabel: clipboardHintLabel, + showClipboardHint: showClipboardHint, + clipboardAddress: clipboardAddress, onOpenDnd: onOpenDnd, onOpenBattery: onOpenBattery, onOpenNotificationSettings: openAppSettings, @@ -485,7 +471,8 @@ class _SetupHints extends StatelessWidget { required this.hasDndAccess, required this.isBatteryIgnored, required this.notificationsGranted, - this.clipboardHintLabel, + this.showClipboardHint = false, + this.clipboardAddress = '', required this.onOpenDnd, required this.onOpenBattery, required this.onOpenNotificationSettings, @@ -494,7 +481,8 @@ class _SetupHints extends StatelessWidget { final bool hasDndAccess; final bool isBatteryIgnored; final bool notificationsGranted; - final String? clipboardHintLabel; + final bool showClipboardHint; + final String clipboardAddress; final VoidCallback onOpenDnd; final VoidCallback onOpenBattery; final VoidCallback onOpenNotificationSettings; @@ -503,8 +491,7 @@ class _SetupHints extends StatelessWidget { Widget build(BuildContext context) { final showPermissionHints = !hasDndAccess || !isBatteryIgnored || !notificationsGranted; - final showClipboardHint = - clipboardHintLabel != null && clipboardHintLabel!.isNotEmpty; + final showClipboardHint = this.showClipboardHint; if (!showPermissionHints && !showClipboardHint) { return const SizedBox.shrink(); } @@ -526,10 +513,9 @@ class _SetupHints extends StatelessWidget { SizedBox(height: 8.h), _HintChip(label: '关闭电池优化可提升息屏续录稳定性', onTap: onOpenBattery), ], - if (clipboardHintLabel != null && - clipboardHintLabel!.isNotEmpty) ...[ + if (showClipboardHint) ...[ SizedBox(height: 8.h), - _HintChip(label: clipboardHintLabel!, onTap: () {}), + _ClipboardAddressClockChip(address: clipboardAddress), ], ], ), @@ -537,6 +523,49 @@ class _SetupHints extends StatelessWidget { } } +class _ClipboardAddressClockChip extends StatefulWidget { + const _ClipboardAddressClockChip({required this.address}); + + final String address; + + @override + State<_ClipboardAddressClockChip> createState() => + _ClipboardAddressClockChipState(); +} + +class _ClipboardAddressClockChipState extends State<_ClipboardAddressClockChip> { + Timer? _clockTimer; + + @override + void initState() { + super.initState(); + _clockTimer = Timer.periodic(const Duration(seconds: 1), (_) { + if (mounted) setState(() {}); + }); + } + + @override + void dispose() { + _clockTimer?.cancel(); + _clockTimer = null; + super.dispose(); + } + + String _buildLabel() { + final nowText = DateTimeFormatter.format( + DateTime.now(), + pattern: 'yyyy-M-d-H:mm:ss', + ); + if (widget.address.isEmpty) return nowText; + return '${widget.address}\n$nowText'; + } + + @override + Widget build(BuildContext context) { + return _HintChip(label: _buildLabel(), onTap: () {}); + } +} + class _HintChip extends StatelessWidget { const _HintChip({required this.label, required this.onTap});