fix(linux): tray icon not showing #541

upgrade old packages
This commit is contained in:
Kingkor Roy Tirtho 2024-04-17 22:20:13 +06:00
parent 6907f9c756
commit 7ac791757a
48 changed files with 840 additions and 722 deletions

View File

@ -1,4 +1,4 @@
{
"flutterSdkVersion": "3.19.1",
"flutterSdkVersion": "3.19.5",
"flavors": {}
}

View File

@ -4,7 +4,7 @@ on:
inputs:
version:
description: Version to publish (x.x.x)
default: 3.1.0
default: 3.6.0
required: true
dry_run:
description: Dry run

View File

@ -26,7 +26,7 @@ on:
default: true
env:
FLUTTER_VERSION: '3.19.1'
FLUTTER_VERSION: '3.19.5'
jobs:
windows:

View File

@ -1,5 +1,5 @@
import 'package:envied/envied.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:spotube/utils/platform.dart';
part 'env.g.dart';
@ -26,7 +26,7 @@ abstract class Env {
static final String _enableUpdateChecker = _Env._enableUpdateChecker;
static bool get enableUpdateChecker =>
DesktopTools.platform.isFlatpak || _enableUpdateChecker == "1";
kIsFlatpak || _enableUpdateChecker == "1";
static String discordAppId = "1176718791388975124";
}

View File

@ -1,9 +1,10 @@
import 'dart:io';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:spotube/utils/platform.dart';
import 'package:win32_registry/win32_registry.dart';
Future<void> registerWindowsScheme(String scheme) async {
if (!DesktopTools.platform.isWindows) return;
if (!kIsWindows) return;
String appPath = Platform.resolvedExecutable;
String protocolRegKey = 'Software\\Classes\\$scheme';

View File

@ -1,6 +1,5 @@
import 'dart:ui';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -24,6 +23,7 @@ import 'package:spotube/provider/user_preferences/user_preferences_provider.dart
import 'package:spotube/provider/user_preferences/user_preferences_state.dart';
import 'package:spotube/provider/volume_provider.dart';
import 'package:spotube/utils/platform.dart';
import 'package:window_manager/window_manager.dart';
class BottomPlayer extends HookConsumerWidget {
BottomPlayer({super.key});
@ -95,19 +95,19 @@ class BottomPlayer extends HookConsumerWidget {
tooltip: context.l10n.mini_player,
icon: const Icon(SpotubeIcons.miniPlayer),
onPressed: () async {
final prevSize =
await DesktopTools.window.getSize();
await DesktopTools.window.setMinimumSize(
if (!kIsDesktop) return;
final prevSize = await windowManager.getSize();
await windowManager.setMinimumSize(
const Size(300, 300),
);
await DesktopTools.window.setAlwaysOnTop(true);
await windowManager.setAlwaysOnTop(true);
if (!kIsLinux) {
await DesktopTools.window.setHasShadow(false);
await windowManager.setHasShadow(false);
}
await DesktopTools.window
await windowManager
.setAlignment(Alignment.topRight);
await DesktopTools.window
.setSize(const Size(400, 500));
await windowManager.setSize(const Size(400, 500));
await Future.delayed(
const Duration(milliseconds: 100),
() async {

View File

@ -1,7 +1,7 @@
import 'package:draggable_scrollbar/draggable_scrollbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:spotube/utils/platform.dart';
class InterScrollbar extends HookWidget {
final Widget child;
@ -15,7 +15,7 @@ class InterScrollbar extends HookWidget {
@override
Widget build(BuildContext context) {
if (DesktopTools.platform.isDesktop) return child;
if (kIsDesktop) return child;
return DraggableScrollbar.semicircle(
controller: controller,

View File

@ -7,7 +7,8 @@ import 'package:titlebar_buttons/titlebar_buttons.dart';
import 'dart:math';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:io' show Platform;
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:window_manager/window_manager.dart';
class PageWindowTitleBar extends StatefulHookConsumerWidget
implements PreferredSizeWidget {
@ -89,7 +90,7 @@ class _PageWindowTitleBarState extends ConsumerState<PageWindowTitleBar> {
final systemTitleBar =
ref.read(userPreferencesProvider.select((s) => s.systemTitleBar));
if (kIsDesktop && !systemTitleBar) {
DesktopTools.window.startDragging();
windowManager.startDragging();
}
}
@ -107,11 +108,7 @@ class _PageWindowTitleBarState extends ConsumerState<PageWindowTitleBar> {
return SliverPadding(
padding: EdgeInsets.only(
left: DesktopTools.platform.isMacOS &&
hasFullscreen &&
hasLeadingOrCanPop
? 65
: 0,
left: kIsMacOS && hasFullscreen && hasLeadingOrCanPop ? 65 : 0,
),
sliver: SliverAppBar(
leading: widget.leading,
@ -149,11 +146,7 @@ class _PageWindowTitleBarState extends ConsumerState<PageWindowTitleBar> {
onVerticalDragStart: onDrag,
child: Padding(
padding: EdgeInsets.only(
left: DesktopTools.platform.isMacOS &&
hasFullscreen &&
hasLeadingOrCanPop
? 65
: 0,
left: kIsMacOS && hasFullscreen && hasLeadingOrCanPop ? 65 : 0,
),
child: AppBar(
leading: widget.leading,
@ -193,12 +186,12 @@ class WindowTitleBarButtons extends HookConsumerWidget {
const type = ThemeType.auto;
Future<void> onClose() async {
await DesktopTools.window.close();
await windowManager.close();
}
useEffect(() {
if (kIsDesktop) {
DesktopTools.window.isMaximized().then((value) {
windowManager.isMaximized().then((value) {
isMaximized.value = value;
});
}
@ -235,14 +228,14 @@ class WindowTitleBarButtons extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MinimizeWindowButton(
onPressed: DesktopTools.window.minimize,
onPressed: windowManager.minimize,
colors: colors,
),
if (isMaximized.value != true)
MaximizeWindowButton(
colors: colors,
onPressed: () {
DesktopTools.window.maximize();
windowManager.maximize();
isMaximized.value = true;
},
)
@ -250,7 +243,7 @@ class WindowTitleBarButtons extends HookConsumerWidget {
RestoreWindowButton(
colors: colors,
onPressed: () {
DesktopTools.window.unmaximize();
windowManager.unmaximize();
isMaximized.value = false;
},
),
@ -270,16 +263,16 @@ class WindowTitleBarButtons extends HookConsumerWidget {
children: [
DecoratedMinimizeButton(
type: type,
onPressed: DesktopTools.window.minimize,
onPressed: windowManager.minimize,
),
DecoratedMaximizeButton(
type: type,
onPressed: () async {
if (await DesktopTools.window.isMaximized()) {
await DesktopTools.window.unmaximize();
if (await windowManager.isMaximized()) {
await windowManager.unmaximize();
isMaximized.value = false;
} else {
await DesktopTools.window.maximize();
await windowManager.maximize();
isMaximized.value = true;
}
},

View File

@ -1,7 +1,7 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
@ -12,6 +12,7 @@ import 'package:spotube/components/shared/tracks_view/track_view_props.dart';
import 'package:gap/gap.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/hooks/utils/use_palette_color.dart';
import 'package:spotube/utils/platform.dart';
class TrackViewFlexHeader extends HookConsumerWidget {
const TrackViewFlexHeader({super.key});
@ -53,7 +54,7 @@ class TrackViewFlexHeader extends HookConsumerWidget {
floating: false,
pinned: true,
expandedHeight: 450,
automaticallyImplyLeading: DesktopTools.platform.isMobile,
automaticallyImplyLeading: kIsMobile,
backgroundColor: palette.color,
title: isExpanded ? null : Text(props.title, style: headingStyle),
flexibleSpace: FlexibleSpaceBar(

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:sliver_tools/sliver_tools.dart';
@ -8,6 +8,7 @@ import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/components/shared/tracks_view/sections/header/flexible_header.dart';
import 'package:spotube/components/shared/tracks_view/sections/body/track_view_body.dart';
import 'package:spotube/components/shared/tracks_view/track_view_props.dart';
import 'package:spotube/utils/platform.dart';
class TrackView extends HookConsumerWidget {
const TrackView({super.key});
@ -18,7 +19,7 @@ class TrackView extends HookConsumerWidget {
final controller = useScrollController();
return Scaffold(
appBar: DesktopTools.platform.isDesktop
appBar: kIsDesktop
? const PageWindowTitleBar(
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,

View File

@ -1,29 +1,31 @@
import 'dart:io';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/hooks/configurators/use_window_listener.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/provider/user_preferences/user_preferences_state.dart';
// ignore: depend_on_referenced_packages
import 'package:local_notifier/local_notifier.dart';
import 'package:spotube/utils/platform.dart';
import 'package:window_manager/window_manager.dart';
final closeNotification = DesktopTools.createNotification(
title: 'Spotube',
message: 'Running in background. Minimized to System Tray',
actions: [
LocalNotificationAction(text: 'Close The App'),
],
)?..onClickAction = (value) {
exit(0);
};
final closeNotification = !kIsDesktop
? null
: (LocalNotification(
title: 'Spotube',
body: 'Running in background. Minimized to System Tray',
actions: [
LocalNotificationAction(text: 'Close The App'),
],
)..onClickAction = (value) {
exit(0);
});
void useCloseBehavior(WidgetRef ref) {
useWindowListener(
onWindowClose: () async {
final preferences = ref.read(userPreferencesProvider);
if (preferences.closeBehavior == CloseBehavior.minimizeToTray) {
await DesktopTools.window.hide();
await windowManager.hide();
closeNotification?.show();
} else {
exit(0);

View File

@ -7,7 +7,7 @@ import 'package:spotube/collections/routes.dart';
import 'package:spotube/provider/spotify_provider.dart';
import 'package:flutter_sharing_intent/flutter_sharing_intent.dart';
import 'package:flutter_sharing_intent/model/sharing_file.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:spotube/utils/platform.dart';
final appLinks = AppLinks();
final linkStream = appLinks.allStringLinkStream.asBroadcastStream();
@ -53,7 +53,7 @@ void useDeepLinking(WidgetRef ref) {
StreamSubscription? mediaStream;
if (DesktopTools.platform.isMobile) {
if (kIsMobile) {
FlutterSharingIntent.instance.getInitialSharing().then(uriListener);
mediaStream =

View File

@ -1,12 +1,12 @@
import 'package:disable_battery_optimization/disable_battery_optimization.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:spotube/hooks/utils/use_async_effect.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/utils/platform.dart';
void useDisableBatteryOptimizations() {
useAsyncEffect(() async {
if (!DesktopTools.platform.isAndroid ||
KVStoreService.askedForBatteryOptimization) return;
if (!kIsAndroid || KVStoreService.askedForBatteryOptimization) return;
await DisableBatteryOptimization.showDisableBatteryOptimizationSettings();

View File

@ -1,17 +1,18 @@
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:spotube/components/library/user_local_tracks.dart';
import 'package:spotube/hooks/utils/use_async_effect.dart';
import 'package:spotube/utils/platform.dart';
void useGetStoragePermissions(WidgetRef ref) {
final context = useContext();
useAsyncEffect(
() async {
if (!DesktopTools.platform.isMobile) return;
if (!kIsMobile) return;
final androidInfo = await DeviceInfoPlugin().androidInfo;

View File

@ -1,128 +0,0 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/intents.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
void useInitSysTray(WidgetRef ref) {
final context = useContext();
final systemTray = useRef<SystemTray?>(null);
final initializeMenu = useCallback(() async {
systemTray.value?.destroy();
final playlist = ref.read(proxyPlaylistProvider);
final playlistQueue = ref.read(proxyPlaylistProvider.notifier);
final preferences = ref.read(userPreferencesProvider);
if (!preferences.showSystemTrayIcon) {
await systemTray.value?.destroy();
systemTray.value = null;
return;
}
final enabled = !playlist.isFetching;
systemTray.value = await DesktopTools.createSystemTrayMenu(
title: DesktopTools.platform.isWindows ? "Spotube" : "",
iconPath: "assets/spotube-logo.png",
windowsIconPath: "assets/spotube-logo.ico",
items: [
MenuItemLabel(
label: "Show/Hide",
name: "show-hide",
onClicked: (item) async {
if (await DesktopTools.window.isVisible()) {
await DesktopTools.window.hide();
} else {
await DesktopTools.window.show();
}
},
),
MenuSeparator(),
MenuItemLabel(
label: "Play/Pause",
name: "play-pause",
enabled: enabled,
onClicked: (_) async {
Actions.maybeInvoke<PlayPauseIntent>(
context, PlayPauseIntent(ref)) ??
PlayPauseAction().invoke(PlayPauseIntent(ref));
},
),
MenuItemLabel(
label: "Next",
name: "next",
enabled: enabled && (playlist.tracks.length) > 1,
onClicked: (p0) async {
await playlistQueue.next();
},
),
MenuItemLabel(
label: "Previous",
name: "previous",
enabled: enabled && (playlist.tracks.length) > 1,
onClicked: (p0) async {
await playlistQueue.previous();
},
),
MenuSeparator(),
MenuItemLabel(
label: "Quit",
name: "quit",
onClicked: (item) async {
exit(0);
},
),
],
onEvent: (event, tray) async {
if (DesktopTools.platform.isWindows) {
switch (event) {
case SystemTrayEvent.click:
await DesktopTools.window.show();
break;
case SystemTrayEvent.rightClick:
await tray.popUpContextMenu();
break;
default:
}
} else {
switch (event) {
case SystemTrayEvent.rightClick:
await DesktopTools.window.show();
break;
case SystemTrayEvent.click:
await tray.popUpContextMenu();
break;
default:
}
}
},
);
}, [ref]);
useReassemble(initializeMenu);
ref.listen<ProxyPlaylist?>(
proxyPlaylistProvider,
(previous, next) {
initializeMenu();
},
);
ref.listen(
userPreferencesProvider.select((s) => s.showSystemTrayIcon),
(previous, next) {
initializeMenu();
},
);
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((_) {
initializeMenu();
});
return () async {
await systemTray.value?.destroy();
};
}, [initializeMenu]);
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:spotube/utils/platform.dart';
import 'package:window_manager/window_manager.dart';
class CallbackWindowListener implements WindowListener {
final VoidCallback? _onWindowClose;
@ -154,6 +156,8 @@ void useWindowListener({
VoidCallback? onWindowEvent,
}) {
useEffect(() {
if (!kIsDesktop) return null;
final listener = CallbackWindowListener(
onWindowClose: onWindowClose,
onWindowFocus: onWindowFocus,
@ -172,9 +176,9 @@ void useWindowListener({
onWindowUndocked: onWindowUndocked,
onWindowEvent: onWindowEvent,
);
DesktopTools.window.addListener(listener);
windowManager.addListener(listener);
return () {
DesktopTools.window.removeListener(listener);
windowManager.removeListener(listener);
};
}, [
onWindowClose,

View File

@ -4,11 +4,11 @@ import 'package:device_preview/device_preview.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:hive/hive.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:local_notifier/local_notifier.dart';
import 'package:media_kit/media_kit.dart';
import 'package:metadata_god/metadata_god.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -19,6 +19,7 @@ import 'package:spotube/hooks/configurators/use_close_behavior.dart';
import 'package:spotube/hooks/configurators/use_deep_linking.dart';
import 'package:spotube/hooks/configurators/use_disable_battery_optimizations.dart';
import 'package:spotube/hooks/configurators/use_get_storage_perms.dart';
import 'package:spotube/provider/tray_manager/tray_manager.dart';
import 'package:spotube/l10n/l10n.dart';
import 'package:spotube/models/logger.dart';
import 'package:spotube/models/skip_segment.dart';
@ -31,15 +32,17 @@ import 'package:spotube/provider/user_preferences/user_preferences_provider.dart
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/cli/cli.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/services/wm_tools/wm_tools.dart';
import 'package:spotube/themes/theme.dart';
import 'package:spotube/utils/persisted_state_notifier.dart';
import 'package:spotube/utils/platform.dart';
import 'package:system_theme/system_theme.dart';
import 'package:path_provider/path_provider.dart';
import 'package:spotube/hooks/configurators/use_init_sys_tray.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:window_manager/window_manager.dart';
Future<void> main(List<String> rawArgs) async {
final arguments = await startCLI(rawArgs);
@ -55,12 +58,12 @@ Future<void> main(List<String> rawArgs) async {
MediaKit.ensureInitialized();
// force High Refresh Rate on some Android devices (like One Plus)
if (DesktopTools.platform.isAndroid) {
if (kIsAndroid) {
await FlutterDisplayMode.setHighRefreshRate();
}
if (DesktopTools.platform.isDesktop) {
await DesktopTools.window.setPreventClose(true);
if (kIsDesktop) {
await windowManager.setPreventClose(true);
}
await SystemTheme.accentColor.load();
@ -69,7 +72,7 @@ Future<void> main(List<String> rawArgs) async {
MetadataGod.initialize();
}
if (DesktopTools.platform.isWindows || DesktopTools.platform.isLinux) {
if (kIsWindows || kIsLinux) {
DiscordRPC.initialize();
}
@ -101,14 +104,10 @@ Future<void> main(List<String> rawArgs) async {
path: hiveCacheDir,
);
await DesktopTools.ensureInitialized(
DesktopWindowOptions(
hideTitleBar: true,
title: "Spotube",
backgroundColor: Colors.transparent,
minimumSize: const Size(300, 700),
),
);
if (kIsDesktop) {
await localNotifier.setup(appName: "Spotube");
await WindowManagerTools.initialize();
}
Catcher2(
enableLogger: arguments["verbose"],
@ -189,9 +188,9 @@ class SpotubeState extends ConsumerState<Spotube> {
ref.listen(playbackServerProvider, (_, __) {});
ref.listen(connectServerProvider, (_, __) {});
ref.listen(connectClientsProvider, (_, __) {});
ref.listen(trayManagerProvider, (_, __) {});
useDisableBatteryOptimizations();
useInitSysTray(ref);
useDeepLinking(ref);
useCloseBehavior(ref);
useGetStoragePermissions(ref);
@ -233,9 +232,7 @@ class SpotubeState extends ConsumerState<Spotube> {
builder: (context, child) {
return DevicePreview.appBuilder(
context,
DesktopTools.platform.isDesktop && !DesktopTools.platform.isMacOS
? DragToResizeArea(child: child!)
: child,
kIsDesktop && !kIsMacOS ? DragToResizeArea(child: child!) : child,
);
},
themeMode: themeMode,

View File

@ -12,7 +12,7 @@ part of 'connect.dart';
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
WebSocketLoadEventData _$WebSocketLoadEventDataFromJson(
Map<String, dynamic> json) {

View File

@ -12,7 +12,7 @@ part of 'home_feed.dart';
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
SpotifySectionPlaylist _$SpotifySectionPlaylistFromJson(
Map<String, dynamic> json) {

View File

@ -12,7 +12,7 @@ part of 'recommendation_seeds.dart';
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$GeneratePlaylistProviderInput {

View File

@ -12,7 +12,7 @@ import 'package:spotube/components/shared/waypoint.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/provider/spotify/spotify.dart';
import 'package:collection/collection.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:spotube/utils/platform.dart';
class GenrePlaylistsPage extends HookConsumerWidget {
final Category category;
@ -27,7 +27,7 @@ class GenrePlaylistsPage extends HookConsumerWidget {
final scrollController = useScrollController();
return Scaffold(
appBar: DesktopTools.platform.isDesktop
appBar: kIsDesktop
? const PageWindowTitleBar(
leading: BackButton(color: Colors.white),
backgroundColor: Colors.transparent,
@ -53,12 +53,12 @@ class GenrePlaylistsPage extends HookConsumerWidget {
controller: scrollController,
slivers: [
SliverAppBar(
automaticallyImplyLeading: DesktopTools.platform.isMobile,
automaticallyImplyLeading: kIsMobile,
expandedHeight: mediaQuery.mdAndDown ? 200 : 150,
title: const Text(""),
backgroundColor: Colors.transparent,
flexibleSpace: FlexibleSpaceBar(
centerTitle: DesktopTools.platform.isDesktop,
centerTitle: kIsDesktop,
title: Text(
category.name!,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
@ -18,6 +17,7 @@ import 'package:spotube/pages/lyrics/synced_lyrics.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/utils/platform.dart';
import 'package:window_manager/window_manager.dart';
class MiniLyricsPage extends HookConsumerWidget {
final Size prevSize;
@ -36,9 +36,11 @@ class MiniLyricsPage extends HookConsumerWidget {
final showLyrics = useState(true);
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
wasMaximized.value = await DesktopTools.window.isMaximized();
});
if (kIsDesktop) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
wasMaximized.value = await windowManager.isMaximized();
});
}
return null;
}, []);
@ -112,11 +114,13 @@ class MiniLyricsPage extends HookConsumerWidget {
areaActive.value = true;
hoverMode.value = false;
await DesktopTools.window.setSize(
showLyrics.value
? const Size(400, 500)
: const Size(400, 150),
);
if (kIsDesktop) {
await windowManager.setSize(
showLyrics.value
? const Size(400, 500)
: const Size(400, 150),
);
}
},
),
IconButton(
@ -135,33 +139,34 @@ class MiniLyricsPage extends HookConsumerWidget {
hoverMode.value = !hoverMode.value;
},
),
FutureBuilder(
future: DesktopTools.window.isAlwaysOnTop(),
builder: (context, snapshot) {
return IconButton(
tooltip: context.l10n.always_on_top,
icon: Icon(
snapshot.data == true
? SpotubeIcons.pinOn
: SpotubeIcons.pinOff,
),
style: ButtonStyle(
foregroundColor: snapshot.data == true
? MaterialStateProperty.all(
theme.colorScheme.primary)
: null,
),
onPressed: snapshot.data == null
? null
: () async {
await DesktopTools.window.setAlwaysOnTop(
snapshot.data == true ? false : true,
);
update();
},
);
},
),
if (kIsDesktop)
FutureBuilder(
future: windowManager.isAlwaysOnTop(),
builder: (context, snapshot) {
return IconButton(
tooltip: context.l10n.always_on_top,
icon: Icon(
snapshot.data == true
? SpotubeIcons.pinOn
: SpotubeIcons.pinOff,
),
style: ButtonStyle(
foregroundColor: snapshot.data == true
? MaterialStateProperty.all(
theme.colorScheme.primary)
: null,
),
onPressed: snapshot.data == null
? null
: () async {
await windowManager.setAlwaysOnTop(
snapshot.data == true ? false : true,
);
update();
},
);
},
),
],
),
),
@ -243,19 +248,20 @@ class MiniLyricsPage extends HookConsumerWidget {
tooltip: context.l10n.exit_mini_player,
icon: const Icon(SpotubeIcons.maximize),
onPressed: () async {
if (!kIsDesktop) return;
try {
await DesktopTools.window
await windowManager
.setMinimumSize(const Size(300, 700));
await DesktopTools.window.setAlwaysOnTop(false);
await windowManager.setAlwaysOnTop(false);
if (wasMaximized.value) {
await DesktopTools.window.maximize();
await windowManager.maximize();
} else {
await DesktopTools.window.setSize(prevSize);
await windowManager.setSize(prevSize);
}
await DesktopTools.window
.setAlignment(Alignment.center);
await windowManager.setAlignment(Alignment.center);
if (!kIsLinux) {
await DesktopTools.window.setHasShadow(true);
await windowManager.setHasShadow(true);
}
await Future.delayed(
const Duration(milliseconds: 200));

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -21,6 +20,7 @@ import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/services/connectivity_adapter.dart';
import 'package:spotube/utils/persisted_state_notifier.dart';
import 'package:spotube/utils/platform.dart';
const rootPaths = {
"/": 0,
@ -206,7 +206,7 @@ class RootApp extends HookConsumerWidget {
),
extendBody: true,
drawerScrimColor: Colors.transparent,
endDrawer: DesktopTools.platform.isDesktop
endDrawer: kIsDesktop
? Container(
constraints: const BoxConstraints(maxWidth: 800),
decoration: BoxDecoration(

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/spotube_icons.dart';
@ -8,6 +7,7 @@ import 'package:spotube/components/shared/adaptive/adaptive_select_tile.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/provider/user_preferences/user_preferences_state.dart';
import 'package:spotube/utils/platform.dart';
class SettingsDesktopSection extends HookConsumerWidget {
const SettingsDesktopSection({super.key});
@ -53,7 +53,7 @@ class SettingsDesktopSection extends HookConsumerWidget {
value: preferences.systemTitleBar,
onChanged: preferencesNotifier.setSystemTitleBar,
),
if (!DesktopTools.platform.isMacOS)
if (!kIsMacOS)
SwitchListTile(
secondary: const Icon(SpotubeIcons.discord),
title: Text(context.l10n.discord_rich_presence),

View File

@ -1,13 +1,13 @@
import 'package:file_picker/file_picker.dart';
import 'package:file_selector/file_selector.dart';
import 'package:flutter/material.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/settings/section_card_with_heading.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/utils/platform.dart';
class SettingsDownloadsSection extends HookConsumerWidget {
const SettingsDownloadsSection({super.key});
@ -18,7 +18,7 @@ class SettingsDownloadsSection extends HookConsumerWidget {
final preferences = ref.watch(userPreferencesProvider);
final pickDownloadLocation = useCallback(() async {
if (DesktopTools.platform.isMobile || DesktopTools.platform.isMacOS) {
if (kIsMobile || kIsMacOS) {
final dirStr = await FilePicker.platform.getDirectoryPath(
initialDirectory: preferences.downloadLocation,
);

View File

@ -1,6 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart';
@ -14,6 +13,7 @@ import 'package:spotube/pages/settings/sections/downloads.dart';
import 'package:spotube/pages/settings/sections/language_region.dart';
import 'package:spotube/pages/settings/sections/playback.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/utils/platform.dart';
class SettingsPage extends HookConsumerWidget {
const SettingsPage({super.key});
@ -45,8 +45,7 @@ class SettingsPage extends HookConsumerWidget {
const SettingsAppearanceSection(),
const SettingsPlaybackSection(),
const SettingsDownloadsSection(),
if (DesktopTools.platform.isDesktop)
const SettingsDesktopSection(),
if (kIsDesktop) const SettingsDesktopSection(),
if (!kIsWeb) const SettingsDevelopersSection(),
const SettingsAboutSection(),
Center(

View File

@ -1,21 +1,19 @@
import 'package:dart_discord_rpc/dart_discord_rpc.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/env.dart';
import 'package:spotube/extensions/artist_simple.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/utils/platform.dart';
class Discord extends ChangeNotifier {
final DiscordRPC? discordRPC;
final bool isEnabled;
Discord(this.isEnabled)
: discordRPC = (DesktopTools.platform.isWindows ||
DesktopTools.platform.isLinux) &&
isEnabled
: discordRPC = (kIsWindows || kIsLinux) && isEnabled
? DiscordRPC(applicationId: Env.discordAppId)
: null {
discordRPC?.start(autoRegister: true);

View File

@ -0,0 +1,79 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/provider/tray_manager/tray_menu.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/utils/platform.dart';
import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart';
class SystemTrayManager with TrayListener {
final Ref ref;
final bool enabled;
SystemTrayManager(
this.ref, {
required this.enabled,
}) {
initialize();
}
Future<void> initialize() async {
if (!kIsDesktop) return;
if (enabled) {
await trayManager.setIcon(
kIsWindows
? 'assets/spotube-logo.ico'
: kIsFlatpak
? 'com.github.KRTirtho.Spotube.png'
: 'assets/spotube-logo.png',
);
trayManager.addListener(this);
} else {
await trayManager.destroy();
}
}
void dispose() {
trayManager.removeListener(this);
}
@override
onTrayIconMouseDown() {
if (kIsWindows) {
windowManager.show();
} else {
trayManager.popUpContextMenu();
}
}
@override
onTrayIconRightMouseDown() {
if (!kIsWindows) {
windowManager.show();
} else {
trayManager.popUpContextMenu();
}
}
}
final trayManagerProvider = Provider(
(ref) {
final enabled = ref.watch(
userPreferencesProvider.select((s) => s.showSystemTrayIcon),
);
ref.listen(trayMenuProvider, (_, menu) {
if (!enabled || !kIsDesktop) return;
trayManager.setContextMenu(menu);
});
final manager = SystemTrayManager(
ref,
enabled: enabled,
);
ref.onDispose(manager.dispose);
return manager;
},
);

View File

@ -0,0 +1,108 @@
import 'dart:io';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/audio_player/loop_mode.dart';
import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart';
final audioPlayerLoopMode = StreamProvider<PlaybackLoopMode>((ref) {
return audioPlayer.loopModeStream;
});
final audioPlayerShuffleMode = StreamProvider<bool>((ref) {
return audioPlayer.shuffledStream;
});
final audioPlayerPlaying = StreamProvider<bool>((ref) {
return audioPlayer.playingStream;
});
final trayMenuProvider = Provider((ref) {
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
final isPlaybackPlaying =
ref.watch(proxyPlaylistProvider.select((s) => s.activeTrack != null));
final isLoopOne =
ref.watch(audioPlayerLoopMode).asData?.value == PlaybackLoopMode.one;
final isShuffled = ref.watch(audioPlayerShuffleMode).asData?.value ?? false;
final isPlaying = ref.watch(audioPlayerPlaying).asData?.value ?? false;
return Menu(
items: [
MenuItem(
label: "Show/Hide Window",
onClick: (menuItem) async {
if (await windowManager.isVisible()) {
await windowManager.hide();
} else {
await windowManager.focus();
await windowManager.show();
}
},
),
MenuItem.separator(),
MenuItem(
label: isPlaying ? "Pause" : "Play",
disabled: !isPlaybackPlaying,
onClick: (menuItem) async {
if (audioPlayer.isPlaying) {
await audioPlayer.pause();
} else {
await audioPlayer.resume();
}
},
),
MenuItem(
label: "Next",
disabled: !isPlaybackPlaying,
onClick: (menuItem) {
playlistNotifier.next();
},
),
MenuItem(
label: "Previous",
disabled: !isPlaybackPlaying,
onClick: (menuItem) {
playlistNotifier.previous();
},
),
MenuItem.submenu(
label: "Playback",
submenu: Menu(
items: [
MenuItem(
label: "Repeat",
checked: isLoopOne,
onClick: (menuItem) {
audioPlayer.setLoopMode(
isLoopOne ? PlaybackLoopMode.none : PlaybackLoopMode.one,
);
},
),
MenuItem(
label: "Shuffle",
checked: isShuffled,
onClick: (menuItem) {
audioPlayer.setShuffle(!isShuffled);
},
),
MenuItem.separator(),
MenuItem(
label: "Stop",
onClick: (menuItem) {
playlistNotifier.stop();
},
),
],
),
),
MenuItem.separator(),
MenuItem(
label: "Quit",
onClick: (menuItem) {
exit(0);
},
),
],
);
});

View File

@ -1,7 +1,6 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:path_provider/path_provider.dart';
import 'package:spotify/spotify.dart';
@ -15,6 +14,7 @@ import 'package:spotube/services/sourced_track/enums.dart';
import 'package:spotube/utils/persisted_state_notifier.dart';
import 'package:spotube/utils/platform.dart';
import 'package:path/path.dart' as path;
import 'package:window_manager/window_manager.dart';
class UserPreferencesNotifier extends PersistedStateNotifier<UserPreferences> {
final Ref ref;
@ -103,8 +103,8 @@ class UserPreferencesNotifier extends PersistedStateNotifier<UserPreferences> {
void setSystemTitleBar(bool isSystemTitleBar) {
state = state.copyWith(systemTitleBar: isSystemTitleBar);
if (DesktopTools.platform.isDesktop) {
DesktopTools.window.setTitleBarStyle(
if (kIsDesktop) {
windowManager.setTitleBarStyle(
isSystemTitleBar ? TitleBarStyle.normal : TitleBarStyle.hidden,
);
}
@ -151,8 +151,8 @@ class UserPreferencesNotifier extends PersistedStateNotifier<UserPreferences> {
);
}
if (DesktopTools.platform.isDesktop) {
await DesktopTools.window.setTitleBarStyle(
if (kIsDesktop) {
await windowManager.setTitleBarStyle(
state.systemTitleBar ? TitleBarStyle.normal : TitleBarStyle.hidden,
);
}

View File

@ -62,10 +62,10 @@ class UserPreferences with _$UserPreferences {
@Default(false) bool amoledDarkTheme,
@Default(true) bool checkUpdate,
@Default(false) bool normalizeAudio,
@Default(true) bool showSystemTrayIcon,
@Default(false) bool showSystemTrayIcon,
@Default(false) bool skipNonMusic,
@Default(false) bool systemTitleBar,
@Default(CloseBehavior.minimizeToTray) CloseBehavior closeBehavior,
@Default(CloseBehavior.close) CloseBehavior closeBehavior,
@Default(SpotubeColor(0xFF2196F3, name: "Blue"))
@JsonKey(
fromJson: UserPreferences._accentColorSchemeFromJson,

View File

@ -12,7 +12,7 @@ part of 'user_preferences_state.dart';
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
UserPreferences _$UserPreferencesFromJson(Map<String, dynamic> json) {
return _UserPreferences.fromJson(json);
@ -415,10 +415,10 @@ class _$UserPreferencesImpl implements _UserPreferences {
this.amoledDarkTheme = false,
this.checkUpdate = true,
this.normalizeAudio = false,
this.showSystemTrayIcon = true,
this.showSystemTrayIcon = false,
this.skipNonMusic = false,
this.systemTitleBar = false,
this.closeBehavior = CloseBehavior.minimizeToTray,
this.closeBehavior = CloseBehavior.close,
@JsonKey(
fromJson: UserPreferences._accentColorSchemeFromJson,
toJson: UserPreferences._accentColorSchemeToJson,

View File

@ -16,12 +16,12 @@ _$UserPreferencesImpl _$$UserPreferencesImplFromJson(
amoledDarkTheme: json['amoledDarkTheme'] as bool? ?? false,
checkUpdate: json['checkUpdate'] as bool? ?? true,
normalizeAudio: json['normalizeAudio'] as bool? ?? false,
showSystemTrayIcon: json['showSystemTrayIcon'] as bool? ?? true,
showSystemTrayIcon: json['showSystemTrayIcon'] as bool? ?? false,
skipNonMusic: json['skipNonMusic'] as bool? ?? false,
systemTitleBar: json['systemTitleBar'] as bool? ?? false,
closeBehavior:
$enumDecodeNullable(_$CloseBehaviorEnumMap, json['closeBehavior']) ??
CloseBehavior.minimizeToTray,
CloseBehavior.close,
accentColorScheme: UserPreferences._accentColorSchemeReadValue(
json, 'accentColorScheme') ==
null

View File

@ -101,7 +101,7 @@ abstract class AudioPlayerInterface {
return _mkPlayer.state.completed;
}
Future<bool> get isShuffled async {
bool get isShuffled {
return _mkPlayer.shuffled;
}

View File

@ -1,5 +1,4 @@
import 'dart:async';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:catcher_2/catcher_2.dart';
import 'package:media_kit/media_kit.dart';
import 'package:flutter_broadcasts/flutter_broadcasts.dart';
@ -7,6 +6,7 @@ import 'package:package_info_plus/package_info_plus.dart';
import 'package:audio_session/audio_session.dart';
// ignore: implementation_imports
import 'package:spotube/services/audio_player/playback_state.dart';
import 'package:spotube/utils/platform.dart';
/// MediaKit [Player] by default doesn't have a state stream.
/// This class adds a state stream to the [Player] class.
@ -54,7 +54,7 @@ class CustomPlayer extends Player {
PackageInfo.fromPlatform().then((packageInfo) {
_packageName = packageInfo.packageName;
});
if (DesktopTools.platform.isAndroid) {
if (kIsAndroid) {
_androidAudioManager = AndroidAudioManager();
AudioSession.instance.then((s) async {
_androidAudioSessionId =
@ -71,7 +71,7 @@ class CustomPlayer extends Player {
}
Future<void> notifyAudioSessionUpdate(bool active) async {
if (DesktopTools.platform.isAndroid) {
if (kIsAndroid) {
sendBroadcast(
BroadcastMessage(
name: active

View File

@ -1,5 +1,4 @@
import 'package:audio_service/audio_service.dart';
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/extensions/artist_simple.dart';
@ -8,6 +7,7 @@ import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/services/audio_services/mobile_audio_service.dart';
import 'package:spotube/services/audio_services/windows_audio_service.dart';
import 'package:spotube/services/sourced_track/sourced_track.dart';
import 'package:spotube/utils/platform.dart';
class AudioServices {
final MobileAudioService? mobile;
@ -19,9 +19,7 @@ class AudioServices {
Ref ref,
ProxyPlaylistNotifier playback,
) async {
final mobile = DesktopTools.platform.isMobile ||
DesktopTools.platform.isMacOS ||
DesktopTools.platform.isLinux
final mobile = kIsMobile || kIsMacOS || kIsLinux
? await AudioService.init(
builder: () => MobileAudioService(playback),
config: const AudioServiceConfig(
@ -31,9 +29,7 @@ class AudioServices {
),
)
: null;
final smtc = DesktopTools.platform.isWindows
? WindowsAudioService(ref, playback)
: null;
final smtc = kIsWindows ? WindowsAudioService(ref, playback) : null;
return AudioServices(
mobile,

View File

@ -1,4 +1,7 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotube/services/wm_tools/wm_tools.dart';
abstract class KVStoreService {
static SharedPreferences? _sharedPreferences;
@ -23,4 +26,21 @@ abstract class KVStoreService {
static Future<void> setRecentSearches(List<String> value) async =>
await sharedPreferences.setStringList('recentSearches', value);
static WindowSize? get windowSize {
final raw = sharedPreferences.getString('windowSize');
if (raw == null) {
return null;
}
return WindowSize.fromJson(jsonDecode(raw));
}
static Future<void> setWindowSize(WindowSize value) async =>
await sharedPreferences.setString(
'windowSize',
jsonEncode(
value.toJson(),
),
);
}

View File

@ -12,7 +12,7 @@ part of 'song_link.dart';
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
SongLink _$SongLinkFromJson(Map<String, dynamic> json) {
return _SongLink.fromJson(json);

View File

@ -163,7 +163,7 @@ class PipedSourcedTrack extends SourcedTrack {
final PipedSearchResult(items: searchResults) = await pipedClient.search(
query,
preference.searchMode == SearchMode.youtube
? PipedFilter.videos
? PipedFilter.video
: PipedFilter.musicSongs,
);

View File

@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/utils/platform.dart';
import 'package:window_manager/window_manager.dart';
class WindowSize {
final double height;
final double width;
final bool maximized;
WindowSize({
required this.height,
required this.width,
required this.maximized,
});
factory WindowSize.fromJson(Map<String, dynamic> json) => WindowSize(
height: json["height"],
width: json["width"],
maximized: json["maximized"],
);
Map<String, dynamic> toJson() => {
"height": height,
"width": width,
"maximized": maximized,
};
}
class WindowManagerTools with WidgetsBindingObserver {
static WindowManagerTools? _instance;
static WindowManagerTools get instance => _instance!;
WindowManagerTools._();
static Future<void> initialize() async {
await windowManager.ensureInitialized();
_instance = WindowManagerTools._();
WidgetsBinding.instance.addObserver(instance);
await windowManager.waitUntilReadyToShow(
const WindowOptions(
title: "Spotube",
backgroundColor: Colors.transparent,
minimumSize: Size(300, 700),
titleBarStyle: TitleBarStyle.hidden,
),
() async {
final savedSize = KVStoreService.windowSize;
await windowManager.setResizable(true);
if (savedSize?.maximized == true &&
!(await windowManager.isMaximized())) {
await windowManager.maximize();
} else if (savedSize != null) {
await windowManager.setSize(Size(savedSize.width, savedSize.height));
}
await windowManager.focus();
await windowManager.show();
},
);
}
Size? _prevSize;
@override
void didChangeMetrics() async {
super.didChangeMetrics();
if (kIsMobile) return;
final size = await windowManager.getSize();
final windowSameDimension =
_prevSize?.width == size.width && _prevSize?.height == size.height;
if (windowSameDimension || _prevSize == null) {
_prevSize = size;
return;
}
final isMaximized = await windowManager.isMaximized();
await KVStoreService.setWindowSize(
WindowSize(
height: size.height,
width: size.width,
maximized: isMaximized,
),
);
_prevSize = size;
}
}

View File

@ -14,7 +14,7 @@
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <system_theme/system_theme_plugin.h>
#include <system_tray/system_tray_plugin.h>
#include <tray_manager/tray_manager_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <window_manager/window_manager_plugin.h>
#include <window_size/window_size_plugin.h>
@ -44,9 +44,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) system_theme_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "SystemThemePlugin");
system_theme_plugin_register_with_registrar(system_theme_registrar);
g_autoptr(FlPluginRegistrar) system_tray_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "SystemTrayPlugin");
system_tray_plugin_register_with_registrar(system_tray_registrar);
g_autoptr(FlPluginRegistrar) tray_manager_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin");
tray_manager_plugin_register_with_registrar(tray_manager_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);

View File

@ -11,7 +11,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
media_kit_libs_linux
screen_retriever
system_theme
system_tray
tray_manager
url_launcher_linux
window_manager
window_size

View File

@ -21,7 +21,7 @@ import screen_retriever
import shared_preferences_foundation
import sqflite
import system_theme
import system_tray
import tray_manager
import url_launcher_macos
import window_manager
import window_size
@ -37,13 +37,13 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin"))
MediaKitLibsMacosAudioPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosAudioPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
SystemThemePlugin.register(with: registry.registrar(forPlugin: "SystemThemePlugin"))
SystemTrayPlugin.register(with: registry.registrar(forPlugin: "SystemTrayPlugin"))
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
WindowSizePlugin.register(with: registry.registrar(forPlugin: "WindowSizePlugin"))

View File

@ -44,7 +44,7 @@ PODS:
- FMDB (>= 2.7.5)
- system_theme (0.0.1):
- FlutterMacOS
- system_tray (0.0.1):
- tray_manager (0.0.1):
- FlutterMacOS
- url_launcher_macos (0.0.1):
- FlutterMacOS
@ -73,7 +73,7 @@ DEPENDENCIES:
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
- system_theme (from `Flutter/ephemeral/.symlinks/plugins/system_theme/macos`)
- system_tray (from `Flutter/ephemeral/.symlinks/plugins/system_tray/macos`)
- tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
- window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`)
@ -122,8 +122,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
system_theme:
:path: Flutter/ephemeral/.symlinks/plugins/system_theme/macos
system_tray:
:path: Flutter/ephemeral/.symlinks/plugins/system_tray/macos
tray_manager:
:path: Flutter/ephemeral/.symlinks/plugins/tray_manager/macos
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
window_manager:
@ -132,11 +132,11 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/window_size/macos
SPEC CHECKSUMS:
app_links: 4481ed4d71f384b0c3ae5016f4633aa73d32ff67
app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a
audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9
audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72
bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720
file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9
flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
@ -147,13 +147,13 @@ SPEC CHECKSUMS:
media_kit_native_event_loop: 7321675377cb9ae8596a29bddf3a3d2b5e8792c5
metadata_god: eceae399d0020475069a5cebc35943ce8562b5d7
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
system_theme: c7b9f6659a5caa26c9bc2284da096781e9a6fcbc
system_tray: e53c972838c69589ff2e77d6d3abfd71332f9e5d
tray_manager: 9064e219c56d75c476e46b9a21182087930baf90
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
window_size: 339dafa0b27a95a62a843042038fa6c3c48de195

File diff suppressed because it is too large Load Diff

View File

@ -13,96 +13,89 @@ environment:
flutter: ">=3.10.0"
dependencies:
args: ^2.3.2
args: ^2.5.0
async: ^2.9.0
audio_service: ^0.18.9
audio_session: ^0.1.18
audio_service: ^0.18.13
audio_service_mpris: ^0.1.3
audio_session: ^0.1.19
auto_size_text: ^3.0.0
buttons_tabbar: ^1.3.6
buttons_tabbar: ^1.3.8
cached_network_image: ^3.3.1
catcher_2: 1.0.0
catcher_2: ^1.2.4
collection: ^1.15.0
cupertino_icons: ^1.0.5
curved_navigation_bar: ^1.0.3
dbus: ^0.7.8
device_info_plus: ^9.1.2
device_info_plus: ^10.1.0
device_preview: ^1.1.0
dio: ^5.4.1
disable_battery_optimization: ^1.1.0+1
dio: ^5.4.3+1
disable_battery_optimization: ^1.1.1
duration: ^3.0.12
envied: ^0.3.0
file_selector: ^1.0.1
fluentui_system_icons: ^1.1.189
envied: ^0.5.4+1
file_picker: ^8.0.0+1
file_selector: ^1.0.3
fluentui_system_icons: ^1.1.234
flutter:
sdk: flutter
flutter_cache_manager: ^3.3.0
flutter_desktop_tools:
git:
url: https://github.com/KRTirtho/flutter_desktop_tools.git
ref: 1f0bec3283626dcbd8ee2f54e238d096d8dea50e
flutter_displaymode: ^0.6.0
flutter_feather_icons: ^2.0.0+1
flutter_hooks: ^0.20.5
flutter_inappwebview: ^6.0.0
flutter_localizations:
sdk: flutter
flutter_native_splash: ^2.3.10
flutter_riverpod: ^2.4.10
flutter_native_splash: ^2.4.0
flutter_riverpod: ^2.5.1
flutter_secure_storage: ^9.0.0
flutter_svg: ^1.1.6
form_validator: ^2.1.1
fuzzywuzzy: ^1.1.6
go_router: 12.1.3 # Stuck on this https://github.com/flutter/flutter/issues/140869
google_fonts: ^6.1.0
google_fonts: ^6.2.1
hive: ^2.2.3
hive_flutter: ^1.1.0
hooks_riverpod: ^2.4.3
hooks_riverpod: ^2.5.1
html: ^0.15.1
http: ^1.2.0
image_picker: ^1.0.4
image_picker: ^1.1.0
intl: ^0.18.0
introduction_screen: ^3.0.2
introduction_screen: ^3.1.14
json_annotation: ^4.8.1
logger: ^2.0.2
media_kit: ^1.1.3
media_kit_libs_audio: ^1.0.3
media_kit: ^1.1.10+1
media_kit_libs_audio: ^1.0.4
metadata_god: ^0.5.2+1
mime: ^1.0.2
package_info_plus: ^4.1.0
package_info_plus: ^6.0.0
palette_generator: ^0.3.3
path: ^1.8.0
path_provider: ^2.0.8
permission_handler: ^11.0.1
piped_client:
git:
url: https://github.com/KRTirtho/piped_client.git
path_provider: ^2.1.3
permission_handler: ^11.3.1
piped_client: ^0.1.1
popover: ^0.3.0
scrobblenaut:
git:
url: https://github.com/KRTirtho/scrobblenaut.git
ref: dart-3-support
scroll_to_index: ^3.0.1
sidebarx: ^0.16.3
shared_preferences: ^2.2.2
sidebarx: ^0.17.1
shared_preferences: ^2.2.3
skeleton_text: ^3.0.1
smtc_windows: ^0.1.1
smtc_windows: ^0.1.2
stroke_text: ^0.0.2
system_theme: ^2.1.0
titlebar_buttons: ^1.0.0
url_launcher: ^6.1.7
uuid: ^3.0.7
url_launcher: ^6.2.6
uuid: ^4.4.0
version: ^3.0.2
visibility_detector: ^0.4.0+2
window_manager: ^0.3.1
window_manager: ^0.3.8
window_size:
git:
url: https://github.com/google/flutter-desktop-embedding.git
ref: a738913c8ce2c9f47515382d40827e794a334274
path: plugins/window_size
youtube_explode_dart: ^2.0.1
simple_icons: ^7.10.0
audio_service_mpris: ^0.1.0
file_picker: ^6.0.0
youtube_explode_dart: ^2.2.0
simple_icons: ^10.1.3
jiosaavn: ^0.1.0
draggable_scrollbar:
git:
@ -116,28 +109,29 @@ dependencies:
url: https://github.com/Tommypop2/dart_discord_rpc.git
html_unescape: ^2.0.0
wikipedia_api: ^0.1.0
skeletonizer: ^0.8.0
app_links: ^3.5.0
win32_registry: ^1.1.2
skeletonizer: ^1.1.1
app_links: ^4.0.1
win32_registry: ^1.1.3
flutter_sharing_intent: ^1.1.0
flutter_broadcasts: ^0.4.0
freezed_annotation: ^2.4.1
spotify: ^0.13.3
spotify: ^0.13.5
bonsoir: ^5.1.9
shelf: ^1.4.1
shelf_router: ^1.1.4
shelf_web_socket: ^1.0.4
web_socket_channel: ^2.4.4
web_socket_channel: ^2.4.5
lrc: ^1.0.2
pub_api_client: ^2.4.0
pubspec_parse: ^1.2.2
timezone: ^0.9.2
crypto: ^3.0.3
local_notifier: ^0.1.6
tray_manager: ^0.2.2
dev_dependencies:
build_runner: ^2.4.9
envied_generator: ^0.3.0+3
flutter_distributor: ^0.0.2
envied_generator: ^0.5.4+1
flutter_gen_runner: ^5.4.0
flutter_launcher_icons: ^0.13.1
flutter_lints: ^3.0.1
@ -147,12 +141,12 @@ dev_dependencies:
sdk: flutter
hive_generator: ^2.0.0
json_serializable: ^6.6.2
freezed: ^2.4.6
custom_lint: ^0.5.11
riverpod_lint: ^2.1.1
freezed: ^2.5.2
custom_lint: ^0.6.4
riverpod_lint: ^2.3.10
dependency_overrides:
system_tray: 2.0.2
uuid: ^4.4.0
flutter:
generate: true

View File

@ -16,7 +16,7 @@
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <system_theme/system_theme_plugin.h>
#include <system_tray/system_tray_plugin.h>
#include <tray_manager/tray_manager_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <window_manager/window_manager_plugin.h>
#include <window_size/window_size_plugin.h>
@ -42,8 +42,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
SystemThemePluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SystemThemePlugin"));
SystemTrayPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SystemTrayPlugin"));
TrayManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("TrayManagerPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
WindowManagerPluginRegisterWithRegistrar(

View File

@ -13,7 +13,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
permission_handler_windows
screen_retriever
system_theme
system_tray
tray_manager
url_launcher_windows
window_manager
window_size