Compare commits
6 Commits
v0.8.17-de
...
v0.8.17
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7acf9c6db3 | ||
|
|
8074547fb4 | ||
|
|
8a01e04871 | ||
|
|
7ddcdd9828 | ||
|
|
d89ed076fd | ||
|
|
f4c3b06cd5 |
4
.github/workflows/build.yml
vendored
@@ -3,7 +3,7 @@ name: build
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- 'v*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
upload-release:
|
upload-release:
|
||||||
if: ${{ !endsWith(github.ref, '-debug') }}
|
if: ${{ !contains(github.ref, '+') }}
|
||||||
permissions: write-all
|
permissions: write-all
|
||||||
needs: [ build ]
|
needs: [ build ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.follow.clash"
|
applicationId "com.follow.clash"
|
||||||
minSdkVersion 21
|
minSdkVersion 24
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.software.leanback"
|
|
||||||
android:required="false" />
|
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.touchscreen"
|
android:name="android.hardware.touchscreen"
|
||||||
android:required="false" />
|
android:required="false" />
|
||||||
@@ -17,12 +14,15 @@
|
|||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" tools:ignore="QueryAllPackagesPermission"/>
|
tools:ignore="SystemPermissionTypo" />
|
||||||
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||||
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:extractNativeLibs="true"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:label="FlClash">
|
android:label="FlClash">
|
||||||
<activity
|
<activity
|
||||||
@@ -73,7 +73,8 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".services.FlClashTileService"
|
android:name=".services.FlClashTileService"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:icon="@drawable/tile_icon"
|
android:icon="@drawable/icon"
|
||||||
|
android:foregroundServiceType="specialUse"
|
||||||
android:label="FlClash"
|
android:label="FlClash"
|
||||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import io.flutter.embedding.engine.FlutterEngine
|
|||||||
import io.flutter.embedding.engine.dart.DartExecutor
|
import io.flutter.embedding.engine.dart.DartExecutor
|
||||||
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.N)
|
|
||||||
class FlClashTileService : TileService() {
|
class FlClashTileService : TileService() {
|
||||||
|
|
||||||
private val observer = Observer<RunState> { runState ->
|
private val observer = Observer<RunState> { runState ->
|
||||||
@@ -43,19 +42,27 @@ class FlClashTileService : TileService() {
|
|||||||
GlobalState.runState.observeForever(observer)
|
GlobalState.runState.observeForever(observer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StartActivityAndCollapseDeprecated")
|
|
||||||
private fun activityTransfer() {
|
private fun activityTransfer() {
|
||||||
val intent = Intent(this, TempActivity::class.java)
|
val intent = Intent(this, TempActivity::class.java)
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
if (Build.VERSION.SDK_INT >= 34) {
|
val pendingIntent = if (Build.VERSION.SDK_INT >= 31) {
|
||||||
val pendingIntent = PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
this,
|
this,
|
||||||
0,
|
0,
|
||||||
intent,
|
intent,
|
||||||
PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
startActivityAndCollapse(pendingIntent)
|
|
||||||
} else {
|
} else {
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
this,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
startActivityAndCollapse(pendingIntent)
|
||||||
|
}else{
|
||||||
startActivityAndCollapse(intent)
|
startActivityAndCollapse(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package com.follow.clash.services
|
package com.follow.clash.services
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.app.Notification.FOREGROUND_SERVICE_IMMEDIATE
|
||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.app.Service
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
||||||
import android.net.ProxyInfo
|
import android.net.ProxyInfo
|
||||||
@@ -13,15 +12,13 @@ import android.os.Binder
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE
|
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
|
||||||
import com.follow.clash.GlobalState
|
import com.follow.clash.GlobalState
|
||||||
import com.follow.clash.MainActivity
|
import com.follow.clash.MainActivity
|
||||||
|
import com.follow.clash.R
|
||||||
import com.follow.clash.models.AccessControl
|
import com.follow.clash.models.AccessControl
|
||||||
import com.follow.clash.models.AccessControlMode
|
import com.follow.clash.models.AccessControlMode
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("WrongConstant")
|
|
||||||
class FlClashVpnService : VpnService() {
|
class FlClashVpnService : VpnService() {
|
||||||
|
|
||||||
|
|
||||||
@@ -100,10 +97,10 @@ class FlClashVpnService : VpnService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val notificationBuilder by lazy {
|
private val notificationBuilder: NotificationCompat.Builder by lazy {
|
||||||
val intent = Intent(this, MainActivity::class.java)
|
val intent = Intent(this, MainActivity::class.java)
|
||||||
|
|
||||||
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
val pendingIntent = if (Build.VERSION.SDK_INT >= 31) {
|
||||||
PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
this,
|
this,
|
||||||
0,
|
0,
|
||||||
@@ -119,43 +116,43 @@ class FlClashVpnService : VpnService() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val icon = IconCompat.createWithResource(this, this.applicationInfo.icon)
|
|
||||||
|
|
||||||
with(NotificationCompat.Builder(this, CHANNEL)) {
|
with(NotificationCompat.Builder(this, CHANNEL)) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
setSmallIcon(R.drawable.ic_stat_name)
|
||||||
setSmallIcon(icon)
|
|
||||||
}
|
|
||||||
setContentTitle("FlClash")
|
setContentTitle("FlClash")
|
||||||
foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
|
|
||||||
setContentIntent(pendingIntent)
|
setContentIntent(pendingIntent)
|
||||||
setCategory(NotificationCompat.CATEGORY_SERVICE)
|
setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||||
priority = NotificationCompat.PRIORITY_LOW
|
priority = NotificationCompat.PRIORITY_MIN
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
|
||||||
|
}
|
||||||
setOngoing(true)
|
setOngoing(true)
|
||||||
setShowWhen(false)
|
setShowWhen(false)
|
||||||
setOnlyAlertOnce(true)
|
setOnlyAlertOnce(true)
|
||||||
|
setAutoCancel(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ForegroundServiceType", "WrongConstant")
|
|
||||||
fun startForeground(title: String, content: String) {
|
fun startForeground(title: String, content: String) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val channel =
|
val channel =
|
||||||
NotificationChannel(CHANNEL, "FlClash", NotificationManager.IMPORTANCE_LOW)
|
NotificationChannel(CHANNEL, "FlClash", NotificationManager.IMPORTANCE_LOW)
|
||||||
val manager = getSystemService(NotificationManager::class.java)
|
val manager = getSystemService(NotificationManager::class.java)
|
||||||
manager.createNotificationChannel(channel)
|
manager.createNotificationChannel(channel)
|
||||||
|
channel.setShowBadge(false)
|
||||||
}
|
}
|
||||||
val notification =
|
val notification =
|
||||||
notificationBuilder.setContentTitle(title).setContentText(content).build()
|
notificationBuilder.setContentTitle(title).setContentText(content).build()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
|
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
|
||||||
} else {
|
}else{
|
||||||
startForeground(notificationId, notification)
|
startForeground(notificationId, notification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopForeground() {
|
private fun stopForeground() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
stopForeground(Service.STOP_FOREGROUND_REMOVE)
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
android/app/src/main/res/drawable-hdpi/ic_stat_name.png
Normal file
|
After Width: | Height: | Size: 763 B |
BIN
android/app/src/main/res/drawable-mdpi/ic_stat_name.png
Normal file
|
After Width: | Height: | Size: 520 B |
BIN
android/app/src/main/res/drawable-xhdpi/ic_stat_name.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
android/app/src/main/res/drawable-xxhdpi/ic_stat_name.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
android/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
android/app/src/main/res/drawable/icon.png
Normal file
|
After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 105 KiB |
@@ -336,8 +336,8 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
|
|||||||
targetConfig.Mode = patchConfig.Mode
|
targetConfig.Mode = patchConfig.Mode
|
||||||
targetConfig.Tun.Enable = patchConfig.Tun.Enable
|
targetConfig.Tun.Enable = patchConfig.Tun.Enable
|
||||||
targetConfig.Tun.Device = patchConfig.Tun.Device
|
targetConfig.Tun.Device = patchConfig.Tun.Device
|
||||||
targetConfig.Tun.DNSHijack = patchConfig.Tun.DNSHijack
|
//targetConfig.Tun.DNSHijack = patchConfig.Tun.DNSHijack
|
||||||
targetConfig.Tun.Stack = patchConfig.Tun.Stack
|
//targetConfig.Tun.Stack = patchConfig.Tun.Stack
|
||||||
targetConfig.GeodataLoader = "standard"
|
targetConfig.GeodataLoader = "standard"
|
||||||
targetConfig.Profile.StoreSelected = false
|
targetConfig.Profile.StoreSelected = false
|
||||||
if targetConfig.DNS.Enable == false {
|
if targetConfig.DNS.Enable == false {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// ignore_for_file: constant_identifier_names
|
// ignore_for_file: constant_identifier_names
|
||||||
|
|
||||||
enum GroupType { Selector, URLTest, Fallback }
|
enum GroupType { Selector, URLTest, Fallback, LoadBalance, Relay }
|
||||||
|
|
||||||
enum GroupName { GLOBAL, Proxy, Auto, Fallback }
|
enum GroupName { GLOBAL, Proxy, Auto, Fallback }
|
||||||
|
|
||||||
@@ -61,4 +61,4 @@ enum MessageType { log, tun, delay, process, now }
|
|||||||
enum RecoveryOption {
|
enum RecoveryOption {
|
||||||
all,
|
all,
|
||||||
onlyProfiles,
|
onlyProfiles,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,13 @@ import 'package:fl_clash/widgets/widgets.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
extension AccessControlExtension on AccessControl {
|
||||||
|
List<String> get currentList => switch (mode) {
|
||||||
|
AccessControlMode.acceptSelected => acceptList,
|
||||||
|
AccessControlMode.rejectSelected => rejectList,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class AccessFragment extends StatefulWidget {
|
class AccessFragment extends StatefulWidget {
|
||||||
const AccessFragment({super.key});
|
const AccessFragment({super.key});
|
||||||
|
|
||||||
@@ -83,137 +90,64 @@ class _AccessFragmentState extends State<AccessFragment> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSelectedAllButton({
|
Widget _buildSearchButton(List<Package> packages) {
|
||||||
required bool isSelectedAll,
|
return IconButton(
|
||||||
required List<String> allValueList,
|
tooltip: appLocalizations.search,
|
||||||
}) {
|
onPressed: () {
|
||||||
return Builder(
|
showSearch(
|
||||||
builder: (context) {
|
context: context,
|
||||||
final tooltip = isSelectedAll
|
delegate: AccessControlSearchDelegate(
|
||||||
? appLocalizations.cancelSelectAll
|
packages: packages,
|
||||||
: appLocalizations.selectAll;
|
),
|
||||||
return IconButton(
|
).then((_) => {setState(() {})});
|
||||||
tooltip: tooltip,
|
|
||||||
onPressed: () {
|
|
||||||
final config = globalState.appController.config;
|
|
||||||
final isAccept =
|
|
||||||
config.accessControl.mode == AccessControlMode.acceptSelected;
|
|
||||||
|
|
||||||
if (isSelectedAll) {
|
|
||||||
config.accessControl = switch (isAccept) {
|
|
||||||
true => config.accessControl.copyWith(
|
|
||||||
acceptList: [],
|
|
||||||
),
|
|
||||||
false => config.accessControl.copyWith(
|
|
||||||
rejectList: [],
|
|
||||||
),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
config.accessControl = switch (isAccept) {
|
|
||||||
true => config.accessControl.copyWith(
|
|
||||||
acceptList: allValueList,
|
|
||||||
),
|
|
||||||
false => config.accessControl.copyWith(
|
|
||||||
rejectList: allValueList,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: isSelectedAll
|
|
||||||
? const Icon(Icons.deselect)
|
|
||||||
: const Icon(Icons.select_all),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
icon: const Icon(Icons.search),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _actionHeader({
|
// Widget _buildSelectedAllButton({
|
||||||
required bool isAccessControl,
|
// required bool isSelectedAll,
|
||||||
required List<String> valueList,
|
// required List<String> allValueList,
|
||||||
required String describe,
|
// }) {
|
||||||
required List<String> packageNameList,
|
// return Builder(
|
||||||
}) {
|
// builder: (context) {
|
||||||
return AbsorbPointer(
|
// final tooltip = isSelectedAll
|
||||||
absorbing: !isAccessControl,
|
// ? appLocalizations.cancelSelectAll
|
||||||
child: Padding(
|
// : appLocalizations.selectAll;
|
||||||
padding: const EdgeInsets.only(
|
// return IconButton(
|
||||||
top: 4,
|
// tooltip: tooltip,
|
||||||
bottom: 4,
|
// onPressed: () {
|
||||||
left: 16,
|
// final config = globalState.appController.config;
|
||||||
right: 8,
|
// final isAccept =
|
||||||
),
|
// config.accessControl.mode == AccessControlMode.acceptSelected;
|
||||||
child: Row(
|
//
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
// if (isSelectedAll) {
|
||||||
mainAxisSize: MainAxisSize.max,
|
// config.accessControl = switch (isAccept) {
|
||||||
children: [
|
// true => config.accessControl.copyWith(
|
||||||
Expanded(
|
// acceptList: [],
|
||||||
child: IntrinsicHeight(
|
// ),
|
||||||
child: Column(
|
// false => config.accessControl.copyWith(
|
||||||
mainAxisSize: MainAxisSize.max,
|
// rejectList: [],
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
// ),
|
||||||
children: [
|
// };
|
||||||
Expanded(
|
// } else {
|
||||||
child: Row(
|
// config.accessControl = switch (isAccept) {
|
||||||
children: [
|
// true => config.accessControl.copyWith(
|
||||||
Flexible(
|
// acceptList: allValueList,
|
||||||
child: Text(
|
// ),
|
||||||
appLocalizations.selected,
|
// false => config.accessControl.copyWith(
|
||||||
style: Theme.of(context)
|
// rejectList: allValueList,
|
||||||
.textTheme
|
// ),
|
||||||
.labelLarge
|
// };
|
||||||
?.copyWith(
|
// }
|
||||||
color:
|
// },
|
||||||
Theme.of(context).colorScheme.primary,
|
// icon: isSelectedAll
|
||||||
),
|
// ? const Icon(Icons.deselect)
|
||||||
),
|
// : const Icon(Icons.select_all),
|
||||||
),
|
// );
|
||||||
const Flexible(
|
// },
|
||||||
child: SizedBox(
|
// );
|
||||||
width: 8,
|
// }
|
||||||
),
|
|
||||||
),
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
"${valueList.length}",
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelLarge
|
|
||||||
?.copyWith(
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Flexible(
|
|
||||||
child: Text(describe),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: _buildSelectedAllButton(
|
|
||||||
isSelectedAll: const ListEquality<String>()
|
|
||||||
.equals(valueList, packageNameList),
|
|
||||||
allValueList: packageNameList,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Flexible(child: _buildFilterSystemAppButton()),
|
|
||||||
Flexible(child: _buildAppProxyModePopup()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildPackageList() {
|
Widget _buildPackageList() {
|
||||||
return ValueListenableBuilder(
|
return ValueListenableBuilder(
|
||||||
@@ -252,14 +186,11 @@ class _AccessFragmentState extends State<AccessFragment> {
|
|||||||
accessControlMode == AccessControlMode.acceptSelected
|
accessControlMode == AccessControlMode.acceptSelected
|
||||||
? acceptPackages
|
? acceptPackages
|
||||||
: rejectPackages;
|
: rejectPackages;
|
||||||
final currentList =
|
final currentList = accessControl.currentList;
|
||||||
accessControlMode == AccessControlMode.acceptSelected
|
|
||||||
? accessControl.acceptList
|
|
||||||
: accessControl.rejectList;
|
|
||||||
final currentPackages = isFilterSystemApp
|
final currentPackages = isFilterSystemApp
|
||||||
? packages
|
? packages
|
||||||
.where((element) => element.isSystem == false)
|
.where((element) => element.isSystem == false)
|
||||||
.toList()
|
.toList()
|
||||||
: packages;
|
: packages;
|
||||||
final packageNameList =
|
final packageNameList =
|
||||||
currentPackages.map((e) => e.packageName).toList();
|
currentPackages.map((e) => e.packageName).toList();
|
||||||
@@ -272,11 +203,82 @@ class _AccessFragmentState extends State<AccessFragment> {
|
|||||||
status: !isAccessControl,
|
status: !isAccessControl,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_actionHeader(
|
AbsorbPointer(
|
||||||
isAccessControl: isAccessControl,
|
absorbing: !isAccessControl,
|
||||||
valueList: valueList,
|
child: Padding(
|
||||||
describe: describe,
|
padding: const EdgeInsets.only(
|
||||||
packageNameList: packageNameList,
|
top: 4,
|
||||||
|
bottom: 4,
|
||||||
|
left: 16,
|
||||||
|
right: 8,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: IntrinsicHeight(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
appLocalizations.selected,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.labelLarge
|
||||||
|
?.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Flexible(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
"${valueList.length}",
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.labelLarge
|
||||||
|
?.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: Text(describe),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: _buildSearchButton(currentPackages)),
|
||||||
|
Flexible(child: _buildFilterSystemAppButton()),
|
||||||
|
Flexible(child: _buildAppProxyModePopup()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@@ -429,3 +431,111 @@ class PackageListItem extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AccessControlSearchDelegate extends SearchDelegate {
|
||||||
|
final List<Package> packages;
|
||||||
|
|
||||||
|
AccessControlSearchDelegate({
|
||||||
|
required this.packages,
|
||||||
|
});
|
||||||
|
|
||||||
|
List<Package> get _results {
|
||||||
|
final lowQuery = query.toLowerCase();
|
||||||
|
return packages
|
||||||
|
.where(
|
||||||
|
(package) =>
|
||||||
|
package.label.toLowerCase().contains(lowQuery) ||
|
||||||
|
package.packageName.contains(lowQuery),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget>? buildActions(BuildContext context) {
|
||||||
|
return [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (query.isEmpty) {
|
||||||
|
close(context, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
query = '';
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.clear),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget? buildLeading(BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
close(context, null);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _packageList(List<Package> packages) {
|
||||||
|
return Selector<Config, PackageListSelectorState>(
|
||||||
|
selector: (_, config) => PackageListSelectorState(
|
||||||
|
accessControl: config.accessControl,
|
||||||
|
isAccessControl: config.isAccessControl,
|
||||||
|
),
|
||||||
|
builder: (context, state, __) {
|
||||||
|
final accessControl = state.accessControl;
|
||||||
|
final isAccessControl = state.isAccessControl;
|
||||||
|
final accessControlMode = accessControl.mode;
|
||||||
|
final currentList = accessControl.currentList;
|
||||||
|
final packageNameList = packages.map((e) => e.packageName).toList();
|
||||||
|
final valueList = currentList.intersection(packageNameList);
|
||||||
|
return DisabledMask(
|
||||||
|
status: !isAccessControl,
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: packages.length,
|
||||||
|
itemBuilder: (_, index) {
|
||||||
|
final package = packages[index];
|
||||||
|
return PackageListItem(
|
||||||
|
key: Key(package.packageName),
|
||||||
|
package: package,
|
||||||
|
value: valueList.contains(package.packageName),
|
||||||
|
isActive: isAccessControl,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value == true) {
|
||||||
|
valueList.add(package.packageName);
|
||||||
|
} else {
|
||||||
|
valueList.remove(package.packageName);
|
||||||
|
}
|
||||||
|
final config = globalState.appController.config;
|
||||||
|
if (accessControlMode == AccessControlMode.acceptSelected) {
|
||||||
|
config.accessControl = config.accessControl.copyWith(
|
||||||
|
acceptList: valueList,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
config.accessControl = config.accessControl.copyWith(
|
||||||
|
rejectList: valueList,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildResults(BuildContext context) {
|
||||||
|
final packages = _results;
|
||||||
|
return _packageList(packages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildSuggestions(BuildContext context) {
|
||||||
|
return _packageList(packages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,9 +17,16 @@ enum ProfileActions {
|
|||||||
delete,
|
delete,
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProfilesFragment extends StatelessWidget {
|
class ProfilesFragment extends StatefulWidget {
|
||||||
const ProfilesFragment({super.key});
|
const ProfilesFragment({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ProfilesFragment> createState() => _ProfilesFragmentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProfilesFragmentState extends State<ProfilesFragment> {
|
||||||
|
final hasPadding = ValueNotifier<bool>(false);
|
||||||
|
|
||||||
_handleShowAddExtendPage() {
|
_handleShowAddExtendPage() {
|
||||||
showExtendPage(
|
showExtendPage(
|
||||||
globalState.navigatorKey.currentState!.context,
|
globalState.navigatorKey.currentState!.context,
|
||||||
@@ -41,7 +48,7 @@ class ProfilesFragment extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_initScaffoldState(BuildContext context) {
|
_initScaffoldState() {
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance.addPostFrameCallback(
|
||||||
(_) {
|
(_) {
|
||||||
final commonScaffoldState =
|
final commonScaffoldState =
|
||||||
@@ -78,7 +85,7 @@ class ProfilesFragment extends StatelessWidget {
|
|||||||
selector: (_, appState) => appState.currentLabel == 'profiles',
|
selector: (_, appState) => appState.currentLabel == 'profiles',
|
||||||
builder: (_, isCurrent, child) {
|
builder: (_, isCurrent, child) {
|
||||||
if (isCurrent) {
|
if (isCurrent) {
|
||||||
_initScaffoldState(context);
|
_initScaffoldState();
|
||||||
}
|
}
|
||||||
return child!;
|
return child!;
|
||||||
},
|
},
|
||||||
@@ -98,27 +105,46 @@ class ProfilesFragment extends StatelessWidget {
|
|||||||
final isMobile = state.viewMode == ViewMode.mobile;
|
final isMobile = state.viewMode == ViewMode.mobile;
|
||||||
return Align(
|
return Align(
|
||||||
alignment: Alignment.topCenter,
|
alignment: Alignment.topCenter,
|
||||||
child: SingleChildScrollView(
|
child: NotificationListener<ScrollNotification>(
|
||||||
padding: !isMobile
|
onNotification: (scrollNotification) {
|
||||||
? const EdgeInsets.symmetric(
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
horizontal: 16,
|
hasPadding.value =
|
||||||
vertical: 16,
|
scrollNotification.metrics.maxScrollExtent > 0;
|
||||||
)
|
});
|
||||||
: EdgeInsets.zero,
|
return true;
|
||||||
child: Grid(
|
},
|
||||||
mainAxisSpacing: isMobile ? 8 : 16,
|
child: ValueListenableBuilder(
|
||||||
crossAxisSpacing: 16,
|
valueListenable: hasPadding,
|
||||||
crossAxisCount: columns,
|
builder: (_, hasPadding, __) {
|
||||||
children: [
|
return SingleChildScrollView(
|
||||||
for (final profile in state.profiles)
|
padding: !isMobile
|
||||||
GridItem(
|
? EdgeInsets.only(
|
||||||
child: ProfileItem(
|
left: 16,
|
||||||
profile: profile,
|
right: 16,
|
||||||
groupValue: state.currentProfileId,
|
top: 16,
|
||||||
onChanged: globalState.appController.changeProfile,
|
bottom: 16 + (hasPadding ? 56 : 0),
|
||||||
),
|
)
|
||||||
|
: EdgeInsets.only(
|
||||||
|
bottom: 0 + (hasPadding ? 56 : 0),
|
||||||
|
),
|
||||||
|
child: Grid(
|
||||||
|
mainAxisSpacing: isMobile ? 8 : 16,
|
||||||
|
crossAxisSpacing: 16,
|
||||||
|
crossAxisCount: columns,
|
||||||
|
children: [
|
||||||
|
for (final profile in state.profiles)
|
||||||
|
GridItem(
|
||||||
|
child: ProfileItem(
|
||||||
|
profile: profile,
|
||||||
|
groupValue: state.currentProfileId,
|
||||||
|
onChanged:
|
||||||
|
globalState.appController.changeProfile,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -199,10 +199,15 @@ class ProxiesTabView extends StatelessWidget {
|
|||||||
|
|
||||||
_delayTest(List<Proxy> proxies) async {
|
_delayTest(List<Proxy> proxies) async {
|
||||||
for (final proxy in proxies) {
|
for (final proxy in proxies) {
|
||||||
|
final appController = globalState.appController;
|
||||||
|
final proxyName = appController.appState.getRealProxyName(proxy.name) ?? proxy.name;
|
||||||
globalState.appController.setDelay(
|
globalState.appController.setDelay(
|
||||||
Delay(name: proxy.name, value: 0),
|
Delay(
|
||||||
|
name: proxyName,
|
||||||
|
value: 0,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
clashCore.getDelay(proxy.name).then((delay) {
|
clashCore.getDelay(proxyName).then((delay) {
|
||||||
globalState.appController.setDelay(delay);
|
globalState.appController.setDelay(delay);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -434,7 +439,7 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
|
|||||||
_controller = AnimationController(
|
_controller = AnimationController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
duration: const Duration(
|
duration: const Duration(
|
||||||
milliseconds: 600,
|
milliseconds: 200,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
_scale = Tween<double>(
|
_scale = Tween<double>(
|
||||||
|
|||||||
@@ -160,5 +160,6 @@
|
|||||||
"checking": "Checking...",
|
"checking": "Checking...",
|
||||||
"country": "Country",
|
"country": "Country",
|
||||||
"checkError": "Check error",
|
"checkError": "Check error",
|
||||||
"ipCheckTimeout": "Ip check timeout"
|
"ipCheckTimeout": "Ip check timeout",
|
||||||
|
"search": "Search"
|
||||||
}
|
}
|
||||||
@@ -160,5 +160,6 @@
|
|||||||
"checking": "检测中...",
|
"checking": "检测中...",
|
||||||
"country": "区域",
|
"country": "区域",
|
||||||
"checkError": "检测失败",
|
"checkError": "检测失败",
|
||||||
"ipCheckTimeout": "Ip检测超时"
|
"ipCheckTimeout": "Ip检测超时",
|
||||||
|
"search": "搜索"
|
||||||
}
|
}
|
||||||
@@ -212,6 +212,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"External resource related info"),
|
"External resource related info"),
|
||||||
"rule": MessageLookupByLibrary.simpleMessage("Rule"),
|
"rule": MessageLookupByLibrary.simpleMessage("Rule"),
|
||||||
"save": MessageLookupByLibrary.simpleMessage("Save"),
|
"save": MessageLookupByLibrary.simpleMessage("Save"),
|
||||||
|
"search": MessageLookupByLibrary.simpleMessage("Search"),
|
||||||
"selectAll": MessageLookupByLibrary.simpleMessage("Select all"),
|
"selectAll": MessageLookupByLibrary.simpleMessage("Select all"),
|
||||||
"selected": MessageLookupByLibrary.simpleMessage("Selected"),
|
"selected": MessageLookupByLibrary.simpleMessage("Selected"),
|
||||||
"settings": MessageLookupByLibrary.simpleMessage("Settings"),
|
"settings": MessageLookupByLibrary.simpleMessage("Settings"),
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"resourcesDesc": MessageLookupByLibrary.simpleMessage("外部资源相关信息"),
|
"resourcesDesc": MessageLookupByLibrary.simpleMessage("外部资源相关信息"),
|
||||||
"rule": MessageLookupByLibrary.simpleMessage("规则"),
|
"rule": MessageLookupByLibrary.simpleMessage("规则"),
|
||||||
"save": MessageLookupByLibrary.simpleMessage("保存"),
|
"save": MessageLookupByLibrary.simpleMessage("保存"),
|
||||||
|
"search": MessageLookupByLibrary.simpleMessage("搜索"),
|
||||||
"selectAll": MessageLookupByLibrary.simpleMessage("全选"),
|
"selectAll": MessageLookupByLibrary.simpleMessage("全选"),
|
||||||
"selected": MessageLookupByLibrary.simpleMessage("已选择"),
|
"selected": MessageLookupByLibrary.simpleMessage("已选择"),
|
||||||
"settings": MessageLookupByLibrary.simpleMessage("设置"),
|
"settings": MessageLookupByLibrary.simpleMessage("设置"),
|
||||||
|
|||||||
@@ -1669,6 +1669,16 @@ class AppLocalizations {
|
|||||||
args: [],
|
args: [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Search`
|
||||||
|
String get search {
|
||||||
|
return Intl.message(
|
||||||
|
'Search',
|
||||||
|
name: 'search',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class AppState with ChangeNotifier {
|
|||||||
} else {
|
} else {
|
||||||
final index = groups.indexWhere((element) => element.name == proxyName);
|
final index = groups.indexWhere((element) => element.name == proxyName);
|
||||||
if (index == -1) return type;
|
if (index == -1) return type;
|
||||||
return "$type(${groups[index].now})";
|
return "$type(${groups[index].now ?? '*'})";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class Tun with _$Tun {
|
|||||||
const factory Tun({
|
const factory Tun({
|
||||||
@Default(false) bool enable,
|
@Default(false) bool enable,
|
||||||
@Default(appName) String device,
|
@Default(appName) String device,
|
||||||
@Default(TunStack.mixed) TunStack stack,
|
@Default(TunStack.gvisor) TunStack stack,
|
||||||
@JsonKey(name: "dns-hijack") @Default(["any:53"]) List<String> dnsHijack,
|
@JsonKey(name: "dns-hijack") @Default(["any:53"]) List<String> dnsHijack,
|
||||||
}) = _Tun;
|
}) = _Tun;
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import '../common/common.dart';
|
|||||||
import 'models.dart';
|
import 'models.dart';
|
||||||
|
|
||||||
part 'generated/config.g.dart';
|
part 'generated/config.g.dart';
|
||||||
|
|
||||||
part 'generated/config.freezed.dart';
|
part 'generated/config.freezed.dart';
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ class _$TunImpl implements _Tun {
|
|||||||
const _$TunImpl(
|
const _$TunImpl(
|
||||||
{this.enable = false,
|
{this.enable = false,
|
||||||
this.device = appName,
|
this.device = appName,
|
||||||
this.stack = TunStack.mixed,
|
this.stack = TunStack.gvisor,
|
||||||
@JsonKey(name: "dns-hijack")
|
@JsonKey(name: "dns-hijack")
|
||||||
final List<String> dnsHijack = const ["any:53"]})
|
final List<String> dnsHijack = const ["any:53"]})
|
||||||
: _dnsHijack = dnsHijack;
|
: _dnsHijack = dnsHijack;
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ _$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
|
|||||||
enable: json['enable'] as bool? ?? false,
|
enable: json['enable'] as bool? ?? false,
|
||||||
device: json['device'] as String? ?? appName,
|
device: json['device'] as String? ?? appName,
|
||||||
stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ??
|
stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ??
|
||||||
TunStack.mixed,
|
TunStack.gvisor,
|
||||||
dnsHijack: (json['dns-hijack'] as List<dynamic>?)
|
dnsHijack: (json['dns-hijack'] as List<dynamic>?)
|
||||||
?.map((e) => e as String)
|
?.map((e) => e as String)
|
||||||
.toList() ??
|
.toList() ??
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ const _$GroupTypeEnumMap = {
|
|||||||
GroupType.Selector: 'Selector',
|
GroupType.Selector: 'Selector',
|
||||||
GroupType.URLTest: 'URLTest',
|
GroupType.URLTest: 'URLTest',
|
||||||
GroupType.Fallback: 'Fallback',
|
GroupType.Fallback: 'Fallback',
|
||||||
|
GroupType.LoadBalance: 'LoadBalance',
|
||||||
|
GroupType.Relay: 'Relay',
|
||||||
};
|
};
|
||||||
|
|
||||||
_$ProxyImpl _$$ProxyImplFromJson(Map<String, dynamic> json) => _$ProxyImpl(
|
_$ProxyImpl _$$ProxyImplFromJson(Map<String, dynamic> json) => _$ProxyImpl(
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
|||||||
return floatingActionButton ?? Container();
|
return floatingActionButton ?? Container();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
|
|
||||||
appBar: PreferredSize(
|
appBar: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(kToolbarHeight),
|
preferredSize: const Size.fromHeight(kToolbarHeight),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
|
|||||||
292
pubspec.lock
@@ -1,7 +1,7 @@
|
|||||||
name: fl_clash
|
name: fl_clash
|
||||||
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.8.16
|
version: 0.8.17
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.1.0 <4.0.0'
|
sdk: '>=3.1.0 <4.0.0'
|
||||||
|
|
||||||
|
|||||||
17
setup.dart
@@ -12,10 +12,7 @@ enum PlatformType {
|
|||||||
macos,
|
macos,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Arch {
|
enum Arch { amd64, arm64, arm }
|
||||||
amd64,
|
|
||||||
arm64,
|
|
||||||
}
|
|
||||||
|
|
||||||
class BuildLibItem {
|
class BuildLibItem {
|
||||||
PlatformType platform;
|
PlatformType platform;
|
||||||
@@ -64,6 +61,11 @@ class Build {
|
|||||||
arch: Arch.amd64,
|
arch: Arch.amd64,
|
||||||
archName: 'amd64',
|
archName: 'amd64',
|
||||||
),
|
),
|
||||||
|
BuildLibItem(
|
||||||
|
platform: PlatformType.android,
|
||||||
|
arch: Arch.arm,
|
||||||
|
archName: 'armeabi-v7a',
|
||||||
|
),
|
||||||
BuildLibItem(
|
BuildLibItem(
|
||||||
platform: PlatformType.android,
|
platform: PlatformType.android,
|
||||||
arch: Arch.arm64,
|
arch: Arch.arm64,
|
||||||
@@ -334,7 +336,7 @@ class BuildCommand extends Command {
|
|||||||
final archName = argResults?['arch'];
|
final archName = argResults?['arch'];
|
||||||
final currentArches =
|
final currentArches =
|
||||||
arches.where((element) => element.name == archName).toList();
|
arches.where((element) => element.name == archName).toList();
|
||||||
final arch = currentArches.isEmpty ? null : arches.first;
|
final arch = currentArches.isEmpty ? null : currentArches.first;
|
||||||
await _buildLib(arch);
|
await _buildLib(arch);
|
||||||
if (build != "all") {
|
if (build != "all") {
|
||||||
return;
|
return;
|
||||||
@@ -357,10 +359,11 @@ class BuildCommand extends Command {
|
|||||||
break;
|
break;
|
||||||
case PlatformType.android:
|
case PlatformType.android:
|
||||||
final targetMap = {
|
final targetMap = {
|
||||||
|
Arch.arm: "android-arm",
|
||||||
|
Arch.arm64: "android-arm64",
|
||||||
Arch.amd64: "android-x64",
|
Arch.amd64: "android-x64",
|
||||||
Arch.arm64: "android-arm64"
|
|
||||||
};
|
};
|
||||||
final defaultArches = [Arch.amd64, Arch.arm64];
|
final defaultArches = [Arch.arm, Arch.arm64, Arch.amd64];
|
||||||
final defaultTargets = defaultArches
|
final defaultTargets = defaultArches
|
||||||
.where((element) => arch == null ? true : element == arch)
|
.where((element) => arch == null ? true : element == arch)
|
||||||
.map((e) => targetMap[e])
|
.map((e) => targetMap[e])
|
||||||
|
|||||||