Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a2ad63f38 | ||
|
|
2ec12c9363 |
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@@ -17,10 +17,16 @@ jobs:
|
||||
os: windows-latest
|
||||
- platform: linux
|
||||
os: ubuntu-latest
|
||||
# - platform: macos
|
||||
# os: macos-13
|
||||
- platform: macos
|
||||
os: macos-13
|
||||
|
||||
steps:
|
||||
- name: Check Matrix
|
||||
run: |
|
||||
echo "Running on ${{ matrix.os }}"
|
||||
echo "Arch: ${{ runner.arch }}"
|
||||
gcc --version
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -5,4 +5,4 @@
|
||||
[submodule "plugins/flutter_distributor"]
|
||||
path = plugins/flutter_distributor
|
||||
url = git@github.com:chen08209/flutter_distributor.git
|
||||
branch = main
|
||||
branch = FlClash
|
||||
|
||||
@@ -10,3 +10,8 @@ data class AccessControl(
|
||||
val acceptList: List<String>,
|
||||
val rejectList: List<String>,
|
||||
)
|
||||
|
||||
data class Props (
|
||||
val accessControl: AccessControl?,
|
||||
val allowBypass: Boolean?,
|
||||
)
|
||||
@@ -17,6 +17,7 @@ import androidx.core.content.ContextCompat
|
||||
import com.follow.clash.GlobalState
|
||||
import com.follow.clash.RunState
|
||||
import com.follow.clash.models.AccessControl
|
||||
import com.follow.clash.models.Props
|
||||
import com.follow.clash.services.FlClashVpnService
|
||||
import com.google.gson.Gson
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
@@ -41,7 +42,7 @@ class ProxyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAwar
|
||||
private var flClashVpnService: FlClashVpnService? = null
|
||||
private var isBound = false
|
||||
private var port: Int? = null
|
||||
private var accessControl: AccessControl? = null
|
||||
private var props: Props? = null
|
||||
private lateinit var title: String
|
||||
private lateinit var content: String
|
||||
|
||||
@@ -73,8 +74,8 @@ class ProxyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAwar
|
||||
"StartProxy" -> {
|
||||
port = call.argument<Int>("port")
|
||||
val args = call.argument<String>("args")
|
||||
accessControl =
|
||||
if (args != null) Gson().fromJson(args, AccessControl::class.java) else null
|
||||
props =
|
||||
if (args != null) Gson().fromJson(args, Props::class.java) else null
|
||||
handleStartVpn()
|
||||
result.success(true)
|
||||
}
|
||||
@@ -121,7 +122,7 @@ class ProxyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAwar
|
||||
|
||||
private fun startVpn(port: Int) {
|
||||
if (GlobalState.runState.value == RunState.START) return;
|
||||
flClashVpnService?.start(port, accessControl)
|
||||
flClashVpnService?.start(port, props)
|
||||
GlobalState.runState.value = RunState.START
|
||||
GlobalState.runTime = Date()
|
||||
startAfter()
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.follow.clash.MainActivity
|
||||
import com.follow.clash.R
|
||||
import com.follow.clash.models.AccessControl
|
||||
import com.follow.clash.models.AccessControlMode
|
||||
import com.follow.clash.models.Props
|
||||
|
||||
|
||||
class FlClashVpnService : VpnService() {
|
||||
@@ -51,12 +52,12 @@ class FlClashVpnService : VpnService() {
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
fun start(port: Int, accessControl: AccessControl?) {
|
||||
fun start(port: Int, props: Props?) {
|
||||
fd = with(Builder()) {
|
||||
addAddress("172.16.0.1", 30)
|
||||
setMtu(9000)
|
||||
addRoute("0.0.0.0", 0)
|
||||
if (accessControl != null) {
|
||||
props?.accessControl?.let { accessControl ->
|
||||
when (accessControl.mode) {
|
||||
AccessControlMode.acceptSelected -> {
|
||||
(accessControl.acceptList + packageName).forEach {
|
||||
@@ -77,7 +78,9 @@ class FlClashVpnService : VpnService() {
|
||||
if (Build.VERSION.SDK_INT >= 29) {
|
||||
setMetered(false)
|
||||
}
|
||||
allowBypass()
|
||||
if (props?.allowBypass == true) {
|
||||
allowBypass()
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
setHttpProxy(
|
||||
ProxyInfo.buildDirectProxy(
|
||||
@@ -144,8 +147,8 @@ class FlClashVpnService : VpnService() {
|
||||
val notification =
|
||||
notificationBuilder.setContentTitle(title).setContentText(content).build()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
|
||||
}else{
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
|
||||
} else {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module core
|
||||
|
||||
go 1.20
|
||||
go 1.21.0
|
||||
|
||||
replace github.com/metacubex/mihomo => ./Clash.Meta
|
||||
|
||||
|
||||
@@ -10,8 +10,7 @@ class Picker {
|
||||
if (Platform.isAndroid) {
|
||||
filePickerResult = await FilePicker.platform.pickFiles(
|
||||
withData: true,
|
||||
type: FileType.custom,
|
||||
allowedExtensions: ['txt', 'conf'],
|
||||
allowMultiple: false,
|
||||
);
|
||||
} else {
|
||||
filePickerResult = await FilePicker.platform.pickFiles(
|
||||
|
||||
@@ -530,12 +530,11 @@ class AccessControlSearchDelegate extends SearchDelegate {
|
||||
|
||||
@override
|
||||
Widget buildResults(BuildContext context) {
|
||||
return _packageList(packages);
|
||||
return buildSuggestions(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildSuggestions(BuildContext context) {
|
||||
final packages = _results;
|
||||
return _packageList(packages);
|
||||
return _packageList(_results);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
@@ -54,7 +56,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
onTab: () {
|
||||
_modifyMixedPort(mixedPort);
|
||||
},
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16,vertical: 4),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
leading: const Icon(Icons.adjust),
|
||||
title: Text(appLocalizations.proxyPort),
|
||||
trailing: FilledButton.tonal(
|
||||
@@ -105,6 +107,24 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
if (Platform.isAndroid)
|
||||
Selector<Config, bool>(
|
||||
selector: (_, config) => config.allowBypass,
|
||||
builder: (_, allowBypass, __) {
|
||||
return ListItem.switchItem(
|
||||
leading: const Icon(Icons.double_arrow),
|
||||
title: Text(appLocalizations.allowBypass),
|
||||
subtitle: Text(appLocalizations.allowBypassDesc),
|
||||
delegate: SwitchDelegate(
|
||||
value: allowBypass,
|
||||
onChanged: (bool value) async {
|
||||
final appController = globalState.appController;
|
||||
appController.config.allowBypass = value;
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Selector<Config, bool>(
|
||||
selector: (_, config) => config.isCompatible,
|
||||
builder: (_, isCompatible, __) {
|
||||
|
||||
@@ -62,7 +62,7 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.download),
|
||||
icon: const Icon(Icons.sync),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
|
||||
@@ -161,5 +161,7 @@
|
||||
"country": "Country",
|
||||
"checkError": "Check error",
|
||||
"ipCheckTimeout": "Ip check timeout",
|
||||
"search": "Search"
|
||||
"search": "Search",
|
||||
"allowBypass": "Allow applications to bypass VPN",
|
||||
"allowBypassDesc": "After opening, some applications can bypass VPN"
|
||||
}
|
||||
@@ -161,5 +161,7 @@
|
||||
"country": "区域",
|
||||
"checkError": "检测失败",
|
||||
"ipCheckTimeout": "Ip检测超时",
|
||||
"search": "搜索"
|
||||
"search": "搜索",
|
||||
"allowBypass": "允许应用绕过vpn",
|
||||
"allowBypassDesc": "开启后部分应用可绕过VPN"
|
||||
}
|
||||
@@ -40,6 +40,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"addressTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Please enter a valid WebDAV address"),
|
||||
"ago": MessageLookupByLibrary.simpleMessage(" Ago"),
|
||||
"allowBypass": MessageLookupByLibrary.simpleMessage(
|
||||
"Allow applications to bypass VPN"),
|
||||
"allowBypassDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"After opening, some applications can bypass VPN"),
|
||||
"allowLan": MessageLookupByLibrary.simpleMessage("AllowLan"),
|
||||
"allowLanDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Allow access proxy through the LAN"),
|
||||
|
||||
@@ -36,6 +36,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"addressHelp": MessageLookupByLibrary.simpleMessage("WebDAV服务器地址"),
|
||||
"addressTip": MessageLookupByLibrary.simpleMessage("请输入有效的WebDAV地址"),
|
||||
"ago": MessageLookupByLibrary.simpleMessage("前"),
|
||||
"allowBypass": MessageLookupByLibrary.simpleMessage("允许应用绕过vpn"),
|
||||
"allowBypassDesc":
|
||||
MessageLookupByLibrary.simpleMessage("开启后部分应用可绕过VPN"),
|
||||
"allowLan": MessageLookupByLibrary.simpleMessage("局域网代理"),
|
||||
"allowLanDesc": MessageLookupByLibrary.simpleMessage("允许通过局域网访问代理"),
|
||||
"appAccessControl": MessageLookupByLibrary.simpleMessage("应用访问控制"),
|
||||
|
||||
@@ -1679,6 +1679,26 @@ class AppLocalizations {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Allow applications to bypass VPN`
|
||||
String get allowBypass {
|
||||
return Intl.message(
|
||||
'Allow applications to bypass VPN',
|
||||
name: 'allowBypass',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `After opening, some applications can bypass VPN`
|
||||
String get allowBypassDesc {
|
||||
return Intl.message(
|
||||
'After opening, some applications can bypass VPN',
|
||||
name: 'allowBypassDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -24,6 +24,17 @@ class AccessControl with _$AccessControl {
|
||||
_$AccessControlFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Props with _$Props {
|
||||
const factory Props({
|
||||
AccessControl? accessControl,
|
||||
bool? allowBypass,
|
||||
}) = _Props;
|
||||
|
||||
factory Props.fromJson(Map<String, Object?> json) =>
|
||||
_$PropsFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class Config extends ChangeNotifier {
|
||||
List<Profile> _profiles;
|
||||
@@ -42,6 +53,7 @@ class Config extends ChangeNotifier {
|
||||
AccessControl _accessControl;
|
||||
bool _isAnimateToPage;
|
||||
bool _autoCheckUpdate;
|
||||
bool _allowBypass;
|
||||
DAV? _dav;
|
||||
|
||||
Config()
|
||||
@@ -58,7 +70,8 @@ class Config extends ChangeNotifier {
|
||||
_isAccessControl = false,
|
||||
_autoCheckUpdate = true,
|
||||
_accessControl = const AccessControl(),
|
||||
_isAnimateToPage = true;
|
||||
_isAnimateToPage = true,
|
||||
_allowBypass = true;
|
||||
|
||||
deleteProfileById(String id) {
|
||||
_profiles = profiles.where((element) => element.id != id).toList();
|
||||
@@ -306,8 +319,22 @@ class Config extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
update(
|
||||
[Config? config, RecoveryOption recoveryOptions = RecoveryOption.all]) {
|
||||
@JsonKey(defaultValue: true)
|
||||
bool get allowBypass {
|
||||
return _allowBypass;
|
||||
}
|
||||
|
||||
set allowBypass(bool value) {
|
||||
if (_allowBypass != value) {
|
||||
_allowBypass = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
update([
|
||||
Config? config,
|
||||
RecoveryOption recoveryOptions = RecoveryOption.all,
|
||||
]) {
|
||||
if (config != null) {
|
||||
_profiles = config._profiles;
|
||||
for (final profile in config._profiles) {
|
||||
@@ -326,6 +353,7 @@ class Config extends ChangeNotifier {
|
||||
_openLog = config._openLog;
|
||||
_themeMode = config._themeMode;
|
||||
_locale = config._locale;
|
||||
_allowBypass = config._allowBypass;
|
||||
_primaryColor = config._primaryColor;
|
||||
_proxiesSortType = config._proxiesSortType;
|
||||
_isMinimizeOnExit = config._isMinimizeOnExit;
|
||||
|
||||
@@ -239,3 +239,172 @@ abstract class _AccessControl implements AccessControl {
|
||||
_$$AccessControlImplCopyWith<_$AccessControlImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
Props _$PropsFromJson(Map<String, dynamic> json) {
|
||||
return _Props.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Props {
|
||||
AccessControl? get accessControl => throw _privateConstructorUsedError;
|
||||
bool? get allowBypass => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$PropsCopyWith<Props> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $PropsCopyWith<$Res> {
|
||||
factory $PropsCopyWith(Props value, $Res Function(Props) then) =
|
||||
_$PropsCopyWithImpl<$Res, Props>;
|
||||
@useResult
|
||||
$Res call({AccessControl? accessControl, bool? allowBypass});
|
||||
|
||||
$AccessControlCopyWith<$Res>? get accessControl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$PropsCopyWithImpl<$Res, $Val extends Props>
|
||||
implements $PropsCopyWith<$Res> {
|
||||
_$PropsCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? accessControl = freezed,
|
||||
Object? allowBypass = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
accessControl: freezed == accessControl
|
||||
? _value.accessControl
|
||||
: accessControl // ignore: cast_nullable_to_non_nullable
|
||||
as AccessControl?,
|
||||
allowBypass: freezed == allowBypass
|
||||
? _value.allowBypass
|
||||
: allowBypass // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$AccessControlCopyWith<$Res>? get accessControl {
|
||||
if (_value.accessControl == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $AccessControlCopyWith<$Res>(_value.accessControl!, (value) {
|
||||
return _then(_value.copyWith(accessControl: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$PropsImplCopyWith<$Res> implements $PropsCopyWith<$Res> {
|
||||
factory _$$PropsImplCopyWith(
|
||||
_$PropsImpl value, $Res Function(_$PropsImpl) then) =
|
||||
__$$PropsImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({AccessControl? accessControl, bool? allowBypass});
|
||||
|
||||
@override
|
||||
$AccessControlCopyWith<$Res>? get accessControl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$PropsImplCopyWithImpl<$Res>
|
||||
extends _$PropsCopyWithImpl<$Res, _$PropsImpl>
|
||||
implements _$$PropsImplCopyWith<$Res> {
|
||||
__$$PropsImplCopyWithImpl(
|
||||
_$PropsImpl _value, $Res Function(_$PropsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? accessControl = freezed,
|
||||
Object? allowBypass = freezed,
|
||||
}) {
|
||||
return _then(_$PropsImpl(
|
||||
accessControl: freezed == accessControl
|
||||
? _value.accessControl
|
||||
: accessControl // ignore: cast_nullable_to_non_nullable
|
||||
as AccessControl?,
|
||||
allowBypass: freezed == allowBypass
|
||||
? _value.allowBypass
|
||||
: allowBypass // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$PropsImpl implements _Props {
|
||||
const _$PropsImpl({this.accessControl, this.allowBypass});
|
||||
|
||||
factory _$PropsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$PropsImplFromJson(json);
|
||||
|
||||
@override
|
||||
final AccessControl? accessControl;
|
||||
@override
|
||||
final bool? allowBypass;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Props(accessControl: $accessControl, allowBypass: $allowBypass)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$PropsImpl &&
|
||||
(identical(other.accessControl, accessControl) ||
|
||||
other.accessControl == accessControl) &&
|
||||
(identical(other.allowBypass, allowBypass) ||
|
||||
other.allowBypass == allowBypass));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, accessControl, allowBypass);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$PropsImplCopyWith<_$PropsImpl> get copyWith =>
|
||||
__$$PropsImplCopyWithImpl<_$PropsImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$PropsImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Props implements Props {
|
||||
const factory _Props(
|
||||
{final AccessControl? accessControl,
|
||||
final bool? allowBypass}) = _$PropsImpl;
|
||||
|
||||
factory _Props.fromJson(Map<String, dynamic> json) = _$PropsImpl.fromJson;
|
||||
|
||||
@override
|
||||
AccessControl? get accessControl;
|
||||
@override
|
||||
bool? get allowBypass;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$PropsImplCopyWith<_$PropsImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
|
||||
: DAV.fromJson(json['dav'] as Map<String, dynamic>)
|
||||
..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true
|
||||
..isCompatible = json['isCompatible'] as bool? ?? true
|
||||
..autoCheckUpdate = json['autoCheckUpdate'] as bool? ?? true;
|
||||
..autoCheckUpdate = json['autoCheckUpdate'] as bool? ?? true
|
||||
..allowBypass = json['allowBypass'] as bool? ?? true;
|
||||
|
||||
Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
|
||||
'profiles': instance.profiles,
|
||||
@@ -52,6 +53,7 @@ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
|
||||
'isAnimateToPage': instance.isAnimateToPage,
|
||||
'isCompatible': instance.isCompatible,
|
||||
'autoCheckUpdate': instance.autoCheckUpdate,
|
||||
'allowBypass': instance.allowBypass,
|
||||
};
|
||||
|
||||
const _$ThemeModeEnumMap = {
|
||||
@@ -93,3 +95,17 @@ const _$AccessControlModeEnumMap = {
|
||||
AccessControlMode.acceptSelected: 'acceptSelected',
|
||||
AccessControlMode.rejectSelected: 'rejectSelected',
|
||||
};
|
||||
|
||||
_$PropsImpl _$$PropsImplFromJson(Map<String, dynamic> json) => _$PropsImpl(
|
||||
accessControl: json['accessControl'] == null
|
||||
? null
|
||||
: AccessControl.fromJson(
|
||||
json['accessControl'] as Map<String, dynamic>),
|
||||
allowBypass: json['allowBypass'] as bool?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$PropsImplToJson(_$PropsImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'accessControl': instance.accessControl,
|
||||
'allowBypass': instance.allowBypass,
|
||||
};
|
||||
|
||||
@@ -107,6 +107,9 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
|
||||
case TorchState.unavailable:
|
||||
icon = const Icon(Icons.flash_off);
|
||||
backgroundColor = Colors.transparent;
|
||||
case TorchState.auto:
|
||||
icon = const Icon(Icons.flash_auto);
|
||||
backgroundColor = Colors.orange;
|
||||
}
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
|
||||
@@ -61,8 +61,14 @@ class GlobalState {
|
||||
required Config config,
|
||||
required ClashConfig clashConfig,
|
||||
}) async {
|
||||
final args =
|
||||
config.isAccessControl ? json.encode(config.accessControl) : null;
|
||||
final args = config.isAccessControl
|
||||
? json.encode(
|
||||
Props(
|
||||
accessControl: config.accessControl,
|
||||
allowBypass: config.allowBypass,
|
||||
),
|
||||
)
|
||||
: null;
|
||||
await proxyManager.startProxy(
|
||||
port: clashConfig.mixedPort,
|
||||
args: args,
|
||||
|
||||
Submodule plugins/flutter_distributor updated: 57cdfc1534...64122ab7e1
@@ -277,10 +277,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: "29c90806ac5f5fb896547720b73b17ee9aed9bba540dc5d91fe29f8c5745b10a"
|
||||
sha256: a7eed9716c82453da5c09d3a82d4644e7070dd2da81bfe5b6c6873ae4f07cf4f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.3"
|
||||
version: "8.0.4"
|
||||
file_selector_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -649,10 +649,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: mobile_scanner
|
||||
sha256: f34c83198d9381f6c100dfaec647c275630840cbcda5d6c5eb6ba264beb96be4
|
||||
sha256: b8c0e9afcfd52534f85ec666f3d52156f560b5e6c25b1e3d4fe2087763607926
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.1"
|
||||
version: "5.1.1"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -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.18
|
||||
version: 0.8.19
|
||||
environment:
|
||||
sdk: '>=3.1.0 <4.0.0'
|
||||
|
||||
@@ -24,7 +24,7 @@ dependencies:
|
||||
windows_single_instance: ^1.0.1
|
||||
json_annotation: ^4.9.0
|
||||
file_picker: ^8.0.3
|
||||
mobile_scanner: 5.0.1
|
||||
mobile_scanner: ^5.1.1
|
||||
app_links: ^3.5.0
|
||||
win32_registry: ^1.1.2
|
||||
tray_manager: ^0.2.1
|
||||
|
||||
Reference in New Issue
Block a user