2022-04-14 10:37:47 +03:00
import ' package:flutter/gestures.dart ' ;
2020-11-15 15:04:05 +03:00
import ' package:flutter/material.dart ' ;
import ' dart:async ' ;
2022-04-19 08:07:45 +03:00
import ' package:flutter_smart_dialog/flutter_smart_dialog.dart ' ;
import ' models/model.dart ' ;
2021-08-02 15:54:56 +03:00
2022-03-07 17:54:34 +03:00
final globalKey = GlobalKey < NavigatorState > ( ) ;
2022-04-12 17:38:39 +03:00
final navigationBarKey = GlobalKey ( ) ;
2022-03-07 17:54:34 +03:00
2022-03-17 16:03:52 +03:00
var isAndroid = false ;
var isIOS = false ;
var isWeb = false ;
2022-05-23 11:02:37 +03:00
var isWebDesktop = false ;
2022-03-17 16:03:52 +03:00
var isDesktop = false ;
var version = " " ;
2022-03-24 12:58:33 +03:00
int androidVersion = 0 ;
2022-03-17 16:03:52 +03:00
2021-08-02 15:54:56 +03:00
typedef F = String Function ( String ) ;
2022-02-09 21:07:53 +03:00
typedef FMethod = String Function ( String , dynamic ) ;
2021-08-02 15:54:56 +03:00
class Translator {
2022-02-17 10:22:14 +03:00
static late F call ;
2021-04-24 19:19:35 +03:00
}
2020-11-17 13:10:49 +03:00
2020-11-15 20:13:26 +03:00
class MyTheme {
2020-11-20 11:37:48 +03:00
MyTheme . _ ( ) ;
2022-02-02 12:25:56 +03:00
2020-11-15 20:13:26 +03:00
static const Color grayBg = Color ( 0xFFEEEEEE ) ;
static const Color white = Color ( 0xFFFFFFFF ) ;
2020-11-16 17:00:09 +03:00
static const Color accent = Color ( 0xFF0071FF ) ;
2020-11-19 12:22:42 +03:00
static const Color accent50 = Color ( 0x770071FF ) ;
2020-11-26 21:14:27 +03:00
static const Color accent80 = Color ( 0xAA0071FF ) ;
2020-11-19 13:41:37 +03:00
static const Color canvasColor = Color ( 0xFF212121 ) ;
2020-11-20 08:06:52 +03:00
static const Color border = Color ( 0xFFCCCCCC ) ;
2022-01-31 11:22:05 +03:00
static const Color idColor = Color ( 0xFF00B6F0 ) ;
static const Color darkGray = Color ( 0xFFB9BABC ) ;
2020-11-15 20:13:26 +03:00
}
2021-08-02 15:54:56 +03:00
final ButtonStyle flatButtonStyle = TextButton . styleFrom (
2022-05-18 10:47:07 +03:00
minimumSize: Size ( 0 , 36 ) ,
padding: EdgeInsets . symmetric ( horizontal: 16.0 , vertical: 10.0 ) ,
2021-08-02 15:54:56 +03:00
shape: const RoundedRectangleBorder (
borderRadius: BorderRadius . all ( Radius . circular ( 2.0 ) ) ,
) ,
) ;
2022-04-19 12:53:35 +03:00
void showToast ( String text , { Duration ? duration } ) {
SmartDialog . showToast ( text , displayTime: duration ) ;
2022-04-15 12:50:15 +03:00
}
2022-04-19 08:07:45 +03:00
void showLoading ( String text , { bool clickMaskDismiss = false } ) {
SmartDialog . dismiss ( ) ;
SmartDialog . showLoading (
clickMaskDismiss: false ,
builder: ( context ) {
return Container (
color: MyTheme . white ,
constraints: BoxConstraints ( maxWidth: 240 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
SizedBox ( height: 30 ) ,
Center ( child: CircularProgressIndicator ( ) ) ,
SizedBox ( height: 20 ) ,
Center (
child: Text ( Translator . call ( text ) ,
style: TextStyle ( fontSize: 15 ) ) ) ,
SizedBox ( height: 20 ) ,
Center (
child: TextButton (
style: flatButtonStyle ,
onPressed: ( ) {
SmartDialog . dismiss ( ) ;
backToHome ( ) ;
} ,
child: Text ( Translator . call ( ' Cancel ' ) ,
style: TextStyle ( color: MyTheme . accent ) ) ) )
] ) ) ;
} ) ;
2020-11-16 16:21:27 +03:00
}
2022-03-13 18:07:52 +03:00
backToHome ( ) {
Navigator . popUntil ( globalKey . currentContext ! , ModalRoute . withName ( " / " ) ) ;
}
2022-03-12 19:32:44 +03:00
typedef DialogBuilder = CustomAlertDialog Function (
2022-04-19 08:07:45 +03:00
StateSetter setState , void Function ( [ dynamic ] ) close ) ;
2022-03-12 19:32:44 +03:00
class DialogManager {
2022-04-19 08:07:45 +03:00
static int _tag = 0 ;
2022-03-12 19:32:44 +03:00
2022-04-19 08:07:45 +03:00
static dismissByTag ( String tag , [ result ] ) {
SmartDialog . dismiss ( tag: tag , result: result ) ;
2022-02-28 13:29:25 +03:00
}
2022-03-12 19:32:44 +03:00
static Future < T ? > show < T > ( DialogBuilder builder ,
2022-04-20 18:43:19 +03:00
{ bool clickMaskDismiss = false ,
2022-04-21 05:02:47 +03:00
bool backDismiss = false ,
2022-04-19 08:07:45 +03:00
String ? tag ,
bool useAnimation = true } ) async {
final t ;
if ( tag ! = null ) {
t = tag ;
} else {
_tag + = 1 ;
t = _tag . toString ( ) ;
}
SmartDialog . dismiss ( status: SmartStatus . allToast ) ;
SmartDialog . dismiss ( status: SmartStatus . loading ) ;
final close = ( [ res ] ) {
SmartDialog . dismiss ( tag: t , result: res ) ;
} ;
final res = await SmartDialog . show < T > (
tag: t ,
2022-04-20 18:43:19 +03:00
clickMaskDismiss: clickMaskDismiss ,
2022-04-21 05:02:47 +03:00
backDismiss: backDismiss ,
2022-04-19 08:07:45 +03:00
useAnimation: useAnimation ,
builder: ( _ ) = > StatefulBuilder (
builder: ( _ , setState ) = > builder ( setState , close ) ) ) ;
2022-03-12 19:32:44 +03:00
return res ;
}
2022-02-28 11:11:21 +03:00
}
2022-02-02 12:25:56 +03:00
2022-03-12 19:32:44 +03:00
class CustomAlertDialog extends StatelessWidget {
CustomAlertDialog (
{ required this . title ,
required this . content ,
required this . actions ,
this . contentPadding } ) ;
2020-11-18 07:49:43 +03:00
2022-03-12 19:32:44 +03:00
final Widget title ;
final Widget content ;
final List < Widget > actions ;
final double ? contentPadding ;
@ override
Widget build ( BuildContext context ) {
2022-04-21 05:02:47 +03:00
return AlertDialog (
scrollable: true ,
title: title ,
contentPadding:
EdgeInsets . symmetric ( horizontal: contentPadding ? ? 25 , vertical: 10 ) ,
content: content ,
actions: actions ,
) ;
2022-03-12 19:32:44 +03:00
}
2020-11-16 17:00:09 +03:00
}
2020-11-16 17:12:32 +03:00
2022-03-12 19:32:44 +03:00
void msgBox ( String type , String title , String text , { bool ? hasCancel } ) {
2020-11-28 20:36:10 +03:00
var wrap = ( String text , void Function ( ) onPressed ) = > ButtonTheme (
padding: EdgeInsets . symmetric ( horizontal: 20 , vertical: 10 ) ,
2022-02-02 12:25:56 +03:00
materialTapTargetSize: MaterialTapTargetSize . shrinkWrap ,
//limits the touch area to the button area
minWidth: 0 ,
//wraps child's width
2020-11-28 20:36:10 +03:00
height: 0 ,
2021-08-02 15:54:56 +03:00
child: TextButton (
style: flatButtonStyle ,
2020-11-28 20:36:10 +03:00
onPressed: onPressed ,
2021-08-02 15:54:56 +03:00
child: Text ( Translator . call ( text ) ,
style: TextStyle ( color: MyTheme . accent ) ) ) ) ;
2020-11-28 20:36:10 +03:00
2022-04-19 08:07:45 +03:00
SmartDialog . dismiss ( ) ;
2020-11-28 20:36:10 +03:00
final buttons = [
2021-08-02 15:54:56 +03:00
wrap ( Translator . call ( ' OK ' ) , ( ) {
2022-04-19 08:07:45 +03:00
SmartDialog . dismiss ( ) ;
2022-03-13 18:07:52 +03:00
backToHome ( ) ;
2020-11-28 20:36:10 +03:00
} )
] ;
2020-11-24 06:25:56 +03:00
if ( hasCancel = = null ) {
hasCancel = type ! = ' error ' ;
}
2020-11-28 20:36:10 +03:00
if ( hasCancel ) {
buttons . insert (
2022-04-19 08:07:45 +03:00
0 ,
2021-08-02 15:54:56 +03:00
wrap ( Translator . call ( ' Cancel ' ) , ( ) {
2022-04-19 08:07:45 +03:00
SmartDialog . dismiss ( ) ;
2020-11-28 20:36:10 +03:00
} ) ) ;
}
2022-04-19 08:07:45 +03:00
DialogManager . show ( ( setState , close ) = > CustomAlertDialog (
title: Text ( translate ( title ) , style: TextStyle ( fontSize: 21 ) ) ,
content: Text ( Translator . call ( text ) , style: TextStyle ( fontSize: 15 ) ) ,
actions: buttons ) ) ;
2020-11-21 09:40:28 +03:00
}
2020-11-25 13:33:09 +03:00
Color str2color ( String str , [ alpha = 0xFF ] ) {
var hash = 160 < < 16 + 114 < < 8 + 91 ;
for ( var i = 0 ; i < str . length ; i + = 1 ) {
hash = str . codeUnitAt ( i ) + ( ( hash < < 5 ) - hash ) ;
}
2021-08-14 09:14:01 +03:00
hash = hash % 16777216 ;
return Color ( ( hash & 0xFF7FFF ) | ( alpha < < 24 ) ) ;
2020-11-25 13:33:09 +03:00
}
2022-02-02 12:25:56 +03:00
2022-03-17 16:03:52 +03:00
const K = 1024 ;
const M = K * K ;
const G = M * K ;
String readableFileSize ( double size ) {
if ( size < K ) {
return size . toString ( ) + " B " ;
} else if ( size < M ) {
return ( size / K ) . toStringAsFixed ( 2 ) + " KB " ;
} else if ( size < G ) {
return ( size / M ) . toStringAsFixed ( 2 ) + " MB " ;
} else {
return ( size / G ) . toStringAsFixed ( 2 ) + " GB " ;
}
}
2022-04-14 10:37:47 +03:00
/// Flutter can't not catch PointerMoveEvent when size is 1
/// This will happen in Android AccessibilityService Input
/// android can't init dispatching size yet ,see: https://stackoverflow.com/questions/59960451/android-accessibility-dispatchgesture-is-it-possible-to-specify-pressure-for-a
/// use this temporary solution until flutter or android fixes the bug
class AccessibilityListener extends StatelessWidget {
final Widget ? child ;
static final offset = 100 ;
AccessibilityListener ( { this . child } ) ;
@ override
Widget build ( BuildContext context ) {
return Listener (
onPointerDown: ( evt ) {
2022-05-26 13:25:16 +03:00
if ( evt . size = = 1 ) {
GestureBinding . instance . handlePointerEvent ( PointerAddedEvent (
2022-04-14 10:37:47 +03:00
pointer: evt . pointer + offset , position: evt . position ) ) ;
2022-05-26 13:25:16 +03:00
GestureBinding . instance . handlePointerEvent ( PointerDownEvent (
2022-04-14 10:37:47 +03:00
pointer: evt . pointer + offset ,
size: 0.1 ,
position: evt . position ) ) ;
}
} ,
onPointerUp: ( evt ) {
if ( evt . size = = 1 & & GestureBinding . instance ! = null ) {
2022-05-26 13:25:16 +03:00
GestureBinding . instance . handlePointerEvent ( PointerUpEvent (
2022-04-14 10:37:47 +03:00
pointer: evt . pointer + offset ,
size: 0.1 ,
position: evt . position ) ) ;
2022-05-26 13:25:16 +03:00
GestureBinding . instance . handlePointerEvent ( PointerRemovedEvent (
2022-04-15 12:45:48 +03:00
pointer: evt . pointer + offset , position: evt . position ) ) ;
2022-04-14 10:37:47 +03:00
}
} ,
onPointerMove: ( evt ) {
if ( evt . size = = 1 & & GestureBinding . instance ! = null ) {
2022-05-26 13:25:16 +03:00
GestureBinding . instance . handlePointerEvent ( PointerMoveEvent (
2022-04-14 10:37:47 +03:00
pointer: evt . pointer + offset ,
size: 0.1 ,
delta: evt . delta ,
position: evt . position ) ) ;
}
} ,
child: child ) ;
}
}
2022-04-20 17:37:47 +03:00
class PermissionManager {
static Completer < bool > ? _completer ;
static Timer ? _timer ;
static var _current = " " ;
static final permissions = [ " audio " , " file " ] ;
static bool isWaitingFile ( ) {
if ( _completer ! = null ) {
return ! _completer ! . isCompleted & & _current = = " file " ;
}
return false ;
}
static Future < bool > check ( String type ) {
if ( ! permissions . contains ( type ) )
return Future . error ( " Wrong permission! $ type " ) ;
return FFI . invokeMethod ( " check_permission " , type ) ;
}
static Future < bool > request ( String type ) {
if ( ! permissions . contains ( type ) )
return Future . error ( " Wrong permission! $ type " ) ;
_current = type ;
_completer = Completer < bool > ( ) ;
FFI . invokeMethod ( " request_permission " , type ) ;
// timeout
_timer ? . cancel ( ) ;
_timer = Timer ( Duration ( seconds: 60 ) , ( ) {
if ( _completer = = null ) return ;
if ( ! _completer ! . isCompleted ) {
_completer ! . complete ( false ) ;
}
_completer = null ;
_current = " " ;
} ) ;
return _completer ! . future ;
}
static complete ( String type , bool res ) {
if ( type ! = _current ) {
res = false ;
}
_timer ? . cancel ( ) ;
_completer ? . complete ( res ) ;
_current = " " ;
}
}