mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-11 21:31:40 +08:00
@@ -1,4 +1,4 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:convert' show utf8;
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:async' show FutureOr;
|
||||
import 'dart:io' show Platform, Directory, File;
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/services.dart' show rootBundle;
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
abstract final class AssetUtils {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
abstract final class FavUtils {
|
||||
abstract final class BiliUtils {
|
||||
static bool isDefaultFav(int? attr) {
|
||||
if (attr == null) {
|
||||
return false;
|
||||
@@ -16,4 +16,13 @@ abstract final class FavUtils {
|
||||
static bool isPublicFav(int attr) {
|
||||
return (attr & 1) == 0;
|
||||
}
|
||||
|
||||
static bool isCustomFollowTag(int? tagid) {
|
||||
return tagid != null && tagid != 0 && tagid != -10 && tagid != -2;
|
||||
}
|
||||
|
||||
static String levelName(
|
||||
Object level, {
|
||||
bool isSeniorMember = false,
|
||||
}) => 'assets/images/lv/lv${isSeniorMember ? '6_s' : level}.png';
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:io' show Directory, File;
|
||||
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart' show Offset, Size;
|
||||
import 'package:screen_retriever/screen_retriever.dart';
|
||||
|
||||
Future<Offset> calcWindowPosition(Size windowSize) async {
|
||||
|
||||
17
lib/utils/color_utils.dart
Normal file
17
lib/utils/color_utils.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:flutter/rendering.dart' show Color;
|
||||
|
||||
abstract final class ColourUtils {
|
||||
static Color parseColor(String color) =>
|
||||
Color(int.parse('FF${color.substring(1)}', radix: 16));
|
||||
|
||||
static Color parseMedalColor(String color) => Color(
|
||||
int.parse('${color.substring(7)}${color.substring(1, 7)}', radix: 16),
|
||||
);
|
||||
|
||||
static Color index2Color(int index, Color color) => switch (index) {
|
||||
0 => const Color(0xFFfdad13),
|
||||
1 => const Color(0xFF8aace1),
|
||||
2 => const Color(0xFFdfa777),
|
||||
_ => color,
|
||||
};
|
||||
}
|
||||
15
lib/utils/connectivity_utils.dart
Normal file
15
lib/utils/connectivity_utils.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
|
||||
abstract final class ConnectivityUtils {
|
||||
static Future<bool> get isWiFi async {
|
||||
try {
|
||||
return PlatformUtils.isMobile &&
|
||||
(await Connectivity().checkConnectivity()).contains(
|
||||
ConnectivityResult.wifi,
|
||||
);
|
||||
} catch (_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
lib/utils/device_utils.dart
Normal file
21
lib/utils/device_utils.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/widgets.dart' show WidgetsBinding, Size;
|
||||
|
||||
abstract final class DeviceUtils {
|
||||
static late int sdkInt;
|
||||
|
||||
static bool get isTablet {
|
||||
return size.shortestSide >= 600;
|
||||
}
|
||||
|
||||
static Size get size {
|
||||
final view = WidgetsBinding.instance.platformDispatcher.views.first;
|
||||
return view.physicalSize / view.devicePixelRatio;
|
||||
}
|
||||
|
||||
static String get platformName => PlatformUtils.isDesktop
|
||||
? 'desktop'
|
||||
: isTablet
|
||||
? 'pad'
|
||||
: 'phone';
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// from Getx
|
||||
@@ -72,10 +71,4 @@ extension ContextExtensions on BuildContext {
|
||||
|
||||
/// True if the current device is Tablet
|
||||
bool get isTablet => isSmallTablet || isLargeTablet;
|
||||
|
||||
String get platformName => PlatformUtils.isDesktop
|
||||
? 'desktop'
|
||||
: isTablet
|
||||
? 'pad'
|
||||
: 'phone';
|
||||
}
|
||||
|
||||
@@ -3,3 +3,16 @@ import 'package:PiliPlus/grpc/bilibili/app/archive/v1.pb.dart' show Dimension;
|
||||
extension DimensionExt on Dimension {
|
||||
bool get isVertical => rotate == .ONE ? width > height : height > width;
|
||||
}
|
||||
|
||||
extension StringExt on String {
|
||||
bool get isVerticalFromUri {
|
||||
try {
|
||||
final params = Uri.parse(this).queryParameters;
|
||||
final width = int.parse(params['player_width']!);
|
||||
final height = int.parse(params['player_height']!);
|
||||
return params['player_rotate'] == '1' ? width > height : height > width;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'dart:io';
|
||||
import 'dart:io' show FileSystemEntity, Directory;
|
||||
|
||||
extension FileSystemEntityExt on FileSystemEntity {
|
||||
Future<void> tryDel({bool recursive = false}) async {
|
||||
|
||||
@@ -6,10 +6,10 @@ extension GetExt on GetInterface {
|
||||
GetInstance().putOrFind(dep, tag: tag);
|
||||
|
||||
void updateMyAppTheme() {
|
||||
final (l, d) = MyApp.getAllTheme();
|
||||
final (light, dark) = MyApp.getAllTheme();
|
||||
rootController
|
||||
..theme = l
|
||||
..darkTheme = d
|
||||
..theme = light
|
||||
..darkTheme = dark
|
||||
..update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:math' show pow;
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/widgets.dart' show BuildContext, MediaQuery;
|
||||
|
||||
extension ImageExtension on num {
|
||||
int? cacheSize(BuildContext context) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/widgets.dart' show ScrollController, Curves;
|
||||
|
||||
extension ScrollControllerExt on ScrollController {
|
||||
void animToTop() => animTo(0);
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import 'package:flex_seed_scheme/flex_seed_scheme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart'
|
||||
show ThemeData, Color, ColorScheme, Brightness, Colors;
|
||||
|
||||
const _pinkLight = Color(0xFFFF6699);
|
||||
const _pinkDark = Color(0xFFD44E7D);
|
||||
|
||||
extension ThemeDataExt on ThemeData {
|
||||
bool get isLight => brightness.isLight;
|
||||
|
||||
bool get isDark => brightness.isDark;
|
||||
}
|
||||
|
||||
extension ColorSchemeExt on ColorScheme {
|
||||
Color get vipColor => brightness.isLight ? _pinkLight : _pinkDark;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// ignore_for_file: constant_identifier_names, non_constant_identifier_names
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:convert' show ascii, base64;
|
||||
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:uuid/v4.dart';
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import 'dart:io';
|
||||
import 'dart:io' show File, Platform;
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
import 'dart:typed_data' show Uint8List;
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/utils/device_utils.dart';
|
||||
import 'package:PiliPlus/utils/extension/file_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/string_ext.dart';
|
||||
import 'package:PiliPlus/utils/global_data.dart';
|
||||
import 'package:PiliPlus/utils/path_utils.dart';
|
||||
import 'package:PiliPlus/utils/permission_handler.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:PiliPlus/utils/share_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
@@ -40,7 +42,7 @@ abstract final class ImageUtils {
|
||||
.share(
|
||||
ShareParams(
|
||||
files: [XFile(path)],
|
||||
sharePositionOrigin: await Utils.sharePositionOrigin,
|
||||
sharePositionOrigin: await ShareUtils.sharePositionOrigin,
|
||||
),
|
||||
)
|
||||
.whenComplete(File(path).tryDel);
|
||||
@@ -80,7 +82,7 @@ abstract final class ImageUtils {
|
||||
|
||||
static Future<bool> checkPermissionDependOnSdkInt() {
|
||||
if (Platform.isAndroid) {
|
||||
if (Utils.sdkInt < 29) {
|
||||
if (DeviceUtils.sdkInt < 29) {
|
||||
return requestPer();
|
||||
} else {
|
||||
return Future.syncValue(true);
|
||||
|
||||
6
lib/utils/parse_int.dart
Normal file
6
lib/utils/parse_int.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
int? safeToInt(dynamic value) => switch (value) {
|
||||
int() => value,
|
||||
String() => int.tryParse(value),
|
||||
num() => value.toInt(),
|
||||
_ => null,
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
String? noneNullOrEmptyString(String? value) {
|
||||
String? nonNullOrEmptyString(String? value) {
|
||||
if (value == null || value.isEmpty) return null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import 'package:PiliPlus/models/common/reply/reply_sort_type.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/theme_ext.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/theme_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -98,6 +100,7 @@ abstract final class ReplyUtils {
|
||||
await Future.delayed(const Duration(seconds: 8));
|
||||
}
|
||||
void showReplyCheckResult(String message, {bool isBan = false}) {
|
||||
final theme = ThemeUtils.theme;
|
||||
final actions = [
|
||||
if (isBan)
|
||||
TextButton(
|
||||
@@ -117,7 +120,7 @@ abstract final class ReplyUtils {
|
||||
'/webview',
|
||||
parameters: {
|
||||
'url':
|
||||
'https://www.bilibili.com/h5/comment/appeal?${Utils.themeUrl(Get.isDarkMode)}',
|
||||
'https://www.bilibili.com/h5/comment/appeal?${ThemeUtils.themeUrl(theme.isDark)}',
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -128,7 +131,7 @@ abstract final class ReplyUtils {
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'关闭',
|
||||
style: TextStyle(color: Get.theme.colorScheme.outline),
|
||||
style: TextStyle(color: theme.colorScheme.outline),
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
@@ -30,11 +30,13 @@ import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/extension/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/size_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/string_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/theme_ext.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:PiliPlus/utils/theme_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -344,6 +346,7 @@ abstract final class RequestUtils {
|
||||
clearCookie: true,
|
||||
);
|
||||
final isSuccess = res.isSuccess;
|
||||
final theme = ThemeUtils.theme;
|
||||
final actions = [
|
||||
if (!isSuccess)
|
||||
TextButton(
|
||||
@@ -354,7 +357,7 @@ abstract final class RequestUtils {
|
||||
'/webview',
|
||||
parameters: {
|
||||
'url':
|
||||
'https://www.bilibili.com/h5/comment/appeal?${Utils.themeUrl(Get.isDarkMode)}',
|
||||
'https://www.bilibili.com/h5/comment/appeal?${ThemeUtils.themeUrl(theme.isDark)}',
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -365,7 +368,7 @@ abstract final class RequestUtils {
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'关闭',
|
||||
style: TextStyle(color: Get.theme.colorScheme.outline),
|
||||
style: TextStyle(color: theme.colorScheme.outline),
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
41
lib/utils/share_utils.dart
Normal file
41
lib/utils/share_utils.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:PiliPlus/utils/device_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/rendering.dart' show Rect;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
abstract final class ShareUtils {
|
||||
static bool? _isIpad;
|
||||
static Future<bool> get isIpad async {
|
||||
if (!Platform.isIOS) return false;
|
||||
return _isIpad ??= (await DeviceInfoPlugin().iosInfo).model
|
||||
.toLowerCase()
|
||||
.contains('ipad');
|
||||
}
|
||||
|
||||
static Future<Rect?> get sharePositionOrigin async {
|
||||
if (await isIpad) {
|
||||
final screenSize = DeviceUtils.size;
|
||||
return Rect.fromLTRB(0, 0, screenSize.width, screenSize.height / 2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<void> shareText(String text) async {
|
||||
if (PlatformUtils.isDesktop) {
|
||||
Utils.copyText(text);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(text: text, sharePositionOrigin: await sharePositionOrigin),
|
||||
);
|
||||
} catch (e) {
|
||||
SmartDialog.showToast(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import 'package:PiliPlus/plugin/pl_player/models/bottom_progress_behavior.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/models/fullscreen_mode.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/models/hwdec_type.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart';
|
||||
import 'package:PiliPlus/utils/device_utils.dart';
|
||||
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
|
||||
import 'package:PiliPlus/utils/global_data.dart';
|
||||
import 'package:PiliPlus/utils/login_utils.dart';
|
||||
@@ -192,7 +193,9 @@ abstract final class Pref {
|
||||
static FullScreenMode get fullScreenMode {
|
||||
int? index = _setting.get(SettingBoxKey.fullScreenMode);
|
||||
if (index == null) {
|
||||
final FullScreenMode mode = horizontalScreen && isTablet ? .none : .auto;
|
||||
final FullScreenMode mode = horizontalScreen && DeviceUtils.isTablet
|
||||
? .none
|
||||
: .auto;
|
||||
_setting.put(SettingBoxKey.fullScreenMode, mode.index);
|
||||
return mode;
|
||||
}
|
||||
@@ -594,15 +597,14 @@ abstract final class Pref {
|
||||
static bool get optTabletNav =>
|
||||
_setting.get(SettingBoxKey.optTabletNav, defaultValue: true);
|
||||
|
||||
static bool get horizontalScreen =>
|
||||
_setting.get(SettingBoxKey.horizontalScreen) ?? isTablet;
|
||||
|
||||
static bool get isTablet {
|
||||
final view = WidgetsBinding.instance.platformDispatcher.views.first;
|
||||
final size = view.physicalSize / view.devicePixelRatio;
|
||||
final isTablet = size.shortestSide >= 600;
|
||||
_setting.put(SettingBoxKey.horizontalScreen, isTablet);
|
||||
return isTablet;
|
||||
static bool get horizontalScreen {
|
||||
bool? horizontalScreen = _setting.get(SettingBoxKey.horizontalScreen);
|
||||
if (horizontalScreen == null) {
|
||||
final isTablet = DeviceUtils.isTablet;
|
||||
_setting.put(SettingBoxKey.horizontalScreen, isTablet);
|
||||
return isTablet;
|
||||
}
|
||||
return horizontalScreen;
|
||||
}
|
||||
|
||||
static String get banWordForDyn =>
|
||||
|
||||
34
lib/utils/storage_utils.dart
Normal file
34
lib/utils/storage_utils.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'dart:io' show File;
|
||||
import 'dart:typed_data' show Uint8List;
|
||||
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
abstract final class StorageUtils {
|
||||
static Future<void> saveBytes2File({
|
||||
required String name,
|
||||
required Uint8List bytes,
|
||||
required List<String> allowedExtensions,
|
||||
FileType type = FileType.custom,
|
||||
}) async {
|
||||
try {
|
||||
final path = await FilePicker.saveFile(
|
||||
allowedExtensions: allowedExtensions,
|
||||
type: type,
|
||||
fileName: name,
|
||||
bytes: PlatformUtils.isDesktop ? null : bytes,
|
||||
);
|
||||
if (path == null) {
|
||||
SmartDialog.showToast("取消保存");
|
||||
return;
|
||||
}
|
||||
if (PlatformUtils.isDesktop) {
|
||||
await File(path).writeAsBytes(bytes);
|
||||
}
|
||||
SmartDialog.showToast("已保存");
|
||||
} catch (e) {
|
||||
SmartDialog.showToast("保存失败: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,31 @@
|
||||
import 'package:PiliPlus/common/style.dart';
|
||||
import 'package:PiliPlus/main.dart';
|
||||
import 'package:PiliPlus/utils/extension/theme_ext.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/cupertino.dart' show CupertinoThemeData;
|
||||
import 'package:flutter/foundation.dart' show PlatformDispatcher;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract final class ThemeUtils {
|
||||
static late ThemeData lightTheme;
|
||||
|
||||
static late ThemeData darkTheme;
|
||||
|
||||
static late ThemeMode themeMode;
|
||||
|
||||
static ThemeData get theme {
|
||||
if (themeMode == .dark ||
|
||||
(themeMode == .system &&
|
||||
PlatformDispatcher.instance.platformBrightness == .dark)) {
|
||||
return darkTheme;
|
||||
}
|
||||
return lightTheme;
|
||||
}
|
||||
|
||||
static bool get isDarkMode => theme.isDark;
|
||||
|
||||
static String themeUrl(bool isDark) =>
|
||||
'native.theme=${isDark ? 2 : 1}&night=${isDark ? 1 : 0}';
|
||||
|
||||
static ThemeData getThemeData({
|
||||
required ColorScheme colorScheme,
|
||||
required bool isDynamic,
|
||||
@@ -134,9 +154,6 @@ abstract final class ThemeUtils {
|
||||
if (Pref.isPureBlackTheme) {
|
||||
themeData = darkenTheme(themeData);
|
||||
}
|
||||
if (Pref.darkVideoPage) {
|
||||
MyApp.darkThemeData = themeData;
|
||||
}
|
||||
}
|
||||
return themeData;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode, debugPrint;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
abstract final class UrlUtils {
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:convert' show JsonEncoder, base64;
|
||||
import 'dart:math' show Random;
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:catcher_2/catcher_2.dart';
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/services.dart'
|
||||
show Clipboard, ClipboardData, MethodChannel;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
abstract final class Utils {
|
||||
static final random = Random();
|
||||
@@ -21,119 +14,6 @@ abstract final class Utils {
|
||||
|
||||
static const jsonEncoder = JsonEncoder.withIndent(' ');
|
||||
|
||||
static bool isCustomFollowTag(int? tagid) {
|
||||
return tagid != null && tagid != 0 && tagid != -10 && tagid != -2;
|
||||
}
|
||||
|
||||
static String levelName(
|
||||
Object level, {
|
||||
bool isSeniorMember = false,
|
||||
}) => 'assets/images/lv/lv${isSeniorMember ? '6_s' : level}.png';
|
||||
|
||||
static Color index2Color(int index, Color color) => switch (index) {
|
||||
0 => const Color(0xFFfdad13),
|
||||
1 => const Color(0xFF8aace1),
|
||||
2 => const Color(0xFFdfa777),
|
||||
_ => color,
|
||||
};
|
||||
|
||||
static bool getDimensionFromUri(String uri) {
|
||||
try {
|
||||
final params = Uri.parse(uri).queryParameters;
|
||||
final width = int.parse(params['player_width']!);
|
||||
final height = int.parse(params['player_height']!);
|
||||
return params['player_rotate'] == '1' ? width > height : height > width;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static String themeUrl(bool isDark) =>
|
||||
'native.theme=${isDark ? 2 : 1}&night=${isDark ? 1 : 0}';
|
||||
|
||||
static Future<void> saveBytes2File({
|
||||
required String name,
|
||||
required Uint8List bytes,
|
||||
required List<String> allowedExtensions,
|
||||
FileType type = FileType.custom,
|
||||
}) async {
|
||||
try {
|
||||
final path = await FilePicker.saveFile(
|
||||
allowedExtensions: allowedExtensions,
|
||||
type: type,
|
||||
fileName: name,
|
||||
bytes: PlatformUtils.isDesktop ? null : bytes,
|
||||
);
|
||||
if (path == null) {
|
||||
SmartDialog.showToast("取消保存");
|
||||
return;
|
||||
}
|
||||
if (PlatformUtils.isDesktop) {
|
||||
await File(path).writeAsBytes(bytes);
|
||||
}
|
||||
SmartDialog.showToast("已保存");
|
||||
} catch (e) {
|
||||
SmartDialog.showToast("保存失败: $e");
|
||||
}
|
||||
}
|
||||
|
||||
static int? safeToInt(dynamic value) => switch (value) {
|
||||
int e => e,
|
||||
String e => int.tryParse(e),
|
||||
num e => e.toInt(),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
static Future<bool> get isWiFi async {
|
||||
try {
|
||||
return PlatformUtils.isMobile &&
|
||||
(await Connectivity().checkConnectivity()).contains(
|
||||
ConnectivityResult.wifi,
|
||||
);
|
||||
} catch (_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static Color parseColor(String color) =>
|
||||
Color(int.parse('FF${color.substring(1)}', radix: 16));
|
||||
|
||||
static Color parseMedalColor(String color) => Color(
|
||||
int.parse('${color.substring(7)}${color.substring(1, 7)}', radix: 16),
|
||||
);
|
||||
|
||||
static late int sdkInt;
|
||||
|
||||
static bool? _isIpad;
|
||||
static Future<bool> get isIpad async {
|
||||
if (!Platform.isIOS) return false;
|
||||
return _isIpad ??= (await DeviceInfoPlugin().iosInfo).model
|
||||
.toLowerCase()
|
||||
.contains('ipad');
|
||||
}
|
||||
|
||||
static Future<Rect?> get sharePositionOrigin async {
|
||||
if (await isIpad) {
|
||||
final size = Get.size;
|
||||
return Rect.fromLTRB(0, 0, size.width, size.height / 2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<void> shareText(String text) async {
|
||||
if (PlatformUtils.isDesktop) {
|
||||
copyText(text);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await SharePlus.instance.share(
|
||||
ShareParams(text: text, sharePositionOrigin: await sharePositionOrigin),
|
||||
);
|
||||
} catch (e) {
|
||||
SmartDialog.showToast(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
static final numericRegex = RegExp(r'^[\d\.]+$');
|
||||
static bool isStringNumeric(String str) {
|
||||
return numericRegex.hasMatch(str);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:PiliPlus/models/common/video/cdn_type.dart';
|
||||
import 'package:PiliPlus/models_new/live/live_room_play_info/codec.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode, debugPrint;
|
||||
|
||||
abstract final class VideoUtils {
|
||||
static CDNService cdnService = Pref.defaultCDNService;
|
||||
|
||||
Reference in New Issue
Block a user