diff --git a/lib/api/cache.dart b/lib/api/cache.dart index 964fd0f..76139ce 100644 --- a/lib/api/cache.dart +++ b/lib/api/cache.dart @@ -109,24 +109,24 @@ class Cache { static Future load([int tryN = 0]) async { final box = await _box; - if (tryN > 2) { + if (tryN > 1) { // recursion error, something's wrong throw Exception("can't load cache"); } - //Doesn't exist, create new - if (box.isEmpty) { - final c = Cache(); - await c.save(); - return c; - } - try { - return (await box.get(0))!; - } catch (e) { - // invalidate cache on error - await box.clear(); - return await load(tryN + 1); + if (box.isNotEmpty) { + try { + return (await box.get(0))!; + } catch (e) { + // invalidate cache on error + await box.clear(); + } } + + // create cache if non-existent or error occurred + final c = Cache(); + await c.save(); + return c; } Future save() async { diff --git a/lib/api/deezer.dart b/lib/api/deezer.dart index daa7850..0eaa41c 100644 --- a/lib/api/deezer.dart +++ b/lib/api/deezer.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:cookie_jar/cookie_jar.dart'; import 'package:crypto/crypto.dart'; import 'package:dio/dio.dart'; @@ -60,9 +58,11 @@ class DeezerAPI { late bool canStreamHQ; final cookieJar = DefaultCookieJar(); - late final dio = - Dio(BaseOptions(headers: headers, responseType: ResponseType.json)) - ..interceptors.add(CookieManager(cookieJar)); + late final dio = Dio(BaseOptions( + headers: headers, + responseType: ResponseType.json, + validateStatus: (status) => true)) + ..interceptors.add(CookieManager(cookieJar)); Future? _authorizing; @@ -93,7 +93,7 @@ class DeezerAPI { Future> callApi(String method, {Map? params, String? gatewayInput}) async { //Post - final res = await dio.post('https://www.deezer.com/ajax/gw-light.php', + final res = await dio.post('https://www.deezer.com/ajax/gw-light.php', queryParameters: { 'api_version': '1.0', 'api_token': token, @@ -145,21 +145,25 @@ class DeezerAPI { }, options: Options(responseType: ResponseType.json)); final accessToken = res.data['access_token'] as String?; - print(res.data); if (accessToken == null) { throw Exception('login failed, access token is null'); } + print(accessToken); + return getArlByAccessToken(accessToken); } // FROM DEEMIX-JS Future getArlByAccessToken(String accessToken) async { //Get SID in cookieJar - await dio.get("https://api.deezer.com/platform/generic/track/3135556", - options: Options(headers: { - 'Authorization': 'Bearer $accessToken', - })); + final res = + await dio.get("https://api.deezer.com/platform/generic/track/3135556", + options: Options(headers: { + 'Authorization': 'Bearer $accessToken', + 'User-Agent': userAgent, + })); + print(res.data); //Get ARL final arlRes = await dio.get("https://www.deezer.com/ajax/gw-light.php", queryParameters: { @@ -170,7 +174,6 @@ class DeezerAPI { }, options: Options(responseType: ResponseType.json)); final arl = arlRes.data["results"]; - print(arlRes.data); if (arl == null) throw Exception('couldn\'t obtain ARL'); return arl; } @@ -178,19 +181,20 @@ class DeezerAPI { //Authorize, bool = success Future rawAuthorize({Function? onError}) async { try { - Map data = await callApi('deezer.getUserData'); - if (data['results']['USER']['USER_ID'] == 0) { + final data = await callApi('deezer.getUserData'); + if (data['results']?['USER']?['USER_ID'] == null || + data['results']['USER']['USER_ID'] == 0) { return false; } else { token = data['results']['checkForm']; userId = data['results']['USER']['USER_ID'].toString(); userName = data['results']['USER']['BLOG_NAME']; favoritesPlaylistId = data['results']['USER']['LOVEDTRACKS_ID']; - canStreamHQ = data['results']['USER']['OPTIONS']['web_hq'] || - data['results']['USER']['OPTIONS']['mobile_hq']; - canStreamLossless = data['results']['USER']['OPTIONS'] - ['web_lossless'] || - data['results']['USER']['OPTIONS']['mobile_lossless']; + canStreamHQ = data['results']['USER']['OPTIONS']['web_hq'] as bool || + data['results']['USER']['OPTIONS']['mobile_hq'] as bool; + canStreamLossless = + data['results']['USER']['OPTIONS']['web_lossless'] as bool || + data['results']['USER']['OPTIONS']['mobile_lossless'] as bool; licenseToken = data['results']['USER']['OPTIONS']['license_token'] as String; @@ -531,18 +535,39 @@ class DeezerAPI { return HomePage.fromPrivateJson(data['results']); } - //Log song listen to deezer - Future logListen(String trackId, - {int seek = 0, int pause = 0, int sync = 1, int? timestamp}) async { + /// Log song listen to deezer + Future logListen( + String trackId, { + /// Amount of times the track's been seeked + int seek = 0, + + /// Amount of times the track's been paused + int pause = 0, + + /// 1 if the track was listened in sync, 0 otherwise + int sync = 1, + + /// If the track is skipped to the next song + bool next = false, + + /// If the track is skipped to the previous song + bool prev = false, + + /// When the timestamp has begun as UTC int (in SECONDS) + int? timestamp, + }) async { await callApi('log.listen', params: { 'params': { - 'timestamp': timestamp ?? DateTime.now().millisecondsSinceEpoch, - 'ts_listen': DateTime.now().millisecondsSinceEpoch, + 'timestamp': + timestamp ?? (DateTime.now().millisecondsSinceEpoch) ~/ 1000, + 'ts_listen': DateTime.now().millisecondsSinceEpoch ~/ 1000, 'type': 1, 'stat': { 'seek': seek, // amount of times seeked 'pause': pause, // amount of times paused - 'sync': sync + 'sync': sync, + if (next) 'next': true, + if (prev) 'prev': true, }, 'media': {'id': trackId, 'type': 'song', 'format': 'MP3_128'} } diff --git a/lib/api/definitions.dart b/lib/api/definitions.dart index 160edb1..3686683 100644 --- a/lib/api/definitions.dart +++ b/lib/api/definitions.dart @@ -10,7 +10,6 @@ import 'package:freezer/page_routes/blur_slide.dart'; import 'package:freezer/page_routes/fade.dart'; import 'package:freezer/settings.dart'; import 'package:hive_flutter/hive_flutter.dart'; -import 'package:isar/isar.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:intl/intl.dart'; import 'package:just_audio/just_audio.dart'; @@ -966,6 +965,8 @@ class HomePageSection { }[json['layout'] ?? '']; if (layout == null) { _logger.warning('UNKNOWN LAYOUT: ${json['layout']}'); + _logger.warning('LAYOUT DATA:'); + _logger.warning(json); return null; } final items = []; @@ -1552,6 +1553,10 @@ extension GetId on FlowConfig { } } +extension LastChars on String { + String withoutLast(int n) => substring(0, length - n); +} + class TrackUrlSource { final String provider; final String url; diff --git a/lib/api/download.dart b/lib/api/download.dart index 8007649..f872258 100644 --- a/lib/api/download.dart +++ b/lib/api/download.dart @@ -1,11 +1,9 @@ -import 'package:flutter/foundation.dart'; import 'package:freezer/main.dart'; import 'package:sqflite/sqflite.dart'; import 'package:disk_space_plus/disk_space_plus.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:freezer/api/deezer.dart'; import 'package:freezer/api/definitions.dart'; @@ -488,12 +486,10 @@ class DownloadManager { } //Check if album, track or playlist is offline - Future _checkOffline( - (Album? album, Track? track, Playlist? playlist) message) async { + Future checkOffline( + {Album? album, Track? track, Playlist? playlist}) async { if (!isSupported) return false; - final (album, track, playlist) = message; - //Track if (track != null) { List res = await db.query('Tracks', @@ -518,10 +514,6 @@ class DownloadManager { return false; } - Future checkOffline({Album? album, Track? track, Playlist? playlist}) { - return compute(_checkOffline, (album, track, playlist)); - } - //Offline search Future search(String? query) async { SearchResults results = diff --git a/lib/api/download_manager/database.dart b/lib/api/download_manager/database.dart index d59d7a7..744361a 100644 --- a/lib/api/download_manager/database.dart +++ b/lib/api/download_manager/database.dart @@ -1,4 +1,3 @@ -import 'package:freezer/api/definitions.dart'; import 'package:isar/isar.dart'; @collection diff --git a/lib/api/download_manager/download_manager.dart b/lib/api/download_manager/download_manager.dart index abbc78e..e5024da 100644 --- a/lib/api/download_manager/download_manager.dart +++ b/lib/api/download_manager/download_manager.dart @@ -7,7 +7,6 @@ import 'package:freezer/api/definitions.dart'; import 'package:freezer/api/download_manager/download_service.dart'; import 'package:freezer/api/download_manager/service_interface.dart'; import 'package:freezer/translations.i18n.dart'; -import '../download.dart' as dl; class DownloadManager { //implements dl.DownloadManager { @@ -62,7 +61,6 @@ class DownloadManager { FlutterBackgroundService().invoke(method, args); } - @override Future addOfflineTrack(Track track, {bool private = true, BuildContext? context, isSingleton = false}) { // TODO: implement addOfflineTrack diff --git a/lib/api/player/audio_handler.dart b/lib/api/player/audio_handler.dart index 5b8c75c..29a46f3 100644 --- a/lib/api/player/audio_handler.dart +++ b/lib/api/player/audio_handler.dart @@ -1,8 +1,6 @@ import 'package:audio_service/audio_service.dart'; import 'package:audio_session/audio_session.dart'; -import 'package:equalizer/equalizer.dart'; import 'package:flutter/foundation.dart'; -import 'package:fluttertoast/fluttertoast.dart'; import 'package:freezer/api/cache.dart'; import 'package:freezer/api/deezer.dart'; import 'package:freezer/api/deezer_audio_source.dart'; @@ -18,11 +16,8 @@ import 'package:just_audio_media_kit/just_audio_media_kit.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; -import 'package:freezer/translations.i18n.dart'; import 'package:collection/collection.dart'; import 'package:scrobblenaut/scrobblenaut.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:media_kit/media_kit.dart' show MPVLogLevel; import '../definitions.dart'; import '../../settings.dart'; @@ -122,6 +117,9 @@ class AudioPlayerTask extends BaseAudioHandler { /// Last logged track id (to avoid duplicates) String? _loggedTrackId; + /// Last track's id + String? _lastTrackId; + // deezer track logging /// Whether we should send log requests to deezer late bool _shouldLogTracks; @@ -132,7 +130,7 @@ class AudioPlayerTask extends BaseAudioHandler { /// Amount of times the song's been seeked int _amountSeeked = 0; - /// When playback begun + /// When playback begun (in SECONDS) int? _timestamp; MediaItem get currentMediaItem => queue.value[_queueIndex]; @@ -155,7 +153,6 @@ class AudioPlayerTask extends BaseAudioHandler { // Linux/Windows specific options JustAudioMediaKit.title = 'Freezer'; JustAudioMediaKit.protocolWhitelist = const ['http']; - JustAudioMediaKit.mpvLogLevel = MPVLogLevel.debug; _deezerAPI = initArgs.deezerAPI; _androidAuto = AndroidAuto(deezerAPI: _deezerAPI); @@ -180,12 +177,22 @@ class AudioPlayerTask extends BaseAudioHandler { //Update track index _player.currentIndexStream.listen((index) { - _amountSeeked = 0; - _amountPaused = 0; - _timestamp = DateTime.now().millisecondsSinceEpoch; if (index != null && queue.value.isNotEmpty) { _queueIndex = index; mediaItem.add(currentMediaItem); + + // log previous track + if (index != 0 && + _lastTrackId != null && + _lastTrackId! != currentMediaItem.id) { + _logListenedTrack(_lastTrackId!, + sync: _amountPaused == 0 && _amountSeeked == 0); + } + + _lastTrackId = currentMediaItem.id; + _amountSeeked = 0; + _amountPaused = 0; + _timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; } if (index == queue.value.length - 1) { @@ -290,7 +297,7 @@ class AudioPlayerTask extends BaseAudioHandler { Future skipToQueueItem(int index) async { _lastPosition = null; - unawaited(_logListenedTrack(currentMediaItem)); + unawaited(_logListenedTrack(currentMediaItem.id, sync: false)); //Skip in player await _player.seek(Duration.zero, index: index); _queueIndex = index; @@ -396,7 +403,7 @@ class AudioPlayerTask extends BaseAudioHandler { _lastPosition = null; if (_queueIndex == queue.value.length - 1) return; //Update buffering state - unawaited(_logListenedTrack(currentMediaItem)); + unawaited(_logListenedTrack(currentMediaItem.id, sync: false, next: true)); _queueIndex++; await _player.seekToNext(); _broadcastState(); @@ -408,24 +415,30 @@ class AudioPlayerTask extends BaseAudioHandler { //Update buffering state //_skipState = AudioProcessingState.skippingToPrevious; //Normal skip to previous - unawaited(_logListenedTrack(currentMediaItem)); + unawaited(_logListenedTrack(currentMediaItem.id, sync: false, prev: true)); _queueIndex--; await _player.seekToPrevious(); //_skipState = null; } - Future _logListenedTrack(MediaItem mediaItem) async { + Future _logListenedTrack(String trackId, + {required bool sync, bool next = false, bool prev = false}) async { + if (_loggedTrackId == trackId) return; + _loggedTrackId = trackId; + print( - 'logging: seek: $_amountSeeked, pause: $_amountPaused, timestamp: $_timestamp (elapsed: ${DateTime.now().millisecondsSinceEpoch - _timestamp!}ms)'); + 'logging: seek: $_amountSeeked, pause: $_amountPaused, sync: $sync, next: $next, prev: $prev, timestamp: $_timestamp (elapsed: ${DateTime.now().millisecondsSinceEpoch ~/ 1000 - _timestamp!}s)'); if (!_shouldLogTracks) return; //Log to Deezer _deezerAPI.logListen( - mediaItem.id, + trackId, seek: _amountSeeked, pause: _amountPaused, - sync: _amountSeeked == 0 && _amountPaused == 0 ? 1 : 0, + sync: sync ? 1 : 0, timestamp: _timestamp, + prev: prev, + next: next, ); } diff --git a/lib/main.dart b/lib/main.dart index 74b76ea..652608c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -97,10 +97,17 @@ void main() async { Hive.init(await Paths.dataDirectory()); //Initialize globals - settings = await Settings.load(); - settings.save(); + try { + cache = await Cache.load(); + settings = await Settings.load(); + settings.save(); + } catch (e) { + // ignore: avoid_print + print( + 'Cannot load cache or settings box. Maybe another instance of the app is running?'); + exit(1); + } downloadManager.init(); - cache = await Cache.load(); // photos cacheManager = DefaultCacheManager(); // cacheManager = HiveCacheManager( @@ -109,9 +116,8 @@ void main() async { deezerAPI.favoritesPlaylistId = cache.favoritesPlaylistId; Logger.root.onRecord.listen((record) { - if (kDebugMode) { - print('${record.level.name}: ${record.time}: ${record.message}'); - } + // ignore: avoid_print + print('${record.level.name}: ${record.time}: ${record.message}'); }); if (kDebugMode) { @@ -699,8 +705,7 @@ class MainScreenState extends State child: _MainRouteNavigator( navigatorKey: navigatorKey, routes: { - Navigator.defaultRouteName: (context) => - const HomeScreen(), + Navigator.defaultRouteName: (context) => HomeScreen(), '/podcasts': (context) => HomePageScreen( cacheable: true, channel: const DeezerChannel( diff --git a/lib/translations.i18n.dart b/lib/translations.i18n.dart index 4c3c980..7c95ce3 100644 --- a/lib/translations.i18n.dart +++ b/lib/translations.i18n.dart @@ -39,7 +39,7 @@ const List languages = [ List get supportedLocales => languages.map((l) => l.getLocale).toList(); extension Localization on String { - static const _t = Translations.from("en_US", {...language_en_us, ...crowdin}); + static final _t = Translations.byLocale("en_US") + language_en_us + crowdin; String get i18n => localize(this, _t); } diff --git a/lib/ui/details_screens.dart b/lib/ui/details_screens.dart index 432e16e..db6bd69 100644 --- a/lib/ui/details_screens.dart +++ b/lib/ui/details_screens.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:math'; -import 'package:draggable_scrollbar/draggable_scrollbar.dart'; import 'package:flutter/material.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:freezer/api/cache.dart'; @@ -864,9 +863,10 @@ class _PlaylistDetailsState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(playlist!.title ?? '')), - body: DraggableScrollbar.rrect( + body: Scrollbar( + interactive: true, controller: _scrollController, - backgroundColor: Theme.of(context).primaryColor, + thickness: 8.0, child: ListView( controller: _scrollController, children: [ diff --git a/lib/ui/home_screen.dart b/lib/ui/home_screen.dart index 3a081aa..39b70a5 100644 --- a/lib/ui/home_screen.dart +++ b/lib/ui/home_screen.dart @@ -1,7 +1,9 @@ +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:freezer/api/deezer.dart'; import 'package:freezer/api/definitions.dart'; import 'package:freezer/api/player/audio_handler.dart'; +import 'package:freezer/main.dart'; import 'package:freezer/ui/error.dart'; import 'package:freezer/ui/menu.dart'; import 'package:freezer/translations.i18n.dart'; @@ -138,8 +140,10 @@ class _HomePageWidgetState extends State { } if (!mounted) return; if (homePage == null) { - //On error - setState(() => _error = true); + if (_homePage == null) { + //On error + setState(() => _error = true); + } return; } if (homePage.sections.isEmpty) return; @@ -183,6 +187,16 @@ class _HomePageWidgetState extends State { }); } + Widget getSectionChild(HomePageSection section) { + switch (section.layout) { + case HomePageSectionLayout.GRID: + return HomePageGridSection(section); + case HomePageSectionLayout.ROW: + default: + return HomepageRowSection(section); + } + } + @override Widget build(BuildContext context) { if (_error) return const ErrorScreen(); @@ -190,74 +204,102 @@ class _HomePageWidgetState extends State { if (_homePage != null) { sections = _homePage!.sections; } - return RefreshIndicator( - key: _indicatorKey, - onRefresh: _load, - child: _homePage == null - ? const SizedBox.expand(child: SingleChildScrollView()) - : ListView.builder( - itemBuilder: (context, index) { - final section = sections![index]; - switch (section.layout) { - case HomePageSectionLayout.GRID: - return HomePageGridSection(section); - case HomePageSectionLayout.ROW: - default: - return HomepageRowSection(section); - } - }, - itemCount: sections!.length, - padding: const EdgeInsets.symmetric(horizontal: 8.0), - ), + + final actualScrollConfiguration = ScrollConfiguration.of(context); + return ScrollConfiguration( + behavior: actualScrollConfiguration.copyWith( + dragDevices: { + PointerDeviceKind.mouse, + PointerDeviceKind.stylus, + PointerDeviceKind.touch, + PointerDeviceKind.trackpad + }, + ), + child: RefreshIndicator( + key: _indicatorKey, + onRefresh: _load, + child: _homePage == null + ? const SizedBox.expand(child: SingleChildScrollView()) + : ListView.builder( + itemBuilder: (context, index) { + return Padding( + padding: index == 0 + ? EdgeInsets.zero + : const EdgeInsets.only(top: 16.0), + child: getSectionChild(sections![index])); + }, + itemCount: sections!.length, + padding: const EdgeInsets.symmetric(horizontal: 8.0), + ), + ), ); } } -class HomepageRowSection extends StatelessWidget { +class HomepageRowSection extends StatefulWidget { final HomePageSection section; const HomepageRowSection(this.section, {super.key}); + @override + State createState() => _HomepageRowSectionState(); +} + +class _HomepageRowSectionState extends State { + final _controller = ScrollController(); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return ListTile( title: Text( - section.title ?? '', + widget.section.title ?? '', textAlign: TextAlign.left, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 20.0, fontWeight: FontWeight.w900), ), - subtitle: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: List.generate(section.items!.length + 1, (j) { - //Has more items - if (j == section.items!.length) { - if (section.hasMore ?? false) { - return TextButton( - child: Text( - 'Show more'.i18n, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 20.0), - ), - onPressed: () => Navigator.of(context).pushRoute( - builder: (context) => HomePageScreen( - title: section.title!, - channel: DeezerChannel(target: section.pagePath), + subtitle: Scrollbar( + controller: _controller, + thickness: MainScreen.of(context).isDesktop ? null : 1.0, + child: SingleChildScrollView( + controller: _controller, + scrollDirection: Axis.horizontal, + child: Row( + children: List.generate(widget.section.items!.length + 1, (j) { + //Has more items + if (j == widget.section.items!.length) { + if (widget.section.hasMore ?? false) { + return TextButton( + child: Text( + 'Show more'.i18n, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 20.0), ), - ), - ); + onPressed: () => Navigator.of(context).pushRoute( + builder: (context) => HomePageScreen( + title: widget.section.title!, + channel: + DeezerChannel(target: widget.section.pagePath), + ), + ), + ); + } + return const SizedBox(); } - return const SizedBox(); - } - //Show item - HomePageItem item = section.items![j]; - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 6.0), - child: HomePageItemWidget(item), - ); - }), + //Show item + HomePageItem item = widget.section.items![j]; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 6.0), + child: HomePageItemWidget(item), + ); + }), + ), ), )); } @@ -265,7 +307,14 @@ class HomepageRowSection extends StatelessWidget { class HomePageGridSection extends StatelessWidget { final HomePageSection section; - const HomePageGridSection(this.section, {super.key}); + final double horizontalSpacing; + final double verticalSpacing; + const HomePageGridSection( + this.section, { + this.horizontalSpacing = 6.0, + this.verticalSpacing = 6.0, + super.key, + }); @override Widget build(BuildContext context) { @@ -284,13 +333,22 @@ class HomePageGridSection extends StatelessWidget { const TextStyle(fontSize: 20.0, fontWeight: FontWeight.w900), ), ), - Wrap( - spacing: 4.0, - runSpacing: 4.0, - alignment: WrapAlignment.spaceEvenly, - children: section.items! - .map((e) => HomePageItemWidget(e)) - .toList(growable: false)), + LayoutBuilder(builder: (context, constraints) { + final amountThatCanFit = + constraints.maxWidth ~/ (150.0 + horizontalSpacing); + final widthOfEach = + constraints.maxWidth / amountThatCanFit - horizontalSpacing; + return Wrap( + spacing: horizontalSpacing, + runSpacing: verticalSpacing, + alignment: WrapAlignment.start, + children: section.items! + .map((e) => SizedBox( + width: widthOfEach, + child: HomePageItemWidget(e), + )) + .toList(growable: false)); + }), ], ); } diff --git a/lib/ui/importer_screen.dart b/lib/ui/importer_screen.dart index 0634c21..6e09e5c 100644 --- a/lib/ui/importer_screen.dart +++ b/lib/ui/importer_screen.dart @@ -9,7 +9,6 @@ import 'package:freezer/api/spotify.dart'; import 'package:freezer/ui/elements.dart'; import 'package:freezer/translations.i18n.dart'; import 'package:spotify/spotify.dart' as spotify; -import 'package:url_launcher/url_launcher.dart'; import 'dart:async'; @@ -576,15 +575,18 @@ class _SpotifyImporterV2MainState extends State { await importer.start(context, title, description, tracks); if (!context.mounted) return; - //Route - Navigator.of(context).pop(); - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (context) => const ImporterStatusScreen())); - } catch (e) { + } catch (e, st) { + print(e); + print(st); ScaffoldMessenger.of(context).snack(e.toString()); - Navigator.of(context).pop(); + Navigator.of(context, rootNavigator: true).pop(); return; } + + //Route + Navigator.of(context, rootNavigator: true).pop(); + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (context) => const ImporterStatusScreen())); } @override diff --git a/lib/ui/library.dart b/lib/ui/library.dart index 0d00751..7f5e5e4 100644 --- a/lib/ui/library.dart +++ b/lib/ui/library.dart @@ -13,7 +13,6 @@ import 'package:freezer/ui/error.dart'; import 'package:freezer/ui/importer_screen.dart'; import 'package:freezer/ui/tiles.dart'; import 'package:freezer/translations.i18n.dart'; -import 'package:draggable_scrollbar/draggable_scrollbar.dart'; import 'menu.dart'; import '../api/download.dart'; @@ -471,9 +470,10 @@ class _LibraryTracksState extends State { ), body: _loading && allTracks.isEmpty ? const Center(child: CircularProgressIndicator()) - : DraggableScrollbar.rrect( + : Scrollbar( + interactive: true, controller: _scrollController, - backgroundColor: Theme.of(context).primaryColor, + thickness: 8.0, child: ListView( controller: _scrollController, children: [ @@ -677,9 +677,10 @@ class _LibraryAlbumsState extends State { ), body: !settings.offlineMode && _albums == null ? const Center(child: CircularProgressIndicator()) - : DraggableScrollbar.rrect( + : Scrollbar( + interactive: true, controller: _scrollController, - backgroundColor: Theme.of(context).primaryColor, + thickness: 8.0, child: ListView( controller: _scrollController, children: [ @@ -886,9 +887,10 @@ class _LibraryArtistsState extends State { ? const Center(child: CircularProgressIndicator()) : _error ? const Center(child: ErrorScreen()) - : DraggableScrollbar.rrect( + : Scrollbar( + interactive: true, controller: _scrollController, - backgroundColor: Theme.of(context).primaryColor, + thickness: 8.0, child: ListView( controller: _scrollController, children: [ @@ -1047,9 +1049,10 @@ class _LibraryPlaylistsState extends State { Container(width: 8.0), ], ), - body: DraggableScrollbar.rrect( + body: Scrollbar( + interactive: true, controller: _scrollController, - backgroundColor: Theme.of(context).primaryColor, + thickness: 8.0, child: ListView( controller: _scrollController, children: [ @@ -1198,9 +1201,10 @@ class _HistoryScreenState extends State { ) ], ), - body: DraggableScrollbar.rrect( + body: Scrollbar( + interactive: true, controller: _scrollController, - backgroundColor: Theme.of(context).primaryColor, + thickness: 8.0, child: ListView.builder( controller: _scrollController, itemCount: cache.history.length, diff --git a/lib/ui/login_screen.dart b/lib/ui/login_screen.dart index df12ab6..5744bb4 100644 --- a/lib/ui/login_screen.dart +++ b/lib/ui/login_screen.dart @@ -1,13 +1,11 @@ import 'dart:io'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:freezer/api/deezer.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:freezer/translations.i18n.dart'; import 'package:url_launcher/url_launcher_string.dart'; -import 'package:desktop_webview_window/desktop_webview_window.dart'; import '../settings.dart'; import '../api/definitions.dart'; @@ -149,73 +147,6 @@ class _LoginWidgetState extends State { } void _loginBrowser() async { - if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { - // TODO: find a way to read arl from webview - if (!await WebviewWindow.isWebviewAvailable()) { - showDialog( - context: context, - builder: (ctx) => AlertDialog( - title: Text('Can\'t login via browser'.i18n), - content: RichText( - text: TextSpan(children: [ - TextSpan( - text: - 'Your system doesn\'t have a WebView implementation.\n\u2022 If you are on Windows,' - .i18n), - TextSpan( - text: - 'make sure it is available on your system.\n'.i18n, - style: const TextStyle(color: Colors.blue), - recognizer: TapGestureRecognizer() - ..onTap = () => launchUrlString( - 'https://developer.microsoft.com/en-us/microsoft-edge/webview2')), - TextSpan( - text: - '\u2022 If you are on Linux, make sure webkit2gtk is installed.\n' - .i18n), - TextSpan( - text: - '\nAlternatively, you can login in your browser on deezer.com, open your developer console with F12 and logging in with the ARL token' - .i18n), - ])), - )); - return; - } - - Navigator.of(context) - .pushRoute(builder: (context) => LoadingWindowWait(update: _update)); - final webview = - await WebviewWindow.create(configuration: CreateConfiguration()); - webview.launch('https://deezer.com/login'); - webview.onClose.then((_) { - Navigator.pop(context); - }); - - webview.addOnUrlRequestCallback((url) { - if (!url.contains('/login') && url.contains('/register')) { - webview.evaluateJavaScript('window.location.href = "/open_app"'); - } - - final uri = Uri.parse(url); - //Parse arl from url - if (uri.scheme == 'intent' && uri.host == 'deezer.page.link') { - try { - //Actual url is in `link` query parameter - Uri linkUri = Uri.parse(uri.queryParameters['link']!); - String? arl = linkUri.queryParameters['arl']; - if (arl != null) { - settings.arl = arl; - webview.close(); - _update(); - } - } catch (e) { - print(e); - } - } - }); - - return; - } Navigator.of(context) .pushRoute(builder: (context) => LoginBrowser(_update)); } @@ -256,106 +187,114 @@ class _LoginWidgetState extends State { // style: ButtonStyle( // foregroundColor: // MaterialStateProperty.all(Colors.white)))), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 32.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const FreezerTitle(), - Container(height: 16.0), - Text( - "Please login using your Deezer account.".i18n, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 16.0), - ), - Container( - height: 16.0, - ), - //Email login dialog - OutlinedButton( - child: Text( - 'Login using email'.i18n, - ), - onPressed: () { - showDialog( - context: context, - builder: (context) => EmailLogin(_update)); - }, - ), - OutlinedButton( - onPressed: _loginBrowser, - child: Text('Login using browser'.i18n), - ), - OutlinedButton( - child: Text('Login using token'.i18n), - onPressed: () { - showDialog( - context: context, - builder: (context) { - Future.delayed( - const Duration(seconds: 1), - () => { - focusNode.requestFocus() - }); // autofocus doesn't work - it's replacement - return AlertDialog( - title: Text('Enter ARL'.i18n), - content: TextField( - onChanged: (String s) => _arl = s, - decoration: InputDecoration( - labelText: 'Token (ARL)'.i18n), - focusNode: focusNode, - controller: controller, - onSubmitted: (String s) { - goARL(focusNode, controller); - }, - ), - actions: [ - TextButton( - child: Text('Save'.i18n), - onPressed: () => - goARL(null, controller), - ) - ], - ); - }); - }, - ), - ]))), - Container(height: 16.0), - Text( - "If you don't have account, you can register on deezer.com for free." - .i18n, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 16.0), + child: Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 700.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const FreezerTitle(), + const SizedBox(height: 16.0), + Text( + "Please login using your Deezer account." + .i18n, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16.0), + ), + const SizedBox(height: 16.0), + //Email login dialog + ElevatedButton( + child: Text( + 'Login using email'.i18n, + ), + onPressed: () { + showDialog( + context: context, + builder: (context) => + EmailLogin(_update)); + }, + ), + const SizedBox(height: 2.0), + + // only supported on android + if (Platform.isAndroid) + ElevatedButton( + onPressed: _loginBrowser, + child: Text('Login using browser'.i18n), + ), + const SizedBox(height: 2.0), + ElevatedButton( + child: Text('Login using token'.i18n), + onPressed: () { + showDialog( + context: context, + builder: (context) { + Future.delayed( + const Duration(seconds: 1), + () => { + focusNode.requestFocus() + }); // autofocus doesn't work - it's replacement + return AlertDialog( + title: Text('Enter ARL'.i18n), + content: TextField( + onChanged: (String s) => _arl = s, + decoration: InputDecoration( + labelText: + 'Token (ARL)'.i18n), + focusNode: focusNode, + controller: controller, + onSubmitted: (String s) { + goARL(focusNode, controller); + }, + ), + actions: [ + TextButton( + child: Text('Save'.i18n), + onPressed: () => + goARL(null, controller), + ) + ], + ); + }); + }, + ), + ]))), + const SizedBox(height: 16.0), + Text( + "If you don't have account, you can register on deezer.com for free." + .i18n, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16.0), + ), + const SizedBox(height: 8.0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32.0), + child: ElevatedButton( + child: Text('Open in browser'.i18n), + onPressed: () { + launchUrlString('https://deezer.com/register'); + }, + ), + ), + const SizedBox(height: 8.0), + const Divider(), + const SizedBox(height: 8.0), + Text( + "By using this app, you don't agree with the Deezer ToS" + .i18n, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 14.0), + ) + ], ), - Container(height: 8.0), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 32.0), - child: OutlinedButton( - child: Text('Open in browser'.i18n), - onPressed: () { - launchUrlString('https://deezer.com/register'); - }, - ), - ), - Container( - height: 8.0, - ), - const Divider(), - Container( - height: 8.0, - ), - Text( - "By using this app, you don't agree with the Deezer ToS".i18n, - textAlign: TextAlign.center, - style: const TextStyle(fontSize: 14.0), - ) - ], + ), ), ), )); @@ -486,37 +425,48 @@ class _EmailLoginState extends State { Widget build(BuildContext context) { return AlertDialog( title: Text('Email Login'.i18n), - content: Column( - mainAxisSize: MainAxisSize.min, - children: _loading - ? [const CircularProgressIndicator()] - : [ - TextFormField( - decoration: InputDecoration(labelText: 'Email'.i18n), - controller: _emailController, - ), - const SizedBox(height: 8.0), - TextFormField( - obscureText: true, - decoration: InputDecoration(labelText: "Password".i18n), - controller: _passwordController, - ) + content: AutofillGroup( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + enabled: !_loading, + decoration: InputDecoration(labelText: 'Email'.i18n), + controller: _emailController, + autofillHints: const [ + AutofillHints.email, ], + ), + const SizedBox(height: 8.0), + TextFormField( + enabled: !_loading, + obscureText: true, + decoration: InputDecoration(labelText: "Password".i18n), + controller: _passwordController, + autofillHints: const [ + AutofillHints.password, + ], + ) + ], + ), ), actions: [ - if (!_loading) - TextButton( - child: const Text('Login'), - onPressed: () async { - if (_emailController.text.isNotEmpty && - _passwordController.text.isNotEmpty) { - await _login(); - } else { - ScaffoldMessenger.of(context) - .snack("Missing email or password!".i18n); - } - }, - ) + TextButton( + onPressed: _loading + ? null + : () async { + if (_emailController.text.isNotEmpty && + _passwordController.text.isNotEmpty) { + await _login(); + } else { + ScaffoldMessenger.of(context) + .snack("Missing email or password!".i18n); + } + }, + child: _loading + ? const CircularProgressIndicator() + : const Text('Login'), + ) ], ); } diff --git a/lib/ui/lyrics_screen.dart b/lib/ui/lyrics_screen.dart index 47f4bae..8b133e8 100644 --- a/lib/ui/lyrics_screen.dart +++ b/lib/ui/lyrics_screen.dart @@ -48,7 +48,7 @@ class _LyricsWidgetState extends State { late StreamSubscription _mediaItemSub; late StreamSubscription _playbackStateSub; int? _currentIndex = -1; - int? _prevIndex = -1; + Duration _nextPosition = Duration.zero; final ScrollController _controller = ScrollController(); final double height = 90; BoxConstraints? _widgetConstraints; @@ -86,6 +86,7 @@ class _LyricsWidgetState extends State { _lyrics = lyrics; }); + _nextPosition = Duration.zero; SchedulerBinding.instance.addPostFrameCallback( (_) => _updatePosition(audioHandler.playbackState.value.position)); } catch (e) { @@ -96,7 +97,7 @@ class _LyricsWidgetState extends State { } } - Future _scrollToLyric() async { + void _scrollToLyric() { if (!_controller.hasClients) return; //Lyric height, screen height, appbar height double scrollTo; @@ -111,27 +112,36 @@ class _LyricsWidgetState extends State { print( '${height * _currentIndex!}, ${MediaQuery.of(context).size.height / 2}'); - if (0 > scrollTo) return; + if (scrollTo < 0.0) scrollTo = 0.0; if (scrollTo > _controller.position.maxScrollExtent) { scrollTo = _controller.position.maxScrollExtent; } _animatedScroll = true; - await _controller.animateTo(scrollTo, - duration: const Duration(milliseconds: 250), curve: Curves.ease); - _animatedScroll = false; + _controller + .animateTo(scrollTo, + duration: const Duration(milliseconds: 250), curve: Curves.ease) + .then((_) => _animatedScroll = false); } void _updatePosition(Duration position) { if (_loading) return; if (!_syncedLyrics) return; + if (position < _nextPosition) return; + _currentIndex = _lyrics?.lyrics?.lastIndexWhere((l) => l.offset! <= position); //Scroll to current lyric if (_currentIndex! < 0) return; - if (_prevIndex == _currentIndex) return; //Update current lyric index - setState(() {}); - _prevIndex = _currentIndex; + if (_currentIndex! < _lyrics!.lyrics!.length) { + // update nextPosition + _nextPosition = _lyrics!.lyrics![_currentIndex! + 1].offset!; + } else { + // dummy position so that the before-hand condition always returns false + _nextPosition = const Duration(days: 69); + } + + setState(() => _currentIndex); if (_freeScroll) return; _scrollToLyric(); } diff --git a/lib/ui/menu.dart b/lib/ui/menu.dart index ebe5ec7..ed7f8a4 100644 --- a/lib/ui/menu.dart +++ b/lib/ui/menu.dart @@ -1,8 +1,6 @@ import 'dart:async'; import 'package:freezer/main.dart'; -import 'package:freezer/ui/fancy_scaffold.dart'; -import 'package:freezer/ui/player_bar.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:flutter/material.dart'; import 'package:freezer/api/cache.dart'; @@ -108,43 +106,40 @@ class MenuSheetOption { class MenuSheet { final BuildContext context; final Function? navigateCallback; - bool _contextMenuOpen = false; MenuSheet(this.context, {this.navigateCallback}); void _showContextMenu(List options, - {required TapDownDetails details}) { + {required TapUpDetails details}) { final overlay = Overlay.of(context).context.findRenderObject() as RenderBox; final actualPosition = overlay.globalToLocal(details.globalPosition); - _contextMenuOpen = true; showMenu( - clipBehavior: Clip.antiAlias, - elevation: 4.0, - context: context, - constraints: const BoxConstraints(maxWidth: 300.0), - position: - RelativeRect.fromSize(actualPosition & Size.zero, overlay.size), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(28.0))), - items: options - .map((option) => PopupMenuItem( - onTap: option.onTap, - child: option.icon == null - ? option.label - : Row(mainAxisSize: MainAxisSize.min, children: [ - option.icon!, - const SizedBox(width: 8.0), - Flexible(child: option.label), - ]))) - .toList(growable: false)) - .then((_) => _contextMenuOpen = false); + clipBehavior: Clip.antiAlias, + elevation: 4.0, + context: context, + constraints: const BoxConstraints(maxWidth: 300.0), + position: + RelativeRect.fromSize(actualPosition & Size.zero, overlay.size), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(28.0))), + items: options + .map((option) => PopupMenuItem( + onTap: option.onTap, + child: option.icon == null + ? option.label + : Row(mainAxisSize: MainAxisSize.min, children: [ + option.icon!, + const SizedBox(width: 8.0), + Flexible(child: option.label), + ]))) + .toList(growable: false)); } //=================== // DEFAULT //=================== - void show(List options, {TapDownDetails? details}) { + void show(List options, {TapUpDetails? details}) { if (details != null) { _showContextMenu(options, details: details); return; @@ -185,7 +180,7 @@ class MenuSheet { //=================== void showWithTrack(Track track, List options, - {TapDownDetails? details}) { + {TapUpDetails? details}) { if (details != null) { _showContextMenu(options, details: details); return; @@ -197,37 +192,38 @@ class MenuSheet { isScrollControlled: true, enableDrag: false, showDragHandle: false, - useSafeArea: true, elevation: 0.0, builder: (BuildContext context) { return DraggableScrollableSheet( initialChildSize: 0.5, minChildSize: 0.45, maxChildSize: 0.75, - builder: (context, scrollController) => Material( - type: MaterialType.card, - clipBehavior: Clip.antiAlias, - borderRadius: - const BorderRadius.vertical(top: Radius.circular(20.0)), - child: CustomScrollView( - controller: scrollController, - slivers: [ - SliverPersistentHeader( - pinned: true, - delegate: SliverTrackPersistentHeader(track, - extent: 128.0 + 16.0 + 16.0)), - SliverList( - delegate: SliverChildListDelegate.fixed(options - .map((option) => ListTile( - title: option.label, - leading: option.icon, - onTap: () { - option.onTap.call(); - Navigator.pop(context); - }, - )) - .toList(growable: false))), - ], + builder: (context, scrollController) => SafeArea( + child: Material( + type: MaterialType.card, + clipBehavior: Clip.antiAlias, + borderRadius: + const BorderRadius.vertical(top: Radius.circular(20.0)), + child: CustomScrollView( + controller: scrollController, + slivers: [ + SliverPersistentHeader( + pinned: true, + delegate: SliverTrackPersistentHeader(track, + extent: 128.0 + 16.0 + 16.0)), + SliverList( + delegate: SliverChildListDelegate.fixed(options + .map((option) => ListTile( + title: option.label, + leading: option.icon, + onTap: () { + option.onTap.call(); + Navigator.pop(context); + }, + )) + .toList(growable: false))), + ], + ), ), ), ); @@ -239,7 +235,7 @@ class MenuSheet { Track track, { List options = const [], Function? onRemove, - TapDownDetails? details, + TapUpDetails? details, }) { showWithTrack( track, @@ -423,7 +419,7 @@ class MenuSheet { void defaultAlbumMenu(Album album, {List options = const [], Function? onRemove, - TapDownDetails? details}) { + TapUpDetails? details}) { show([ album.library! ? removeAlbum(album, onRemove: onRemove) @@ -486,7 +482,7 @@ class MenuSheet { void defaultArtistMenu(Artist artist, {List options = const [], Function? onRemove, - TapDownDetails? details}) { + TapUpDetails? details}) { show(details: details, [ artist.library! ? removeArtist(artist, onRemove: onRemove) @@ -529,7 +525,7 @@ class MenuSheet { {List options = const [], Function? onRemove, Function? onUpdate, - TapDownDetails? details}) { + TapUpDetails? details}) { show(details: details, [ if (playlist.library != null) playlist.library! @@ -614,7 +610,7 @@ class MenuSheet { //=================== defaultShowEpisodeMenu(Show s, ShowEpisode e, - {List options = const [], TapDownDetails? details}) { + {List options = const [], TapUpDetails? details}) { show(details: details, [ shareTile('episode', e.id), shareShow(s.id), diff --git a/lib/ui/player_bar.dart b/lib/ui/player_bar.dart index dc6d0b7..d35c160 100644 --- a/lib/ui/player_bar.dart +++ b/lib/ui/player_bar.dart @@ -4,7 +4,6 @@ import 'package:audio_service/audio_service.dart'; import 'package:flutter/material.dart'; import 'package:freezer/settings.dart'; import 'package:freezer/translations.i18n.dart'; -import 'package:freezer/ui/fancy_scaffold.dart'; import 'package:rxdart/rxdart.dart'; import '../api/player/audio_handler.dart'; diff --git a/lib/ui/player_screen.dart b/lib/ui/player_screen.dart index 3d3a2d2..fa3b5c0 100644 --- a/lib/ui/player_screen.dart +++ b/lib/ui/player_screen.dart @@ -287,26 +287,26 @@ class PlayerScreenVertical extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( - padding: EdgeInsets.symmetric(horizontal: 4.0), + padding: const EdgeInsets.symmetric(horizontal: 4.0), child: PlayerScreenTopRow( - textSize: 20.spMax, - iconSize: 24.spMax, + textSize: 14.spMax, + iconSize: 20.spMax, ), ), - Flexible(child: const BigAlbumArt()), + const Flexible(child: BigAlbumArt()), Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: PlayerTextSubtext(textSize: 32.spMax), + child: PlayerTextSubtext(textSize: 24.spMax), ), - SeekBar(textSize: 20.spMax), + SeekBar(textSize: 14.spMax), Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: PlaybackControls(40.spMax), + child: PlaybackControls(32.spMax), ), Padding( padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16.0), - child: BottomBarControls(size: 28.spMax), + child: BottomBarControls(size: 22.spMax), ) ], )); @@ -350,7 +350,7 @@ class PlayerScreenDesktop extends StatelessWidget { const EdgeInsets.symmetric(vertical: 0, horizontal: 16.0), child: BottomBarControls( size: 16.sp, - showLyricsButton: false, + desktopMode: true, ), ) ]), @@ -359,7 +359,7 @@ class PlayerScreenDesktop extends StatelessWidget { const Expanded( flex: 2, child: Padding( - padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 24.0), + padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 16.0), child: _DesktopTabView(), )), ]); @@ -395,6 +395,7 @@ class _DesktopTabView extends StatelessWidget { child: TabBarView(children: [ QueueListWidget( closePlayer: FancyScaffold.of(context)!.closePanel, + isInsidePlayer: true, ), const LyricsWidget(), ]), @@ -478,53 +479,50 @@ class PlayerTextSubtext extends StatelessWidget { return const SizedBox(); } final currentMediaItem = snapshot.data!; - return Column(mainAxisSize: MainAxisSize.min, children: [ - SizedBox( - height: textSize * 1.25, - width: double.infinity, - child: FitOrScrollText( + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + FitOrScrollText( key: Key(currentMediaItem.displayTitle!), text: currentMediaItem.displayTitle!, maxLines: 1, style: TextStyle( - fontSize: textSize, fontWeight: FontWeight.bold))), - // child: currentMediaItem.displayTitle!.length >= 26 - // ? Marquee( - // key: Key(currentMediaItem.displayTitle!), - // text: currentMediaItem.displayTitle!, - // style: TextStyle( - // fontSize: textSize, fontWeight: FontWeight.bold), - // blankSpace: 32.0, - // startPadding: 0.0, - // accelerationDuration: const Duration(seconds: 1), - // pauseAfterRound: const Duration(seconds: 2), - // crossAxisAlignment: CrossAxisAlignment.start, - // fadingEdgeEndFraction: 0.05, - // fadingEdgeStartFraction: 0.05, - // ) - // : Text( - // currentMediaItem.displayTitle!, - // maxLines: 1, - // overflow: TextOverflow.ellipsis, - // textAlign: TextAlign.start, - // style: TextStyle( - // fontSize: textSize, fontWeight: FontWeight.bold), - // )), - const SizedBox(height: 4), - SizedBox( - width: double.infinity, - child: Text( - currentMediaItem.displaySubtitle ?? '', - maxLines: 1, - textAlign: TextAlign.start, - overflow: TextOverflow.clip, - style: TextStyle( - fontSize: textSize * 0.8, // 20% smaller - color: Theme.of(context).colorScheme.primary, + fontSize: textSize, fontWeight: FontWeight.bold)), + // child: currentMediaItem.displayTitle!.length >= 26 + // ? Marquee( + // key: Key(currentMediaItem.displayTitle!), + // text: currentMediaItem.displayTitle!, + // style: TextStyle( + // fontSize: textSize, fontWeight: FontWeight.bold), + // blankSpace: 32.0, + // startPadding: 0.0, + // accelerationDuration: const Duration(seconds: 1), + // pauseAfterRound: const Duration(seconds: 2), + // crossAxisAlignment: CrossAxisAlignment.start, + // fadingEdgeEndFraction: 0.05, + // fadingEdgeStartFraction: 0.05, + // ) + // : Text( + // currentMediaItem.displayTitle!, + // maxLines: 1, + // overflow: TextOverflow.ellipsis, + // textAlign: TextAlign.start, + // style: TextStyle( + // fontSize: textSize, fontWeight: FontWeight.bold), + // )), + const SizedBox(height: 2.0), + Text( + currentMediaItem.displaySubtitle ?? '', + maxLines: 1, + textAlign: TextAlign.start, + overflow: TextOverflow.clip, + style: TextStyle( + fontSize: textSize * 0.8, // 20% smaller + color: Theme.of(context).colorScheme.primary, + ), ), - ), - ), - ]); + ]); }); } } @@ -560,6 +558,47 @@ class QualityInfoWidget extends StatelessWidget { } } +void Function([TapUpDetails?]) _onMenuPressedCallback(BuildContext context) { + return ([details]) { + final currentMediaItem = audioHandler.mediaItem.value!; + Track t = Track.fromMediaItem(currentMediaItem); + MenuSheet m = MenuSheet(context, navigateCallback: () { + // close player + FancyScaffold.of(context)?.closePanel(); + }); + if (currentMediaItem.extras!['show'] == null) { + m.defaultTrackMenu(t, + options: [m.sleepTimer(), m.wakelock()], details: details); + } else { + m.defaultShowEpisodeMenu(Show.fromJson(currentMediaItem.extras!['show']), + ShowEpisode.fromMediaItem(currentMediaItem), + options: [m.sleepTimer(), m.wakelock()], details: details); + } + }; +} + +class PlayerMenuButtonDesktop extends StatelessWidget { + final double size; + + const PlayerMenuButtonDesktop({super.key, required this.size}); + + @override + Widget build(BuildContext context) { + return InkWell( + customBorder: const CircleBorder(), + onTapUp: _onMenuPressedCallback(context), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon( + Icons.more_vert, + semanticLabel: "Options".i18n, + size: size, + ), + ), + ); + } +} + class PlayerMenuButton extends StatelessWidget { final double size; const PlayerMenuButton({super.key, required this.size}); @@ -567,28 +606,12 @@ class PlayerMenuButton extends StatelessWidget { @override Widget build(BuildContext context) { return IconButton( - iconSize: size, - icon: Icon( - Icons.more_vert, - semanticLabel: "Options".i18n, - ), - onPressed: () { - final currentMediaItem = audioHandler.mediaItem.value!; - Track t = Track.fromMediaItem(currentMediaItem); - MenuSheet m = MenuSheet(context, navigateCallback: () { - // close player - FancyScaffold.of(context)?.closePanel(); - }); - if (currentMediaItem.extras!['show'] == null) { - m.defaultTrackMenu(t, options: [m.sleepTimer(), m.wakelock()]); - } else { - m.defaultShowEpisodeMenu( - Show.fromJson(currentMediaItem.extras!['show']), - ShowEpisode.fromMediaItem(currentMediaItem), - options: [m.sleepTimer(), m.wakelock()]); - } - }, - ); + iconSize: size, + icon: Icon( + Icons.more_vert, + semanticLabel: "Options".i18n, + ), + onPressed: _onMenuPressedCallback(context)); } } @@ -767,7 +790,7 @@ class PlaybackControls extends StatelessWidget { : Theme.of(context).brightness == Brightness.light ? provider.dominantColor! : darken(provider.dominantColor!); - return PlayPauseButton(size * 2.0, + return PlayPauseButton(size * 2.25, filled: true, color: color, iconColor: Color.lerp( @@ -975,7 +998,8 @@ class PlayerScreenTopRow extends StatelessWidget { text: TextSpan(children: [ if (!short) TextSpan( - text: 'PLAYING FROM\n'.i18n, + text: + '${'Playing from:'.i18n.toUpperCase().withoutLast(1)}\n', style: TextStyle( fontWeight: FontWeight.bold, letterSpacing: 1.5, @@ -1118,11 +1142,11 @@ class _SeekBarState extends State { class BottomBarControls extends StatelessWidget { final double size; final bool - showLyricsButton; // removed in desktop mode, because there's a tabbed view which includes it + desktopMode; // removed in desktop mode, because there's a tabbed view which includes it const BottomBarControls({ super.key, required this.size, - this.showLyricsButton = true, + this.desktopMode = false, }); @override @@ -1145,7 +1169,7 @@ class BottomBarControls extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ QualityInfoWidget(textSize: size * 0.75), - if (showLyricsButton) + if (!desktopMode) IconButton( iconSize: size, icon: Icon( @@ -1185,7 +1209,9 @@ class BottomBarControls extends StatelessWidget { // }, // ), FavoriteButton(size: size * 0.85), - PlayerMenuButton(size: size) + desktopMode + ? PlayerMenuButtonDesktop(size: size) + : PlayerMenuButton(size: size) ], ); } diff --git a/lib/ui/queue_screen.dart b/lib/ui/queue_screen.dart index b9a2c65..1562ddb 100644 --- a/lib/ui/queue_screen.dart +++ b/lib/ui/queue_screen.dart @@ -6,7 +6,6 @@ import 'package:flutter/services.dart'; import 'package:freezer/api/definitions.dart'; import 'package:freezer/api/player/audio_handler.dart'; import 'package:freezer/translations.i18n.dart'; -import 'package:freezer/ui/fancy_scaffold.dart'; import 'package:freezer/ui/menu.dart'; import 'package:freezer/ui/tiles.dart'; diff --git a/lib/ui/settings_screen.dart b/lib/ui/settings_screen.dart index c1c1995..598c408 100644 --- a/lib/ui/settings_screen.dart +++ b/lib/ui/settings_screen.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'dart:math'; import 'package:country_pickers/country.dart'; @@ -8,12 +7,10 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; -import 'package:flutter_material_color_picker/flutter_material_color_picker.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/web_symbols_icons.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:freezer/api/definitions.dart'; -import 'package:google_fonts/google_fonts.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:scrobblenaut/scrobblenaut.dart'; @@ -96,8 +93,7 @@ class _SettingsScreenState extends State { title: Text(l.name), subtitle: Text("${l.locale}-${l.country}"), onTap: () async { - setState(() => settings.language = - "${l.locale}_${l.country}"); + settings.language = "${l.locale}_${l.country}"; await settings.save(); showDialog( context: context, @@ -173,55 +169,54 @@ class _AppearanceSettingsState extends State { subtitle: Text( '${'Currently'.i18n}: ${settings.theme.toString().split('.').lastItem}'), leading: const Icon(Icons.color_lens), - onTap: settings.materialYouAccent - ? null - : () { - showDialog( - context: context, - builder: (context) { - return SimpleDialog( - title: Text('Select theme'.i18n), - children: [ - SimpleDialogOption( - child: Text('Light'.i18n), - onPressed: () { - settings.theme = Themes.Light; - settings.save(); - updateTheme(context); - Navigator.of(context).pop(); - }, - ), - SimpleDialogOption( - child: Text('Dark'.i18n), - onPressed: () { - settings.theme = Themes.Dark; - settings.save(); - updateTheme(context); - Navigator.of(context).pop(); - }, - ), - SimpleDialogOption( - child: Text('Black (AMOLED)'.i18n), - onPressed: () { - settings.theme = Themes.Black; - settings.save(); - updateTheme(context); - Navigator.of(context).pop(); - }, - ), - SimpleDialogOption( - child: Text('Deezer (Dark)'.i18n), - onPressed: () { - settings.theme = Themes.Deezer; - settings.save(); - updateTheme(context); - Navigator.of(context).pop(); - }, - ), - ], - ); - }); - }, + enabled: !settings.materialYouAccent, + onTap: () { + showDialog( + context: context, + builder: (context) { + return SimpleDialog( + title: Text('Select theme'.i18n), + children: [ + SimpleDialogOption( + child: Text('Light'.i18n), + onPressed: () { + settings.theme = Themes.Light; + settings.save(); + updateTheme(context); + Navigator.of(context).pop(); + }, + ), + SimpleDialogOption( + child: Text('Dark'.i18n), + onPressed: () { + settings.theme = Themes.Dark; + settings.save(); + updateTheme(context); + Navigator.of(context).pop(); + }, + ), + SimpleDialogOption( + child: Text('Black (AMOLED)'.i18n), + onPressed: () { + settings.theme = Themes.Black; + settings.save(); + updateTheme(context); + Navigator.of(context).pop(); + }, + ), + SimpleDialogOption( + child: Text('Deezer (Dark)'.i18n), + onPressed: () { + settings.theme = Themes.Deezer; + settings.save(); + updateTheme(context); + Navigator.of(context).pop(); + }, + ), + ], + ); + }); + }, ), SwitchListTile( title: Text('Use system theme'.i18n), @@ -512,21 +507,23 @@ class _ColorPickerState extends State<_ColorPicker> { content: SizedBox( height: 600.0, width: min(MediaQuery.of(context).size.width * 0.9, 600.0), - child: ColorPicker( - width: 56.0, - height: 56.0, - borderRadius: 50.0, - onColorChanged: (color) => setState(() => this.color = color), - color: color, - showColorCode: true, - pickersEnabled: const { - ColorPickerType.both: false, - ColorPickerType.primary: true, - ColorPickerType.accent: false, - ColorPickerType.bw: false, - ColorPickerType.custom: true, - ColorPickerType.wheel: true, - }, + child: SingleChildScrollView( + child: ColorPicker( + width: 56.0, + height: 56.0, + borderRadius: 50.0, + onColorChanged: (color) => setState(() => this.color = color), + color: color, + showColorCode: true, + pickersEnabled: const { + ColorPickerType.both: false, + ColorPickerType.primary: true, + ColorPickerType.accent: false, + ColorPickerType.bw: false, + ColorPickerType.custom: true, + ColorPickerType.wheel: true, + }, + ), ), ), actions: [ @@ -1476,11 +1473,12 @@ class _GeneralSettingsState extends State { child: Text('Log out & Exit'.i18n), onPressed: () async { try { - audioHandler.stop(); + await audioHandler.stop(); + await DownloadManager.platform + .invokeMethod("kill"); } catch (e) {} await logOut(); - await DownloadManager.platform - .invokeMethod("kill"); + SystemNavigator.pop(); }, ) diff --git a/lib/ui/tiles.dart b/lib/ui/tiles.dart index c535c78..1e96218 100644 --- a/lib/ui/tiles.dart +++ b/lib/ui/tiles.dart @@ -13,7 +13,7 @@ import 'cached_image.dart'; import 'dart:async'; -typedef SecondaryTapCallback = void Function(TapDownDetails?); +typedef SecondaryTapCallback = void Function(TapUpDetails?); VoidCallback? normalizeSecondary(SecondaryTapCallback? callback) { if (callback == null) return null; @@ -90,7 +90,7 @@ class TrackTile extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - onSecondaryTapDown: onSecondary, + onSecondaryTapUp: onSecondary, child: ListTile( title: StreamBuilder( stream: audioHandler.mediaItem, @@ -175,7 +175,7 @@ class AlbumTile extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - onSecondaryTapDown: onSecondary, + onSecondaryTapUp: onSecondary, child: ListTile( title: Text( album!.title!, @@ -212,7 +212,7 @@ class ArtistTile extends StatelessWidget { borderRadius: const BorderRadius.all(Radius.circular(4.0)), onTap: onTap, onLongPress: normalizeSecondary(onSecondary), - onSecondaryTapDown: onSecondary, + onSecondaryTapUp: onSecondary, child: Column(mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 4.0), CachedImage( @@ -256,7 +256,7 @@ class PlaylistTile extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - onSecondaryTapDown: onSecondary, + onSecondaryTapUp: onSecondary, child: ListTile( title: Text( playlist!.title!, @@ -317,7 +317,7 @@ class PlaylistCardTile extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - onSecondaryTapDown: onSecondary, + onSecondaryTapUp: onSecondary, child: SizedBox( height: 180.0, child: InkWell( @@ -814,7 +814,7 @@ class ShowEpisodeTile extends StatelessWidget { return InkWell( onTap: onTap, onLongPress: normalizeSecondary(onSecondary), - onSecondaryTapDown: onSecondary, + onSecondaryTapUp: onSecondary, child: Column( mainAxisSize: MainAxisSize.min, children: [ diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index f847d24..606c5a6 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,16 +6,12 @@ #include "generated_plugin_registrant.h" -#include #include #include #include #include void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin"); - desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar); g_autoptr(FlPluginRegistrar) dynamic_color_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 9f7f4e8..f1b77ef 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,7 +3,6 @@ # list(APPEND FLUTTER_PLUGIN_LIST - desktop_webview_window dynamic_color isar_flutter_libs media_kit_libs_linux diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index c9c340a..fcd3f4c 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,7 +8,6 @@ import Foundation import audio_service import audio_session import connectivity_plus -import desktop_webview_window import dynamic_color import flutter_local_notifications import isar_flutter_libs @@ -24,7 +23,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) - DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 44f27b8..25ff371 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -322,14 +322,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.8" - desktop_webview_window: - dependency: "direct main" - description: - name: desktop_webview_window - sha256: "57cf20d81689d5cbb1adfd0017e96b669398a669d927906073b0e42fc64111c0" - url: "https://pub.dev" - source: hosted - version: "0.2.3" dio: dependency: "direct main" description: @@ -354,14 +346,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.3" - draggable_scrollbar: - dependency: "direct main" - description: - name: draggable_scrollbar - sha256: a906e27fc1ee056e2942d66989dd0cb5b70a361e7d44af566cfa1b584054eac3 - url: "https://pub.dev" - source: hosted - version: "0.1.0" dynamic_color: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index fa5b1df..2b8bd72 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Freezer # The following line prevents the package from being accidentally published to # pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -18,163 +18,162 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 0.6.14+1 environment: - sdk: '>=3.0.0 <4.0.0' + sdk: ">=3.0.0 <4.0.0" dependencies: - flutter: - sdk: flutter - flutter_localizations: - sdk: flutter - spotify: ^0.12.0 - flutter_displaymode: ^0.6.0 - crypto: ^3.0.3 - http: ^1.1.0 - cookie_jar: ^4.0.8 - json_annotation: ^4.8.1 - path_provider: ^2.0.15 - path: ^1.6.4 - sqflite: ^2.0.0+3 - permission_handler: ^10.4.3 - intl: ^0.18.0 - filesize: ^2.0.1 - fluttertoast: ^8.0.8 - palette_generator: ^0.3.0 - flutter_material_color_picker: ^1.0.5 - flutter_inappwebview: ^5.3.2 - country_pickers: ^2.0.0 - move_to_background: ^1.0.1 - flutter_local_notifications: ^15.1.0+1 - collection: ^1.17.1 - random_string: ^2.0.1 - async: ^2.6.1 - html: ^0.15.0 - flutter_screenutil: ^5.0.0+2 - marquee: ^2.2.0 - flutter_cache_manager: ^3.0.0 - cached_network_image: ^3.1.0 - i18n_extension: ^9.0.2 - fluttericon: ^2.0.0 - url_launcher: ^6.0.5 - uni_links: ^0.5.1 - numberpicker: ^2.1.1 - quick_actions: ^1.0.5 - photo_view: ^0.14.0 - draggable_scrollbar: ^0.1.0 - scrobblenaut: - git: - url: https://github.com/Pato05/Scrobblenaut.git - ref: main - open_file: ^3.0.3 - version: ^3.0.2 - wakelock_plus: ^1.1.1 - google_fonts: ^5.1.0 - equalizer: - git: https://github.com/gladson97/equalizer.git - audio_session: ^0.1.6 - audio_service: ^0.18.1 - provider: ^6.0.0 - hive_flutter: ^1.1.0 - connectivity_plus: ^4.0.1 - share_plus: ^7.0.2 - disk_space_plus: ^0.2.3 - dynamic_color: ^1.6.6 - package_info_plus: ^4.0.2 - encrypt: ^5.0.1 - dart_blowfish: - git: - url: https://github.com/Pato05/dart_blowfish.git - ref: main - logging: ^1.2.0 - just_audio: ^0.9.35 - just_audio_media_kit: - git: https://github.com/Pato05/just_audio_media_kit.git - rxdart: ^0.27.7 - flutter_isolate: ^2.0.4 - isar: ^3.1.0+1 - isar_flutter_libs: ^3.1.0+1 - flutter_background_service: ^5.0.1 - audio_service_mpris: ^0.1.0 - desktop_webview_window: ^0.2.3 - dio: ^5.3.3 - dio_cookie_manager: ^3.1.1 - flutter_cache_manager_hive: - git: https://github.com/Pato05/flutter_cache_manager_hive.git - flex_color_picker: ^3.3.0 - - #deezcryptor: - #path: deezcryptor/ + flutter: + sdk: flutter + flutter_localizations: + sdk: flutter + spotify: ^0.12.0 + flutter_displaymode: ^0.6.0 + crypto: ^3.0.3 + http: ^1.1.0 + cookie_jar: ^4.0.8 + json_annotation: ^4.8.1 + path_provider: ^2.0.15 + path: ^1.6.4 + sqflite: ^2.0.0+3 + permission_handler: ^10.4.3 + intl: ^0.18.0 + filesize: ^2.0.1 + fluttertoast: ^8.0.8 + palette_generator: ^0.3.0 + flutter_material_color_picker: ^1.0.5 + flutter_inappwebview: ^5.3.2 + country_pickers: ^2.0.0 + move_to_background: ^1.0.1 + flutter_local_notifications: ^15.1.0+1 + collection: ^1.17.1 + random_string: ^2.0.1 + async: ^2.6.1 + html: ^0.15.0 + flutter_screenutil: ^5.0.0+2 + marquee: ^2.2.0 + flutter_cache_manager: ^3.0.0 + cached_network_image: ^3.1.0 + i18n_extension: ^9.0.2 + fluttericon: ^2.0.0 + url_launcher: ^6.0.5 + uni_links: ^0.5.1 + numberpicker: ^2.1.1 + quick_actions: ^1.0.5 + photo_view: ^0.14.0 + scrobblenaut: + git: + url: https://github.com/Pato05/Scrobblenaut.git + ref: main + open_file: ^3.0.3 + version: ^3.0.2 + wakelock_plus: ^1.1.1 + google_fonts: ^5.1.0 + equalizer: + git: https://github.com/gladson97/equalizer.git + audio_session: ^0.1.6 + audio_service: ^0.18.1 + provider: ^6.0.0 + hive_flutter: ^1.1.0 + connectivity_plus: ^4.0.1 + share_plus: ^7.0.2 + disk_space_plus: ^0.2.3 + dynamic_color: ^1.6.6 + package_info_plus: ^4.0.2 + encrypt: ^5.0.1 + dart_blowfish: + git: + url: https://github.com/Pato05/dart_blowfish.git + ref: main + logging: ^1.2.0 + just_audio: ^0.9.35 + just_audio_media_kit: + git: https://github.com/Pato05/just_audio_media_kit.git + rxdart: ^0.27.7 + flutter_isolate: ^2.0.4 + isar: ^3.1.0+1 + isar_flutter_libs: ^3.1.0+1 + flutter_background_service: ^5.0.1 + audio_service_mpris: ^0.1.0 + dio: ^5.3.3 + dio_cookie_manager: ^3.1.1 + flutter_cache_manager_hive: + git: https://github.com/Pato05/flutter_cache_manager_hive.git + flex_color_picker: + ^3.3.0 + + #deezcryptor: + #path: deezcryptor/ dev_dependencies: - flutter_test: - sdk: flutter - json_serializable: ^6.0.1 - build_runner: ^2.4.6 - hive_generator: ^2.0.0 - flutter_lints: ^2.0.3 - isar_generator: ^3.1.0+1 + flutter_test: + sdk: flutter + json_serializable: ^6.0.1 + build_runner: ^2.4.6 + hive_generator: ^2.0.0 + flutter_lints: ^2.0.3 + isar_generator: ^3.1.0+1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter. flutter: - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg - assets: - - assets/cover.jpg - - assets/cover_thumb.jpg - - assets/icon.png - - assets/favorites_thumb.jpg - - assets/browse_icon.png + assets: + - assets/cover.jpg + - assets/cover_thumb.jpg + - assets/icon.png + - assets/favorites_thumb.jpg + - assets/browse_icon.png - fonts: - # - family: Montserrat - # fonts: - # - asset: assets/fonts/Montserrat-Regular.ttf - # - asset: assets/fonts/Montserrat-Bold.ttf - # weight: 700 - # - asset: assets/fonts/Montserrat-Italic.ttf - # style: italic - - family: MabryPro - fonts: - - asset: assets/fonts/MabryPro.otf - - asset: assets/fonts/MabryProItalic.otf - style: italic - - asset: assets/fonts/MabryProBold.otf - weight: 700 - - asset: assets/fonts/MabryProBlack.otf - weight: 900 + fonts: + # - family: Montserrat + # fonts: + # - asset: assets/fonts/Montserrat-Regular.ttf + # - asset: assets/fonts/Montserrat-Bold.ttf + # weight: 700 + # - asset: assets/fonts/Montserrat-Italic.ttf + # style: italic + - family: MabryPro + fonts: + - asset: assets/fonts/MabryPro.otf + - asset: assets/fonts/MabryProItalic.otf + style: italic + - asset: assets/fonts/MabryProBold.otf + weight: 700 + - asset: assets/fonts/MabryProBlack.otf + weight: 900 - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 7349940..1dc0921 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,7 +7,6 @@ #include "generated_plugin_registrant.h" #include -#include #include #include #include @@ -18,8 +17,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); - DesktopWebviewWindowPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin")); DynamicColorPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); IsarFlutterLibsPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 56ca898..79c3036 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,7 +4,6 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus - desktop_webview_window dynamic_color isar_flutter_libs media_kit_libs_windows_audio