import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_tesseract_ocr/flutter_tesseract_ocr.dart'; import 'package:prasule/main.dart'; import 'package:prasule/network/tessdata.dart'; import 'package:prasule/pw/platformbutton.dart'; /// Used to manage downloaded Tessdata for OCR class TessdataListView extends StatefulWidget { /// Used to manage downloaded Tessdata for OCR const TessdataListView({super.key}); @override State createState() => _TessdataListViewState(); } class _TessdataListViewState extends State { final _tessdata = [ {"eng": true}, ]; @override void didChangeDependencies() { super.didChangeDependencies(); loadAllTessdata(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(AppLocalizations.of(context).ocrData)), body: Center( child: SizedBox( width: MediaQuery.of(context).size.width * 0.9, height: MediaQuery.of(context).size.height, child: (_tessdata.length == 1) ? const Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: 80, height: 80, child: CircularProgressIndicator(), ), ], ) : ListView.builder( itemBuilder: (context, i) => ListTile( title: Text(_tessdata[i].keys.first), trailing: TextButton( child: Text( _tessdata[i][_tessdata[i].keys.first]! ? AppLocalizations.of(context).downloaded : AppLocalizations.of(context).download, ), onPressed: () async { final lang = _tessdata[i].keys.first; if (_tessdata[i][lang]!) { // deleting data await showAdaptiveDialog( context: context, builder: (context) => AlertDialog.adaptive( title: Text(AppLocalizations.of(context).sureDialog), content: Text( AppLocalizations.of(context).deleteOcr(lang), ), actions: [ PlatformButton( text: AppLocalizations.of(context).yes, onPressed: () async { await TessdataApi.deleteData(lang); _tessdata[i][lang] = true; if (context.mounted) { Navigator.of(context).pop(); } }, ), PlatformButton( text: AppLocalizations.of(context).no, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ); setState(() {}); return; } // TODO: handle wifi errors //* downloading traineddata final progressStream = StreamController(); unawaited( showAdaptiveDialog( context: context, builder: (c) => AlertDialog.adaptive( title: Text( AppLocalizations.of(context) .langDownloadDialog(lang), ), content: StreamBuilder( builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } if (snapshot.hasError) { return const Text("Error"); } return Text( AppLocalizations.of(context) .langDownloadProgress(snapshot.data!), ); }, stream: progressStream.stream, ), ), ), ); await TessdataApi.downloadData( lang, callback: (a, b) { if (progressStream.isClosed) return; final p = a / b * 1000; progressStream.add(p.roundToDouble() / 10); if (p / 10 >= 100) { logger.i("Done"); Navigator.of(context, rootNavigator: true) .pop("dialog"); _tessdata[i][lang] = true; progressStream.close(); } setState(() {}); }, ); }, ), ), itemCount: _tessdata.length, ), ), ), ); } /// Used to find which `.traineddata` is already downloaded and which not /// so we can show it to the user Future loadAllTessdata() async { final tessDir = Directory(await FlutterTesseractOcr.getTessdataPath()); if (!tessDir.existsSync()) tessDir.createSync(recursive: true); final d = await TessdataApi.getAvailableData(); final dataStatus = >[]; for (final data in d) { final e = {}; e[data] = false; dataStatus.add(e); } final appDir = tessDir.listSync(); for (final file in appDir) { if (file is! File || !file.path.endsWith("traineddata") || file.path.endsWith("eng.traineddata")) continue; logger.i(file.path); final filename = file.path.split("/").last; dataStatus[dataStatus.indexWhere( (element) => element.keys.first == filename.replaceAll(".traineddata", ""), )][filename.replaceAll(".traineddata", "")] = true; } _tessdata.addAll(dataStatus); setState(() {}); } }