mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-05-09 00:34:17 +08:00
app blacklist
This commit is contained in:
@@ -31,6 +31,7 @@ class ProxyVpnService : VpnService(), ProtectSocket {
|
||||
const val PROXY_HOST_KEY = "ProxyHost"
|
||||
const val PROXY_PORT_KEY = "ProxyPort"
|
||||
const val ALLOW_APPS_KEY = "AllowApps" //允许的名单
|
||||
const val DISALLOW_APPS_KEY = "DisallowApps" //禁止的名单
|
||||
|
||||
/**
|
||||
* 动作:断开连接
|
||||
@@ -48,6 +49,7 @@ class ProxyVpnService : VpnService(), ProtectSocket {
|
||||
var host: String? = null
|
||||
var port: Int = 0
|
||||
var allowApps: ArrayList<String>? = null
|
||||
private var disallowApps: ArrayList<String>? = null
|
||||
|
||||
fun stopVpnIntent(context: Context): Intent {
|
||||
return Intent(context, ProxyVpnService::class.java).also {
|
||||
@@ -59,12 +61,14 @@ class ProxyVpnService : VpnService(), ProtectSocket {
|
||||
context: Context,
|
||||
proxyHost: String? = host,
|
||||
proxyPort: Int? = port,
|
||||
allowApps: ArrayList<String>? = this.allowApps
|
||||
allowApps: ArrayList<String>? = this.allowApps,
|
||||
disallowApps: ArrayList<String>? = this.disallowApps
|
||||
): Intent {
|
||||
return Intent(context, ProxyVpnService::class.java).also {
|
||||
it.putExtra(PROXY_HOST_KEY, proxyHost)
|
||||
it.putExtra(PROXY_PORT_KEY, proxyPort)
|
||||
it.putStringArrayListExtra(ALLOW_APPS_KEY, allowApps)
|
||||
it.putStringArrayListExtra(DISALLOW_APPS_KEY, disallowApps)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +86,8 @@ class ProxyVpnService : VpnService(), ProtectSocket {
|
||||
connect(
|
||||
intent.getStringExtra(PROXY_HOST_KEY) ?: host!!,
|
||||
intent.getIntExtra(PROXY_PORT_KEY, port),
|
||||
intent.getStringArrayListExtra(ALLOW_APPS_KEY) ?: allowApps
|
||||
intent.getStringArrayListExtra(ALLOW_APPS_KEY) ?: allowApps,
|
||||
intent.getStringArrayListExtra(DISALLOW_APPS_KEY)
|
||||
)
|
||||
START_STICKY
|
||||
}
|
||||
@@ -98,13 +103,19 @@ class ProxyVpnService : VpnService(), ProtectSocket {
|
||||
isRunning = false
|
||||
}
|
||||
|
||||
private fun connect(proxyHost: String, proxyPort: Int, allowPackages: ArrayList<String>?) {
|
||||
private fun connect(
|
||||
proxyHost: String,
|
||||
proxyPort: Int,
|
||||
allowPackages: ArrayList<String>?,
|
||||
disallowPackages: ArrayList<String>?
|
||||
) {
|
||||
Log.i("ProxyVpnService", "startVpn $host:$port $allowApps")
|
||||
|
||||
host = proxyHost
|
||||
port = proxyPort
|
||||
allowApps = allowPackages
|
||||
vpnInterface = createVpnInterface(proxyHost, proxyPort, allowPackages)
|
||||
disallowApps = disallowPackages
|
||||
vpnInterface = createVpnInterface(proxyHost, proxyPort, allowPackages, disallowPackages)
|
||||
if (vpnInterface == null) {
|
||||
val alertDialog = Intent(applicationContext, VpnAlertDialog::class.java)
|
||||
.setAction("com.network.proxy.ProxyVpnService")
|
||||
@@ -152,7 +163,12 @@ class ProxyVpnService : VpnService(), ProtectSocket {
|
||||
}
|
||||
|
||||
|
||||
private fun createVpnInterface(proxyHost: String, proxyPort: Int, allowPackages: List<String>?):
|
||||
private fun createVpnInterface(
|
||||
proxyHost: String,
|
||||
proxyPort: Int,
|
||||
allowPackages: List<String>?,
|
||||
disallowApps: ArrayList<String>?
|
||||
):
|
||||
ParcelFileDescriptor? {
|
||||
val build = Builder()
|
||||
.setMtu(MAX_PACKET_LEN)
|
||||
@@ -170,6 +186,10 @@ class ProxyVpnService : VpnService(), ProtectSocket {
|
||||
build.addDisallowedApplication(baseContext.packageName)
|
||||
}
|
||||
|
||||
disallowApps?.forEach {
|
||||
build.addDisallowedApplication(it)
|
||||
}
|
||||
|
||||
build.setConfigureIntent(
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
|
||||
@@ -24,6 +24,8 @@ class PictureInPicturePlugin : AndroidFlutterPlugin() {
|
||||
var channel: MethodChannel? = null
|
||||
var proxyHost: String? = null
|
||||
var proxyPort: Int? = null
|
||||
var allowApps: ArrayList<String>? = null
|
||||
var disallowApps: ArrayList<String>? = null
|
||||
|
||||
///广播事件接受者
|
||||
private val vpnBroadcastReceiver = object : BroadcastReceiver() {
|
||||
@@ -43,7 +45,15 @@ class PictureInPicturePlugin : AndroidFlutterPlugin() {
|
||||
if (isRunning) {
|
||||
activity.startService(ProxyVpnService.stopVpnIntent(activity))
|
||||
} else {
|
||||
activity.startService(ProxyVpnService.startVpnIntent(activity, proxyHost, proxyPort))
|
||||
activity.startService(
|
||||
ProxyVpnService.startVpnIntent(
|
||||
activity,
|
||||
proxyHost,
|
||||
proxyPort,
|
||||
allowApps,
|
||||
disallowApps
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
//设置画中画参数
|
||||
@@ -67,6 +77,8 @@ class PictureInPicturePlugin : AndroidFlutterPlugin() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
proxyHost = call.argument<String>("proxyHost")
|
||||
proxyPort = call.argument<Int>("proxyPort")
|
||||
allowApps = call.argument<ArrayList<String>>("allowApps")
|
||||
disallowApps = call.argument<ArrayList<String>>("disallowApps")
|
||||
|
||||
val param = updatePictureInPictureParams(ProxyVpnService.isRunning)
|
||||
if (!registerBroadcast) {
|
||||
|
||||
@@ -23,9 +23,10 @@ class VpnServicePlugin : AndroidFlutterPlugin() {
|
||||
val host = call.argument<String>("proxyHost")
|
||||
val port = call.argument<Int>("proxyPort")
|
||||
val allowApps = call.argument<ArrayList<String>>("allowApps")
|
||||
val disallowApps = call.argument<ArrayList<String>>("disallowApps")
|
||||
val prepareVpn = prepareVpn(host!!, port!!, allowApps)
|
||||
if (prepareVpn) {
|
||||
startVpn(host, port, allowApps)
|
||||
startVpn(host, port, allowApps, disallowApps)
|
||||
}
|
||||
result.success(prepareVpn)
|
||||
}
|
||||
@@ -39,8 +40,9 @@ class VpnServicePlugin : AndroidFlutterPlugin() {
|
||||
val host = call.argument<String>("proxyHost")
|
||||
val port = call.argument<Int>("proxyPort")
|
||||
val allowApps = call.argument<ArrayList<String>>("allowApps")
|
||||
val disallowApps = call.argument<ArrayList<String>>("disallowApps")
|
||||
stopVpn()
|
||||
startVpn(host!!, port!!, allowApps)
|
||||
startVpn(host!!, port!!, allowApps, disallowApps)
|
||||
}
|
||||
|
||||
else -> {
|
||||
@@ -69,8 +71,13 @@ class VpnServicePlugin : AndroidFlutterPlugin() {
|
||||
/**
|
||||
* 启动vpn服务
|
||||
*/
|
||||
private fun startVpn(host: String, port: Int, allowApps: ArrayList<String>?) {
|
||||
val intent = ProxyVpnService.startVpnIntent(activity, host, port, allowApps)
|
||||
private fun startVpn(
|
||||
host: String,
|
||||
port: Int,
|
||||
allowApps: ArrayList<String>?,
|
||||
disallowApps: ArrayList<String>?
|
||||
) {
|
||||
val intent = ProxyVpnService.startVpnIntent(activity, host, port, allowApps, disallowApps)
|
||||
activity.startService(intent)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class ProcessInfoManager private constructor() {
|
||||
}
|
||||
|
||||
private val localPortUidMap =
|
||||
CacheBuilder.newBuilder().maximumSize(10_000).expireAfterAccess(120, TimeUnit.SECONDS)
|
||||
CacheBuilder.newBuilder().maximumSize(10_000).expireAfterAccess(60, TimeUnit.SECONDS)
|
||||
.build<Int, Int>()
|
||||
|
||||
private val appInfoCache =
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
"domainBlacklist": "Domain Blacklist",
|
||||
"domainFilter": "Domain Filter",
|
||||
"appWhitelist": "App Whitelist",
|
||||
"appBlacklist": "App Blacklist",
|
||||
"scanCode": "Scan Code Connect",
|
||||
"addBlacklist": "Add Filter Blacklist",
|
||||
"addWhitelist": "Add Filter Whitelist",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"domainWhitelist": "域名白名单",
|
||||
"domainBlacklist" :"域名黑名单",
|
||||
"appWhitelist": "应用白名单",
|
||||
"appBlacklist": "应用黑名单",
|
||||
"domainFilter": "域名过滤",
|
||||
"scanCode": "扫码连接",
|
||||
"addBlacklist": "添加黑名单",
|
||||
|
||||
@@ -28,9 +28,10 @@ class PictureInPicture {
|
||||
});
|
||||
|
||||
///进入画中画模式
|
||||
static Future<bool> enterPictureInPictureMode(String host, int port) async {
|
||||
final bool enterPictureInPictureMode =
|
||||
await _channel.invokeMethod('enterPictureInPictureMode', {"proxyHost": host, "proxyPort": port});
|
||||
static Future<bool> enterPictureInPictureMode(String host, int port,
|
||||
{List<String>? appList, List<String>? disallowApps}) async {
|
||||
final bool enterPictureInPictureMode = await _channel.invokeMethod('enterPictureInPictureMode',
|
||||
{"proxyHost": host, "proxyPort": port, "allowApps": appList, "disallowApps": disallowApps});
|
||||
inPip = true;
|
||||
|
||||
return enterPictureInPictureMode;
|
||||
|
||||
@@ -5,9 +5,9 @@ class Vpn {
|
||||
|
||||
static bool isVpnStarted = false; //vpn是否已经启动
|
||||
|
||||
static startVpn(String host, int port, {List<String>? appList, bool? backgroundAudioEnable = true}) {
|
||||
proxyVpnChannel.invokeMethod("startVpn",
|
||||
{"proxyHost": host, "proxyPort": port, "allowApps": appList, "backgroundAudioEnable": backgroundAudioEnable});
|
||||
static startVpn(String host, int port, {List<String>? appList, List<String>? disallowApps}) {
|
||||
proxyVpnChannel.invokeMethod(
|
||||
"startVpn", {"proxyHost": host, "proxyPort": port, "allowApps": appList, "disallowApps": disallowApps});
|
||||
isVpnStarted = true;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ class Vpn {
|
||||
}
|
||||
|
||||
//重启vpn
|
||||
static restartVpn(String host, int port, {List<String>? appList, bool? backgroundAudioEnable = true}) {
|
||||
proxyVpnChannel.invokeMethod("restartVpn",
|
||||
{"proxyHost": host, "proxyPort": port, "allowApps": appList, "backgroundAudioEnable": backgroundAudioEnable});
|
||||
static restartVpn(String host, int port, {List<String>? appList, List<String>? disallowApps}) {
|
||||
proxyVpnChannel.invokeMethod(
|
||||
"restartVpn", {"proxyHost": host, "proxyPort": port, "allowApps": appList, "disallowApps": disallowApps});
|
||||
|
||||
isVpnStarted = true;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ class Configuration {
|
||||
//白名单应用
|
||||
List<String> appWhitelist = [];
|
||||
|
||||
//应用黑名单
|
||||
List<String>? appBlacklist;
|
||||
|
||||
//远程连接 不持久化保存
|
||||
String? remoteHost;
|
||||
|
||||
@@ -84,6 +87,7 @@ class Configuration {
|
||||
externalProxy = ProxyInfo.fromJson(config['externalProxy']);
|
||||
}
|
||||
appWhitelist = List<String>.from(config['appWhitelist'] ?? []);
|
||||
appBlacklist = config['appBlacklist'] == null ? null : List<String>.from(config['appBlacklist']);
|
||||
HostFilter.whitelist.load(config['whitelist']);
|
||||
HostFilter.blacklist.load(config['blacklist']);
|
||||
}
|
||||
@@ -131,6 +135,7 @@ class Configuration {
|
||||
'proxyPassDomains': proxyPassDomains,
|
||||
'externalProxy': externalProxy?.toJson(),
|
||||
'appWhitelist': appWhitelist,
|
||||
'appBlacklist': appBlacklist,
|
||||
'historyCacheTime': historyCacheTime,
|
||||
'whitelist': HostFilter.whitelist.toJson(),
|
||||
'blacklist': HostFilter.blacklist.toJson(),
|
||||
|
||||
@@ -15,6 +15,15 @@ class ExpiringCache<K, V> {
|
||||
_expirationTimes[key] = Timer(duration, () => remove(key));
|
||||
}
|
||||
|
||||
V? putIfAbsent(K key, V Function() ifAbsent) {
|
||||
if (_cache.containsKey(key)) {
|
||||
return _cache[key];
|
||||
}
|
||||
final value = ifAbsent();
|
||||
set(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
V? get(K key) {
|
||||
return _cache[key];
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import 'package:network_proxy/ui/configuration.dart';
|
||||
import 'package:network_proxy/ui/mobile/menu/preference.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/favorite.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/history.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/app_whitelist.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/app_filter.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/filter.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/request_block.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/request_rewrite.dart';
|
||||
@@ -153,6 +153,12 @@ class FilterMenu extends StatelessWidget {
|
||||
title: Text(localizations.appWhitelist),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () => navigator(context, AppWhitelist(proxyServer: proxyServer))),
|
||||
Platform.isIOS
|
||||
? const SizedBox()
|
||||
: ListTile(
|
||||
title: Text(localizations.appBlacklist),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () => navigator(context, AppBlacklist(proxyServer: proxyServer))),
|
||||
])));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,8 @@ class MobileHomeState extends State<MobileHomePage> implements EventListener, Li
|
||||
}
|
||||
|
||||
return PictureInPicture.enterPictureInPictureMode(
|
||||
Platform.isAndroid ? await localIp() : "127.0.0.1", proxyServer.port);
|
||||
Platform.isAndroid ? await localIp() : "127.0.0.1", proxyServer.port,
|
||||
appList: proxyServer.configuration.appWhitelist, disallowApps: proxyServer.configuration.appBlacklist);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -207,8 +208,8 @@ class MobileHomeState extends State<MobileHomePage> implements EventListener, Li
|
||||
serverLaunch: false,
|
||||
onStart: () async {
|
||||
Vpn.startVpn(Platform.isAndroid ? await localIp() : "127.0.0.1", proxyServer.port,
|
||||
backgroundAudioEnable: widget.appConfiguration.iosVpnBackgroundAudioEnable,
|
||||
appList: proxyServer.configuration.appWhitelist);
|
||||
appList: proxyServer.configuration.appWhitelist,
|
||||
disallowApps: proxyServer.configuration.appBlacklist);
|
||||
},
|
||||
onStop: () => Vpn.stopVpn())),
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/host_port.dart';
|
||||
import 'package:network_proxy/network/http/http.dart';
|
||||
import 'package:network_proxy/network/http_client.dart';
|
||||
import 'package:network_proxy/network/util/cache.dart';
|
||||
import 'package:network_proxy/storage/favorites.dart';
|
||||
import 'package:network_proxy/ui/component/utils.dart';
|
||||
import 'package:network_proxy/ui/content/panel.dart';
|
||||
@@ -42,6 +43,8 @@ class RequestRow extends StatefulWidget {
|
||||
}
|
||||
|
||||
class RequestRowState extends State<RequestRow> {
|
||||
static ExpiringCache<String, Image> imageCache = ExpiringCache<String, Image>(const Duration(minutes: 5));
|
||||
|
||||
late HttpRequest request;
|
||||
HttpResponse? response;
|
||||
bool selected = false;
|
||||
@@ -114,8 +117,10 @@ class RequestRowState extends State<RequestRow> {
|
||||
}
|
||||
|
||||
//如果有缓存图标直接返回图标
|
||||
if(request.processInfo!.hasCacheIcon){
|
||||
return Image.memory(request.processInfo!.cacheIcon!, width: 40);
|
||||
if (request.processInfo!.hasCacheIcon) {
|
||||
return imageCache.putIfAbsent(request.processInfo!.id, () {
|
||||
return Image.memory(request.processInfo!.cacheIcon!, width: 40, gaplessPlayback: true);
|
||||
});
|
||||
}
|
||||
|
||||
return FutureBuilder(
|
||||
|
||||
@@ -6,7 +6,6 @@ import 'package:network_proxy/native/installed_apps.dart';
|
||||
import 'package:network_proxy/native/vpn.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/ui/configuration.dart';
|
||||
|
||||
//应用白名单 目前只支持安卓 ios没办法获取安装的列表
|
||||
class AppWhitelist extends StatefulWidget {
|
||||
@@ -37,8 +36,7 @@ class _AppWhitelistState extends State<AppWhitelist> {
|
||||
configuration.flushConfig();
|
||||
if (Vpn.isVpnStarted) {
|
||||
Vpn.restartVpn("127.0.0.1", widget.proxyServer.port,
|
||||
appList: configuration.appWhitelist,
|
||||
backgroundAudioEnable: AppConfiguration.current?.iosVpnBackgroundAudioEnable);
|
||||
appList: configuration.appWhitelist, disallowApps: configuration.appBlacklist);
|
||||
}
|
||||
}
|
||||
super.dispose();
|
||||
@@ -126,6 +124,118 @@ class _AppWhitelistState extends State<AppWhitelist> {
|
||||
}
|
||||
}
|
||||
|
||||
class AppBlacklist extends StatefulWidget {
|
||||
final ProxyServer proxyServer;
|
||||
|
||||
const AppBlacklist({super.key, required this.proxyServer});
|
||||
|
||||
@override
|
||||
State<AppBlacklist> createState() => _AppBlacklistState();
|
||||
}
|
||||
|
||||
class _AppBlacklistState extends State<AppBlacklist> {
|
||||
late Configuration configuration;
|
||||
|
||||
bool changed = false;
|
||||
|
||||
AppLocalizations get localizations => AppLocalizations.of(context)!;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
configuration = widget.proxyServer.configuration;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (changed) {
|
||||
configuration.flushConfig();
|
||||
if (Vpn.isVpnStarted) {
|
||||
Vpn.restartVpn("127.0.0.1", widget.proxyServer.port,
|
||||
appList: configuration.appWhitelist, disallowApps: configuration.appBlacklist);
|
||||
}
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isCN = Localizations.localeOf(context) == const Locale.fromSubtags(languageCode: 'zh');
|
||||
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 Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(localizations.appBlacklist, style: const TextStyle(fontSize: 16)),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () {
|
||||
//添加
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (context) => const InstalledAppsWidget()))
|
||||
.then((value) {
|
||||
if (value != null) {
|
||||
if (configuration.appBlacklist?.contains(value) == true) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
configuration.appBlacklist ??= [];
|
||||
configuration.appBlacklist?.add(value);
|
||||
changed = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: FutureBuilder(
|
||||
future: Future.wait(appBlacklist),
|
||||
builder: (BuildContext context, AsyncSnapshot<List<AppInfo>> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
if (snapshot.data!.isEmpty) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: Text(localizations.emptyData, style: const TextStyle(color: Colors.grey))),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: snapshot.data!.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
AppInfo appInfo = snapshot.data![index];
|
||||
return ListTile(
|
||||
leading: appInfo.icon == null ? const Icon(Icons.question_mark) : Image.memory(appInfo.icon!),
|
||||
title: Text(appInfo.name ?? ""),
|
||||
subtitle: Text(appInfo.packageName ?? ""),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
//删除
|
||||
setState(() {
|
||||
configuration.appBlacklist?.remove(appInfo.packageName);
|
||||
changed = true;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
///已安装的app列表
|
||||
class InstalledAppsWidget extends StatefulWidget {
|
||||
const InstalledAppsWidget({super.key});
|
||||
@@ -130,7 +130,9 @@ class _PictureInPictureState extends State<PictureInPictureIcon> {
|
||||
tooltip: localizations.windowMode,
|
||||
onPressed: () async {
|
||||
PictureInPicture.enterPictureInPictureMode(
|
||||
Platform.isAndroid ? await localIp() : "127.0.0.1", widget.proxyServer.port);
|
||||
Platform.isAndroid ? await localIp() : "127.0.0.1", widget.proxyServer.port,
|
||||
appList: widget.proxyServer.configuration.appWhitelist,
|
||||
disallowApps: widget.proxyServer.configuration.appBlacklist);
|
||||
},
|
||||
icon: const Icon(Icons.picture_in_picture_alt))),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user