Fix android tile service issues

This commit is contained in:
chen08209
2024-08-01 23:51:00 +08:00
parent 8cdaf30de0
commit 00a78b5fb4
18 changed files with 268 additions and 173 deletions

View File

@@ -142,6 +142,7 @@ class ApplicationState extends State<Application> {
locale: config.locale,
themeMode: config.themeMode,
primaryColor: config.primaryColor,
prueBlack: config.prueBlack,
),
builder: (_, state, child) {
return DynamicColorBuilder(
@@ -180,7 +181,7 @@ class ApplicationState extends State<Application> {
brightness: Brightness.dark,
systemColorSchemes: systemColorSchemes,
primaryColor: state.primaryColor,
),
).toPrueBlack(state.prueBlack),
),
home: child,
);

View File

@@ -16,4 +16,13 @@ extension ColorExtension on Color {
toLittle() {
return withOpacity(0.03);
}
}
}
extension ColorSchemeExtension on ColorScheme {
ColorScheme toPrueBlack(bool isPrueBlack) => isPrueBlack
? copyWith(
surface: Colors.black,
background: Colors.black,
)
: this;
}

View File

@@ -89,8 +89,11 @@ class _EditProfileState extends State<EditProfile> {
});
}
Future<FileInfo> _getFileInfo(path) async {
Future<FileInfo?> _getFileInfo(path) async {
final file = File(path);
if (!await file.exists()) {
return null;
}
final lastModified = await file.lastModified();
final size = await file.length();
return FileInfo(
@@ -127,59 +130,6 @@ class _EditProfileState extends State<EditProfile> {
);
}
Widget _buildSubtitle() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 4,
),
ValueListenableBuilder<FileInfo?>(
valueListenable: fileInfoNotifier,
builder: (_, fileInfo, __) {
final height =
globalState.appController.measure.bodyMediumHeight + 4;
return SizedBox(
height: height,
child: FadeBox(
child: fileInfo == null
? SizedBox(
width: height,
height: height,
child: const CircularProgressIndicator(
strokeWidth: 2,
),
)
: Text(
fileInfo.desc,
),
),
);
},
),
const SizedBox(
height: 8,
),
Wrap(
runSpacing: 6,
spacing: 12,
children: [
CommonChip(
avatar: const Icon(Icons.edit),
label: appLocalizations.edit,
onPressed: _editProfileFile,
),
CommonChip(
avatar: const Icon(Icons.upload),
label: appLocalizations.upload,
onPressed: _uploadProfileFile,
),
],
),
],
);
}
@override
Widget build(BuildContext context) {
final items = [
@@ -250,9 +200,49 @@ class _EditProfileState extends State<EditProfile> {
),
),
],
ListItem(
title: Text(appLocalizations.profile),
subtitle: _buildSubtitle(),
ValueListenableBuilder<FileInfo?>(
valueListenable: fileInfoNotifier,
builder: (_, fileInfo, __) {
return FadeBox(
child: fileInfo == null
? Container()
: ListItem(
title: Text(
appLocalizations.profile,
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 4,
),
Text(
fileInfo.desc,
),
const SizedBox(
height: 8,
),
Wrap(
runSpacing: 6,
spacing: 12,
children: [
CommonChip(
avatar: const Icon(Icons.edit),
label: appLocalizations.edit,
onPressed: _editProfileFile,
),
CommonChip(
avatar: const Icon(Icons.upload),
label: appLocalizations.upload,
onPressed: _uploadProfileFile,
),
],
),
],
),
),
);
},
),
];
return FloatLayout(

View File

@@ -189,7 +189,7 @@ class _ProfileItemState extends State<ProfileItem> {
),
onTab: () async {
await globalState.appController.deleteProfile(widget.profile.id);
if(mounted){
if (mounted) {
Navigator.of(context).pop();
}
},
@@ -231,75 +231,90 @@ class _ProfileItemState extends State<ProfileItem> {
);
}
List<Widget> _buildUserInfo(UserInfo userInfo) {
final use = userInfo.upload + userInfo.download;
final total = userInfo.total;
if(total == 0){
return [];
}
final useShow = TrafficValue(value: use).show;
final totalShow = TrafficValue(value: total).show;
final progress = total == 0 ? 0.0 : use / total;
final expireShow = userInfo.expire == 0
? appLocalizations.infiniteTime
: DateTime.fromMillisecondsSinceEpoch(userInfo.expire * 1000).show;
return [
LinearProgressIndicator(
minHeight: 6,
value: progress,
),
const SizedBox(
height: 8,
),
Text(
"$useShow / $totalShow · $expireShow",
style: context.textTheme.labelMedium?.toLight,
),
const SizedBox(
height: 4,
),
];
}
List<Widget> _buildUrlProfileInfo(Profile profile) {
final userInfo = profile.userInfo;
return [
const SizedBox(
height: 8,
),
if (userInfo != null) ..._buildUserInfo(userInfo),
Text(
profile.lastUpdateDate?.lastUpdateTimeDesc ?? "",
style: context.textTheme.labelMedium?.toLight,
),
];
}
List<Widget> _buildFileProfileInfo(Profile profile) {
return [
const SizedBox(
height: 8,
),
Text(
profile.lastUpdateDate?.lastUpdateTimeDesc ?? "",
style: context.textTheme.labelMedium?.toLight,
),
];
}
_buildTitle(Profile profile) {
final textTheme = context.textTheme;
return Container(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
Text(
profile.label ?? profile.id,
style: context.textTheme.titleMedium,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: Text(
profile.label ?? profile.id,
style: textTheme.titleMedium,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Text(
profile.lastUpdateDate?.lastUpdateTimeDesc ?? '',
style: textTheme.labelMedium?.toLight,
),
...switch (profile.type) {
ProfileType.file => _buildFileProfileInfo(
profile,
),
ProfileType.url => _buildUrlProfileInfo(
profile,
),
},
],
),
Builder(builder: (context) {
final userInfo = profile.userInfo ?? const UserInfo();
final use = userInfo.upload + userInfo.download;
final total = userInfo.total;
final useShow = TrafficValue(value: use).show;
final totalShow = TrafficValue(value: total).show;
final progress = total == 0 ? 0.0 : use / total;
final expireShow = userInfo.expire == 0
? appLocalizations.infiniteTime
: DateTime.fromMillisecondsSinceEpoch(userInfo.expire * 1000)
.show;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
margin: const EdgeInsets.symmetric(
vertical: 8,
),
child: LinearProgressIndicator(
minHeight: 6,
value: progress,
),
),
Text(
"$useShow / $totalShow",
style: textTheme.labelMedium?.toLight,
),
const SizedBox(
height: 2,
),
Row(
children: [
Text(
expireShow,
style: textTheme.labelMedium?.toLight,
),
],
)
],
);
}),
],
),
);

View File

@@ -26,9 +26,7 @@ class ThemeFragment extends StatelessWidget {
final previewCard = Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: CommonCard(
onPressed: (){
},
onPressed: () {},
info: Info(
label: appLocalizations.preview,
iconData: Icons.looks,
@@ -87,7 +85,6 @@ class ThemeColorsBox extends StatefulWidget {
}
class _ThemeColorsBoxState extends State<ThemeColorsBox> {
Widget _themeModeCheckBox({
bool? isSelected,
required ThemeModeItem themeModeItem,
@@ -229,6 +226,27 @@ class _ThemeColorsBoxState extends State<ThemeColorsBox> {
),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Selector<Config, bool>(
selector: (_, config) => config.prueBlack,
builder: (_, value, ___) {
return ListItem.switchItem(
leading: Icon(
Icons.contrast,
color: context.colorScheme.primary,
),
title: Text(appLocalizations.prueBlackMode),
delegate: SwitchDelegate(
value: value,
onChanged: (value){
globalState.appController.config.prueBlack = value;
}
),
);
},
),
)
],
);
}

View File

@@ -219,5 +219,6 @@
"autoCloseConnectionsDesc": "Auto close connections after change node",
"onlyStatisticsProxy": "Only statistics proxy",
"onlyStatisticsProxyDesc": "When turned on, only statistics proxy traffic",
"deleteProfileTip": "Sure you want to delete the current profile?"
"deleteProfileTip": "Sure you want to delete the current profile?",
"prueBlackMode": "Prue black mode"
}

View File

@@ -219,5 +219,6 @@
"autoCloseConnectionsDesc": "切换节点后自动关闭连接",
"onlyStatisticsProxy": "仅统计代理",
"onlyStatisticsProxyDesc": "开启后,将只统计代理流量",
"deleteProfileTip": "确定要删除当前配置吗?"
"deleteProfileTip": "确定要删除当前配置吗?",
"prueBlackMode": "纯黑模式"
}

View File

@@ -257,6 +257,8 @@ class MessageLookup extends MessageLookupByLibrary {
"proxyPort": MessageLookupByLibrary.simpleMessage("ProxyPort"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage(
"Set the Clash listening port"),
"prueBlackMode":
MessageLookupByLibrary.simpleMessage("Prue black mode"),
"qrcode": MessageLookupByLibrary.simpleMessage("QR code"),
"qrcodeDesc": MessageLookupByLibrary.simpleMessage(
"Scan QR code to obtain profile"),

View File

@@ -208,6 +208,7 @@ class MessageLookup extends MessageLookupByLibrary {
"proxyGroup": MessageLookupByLibrary.simpleMessage("代理组"),
"proxyPort": MessageLookupByLibrary.simpleMessage("代理端口"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage("设置Clash监听端口"),
"prueBlackMode": MessageLookupByLibrary.simpleMessage("纯黑模式"),
"qrcode": MessageLookupByLibrary.simpleMessage("二维码"),
"qrcodeDesc": MessageLookupByLibrary.simpleMessage("扫描二维码获取配置文件"),
"recovery": MessageLookupByLibrary.simpleMessage("恢复"),

View File

@@ -2259,6 +2259,16 @@ class AppLocalizations {
args: [],
);
}
/// `Prue black mode`
String get prueBlackMode {
return Intl.message(
'Prue black mode',
name: 'prueBlackMode',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -82,6 +82,7 @@ class Config extends ChangeNotifier {
String _testUrl;
WindowProps _windowProps;
bool _onlyProxy;
bool _prueBlack;
Config()
: _profiles = [],
@@ -107,6 +108,7 @@ class Config extends ChangeNotifier {
_windowProps = defaultWindowProps,
_proxiesType = ProxiesType.tab,
_proxiesColumns = 2,
_prueBlack = false,
_onlyProxy = false;
deleteProfileById(String id) {
@@ -423,6 +425,17 @@ class Config extends ChangeNotifier {
}
}
@JsonKey(defaultValue: false)
bool get prueBlack {
return _prueBlack;
}
set prueBlack(bool value) {
if (_prueBlack != value) {
_prueBlack = value;
notifyListeners();
}
}
@JsonKey(defaultValue: false)
bool get isCloseConnections {
@@ -530,6 +543,7 @@ class Config extends ChangeNotifier {
_accessControl = config._accessControl;
_isAnimateToPage = config._isAnimateToPage;
_autoCheckUpdate = config._autoCheckUpdate;
_prueBlack = config._prueBlack;
_testUrl = config._testUrl;
_isExclude = config._isExclude;
_windowProps = config._windowProps;

View File

@@ -36,6 +36,7 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
..allowBypass = json['allowBypass'] as bool? ?? true
..systemProxy = json['systemProxy'] as bool? ?? false
..onlyProxy = json['onlyProxy'] as bool? ?? false
..prueBlack = json['prueBlack'] as bool? ?? false
..isCloseConnections = json['isCloseConnections'] as bool? ?? false
..proxiesType = $enumDecodeNullable(_$ProxiesTypeEnumMap, json['proxiesType'],
unknownValue: ProxiesType.tab) ??
@@ -71,6 +72,7 @@ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
'allowBypass': instance.allowBypass,
'systemProxy': instance.systemProxy,
'onlyProxy': instance.onlyProxy,
'prueBlack': instance.prueBlack,
'isCloseConnections': instance.isCloseConnections,
'proxiesType': _$ProxiesTypeEnumMap[instance.proxiesType]!,
'proxyCardType': _$ProxyCardTypeEnumMap[instance.proxyCardType]!,

View File

@@ -633,6 +633,7 @@ mixin _$ApplicationSelectorState {
String? get locale => throw _privateConstructorUsedError;
ThemeMode? get themeMode => throw _privateConstructorUsedError;
int? get primaryColor => throw _privateConstructorUsedError;
bool get prueBlack => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ApplicationSelectorStateCopyWith<ApplicationSelectorState> get copyWith =>
@@ -645,7 +646,11 @@ abstract class $ApplicationSelectorStateCopyWith<$Res> {
$Res Function(ApplicationSelectorState) then) =
_$ApplicationSelectorStateCopyWithImpl<$Res, ApplicationSelectorState>;
@useResult
$Res call({String? locale, ThemeMode? themeMode, int? primaryColor});
$Res call(
{String? locale,
ThemeMode? themeMode,
int? primaryColor,
bool prueBlack});
}
/// @nodoc
@@ -665,6 +670,7 @@ class _$ApplicationSelectorStateCopyWithImpl<$Res,
Object? locale = freezed,
Object? themeMode = freezed,
Object? primaryColor = freezed,
Object? prueBlack = null,
}) {
return _then(_value.copyWith(
locale: freezed == locale
@@ -679,6 +685,10 @@ class _$ApplicationSelectorStateCopyWithImpl<$Res,
? _value.primaryColor
: primaryColor // ignore: cast_nullable_to_non_nullable
as int?,
prueBlack: null == prueBlack
? _value.prueBlack
: prueBlack // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
@@ -692,7 +702,11 @@ abstract class _$$ApplicationSelectorStateImplCopyWith<$Res>
__$$ApplicationSelectorStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String? locale, ThemeMode? themeMode, int? primaryColor});
$Res call(
{String? locale,
ThemeMode? themeMode,
int? primaryColor,
bool prueBlack});
}
/// @nodoc
@@ -711,6 +725,7 @@ class __$$ApplicationSelectorStateImplCopyWithImpl<$Res>
Object? locale = freezed,
Object? themeMode = freezed,
Object? primaryColor = freezed,
Object? prueBlack = null,
}) {
return _then(_$ApplicationSelectorStateImpl(
locale: freezed == locale
@@ -725,6 +740,10 @@ class __$$ApplicationSelectorStateImplCopyWithImpl<$Res>
? _value.primaryColor
: primaryColor // ignore: cast_nullable_to_non_nullable
as int?,
prueBlack: null == prueBlack
? _value.prueBlack
: prueBlack // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
@@ -733,7 +752,10 @@ class __$$ApplicationSelectorStateImplCopyWithImpl<$Res>
class _$ApplicationSelectorStateImpl implements _ApplicationSelectorState {
const _$ApplicationSelectorStateImpl(
{this.locale, this.themeMode, this.primaryColor});
{required this.locale,
required this.themeMode,
required this.primaryColor,
required this.prueBlack});
@override
final String? locale;
@@ -741,10 +763,12 @@ class _$ApplicationSelectorStateImpl implements _ApplicationSelectorState {
final ThemeMode? themeMode;
@override
final int? primaryColor;
@override
final bool prueBlack;
@override
String toString() {
return 'ApplicationSelectorState(locale: $locale, themeMode: $themeMode, primaryColor: $primaryColor)';
return 'ApplicationSelectorState(locale: $locale, themeMode: $themeMode, primaryColor: $primaryColor, prueBlack: $prueBlack)';
}
@override
@@ -756,11 +780,14 @@ class _$ApplicationSelectorStateImpl implements _ApplicationSelectorState {
(identical(other.themeMode, themeMode) ||
other.themeMode == themeMode) &&
(identical(other.primaryColor, primaryColor) ||
other.primaryColor == primaryColor));
other.primaryColor == primaryColor) &&
(identical(other.prueBlack, prueBlack) ||
other.prueBlack == prueBlack));
}
@override
int get hashCode => Object.hash(runtimeType, locale, themeMode, primaryColor);
int get hashCode =>
Object.hash(runtimeType, locale, themeMode, primaryColor, prueBlack);
@JsonKey(ignore: true)
@override
@@ -772,9 +799,10 @@ class _$ApplicationSelectorStateImpl implements _ApplicationSelectorState {
abstract class _ApplicationSelectorState implements ApplicationSelectorState {
const factory _ApplicationSelectorState(
{final String? locale,
final ThemeMode? themeMode,
final int? primaryColor}) = _$ApplicationSelectorStateImpl;
{required final String? locale,
required final ThemeMode? themeMode,
required final int? primaryColor,
required final bool prueBlack}) = _$ApplicationSelectorStateImpl;
@override
String? get locale;
@@ -783,6 +811,8 @@ abstract class _ApplicationSelectorState implements ApplicationSelectorState {
@override
int? get primaryColor;
@override
bool get prueBlack;
@override
@JsonKey(ignore: true)
_$$ApplicationSelectorStateImplCopyWith<_$ApplicationSelectorStateImpl>
get copyWith => throw _privateConstructorUsedError;

View File

@@ -41,9 +41,10 @@ class ProfilesSelectorState with _$ProfilesSelectorState {
@freezed
class ApplicationSelectorState with _$ApplicationSelectorState {
const factory ApplicationSelectorState({
String? locale,
ThemeMode? themeMode,
int? primaryColor,
required String? locale,
required ThemeMode? themeMode,
required int? primaryColor,
required bool prueBlack,
}) = _ApplicationSelectorState;
}

View File

@@ -12,15 +12,15 @@ class SystemColorSchemes {
});
getSystemColorSchemeForBrightness(Brightness? brightness) {
if (brightness != null && brightness == Brightness.dark) {
if (brightness == Brightness.dark) {
return darkColorScheme != null
? ColorScheme.fromSeed(
seedColor: darkColorScheme!.primary,
brightness: brightness,
brightness: Brightness.dark,
)
: ColorScheme.fromSeed(
seedColor: defaultPrimaryColor,
brightness: brightness,
brightness: Brightness.dark,
);
}
return lightColorScheme != null

View File

@@ -111,6 +111,15 @@ class GlobalState {
config: config,
clashConfig: clashConfig,
);
clashCore.setState(
CoreState(
accessControl: config.isAccessControl ? config.accessControl : null,
allowBypass: config.allowBypass,
systemProxy: config.systemProxy,
mixedPort: clashConfig.mixedPort,
onlyProxy: config.onlyProxy,
),
);
}
updateCoreVersionInfo(appState);
}

View File

@@ -1,14 +1,11 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'side_sheet.dart';
showExtendPage(
BuildContext context, {
showExtendPage(BuildContext context, {
required Widget body,
required String title,
double? extendPageWidth,
@@ -20,35 +17,31 @@ showExtendPage(
key: globalKey,
child: body,
);
final isMobile = globalState.appController.appState.viewMode ==
ViewMode.mobile;
navigator.push(
ModalSideSheetRoute(
modalBarrierColor: Colors.black38,
builder: (context) => Selector<AppState, double>(
selector: (_, appState) => appState.viewWidth,
builder: (_, viewWidth, __) {
final isMobile =
globalState.appController.appState.viewMode == ViewMode.mobile;
final commonScaffold = CommonScaffold(
automaticallyImplyLeading: isMobile ? true : false,
actions: isMobile
? null
: [
const SizedBox(
height: kToolbarHeight,
width: kToolbarHeight,
child: CloseButton(),
),
],
title: title,
body: uniqueBody,
);
return AnimatedContainer(
duration: kThemeAnimationDuration,
width: isMobile ? viewWidth : extendPageWidth ?? 300,
child: commonScaffold,
);
},
),
builder: (context) {
final commonScaffold = CommonScaffold(
automaticallyImplyLeading: isMobile ? true : false,
actions: isMobile
? null
: [
const SizedBox(
height: kToolbarHeight,
width: kToolbarHeight,
child: CloseButton(),
),
],
title: title,
body: uniqueBody,
);
return SizedBox(
width: isMobile ? context.width : extendPageWidth ?? 300,
child: commonScaffold,
);
},
constraints: const BoxConstraints(),
filter: filter,
),