|
|
@ -2,12 +2,15 @@ import 'dart:io'; |
|
|
import 'dart:async'; |
|
|
import 'dart:async'; |
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart'; |
|
|
import 'package:flutter/material.dart'; |
|
|
|
|
|
import 'package:flutter/services.dart'; |
|
|
import 'package:get/get.dart'; |
|
|
import 'package:get/get.dart'; |
|
|
import 'package:get_storage/get_storage.dart'; |
|
|
import 'package:get_storage/get_storage.dart'; |
|
|
import 'package:intl/intl.dart'; |
|
|
import 'package:intl/intl.dart'; |
|
|
|
|
|
import 'package:native_plugin/native_plugin.dart'; |
|
|
import 'package:path_provider/path_provider.dart'; |
|
|
import 'package:path_provider/path_provider.dart'; |
|
|
import 'package:permission_handler/permission_handler.dart'; |
|
|
import 'package:permission_handler/permission_handler.dart'; |
|
|
import 'package:device_info_plus/device_info_plus.dart'; |
|
|
import 'package:device_info_plus/device_info_plus.dart'; |
|
|
|
|
|
import 'package:pip/pip.dart'; |
|
|
import '../../../data/services/ast_service.dart'; |
|
|
import '../../../data/services/ast_service.dart'; |
|
|
import '../../../data/services/bluetooth_manager.dart'; |
|
|
import '../../../data/services/bluetooth_manager.dart'; |
|
|
import '../../../data/services/music_manager.dart'; |
|
|
import '../../../data/services/music_manager.dart'; |
|
|
@ -49,7 +52,12 @@ class TranslationController extends GetxController with WidgetsBindingObserver { |
|
|
final BleManager bleManager = Get.find<BleManager>(); |
|
|
final BleManager bleManager = Get.find<BleManager>(); |
|
|
final _musiceManager = Get.find<MusicManager>(); |
|
|
final _musiceManager = Get.find<MusicManager>(); |
|
|
late final TranslationUsageService _usageService; |
|
|
late final TranslationUsageService _usageService; |
|
|
|
|
|
// 安卓悬浮窗管理器 |
|
|
late final FloatingWindowManager _floatingWindowManager; |
|
|
late final FloatingWindowManager _floatingWindowManager; |
|
|
|
|
|
// 苹果悬浮窗管理器 |
|
|
|
|
|
late final Pip _pip; |
|
|
|
|
|
late final NativePlugin _nativePlugin; |
|
|
|
|
|
int _pipContentView = 0; |
|
|
|
|
|
|
|
|
// ==================== 悬浮窗相关 ==================== |
|
|
// ==================== 悬浮窗相关 ==================== |
|
|
final isFloatingWindowEnabled = false.obs; // 悬浮窗状态 |
|
|
final isFloatingWindowEnabled = false.obs; // 悬浮窗状态 |
|
|
@ -135,7 +143,7 @@ class TranslationController extends GetxController with WidgetsBindingObserver { |
|
|
// 底部控制栏高度 |
|
|
// 底部控制栏高度 |
|
|
double _bottomBarHeight = 0.0; |
|
|
double _bottomBarHeight = 0.0; |
|
|
|
|
|
|
|
|
/// 初始化悬浮窗管理器 |
|
|
/// 初始化安卓悬浮窗管理器 |
|
|
void _initFloatingWindowManager() { |
|
|
void _initFloatingWindowManager() { |
|
|
try { |
|
|
try { |
|
|
if (!Get.isRegistered<FloatingWindowManager>()) { |
|
|
if (!Get.isRegistered<FloatingWindowManager>()) { |
|
|
@ -148,13 +156,68 @@ class TranslationController extends GetxController with WidgetsBindingObserver { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 初始化苹果悬浮窗管理器 |
|
|
|
|
|
Future<void> initPlatformState() async { |
|
|
|
|
|
_pip = Pip(); |
|
|
|
|
|
_nativePlugin = NativePlugin(); |
|
|
|
|
|
try { |
|
|
|
|
|
isFloatingWindowEnabled.value = await _pip.isActived(); |
|
|
|
|
|
await _pip.registerStateChangedObserver( |
|
|
|
|
|
PipStateChangedObserver( |
|
|
|
|
|
onPipStateChanged: (state, error) { |
|
|
|
|
|
isFloatingWindowEnabled.value = state == PipState.pipStateStarted; |
|
|
|
|
|
|
|
|
|
|
|
if (state == PipState.pipStateFailed) { |
|
|
|
|
|
_pip.dispose(); |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
), |
|
|
|
|
|
); |
|
|
|
|
|
} on PlatformException { |
|
|
|
|
|
isFloatingWindowEnabled.value = false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/// 切换悬浮窗功能 |
|
|
/// 切换悬浮窗功能 |
|
|
Future<void> toggleFloatingWindow() async { |
|
|
Future<void> toggleFloatingWindow() async { |
|
|
if (isFloatingWindowEnabled.value) { |
|
|
if (isFloatingWindowEnabled.value) { |
|
|
await _disableFloatingWindow(); |
|
|
if (Platform.isAndroid) { |
|
|
|
|
|
await _disableFloatingWindow(); |
|
|
|
|
|
} else if (Platform.isIOS) { |
|
|
|
|
|
await _pip.dispose(); |
|
|
|
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
await _enableFloatingWindow(); |
|
|
if (Platform.isAndroid) { |
|
|
|
|
|
await _enableFloatingWindow(); |
|
|
|
|
|
} else if (Platform.isIOS) { |
|
|
|
|
|
await _startPip(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Future<void> _startPip() async { |
|
|
|
|
|
if (_pipContentView == 0) { |
|
|
|
|
|
_pipContentView = await _nativePlugin.createPipContentView(); |
|
|
} |
|
|
} |
|
|
|
|
|
final options = PipOptions( |
|
|
|
|
|
autoEnterEnabled: true, |
|
|
|
|
|
aspectRatioX: 375, |
|
|
|
|
|
aspectRatioY: 400, |
|
|
|
|
|
sourceRectHintLeft: 0, |
|
|
|
|
|
sourceRectHintTop: 0, |
|
|
|
|
|
sourceRectHintRight: 0, |
|
|
|
|
|
sourceRectHintBottom: 0, |
|
|
|
|
|
seamlessResizeEnabled: true, |
|
|
|
|
|
useExternalStateMonitor: true, |
|
|
|
|
|
externalStateMonitorInterval: 100, |
|
|
|
|
|
contentView: _pipContentView, |
|
|
|
|
|
preferredContentWidth: 375, |
|
|
|
|
|
preferredContentHeight: 400, |
|
|
|
|
|
controlStyle: 2, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
await _pip.setup(options); |
|
|
|
|
|
await _pip.start(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 启用悬浮窗功能 |
|
|
/// 启用悬浮窗功能 |
|
|
@ -217,16 +280,24 @@ class TranslationController extends GetxController with WidgetsBindingObserver { |
|
|
_languageManager.getChineseNameByAsrCode(item.targetLanguageCode) ?? |
|
|
_languageManager.getChineseNameByAsrCode(item.targetLanguageCode) ?? |
|
|
'unknown'; |
|
|
'unknown'; |
|
|
|
|
|
|
|
|
final data = FloatingWindowData( |
|
|
if (Platform.isAndroid) { |
|
|
sourceText: item.sourceText, |
|
|
final data = FloatingWindowData( |
|
|
translatedText: item.translatedText, |
|
|
sourceText: item.sourceText, |
|
|
sourceLanguage: sourceLanguageName, |
|
|
translatedText: item.translatedText, |
|
|
targetLanguage: targetLanguageName, |
|
|
sourceLanguage: sourceLanguageName, |
|
|
timestamp: item.timestamp, |
|
|
targetLanguage: targetLanguageName, |
|
|
isIntermediate: item.isIntermediate, |
|
|
timestamp: item.timestamp, |
|
|
); |
|
|
isIntermediate: item.isIntermediate, |
|
|
|
|
|
); |
|
|
await _floatingWindowManager.updateFloatingWindowContent(data); |
|
|
await _floatingWindowManager.updateFloatingWindowContent(data); |
|
|
|
|
|
} else if (Platform.isIOS) { |
|
|
|
|
|
await _nativePlugin.updatePipContentView({ |
|
|
|
|
|
'viewId': _pipContentView, |
|
|
|
|
|
'cn': item.sourceText, |
|
|
|
|
|
'en': item.translatedText, |
|
|
|
|
|
'lang': '$sourceLanguageName -> $targetLanguageName', |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 更改翻译模式 |
|
|
/// 更改翻译模式 |
|
|
@ -374,8 +445,13 @@ class TranslationController extends GetxController with WidgetsBindingObserver { |
|
|
_timerManager = RecordTimerController(); |
|
|
_timerManager = RecordTimerController(); |
|
|
_historyManager = TranslationHistoryManager(); |
|
|
_historyManager = TranslationHistoryManager(); |
|
|
|
|
|
|
|
|
// 初始化悬浮窗管理器 |
|
|
if (Platform.isAndroid) { |
|
|
_initFloatingWindowManager(); |
|
|
// 初始化安卓悬浮窗管理器 |
|
|
|
|
|
_initFloatingWindowManager(); |
|
|
|
|
|
} else if (Platform.isIOS) { |
|
|
|
|
|
// 初始化苹果悬浮窗管理器 |
|
|
|
|
|
initPlatformState(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 预先检查权限,避免在用户交互时阻塞 |
|
|
// 预先检查权限,避免在用户交互时阻塞 |
|
|
_preCheckPermissions(); |
|
|
_preCheckPermissions(); |
|
|
@ -665,6 +741,15 @@ class TranslationController extends GetxController with WidgetsBindingObserver { |
|
|
// _asrService.dispose(); |
|
|
// _asrService.dispose(); |
|
|
// _astService.dispose(); |
|
|
// _astService.dispose(); |
|
|
|
|
|
|
|
|
|
|
|
if (Platform.isAndroid) { |
|
|
|
|
|
_disableFloatingWindow(); |
|
|
|
|
|
} else if (Platform.isIOS) { |
|
|
|
|
|
_pip.dispose(); |
|
|
|
|
|
if (_pipContentView != 0) { |
|
|
|
|
|
_nativePlugin.disposePipContentView(_pipContentView); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 释放管理器资源 |
|
|
// 释放管理器资源 |
|
|
_timerManager.dispose(); |
|
|
_timerManager.dispose(); |
|
|
_historyManager.dispose(); |
|
|
_historyManager.dispose(); |
|
|
|