Files
PiliPlus/lib/main.dart
My-Responsitories ce5e85e64b tweaks (#1780)
* opt: sized

* fix: self send

* feat: ctrl enter to send

* opt: checked

* opt: download notifier

* opt: Future.syncValue

* mod: account

* mod: loading state

* opt: DebounceStreamMixin

* opt: report

* opt: enum map

* opt: file handler

* opt: dyn color

* opt: Uint8List subview

* opt: FileExt

* opt: computeLuminance

* opt: isNullOrEmpty

* opt: Get context

* update [skip ci]

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

* opt dynamicColor [skip ci]

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

* fixes [skip ci]

* update

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

* update

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

---------

Signed-off-by: My-Responsitories <107370289+My-Responsitories@users.noreply.github.com>
Co-authored-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-12-17 17:01:10 +08:00

411 lines
13 KiB
Dart

import 'dart:io';
import 'package:PiliPlus/build_config.dart';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/custom_toast.dart';
import 'package:PiliPlus/common/widgets/mouse_back.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/models/common/theme/theme_color_type.dart';
import 'package:PiliPlus/plugin/pl_player/controller.dart';
import 'package:PiliPlus/router/app_pages.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/services/download/download_service.dart';
import 'package:PiliPlus/services/service_locator.dart';
import 'package:PiliPlus/utils/app_scheme.dart';
import 'package:PiliPlus/utils/cache_manager.dart';
import 'package:PiliPlus/utils/calc_window_position.dart';
import 'package:PiliPlus/utils/date_utils.dart';
import 'package:PiliPlus/utils/json_file_handler.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/path_utils.dart';
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:PiliPlus/utils/request_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:catcher_2/catcher_2.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:flex_seed_scheme/flex_seed_scheme.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart' show PointerDeviceKind;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:media_kit/media_kit.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:window_manager/window_manager.dart' hide calcWindowPosition;
WebViewEnvironment? webViewEnvironment;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized();
tmpDirPath = (await getTemporaryDirectory()).path;
appSupportDirPath = (await getApplicationSupportDirectory()).path;
try {
await GStorage.init();
} catch (e) {
await Utils.copyText(e.toString());
if (kDebugMode) debugPrint('GStorage init error: $e');
exit(0);
}
if (PlatformUtils.isDesktop) {
final customDownPath = Pref.downloadPath;
if (customDownPath != null && customDownPath.isNotEmpty) {
try {
final dir = Directory(customDownPath);
if (!dir.existsSync()) {
await dir.create(recursive: true);
}
downloadPath = customDownPath;
} catch (e) {
downloadPath = defDownloadPath;
await GStorage.setting.delete(SettingBoxKey.downloadPath);
if (kDebugMode) {
debugPrint('download path error: $e');
}
}
} else {
downloadPath = defDownloadPath;
}
} else if (Platform.isAndroid) {
final externalStorageDirPath = (await getExternalStorageDirectory())?.path;
downloadPath = externalStorageDirPath != null
? path.join(externalStorageDirPath, PathUtils.downloadDir)
: defDownloadPath;
} else {
downloadPath = defDownloadPath;
}
Get
..lazyPut(AccountService.new)
..lazyPut(DownloadService.new);
HttpOverrides.global = _CustomHttpOverrides();
CacheManager.autoClearCache();
if (PlatformUtils.isMobile) {
await Future.wait([
SystemChrome.setPreferredOrientations(
[
DeviceOrientation.portraitUp,
if (Pref.horizontalScreen) ...[
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
],
],
),
setupServiceLocator(),
]);
}
if (Platform.isWindows) {
if (await WebViewEnvironment.getAvailableVersion() != null) {
webViewEnvironment = await WebViewEnvironment.create(
settings: WebViewEnvironmentSettings(
userDataFolder: path.join(appSupportDirPath, 'flutter_inappwebview'),
),
);
}
}
Request();
Request.setCookie();
RequestUtils.syncHistoryStatus();
SmartDialog.config.toast = SmartConfigToast(
displayType: SmartToastType.onlyRefresh,
);
if (PlatformUtils.isMobile) {
PiliScheme.init();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
systemNavigationBarColor: Colors.transparent,
systemNavigationBarDividerColor: Colors.transparent,
statusBarColor: Colors.transparent,
systemNavigationBarContrastEnforced: false,
),
);
if (Platform.isAndroid) {
FlutterDisplayMode.supported.then((mode) {
final String? storageDisplay = GStorage.setting.get(
SettingBoxKey.displayMode,
);
DisplayMode? displayMode;
if (storageDisplay != null) {
displayMode = mode.firstWhereOrNull(
(e) => e.toString() == storageDisplay,
);
}
FlutterDisplayMode.setPreferredMode(displayMode ?? DisplayMode.auto);
});
}
} else if (PlatformUtils.isDesktop) {
await windowManager.ensureInitialized();
final windowOptions = WindowOptions(
minimumSize: const Size(400, 720),
skipTaskbar: false,
titleBarStyle: Pref.showWindowTitleBar
? TitleBarStyle.normal
: TitleBarStyle.hidden,
title: Constants.appName,
);
windowManager.waitUntilReadyToShow(windowOptions, () async {
final windowSize = Pref.windowSize;
await windowManager.setBounds(
await calcWindowPosition(windowSize) & windowSize,
);
if (Pref.isWindowMaximized) await windowManager.maximize();
await windowManager.show();
await windowManager.focus();
});
}
if (Pref.dynamicColor) {
await MyApp.initPlatformState();
}
if (Pref.enableLog) {
// 异常捕获 logo记录
final customParameters = {
'BuildConfig':
'\nBuild Time: ${DateFormatUtils.format(BuildConfig.buildTime, format: DateFormatUtils.longFormatDs)}\n'
'Commit Hash: ${BuildConfig.commitHash}',
};
final fileHandler = await JsonFileHandler.init();
final Catcher2Options debugConfig = Catcher2Options(
SilentReportMode(),
[
?fileHandler,
ConsoleHandler(
enableDeviceParameters: false,
enableApplicationParameters: false,
enableCustomParameters: true,
),
],
customParameters: customParameters,
);
final Catcher2Options releaseConfig = Catcher2Options(
SilentReportMode(),
[
?fileHandler,
ConsoleHandler(enableCustomParameters: true),
],
customParameters: customParameters,
);
Catcher2(
debugConfig: debugConfig,
releaseConfig: releaseConfig,
rootWidget: const MyApp(),
);
} else {
runApp(const MyApp());
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
static ColorScheme? _light, _dark;
static ThemeData? darkThemeData;
static void _onBack() {
if (SmartDialog.checkExist()) {
SmartDialog.dismiss();
return;
}
if (Get.isDialogOpen ?? Get.isBottomSheetOpen ?? false) {
Get.back();
return;
}
final plCtr = PlPlayerController.instance;
if (plCtr != null) {
if (plCtr.isFullScreen.value) {
plCtr
..triggerFullScreen(status: false)
..controlsLock.value = false
..showControls.value = false;
return;
}
if (plCtr.isDesktopPip) {
plCtr
..exitDesktopPip().whenComplete(
() => plCtr.initialFocalPoint = Offset.zero,
)
..controlsLock.value = false
..showControls.value = false;
return;
}
}
Get.back();
}
@override
Widget build(BuildContext context) {
final dynamicColor = Pref.dynamicColor && _light != null && _dark != null;
late final brandColor = colorThemeTypes[Pref.customColor].color;
late final variant = FlexSchemeVariant.values[Pref.schemeVariant];
return GetMaterialApp(
title: Constants.appName,
theme: ThemeUtils.getThemeData(
colorScheme: dynamicColor
? _light!
: SeedColorScheme.fromSeeds(
variant: variant,
primaryKey: brandColor,
brightness: Brightness.light,
useExpressiveOnContainerColors: false,
),
isDynamic: dynamicColor,
),
darkTheme: ThemeUtils.getThemeData(
isDark: true,
colorScheme: dynamicColor
? _dark!
: SeedColorScheme.fromSeeds(
variant: variant,
primaryKey: brandColor,
brightness: Brightness.dark,
useExpressiveOnContainerColors: false,
),
isDynamic: dynamicColor,
),
themeMode: Pref.themeMode,
localizationsDelegates: const [
GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
locale: const Locale("zh", "CN"),
fallbackLocale: const Locale("zh", "CN"),
supportedLocales: const [Locale("zh", "CN"), Locale("en", "US")],
initialRoute: '/',
getPages: Routes.getPages,
defaultTransition: Pref.pageTransition,
builder: FlutterSmartDialog.init(
toastBuilder: (String msg) => CustomToast(msg: msg),
loadingBuilder: (msg) => LoadingWidget(msg: msg),
builder: (context, child) {
child = MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(Pref.defaultTextScale),
),
child: child!,
);
if (PlatformUtils.isDesktop) {
return Focus(
canRequestFocus: false,
onKeyEvent: (_, event) {
if (event.logicalKey == LogicalKeyboardKey.escape &&
event is KeyDownEvent) {
_onBack();
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
child: MouseBackDetector(
onTapDown: _onBack,
child: child,
),
);
}
return child;
},
),
navigatorObservers: [
PageUtils.routeObserver,
FlutterSmartDialog.observer,
],
scrollBehavior: const MaterialScrollBehavior().copyWith(
scrollbars: false,
dragDevices: {
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.invertedStylus,
PointerDeviceKind.trackpad,
PointerDeviceKind.unknown,
if (PlatformUtils.isDesktop) PointerDeviceKind.mouse,
},
),
);
}
/// from [DynamicColorBuilderState.initPlatformState]
static Future<void> initPlatformState() async {
if (_light != null || _dark != null) return;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
final corePalette = await DynamicColorPlugin.getCorePalette();
if (corePalette != null) {
if (kDebugMode) {
debugPrint('dynamic_color: Core palette detected.');
}
_light = corePalette.toColorScheme();
_dark = corePalette.toColorScheme(brightness: Brightness.dark);
return;
}
} on PlatformException {
if (kDebugMode) {
debugPrint('dynamic_color: Failed to obtain core palette.');
}
}
try {
final Color? accentColor = await DynamicColorPlugin.getAccentColor();
if (accentColor != null) {
if (kDebugMode) {
debugPrint('dynamic_color: Accent color detected.');
}
_light = ColorScheme.fromSeed(
seedColor: accentColor,
brightness: Brightness.light,
);
_dark = ColorScheme.fromSeed(
seedColor: accentColor,
brightness: Brightness.dark,
);
return;
}
} on PlatformException {
if (kDebugMode) {
debugPrint('dynamic_color: Failed to obtain accent color.');
}
}
if (kDebugMode) {
debugPrint('dynamic_color: Dynamic color not detected on this device.');
}
}
}
class _CustomHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
final client = super.createHttpClient(context)
// ..maxConnectionsPerHost = 32
..idleTimeout = const Duration(seconds: 15);
if (kDebugMode || Pref.badCertificateCallback) {
client.badCertificateCallback = (cert, host, port) => true;
}
return client;
}
}