From f4b6d0911cf919d095407d46db628c8b957b5d4c Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Thu, 31 Oct 2024 08:31:04 +0800 Subject: [PATCH 1/6] fix: `pendingActivityIntent` not working --- android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt b/android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt index 868bca4..fdfbff3 100644 --- a/android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt +++ b/android/app/src/main/kotlin/com/network/proxy/ProxyVpnService.kt @@ -153,6 +153,7 @@ class ProxyVpnService : VpnService(), ProtectSocket { val notification: Notification = NotificationCompat.Builder(this, VPN_NOTIFICATION_CHANNEL_ID) + .setSmallIcon(R.mipmap.ic_launcher) .setContentIntent(pendingActivityIntent) .setContentTitle(getString(R.string.vpn_active_notification_title)) .setContentText(getString(R.string.vpn_active_notification_content)) From ccc002b70dcd331c3fd5dc8b2274ae086f0fb2c9 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Thu, 31 Oct 2024 08:32:06 +0800 Subject: [PATCH 2/6] opt: remove `fixAutoLines` for textfield --- lib/ui/mobile/setting/request_block.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/mobile/setting/request_block.dart b/lib/ui/mobile/setting/request_block.dart index ac16ab8..cf69936 100644 --- a/lib/ui/mobile/setting/request_block.dart +++ b/lib/ui/mobile/setting/request_block.dart @@ -183,7 +183,7 @@ class RequestBlockAddDialog extends StatelessWidget { SwitchWidget(title: localizations.enable, value: item.enabled, onChanged: (val) => enabled = val), const SizedBox(height: 10), TextFormField( - initialValue: item.url.fixAutoLines(), + initialValue: item.url, maxLines: 3, minLines: 1, decoration: const InputDecoration( From d5160458e06687d2dfbbbb7aaa026f7653c3d8a5 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Thu, 31 Oct 2024 10:38:49 +0800 Subject: [PATCH 3/6] opt: filter added apps --- lib/native/installed_apps.dart | 14 ++++++ lib/ui/mobile/setting/app_filter.dart | 68 ++++++++++++++++----------- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/lib/native/installed_apps.dart b/lib/native/installed_apps.dart index b422ed1..0d29901 100644 --- a/lib/native/installed_apps.dart +++ b/lib/native/installed_apps.dart @@ -50,4 +50,18 @@ class AppInfo { String toString() { return toJson().toString(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other is AppInfo) { + return packageName == other.packageName; + } + return false; + } + + @override + int get hashCode => packageName.hashCode; } diff --git a/lib/ui/mobile/setting/app_filter.dart b/lib/ui/mobile/setting/app_filter.dart index f82c2a9..3558308 100644 --- a/lib/ui/mobile/setting/app_filter.dart +++ b/lib/ui/mobile/setting/app_filter.dart @@ -75,21 +75,24 @@ class _AppWhitelistState extends State { actions: [ IconButton( icon: const Icon(Icons.add), - onPressed: () { + onPressed: () async { //添加 - Navigator.of(context) - .push(MaterialPageRoute(builder: (context) => const InstalledAppsWidget())) - .then((value) { - if (value != null) { - if (configuration.appWhitelist.contains(value)) { - return; + List list = await Future.wait(appWhitelist); + if (context.mounted) { + Navigator.of(context).push(MaterialPageRoute(builder: (context) { + return InstalledAppsWidget(addedList: list); + })).then((value) { + if (value != null) { + if (configuration.appWhitelist.contains(value)) { + return; + } + setState(() { + configuration.appWhitelist.add(value); + changed = true; + }); } - setState(() { - configuration.appWhitelist.add(value); - changed = true; - }); - } - }); + }); + } }, ), ], @@ -203,22 +206,25 @@ class _AppBlacklistState extends State { actions: [ IconButton( icon: const Icon(Icons.add), - onPressed: () { + onPressed: () async { //添加 - Navigator.of(context) - .push(MaterialPageRoute(builder: (context) => const InstalledAppsWidget())) - .then((value) { - if (value != null) { - if (configuration.appBlacklist?.contains(value) == true) { - return; + List list = await Future.wait(appBlacklist); + if (context.mounted) { + Navigator.of(context) + .push(MaterialPageRoute(builder: (context) => InstalledAppsWidget(addedList: list))) + .then((value) { + if (value != null) { + if (configuration.appBlacklist?.contains(value) == true) { + return; + } + setState(() { + configuration.appBlacklist ??= []; + configuration.appBlacklist?.add(value); + changed = true; + }); } - setState(() { - configuration.appBlacklist ??= []; - configuration.appBlacklist?.add(value); - changed = true; - }); - } - }); + }); + } }, ), ], @@ -267,7 +273,12 @@ class _AppBlacklistState extends State { ///已安装的app列表 class InstalledAppsWidget extends StatefulWidget { - const InstalledAppsWidget({super.key}); + const InstalledAppsWidget({ + super.key, + required this.addedList, + }); + + final List addedList; @override State createState() => _InstalledAppsWidgetState(); @@ -306,6 +317,7 @@ class _InstalledAppsWidgetState extends State { builder: (BuildContext context, AsyncSnapshot> snapshot) { if (snapshot.hasData) { List appInfoList = snapshot.data!; + appInfoList = appInfoList.toSet().difference(widget.addedList.toSet()).toList(); if (keyword != null && keyword!.trim().isNotEmpty) { appInfoList = appInfoList .where((element) => From 02dba7607de64c8c308fe84c2a34404d917e1fab Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Thu, 31 Oct 2024 10:57:48 +0800 Subject: [PATCH 4/6] feat: clear invalid apps --- lib/native/installed_apps.dart | 3 +++ lib/ui/mobile/setting/app_filter.dart | 36 +++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/native/installed_apps.dart b/lib/native/installed_apps.dart index 0d29901..13d0d94 100644 --- a/lib/native/installed_apps.dart +++ b/lib/native/installed_apps.dart @@ -23,11 +23,14 @@ class AppInfo { //icon Uint8List? icon; + bool? inValid; + AppInfo({ this.name, this.packageName, this.versionName, this.icon, + this.inValid, }); AppInfo.formJson(Map json) { diff --git a/lib/ui/mobile/setting/app_filter.dart b/lib/ui/mobile/setting/app_filter.dart index 3558308..e9b7874 100644 --- a/lib/ui/mobile/setting/app_filter.dart +++ b/lib/ui/mobile/setting/app_filter.dart @@ -65,7 +65,7 @@ class _AppWhitelistState extends State { var appWhitelist = >[]; for (var element in configuration.appWhitelist) { appWhitelist.add(InstalledApps.getAppInfo(element).catchError((e) { - return AppInfo(name: isCN ? "未知应用" : "Unknown app", packageName: element); + return AppInfo(name: isCN ? "未知应用" : "Unknown app", packageName: element, inValid: true); })); } @@ -95,6 +95,22 @@ class _AppWhitelistState extends State { } }, ), + IconButton( + tooltip: isCN ? '清除失效应用' : 'clear invalid apps', + onPressed: () async { + if (configuration.appWhitelist.isEmpty) return; + List list = await Future.wait(appWhitelist); + for (AppInfo appInfo in list) { + if (appInfo.inValid == true) { + configuration.appWhitelist.remove(appInfo.packageName); + } + } + setState(() { + changed = true; + }); + }, + icon: Icon(Icons.cleaning_services_outlined), + ), ], ), body: Column(children: [ @@ -196,7 +212,7 @@ class _AppBlacklistState extends State { var appBlacklist = >[]; for (var element in configuration.appBlacklist ?? []) { appBlacklist.add(InstalledApps.getAppInfo(element).catchError((e) { - return AppInfo(name: isCN ? "未知应用" : "Unknown app", packageName: element); + return AppInfo(name: isCN ? "未知应用" : "Unknown app", packageName: element, inValid: true); })); } @@ -227,6 +243,22 @@ class _AppBlacklistState extends State { } }, ), + IconButton( + tooltip: isCN ? '清除失效应用' : 'clear invalid apps', + onPressed: () async { + if (configuration.appBlacklist?.isEmpty == true) return; + List list = await Future.wait(appBlacklist); + for (AppInfo appInfo in list) { + if (appInfo.inValid == true) { + configuration.appBlacklist?.remove(appInfo.packageName); + } + } + setState(() { + changed = true; + }); + }, + icon: Icon(Icons.cleaning_services_outlined), + ), ], ), body: FutureBuilder( From fadd09f151b449d26867e1dca03ed0606a238f7a Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Thu, 31 Oct 2024 11:21:49 +0800 Subject: [PATCH 5/6] fix: textAlignVertical --- lib/ui/mobile/request/search.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ui/mobile/request/search.dart b/lib/ui/mobile/request/search.dart index a12c944..20df31d 100644 --- a/lib/ui/mobile/request/search.dart +++ b/lib/ui/mobile/request/search.dart @@ -58,6 +58,7 @@ class MobileSearchState extends State { padding: const EdgeInsets.only(left: 20), child: TextFormField( controller: _keywordController, + textAlignVertical: TextAlignVertical.center, cursorHeight: 20, keyboardType: TextInputType.url, onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(), From 9bff3ce3b9501d9e989db51cb8f7355c6280ff96 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Fri, 1 Nov 2024 13:24:51 +0800 Subject: [PATCH 6/6] opt: pip button --- lib/ui/mobile/mobile.dart | 39 +++++++++++---------- lib/ui/mobile/widgets/pip.dart | 64 ++++++++++++++++++---------------- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/lib/ui/mobile/mobile.dart b/lib/ui/mobile/mobile.dart index d9ee1bc..d6c29c3 100644 --- a/lib/ui/mobile/mobile.dart +++ b/lib/ui/mobile/mobile.dart @@ -348,25 +348,26 @@ class RequestPageState extends State { @override Widget build(BuildContext context) { - return Scaffold( - floatingActionButton: PictureInPictureIcon(proxyServer), - body: Scaffold( - appBar: _MobileAppBar(widget.appConfiguration, proxyServer, remoteDevice: remoteDevice), - drawer: widget.appConfiguration.bottomNavigation - ? null - : DrawerWidget(proxyServer: proxyServer, container: MobileApp.container), - floatingActionButton: _launchActionButton(), - body: ValueListenableBuilder( - valueListenable: remoteDevice, - builder: (context, value, _) { - return Column(children: [ - value.connect ? remoteConnect(value) : const SizedBox(), - Expanded( - child: RequestListWidget( - key: MobileApp.requestStateKey, proxyServer: proxyServer, list: MobileApp.container)) - ]); - }), - )); + return Stack(children: [ + Scaffold( + appBar: _MobileAppBar(widget.appConfiguration, proxyServer, remoteDevice: remoteDevice), + drawer: widget.appConfiguration.bottomNavigation + ? null + : DrawerWidget(proxyServer: proxyServer, container: MobileApp.container), + floatingActionButton: _launchActionButton(), + body: ValueListenableBuilder( + valueListenable: remoteDevice, + builder: (context, value, _) { + return Column(children: [ + value.connect ? remoteConnect(value) : const SizedBox(), + Expanded( + child: RequestListWidget( + key: MobileApp.requestStateKey, proxyServer: proxyServer, list: MobileApp.container)) + ]); + }), + ), + PictureInPictureIcon(proxyServer), + ]); } Widget _launchActionButton() { diff --git a/lib/ui/mobile/widgets/pip.dart b/lib/ui/mobile/widgets/pip.dart index c32bb84..7c44b1d 100644 --- a/lib/ui/mobile/widgets/pip.dart +++ b/lib/ui/mobile/widgets/pip.dart @@ -94,6 +94,9 @@ class _PictureInPictureState extends State { static double xPosition = -1; static double yPosition = -1; static Size? size; + late final double _top; + late final double _right; + late final double _bottom; AppLocalizations get localizations => AppLocalizations.of(context)!; @@ -117,40 +120,41 @@ class _PictureInPictureState extends State { } if (xPosition == -1) { - xPosition = size!.width * 0.9; + xPosition = size!.width - 48; yPosition = size!.height * 0.35; + _top = MediaQuery.of(context).padding.top; + _right = xPosition; + _bottom = size!.height - 48 - (AppConfiguration.current?.bottomNavigation == false ? 0 : 56); } - return Stack(children: [ - Positioned( - top: yPosition, - left: xPosition, - child: GestureDetector( - onPanUpdate: (tapInfo) { - if (xPosition + tapInfo.delta.dx < 0) return; - if (yPosition + tapInfo.delta.dy < 0) return; + return Positioned( + top: yPosition, + left: xPosition, + child: GestureDetector( + onPanUpdate: (tapInfo) { + // if (xPosition + tapInfo.delta.dx < 0) return; + // if (yPosition + tapInfo.delta.dy < 0) return; - setState(() { - xPosition += tapInfo.delta.dx; - yPosition += tapInfo.delta.dy; - }); - }, - child: IconButton( - tooltip: localizations.windowMode, - onPressed: () async { - var configuration = widget.proxyServer.configuration; - List? appList = configuration.appWhitelistEnabled ? configuration.appWhitelist : []; - List? disallowApps; - if (appList.isEmpty) { - disallowApps = configuration.appBlacklist ?? []; - } + setState(() { + xPosition = (xPosition + tapInfo.delta.dx).clamp(0, _right); + yPosition = (yPosition + tapInfo.delta.dy).clamp(_top, _bottom); + }); + }, + child: IconButton( + tooltip: localizations.windowMode, + onPressed: () async { + var configuration = widget.proxyServer.configuration; + List? appList = configuration.appWhitelistEnabled ? configuration.appWhitelist : []; + List? disallowApps; + if (appList.isEmpty) { + disallowApps = configuration.appBlacklist ?? []; + } - PictureInPicture.enterPictureInPictureMode( - Platform.isAndroid ? await localIp() : "127.0.0.1", widget.proxyServer.port, - appList: appList, disallowApps: disallowApps); - }, - icon: const Icon(Icons.picture_in_picture_alt))), - ) - ]); + PictureInPicture.enterPictureInPictureMode( + Platform.isAndroid ? await localIp() : "127.0.0.1", widget.proxyServer.port, + appList: appList, disallowApps: disallowApps); + }, + icon: const Icon(Icons.picture_in_picture_alt))), + ); } }