Merge branch 'refs/heads/main' into develop

This commit is contained in:
wanghongenpin
2024-11-01 20:22:31 +08:00
7 changed files with 144 additions and 76 deletions

View File

@@ -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))

View File

@@ -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<dynamic, dynamic> json) {
@@ -50,4 +53,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;
}

View File

@@ -348,25 +348,26 @@ class RequestPageState extends State<RequestPage> {
@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() {

View File

@@ -58,6 +58,7 @@ class MobileSearchState extends State<MobileSearch> {
padding: const EdgeInsets.only(left: 20),
child: TextFormField(
controller: _keywordController,
textAlignVertical: TextAlignVertical.center,
cursorHeight: 20,
keyboardType: TextInputType.url,
onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(),

View File

@@ -65,7 +65,7 @@ class _AppWhitelistState extends State<AppWhitelist> {
var appWhitelist = <Future<AppInfo>>[];
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);
}));
}
@@ -75,22 +75,41 @@ class _AppWhitelistState extends State<AppWhitelist> {
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<AppInfo> 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;
});
});
}
},
),
IconButton(
tooltip: isCN ? '清除失效应用' : 'clear invalid apps',
onPressed: () async {
if (configuration.appWhitelist.isEmpty) return;
List<AppInfo> 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),
),
],
),
@@ -193,7 +212,7 @@ class _AppBlacklistState extends State<AppBlacklist> {
var appBlacklist = <Future<AppInfo>>[];
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);
}));
}
@@ -203,23 +222,42 @@ class _AppBlacklistState extends State<AppBlacklist> {
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<AppInfo> 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;
});
});
}
},
),
IconButton(
tooltip: isCN ? '清除失效应用' : 'clear invalid apps',
onPressed: () async {
if (configuration.appBlacklist?.isEmpty == true) return;
List<AppInfo> 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),
),
],
),
@@ -267,7 +305,12 @@ class _AppBlacklistState extends State<AppBlacklist> {
///已安装的app列表
class InstalledAppsWidget extends StatefulWidget {
const InstalledAppsWidget({super.key});
const InstalledAppsWidget({
super.key,
required this.addedList,
});
final List<AppInfo> addedList;
@override
State<InstalledAppsWidget> createState() => _InstalledAppsWidgetState();
@@ -306,6 +349,7 @@ class _InstalledAppsWidgetState extends State<InstalledAppsWidget> {
builder: (BuildContext context, AsyncSnapshot<List<AppInfo>> snapshot) {
if (snapshot.hasData) {
List<AppInfo> appInfoList = snapshot.data!;
appInfoList = appInfoList.toSet().difference(widget.addedList.toSet()).toList();
if (keyword != null && keyword!.trim().isNotEmpty) {
appInfoList = appInfoList
.where((element) =>

View File

@@ -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(

View File

@@ -94,6 +94,9 @@ class _PictureInPictureState extends State<PictureInPictureIcon> {
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<PictureInPictureIcon> {
}
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<String>? appList = configuration.appWhitelistEnabled ? configuration.appWhitelist : [];
List<String>? 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<String>? appList = configuration.appWhitelistEnabled ? configuration.appWhitelist : [];
List<String>? 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))),
);
}
}