366 lines
12 KiB
Dart
366 lines
12 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_text_detect_area/flutter_text_detect_area.dart';
|
|
import 'package:flutter_translate/ads/interstitial_ad_manage.dart';
|
|
import 'package:flutter_translate/common/components/speak_alert.dart';
|
|
import 'package:flutter_translate/common/hive/history_data.dart';
|
|
import 'package:flutter_translate/common/utils/bot_toast.dart';
|
|
import 'package:flutter_translate/common/utils/log_utils.dart';
|
|
import 'package:flutter_translate/common/utils/number_utils.dart';
|
|
import 'package:flutter_translate/common/utils/object_utils.dart';
|
|
import 'package:flutter_translate/common/utils/permission_utils.dart';
|
|
import 'package:flutter_translate/common/utils/shared_util.dart';
|
|
import 'package:flutter_translate/generated/assets.dart';
|
|
import 'package:flutter_translate/global/app_lifecycle_reactor.dart';
|
|
import 'package:flutter_translate/global/app_tracking_transparency_manager.dart';
|
|
import 'package:flutter_translate/manager/speech_convert.dart';
|
|
import 'package:flutter_translate/manager/translate.dart';
|
|
import 'package:flutter_translate/manager/tts.dart';
|
|
import 'package:flutter_translate/model/history_model.dart';
|
|
import 'package:flutter_translate/model/language_model.dart';
|
|
import 'package:flutter_translate/model/scene_model.dart';
|
|
import 'package:flutter_translate/pages/home/components/photo_picker.dart';
|
|
import 'package:flutter_translate/router/get_gages.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:image_picker/image_picker.dart';
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
import 'package:speech_to_text/speech_recognition_result.dart';
|
|
|
|
class HomeController extends GetxController {
|
|
static HomeController get to => Get.find<HomeController>();
|
|
|
|
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
|
|
|
|
var versionName = ''.obs;
|
|
|
|
var options = ['Privacy Policy', 'User Agreement', 'Color Setting'];
|
|
|
|
var lastWords = '';
|
|
|
|
var fromLanguage = Rx<LanguageModel>(
|
|
LanguageModel(code: 'en', name: 'English'),
|
|
);
|
|
var toLanguage = Rx<LanguageModel>(
|
|
LanguageModel(code: 'zh-cn', name: 'Chinese (Simplified)'),
|
|
);
|
|
|
|
var fromScene = Rx<String>("loading");
|
|
var toScene = Rx<String>("loading");
|
|
|
|
RxBool isPlayingFrom = false.obs;
|
|
RxBool isPlayingTo = false.obs;
|
|
|
|
RxInt appColor = 0xff529F2D.obs;
|
|
|
|
final languageModels = <LanguageModel>[
|
|
LanguageModel(code: 'zh-cn', name: 'Chinese (Simplified)'),
|
|
LanguageModel(code: 'ar', name: 'Arabic'),
|
|
LanguageModel(code: 'bn', name: 'Bengali'),
|
|
LanguageModel(code: 'en', name: 'English'),
|
|
LanguageModel(code: 'fr', name: 'French'),
|
|
LanguageModel(code: 'de', name: 'German'),
|
|
LanguageModel(code: 'hi', name: 'Hindi'),
|
|
LanguageModel(code: 'ja', name: 'Japanese'),
|
|
LanguageModel(code: 'jv', name: 'Javanese'),
|
|
LanguageModel(code: 'ko', name: 'Korean'),
|
|
LanguageModel(code: 'pt', name: 'Portuguese'),
|
|
LanguageModel(code: 'pa', name: 'Punjabi'),
|
|
LanguageModel(code: 'ru', name: 'Russian'),
|
|
LanguageModel(code: 'es', name: 'Spanish'),
|
|
];
|
|
var historyList = <HistoryModel>[].obs;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
AppTrackingTransparencyManager().requestATT();
|
|
AppLifecycleReactor().listenToAppStateChanges();
|
|
|
|
appColor.value = SharedUtil.getInstance().get<int>("appColor") ?? 0xff554281;
|
|
_getAppVersion();
|
|
}
|
|
|
|
@override
|
|
void onReady() {
|
|
super.onReady();
|
|
initData();
|
|
initHistoryList();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
AppLifecycleReactor().appLifecycleListener?.dispose();
|
|
super.onClose();
|
|
}
|
|
|
|
void initHistoryList() {
|
|
historyList.value = HistoryData().findAll().reversed.toList();
|
|
}
|
|
|
|
Future<void> initData() async {
|
|
LanguageModel fromModel = Translate().fromLanguageEntity.value;
|
|
LanguageModel toModel = Translate().toLanguageEntity.value;
|
|
|
|
LanguageModel? f =
|
|
languageModels.firstWhereOrNull((e) => e.code == fromModel.code);
|
|
if (f != null) fromLanguage.value = f;
|
|
|
|
LanguageModel? t = languageModels.firstWhereOrNull((e) => e.code == toModel.code);
|
|
if (t != null) toLanguage.value = t;
|
|
|
|
var data = jsonDecode(await rootBundle.loadString(Assets.jsonScene));
|
|
|
|
if (data != null && data is List) {
|
|
var sceneTypeList = data.map((e) => SceneModel.fromMap(e)).toList();
|
|
if (sceneTypeList.isNotEmpty) {
|
|
var x = NumberUtils.getRandomNumber(0, sceneTypeList.length);
|
|
SceneModel entity = sceneTypeList[x];
|
|
if (entity.sceneList != null && entity.sceneList!.isNotEmpty) {
|
|
var y = NumberUtils.getRandomNumber(0, entity.sceneList!.length);
|
|
fromScene.value = _getSentence(true, entity.sceneList![y]);
|
|
toScene.value = _getSentence(false, entity.sceneList![y]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 获取版本号
|
|
void _getAppVersion() async {
|
|
final packageInfo = await PackageInfo.fromPlatform();
|
|
versionName.value = 'version number ${packageInfo.version}';
|
|
}
|
|
|
|
void openHomeDrawer() {
|
|
scaffoldKey.currentState?.openDrawer();
|
|
}
|
|
|
|
void clickItem(int index) {
|
|
Log.d(index);
|
|
if (index == 2) {
|
|
Get.toNamed(GetPages.colorSetting);
|
|
} else {
|
|
Get.toNamed(GetPages.webView, arguments: {
|
|
'title': options[index],
|
|
'url': index == 0
|
|
? 'https://trans-globe.mystrikingly.com/privacy'
|
|
: 'https://trans-globe.mystrikingly.com/terms',
|
|
});
|
|
}
|
|
scaffoldKey.currentState?.closeDrawer();
|
|
}
|
|
|
|
String _getSentence(bool isFrom, SceneList sceneList) {
|
|
List keys = sceneList.toMap().keys.toList();
|
|
List values = sceneList.toMap().values.toList();
|
|
int index = keys.indexWhere(
|
|
(e) => e == (isFrom ? fromLanguage.value.name : toLanguage.value.name));
|
|
if (index != -1) {
|
|
return values[index];
|
|
}
|
|
return 'loading';
|
|
}
|
|
|
|
void textConvertVoice(bool isFrom) async {
|
|
isPlayingTo.value = false;
|
|
isPlayingFrom.value = false;
|
|
if (isFrom) {
|
|
isPlayingFrom.value = true;
|
|
await Tts().voiceTranslator(fromScene.value, fromLanguage.value.code);
|
|
isPlayingFrom.value = false;
|
|
} else {
|
|
isPlayingTo.value = true;
|
|
await Tts().voiceTranslator(toScene.value, toLanguage.value.code);
|
|
isPlayingTo.value = false;
|
|
}
|
|
}
|
|
|
|
void sceneCategory() {
|
|
Get.toNamed(GetPages.sceneCategory);
|
|
}
|
|
|
|
Future photos() async {
|
|
Get.bottomSheet(
|
|
isScrollControlled: true,
|
|
PhotoPicker(
|
|
funCamera: () async {
|
|
await PermissionUtils.checkPermission(
|
|
[Permission.camera],
|
|
).then((res) {
|
|
if (!res) return;
|
|
_openCameraGallery(ImageSource.camera);
|
|
});
|
|
},
|
|
funGallery: () async {
|
|
Permission permission = Permission.photos;
|
|
await PermissionUtils.checkPermission(
|
|
[permission],
|
|
).then((res) {
|
|
if (!res) return;
|
|
_openCameraGallery(ImageSource.gallery);
|
|
});
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
void face2face() {
|
|
InterstitialAdManager().showAdIfReady(
|
|
InterstitialAdManager().adIds[2],
|
|
onTap: () async {
|
|
bool micResult = await PermissionUtils.checkPermission(
|
|
[Permission.microphone],
|
|
);
|
|
if (!micResult) return;
|
|
if (Platform.isIOS) {
|
|
bool speechResult = await PermissionUtils.checkPermission(
|
|
[Permission.speech],
|
|
);
|
|
if (!speechResult) return;
|
|
}
|
|
Get.toNamed(GetPages.faceToFace);
|
|
},
|
|
);
|
|
}
|
|
|
|
void history() {
|
|
Get.toNamed(GetPages.history);
|
|
}
|
|
|
|
void translator() {
|
|
Get.toNamed(GetPages.translator);
|
|
}
|
|
|
|
Future speak() async {
|
|
bool micResult =
|
|
await PermissionUtils.checkPermission([Permission.microphone]);
|
|
if (!micResult) return;
|
|
if (Platform.isIOS) {
|
|
bool speechResult =
|
|
await PermissionUtils.checkPermission([Permission.speech]);
|
|
if (!speechResult) return;
|
|
}
|
|
await Get.dialog(
|
|
barrierDismissible: true,
|
|
useSafeArea: false,
|
|
SpeakAlert(
|
|
isListening: SpeechConvert().isListening,
|
|
onTap: () async {
|
|
if (!SpeechConvert().hasSpeech) {
|
|
await SpeechConvert().init();
|
|
}
|
|
if (SpeechConvert().hasSpeech) {
|
|
if (SpeechConvert().isListening.value) {
|
|
_translatedText();
|
|
} else {
|
|
SpeechConvert().start(
|
|
Translate().fromLanguageEntity.value.code,
|
|
(SpeechRecognitionResult result) {
|
|
Log.d('识别结果:${result.recognizedWords}');
|
|
lastWords = result.recognizedWords;
|
|
},
|
|
);
|
|
}
|
|
} else {
|
|
Get.back();
|
|
toast('Speech not available');
|
|
}
|
|
},
|
|
),
|
|
);
|
|
if (Get.isDialogOpen != null && !Get.isDialogOpen!) {
|
|
SpeechConvert().stop(showLoading: false);
|
|
}
|
|
}
|
|
|
|
void _translatedText() {
|
|
if (ObjectUtils.isEmpty(lastWords)) {
|
|
toast('No text recognized');
|
|
return;
|
|
}
|
|
Get.back();
|
|
Get.toNamed(
|
|
GetPages.translateText,
|
|
arguments: {"sourceText": lastWords},
|
|
);
|
|
}
|
|
|
|
Future<void> _openCameraGallery(ImageSource source) async {
|
|
String detectedValue = "";
|
|
final ImagePicker picker = ImagePicker();
|
|
final XFile? photo = await picker.pickImage(source: source);
|
|
if (photo != null) {
|
|
Get.to(() => SelectImageAreaTextDetect(
|
|
detectOnce: true,
|
|
enableImageInteractions: true,
|
|
imagePath: photo.path,
|
|
onDetectText: (v) {
|
|
if (v is String) {
|
|
detectedValue = v;
|
|
}
|
|
if (v is List) {
|
|
int counter = 0;
|
|
for (var element in v) {
|
|
detectedValue += "$counter. \t\t $element \n\n";
|
|
counter++;
|
|
}
|
|
}
|
|
},
|
|
onDetectError: (error) {
|
|
///This error will occurred in Android only while user will try to crop image at max zoom level then ml kit will throw max 32 height/width exception
|
|
if (error is PlatformException &&
|
|
(error.message?.contains(
|
|
"InputImage width and height should be at least 32!",
|
|
) ??
|
|
false)) {
|
|
toast(
|
|
'Selected area should be able to crop image with at least 32 width and height.',
|
|
);
|
|
}
|
|
},
|
|
))?.then(
|
|
(onValue) {
|
|
InterstitialAdManager().showAdIfReady(
|
|
InterstitialAdManager().adIds[3],
|
|
onTap: () {
|
|
if (ObjectUtils.isEmpty(detectedValue)) {
|
|
toast('No text recognized');
|
|
return;
|
|
}
|
|
Get.toNamed(
|
|
GetPages.translateText,
|
|
arguments: {"sourceText": detectedValue},
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
void removeHistoryByIndex(int index) {
|
|
// 计算原始列表中的对应索引
|
|
int indexToRemoveFromDb = HistoryData().findAll().length - 1 - index;
|
|
historyList.removeAt(indexToRemoveFromDb);
|
|
HistoryData().remove(indexToRemoveFromDb);
|
|
}
|
|
|
|
void historyTranslate(HistoryModel historyModel) {
|
|
Get.toNamed(
|
|
GetPages.translateText,
|
|
arguments: {
|
|
'isHistory': true,
|
|
'sourceText': historyModel.sourceText,
|
|
'targetText': historyModel.targetText,
|
|
'fromLanguageName': historyModel.sourceLanguageName,
|
|
'toLanguageName': historyModel.targetLanguageName,
|
|
'fromLanguageCode': historyModel.sourceLanguageCode,
|
|
'toLanguageCode': historyModel.targetLanguageCode,
|
|
},
|
|
);
|
|
}
|
|
}
|