Fix android system dns issues
Optimize dns default option Fix some issues
This commit is contained in:
@@ -64,6 +64,14 @@
|
||||
|
||||
<data android:host="install-config" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<action android:name="com.follow.clash.action.START" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<action android:name="com.follow.clash.action.STOP" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- <meta-data-->
|
||||
|
||||
@@ -33,7 +33,7 @@ object GlobalState {
|
||||
return currentEngine?.plugins?.get(AppPlugin::class.java) as AppPlugin?
|
||||
}
|
||||
|
||||
fun getCurrentTitlePlugin(): TilePlugin? {
|
||||
fun getCurrentTilePlugin(): TilePlugin? {
|
||||
val currentEngine = if (flutterEngine != null) flutterEngine else serviceEngine
|
||||
return currentEngine?.plugins?.get(TilePlugin::class.java) as TilePlugin?
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.follow.clash
|
||||
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.follow.clash.plugins.AppPlugin
|
||||
import com.follow.clash.plugins.ServicePlugin
|
||||
import com.follow.clash.plugins.VpnPlugin
|
||||
@@ -9,6 +11,18 @@ import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
|
||||
class MainActivity : FlutterActivity() {
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
when (intent.action) {
|
||||
"com.follow.clash.action.START" -> {
|
||||
GlobalState.getCurrentTilePlugin()?.handleStart()
|
||||
}
|
||||
|
||||
"com.follow.clash.action.STOP" -> {
|
||||
GlobalState.getCurrentTilePlugin()?.handleStop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
|
||||
@@ -34,9 +34,9 @@ fun Metadata.getProtocol(): Int? {
|
||||
}
|
||||
|
||||
|
||||
fun ConnectivityManager.resolvePrimaryDns(network: Network?): String? {
|
||||
val properties = getLinkProperties(network) ?: return null
|
||||
return properties.dnsServers.firstOrNull()?.asSocketAddressText(53)
|
||||
fun ConnectivityManager.resolveDns(network: Network?): List<String> {
|
||||
val properties = getLinkProperties(network) ?: return listOf()
|
||||
return properties.dnsServers.map { it.asSocketAddressText(53) }
|
||||
}
|
||||
|
||||
fun InetAddress.asSocketAddressText(port: Int): String {
|
||||
|
||||
@@ -16,7 +16,7 @@ import com.follow.clash.BaseServiceInterface
|
||||
import com.follow.clash.GlobalState
|
||||
import com.follow.clash.RunState
|
||||
import com.follow.clash.extensions.getProtocol
|
||||
import com.follow.clash.extensions.resolvePrimaryDns
|
||||
import com.follow.clash.extensions.resolveDns
|
||||
import com.follow.clash.models.Props
|
||||
import com.follow.clash.models.TunProps
|
||||
import com.follow.clash.services.FlClashService
|
||||
@@ -177,9 +177,11 @@ class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
|
||||
val networks = mutableSetOf<Network>()
|
||||
|
||||
fun onUpdateNetwork() {
|
||||
val dns = networks.mapNotNull {
|
||||
connectivity?.resolvePrimaryDns(it)
|
||||
}.joinToString(separator = ",")
|
||||
val dns = networks.flatMap { network ->
|
||||
connectivity?.resolveDns(network) ?: emptyList()
|
||||
}
|
||||
.toSet()
|
||||
.joinToString(",")
|
||||
scope.launch {
|
||||
withContext(Dispatchers.Main) {
|
||||
flutterMethodChannel.invokeMethod("dnsChanged", dns)
|
||||
|
||||
@@ -67,15 +67,15 @@ class FlClashTileService : TileService() {
|
||||
activityTransfer()
|
||||
if (GlobalState.runState.value == RunState.STOP) {
|
||||
GlobalState.runState.value = RunState.PENDING
|
||||
val titlePlugin = GlobalState.getCurrentTitlePlugin()
|
||||
if (titlePlugin != null) {
|
||||
titlePlugin.handleStart()
|
||||
val tilePlugin = GlobalState.getCurrentTilePlugin()
|
||||
if (tilePlugin != null) {
|
||||
tilePlugin.handleStart()
|
||||
} else {
|
||||
GlobalState.initServiceEngine(applicationContext)
|
||||
}
|
||||
} else if (GlobalState.runState.value == RunState.START) {
|
||||
GlobalState.runState.value = RunState.PENDING
|
||||
GlobalState.getCurrentTitlePlugin()?.handleStop()
|
||||
GlobalState.getCurrentTilePlugin()?.handleStop()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -159,6 +159,22 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
}
|
||||
|
||||
val stopPendingIntent = if (Build.VERSION.SDK_INT >= 31) {
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
Intent("com.follow.clash.action.STOP"),
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
} else {
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
Intent("com.follow.clash.action.STOP"),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
}
|
||||
with(NotificationCompat.Builder(this, CHANNEL)) {
|
||||
setSmallIcon(R.drawable.ic_stat_name)
|
||||
setContentTitle("FlClash")
|
||||
@@ -172,6 +188,7 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
setShowWhen(false)
|
||||
setOnlyAlertOnce(true)
|
||||
setAutoCancel(true)
|
||||
addAction(0, "Stop", stopPendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +227,7 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
val isSuccess = super.onTransact(code, data, reply, flags)
|
||||
if (!isSuccess) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
GlobalState.getCurrentTitlePlugin()?.handleStop()
|
||||
GlobalState.getCurrentTilePlugin()?.handleStop()
|
||||
}
|
||||
}
|
||||
return isSuccess
|
||||
|
||||
@@ -47,7 +47,7 @@ func Start(tunProps Props) (*sing_tun.Listener, error) {
|
||||
options := LC.Tun{
|
||||
Enable: true,
|
||||
Device: sing_tun.InterfaceName,
|
||||
Stack: constant.TunSystem,
|
||||
Stack: constant.TunMixed,
|
||||
DNSHijack: dnsHijack,
|
||||
AutoRoute: false,
|
||||
AutoDetectInterface: false,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:fl_clash/l10n/l10n.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
@@ -29,6 +27,9 @@ runAppWithPreferences(
|
||||
ChangeNotifierProvider<Config>(
|
||||
create: (_) => config,
|
||||
),
|
||||
ChangeNotifierProvider<AppFlowingState>(
|
||||
create: (_) => AppFlowingState(),
|
||||
),
|
||||
ChangeNotifierProxyProvider2<Config, ClashConfig, AppState>(
|
||||
create: (_) => appState,
|
||||
update: (_, config, clashConfig, appState) {
|
||||
@@ -85,6 +86,7 @@ class ApplicationState extends State<Application> {
|
||||
super.initState();
|
||||
_initTimer();
|
||||
globalState.appController = AppController(context);
|
||||
globalState.measure = Measure.of(context);
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
final currentContext = globalState.navigatorKey.currentContext;
|
||||
if (currentContext != null) {
|
||||
@@ -179,8 +181,15 @@ class ApplicationState extends State<Application> {
|
||||
GlobalWidgetsLocalizations.delegate
|
||||
],
|
||||
builder: (_, child) {
|
||||
return MediaManager(
|
||||
child: _buildPage(child!),
|
||||
return LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
final appController = globalState.appController;
|
||||
final maxWidth = container.maxWidth;
|
||||
if (appController.appState.viewWidth != maxWidth) {
|
||||
globalState.appController.updateViewWidth(maxWidth);
|
||||
}
|
||||
return _buildPage(child!);
|
||||
},
|
||||
);
|
||||
},
|
||||
scrollBehavior: BaseScrollBehavior(),
|
||||
|
||||
@@ -11,7 +11,7 @@ extension BuildContextExtension on BuildContext {
|
||||
return MediaQuery.of(this).size;
|
||||
}
|
||||
|
||||
double get width {
|
||||
double get viewWidth {
|
||||
return appSize.width;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,13 @@ class FlClashHttpOverrides extends HttpOverrides {
|
||||
client.badCertificateCallback = (_, __, ___) => true;
|
||||
client.findProxy = (url) {
|
||||
debugPrint("find $url");
|
||||
final port = globalState.appController.clashConfig.mixedPort;
|
||||
final isStart = globalState.appController.appState.isStart;
|
||||
final appController = globalState.appController;
|
||||
final port = appController.clashConfig.mixedPort;
|
||||
final isStart = appController.appFlowingState.isStart;
|
||||
if (!isStart) return "DIRECT";
|
||||
if (appController.appState.groups.isEmpty) {
|
||||
return "DIRECT";
|
||||
}
|
||||
return "PROXY localhost:$port";
|
||||
};
|
||||
return client;
|
||||
|
||||
@@ -62,6 +62,6 @@ extension DoubleListExt on List<double> {
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // 这行理论上不会执行到,但为了完整性保留
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ class Measure {
|
||||
final TextScaler _textScale;
|
||||
late BuildContext context;
|
||||
|
||||
Measure.of(this.context) : _textScale = MediaQuery.of(context).textScaler;
|
||||
Measure.of(this.context)
|
||||
: _textScale = TextScaler.linear(
|
||||
WidgetsBinding.instance.platformDispatcher.textScaleFactor);
|
||||
|
||||
Size computeTextSize(Text text) {
|
||||
final textPainter = TextPainter(
|
||||
|
||||
@@ -220,6 +220,12 @@ class Other {
|
||||
String getBackupFileName() {
|
||||
return "${appName}_backup_${DateTime.now().show}.zip";
|
||||
}
|
||||
|
||||
Size getScreenSize() {
|
||||
final view = WidgetsBinding.instance.platformDispatcher.views.first;
|
||||
return view.physicalSize / view.devicePixelRatio;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final other = Other();
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'dart:io';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'window.dart';
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import 'common/common.dart';
|
||||
class AppController {
|
||||
final BuildContext context;
|
||||
late AppState appState;
|
||||
late AppFlowingState appFlowingState;
|
||||
late Config config;
|
||||
late ClashConfig clashConfig;
|
||||
late Function updateClashConfigDebounce;
|
||||
@@ -30,6 +31,7 @@ class AppController {
|
||||
appState = context.read<AppState>();
|
||||
config = context.read<Config>();
|
||||
clashConfig = context.read<ClashConfig>();
|
||||
appFlowingState = context.read<AppFlowingState>();
|
||||
updateClashConfigDebounce = debounce<Function()>(() async {
|
||||
await updateClashConfig();
|
||||
});
|
||||
@@ -60,9 +62,9 @@ class AppController {
|
||||
} else {
|
||||
await globalState.handleStop();
|
||||
clashCore.resetTraffic();
|
||||
appState.traffics = [];
|
||||
appState.totalTraffic = Traffic();
|
||||
appState.runTime = null;
|
||||
appFlowingState.traffics = [];
|
||||
appFlowingState.totalTraffic = Traffic();
|
||||
appFlowingState.runTime = null;
|
||||
addCheckIpNumDebounce();
|
||||
}
|
||||
}
|
||||
@@ -76,15 +78,15 @@ class AppController {
|
||||
if (startTime != null) {
|
||||
final startTimeStamp = startTime.millisecondsSinceEpoch;
|
||||
final nowTimeStamp = DateTime.now().millisecondsSinceEpoch;
|
||||
appState.runTime = nowTimeStamp - startTimeStamp;
|
||||
appFlowingState.runTime = nowTimeStamp - startTimeStamp;
|
||||
} else {
|
||||
appState.runTime = null;
|
||||
appFlowingState.runTime = null;
|
||||
}
|
||||
}
|
||||
|
||||
updateTraffic() {
|
||||
globalState.updateTraffic(
|
||||
appState: appState,
|
||||
appFlowingState: appFlowingState,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -163,7 +165,7 @@ class AppController {
|
||||
try {
|
||||
updateProfile(profile);
|
||||
} catch (e) {
|
||||
appState.addLog(
|
||||
appFlowingState.addLog(
|
||||
Log(
|
||||
logLevel: LogLevel.info,
|
||||
payload: e.toString(),
|
||||
@@ -241,7 +243,7 @@ class AppController {
|
||||
clashCore.startLog();
|
||||
} else {
|
||||
clashCore.stopLog();
|
||||
appState.logs = [];
|
||||
appFlowingState.logs = [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,7 +548,7 @@ class AppController {
|
||||
}
|
||||
|
||||
updateStart() {
|
||||
updateStatus(!appState.isStart);
|
||||
updateStatus(!appFlowingState.isStart);
|
||||
}
|
||||
|
||||
updateAutoLaunch() {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/models/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fl_clash/common/app_localizations.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
@@ -18,7 +17,15 @@ class OverrideItem extends StatelessWidget {
|
||||
commonScaffoldState?.actions = [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
globalState.appController.clashConfig.dns = const Dns();
|
||||
globalState.showMessage(
|
||||
title: appLocalizations.resetDns,
|
||||
message: TextSpan(
|
||||
text: appLocalizations.dnsResetTip,
|
||||
),
|
||||
onTab: () {
|
||||
globalState.appController.clashConfig.dns = const Dns();
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
},
|
||||
tooltip: appLocalizations.resetDns,
|
||||
icon: const Icon(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
|
||||
@@ -26,9 +26,10 @@ class _NetworkDetectionState extends State<NetworkDetection> {
|
||||
|
||||
_checkIp() async {
|
||||
final appState = globalState.appController.appState;
|
||||
final appFlowingState = globalState.appController.appFlowingState;
|
||||
final isInit = appState.isInit;
|
||||
if (!isInit) return;
|
||||
final isStart = appState.isStart;
|
||||
final isStart = appFlowingState.isStart;
|
||||
if (_preIsStart == false && _preIsStart == isStart) return;
|
||||
networkDetectionState.value = networkDetectionState.value.copyWith(
|
||||
isTesting: true,
|
||||
|
||||
@@ -116,8 +116,8 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
|
||||
label: appLocalizations.networkSpeed,
|
||||
iconData: Icons.speed_sharp,
|
||||
),
|
||||
child: Selector<AppState, List<Traffic>>(
|
||||
selector: (_, appState) => appState.traffics,
|
||||
child: Selector<AppFlowingState, List<Traffic>>(
|
||||
selector: (_, appFlowingState) => appFlowingState.traffics,
|
||||
builder: (_, traffics, __) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
|
||||
@@ -34,7 +34,7 @@ class _StartButtonState extends State<StartButton>
|
||||
|
||||
handleSwitchStart() {
|
||||
final appController = globalState.appController;
|
||||
if (isStart == appController.appState.isStart) {
|
||||
if (isStart == appController.appFlowingState.isStart) {
|
||||
isStart = !isStart;
|
||||
updateController();
|
||||
appController.updateStatus(isStart);
|
||||
@@ -50,8 +50,8 @@ class _StartButtonState extends State<StartButton>
|
||||
}
|
||||
|
||||
Widget _updateControllerContainer(Widget child) {
|
||||
return Selector<AppState, bool>(
|
||||
selector: (_, appState) => appState.isStart,
|
||||
return Selector<AppFlowingState, bool>(
|
||||
selector: (_, appFlowingState) => appFlowingState.isStart,
|
||||
builder: (_, isStart, child) {
|
||||
if (isStart != this.isStart) {
|
||||
this.isStart = isStart;
|
||||
@@ -127,8 +127,8 @@ class _StartButtonState extends State<StartButton>
|
||||
);
|
||||
},
|
||||
child: _updateControllerContainer(
|
||||
Selector<AppState, int?>(
|
||||
selector: (_, appState) => appState.runTime,
|
||||
Selector<AppFlowingState, int?>(
|
||||
selector: (_, appFlowingState) => appFlowingState.runTime,
|
||||
builder: (_, int? value, __) {
|
||||
final text = other.getTimeText(value);
|
||||
return Text(
|
||||
|
||||
@@ -56,8 +56,8 @@ class TrafficUsage extends StatelessWidget {
|
||||
label: appLocalizations.trafficUsage,
|
||||
iconData: Icons.data_saver_off,
|
||||
),
|
||||
child: Selector<AppState, Traffic>(
|
||||
selector: (_, appState) => appState.totalTraffic,
|
||||
child: Selector<AppFlowingState, Traffic>(
|
||||
selector: (_, appFlowingState) => appFlowingState.totalTraffic,
|
||||
builder: (_, totalTraffic, __) {
|
||||
final upTotalTrafficValue = totalTraffic.up;
|
||||
final downTotalTrafficValue = totalTraffic.down;
|
||||
|
||||
@@ -29,14 +29,14 @@ class _LogsFragmentState extends State<LogsFragment> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final appState = globalState.appController.appState;
|
||||
logsNotifier.value = logsNotifier.value.copyWith(logs: appState.logs);
|
||||
final appFlowingState = globalState.appController.appFlowingState;
|
||||
logsNotifier.value = logsNotifier.value.copyWith(logs: appFlowingState.logs);
|
||||
if (timer != null) {
|
||||
timer?.cancel();
|
||||
timer = null;
|
||||
}
|
||||
timer = Timer.periodic(const Duration(milliseconds: 200), (timer) {
|
||||
final logs = appState.logs;
|
||||
final logs = appFlowingState.logs;
|
||||
if (!const ListEquality<Log>().equals(
|
||||
logsNotifier.value.logs,
|
||||
logs,
|
||||
|
||||
@@ -133,7 +133,7 @@ class ProxyCard extends StatelessWidget {
|
||||
final measure = globalState.measure;
|
||||
final delayText = _buildDelayText();
|
||||
final proxyNameText = _buildProxyNameText(context);
|
||||
return currentGroupProxyNameBuilder(
|
||||
return currentSelectedProxyNameBuilder(
|
||||
groupName: groupName,
|
||||
builder: (currentGroupName) {
|
||||
return Stack(
|
||||
@@ -214,23 +214,9 @@ class ProxyCard extends StatelessWidget {
|
||||
config.currentSelectedMap[groupName];
|
||||
return selectedProxyName ?? '';
|
||||
},
|
||||
builder: (_, value, __) {
|
||||
builder: (_, value, child) {
|
||||
if (value != proxy.name) return Container();
|
||||
return Positioned.fill(
|
||||
child: Container(
|
||||
alignment: Alignment.topRight,
|
||||
margin: const EdgeInsets.all(8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color:
|
||||
Theme.of(context).colorScheme.secondaryContainer,
|
||||
),
|
||||
child: const SelectIcon(),
|
||||
),
|
||||
),
|
||||
);
|
||||
return child!;
|
||||
},
|
||||
child: Positioned.fill(
|
||||
child: Container(
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
Widget currentGroupProxyNameBuilder({
|
||||
Widget currentSelectedProxyNameBuilder({
|
||||
required String groupName,
|
||||
required Widget Function(String currentGroupName) builder,
|
||||
}) {
|
||||
@@ -16,8 +16,8 @@ Widget currentGroupProxyNameBuilder({
|
||||
final selectedProxyName = config.currentSelectedMap[groupName];
|
||||
return group?.getCurrentSelectedName(selectedProxyName ?? "") ?? "";
|
||||
},
|
||||
builder: (_, currentGroupName, ___) {
|
||||
return builder(currentGroupName);
|
||||
builder: (_, currentSelectedProxyName, ___) {
|
||||
return builder(currentSelectedProxyName);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ class _ListHeaderState extends State<ListHeader>
|
||||
),
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: currentGroupProxyNameBuilder(
|
||||
child: currentSelectedProxyNameBuilder(
|
||||
groupName: groupName,
|
||||
builder: (currentGroupName) {
|
||||
return Row(
|
||||
|
||||
@@ -321,7 +321,7 @@ class ProxyGroupViewState extends State<ProxyGroupView> {
|
||||
top: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 80,
|
||||
bottom: 96,
|
||||
),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: columns,
|
||||
|
||||
@@ -303,5 +303,6 @@
|
||||
"inputCorrectHotkey": "Please enter the correct hotkey",
|
||||
"hotkeyConflict": "Hotkey conflict",
|
||||
"remove": "Remove",
|
||||
"noHotKey": "No HotKey"
|
||||
"noHotKey": "No HotKey",
|
||||
"dnsResetTip": "Make sure to reset the DNS"
|
||||
}
|
||||
@@ -303,5 +303,6 @@
|
||||
"inputCorrectHotkey": "请输入正确的快捷键",
|
||||
"hotkeyConflict": "快捷键冲突",
|
||||
"remove": "移除",
|
||||
"noHotKey": "暂无快捷键"
|
||||
"noHotKey": "暂无快捷键",
|
||||
"dnsResetTip": "确定重置DNS"
|
||||
}
|
||||
@@ -146,6 +146,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"dnsDesc":
|
||||
MessageLookupByLibrary.simpleMessage("Update DNS related settings"),
|
||||
"dnsMode": MessageLookupByLibrary.simpleMessage("DNS mode"),
|
||||
"dnsResetTip":
|
||||
MessageLookupByLibrary.simpleMessage("Make sure to reset the DNS"),
|
||||
"doYouWantToPass":
|
||||
MessageLookupByLibrary.simpleMessage("Do you want to pass"),
|
||||
"domain": MessageLookupByLibrary.simpleMessage("Domain"),
|
||||
|
||||
@@ -120,6 +120,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"discovery": MessageLookupByLibrary.simpleMessage("发现新版本"),
|
||||
"dnsDesc": MessageLookupByLibrary.simpleMessage("更新DNS相关设置"),
|
||||
"dnsMode": MessageLookupByLibrary.simpleMessage("DNS模式"),
|
||||
"dnsResetTip": MessageLookupByLibrary.simpleMessage("确定重置DNS"),
|
||||
"doYouWantToPass": MessageLookupByLibrary.simpleMessage("是否要通过"),
|
||||
"domain": MessageLookupByLibrary.simpleMessage("域名"),
|
||||
"download": MessageLookupByLibrary.simpleMessage("下载"),
|
||||
|
||||
@@ -3099,6 +3099,16 @@ class AppLocalizations {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Make sure to reset the DNS`
|
||||
String get dnsResetTip {
|
||||
return Intl.message(
|
||||
'Make sure to reset the DNS',
|
||||
name: 'dnsResetTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/http.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/plugins/tile.dart';
|
||||
import 'package:fl_clash/plugins/vpn.dart';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@@ -137,7 +137,7 @@ class _ClashContainerState extends State<ClashManager>
|
||||
|
||||
@override
|
||||
void onLog(Log log) {
|
||||
globalState.appController.appState.addLog(log);
|
||||
globalState.appController.appFlowingState.addLog(log);
|
||||
if (log.logLevel == LogLevel.error) {
|
||||
globalState.appController.showSnackBar(log.payload ?? '');
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ class ProxyManager extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector3<AppState, Config, ClashConfig, ProxyState>(
|
||||
selector: (_, appState, config, clashConfig) => ProxyState(
|
||||
isStart: appState.isStart,
|
||||
return Selector3<AppFlowingState, Config, ClashConfig, ProxyState>(
|
||||
selector: (_, appFlowingState, config, clashConfig) => ProxyState(
|
||||
isStart: appFlowingState.isStart,
|
||||
systemProxy: config.desktopProps.systemProxy,
|
||||
port: clashConfig.mixedPort,
|
||||
),
|
||||
|
||||
@@ -42,7 +42,7 @@ class _TrayContainerState extends State<TrayManager> with TrayListener {
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness,
|
||||
),
|
||||
);
|
||||
if(!Platform.isLinux){
|
||||
if (!Platform.isLinux) {
|
||||
await trayManager.setToolTip(
|
||||
appName,
|
||||
);
|
||||
@@ -138,11 +138,12 @@ class _TrayContainerState extends State<TrayManager> with TrayListener {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector3<AppState, Config, ClashConfig, TrayState>(
|
||||
selector: (_, appState, config, clashConfig) => TrayState(
|
||||
return Selector4<AppState, AppFlowingState, Config, ClashConfig, TrayState>(
|
||||
selector: (_, appState, appFlowingState, config, clashConfig) =>
|
||||
TrayState(
|
||||
mode: clashConfig.mode,
|
||||
autoLaunch: config.autoLaunch,
|
||||
isStart: appState.isStart,
|
||||
isStart: appFlowingState.isStart,
|
||||
locale: config.locale,
|
||||
systemProxy: config.desktopProps.systemProxy,
|
||||
tunEnable: clashConfig.tun.enable,
|
||||
|
||||
@@ -24,8 +24,8 @@ class _VpnContainerState extends State<VpnManager> {
|
||||
showTip() {
|
||||
vpnTipDebounce ??= debounce<Function()>(() async {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final appState = globalState.appController.appState;
|
||||
if (appState.isStart) {
|
||||
final appFlowingState = globalState.appController.appFlowingState;
|
||||
if (appFlowingState.isStart) {
|
||||
globalState.showSnackBar(
|
||||
context,
|
||||
message: appLocalizations.vpnTip,
|
||||
|
||||
@@ -12,12 +12,8 @@ typedef DelayMap = Map<String, int?>;
|
||||
|
||||
class AppState with ChangeNotifier {
|
||||
List<NavigationItem> _navigationItems;
|
||||
int? _runTime;
|
||||
bool _isInit;
|
||||
VersionInfo? _versionInfo;
|
||||
List<Traffic> _traffics;
|
||||
Traffic _totalTraffic;
|
||||
List<Log> _logs;
|
||||
String _currentLabel;
|
||||
SystemColorSchemes _systemColorSchemes;
|
||||
num _sortNum;
|
||||
@@ -42,16 +38,13 @@ class AppState with ChangeNotifier {
|
||||
}) : _navigationItems = [],
|
||||
_isInit = false,
|
||||
_currentLabel = "dashboard",
|
||||
_traffics = [],
|
||||
_logs = [],
|
||||
_viewWidth = 0,
|
||||
_viewWidth = other.getScreenSize().width,
|
||||
_selectedMap = selectedMap,
|
||||
_sortNum = 0,
|
||||
_checkIpNum = 0,
|
||||
_requests = [],
|
||||
_mode = mode,
|
||||
_brightness = null,
|
||||
_totalTraffic = Traffic(),
|
||||
_delayMap = {},
|
||||
_groups = [],
|
||||
_providers = [],
|
||||
@@ -101,17 +94,6 @@ class AppState with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
bool get isStart => _runTime != null;
|
||||
|
||||
int? get runTime => _runTime;
|
||||
|
||||
set runTime(int? value) {
|
||||
if (_runTime != value) {
|
||||
_runTime = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
String getDesc(String type, String proxyName) {
|
||||
final groupTypeNamesList = GroupType.values.map((e) => e.name).toList();
|
||||
if (!groupTypeNamesList.contains(type)) {
|
||||
@@ -158,33 +140,6 @@ class AppState with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
List<Traffic> get traffics => _traffics;
|
||||
|
||||
set traffics(List<Traffic> value) {
|
||||
if (_traffics != value) {
|
||||
_traffics = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
addTraffic(Traffic traffic) {
|
||||
_traffics = List.from(_traffics)..add(traffic);
|
||||
const maxLength = 60;
|
||||
if (_traffics.length > maxLength) {
|
||||
_traffics = _traffics.sublist(_traffics.length - maxLength);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Traffic get totalTraffic => _totalTraffic;
|
||||
|
||||
set totalTraffic(Traffic value) {
|
||||
if (_totalTraffic != value) {
|
||||
_totalTraffic = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
List<Connection> get requests => _requests;
|
||||
|
||||
set requests(List<Connection> value) {
|
||||
@@ -203,24 +158,6 @@ class AppState with ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<Log> get logs => _logs;
|
||||
|
||||
set logs(List<Log> value) {
|
||||
if (_logs != value) {
|
||||
_logs = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
addLog(Log log) {
|
||||
_logs = List.from(_logs)..add(log);
|
||||
final maxLength = Platform.isAndroid ? 1000 : 60;
|
||||
if (_logs.length > maxLength) {
|
||||
_logs = _logs.sublist(_logs.length - maxLength);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
SystemColorSchemes get systemColorSchemes => _systemColorSchemes;
|
||||
|
||||
set systemColorSchemes(SystemColorSchemes value) {
|
||||
@@ -383,3 +320,71 @@ class AppState with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AppFlowingState with ChangeNotifier {
|
||||
int? _runTime;
|
||||
List<Log> _logs;
|
||||
List<Traffic> _traffics;
|
||||
Traffic _totalTraffic;
|
||||
|
||||
AppFlowingState()
|
||||
: _logs = [],
|
||||
_traffics = [],
|
||||
_totalTraffic = Traffic();
|
||||
|
||||
bool get isStart => _runTime != null;
|
||||
|
||||
int? get runTime => _runTime;
|
||||
|
||||
set runTime(int? value) {
|
||||
if (_runTime != value) {
|
||||
_runTime = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
List<Log> get logs => _logs;
|
||||
|
||||
set logs(List<Log> value) {
|
||||
if (_logs != value) {
|
||||
_logs = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
addLog(Log log) {
|
||||
_logs = List.from(_logs)..add(log);
|
||||
final maxLength = Platform.isAndroid ? 1000 : 60;
|
||||
if (_logs.length > maxLength) {
|
||||
_logs = _logs.sublist(_logs.length - maxLength);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<Traffic> get traffics => _traffics;
|
||||
|
||||
set traffics(List<Traffic> value) {
|
||||
if (_traffics != value) {
|
||||
_traffics = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
addTraffic(Traffic traffic) {
|
||||
_traffics = List.from(_traffics)..add(traffic);
|
||||
const maxLength = 60;
|
||||
if (_traffics.length > maxLength) {
|
||||
_traffics = _traffics.sublist(_traffics.length - maxLength);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Traffic get totalTraffic => _totalTraffic;
|
||||
|
||||
set totalTraffic(Traffic value) {
|
||||
if (_totalTraffic != value) {
|
||||
_totalTraffic = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class Dns with _$Dns {
|
||||
@Default(false) @JsonKey(name: "prefer-h3") bool preferH3,
|
||||
@Default(true) @JsonKey(name: "use-hosts") bool useHosts,
|
||||
@Default(true) @JsonKey(name: "use-system-hosts") bool useSystemHosts,
|
||||
@Default(true) @JsonKey(name: "respect-rules") bool respectRules,
|
||||
@Default(false) @JsonKey(name: "respect-rules") bool respectRules,
|
||||
@Default(false) bool ipv6,
|
||||
@Default(["223.5.5.5"])
|
||||
@JsonKey(name: "default-nameserver")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
@@ -772,7 +772,7 @@ class _$DnsImpl implements _Dns {
|
||||
@JsonKey(name: "prefer-h3") this.preferH3 = false,
|
||||
@JsonKey(name: "use-hosts") this.useHosts = true,
|
||||
@JsonKey(name: "use-system-hosts") this.useSystemHosts = true,
|
||||
@JsonKey(name: "respect-rules") this.respectRules = true,
|
||||
@JsonKey(name: "respect-rules") this.respectRules = false,
|
||||
this.ipv6 = false,
|
||||
@JsonKey(name: "default-nameserver")
|
||||
final List<String> defaultNameserver = const ["223.5.5.5"],
|
||||
|
||||
@@ -141,7 +141,7 @@ _$DnsImpl _$$DnsImplFromJson(Map<String, dynamic> json) => _$DnsImpl(
|
||||
preferH3: json['prefer-h3'] as bool? ?? false,
|
||||
useHosts: json['use-hosts'] as bool? ?? true,
|
||||
useSystemHosts: json['use-system-hosts'] as bool? ?? true,
|
||||
respectRules: json['respect-rules'] as bool? ?? true,
|
||||
respectRules: json['respect-rules'] as bool? ?? false,
|
||||
ipv6: json['ipv6'] as bool? ?? false,
|
||||
defaultNameserver: (json['default-nameserver'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
@@ -155,56 +154,47 @@ class HomePage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BackScope(
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
final appController = globalState.appController;
|
||||
final maxWidth = container.maxWidth;
|
||||
if (appController.appState.viewWidth != maxWidth) {
|
||||
globalState.appController.updateViewWidth(maxWidth);
|
||||
}
|
||||
return Selector2<AppState, Config, HomeState>(
|
||||
selector: (_, appState, config) {
|
||||
return HomeState(
|
||||
currentLabel: appState.currentLabel,
|
||||
navigationItems: appState.currentNavigationItems,
|
||||
viewMode: other.getViewMode(maxWidth),
|
||||
locale: config.locale,
|
||||
);
|
||||
},
|
||||
shouldRebuild: (prev, next) {
|
||||
return prev != next;
|
||||
},
|
||||
builder: (_, state, child) {
|
||||
final viewMode = state.viewMode;
|
||||
final navigationItems = state.navigationItems;
|
||||
final currentLabel = state.currentLabel;
|
||||
final index = navigationItems.lastIndexWhere(
|
||||
(element) => element.label == currentLabel,
|
||||
);
|
||||
final currentIndex = index == -1 ? 0 : index;
|
||||
final navigationBar = _getNavigationBar(
|
||||
context: context,
|
||||
viewMode: viewMode,
|
||||
navigationItems: navigationItems,
|
||||
currentIndex: currentIndex,
|
||||
);
|
||||
final bottomNavigationBar =
|
||||
viewMode == ViewMode.mobile ? navigationBar : null;
|
||||
final sideNavigationBar =
|
||||
viewMode != ViewMode.mobile ? navigationBar : null;
|
||||
return CommonScaffold(
|
||||
key: globalState.homeScaffoldKey,
|
||||
title: Intl.message(
|
||||
currentLabel,
|
||||
),
|
||||
sideNavigationBar: sideNavigationBar,
|
||||
body: child!,
|
||||
bottomNavigationBar: bottomNavigationBar,
|
||||
);
|
||||
},
|
||||
child: _buildPageView(),
|
||||
child: Selector2<AppState, Config, HomeState>(
|
||||
selector: (_, appState, config) {
|
||||
return HomeState(
|
||||
currentLabel: appState.currentLabel,
|
||||
navigationItems: appState.currentNavigationItems,
|
||||
viewMode: appState.viewMode,
|
||||
locale: config.locale,
|
||||
);
|
||||
},
|
||||
shouldRebuild: (prev, next) {
|
||||
return prev != next;
|
||||
},
|
||||
builder: (_, state, child) {
|
||||
final viewMode = state.viewMode;
|
||||
final navigationItems = state.navigationItems;
|
||||
final currentLabel = state.currentLabel;
|
||||
final index = navigationItems.lastIndexWhere(
|
||||
(element) => element.label == currentLabel,
|
||||
);
|
||||
final currentIndex = index == -1 ? 0 : index;
|
||||
final navigationBar = _getNavigationBar(
|
||||
context: context,
|
||||
viewMode: viewMode,
|
||||
navigationItems: navigationItems,
|
||||
currentIndex: currentIndex,
|
||||
);
|
||||
final bottomNavigationBar =
|
||||
viewMode == ViewMode.mobile ? navigationBar : null;
|
||||
final sideNavigationBar =
|
||||
viewMode != ViewMode.mobile ? navigationBar : null;
|
||||
return CommonScaffold(
|
||||
key: globalState.homeScaffoldKey,
|
||||
title: Intl.message(
|
||||
currentLabel,
|
||||
),
|
||||
sideNavigationBar: sideNavigationBar,
|
||||
body: child!,
|
||||
bottomNavigationBar: bottomNavigationBar,
|
||||
);
|
||||
},
|
||||
child: _buildPageView(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class Service {
|
||||
@@ -28,4 +27,4 @@ class Service {
|
||||
}
|
||||
|
||||
final service =
|
||||
Platform.isAndroid && !globalState.isVpnService ? Service() : null;
|
||||
Platform.isAndroid ? Service() : null;
|
||||
|
||||
@@ -6,8 +6,6 @@ import 'dart:isolate';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class Vpn {
|
||||
@@ -105,4 +103,4 @@ class Vpn {
|
||||
}
|
||||
}
|
||||
|
||||
final vpn = Platform.isAndroid && globalState.isVpnService ? Vpn() : null;
|
||||
final vpn = Platform.isAndroid ? Vpn() : null;
|
||||
|
||||
@@ -76,7 +76,7 @@ class GlobalState {
|
||||
required ClashConfig clashConfig,
|
||||
}) async {
|
||||
clashCore.start();
|
||||
if (vpn != null) {
|
||||
if (globalState.isVpnService) {
|
||||
await vpn?.startVpn(clashConfig.mixedPort);
|
||||
startListenUpdate();
|
||||
return;
|
||||
@@ -222,7 +222,7 @@ class GlobalState {
|
||||
}
|
||||
|
||||
updateTraffic({
|
||||
AppState? appState,
|
||||
AppFlowingState? appFlowingState,
|
||||
}) {
|
||||
final traffic = clashCore.getTraffic();
|
||||
if (Platform.isAndroid && isVpnService == true) {
|
||||
@@ -231,9 +231,9 @@ class GlobalState {
|
||||
content: "$traffic",
|
||||
);
|
||||
} else {
|
||||
if (appState != null) {
|
||||
appState.addTraffic(traffic);
|
||||
appState.totalTraffic = clashCore.getTotalTraffic();
|
||||
if (appFlowingState != null) {
|
||||
appFlowingState.addTraffic(traffic);
|
||||
appFlowingState.totalTraffic = clashCore.getTotalTraffic();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,7 @@ class GlobalState {
|
||||
required String message,
|
||||
SnackBarAction? action,
|
||||
}) {
|
||||
final width = context.width;
|
||||
final width = context.viewWidth;
|
||||
EdgeInsets margin;
|
||||
if (width < 600) {
|
||||
margin = const EdgeInsets.only(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'text.dart';
|
||||
|
||||
@@ -462,7 +462,6 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
|
||||
);
|
||||
TweenSequence<Color?>? colorTween;
|
||||
TweenSequence<double>? closedOpacityTween, openOpacityTween;
|
||||
Animatable<Color?>? scrimTween;
|
||||
switch (animation.status) {
|
||||
case AnimationStatus.dismissed:
|
||||
case AnimationStatus.forward:
|
||||
|
||||
@@ -53,7 +53,7 @@ showExtendPage(
|
||||
body: uniqueBody,
|
||||
);
|
||||
return SizedBox(
|
||||
width: isMobile ? context.width : extendPageWidth ?? 300,
|
||||
width: isMobile ? context.viewWidth : extendPageWidth ?? 300,
|
||||
child: commonScaffold,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: fl_clash
|
||||
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
||||
publish_to: 'none'
|
||||
version: 0.8.60+202409171
|
||||
version: 0.8.61+202409201
|
||||
environment:
|
||||
sdk: '>=3.1.0 <4.0.0'
|
||||
flutter: 3.22.0
|
||||
|
||||
Reference in New Issue
Block a user