2022-05-30 13:25:06 +08:00
import ' dart:async ' ;
2022-08-17 21:28:36 +08:00
import ' dart:convert ' ;
2022-08-03 22:03:31 +08:00
import ' dart:io ' ;
2022-08-17 21:28:36 +08:00
import ' dart:typed_data ' ;
2022-05-30 13:25:06 +08:00
2022-08-17 21:28:36 +08:00
import ' package:back_button_interceptor/back_button_interceptor.dart ' ;
2022-08-09 19:32:19 +08:00
import ' package:desktop_multi_window/desktop_multi_window.dart ' ;
2022-04-14 15:37:47 +08:00
import ' package:flutter/gestures.dart ' ;
2020-11-15 20:04:05 +08:00
import ' package:flutter/material.dart ' ;
2022-09-03 18:19:50 +08:00
import ' package:flutter/services.dart ' ;
2022-08-16 21:27:21 +08:00
import ' package:flutter_hbb/desktop/widgets/tabbar_widget.dart ' ;
2022-08-27 00:45:09 +08:00
import ' package:flutter_hbb/models/peer_model.dart ' ;
2022-08-20 19:57:16 +08:00
import ' package:get/get.dart ' ;
2022-07-29 16:47:24 +08:00
import ' package:shared_preferences/shared_preferences.dart ' ;
2022-08-09 09:01:06 +08:00
import ' package:window_manager/window_manager.dart ' ;
2022-04-19 13:07:45 +08:00
2022-09-13 09:03:34 +08:00
import ' common/widgets/overlay.dart ' ;
2022-04-19 13:07:45 +08:00
import ' models/model.dart ' ;
2022-08-03 22:03:31 +08:00
import ' models/platform_model.dart ' ;
2021-08-02 20:54:56 +08:00
2022-03-07 22:54:34 +08:00
final globalKey = GlobalKey < NavigatorState > ( ) ;
2022-04-12 22:38:39 +08:00
final navigationBarKey = GlobalKey ( ) ;
2022-03-07 22:54:34 +08:00
2022-08-23 15:25:18 +08:00
final isAndroid = Platform . isAndroid ;
final isIOS = Platform . isIOS ;
final isDesktop = Platform . isWindows | | Platform . isMacOS | | Platform . isLinux ;
2022-03-17 21:03:52 +08:00
var isWeb = false ;
2022-05-23 16:02:37 +08:00
var isWebDesktop = false ;
2022-03-17 21:03:52 +08:00
var version = " " ;
2022-03-24 17:58:33 +08:00
int androidVersion = 0 ;
2022-09-05 16:01:53 +08:00
late final DesktopType ? desktopType ;
2022-03-17 21:03:52 +08:00
2021-08-02 20:54:56 +08:00
typedef F = String Function ( String ) ;
2022-02-10 02:07:53 +08:00
typedef FMethod = String Function ( String , dynamic ) ;
2021-08-02 20:54:56 +08:00
2022-09-10 19:50:48 -07:00
typedef StreamEventHandler = Future < void > Function ( Map < String , dynamic > ) ;
2022-08-18 19:49:41 +08:00
late final iconKeyboard = MemoryImage ( Uint8List . fromList ( base64Decode (
2022-08-17 21:28:36 +08:00
" iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAgVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9d3yJTAAAAKnRSTlMA0Gd/0y8ILZgbJffDPUwV2nvzt+TMqZxyU7CMb1pYQyzsvKunkXE4AwJnNC24AAAA+0lEQVQ4y83O2U7DMBCF4ZMxk9rZk26kpQs7nPd/QJy4EiLbLf01N5Y/2YP/qxDFQvGB5NPC/ZpVnfJx4b5xyGfF95rkHvNCWH1u+N6J6T0sC7gqRy8uGPfBLEbozPXUjlkQKwGaFPNizwQbwkx0TDvhCii34ExZCSQVBdzIOEOyeclSHgBGXkpeygXSQgStACtWx4Z8rr8COHOvfEP/IbbsQAToFUAAV1M408IIjIGYAPoCSNRP7DQutfQTqxuAiH7UUg1FaJR2AGrrx52sK2ye28LZ0wBAEyR6y8X+NADhm1B4fgiiHXbRrTrxpwEY9RdM9wsepnvFHfUDwYEeiwAJr/gAAAAASUVORK5CYII= " ) ) ) ;
2022-08-18 19:49:41 +08:00
late final iconClipboard = MemoryImage ( Uint8List . fromList ( base64Decode (
2022-08-17 21:28:36 +08:00
' iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAjVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8DizOFAAAALnRSTlMAnIsyZy8YZF3NSAuabRL34cq6trCScyZ4qI9CQDwV+fPl2tnTwzkeB+m/pIFK/Xx0ewAAAQlJREFUOMudktduhDAQRWep69iY3tle0+7/f16Qg7MsJUQ5Dwh8jzRzhemJPIaf3GiW7eFQfOwDPp1ek/iMnKgBi5PrhJAhZAa1lCxE9pw5KWMswOMAQXuQOvqTB7tLFJ36wimKLrufZTzUaoRtdthqRA2vEwS+tR4qguiElRKk1YMrYfUQRkwLmwVBYDMvJKF8R0o3V2MOhNrfo+hXSYYjPn1L/S+n438t8gWh+q1F+cYFBMm1Jh8Ia7y2OWXQxMMRLqr2eTc1crSD84cWfEGwYM4LlaACEee2ZjsQXJxR3qmYb+GpC8ZfNM5oh3yxxbxgQE7lEkb3ZvvH1BiRHn1bu02ICcKGWr4AudUkyYxmvywAAAAASUVORK5CYII= ' ) ) ) ;
2022-08-18 19:49:41 +08:00
late final iconAudio = MemoryImage ( Uint8List . fromList ( base64Decode (
2022-08-17 21:28:36 +08:00
' iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAk1BMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////ROyVeAAAAMHRSTlMAgfz08DDqCAThvraZjEcoGA751JxzbGdfTRP25NrIpaGTcEM+HAvMuKinhXhWNx9Yzm/gAAABFUlEQVQ4y82S2XLCMAxFheMsQNghCQFalkL39vz/11V4GpNk0r629+Va1pmxPFfyh1ravOP2Y1ydJmBO0lYP3r+PyQ62s2Y7fgF6VRXOYdToT++ogIuoVhCUtX7YpwJG3F8f6V8rr3WABwwUahlEvr8y3IBniGKdKYBQ5OGQpukQakBpIVcfwptIhJcf8hWGakdndAAhBInIGHbdQGJg6jjbDUgEE5EpmB+AAM4uj6gb+AQT6wdhITLvAHJ4VCtgoAlG1tpNA0gWON/f4ioHdSADc1bfgt+PZFkDlD6ojWF+kVoaHlhvFjPHuVRrefohY1GdcFm1N8JvwEyrJ/X2Th2rIoVgIi3Fo6Xf0z5k8psKu5f/oi+nHjjI92o36AAAAABJRU5ErkJggg== ' ) ) ) ;
2022-08-18 19:49:41 +08:00
late final iconFile = MemoryImage ( Uint8List . fromList ( base64Decode (
2022-08-17 21:28:36 +08:00
' iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAH+CAESEN8jyZkcIb5N/ONy3vmHhmiGjUm7UwS+YAAAHZSURBVGje7dnbboMwDIBhBwgQoFAO7Ta//4NOqCAXYZQstatq4r+r5ubrgQSpg8iyC4ZURa+PlIpQYGiwrzyeHtYZjAL8T05O4H8BbbKvFgRa4NoBU8pXeYEkDDgaaLQBcwJrmeErJQB/7wes3QBWGnCIX0+AQycL1PO6BMwPa0nA4ZxbgTvOjUYMGPHRnZkQAY4mxPZBjmy53E7ukSkFKYB/D4XsWZQx64sCeYebOogGsoOBYvv6/UCb8F0IOBZ0TlP6lEYdANY350AJqB9/qPVuOI5evw4A1hgLigAlepnyxW80bcCcwN++A2s82Vcu02ta+ceq9BoL5KGTTRwQPlpqA3gCnwWU2kCDgeWRQPj2jAPCDxgCMjhI6uZnToDpvd/BJeFrJQB/fsAa02gCt3mi1wNuy8GgBNDZlysBNNSrADVSjcJl6vCpUn6jOdx0kz0q6PMhQRa4465SFKhx35cgUCBTwj2/NHwZAb71qR8GEP2H1XcmAtBPTEO67GP6FUUAIKGABbDLQ0EArhN2sAIGesRO+iyy+RMAjckVTlMCKFVAbh/4Af9OPgG61SkDVco3BQGT3GXaDAnTIAcYZDuBTwGsAGDxuBFeAQqIqwoFMlAVLrHr/wId5MPt0nilGgAAAABJRU5ErkJggg== ' ) ) ) ;
2022-08-18 19:49:41 +08:00
late final iconRestart = MemoryImage ( Uint8List . fromList ( base64Decode (
' iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAB7BAAAewQHDaVRTAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAbhJREFUWIXVlrFqFGEUhb+7UYxaWCQKlrKKxaZSQVGDJih2tj6MD2DnMwiWvoAIRnENIpZiYxEro6IooiS7SPwsMgNLkk3mjmYmnmb45/73nMNwz/x/qH3gMu2gH6rAU+Blw+Lngau4jpmGxVF7qp1iPWjaQKnZ2WnXbuP/NqAeUPc3ZkA9XDwvqc+BVWCgPlJ7tRwUKThZce819b46VH+pfXVRXVO/q2cSul3VOgZUl0ejq86r39TXI8mqZKDuDEwCw3IREQvAbWAGmMsQZQ0sAl3gHPB1Q+0e8BuYzRDuy2yOiFVgaUxtRf0ETGc4syk4rc6PqU0Cx9j8Zf6dAeAK8Fi9sUXtFjABvEgxJlNwRP2svlNPjbw/q35U36oTFbnyMSwabxb/gB/qA3VBHagrauV7RW0DRfP1IvMlXqkXkhz1DYyQTKtHa/Z2VVMx3IiI+PI3/bCHjuOpFrSnAMpL6QfgTcMGesDx0kBr2BMzsNyi/vtQu8CJlgwsRbZDnWP90NkKaxHxJMOXMqAeAn5u0ydwMCKGY+qbkB3C2W3EKWoXk5zVoHbUZ+6Mh7tl4G4F8RJ3qvL+AfV3r5Vdpj70AAAAAElFTkSuQmCC ' ) ) ) ;
2022-08-17 21:28:36 +08:00
2022-09-05 16:01:53 +08:00
enum DesktopType {
main ,
remote ,
fileTransfer ,
cm ,
portForward ,
rdp ,
}
2022-08-20 19:57:16 +08:00
class IconFont {
2022-08-24 11:01:58 +08:00
static const _family1 = ' Tabbar ' ;
static const _family2 = ' PeerSearchbar ' ;
2022-08-20 19:57:16 +08:00
IconFont . _ ( ) ;
2022-08-24 11:01:58 +08:00
static const IconData max = IconData ( 0xe606 , fontFamily: _family1 ) ;
static const IconData restore = IconData ( 0xe607 , fontFamily: _family1 ) ;
static const IconData close = IconData ( 0xe668 , fontFamily: _family1 ) ;
static const IconData min = IconData ( 0xe609 , fontFamily: _family1 ) ;
static const IconData add = IconData ( 0xe664 , fontFamily: _family1 ) ;
static const IconData menu = IconData ( 0xe628 , fontFamily: _family1 ) ;
static const IconData search = IconData ( 0xe6a4 , fontFamily: _family2 ) ;
static const IconData round_close = IconData ( 0xe6ed , fontFamily: _family2 ) ;
2022-08-20 19:57:16 +08:00
}
class ColorThemeExtension extends ThemeExtension < ColorThemeExtension > {
const ColorThemeExtension ( {
required this . bg ,
required this . grayBg ,
required this . text ,
required this . lightText ,
required this . lighterText ,
2022-08-22 17:58:48 +08:00
required this . placeholder ,
2022-08-20 19:57:16 +08:00
required this . border ,
} ) ;
final Color ? bg ;
final Color ? grayBg ;
final Color ? text ;
final Color ? lightText ;
final Color ? lighterText ;
2022-08-22 17:58:48 +08:00
final Color ? placeholder ;
2022-08-20 19:57:16 +08:00
final Color ? border ;
static const light = ColorThemeExtension (
bg: Color ( 0xFFFFFFFF ) ,
grayBg: Color ( 0xFFEEEEEE ) ,
text: Color ( 0xFF222222 ) ,
lightText: Color ( 0xFF666666 ) ,
lighterText: Color ( 0xFF888888 ) ,
2022-08-22 17:58:48 +08:00
placeholder: Color ( 0xFFAAAAAA ) ,
2022-08-20 19:57:16 +08:00
border: Color ( 0xFFCCCCCC ) ,
) ;
static const dark = ColorThemeExtension (
bg: Color ( 0xFF252525 ) ,
grayBg: Color ( 0xFF141414 ) ,
text: Color ( 0xFFFFFFFF ) ,
lightText: Color ( 0xFF999999 ) ,
lighterText: Color ( 0xFF777777 ) ,
2022-08-22 17:58:48 +08:00
placeholder: Color ( 0xFF555555 ) ,
2022-08-20 19:57:16 +08:00
border: Color ( 0xFF555555 ) ,
) ;
@ override
ThemeExtension < ColorThemeExtension > copyWith (
{ Color ? bg ,
Color ? grayBg ,
Color ? text ,
Color ? lightText ,
Color ? lighterText ,
2022-08-22 17:58:48 +08:00
Color ? placeholder ,
2022-08-20 19:57:16 +08:00
Color ? border } ) {
return ColorThemeExtension (
bg: bg ? ? this . bg ,
grayBg: grayBg ? ? this . grayBg ,
text: text ? ? this . text ,
lightText: lightText ? ? this . lightText ,
lighterText: lighterText ? ? this . lighterText ,
2022-08-22 17:58:48 +08:00
placeholder: placeholder ? ? this . placeholder ,
2022-08-20 19:57:16 +08:00
border: border ? ? this . border ,
) ;
}
@ override
ThemeExtension < ColorThemeExtension > lerp (
ThemeExtension < ColorThemeExtension > ? other , double t ) {
if ( other is ! ColorThemeExtension ) {
return this ;
}
return ColorThemeExtension (
bg: Color . lerp ( bg , other . bg , t ) ,
grayBg: Color . lerp ( grayBg , other . grayBg , t ) ,
text: Color . lerp ( text , other . text , t ) ,
lightText: Color . lerp ( lightText , other . lightText , t ) ,
lighterText: Color . lerp ( lighterText , other . lighterText , t ) ,
2022-08-22 17:58:48 +08:00
placeholder: Color . lerp ( placeholder , other . placeholder , t ) ,
2022-08-20 19:57:16 +08:00
border: Color . lerp ( border , other . border , t ) ,
) ;
}
}
2020-11-16 01:13:26 +08:00
class MyTheme {
2020-11-20 16:37:48 +08:00
MyTheme . _ ( ) ;
2022-02-02 17:25:56 +08:00
2020-11-16 01:13:26 +08:00
static const Color grayBg = Color ( 0xFFEEEEEE ) ;
static const Color white = Color ( 0xFFFFFFFF ) ;
2020-11-16 22:00:09 +08:00
static const Color accent = Color ( 0xFF0071FF ) ;
2020-11-19 17:22:42 +08:00
static const Color accent50 = Color ( 0x770071FF ) ;
2020-11-27 02:14:27 +08:00
static const Color accent80 = Color ( 0xAA0071FF ) ;
2020-11-19 18:41:37 +08:00
static const Color canvasColor = Color ( 0xFF212121 ) ;
2020-11-20 13:06:52 +08:00
static const Color border = Color ( 0xFFCCCCCC ) ;
2022-01-31 16:22:05 +08:00
static const Color idColor = Color ( 0xFF00B6F0 ) ;
static const Color darkGray = Color ( 0xFFB9BABC ) ;
2022-08-17 21:28:36 +08:00
static const Color cmIdColor = Color ( 0xFF21790B ) ;
2022-05-30 13:25:06 +08:00
static const Color dark = Colors . black87 ;
2022-08-22 17:58:48 +08:00
static const Color button = Color ( 0xFF2C8CFF ) ;
static const Color hoverBorder = Color ( 0xFF999999 ) ;
2022-07-29 16:47:24 +08:00
static ThemeData lightTheme = ThemeData (
brightness: Brightness . light ,
primarySwatch: Colors . blue ,
visualDensity: VisualDensity . adaptivePlatformDensity ,
2022-09-04 11:03:16 +08:00
tabBarTheme: const TabBarTheme (
2022-08-20 19:57:16 +08:00
labelColor: Colors . black87 ,
) ,
2022-08-22 17:58:48 +08:00
splashColor: Colors . transparent ,
highlightColor: Colors . transparent ,
2022-08-20 19:57:16 +08:00
) . copyWith (
extensions: < ThemeExtension < dynamic > > [
ColorThemeExtension . light ,
2022-09-04 11:03:16 +08:00
TabbarTheme . light ,
2022-08-20 19:57:16 +08:00
] ,
2022-07-29 16:47:24 +08:00
) ;
static ThemeData darkTheme = ThemeData (
2022-08-20 19:57:16 +08:00
brightness: Brightness . dark ,
primarySwatch: Colors . blue ,
visualDensity: VisualDensity . adaptivePlatformDensity ,
2022-09-04 11:03:16 +08:00
tabBarTheme: const TabBarTheme (
2022-08-20 19:57:16 +08:00
labelColor: Colors . white70 ,
) ,
2022-08-22 17:58:48 +08:00
splashColor: Colors . transparent ,
highlightColor: Colors . transparent ,
2022-08-20 19:57:16 +08:00
) . copyWith (
extensions: < ThemeExtension < dynamic > > [
ColorThemeExtension . dark ,
2022-09-04 11:03:16 +08:00
TabbarTheme . dark ,
2022-08-20 19:57:16 +08:00
] ,
) ;
2022-09-06 22:34:01 +08:00
static changeTo ( bool dark ) {
2022-09-07 18:57:49 +08:00
if ( Get . isDarkMode ! = dark ) {
Get . find < SharedPreferences > ( ) . setString ( " darkTheme " , dark ? " Y " : " " ) ;
Get . changeThemeMode ( dark ? ThemeMode . dark : ThemeMode . light ) ;
if ( desktopType = = DesktopType . main ) {
bind . mainChangeTheme ( dark: dark ) ;
}
}
2022-09-06 22:34:01 +08:00
}
static bool _themeInitialed = false ;
2022-09-07 18:57:49 +08:00
static ThemeMode initialThemeMode ( { bool mainPage = false } ) {
2022-09-06 22:34:01 +08:00
bool dark ;
// Brightnesss is always light on windows, Flutter 3.0.5
if ( _themeInitialed | | ! mainPage | | Platform . isWindows ) {
2022-09-07 18:57:49 +08:00
dark = " Y " = = Get . find < SharedPreferences > ( ) . getString ( " darkTheme " ) ;
2022-09-06 22:34:01 +08:00
} else {
dark = WidgetsBinding . instance . platformDispatcher . platformBrightness = =
Brightness . dark ;
Get . find < SharedPreferences > ( ) . setString ( " darkTheme " , dark ? " Y " : " " ) ;
}
_themeInitialed = true ;
2022-09-07 18:57:49 +08:00
return dark ? ThemeMode . dark : ThemeMode . light ;
}
2022-08-20 19:57:16 +08:00
static ColorThemeExtension color ( BuildContext context ) {
return Theme . of ( context ) . extension < ColorThemeExtension > ( ) ! ;
}
2022-09-04 11:03:16 +08:00
static TabbarTheme tabbar ( BuildContext context ) {
return Theme . of ( context ) . extension < TabbarTheme > ( ) ! ;
}
2022-07-29 16:47:24 +08:00
}
bool isDarkTheme ( ) {
2022-09-07 18:57:49 +08:00
return Get . isDarkMode ;
2020-11-16 01:13:26 +08:00
}
2021-08-02 20:54:56 +08:00
final ButtonStyle flatButtonStyle = TextButton . styleFrom (
2022-05-18 15:47:07 +08:00
minimumSize: Size ( 0 , 36 ) ,
padding: EdgeInsets . symmetric ( horizontal: 16.0 , vertical: 10.0 ) ,
2021-08-02 20:54:56 +08:00
shape: const RoundedRectangleBorder (
borderRadius: BorderRadius . all ( Radius . circular ( 2.0 ) ) ,
) ,
) ;
2022-08-18 00:34:04 +08:00
String formatDurationToTime ( Duration duration ) {
var totalTime = duration . inSeconds ;
final secs = totalTime % 60 ;
totalTime = ( totalTime - secs ) ~ / 60 ;
final mins = totalTime % 60 ;
totalTime = ( totalTime - mins ) ~ / 60 ;
return " ${ totalTime . toString ( ) . padLeft ( 2 , " 0 " ) } : ${ mins . toString ( ) . padLeft ( 2 , " 0 " ) } : ${ secs . toString ( ) . padLeft ( 2 , " 0 " ) } " ;
}
2022-08-16 21:27:21 +08:00
closeConnection ( { String ? id } ) {
2022-08-12 18:42:02 +08:00
if ( isAndroid | | isIOS ) {
Navigator . popUntil ( globalKey . currentContext ! , ModalRoute . withName ( " / " ) ) ;
} else {
2022-08-24 20:56:42 +08:00
final controller = Get . find < DesktopTabController > ( ) ;
controller . closeBy ( id ) ;
2022-08-12 18:42:02 +08:00
}
2022-03-13 23:07:52 +08:00
}
2022-08-09 19:32:19 +08:00
void window_on_top ( int ? id ) {
if ( id = = null ) {
// main window
windowManager . restore ( ) ;
windowManager . show ( ) ;
windowManager . focus ( ) ;
} else {
2022-08-09 21:12:55 +08:00
WindowController . fromWindowId ( id )
. . focus ( )
. . show ( ) ;
2022-08-09 19:32:19 +08:00
}
2022-08-09 09:01:06 +08:00
}
2022-03-13 00:32:44 +08:00
typedef DialogBuilder = CustomAlertDialog Function (
2022-04-19 13:07:45 +08:00
StateSetter setState , void Function ( [ dynamic ] ) close ) ;
2022-03-13 00:32:44 +08:00
2022-08-12 18:42:02 +08:00
class Dialog < T > {
OverlayEntry ? entry ;
2022-08-15 19:31:58 +08:00
Completer < T ? > completer = Completer < T ? > ( ) ;
2022-08-12 18:42:02 +08:00
Dialog ( ) ;
void complete ( T ? res ) {
try {
if ( ! completer . isCompleted ) {
completer . complete ( res ) ;
}
} catch ( e ) {
debugPrint ( " Dialog complete catch error: $ e " ) ;
2022-08-15 19:31:58 +08:00
} finally {
entry ? . remove ( ) ;
2022-08-12 18:42:02 +08:00
}
}
}
class OverlayDialogManager {
OverlayState ? _overlayState ;
2022-09-08 22:18:02 +08:00
final Map < String , Dialog > _dialogs = { } ;
2022-08-12 18:42:02 +08:00
int _tagCount = 0 ;
2022-03-13 00:32:44 +08:00
2022-09-08 22:18:02 +08:00
OverlayEntry ? _mobileActionsOverlayEntry ;
2022-08-12 18:42:02 +08:00
/// By default OverlayDialogManager use global overlay
OverlayDialogManager ( ) {
_overlayState = globalKey . currentState ? . overlay ;
2022-02-28 18:29:25 +08:00
}
2022-03-13 00:32:44 +08:00
2022-08-12 18:42:02 +08:00
void setOverlayState ( OverlayState ? overlayState ) {
_overlayState = overlayState ;
}
void dismissAll ( ) {
_dialogs . forEach ( ( key , value ) {
value . complete ( null ) ;
BackButtonInterceptor . removeByName ( key ) ;
} ) ;
_dialogs . clear ( ) ;
}
void dismissByTag ( String tag ) {
_dialogs [ tag ] ? . complete ( null ) ;
_dialogs . remove ( tag ) ;
BackButtonInterceptor . removeByName ( tag ) ;
}
Future < T ? > show < T > ( DialogBuilder builder ,
2022-04-20 23:43:19 +08:00
{ bool clickMaskDismiss = false ,
2022-08-03 22:03:31 +08:00
bool backDismiss = false ,
String ? tag ,
2022-08-12 18:42:02 +08:00
bool useAnimation = true ,
bool forceGlobal = false } ) {
final overlayState =
forceGlobal ? globalKey . currentState ? . overlay : _overlayState ;
if ( overlayState = = null ) {
return Future . error (
" [OverlayDialogManager] Failed to show dialog, _overlayState is null, call [setOverlayState] first " ) ;
}
final _tag ;
2022-04-19 13:07:45 +08:00
if ( tag ! = null ) {
2022-08-12 18:42:02 +08:00
_tag = tag ;
2022-04-19 13:07:45 +08:00
} else {
2022-08-12 18:42:02 +08:00
_tag = _tagCount . toString ( ) ;
_tagCount + + ;
2022-04-19 13:07:45 +08:00
}
2022-08-12 18:42:02 +08:00
final dialog = Dialog < T > ( ) ;
_dialogs [ _tag ] = dialog ;
2022-04-19 13:07:45 +08:00
final close = ( [ res ] ) {
2022-08-12 18:42:02 +08:00
_dialogs . remove ( _tag ) ;
dialog . complete ( res ) ;
BackButtonInterceptor . removeByName ( _tag ) ;
2022-04-19 13:07:45 +08:00
} ;
2022-08-12 18:42:02 +08:00
dialog . entry = OverlayEntry ( builder: ( _ ) {
2022-08-15 14:39:31 +08:00
bool innerClicked = false ;
return Listener (
onPointerUp: ( _ ) {
if ( ! innerClicked & & clickMaskDismiss ) {
close ( ) ;
}
innerClicked = false ;
} ,
child: Container (
color: Colors . black12 ,
child: StatefulBuilder ( builder: ( context , setState ) {
return Listener (
onPointerUp: ( _ ) = > innerClicked = true ,
child: builder ( setState , close ) ,
) ;
} ) ) ) ;
2022-08-12 18:42:02 +08:00
} ) ;
overlayState . insert ( dialog . entry ! ) ;
BackButtonInterceptor . add ( ( stopDefaultButtonEvent , routeInfo ) {
if ( backDismiss ) {
close ( ) ;
}
return true ;
} , name: _tag ) ;
return dialog . completer . future ;
}
void showLoading ( String text ,
2022-08-15 14:39:31 +08:00
{ bool clickMaskDismiss = false ,
bool showCancel = true ,
VoidCallback ? onCancel } ) {
2022-09-03 18:19:50 +08:00
show ( ( setState , close ) {
cancel ( ) {
dismissAll ( ) ;
if ( onCancel ! = null ) {
onCancel ( ) ;
}
}
return CustomAlertDialog (
2022-08-12 18:42:02 +08:00
content: Container (
2022-09-03 18:19:50 +08:00
constraints: const BoxConstraints ( maxWidth: 240 ) ,
2022-08-12 18:42:02 +08:00
child: Column (
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
2022-09-03 18:19:50 +08:00
const SizedBox ( height: 30 ) ,
const Center ( child: CircularProgressIndicator ( ) ) ,
const SizedBox ( height: 20 ) ,
2022-08-12 18:42:02 +08:00
Center (
child: Text ( translate ( text ) ,
2022-09-03 18:19:50 +08:00
style: const TextStyle ( fontSize: 15 ) ) ) ,
const SizedBox ( height: 20 ) ,
2022-08-15 14:39:31 +08:00
Offstage (
offstage: ! showCancel ,
child: Center (
child: TextButton (
style: flatButtonStyle ,
2022-09-03 18:19:50 +08:00
onPressed: cancel ,
2022-08-15 14:39:31 +08:00
child: Text ( translate ( ' Cancel ' ) ,
2022-09-03 18:19:50 +08:00
style:
const TextStyle ( color: MyTheme . accent ) ) ) ) )
] ) ) ,
onCancel: showCancel ? cancel : null ,
) ;
} ) ;
2022-08-12 18:42:02 +08:00
}
2022-09-08 22:18:02 +08:00
void resetMobileActionsOverlay ( { FFI ? ffi } ) {
if ( _mobileActionsOverlayEntry = = null ) return ;
hideMobileActionsOverlay ( ) ;
showMobileActionsOverlay ( ffi: ffi ) ;
}
void showMobileActionsOverlay ( { FFI ? ffi } ) {
if ( _mobileActionsOverlayEntry ! = null ) return ;
if ( _overlayState = = null ) return ;
// compute overlay position
final screenW = MediaQuery . of ( globalKey . currentContext ! ) . size . width ;
final screenH = MediaQuery . of ( globalKey . currentContext ! ) . size . height ;
const double overlayW = 200 ;
const double overlayH = 45 ;
final left = ( screenW - overlayW ) / 2 ;
final top = screenH - overlayH - 80 ;
final overlay = OverlayEntry ( builder: ( context ) {
final session = ffi ? ? gFFI ;
return DraggableMobileActions (
position: Offset ( left , top ) ,
width: overlayW ,
height: overlayH ,
onBackPressed: ( ) = > session . tap ( MouseButtons . right ) ,
onHomePressed: ( ) = > session . tap ( MouseButtons . wheel ) ,
onRecentPressed: ( ) async {
session . sendMouse ( ' down ' , MouseButtons . wheel ) ;
await Future . delayed ( const Duration ( milliseconds: 500 ) ) ;
session . sendMouse ( ' up ' , MouseButtons . wheel ) ;
} ,
onHidePressed: ( ) = > hideMobileActionsOverlay ( ) ,
) ;
} ) ;
_overlayState ! . insert ( overlay ) ;
_mobileActionsOverlayEntry = overlay ;
}
void hideMobileActionsOverlay ( ) {
if ( _mobileActionsOverlayEntry ! = null ) {
_mobileActionsOverlayEntry ! . remove ( ) ;
_mobileActionsOverlayEntry = null ;
return ;
}
}
void toggleMobileActionsOverlay ( { FFI ? ffi } ) {
if ( _mobileActionsOverlayEntry = = null ) {
showMobileActionsOverlay ( ffi: ffi ) ;
} else {
hideMobileActionsOverlay ( ) ;
}
}
2022-08-15 14:39:31 +08:00
}
2022-08-12 18:42:02 +08:00
2022-08-15 14:39:31 +08:00
void showToast ( String text , { Duration timeout = const Duration ( seconds: 2 ) } ) {
final overlayState = globalKey . currentState ? . overlay ;
if ( overlayState = = null ) return ;
final entry = OverlayEntry ( builder: ( _ ) {
return IgnorePointer (
child: Align (
2022-09-03 18:19:50 +08:00
alignment: const Alignment ( 0.0 , 0.8 ) ,
2022-08-15 14:39:31 +08:00
child: Container (
decoration: BoxDecoration (
color: Colors . black . withOpacity ( 0.6 ) ,
2022-09-03 18:19:50 +08:00
borderRadius: const BorderRadius . all (
2022-08-15 14:39:31 +08:00
Radius . circular ( 20 ) ,
) ,
) ,
2022-09-03 18:19:50 +08:00
padding: const EdgeInsets . symmetric ( horizontal: 20 , vertical: 5 ) ,
2022-08-15 14:39:31 +08:00
child: Text (
text ,
2022-09-03 18:19:50 +08:00
style: const TextStyle (
2022-08-15 14:39:31 +08:00
decoration: TextDecoration . none ,
fontWeight: FontWeight . w300 ,
fontSize: 18 ,
color: Colors . white ) ,
) ,
) ) ) ;
} ) ;
overlayState . insert ( entry ) ;
Future . delayed ( timeout , ( ) {
entry . remove ( ) ;
} ) ;
2022-02-28 16:11:21 +08:00
}
2022-02-02 17:25:56 +08:00
2022-03-13 00:32:44 +08:00
class CustomAlertDialog extends StatelessWidget {
2022-09-03 18:19:50 +08:00
const CustomAlertDialog (
{ Key ? key ,
this . title ,
required this . content ,
this . actions ,
this . contentPadding ,
this . onSubmit ,
this . onCancel } )
: super ( key: key ) ;
2020-11-18 12:49:43 +08:00
2022-08-12 18:42:02 +08:00
final Widget ? title ;
2022-03-13 00:32:44 +08:00
final Widget content ;
2022-08-12 18:42:02 +08:00
final List < Widget > ? actions ;
2022-03-13 00:32:44 +08:00
final double ? contentPadding ;
2022-09-03 18:19:50 +08:00
final Function ( ) ? onSubmit ;
final Function ( ) ? onCancel ;
2022-03-13 00:32:44 +08:00
@ override
Widget build ( BuildContext context ) {
2022-09-03 18:19:50 +08:00
FocusNode focusNode = FocusNode ( ) ;
// request focus if there is no focused FocusNode in the dialog
Future . delayed ( Duration . zero , ( ) {
if ( ! focusNode . hasFocus ) focusNode . requestFocus ( ) ;
} ) ;
return Focus (
focusNode: focusNode ,
autofocus: true ,
onKey: ( node , key ) {
if ( key . logicalKey = = LogicalKeyboardKey . escape ) {
if ( key is RawKeyDownEvent ) {
onCancel ? . call ( ) ;
}
return KeyEventResult . handled ; // avoid TextField exception on escape
} else if ( onSubmit ! = null & &
key . logicalKey = = LogicalKeyboardKey . enter ) {
if ( key is RawKeyDownEvent ) onSubmit ? . call ( ) ;
return KeyEventResult . handled ;
}
return KeyEventResult . ignored ;
} ,
child: AlertDialog (
scrollable: true ,
title: title ,
contentPadding: EdgeInsets . symmetric (
horizontal: contentPadding ? ? 25 , vertical: 10 ) ,
content: content ,
actions: actions ,
) ,
2022-04-21 10:02:47 +08:00
) ;
2022-03-13 00:32:44 +08:00
}
2020-11-16 22:00:09 +08:00
}
2020-11-16 22:12:32 +08:00
2022-08-12 18:42:02 +08:00
void msgBox (
String type , String title , String text , OverlayDialogManager dialogManager ,
{ bool ? hasCancel } ) {
dialogManager . dismissAll ( ) ;
2022-08-08 22:00:01 +08:00
List < Widget > buttons = [ ] ;
2022-09-03 18:19:50 +08:00
bool hasOk = false ;
submit ( ) {
dialogManager . dismissAll ( ) ;
// https://github.com/fufesou/rustdesk/blob/5e9a31340b899822090a3731769ae79c6bf5f3e5/src/ui/common.tis#L263
2022-09-05 16:01:53 +08:00
if ( ! type . contains ( " custom " ) & &
! ( desktopType = = DesktopType . portForward | |
desktopType = = DesktopType . rdp ) ) {
2022-09-03 18:19:50 +08:00
closeConnection ( ) ;
}
}
cancel ( ) {
dialogManager . dismissAll ( ) ;
}
2022-08-31 18:41:55 +08:00
if ( type ! = " connecting " & & type ! = " success " & & ! type . contains ( " nook " ) ) {
2022-09-03 18:19:50 +08:00
hasOk = true ;
buttons . insert ( 0 , msgBoxButton ( translate ( ' OK ' ) , submit ) ) ;
2022-08-08 22:00:01 +08:00
}
2022-08-31 18:41:55 +08:00
hasCancel ? ? = ! type . contains ( " error " ) & &
! type . contains ( " nocancel " ) & &
type ! = " restarting " ;
2020-11-29 01:36:10 +08:00
if ( hasCancel ) {
2022-09-03 18:19:50 +08:00
buttons . insert ( 0 , msgBoxButton ( translate ( ' Cancel ' ) , cancel ) ) ;
2020-11-29 01:36:10 +08:00
}
2022-08-08 22:00:01 +08:00
// TODO: test this button
2022-08-31 18:41:55 +08:00
if ( type . contains ( " hasclose " ) ) {
2022-08-08 22:00:01 +08:00
buttons . insert (
0 ,
2022-08-31 23:02:02 -07:00
msgBoxButton ( translate ( ' Close ' ) , ( ) {
2022-08-12 18:42:02 +08:00
dialogManager . dismissAll ( ) ;
2022-08-08 22:00:01 +08:00
} ) ) ;
}
2022-08-12 18:42:02 +08:00
dialogManager . show ( ( setState , close ) = > CustomAlertDialog (
2022-09-03 18:19:50 +08:00
title: _msgBoxTitle ( title ) ,
content: Text ( translate ( text ) , style: const TextStyle ( fontSize: 15 ) ) ,
actions: buttons ,
onSubmit: hasOk ? submit : null ,
onCancel: hasCancel = = true ? cancel : null ,
) ) ;
2020-11-21 14:40:28 +08:00
}
2020-11-25 18:33:09 +08:00
2022-08-31 23:02:02 -07:00
Widget msgBoxButton ( String text , void Function ( ) onPressed ) {
2022-08-31 18:41:55 +08:00
return ButtonTheme (
padding: EdgeInsets . symmetric ( horizontal: 20 , vertical: 10 ) ,
materialTapTargetSize: MaterialTapTargetSize . shrinkWrap ,
//limits the touch area to the button area
minWidth: 0 ,
//wraps child's width
height: 0 ,
child: TextButton (
style: flatButtonStyle ,
onPressed: onPressed ,
child:
Text ( translate ( text ) , style: TextStyle ( color: MyTheme . accent ) ) ) ) ;
}
2022-09-03 18:19:50 +08:00
Widget _msgBoxTitle ( String title ) = >
Text ( translate ( title ) , style: TextStyle ( fontSize: 21 ) ) ;
2022-08-31 23:02:02 -07:00
2022-08-31 18:41:55 +08:00
void msgBoxCommon ( OverlayDialogManager dialogManager , String title ,
2022-09-03 18:19:50 +08:00
Widget content , List < Widget > buttons ,
{ bool hasCancel = true } ) {
2022-08-31 18:41:55 +08:00
dialogManager . dismissAll ( ) ;
dialogManager . show ( ( setState , close ) = > CustomAlertDialog (
2022-09-03 18:19:50 +08:00
title: _msgBoxTitle ( title ) ,
content: content ,
actions: buttons ,
onCancel: hasCancel ? close : null ,
) ) ;
2022-08-31 18:41:55 +08:00
}
2020-11-25 18:33:09 +08: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 14:14:01 +08:00
hash = hash % 16777216 ;
return Color ( ( hash & 0xFF7FFF ) | ( alpha < < 24 ) ) ;
2020-11-25 18:33:09 +08:00
}
2022-02-02 17:25:56 +08:00
2022-03-17 21:03:52 +08:00
const K = 1024 ;
const M = K * K ;
const G = M * K ;
String readableFileSize ( double size ) {
if ( size < K ) {
2022-08-31 18:41:55 +08:00
return " ${ size . toStringAsFixed ( 2 ) } B " ;
2022-03-17 21:03:52 +08:00
} else if ( size < M ) {
2022-08-31 18:41:55 +08:00
return " ${ ( size / K ) . toStringAsFixed ( 2 ) } KB " ;
2022-03-17 21:03:52 +08:00
} else if ( size < G ) {
2022-08-31 18:41:55 +08:00
return " ${ ( size / M ) . toStringAsFixed ( 2 ) } MB " ;
2022-03-17 21:03:52 +08:00
} else {
2022-08-31 18:41:55 +08:00
return " ${ ( size / G ) . toStringAsFixed ( 2 ) } GB " ;
2022-03-17 21:03:52 +08:00
}
}
2022-04-14 15:37:47 +08: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 18:25:16 +08:00
if ( evt . size = = 1 ) {
GestureBinding . instance . handlePointerEvent ( PointerAddedEvent (
2022-04-14 15:37:47 +08:00
pointer: evt . pointer + offset , position: evt . position ) ) ;
2022-05-26 18:25:16 +08:00
GestureBinding . instance . handlePointerEvent ( PointerDownEvent (
2022-04-14 15:37:47 +08:00
pointer: evt . pointer + offset ,
size: 0.1 ,
position: evt . position ) ) ;
}
} ,
onPointerUp: ( evt ) {
2022-06-19 17:15:37 +01:00
if ( evt . size = = 1 ) {
2022-05-26 18:25:16 +08:00
GestureBinding . instance . handlePointerEvent ( PointerUpEvent (
2022-04-14 15:37:47 +08:00
pointer: evt . pointer + offset ,
size: 0.1 ,
position: evt . position ) ) ;
2022-05-26 18:25:16 +08:00
GestureBinding . instance . handlePointerEvent ( PointerRemovedEvent (
2022-04-15 17:45:48 +08:00
pointer: evt . pointer + offset , position: evt . position ) ) ;
2022-04-14 15:37:47 +08:00
}
} ,
onPointerMove: ( evt ) {
2022-06-19 17:15:37 +01:00
if ( evt . size = = 1 ) {
2022-05-26 18:25:16 +08:00
GestureBinding . instance . handlePointerEvent ( PointerMoveEvent (
2022-04-14 15:37:47 +08:00
pointer: evt . pointer + offset ,
size: 0.1 ,
delta: evt . delta ,
position: evt . position ) ) ;
}
} ,
child: child ) ;
}
}
2022-04-20 22:37:47 +08:00
class PermissionManager {
static Completer < bool > ? _completer ;
static Timer ? _timer ;
static var _current = " " ;
2022-07-16 22:31:44 +08:00
static final permissions = [
" audio " ,
" file " ,
" ignore_battery_optimizations " ,
" application_details_settings "
] ;
2022-04-20 22:37:47 +08:00
static bool isWaitingFile ( ) {
if ( _completer ! = null ) {
return ! _completer ! . isCompleted & & _current = = " file " ;
}
return false ;
}
static Future < bool > check ( String type ) {
2022-08-18 00:34:04 +08:00
if ( isDesktop ) {
return Future . value ( true ) ;
}
2022-04-20 22:37:47 +08:00
if ( ! permissions . contains ( type ) )
return Future . error ( " Wrong permission! $ type " ) ;
2022-06-13 21:07:26 +08:00
return gFFI . invokeMethod ( " check_permission " , type ) ;
2022-04-20 22:37:47 +08:00
}
static Future < bool > request ( String type ) {
2022-08-18 00:34:04 +08:00
if ( isDesktop ) {
return Future . value ( true ) ;
}
2022-04-20 22:37:47 +08:00
if ( ! permissions . contains ( type ) )
return Future . error ( " Wrong permission! $ type " ) ;
2022-08-01 14:33:08 +08:00
gFFI . invokeMethod ( " request_permission " , type ) ;
2022-07-14 17:44:37 +08:00
if ( type = = " ignore_battery_optimizations " ) {
return Future . value ( false ) ;
}
2022-04-20 22:37:47 +08:00
_current = type ;
_completer = Completer < bool > ( ) ;
2022-06-13 21:07:26 +08:00
gFFI . invokeMethod ( " request_permission " , type ) ;
2022-04-20 22:37:47 +08:00
// 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 = " " ;
}
}
2022-06-13 21:07:26 +08:00
2022-07-29 18:34:25 +08:00
RadioListTile < T > getRadio < T > (
String name , T toValue , T curValue , void Function ( T ? ) onChange ) {
return RadioListTile < T > (
controlAffinity: ListTileControlAffinity . trailing ,
title: Text ( translate ( name ) ) ,
value: toValue ,
groupValue: curValue ,
onChanged: onChange ,
dense: true ,
) ;
}
2022-08-01 10:44:05 +08:00
2022-08-05 20:29:43 +08:00
CheckboxListTile getToggle (
2022-08-15 20:26:20 +08:00
String id , void Function ( void Function ( ) ) setState , option , name ,
{ FFI ? ffi } ) {
2022-08-16 15:22:57 +08:00
final opt = bind . sessionGetToggleOptionSync ( id: id , arg: option ) ;
2022-08-05 20:29:43 +08:00
return CheckboxListTile (
value: opt ,
onChanged: ( v ) {
setState ( ( ) {
bind . sessionToggleOption ( id: id , value: option ) ;
} ) ;
if ( option = = " show-quality-monitor " ) {
2022-08-15 20:26:20 +08:00
( ffi ? ? gFFI ) . qualityMonitorModel . checkShowQualityMonitor ( id ) ;
2022-08-05 20:29:43 +08:00
}
} ,
dense: true ,
title: Text ( translate ( name ) ) ) ;
}
2022-06-13 21:07:26 +08:00
/// find ffi, tag is Remote ID
/// for session specific usage
FFI ffi ( String ? tag ) {
return Get . find < FFI > ( tag: tag ) ;
}
/// Global FFI object
late FFI _globalFFI ;
FFI get gFFI = > _globalFFI ;
Future < void > initGlobalFFI ( ) async {
2022-08-04 17:24:02 +08:00
debugPrint ( " _globalFFI init " ) ;
2022-06-13 21:07:26 +08:00
_globalFFI = FFI ( ) ;
2022-08-04 17:24:02 +08:00
debugPrint ( " _globalFFI init end " ) ;
2022-06-13 21:07:26 +08:00
// after `put`, can also be globally found by Get.find<FFI>();
Get . put ( _globalFFI , permanent: true ) ;
2022-07-29 16:47:24 +08:00
// global shared preference
await Get . putAsync ( ( ) = > SharedPreferences . getInstance ( ) ) ;
2022-08-03 22:03:31 +08:00
}
2022-08-08 17:53:51 +08:00
String translate ( String name ) {
if ( name . startsWith ( ' Failed to ' ) & & name . contains ( ' : ' ) ) {
return name . split ( ' : ' ) . map ( ( x ) = > translate ( x ) ) . join ( ' : ' ) ;
}
return platformFFI . translate ( name , localeName ) ;
}
2022-08-13 12:43:35 +08:00
2022-08-15 11:08:42 +08:00
bool option2bool ( String option , String value ) {
2022-08-13 12:43:35 +08:00
bool res ;
2022-08-15 11:08:42 +08:00
if ( option . startsWith ( " enable- " ) ) {
2022-08-13 12:43:35 +08:00
res = value ! = " N " ;
2022-08-15 11:08:42 +08:00
} else if ( option . startsWith ( " allow- " ) | |
option = = " stop-service " | |
option = = " direct-server " | |
option = = " stop-rendezvous-service " ) {
2022-08-13 12:43:35 +08:00
res = value = = " Y " ;
} else {
assert ( false ) ;
res = value ! = " N " ;
}
return res ;
}
2022-08-15 11:08:42 +08:00
String bool2option ( String option , bool b ) {
2022-08-13 12:43:35 +08:00
String res ;
2022-08-15 11:08:42 +08:00
if ( option . startsWith ( ' enable- ' ) ) {
res = b ? ' ' : ' N ' ;
} else if ( option . startsWith ( ' allow- ' ) | |
option = = " stop-service " | |
option = = " direct-server " | |
option = = " stop-rendezvous-service " ) {
res = b ? ' Y ' : ' ' ;
2022-08-13 12:43:35 +08:00
} else {
assert ( false ) ;
2022-08-15 11:08:42 +08:00
res = b ? ' Y ' : ' N ' ;
2022-08-13 12:43:35 +08:00
}
return res ;
}
2022-08-27 00:45:09 +08:00
Future < bool > matchPeer ( String searchText , Peer peer ) async {
if ( searchText . isEmpty ) {
return true ;
}
if ( peer . id . toLowerCase ( ) . contains ( searchText ) ) {
return true ;
}
if ( peer . hostname . toLowerCase ( ) . contains ( searchText ) | |
peer . username . toLowerCase ( ) . contains ( searchText ) ) {
return true ;
}
final alias = await bind . mainGetPeerOption ( id: peer . id , key: ' alias ' ) ;
if ( alias . isEmpty ) {
return false ;
}
return alias . toLowerCase ( ) . contains ( searchText ) ;
}
Future < List < Peer > > ? matchPeers ( String searchText , List < Peer > peers ) async {
2022-08-27 01:03:20 +08:00
searchText = searchText . trim ( ) ;
2022-08-27 00:45:09 +08:00
if ( searchText . isEmpty ) {
return peers ;
}
searchText = searchText . toLowerCase ( ) ;
final matches =
await Future . wait ( peers . map ( ( peer ) = > matchPeer ( searchText , peer ) ) ) ;
final filteredList = List < Peer > . empty ( growable: true ) ;
for ( var i = 0 ; i < peers . length ; i + + ) {
if ( matches [ i ] ) {
filteredList . add ( peers [ i ] ) ;
}
}
return filteredList ;
2022-08-27 01:03:20 +08:00
}