final
parent
73a518733d
commit
7909116d8e
15
.metadata
15
.metadata
|
@ -15,21 +15,6 @@ migration:
|
||||||
- platform: root
|
- platform: root
|
||||||
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
||||||
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
||||||
- platform: android
|
|
||||||
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
|
||||||
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
|
||||||
- platform: ios
|
|
||||||
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
|
||||||
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
|
||||||
- platform: linux
|
|
||||||
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
|
||||||
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
|
||||||
- platform: macos
|
|
||||||
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
|
||||||
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
|
||||||
- platform: web
|
|
||||||
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
|
||||||
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
|
||||||
- platform: windows
|
- platform: windows
|
||||||
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
||||||
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
|
||||||
|
|
|
@ -23,20 +23,6 @@
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"flutterMode": "release"
|
"flutterMode": "release"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Flutter Web (Chrome)",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "dart",
|
|
||||||
"program": "lib/main.dart",
|
|
||||||
"args": [
|
|
||||||
"--web-port", "8080"
|
|
||||||
],
|
|
||||||
"flutterMode": "debug",
|
|
||||||
"disableWebSecurity": true,
|
|
||||||
"webRenderer": "auto",
|
|
||||||
"deviceName": "Chrome",
|
|
||||||
"args2": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
20
README.md
20
README.md
|
@ -1,22 +1,12 @@
|
||||||
# optictext
|
# optictext
|
||||||
|
|
||||||
Ein Tool, das dem Benutzer die Option gibt, OCR auf Bildern anzuwenden oder Text in Bildern zu übersetzen und den übersetzten Text wieder auf das Bild zu setzen.
|
Leider habe ich es nicht hinbekommen meine requests zu mocken.
|
||||||
|
Deshalb habe ich nur UI Elemente getestet.
|
||||||
|
|
||||||
Screen1: Wahl zwischen Image tools und Audio Tools(falls ich in der Zukunft weiter daran Arbeiten werde).
|
Falls ich den Code vom Server noch nachreichen soll, kann ich dies machen.
|
||||||
|
|
||||||
Image tools screen: DoOCR und ImageTranslate.
|
|
||||||
DoOCR: User kann ein Bild hochladen und OCR darauf anwenden.
|
|
||||||
ImageTranslate: User kann ein Bild hochladen welches dann übersetzt wird.
|
|
||||||
|
|
||||||
|
|
||||||
Den user die Sprache wählen zu lassen und dann in eine gewählte sprache zu übersetzen ist leicht gemacht,
|
|
||||||
allerding ist es mir wichtig, das die spracherkennung auch automatisch funktioniert.
|
|
||||||
Im ersten schritt war es wichtig diese funktionalität zu haben.
|
|
||||||
Aktuell läuft das auf einem vps, dafür ist der http request da.
|
|
||||||
Wenn möglich implementiere ich die langauge classfication in dart.
|
|
||||||
|
|
||||||
aktuell teste ich auf einem android phone, es soll aber am ende auch auf IOS laufen.
|
|
||||||
|
|
||||||
|
Flutter build apk wirft eine Fehlermeldung aber funktioniert. Bei der Präsentation hatte
|
||||||
|
ich die release version dabei, die nicht mit "debug" gekennzeichnet war.
|
||||||
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
|
@ -45,7 +45,7 @@ android {
|
||||||
applicationId "com.example.optictext"
|
applicationId "com.example.optictext"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion 21
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<application
|
<application
|
||||||
android:label="optictext"
|
android:label="optictext"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
|
|
|
@ -22,6 +22,7 @@ rootProject.buildDir = '../build'
|
||||||
subprojects {
|
subprojects {
|
||||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 238 KiB |
BIN
assets/lorem.png
BIN
assets/lorem.png
Binary file not shown.
Before Width: | Height: | Size: 11 KiB |
BIN
assets/rus.png
BIN
assets/rus.png
Binary file not shown.
Before Width: | Height: | Size: 97 KiB |
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,11 +1,9 @@
|
||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
"eng.traineddata",
|
"eng.traineddata",
|
||||||
"ara.traineddata",
|
|
||||||
"rus.traineddata",
|
"rus.traineddata",
|
||||||
"deu.traineddata",
|
"deu.traineddata",
|
||||||
"jpn.traineddata",
|
"jpn.traineddata",
|
||||||
"ell.traineddata",
|
"fra.traineddata"
|
||||||
"chi_sim.traineddata"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1,51 +0,0 @@
|
||||||
// ignore_for_file: library_private_types_in_public_api
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
//might be useless. keep for now
|
|
||||||
class BottomBar extends StatefulWidget {
|
|
||||||
const BottomBar({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_BottomBarState createState() => _BottomBarState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BottomBarState extends State<BottomBar> {
|
|
||||||
int _selectedIndex = 0;
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
static const List<Widget> _widgetOptions = <Widget>[
|
|
||||||
Text('Home Page'),
|
|
||||||
Text('todo'),
|
|
||||||
Text('pro'),
|
|
||||||
];
|
|
||||||
|
|
||||||
void _onItemTapped(int index) {
|
|
||||||
setState(() {
|
|
||||||
_selectedIndex = index;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BottomNavigationBar(
|
|
||||||
items: const <BottomNavigationBarItem>[
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.home),
|
|
||||||
label: 'Home',
|
|
||||||
),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.business),
|
|
||||||
label: 'todo',
|
|
||||||
),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.school),
|
|
||||||
label: 'pro',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
currentIndex: _selectedIndex,
|
|
||||||
selectedItemColor: Colors.amber[800],
|
|
||||||
onTap: _onItemTapped,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
class FileUtils {
|
||||||
|
static void saveTranslatedImage(Uint8List translatedImage) async {
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
final result = await ImageGallerySaver.saveImage(translatedImage);
|
||||||
|
if (result['isSuccess']) {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: "Image saved successfully!",
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
timeInSecForIosWeb: 1,
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: "Failed to save image",
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
timeInSecForIosWeb: 1,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//vorerst... finde noch was besseres
|
||||||
|
final file = File('C:/Users/hfggvcb/Desktop/image.png');
|
||||||
|
await file.writeAsBytes(translatedImage);
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: "Image saved successfully on Windows!",
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
timeInSecForIosWeb: 1,
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,21 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:cpd_app/language_utils.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:http/http.dart';
|
||||||
import 'package:http_parser/http_parser.dart';
|
import 'package:http_parser/http_parser.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:connectivity/connectivity.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
class HttpUtils {
|
class HttpUtils {
|
||||||
var client = http.Client();
|
var client = http.Client();
|
||||||
Future<String> checkLang(Uint8List imageBytes, String imageName) async {
|
|
||||||
var client = http.Client();
|
|
||||||
|
|
||||||
var postUri = Uri.parse("http://130.61.88.150/upload");
|
Future<String> checkLang(Uint8List imageBytes, String imageName) async {
|
||||||
|
var postUri = Uri.parse("http://130.61.27.201/upload");
|
||||||
|
|
||||||
http.MultipartRequest request = http.MultipartRequest("POST", postUri);
|
http.MultipartRequest request = http.MultipartRequest("POST", postUri);
|
||||||
|
|
||||||
http.MultipartFile multipartFile = http.MultipartFile.fromBytes(
|
http.MultipartFile multipartFile = http.MultipartFile.fromBytes(
|
||||||
|
@ -19,6 +26,7 @@ class HttpUtils {
|
||||||
);
|
);
|
||||||
|
|
||||||
request.files.add(multipartFile);
|
request.files.add(multipartFile);
|
||||||
|
|
||||||
http.StreamedResponse response = await client.send(request);
|
http.StreamedResponse response = await client.send(request);
|
||||||
http.Response finalResponse = await http.Response.fromStream(response);
|
http.Response finalResponse = await http.Response.fromStream(response);
|
||||||
var lang = "";
|
var lang = "";
|
||||||
|
@ -30,4 +38,151 @@ class HttpUtils {
|
||||||
}
|
}
|
||||||
return lang;
|
return lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> extractTextKnownSource(
|
||||||
|
Uint8List imageBytes, String sourceLang) async {
|
||||||
|
var postUri = Uri.parse("http://130.61.27.201/extractWithKnownSource");
|
||||||
|
|
||||||
|
http.MultipartRequest request = http.MultipartRequest("POST", postUri);
|
||||||
|
http.MultipartFile multipartFile = http.MultipartFile.fromBytes(
|
||||||
|
'file',
|
||||||
|
imageBytes,
|
||||||
|
filename: 'lorem.png',
|
||||||
|
contentType: MediaType('image', 'png'),
|
||||||
|
);
|
||||||
|
|
||||||
|
MultipartFile source = MultipartFile.fromString(
|
||||||
|
'sourceLang',
|
||||||
|
sourceLang,
|
||||||
|
);
|
||||||
|
|
||||||
|
request.files.add(multipartFile);
|
||||||
|
request.files.add(source);
|
||||||
|
|
||||||
|
http.StreamedResponse response = await client.send(request);
|
||||||
|
http.Response finalResponse = await http.Response.fromStream(response);
|
||||||
|
var lang = "";
|
||||||
|
if (finalResponse.statusCode == 200) {
|
||||||
|
lang = finalResponse.body.toString();
|
||||||
|
} else {
|
||||||
|
throw ('Error: ${finalResponse.statusCode}');
|
||||||
|
}
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> extractTextUnknownSource(Uint8List imageBytes) async {
|
||||||
|
var postUri = Uri.parse("http://130.61.27.201/extractWithoutKnownSource");
|
||||||
|
|
||||||
|
http.MultipartRequest request = http.MultipartRequest("POST", postUri);
|
||||||
|
|
||||||
|
http.MultipartFile multipartFile = http.MultipartFile.fromBytes(
|
||||||
|
'file',
|
||||||
|
imageBytes,
|
||||||
|
filename: 'lorem.png',
|
||||||
|
contentType: MediaType('image', 'png'),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.files.add(multipartFile);
|
||||||
|
|
||||||
|
http.StreamedResponse response = await client.send(request);
|
||||||
|
http.Response finalResponse = await http.Response.fromStream(response);
|
||||||
|
var lang = "";
|
||||||
|
if (finalResponse.statusCode == 200) {
|
||||||
|
lang = finalResponse.body.toString();
|
||||||
|
} else {
|
||||||
|
throw ('Error: ${finalResponse.statusCode}');
|
||||||
|
}
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List> translateKnownSource(
|
||||||
|
Uint8List imageBytes, String sourceLang, String targetLang) async {
|
||||||
|
var url = Uri.parse("http://130.61.27.201/translKnownSource");
|
||||||
|
|
||||||
|
http.MultipartRequest request = http.MultipartRequest("POST", url);
|
||||||
|
http.MultipartFile multipartFile = http.MultipartFile.fromBytes(
|
||||||
|
'file',
|
||||||
|
imageBytes,
|
||||||
|
filename: 'lorem.png',
|
||||||
|
contentType: MediaType('image', 'png'),
|
||||||
|
);
|
||||||
|
|
||||||
|
MultipartFile source = MultipartFile.fromString(
|
||||||
|
'sourceLang',
|
||||||
|
LanguageUtils.translatorLanguages[sourceLang]!,
|
||||||
|
);
|
||||||
|
|
||||||
|
MultipartFile target = MultipartFile.fromString(
|
||||||
|
'targetLang',
|
||||||
|
LanguageUtils.translatorLanguages[targetLang]!,
|
||||||
|
);
|
||||||
|
|
||||||
|
request.files.add(multipartFile);
|
||||||
|
request.files.add(source);
|
||||||
|
request.files.add(target);
|
||||||
|
http.StreamedResponse response = await client.send(request);
|
||||||
|
http.Response finalResponse = await http.Response.fromStream(response);
|
||||||
|
Uint8List lang = Uint8List(0);
|
||||||
|
if (finalResponse.statusCode == 200) {
|
||||||
|
lang = Uint8List.fromList(finalResponse.bodyBytes);
|
||||||
|
} else {
|
||||||
|
throw ('Error: ${finalResponse.statusCode}');
|
||||||
|
}
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
void showProgressBar(BuildContext context) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return const Dialog(
|
||||||
|
backgroundColor: Color.fromARGB(0, 44, 44, 44),
|
||||||
|
elevation: 0,
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hideProgressBar(BuildContext context) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> internetConnectivityCheck() async {
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
var connectivityResult = await Connectivity().checkConnectivity();
|
||||||
|
return connectivityResult == ConnectivityResult.wifi ||
|
||||||
|
connectivityResult == ConnectivityResult.mobile;
|
||||||
|
} else {
|
||||||
|
final response = await http.get(Uri.parse('http://130.61.27.201'));
|
||||||
|
return response.statusCode == 200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void triggerNoInetToast() {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: "No internet connection",
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
timeInSecForIosWeb: 1,
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void triggerConnectedToast() {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: "Connected",
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
timeInSecForIosWeb: 1,
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:cpd_app/file_utils.dart';
|
||||||
import 'package:cpd_app/http_utils.dart';
|
import 'package:cpd_app/http_utils.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:cpd_app/session_list.dart';
|
||||||
import 'package:cpd_app/image_uploader.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'language_utils.dart';
|
import 'language_utils.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
|
||||||
class ImageTranslation extends StatefulWidget {
|
class ImageTranslation extends StatefulWidget {
|
||||||
|
@ -16,64 +17,49 @@ class ImageTranslation extends StatefulWidget {
|
||||||
|
|
||||||
class _ImageTranslationState extends State<ImageTranslation> {
|
class _ImageTranslationState extends State<ImageTranslation> {
|
||||||
XFile? _selectedImage;
|
XFile? _selectedImage;
|
||||||
String _sourceLanguage = 'Auto';
|
bool _wasTranslated = false;
|
||||||
String _targetLanguage = 'English';
|
String _sourceLanguage = 'English';
|
||||||
String _translatedText = '';
|
String _targetLanguage = 'German';
|
||||||
final ImageUploader _imageUploader = ImageUploader();
|
Uint8List translatedIamge = Uint8List(0);
|
||||||
final HttpUtils _httpUtils = HttpUtils();
|
final HttpUtils _httpUtils = HttpUtils();
|
||||||
/*1. Text extracten
|
|
||||||
2. Text übersetzen
|
Uint8List buildNewImage(
|
||||||
3. Text aus dem alten bild mit farbe überdecken (koordinaten etwas weiter links oben + rechts unten als die erkannten koordinaten)
|
Uint8List imageBytes, String text, String sourceLang, String targetLang) {
|
||||||
4. Übersetzen Text in neues bild einfügen
|
return imageBytes;
|
||||||
*/
|
}
|
||||||
|
|
||||||
Future<void> uploadImage(
|
Future<void> uploadImage(
|
||||||
Uint8List imageBytes,
|
Uint8List imageBytes,
|
||||||
String sourceLang,
|
String sourceLang,
|
||||||
String targetLang,
|
String targetLang,
|
||||||
) async {
|
) async {
|
||||||
setState(() {
|
Uint8List newImageBytes = Uint8List(0);
|
||||||
_translatedText = '';
|
_httpUtils.showProgressBar(context);
|
||||||
});
|
try {
|
||||||
|
newImageBytes = await _httpUtils.translateKnownSource(
|
||||||
//preprocessing
|
imageBytes, sourceLang, targetLang);
|
||||||
String text = '';
|
} finally {
|
||||||
if (LanguageUtils.tessLanguages[sourceLang] == 'auto') {
|
// ignore: use_build_context_synchronously
|
||||||
String lang = await _httpUtils.checkLang(imageBytes, 'image.jpg');
|
_httpUtils.hideProgressBar(context);
|
||||||
text = await _imageUploader.performOcr(imageBytes, 'image.jpg', lang);
|
|
||||||
sourceLang = LanguageUtils.tessLanguages[sourceLang]!;
|
|
||||||
} else {
|
|
||||||
String langCode = LanguageUtils.tessLanguages[sourceLang]!;
|
|
||||||
text = await _imageUploader.performOcr(imageBytes, 'image.jpg', langCode);
|
|
||||||
}
|
}
|
||||||
if (text != '') {
|
if (newImageBytes.isEmpty) {
|
||||||
Map<String, String> requestBody = {
|
Fluttertoast.showToast(
|
||||||
'source_language': sourceLang,
|
msg: "Image could not be translated",
|
||||||
'target_language': LanguageUtils.translatorLanguages[targetLang]!,
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
'text': text,
|
gravity: ToastGravity.BOTTOM,
|
||||||
};
|
timeInSecForIosWeb: 1,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
Map<String, String> headers = {
|
textColor: Colors.white,
|
||||||
'content-type': 'application/x-www-form-urlencoded',
|
fontSize: 16.0,
|
||||||
'X-RapidAPI-Key': 'd0fa3c2f3cmsh1805e7a2fed7cc2p1683ebjsna722e2bffafe',
|
|
||||||
'X-RapidAPI-Host': 'text-translator2.p.rapidapi.com',
|
|
||||||
};
|
|
||||||
|
|
||||||
final response = await http.post(
|
|
||||||
Uri.parse('https://text-translator2.p.rapidapi.com/translate'),
|
|
||||||
headers: headers,
|
|
||||||
body: requestBody,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
_translatedText = response.body;
|
|
||||||
setState(() {
|
|
||||||
_translatedText = text;
|
|
||||||
_selectedImage = null;
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
print('Error: ${response.statusCode}');
|
DateTime now = DateTime.now();
|
||||||
}
|
int milliseconds = now.millisecondsSinceEpoch;
|
||||||
print(_translatedText);
|
ImageList.addImage(milliseconds.toString(), newImageBytes);
|
||||||
|
_wasTranslated = true;
|
||||||
|
setState(() {
|
||||||
|
translatedIamge = newImageBytes;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,8 +70,22 @@ class _ImageTranslationState extends State<ImageTranslation> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
backgroundColor: const Color.fromARGB(99, 78, 72, 72),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
title: const Text('Image Translation'),
|
title: const Text('Image Translation'),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.wifi),
|
||||||
|
onPressed: () async {
|
||||||
|
if (!await _httpUtils.internetConnectivityCheck()) {
|
||||||
|
_httpUtils.triggerNoInetToast();
|
||||||
|
} else {
|
||||||
|
_httpUtils.triggerConnectedToast();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -95,7 +95,6 @@ class _ImageTranslationState extends State<ImageTranslation> {
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
// From - Subtle blue background
|
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.blue.shade100,
|
color: Colors.blue.shade100,
|
||||||
|
@ -114,6 +113,7 @@ class _ImageTranslationState extends State<ImageTranslation> {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
items: sourceLanguages
|
items: sourceLanguages
|
||||||
|
.where((value) => value != 'Auto')
|
||||||
.map<DropdownMenuItem<String>>((String value) {
|
.map<DropdownMenuItem<String>>((String value) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: value,
|
value: value,
|
||||||
|
@ -124,7 +124,6 @@ class _ImageTranslationState extends State<ImageTranslation> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Target - Subtle red background
|
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.red.shade100,
|
color: Colors.red.shade100,
|
||||||
|
@ -143,6 +142,7 @@ class _ImageTranslationState extends State<ImageTranslation> {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
items: targetLanguages
|
items: targetLanguages
|
||||||
|
.where((value) => value != 'Auto')
|
||||||
.map<DropdownMenuItem<String>>((String value) {
|
.map<DropdownMenuItem<String>>((String value) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: value,
|
value: value,
|
||||||
|
@ -158,6 +158,7 @@ class _ImageTranslationState extends State<ImageTranslation> {
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
if (!_wasTranslated) {
|
||||||
final ImagePicker picker = ImagePicker();
|
final ImagePicker picker = ImagePicker();
|
||||||
final XFile? image =
|
final XFile? image =
|
||||||
await picker.pickImage(source: ImageSource.gallery);
|
await picker.pickImage(source: ImageSource.gallery);
|
||||||
|
@ -166,14 +167,20 @@ class _ImageTranslationState extends State<ImageTranslation> {
|
||||||
_selectedImage = image;
|
_selectedImage = image;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (BuildContext context) =>
|
||||||
|
const ImageTranslation(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
style: ButtonStyle(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: MaterialStateProperty.all<Color>(Colors.blue),
|
foregroundColor: const Color.fromARGB(255, 0, 0, 0),
|
||||||
),
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17)),
|
||||||
child: const Text(
|
child: Text(_wasTranslated ? 'Try Another' : 'Select Image'),
|
||||||
'Upload Image',
|
|
||||||
style: TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _selectedImage != null
|
onPressed: _selectedImage != null
|
||||||
|
@ -186,29 +193,28 @@ class _ImageTranslationState extends State<ImageTranslation> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
child: const Text('Extract Text'),
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
foregroundColor: const Color.fromARGB(255, 0, 0, 0)),
|
||||||
|
child: const Text('Translate Image'),
|
||||||
),
|
),
|
||||||
_translatedText.isNotEmpty
|
if (translatedIamge.isEmpty && _selectedImage != null)
|
||||||
? Expanded(
|
Image.file(File(_selectedImage!.path)),
|
||||||
child: Padding(
|
if (translatedIamge.isNotEmpty) Image.memory(translatedIamge),
|
||||||
padding: const EdgeInsets.all(8.0),
|
if (translatedIamge.isNotEmpty)
|
||||||
child: TextFormField(
|
Visibility(
|
||||||
readOnly: true,
|
visible: translatedIamge.isNotEmpty,
|
||||||
initialValue: _translatedText,
|
child: ElevatedButton(
|
||||||
maxLines: null,
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'Extracted Text',
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Clipboard.setData(ClipboardData(text: _translatedText));
|
if (translatedIamge.isNotEmpty) {
|
||||||
|
FileUtils.saveTranslatedImage(translatedIamge);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: const Text('Copy to Clipboard'),
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
foregroundColor: const Color.fromARGB(255, 0, 0, 0)),
|
||||||
|
child: const Text('Save Translated Image'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -4,106 +4,21 @@ class LanguageUtils {
|
||||||
'English': 'eng',
|
'English': 'eng',
|
||||||
'German': 'deu',
|
'German': 'deu',
|
||||||
'French': 'fra',
|
'French': 'fra',
|
||||||
'Spanish': 'spa',
|
|
||||||
'Italian': 'ita',
|
|
||||||
'Portuguese': 'por',
|
|
||||||
'Dutch': 'dut',
|
|
||||||
'Polish': 'pol',
|
|
||||||
'Russian': 'rus',
|
'Russian': 'rus',
|
||||||
'Japanese': 'jpn',
|
|
||||||
'Chinese (Simplified)': 'chi_sim',
|
|
||||||
'Chinese (Traditional)': 'chi_tra',
|
|
||||||
'Korean': 'kor',
|
|
||||||
'Arabic': 'ara',
|
|
||||||
'Hindi': 'hin',
|
|
||||||
'Indonesian': 'ind',
|
|
||||||
'Malay': 'mal',
|
|
||||||
'Thai': 'tha',
|
|
||||||
'Vietnamese': 'vie',
|
|
||||||
'Turkish': 'tur',
|
|
||||||
'Ukrainian': 'ukr',
|
|
||||||
'Hungarian': 'hun',
|
|
||||||
'Czech': 'cze',
|
|
||||||
'Finnish': 'fin',
|
|
||||||
'Danish': 'dan',
|
|
||||||
'Norwegian': 'nor',
|
|
||||||
'Swedish': 'swe',
|
|
||||||
'Greek': 'gre',
|
|
||||||
'Slovak': 'slo',
|
|
||||||
'Croatian': 'hrv',
|
|
||||||
'Lithuanian': 'lit',
|
|
||||||
'Romanian': 'rum',
|
|
||||||
'Bulgarian': 'bul',
|
|
||||||
'Latvian': 'lav',
|
|
||||||
'Estonian': 'est',
|
|
||||||
'Persian': 'per',
|
|
||||||
'Hebrew': 'heb',
|
|
||||||
'Serbian': 'srp',
|
|
||||||
'Albanian': 'alb',
|
|
||||||
'Tagalog': 'tgl',
|
|
||||||
'Azerbaijani': 'aze',
|
|
||||||
'Basque': 'baq',
|
|
||||||
'Belarusian': 'bel',
|
|
||||||
'Bengali': 'ben',
|
|
||||||
'Bosnian': 'bos',
|
|
||||||
'Cebuano': 'ceb',
|
|
||||||
'Esperanto': 'epo',
|
|
||||||
'Galician': 'glg',
|
|
||||||
'Georgian': 'geo',
|
|
||||||
'Gujarati': 'guj',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static Map<String, String> translatorLanguages = {
|
static Map<String, String> translatorLanguages = {
|
||||||
'Auto': 'auto',
|
|
||||||
'English': 'en',
|
'English': 'en',
|
||||||
|
'Auto': 'auto',
|
||||||
'German': 'de',
|
'German': 'de',
|
||||||
'French': 'fr',
|
'French': 'fr',
|
||||||
'Spanish': 'spa',
|
'Russian': 'ru',
|
||||||
'Italian': 'ita',
|
};
|
||||||
'Portuguese': 'por',
|
|
||||||
'Dutch': 'dut',
|
static Map<String, String> reMapping = {
|
||||||
'Polish': 'pol',
|
'deu': 'de',
|
||||||
'Russian': 'rus',
|
'eng': 'en',
|
||||||
'Japanese': 'jpn',
|
'fra': 'fr',
|
||||||
'Chinese (Simplified)': 'chi_sim',
|
'rus': 'ru',
|
||||||
'Chinese (Traditional)': 'chi_tra',
|
|
||||||
'Korean': 'kor',
|
|
||||||
'Arabic': 'ara',
|
|
||||||
'Hindi': 'hin',
|
|
||||||
'Indonesian': 'ind',
|
|
||||||
'Malay': 'mal',
|
|
||||||
'Thai': 'tha',
|
|
||||||
'Vietnamese': 'vie',
|
|
||||||
'Turkish': 'tur',
|
|
||||||
'Ukrainian': 'ukr',
|
|
||||||
'Hungarian': 'hun',
|
|
||||||
'Czech': 'cze',
|
|
||||||
'Finnish': 'fin',
|
|
||||||
'Danish': 'dan',
|
|
||||||
'Norwegian': 'nor',
|
|
||||||
'Swedish': 'swe',
|
|
||||||
'Greek': 'gre',
|
|
||||||
'Slovak': 'slo',
|
|
||||||
'Croatian': 'hrv',
|
|
||||||
'Lithuanian': 'lit',
|
|
||||||
'Romanian': 'rum',
|
|
||||||
'Bulgarian': 'bul',
|
|
||||||
'Latvian': 'lav',
|
|
||||||
'Estonian': 'est',
|
|
||||||
'Persian': 'per',
|
|
||||||
'Hebrew': 'heb',
|
|
||||||
'Serbian': 'srp',
|
|
||||||
'Albanian': 'alb',
|
|
||||||
'Tagalog': 'tgl',
|
|
||||||
'Azerbaijani': 'aze',
|
|
||||||
'Basque': 'baq',
|
|
||||||
'Belarusian': 'bel',
|
|
||||||
'Bengali': 'ben',
|
|
||||||
'Bosnian': 'bos',
|
|
||||||
'Cebuano': 'ceb',
|
|
||||||
'Esperanto': 'epo',
|
|
||||||
'Galician': 'glg',
|
|
||||||
'Georgian': 'geo',
|
|
||||||
'Gujarati': 'guj',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,23 @@
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:cpd_app/image_translator.dart';
|
import 'package:cpd_app/image_translator.dart';
|
||||||
import 'package:cpd_app/ocr_page.dart';
|
import 'package:cpd_app/ocr_page.dart';
|
||||||
import 'package:cpd_app/bottom_bar.dart';
|
import 'package:cpd_app/session_list_view.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
void main() {
|
Future<void> main() async {
|
||||||
|
FlutterView? flutterView = PlatformDispatcher.instance.views.firstOrNull;
|
||||||
|
if (flutterView == null || flutterView.physicalSize.isEmpty) {
|
||||||
|
PlatformDispatcher.instance.onMetricsChanged = () {
|
||||||
|
flutterView = PlatformDispatcher.instance.views.firstOrNull;
|
||||||
|
if (flutterView != null && !flutterView!.physicalSize.isEmpty) {
|
||||||
|
PlatformDispatcher.instance.onMetricsChanged = null;
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
|
@ -24,8 +37,9 @@ class OpticText extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
backgroundColor: const Color.fromARGB(99, 78, 72, 72),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
title: const Text('Optic Text'),
|
title: const Text('Optic Text'),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
|
@ -36,10 +50,12 @@ class OpticText extends StatelessWidget {
|
||||||
MaterialPageRoute(builder: (context) => const ImageToolsPage()),
|
MaterialPageRoute(builder: (context) => const ImageToolsPage()),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
foregroundColor: const Color.fromARGB(255, 0, 0, 0)),
|
||||||
child: const Text('Image Tools'),
|
child: const Text('Image Tools'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
bottomNavigationBar: const BottomBar(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +66,9 @@ class ImageToolsPage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
backgroundColor: const Color.fromARGB(99, 78, 72, 72),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
title: const Text('Image Tools'),
|
title: const Text('Image Tools'),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
|
@ -58,12 +76,16 @@ class ImageToolsPage extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
|
key: const Key('ocr_button'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (context) => const OCRPage()),
|
MaterialPageRoute(builder: (context) => const OCRPage()),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
foregroundColor: const Color.fromARGB(255, 0, 0, 0)),
|
||||||
child: const Text('OCR'),
|
child: const Text('OCR'),
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
|
@ -74,8 +96,24 @@ class ImageToolsPage extends StatelessWidget {
|
||||||
builder: (context) => const ImageTranslation()),
|
builder: (context) => const ImageTranslation()),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
foregroundColor: const Color.fromARGB(255, 0, 0, 0)),
|
||||||
child: const Text('Image Translation'),
|
child: const Text('Image Translation'),
|
||||||
),
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const SessionListView()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
foregroundColor: const Color.fromARGB(255, 0, 0, 0)),
|
||||||
|
child: const Text('Previous Images'),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -5,48 +5,98 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'language_utils.dart';
|
import 'language_utils.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
class OCRPage extends StatefulWidget {
|
class OCRPage extends StatefulWidget {
|
||||||
const OCRPage({Key? key}) : super(key: key);
|
const OCRPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<OCRPage> createState() => _OCRPageState();
|
State<OCRPage> createState() => OCRPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _OCRPageState extends State<OCRPage> {
|
class OCRPageState extends State<OCRPage> {
|
||||||
String _extractedText = '';
|
List<String> tessLangCodes = LanguageUtils.tessLanguages.keys.toList();
|
||||||
|
String extractedText = '';
|
||||||
final ImageUploader _imageUploader = ImageUploader();
|
final ImageUploader _imageUploader = ImageUploader();
|
||||||
final HttpUtils _httpUtils = HttpUtils();
|
final HttpUtils _httpUtils = HttpUtils();
|
||||||
String _selectedLanguage = 'Auto';
|
String _selectedLanguage = 'Auto';
|
||||||
XFile? _selectedImage;
|
XFile? selectedImage;
|
||||||
|
|
||||||
Future<void> uploadImage(
|
Future<void> getText(Uint8List imageBytes, String selectedLanguage) async {
|
||||||
Uint8List imageBytes, String selectedLanguage) async {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_extractedText = '';
|
extractedText = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
//preprocessing, maybe
|
if (!(await _httpUtils.internetConnectivityCheck())) {
|
||||||
|
_httpUtils.triggerNoInetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
String text = '';
|
String text = '';
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
_httpUtils.showProgressBar(context);
|
||||||
|
try {
|
||||||
|
if (Platform.isWindows) {
|
||||||
|
if (selectedLanguage == 'Auto') {
|
||||||
|
String lang = await _httpUtils.checkLang(imageBytes, 'image.jpg');
|
||||||
|
String mappedLang = LanguageUtils.reMapping[lang]!;
|
||||||
|
text =
|
||||||
|
await _httpUtils.extractTextKnownSource(imageBytes, mappedLang);
|
||||||
|
} else {
|
||||||
|
String langCode =
|
||||||
|
LanguageUtils.translatorLanguages[selectedLanguage]!;
|
||||||
|
text = await _httpUtils.extractTextKnownSource(imageBytes, langCode);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (selectedLanguage == 'Auto') {
|
if (selectedLanguage == 'Auto') {
|
||||||
String lang = await _httpUtils.checkLang(imageBytes, 'image.jpg');
|
String lang = await _httpUtils.checkLang(imageBytes, 'image.jpg');
|
||||||
text = await _imageUploader.performOcr(imageBytes, 'image.jpg', lang);
|
text = await _imageUploader.performOcr(imageBytes, 'image.jpg', lang);
|
||||||
} else {
|
} else {
|
||||||
String langCode = LanguageUtils.tessLanguages[selectedLanguage]!;
|
String langCode = LanguageUtils.tessLanguages[selectedLanguage]!;
|
||||||
text = await _imageUploader.performOcr(imageBytes, 'image.jpg', langCode);
|
text = await _imageUploader.performOcr(
|
||||||
|
imageBytes, 'image.jpg', langCode);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
_httpUtils.hideProgressBar(context);
|
||||||
|
}
|
||||||
|
if (text == "") {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: "Text could not be extracted",
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
timeInSecForIosWeb: 1,
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
_extractedText = text;
|
extractedText = text;
|
||||||
_selectedImage = null;
|
selectedImage = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<String> languages = LanguageUtils.tessLanguages.keys.toList();
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
backgroundColor: const Color.fromARGB(99, 78, 72, 72),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
title: const Text('OCR'),
|
title: const Text('OCR'),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.wifi),
|
||||||
|
onPressed: () async {
|
||||||
|
if (!await _httpUtils.internetConnectivityCheck()) {
|
||||||
|
_httpUtils.triggerNoInetToast();
|
||||||
|
} else {
|
||||||
|
_httpUtils.triggerConnectedToast();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -54,23 +104,33 @@ class _OCRPageState extends State<OCRPage> {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
DropdownButton<String>(
|
DropdownButton<String>(
|
||||||
value: _selectedLanguage,
|
value: _selectedLanguage,
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
dropdownColor: const Color(0xFF4E4848),
|
||||||
onChanged: (newValue) {
|
onChanged: (newValue) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedLanguage = newValue!;
|
_selectedLanguage = newValue!;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
items: languages.map<DropdownMenuItem<String>>((String value) {
|
items:
|
||||||
|
tessLangCodes.map<DropdownMenuItem<String>>((String value) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: value,
|
value: value,
|
||||||
child: Text(value),
|
child: Text(value),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
_selectedImage != null
|
//Image preview hier
|
||||||
? SizedBox(
|
selectedImage != null
|
||||||
|
? Container(
|
||||||
width: MediaQuery.of(context).size.width * 0.8,
|
width: MediaQuery.of(context).size.width * 0.8,
|
||||||
height: MediaQuery.of(context).size.height * 0.4,
|
height: MediaQuery.of(context).size.height * 0.4,
|
||||||
child: Image.file(File(_selectedImage!.path)),
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Image.file(File(selectedImage!.path)),
|
||||||
)
|
)
|
||||||
: Container(),
|
: Container(),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
|
@ -80,33 +140,44 @@ class _OCRPageState extends State<OCRPage> {
|
||||||
await picker.pickImage(source: ImageSource.gallery);
|
await picker.pickImage(source: ImageSource.gallery);
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedImage = image;
|
selectedImage = image;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
foregroundColor: const Color.fromARGB(255, 0, 0, 0)),
|
||||||
child: const Text('Select Image'),
|
child: const Text('Select Image'),
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _selectedImage != null
|
onPressed: selectedImage != null
|
||||||
? () async {
|
? () async {
|
||||||
if (_selectedImage != null) {
|
if (selectedImage != null && selectedImage!.path != "") {
|
||||||
File imageFile = File(_selectedImage!.path);
|
File imageFile = File(selectedImage!.path);
|
||||||
List<int> imageBytes = imageFile.readAsBytesSync();
|
List<int> imageBytes = imageFile.readAsBytesSync();
|
||||||
await uploadImage(
|
await getText(
|
||||||
Uint8List.fromList(imageBytes), _selectedLanguage);
|
Uint8List.fromList(imageBytes), _selectedLanguage);
|
||||||
|
} else if (selectedImage != null &&
|
||||||
|
selectedImage!.path == "") {
|
||||||
|
Uint8List? list = await selectedImage?.readAsBytes();
|
||||||
|
await getText(list!, _selectedLanguage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
foregroundColor: const Color.fromARGB(255, 0, 0, 0)),
|
||||||
child: const Text('Extract Text'),
|
child: const Text('Extract Text'),
|
||||||
),
|
),
|
||||||
_extractedText.isNotEmpty
|
extractedText.isNotEmpty
|
||||||
? Expanded(
|
? Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
initialValue: _extractedText,
|
initialValue: extractedText,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Extracted Text',
|
labelText: 'Extracted Text',
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
|
@ -115,12 +186,19 @@ class _OCRPageState extends State<OCRPage> {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Container(),
|
: Container(),
|
||||||
TextButton(
|
|
||||||
|
extractedText.isNotEmpty
|
||||||
|
? TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Clipboard.setData(ClipboardData(text: _extractedText));
|
Clipboard.setData(ClipboardData(text: extractedText));
|
||||||
},
|
},
|
||||||
child: const Text('Copy to Clipboard'),
|
style: TextButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(0, 33, 149, 243),
|
||||||
|
foregroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
),
|
),
|
||||||
|
child: const Text('Copy to Clipboard'),
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
class ImageList {
|
||||||
|
static Map<String, Uint8List> images = {};
|
||||||
|
|
||||||
|
static Map<String, Uint8List> getImages() {
|
||||||
|
return images;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addImage(String name, Uint8List image) {
|
||||||
|
images[name] = image;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:cpd_app/file_utils.dart';
|
||||||
|
import 'package:cpd_app/session_list.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SessionListView extends StatefulWidget {
|
||||||
|
const SessionListView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SessionListView> createState() => _SessionListViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SessionListViewState extends State<SessionListView> {
|
||||||
|
final Map<String, Uint8List> _images = ImageList.getImages();
|
||||||
|
String? _selectedImageKey;
|
||||||
|
bool _showSaveButton = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: const Color.fromARGB(99, 78, 72, 72),
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 161, 120, 17),
|
||||||
|
title: const Text('Image Gallery'),
|
||||||
|
),
|
||||||
|
body: GridView.builder(
|
||||||
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 3,
|
||||||
|
crossAxisSpacing: 4.0,
|
||||||
|
mainAxisSpacing: 4.0,
|
||||||
|
),
|
||||||
|
itemCount: _images.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
String imageKey = _images.keys.elementAt(index);
|
||||||
|
Uint8List imageData = _images[imageKey]!;
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_selectedImageKey = imageKey;
|
||||||
|
_showSaveButton = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: _selectedImageKey == imageKey
|
||||||
|
? Colors.blue
|
||||||
|
: Colors.transparent,
|
||||||
|
width: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Image.memory(
|
||||||
|
imageData,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
floatingActionButton: _showSaveButton
|
||||||
|
? FloatingActionButton(
|
||||||
|
onPressed: () {
|
||||||
|
saveImage(_selectedImageKey!);
|
||||||
|
setState(() {
|
||||||
|
_showSaveButton = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Icon(Icons.save),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveImage(String imageKey) {
|
||||||
|
FileUtils.saveTranslatedImage(ImageList.getImages()[imageKey]!);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,10 +5,12 @@
|
||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import connectivity_macos
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
}
|
}
|
||||||
|
|
48
pubspec.lock
48
pubspec.lock
|
@ -97,6 +97,38 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
|
connectivity:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: connectivity
|
||||||
|
sha256: a8e91263cf3e25fb5cc95e19dfde4999e32a648ac3b9e8a558a28165731678f8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.6"
|
||||||
|
connectivity_for_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: connectivity_for_web
|
||||||
|
sha256: "01a390c1d5adc2ed1fa1f52d120c07fe9fd01166a93f965a832fd6cfc0ea6482"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.0+1"
|
||||||
|
connectivity_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: connectivity_macos
|
||||||
|
sha256: "51ae08d5162eca9669b9d8951ed83ce19c5355a81149f94e4dee2740beb93628"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.1+2"
|
||||||
|
connectivity_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: connectivity_platform_interface
|
||||||
|
sha256: "2d82e942df9d49f29a24bb07fb5ce085d4a53e47818c62364d2b6deb9e0d7a8e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -256,6 +288,14 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
fluttertoast:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fluttertoast
|
||||||
|
sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.2.4"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -280,6 +320,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
|
image_gallery_saver:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: image_gallery_saver
|
||||||
|
sha256: "0aba74216a4d9b0561510cb968015d56b701ba1bd94aace26aacdd8ae5761816"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
13
pubspec.yaml
13
pubspec.yaml
|
@ -9,7 +9,7 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
dio: ^4.0.0 # For making HTTP requests
|
dio: ^4.0.0
|
||||||
file_picker: ^4.1.4
|
file_picker: ^4.1.4
|
||||||
image_picker: ^0.8.1
|
image_picker: ^0.8.1
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
|
@ -17,7 +17,10 @@ dependencies:
|
||||||
http: ^1.1.0
|
http: ^1.1.0
|
||||||
flutter_tesseract_ocr:
|
flutter_tesseract_ocr:
|
||||||
http_parser: ^4.0.2
|
http_parser: ^4.0.2
|
||||||
#opencv_4: ^1.0.0
|
fluttertoast: ^8.0.8
|
||||||
|
connectivity: ^3.0.6
|
||||||
|
image_gallery_saver: '^2.0.3'
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -27,14 +30,12 @@ dev_dependencies:
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
assets:
|
assets:
|
||||||
- assets/
|
- assets/schild.png
|
||||||
- assets/tessdata_config.json
|
- assets/tessdata_config.json
|
||||||
- assets/tessdata/eng.traineddata
|
- assets/tessdata/eng.traineddata
|
||||||
- assets/tessdata/deu.traineddata
|
- assets/tessdata/deu.traineddata
|
||||||
- assets/tessdata/jpn.traineddata
|
- assets/tessdata/jpn.traineddata
|
||||||
- assets/tessdata/chi_sim.traineddata
|
- assets/tessdata/fra.traineddata
|
||||||
- assets/tessdata/ell.traineddata
|
|
||||||
- assets/tessdata/ara.traineddata
|
|
||||||
- assets/tessdata/rus.traineddata
|
- assets/tessdata/rus.traineddata
|
||||||
|
|
||||||
uses-material-design: true
|
uses-material-design: true
|
Binary file not shown.
|
@ -1,21 +1,125 @@
|
||||||
import 'package:cpd_app/image_uploader.dart';
|
import 'dart:io';
|
||||||
|
import 'package:cpd_app/session_list.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:cpd_app/main.dart';
|
||||||
|
import 'package:cpd_app/ocr_page.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
|
||||||
|
Future<Uint8List> loadImageBytesFromAssets(String imagePath) async {
|
||||||
|
final ByteData data = await rootBundle.load(imagePath);
|
||||||
|
return data.buffer.asUint8List();
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
setUpAll(() {
|
||||||
|
HttpOverrides.global = null;
|
||||||
|
});
|
||||||
|
testWidgets('Check ocr UI', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(const MyApp());
|
||||||
|
|
||||||
test('Test OCR functionality', () async {
|
final buttonsFinder = find.byType(ElevatedButton);
|
||||||
final ImageUploader imageUploader = ImageUploader();
|
|
||||||
String imageName = "lorem.png";
|
|
||||||
|
|
||||||
var img = await imageUploader.buildImageFile(imageName);
|
expect(buttonsFinder, findsWidgets);
|
||||||
assert(img.lengthInBytes > 0);
|
await tester.tap(find.widgetWithText(ElevatedButton, "Image Tools"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('OCR'), findsOneWidget);
|
||||||
|
|
||||||
// momentan auskommentiert, weil das pathproviderplugin irgentwie gemockt werden muss und ich es noch nicht hinbekommen habe
|
var ocrbtn = find.widgetWithText(ElevatedButton, "OCR");
|
||||||
//mock response, weil man keine http requests in tests machen kann
|
expect(ocrbtn, findsOneWidget);
|
||||||
// String mockResponse = "eng";
|
|
||||||
|
|
||||||
// String text = await imageUploader.performOcr(img, imageName, mockResponse);
|
var imgtrans = find.widgetWithText(ElevatedButton, "Image Translation");
|
||||||
// assert(text.contains("Lorem ipsum"));
|
expect(imgtrans, findsOneWidget);
|
||||||
|
var prev = find.widgetWithText(ElevatedButton, "Previous Images");
|
||||||
|
expect(prev, findsOneWidget);
|
||||||
|
|
||||||
|
await tester.tap(find.widgetWithText(ElevatedButton, "OCR"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('OCR'), findsOneWidget);
|
||||||
|
expect(find.text('Select Image'), findsOneWidget);
|
||||||
|
expect(find.widgetWithText(DropdownButton<String>, 'Auto'), findsOneWidget);
|
||||||
|
await tester.tap(find.widgetWithText(DropdownButton<String>, 'Auto'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('German'), findsOneWidget);
|
||||||
|
expect(find.text('English'), findsOneWidget);
|
||||||
|
expect(find.text('French'), findsOneWidget);
|
||||||
|
expect(find.text('Russian'), findsOneWidget);
|
||||||
|
await tester.tap(find.widgetWithText(DropdownMenuItem<String>, 'German'));
|
||||||
|
await tester.tap(find.widgetWithText(ElevatedButton, "Select Image"));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
final ocrFinder = find.byType(OCRPage);
|
||||||
|
final ocrState = tester.state<OCRPageState>(ocrFinder);
|
||||||
|
Uint8List bytes = await loadImageBytesFromAssets('assets/schild.png');
|
||||||
|
|
||||||
|
ocrState.setState(() {
|
||||||
|
var image = XFile.fromData(bytes);
|
||||||
|
ocrState.selectedImage = image;
|
||||||
|
});
|
||||||
|
expect(ocrState.selectedImage, isNotNull);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.widgetWithText(ElevatedButton, "Extract Text"), findsOneWidget);
|
||||||
|
});
|
||||||
|
testWidgets('Check imageTranslateUI', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(const MyApp());
|
||||||
|
final buttonsFinder = find.byType(ElevatedButton);
|
||||||
|
expect(buttonsFinder, findsWidgets);
|
||||||
|
await tester.tap(find.widgetWithText(ElevatedButton, "Image Tools"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('Image Translation'), findsOneWidget);
|
||||||
|
var btn1 = find.widgetWithText(ElevatedButton, "Image Translation");
|
||||||
|
expect(btn1, findsOneWidget);
|
||||||
|
await tester.tap(btn1);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('Image Translation'), findsOneWidget);
|
||||||
|
expect(
|
||||||
|
find.widgetWithText(DropdownButton<String>, 'English'), findsOneWidget);
|
||||||
|
await tester.tap(find.widgetWithText(DropdownButton<String>, 'English'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('German'), findsExactly(2));
|
||||||
|
expect(find.text('English'), findsExactly(2));
|
||||||
|
expect(find.text('French'), findsExactly(1));
|
||||||
|
expect(find.text('Russian'), findsExactly(1));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.widgetWithText(DropdownButton<String>, 'German'));
|
||||||
|
expect(find.text('German'), findsExactly(2));
|
||||||
|
expect(find.text('English'), findsExactly(2));
|
||||||
|
expect(find.text('French'), findsExactly(1));
|
||||||
|
expect(find.text('Russian'), findsExactly(1));
|
||||||
|
//
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Check listPreview', (WidgetTester tester) async {
|
||||||
|
ImageList.addImage(
|
||||||
|
"test1", await loadImageBytesFromAssets('assets/schild.png'));
|
||||||
|
ImageList.addImage(
|
||||||
|
"test2", await loadImageBytesFromAssets('assets/schild.png'));
|
||||||
|
ImageList.addImage(
|
||||||
|
"test3", await loadImageBytesFromAssets('assets/schild.png'));
|
||||||
|
ImageList.addImage(
|
||||||
|
"test3", await loadImageBytesFromAssets('assets/schild.png'));
|
||||||
|
|
||||||
|
await tester.pumpWidget(const MyApp());
|
||||||
|
|
||||||
|
final buttonsFinder = find.byType(ElevatedButton);
|
||||||
|
expect(buttonsFinder, findsWidgets);
|
||||||
|
|
||||||
|
await tester.tap(find.widgetWithText(ElevatedButton, "Image Tools"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
var btn1 = find.widgetWithText(ElevatedButton, "Previous Images");
|
||||||
|
expect(btn1, findsOneWidget);
|
||||||
|
await tester.tap(btn1);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('Image Gallery'), findsOneWidget);
|
||||||
|
|
||||||
|
final gridViewFinder = find.byType(GridView);
|
||||||
|
expect(gridViewFinder, findsOneWidget);
|
||||||
|
|
||||||
|
const numberOfEntries = 4;
|
||||||
|
expect(find.byType(GestureDetector), findsNWidgets(numberOfEntries));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake)
|
||||||
# https://github.com/flutter/flutter/issues/57146.
|
# https://github.com/flutter/flutter/issues/57146.
|
||||||
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
|
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
|
||||||
|
|
||||||
|
# Set fallback configurations for older versions of the flutter tool.
|
||||||
|
if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
|
||||||
|
set(FLUTTER_TARGET_PLATFORM "windows-x64")
|
||||||
|
endif()
|
||||||
|
|
||||||
# === Flutter Library ===
|
# === Flutter Library ===
|
||||||
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
|
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
|
||||||
|
|
||||||
|
@ -92,7 +97,7 @@ add_custom_command(
|
||||||
COMMAND ${CMAKE_COMMAND} -E env
|
COMMAND ${CMAKE_COMMAND} -E env
|
||||||
${FLUTTER_TOOL_ENVIRONMENT}
|
${FLUTTER_TOOL_ENVIRONMENT}
|
||||||
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
|
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
|
||||||
windows-x64 $<CONFIG>
|
${FLUTTER_TARGET_PLATFORM} $<CONFIG>
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
add_custom_target(flutter_assemble DEPENDS
|
add_custom_target(flutter_assemble DEPENDS
|
||||||
|
|
Loading…
Reference in New Issue