content: import 'dart:async';
  
  import 'package:flutter/material.dart';
  import 'package:flutter/services.dart';
  import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
  import 'package:flutter_hbb/mobile/widgets/dialog.dart';
  import 'package:flutter_hbb/models/chat_model.dart';
  import 'package:get/get.dart';
  import 'package:provider/provider.dart';
  
  import '../../common.dart';
  import '../../common/widgets/dialog.dart';
  import '../../consts.dart';
  import '../../models/platform_model.dart';
  import '../../models/server_model.dart';
  import 'home_page.dart';
  
  class ServerPage extends StatefulWidget implements PageShape {
    @override
    final title = translate("Share screen");
  
    @override
    final icon = const Icon(Icons.mobile_screen_share);
  
    @override
    final appBarActions = (!bind.isDisableSettings() &&
            bind.mainGetBuildinOption(key: kOptionHideSecuritySetting) != 'Y')
        ? [_DropDownAction()]
        : [];
  
    ServerPage({Key? key}) : super(key: key);
  
    @override
    State<StatefulWidget> createState() => _ServerPageState();
  }
  
  class _DropDownAction extends StatelessWidget {
    _DropDownAction();
  
    // should only have one action
    final actions = [
      PopupMenuButton<String>(
          tooltip: "",
          icon: const Icon(Icons.more_vert),
          itemBuilder: (context) {
            listTile(String text, bool checked) {
              return ListTile(
                  title: Text(translate(text)),
                  trailing: Icon(
                    Icons.check,
                    color: checked ? null : Colors.transparent,
                  ));
            }
  
            final approveMode = gFFI.serverModel.approveMode;
            final verificationMethod = gFFI.serverModel.verificationMethod;
            final showPasswordOption = approveMode != 'click';
            final isApproveModeFixed = isOptionFixed(kOptionApproveMode);
            final isNumericOneTimePasswordFixed =
                isOptionFixed(kOptionAllowNumericOneTimePassword);
            final isAllowNumericOneTimePassword =
                gFFI.serverModel.allowNumericOneTimePassword;
            return [
              PopupMenuItem(
                enabled: gFFI.serverModel.connectStatus > 0,
                value: "changeID",
                child: Text(translate("Change ID")),
              ),
              const PopupMenuDivider(),
              PopupMenuItem(
                value: 'AcceptSessionsViaPassword',
                child: listTile(
                    'Accept sessions via password', approveMode == 'password'),
                enabled: !isApproveModeFixed,
              ),
              PopupMenuItem(
                value: 'AcceptSessionsViaClick',
                child:
                    listTile('Accept sessions via click', approveMode == 'click'),
                enabled: !isApproveModeFixed,
              ),
              PopupMenuItem(
                value: "AcceptSessionsViaBoth",
                child: listTile("Accept sessions via both",
                    approveMode != 'password' && approveMode != 'click'),
                enabled: !isApproveModeFixed,
              ),
              if (showPasswordOption) const PopupMenuDivider(),
              if (showPasswordOption &&
                  verificationMethod != kUseTemporaryPassword)
                PopupMenuItem(
                  value: "setPermanentPassword",
                  child: Text(translate("Set permanent password")),
                ),
              if (showPasswordOption &&
                  verificationMethod != kUsePermanentPassword)
                PopupMenuItem(
                  value: "setTemporaryPasswordLength",
                  child: Text(translate("One-time password length")),
                ),
              if (showPasswordOption &&
                  verificationMethod != kUsePermanentPassword)
                PopupMenuItem(
                  value: "allowNumericOneTimePassword",
                  child: listTile(translate("Numeric one-time password"),
                      isAllowNumericOneTimePassword),
                  enabled: !isNumericOneTimePasswordFixed,
                ),
              if (showPasswordOption) const PopupMenuDivider(),
              if (showPasswordOption)
                PopupMenuItem(
                  value: kUseTemporaryPassword,
                  child: listTile('Use one-time password',
                      verificationMethod == kUseTemporaryPassword),
                ),
              if (showPasswordOption)
                PopupMenuItem(
                  value: kUsePermanentPassword,
                  child: listTile('Use permanent password',
                      verificationMethod == kUsePermanentPassword),
                ),
              if (showPasswordOption)
                PopupMenuItem(
                  value: kUseBothPasswords,
                  child: listTile(
                      'Use both passwords',
                      verificationMethod != kUseTemporaryPassword &&
                          verificationMethod != kUsePermanentPassword),
                ),
            ];
          },
          onSelected: (value) async {
            if (value == "changeID") {
              changeIdDialog();
            } else if (value == "setPermanentPassword") {
              setPasswordDialog();
            } else if (value == "setTemporaryPasswordLength") {
              setTemporaryPasswordLengthDialog(gFFI.dialogManager);
            } else if (value == "allowNumericOneTimePassword") {
              gFFI.serverModel.switchAllowNumericOneTimePassword();
              gFFI.serverModel.updatePasswordModel();
            } else if (value == kUsePermanentPassword ||
                value == kUseTemporaryPassword ||
                value == kUseBothPasswords) {
              callback() {
                bind.mainSetOption(key: kOptionVerificationMethod, value: value);
                gFFI.serverModel.updatePasswordModel();
              }
  
              if (value == kUsePermanentPassword &&
                  (await bind.mainGetPermanentPassword()).isEmpty) {
                setPasswordDialog(notEmptyCallback: callback);
              } else {
                callback();
              }
            } else if (value.startsWith("AcceptSessionsVia")) {
              value = value.substring("AcceptSessionsVia".length);
              if (value == "Password") {
                gFFI.serverModel.setApproveMode('password');
              } else if (value == "Click") {
                gFFI.serverModel.setApproveMode('click');
              } else {
                gFFI.serverModel.setApproveMode(defaultOptionApproveMode);
              }
            }
          })
    ];
  
    @override
    Widget build(BuildContext context) {
      return actions[0];
    }
  }
  
  class _ServerPageState extends State<ServerPage> {
    Timer? _updateTimer;
  
    @override
    void initState() {
      super.initState();
      _updateTimer = periodic_immediate(const Duration(seconds: 3), () async {
        await gFFI.serverModel.fetchID();
      });
      gFFI.serverModel.checkAndroidPermission();
    }
  
    @override
    void dispose() {
      _updateTimer?.cancel();
      super.dispose();
    }
  
    @override
    Widget build(BuildContext context) {
      checkService();
      return ChangeNotifierProvider.value(
          value: gFFI.serverModel,
          child: Consumer<ServerModel>(
              builder: (context, serverModel, child) => SingleChildScrollView(
                    controller: gFFI.serverModel.controller,
                    child: Center(
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: [
                          buildPresetPasswordWarningMobile(),
                          gFFI.serverModel.isStart
                              ? ServerInfo()
                              : ServiceNotRunningNotification(),
                          const ConnectionManager(),
                          const PermissionChecker(),
                          SizedBox.fromSize(size: const Size(0, 15.0)),
                        ],
                      ),
                    ),
                  )));
    }
  }
  
  void checkService() async {
    gFFI.invokeMethod("check_service");
    // for Android 10/11, request MANAGE_EXTERNAL_STORAGE permission from system setting page
    if (AndroidPermissionManager.isWaitingFile() && !gFFI.serverModel.fileOk) {
      AndroidPermissionManager.complete(kManageExternalStorage,
          await AndroidPermissionManager.check(kManageExternalStorage));
      debugPrint("file permission finished");
    }
  }
  
  class ServiceNotRunningNotification extends StatelessWidget {
    ServiceNotRunningNotification({Key? key}) : super(key: key);
  
    @override
    Widget build(BuildContext context) {
      final serverModel = Provider.of<ServerModel>(context);
  
      return PaddingCard(
          title: translate("Service is not running"),
          titleIcon:
              const Icon(Icons.warning_amber_sharp, color: Colors.redAccent),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(translate("android_start_service_tip"),
                      style:
                          const TextStyle(fontSize: 12, color: MyTheme.darkGray))
                  .marginOnly(bottom: 8),
              ElevatedButton.icon(
                  icon: const Icon(Icons.play_arrow),
                  onPressed: () {
                    if (gFFI.userModel.userName.value.isEmpty &&
                        bind.mainGetLocalOption(key: "show-scam-warning") !=
                            "N") {
                      showScamWarning(context, serverModel);
                    } else {
                      serverModel.toggleService();
                    }
                  },
                  label: Text(translate("Start service")))
            ],
          ));
    }
  }
  
  class ScamWarningDialog extends StatefulWidget {
    final ServerModel serverModel;
  
    ScamWarningDialog({required this.serverModel});
  
    @override
    ScamWarningDialogState createState() => ScamWarningDialogState();
  }
  
  class ScamWarningDialogState extends State<ScamWarningDialog> {
    int _countdown = bind.isCustomClient() ? 0 : 12;
    bool show_warning = false;
    late Timer _timer;
    late ServerModel _serverModel;
  
    @override
    void initState() {
      super.initState();
      _serverModel = widget.serverModel;
      startCountdown();
    }
  
    void startCountdown() {
      const oneSecond = Duration(seconds: 1);
      _timer = Timer.periodic(oneSecond, (timer) {
        setState(() {
          _countdown--;
          if (_countdown <= 0) {
            timer.cancel();
          }
        });
      });
    }
  
    @override
    void dispose() {
      _timer.cancel();
      super.dispose();
    }
  
    @override
    Widget build(BuildContext context) {
      final isButtonLocked = _countdown > 0;
  
      return AlertDialog(
        content: ClipRRect(
          borderRadius: BorderRadius.circular(20.0),
          child: SingleChildScrollView(
            child: Container(
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment.topRight,
                  end: Alignment.bottomLeft,
                  colors: [
                    Color(0xffe242bc),
                    Color(0xfff4727c),
                  ],
                ),
              ),
              padding: EdgeInsets.all(25.0),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      Icon(
                        Icons.warning_amber_sharp,
                        color: Colors.white,
                      ),
                      SizedBox(width: 10),
                      Text(
                        translate("Warning"),
                        style: TextStyle(
                          color: Colors.white,
                          fontWeight: FontWeight.bold,
                          fontSize: 20.0,
                        ),
                      ),
                    ],
                  ),
                  SizedBox(height: 20),
                  Center(
                    child: Image.asset(
                      'assets/scam.png',
                      width: 180,
                    ),
                  ),
                  SizedBox(height: 18),
                  Text(
                    translate("scam_title"),
                    textAlign: TextAlign.center,
                    style: TextStyle(
                      color: Colors.white,
                      fontWeight: FontWeight.bold,
                      fontSize: 22.0,
                    ),
                  ),
                  SizedBox(height: 18),
                  Text(
                    "${translate("scam_text1")}\n\n${translate("scam_text2")}\n",
                    style: TextStyle(
                      color: Colors.white,
                      fontWeight: FontWeight.bold,
                      fontSize: 16.0,
                    ),
                  ),
                  Row(
                    children: <Widget>[
                      Checkbox(
                        value: show_warning,
                        onChanged: (value) {
                          setState(() {
                            show_warning = value!;
                          });
                        },
                      ),
                      Text(
                        translate("Don't show again"),
                        style: TextStyle(
                          color: Colors.white,
                          fontWeight: FontWeight.bold,
                          fontSize: 15.0,
                        ),
                      ),
                    ],
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.end,
                    children: [
                      Container(
                        constraints: BoxConstraints(maxWidth: 150),
                        child: ElevatedButton(
                          onPressed: isButtonLocked
                              ? null
                              : () {
                                  Navigator.of(context).pop();
                                  _serverModel.toggleService();
                                  if (show_warning) {
                                    bind.mainSetLocalOption(
                                        key: "show-scam-warning", value: "N");
                                  }
                                },
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.blueAccent,
                          ),
                          child: Text(
                            isButtonLocked
                                ? "${translate("I Agree")} (${_countdown}s)"
                                : translate("I Agree"),
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 13.0,
                            ),
                            maxLines: 2,
                            overflow: TextOverflow.ellipsis,
                          ),
                        ),
                      ),
                      SizedBox(width: 15),
                      Container(
                        constraints: BoxConstraints(maxWidth: 150),
                        child: ElevatedButton(
                          onPressed: () {
                            Navigator.of(context).pop();
                          },
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.blueAccent,
                          ),
                          child: Text(
                            translate("Decline"),
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 13.0,
                            ),
                            maxLines: 2,
                            overflow: TextOverflow.ellipsis,
                          ),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
        ),
        contentPadding: EdgeInsets.all(0.0),
      );
    }
  }
  
  class ServerInfo extends StatelessWidget {
    final model = gFFI.serverModel;
    final emptyController = TextEditingController(text: "-");
  
    ServerInfo({Key? key}) : super(key: key);
  
    @override
    Widget build(BuildContext context) {
      final serverModel = Provider.of<ServerModel>(context);
  
      const Color colorPositive = Colors.green;
      const Color colorNegative = Colors.red;
      const double iconMarginRight = 15;
      const double iconSize = 24;
      const TextStyle textStyleHeading = TextStyle(
          fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.grey);
      const TextStyle textStyleValue =
          TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold);
  
      void copyToClipboard(String value) {
        Clipboard.setData(ClipboardData(text: value));
        showToast(translate('Copied'));
      }
  
      Widget ConnectionStateNotification() {
        if (serverModel.connectStatus == -1) {
          return Row(children: [
            const Icon(Icons.warning_amber_sharp,
                    color: colorNegative, size: iconSize)
                .marginOnly(right: iconMarginRight),
            Expanded(child: Text(translate('not_ready_status')))
          ]);
        } else if (serverModel.connectStatus == 0) {
          return Row(children: [
            SizedBox(width: 20, height: 20, child: CircularProgressIndicator())
                .marginOnly(left: 4, right: iconMarginRight),
            Expanded(child: Text(translate('connecting_status')))
          ]);
        } else {
          return Row(children: [
            const Icon(Icons.check, color: colorPositive, size: iconSize)
                .marginOnly(right: iconMarginRight),
            Expanded(child: Text(translate('Ready')))
          ]);
        }
      }
  
      final showOneTime = serverModel.approveMode != 'click' &&
          serverModel.verificationMethod != kUsePermanentPassword;
      return PaddingCard(
          title: translate('Your Device'),
          child: Column(
            // ID
            children: [
              Row(children: [
                const Icon(Icons.perm_identity,
                        color: Colors.grey, size: iconSize)
                    .marginOnly(right: iconMarginRight),
                Text(
                  translate('ID'),
                  style: textStyleHeading,
                )
              ]),
              Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
                Text(
                  model.serverId.value.text,
                  style: textStyleValue,
                ),
                IconButton(
                    visualDensity: VisualDensity.compact,
                    icon: Icon(Icons.copy_outlined),
                    onPressed: () {
                      copyToClipboard(model.serverId.value.text.trim());
                    })
              ]).marginOnly(left: 39, bottom: 10),
              // Password
              Row(children: [
                const Icon(Icons.lock_outline, color: Colors.grey, size: iconSize)
                    .marginOnly(right: iconMarginRight),
                Text(
                  translate('One-time Password'),
                  style: textStyleHeading,
                )
              ]),
              Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
                Text(
                  !showOneTime ? '-' : model.serverPasswd.value.text,
                  style: textStyleValue,
                ),
                !showOneTime
                    ? SizedBox.shrink()
                    : Row(children: [
                        IconButton(
                            visualDensity: VisualDensity.compact,
                            icon: const Icon(Icons.refresh),
                            onPressed: () => bind.mainUpdateTemporaryPassword()),
                        IconButton(
                            visualDensity: VisualDensity.compact,
                            icon: Icon(Icons.copy_outlined),
                            onPressed: () {
                              copyToClipboard(
                                  model.serverPasswd.value.text.trim());
                            })
                      ])
              ]).marginOnly(left: 40, bottom: 15),
              ConnectionStateNotification()
            ],
          ));
    }
  }
  
  class PermissionChecker extends StatefulWidget {
    const PermissionChecker({Key? key}) : super(key: key);
  
    @override
    State<PermissionChecker> createState() => _PermissionCheckerState();
  }
  
  class _PermissionCheckerState extends State<PermissionChecker> {
    @override
    Widget build(BuildContext context) {
      final serverModel = Provider.of<ServerModel>(context);
      final hasAudioPermission = androidVersion >= 30;
      return PaddingCard(
          title: translate("Permissions"),
          child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
            serverModel.mediaOk
                ? ElevatedButton.icon(
                        style: ButtonStyle(
                            backgroundColor:
                                MaterialStateProperty.all(Colors.red)),
                        icon: const Icon(Icons.stop),
                        onPressed: serverModel.toggleService,
                        label: Text(translate("Stop service")))
                    .marginOnly(bottom: 8)
                : SizedBox.shrink(),
            PermissionRow(
                translate("Screen Capture"),
                serverModel.mediaOk,
                !serverModel.mediaOk &&
                        gFFI.userModel.userName.value.isEmpty &&
                        bind.mainGetLocalOption(key: "show-scam-warning") != "N"
                    ? () => showScamWarning(context, serverModel)
                    : serverModel.toggleService),
            PermissionRow(translate("Input Control"), serverModel.inputOk,
                serverModel.toggleInput),
            PermissionRow(translate("Transfer file"), serverModel.fileOk,
                serverModel.toggleFile),
            hasAudioPermission
                ? PermissionRow(translate("Audio Capture"), serverModel.audioOk,
                    serverModel.toggleAudio)
                : Row(children: [
                    Icon(Icons.info_outline).marginOnly(right: 15),
                    Expanded(
                        child: Text(
                      translate("android_version_audio_tip"),
                      style: const TextStyle(color: MyTheme.darkGray),
                    ))
                  ]),
            PermissionRow(translate("Enable clipboard"), serverModel.clipboardOk,
                serverModel.toggleClipboard),
          ]));
    }
  }
  
  class PermissionRow extends StatelessWidget {
    const PermissionRow(this.name, this.isOk, this.onPressed, {Key? key})
        : super(key: key);
  
    final String name;
    final bool isOk;
    final VoidCallback onPressed;
  
    @override
    Widget build(BuildContext context) {
      return SwitchListTile(
          visualDensity: VisualDensity.compact,
          contentPadding: EdgeInsets.all(0),
          title: Text(name),
          value: isOk,
          onChanged: (bool value) {
            onPressed();
          });
    }
  }
  
  class ConnectionManager extends StatelessWidget {
    const ConnectionManager({Key? key}) : super(key: key);
  
    @override
    Widget build(BuildContext context) {
      final serverModel = Provider.of<ServerModel>(context);
      return Column(
          children: serverModel.clients
              .map((client) => PaddingCard(
                  title: translate(client.isFileTransfer
                      ? "Transfer file"
                      : "Share screen"),
                  titleIcon: client.isFileTransfer
                      ? Icon(Icons.folder_outlined)
                      : Icon(Icons.mobile_screen_share),
                  child: Column(children: [
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        Expanded(child: ClientInfo(client)),
                        Expanded(
                            flex: -1,
                            child: client.isFileTransfer || !client.authorized
                                ? const SizedBox.shrink()
                                : IconButton(
                                    onPressed: () {
                                      gFFI.chatModel.changeCurrentKey(
                                          MessageKey(client.peerId, client.id));
                                      final bar = navigationBarKey.currentWidget;
                                      if (bar != null) {
                                        bar as BottomNavigationBar;
                                        bar.onTap!(1);
                                      }
                                    },
                                    icon: unreadTopRightBuilder(
                                        client.unreadChatMessageCount)))
                      ],
                    ),
                    client.authorized
                        ? const SizedBox.shrink()
                        : Text(
                            translate("android_new_connection_tip"),
                            style: Theme.of(context).textTheme.bodyMedium,
                          ).marginOnly(bottom: 5),
                    client.authorized
                        ? _buildDisconnectButton(client)
                        : _buildNewConnectionHint(serverModel, client),
                    if (client.incomingVoiceCall && !client.inVoiceCall)
                      ..._buildNewVoiceCallHint(context, serverModel, client),
                  ])))
              .toList());
    }
  
    Widget _buildDisconnectButton(Client client) {
      final disconnectButton = ElevatedButton.icon(
        style: ButtonStyle(backgroundColor: MaterialStatePropertyAll(Colors.red)),
        icon: const Icon(Icons.close),
        onPressed: () {
          bind.cmCloseConnection(connId: client.id);
          gFFI.invokeMethod("cancel_notification", client.id);
        },
        label: Text(translate("Disconnect")),
      );
      final buttons = [disconnectButton];
      if (client.inVoiceCall) {
        buttons.insert(
          0,
          ElevatedButton.icon(
            style: ButtonStyle(
                backgroundColor: MaterialStatePropertyAll(Colors.red)),
            icon: const Icon(Icons.phone),
            label: Text(translate("Stop")),
            onPressed: () {
              bind.cmCloseVoiceCall(id: client.id);
              gFFI.invokeMethod("cancel_notification", client.id);
            },
          ),
        );
      }
  
      if (buttons.length == 1) {
        return Container(
          alignment: Alignment.centerRight,
          child: disconnectButton,
        );
      } else {
        return Row(
          children: buttons,
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
        );
      }
    }
  
    Widget _buildNewConnectionHint(ServerModel serverModel, Client client) {
      return Row(mainAxisAlignment: MainAxisAlignment.end, children: [
        TextButton(
            child: Text(translate("Dismiss")),
            onPressed: () {
              serverModel.sendLoginResponse(client, false);
            }).marginOnly(right: 15),
        if (serverModel.approveMode != 'password')
          ElevatedButton.icon(
              icon: const Icon(Icons.check),
              label: Text(translate("Accept")),
              onPressed: () {
                serverModel.sendLoginResponse(client, true);
              }),
      ]);
    }
  
    List<Widget> _buildNewVoiceCallHint(
        BuildContext context, ServerModel serverModel, Client client) {
      return [
        Text(
          translate("android_new_voice_call_tip"),
          style: Theme.of(context).textTheme.bodyMedium,
        ).marginOnly(bottom: 5),
        Row(mainAxisAlignment: MainAxisAlignment.end, children: [
          TextButton(
              child: Text(translate("Dismiss")),
              onPressed: () {
                serverModel.handleVoiceCall(client, false);
              }).marginOnly(right: 15),
          if (serverModel.approveMode != 'password')
            ElevatedButton.icon(
                icon: const Icon(Icons.check),
                label: Text(translate("Accept")),
                onPressed: () {
                  serverModel.handleVoiceCall(client, true);
                }),
        ])
      ];
    }
  }
  
  class PaddingCard extends StatelessWidget {
    const PaddingCard({Key? key, required this.child, this.title, this.titleIcon})
        : super(key: key);
  
    final String? title;
    final Icon? titleIcon;
    final Widget child;
  
    @override
    Widget build(BuildContext context) {
      final children = [child];
      if (title != null) {
        children.insert(
            0,
            Padding(
                padding: const EdgeInsets.fromLTRB(0, 5, 0, 8),
                child: Row(
                  children: [
                    titleIcon?.marginOnly(right: 10) ?? const SizedBox.shrink(),
                    Expanded(
                      child: Text(title!,
                          style: Theme.of(context)
                              .textTheme
                              .titleLarge
                              ?.merge(TextStyle(fontWeight: FontWeight.bold))),
                    )
                  ],
                )));
      }
      return SizedBox(
          width: double.maxFinite,
          child: Card(
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(13),
            ),
            margin: const EdgeInsets.fromLTRB(12.0, 10.0, 12.0, 0),
            child: Padding(
              padding:
                  const EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0),
              child: Column(
                children: children,
              ),
            ),
          ));
    }
  }
  
  class ClientInfo extends StatelessWidget {
    final Client client;
    ClientInfo(this.client);
  
    @override
    Widget build(BuildContext context) {
      return Padding(
          padding: const EdgeInsets.symmetric(vertical: 8),
          child: Column(children: [
            Row(
              children: [
                Expanded(
                    flex: -1,
                    child: Padding(
                        padding: const EdgeInsets.only(right: 12),
                        child: CircleAvatar(
                            backgroundColor: str2color(
                                client.name,
                                Theme.of(context).brightness == Brightness.light
                                    ? 255
                                    : 150),
                            child: Text(client.name[0])))),
                Expanded(
                    child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                      Text(client.name, style: const TextStyle(fontSize: 18)),
                      const SizedBox(width: 8),
                      Text(client.peerId, style: const TextStyle(fontSize: 10))
                    ]))
              ],
            ),
          ]));
    }
  }
  
  void androidChannelInit() {
    gFFI.setMethodCallHandler((method, arguments) {
      debugPrint("flutter got android msg,$method,$arguments");
      try {
        switch (method) {
          case "start_capture":
            {
              gFFI.dialogManager.dismissAll();
              gFFI.serverModel.updateClientState();
              break;
            }
          case "on_state_changed":
            {
              var name = arguments["name"] as String;
              var value = arguments["value"] as String == "true";
              debugPrint("from jvm:on_state_changed,$name:$value");
              gFFI.serverModel.changeStatue(name, value);
              break;
            }
          case "on_android_permission_result":
            {
              var type = arguments["type"] as String;
              var result = arguments["result"] as bool;
              AndroidPermissionManager.complete(type, result);
              break;
            }
          case "on_media_projection_canceled":
            {
              gFFI.serverModel.stopService();
              break;
            }
          case "msgbox":
            {
              var type = arguments["type"] as String;
              var title = arguments["title"] as String;
              var text = arguments["text"] as String;
              var link = (arguments["link"] ?? '') as String;
              msgBox(gFFI.sessionId, type, title, text, link, gFFI.dialogManager);
              break;
            }
          case "stop_service":
            {
              print(
                  "stop_service by kotlin, isStart:${gFFI.serverModel.isStart}");
              if (gFFI.serverModel.isStart) {
                gFFI.serverModel.stopService();
              }
              break;
            }
        }
      } catch (e) {
        debugPrintStack(label: "MethodCallHandler err:$e");
      }
      return "";
    });
  }
  
  void showScamWarning(BuildContext context, ServerModel serverModel) {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return ScamWarningDialog(serverModel: serverModel);
      },
    );
  }
  