123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910 |
- 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);
- 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) 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 == 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
- ? "File Connection"
- : "Screen Connection"),
- 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);
- },
- );
- }
|