From e89cda83c240af5e3916a0cf99f8f9630ba59d08 Mon Sep 17 00:00:00 2001 From: Dr-Blank <64108942+Dr-Blank@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:58:52 -0400 Subject: [PATCH] feat: add notification settings customisation options --- .../ic_stat_notification_logo.png | Bin 0 -> 1139 bytes .../ic_stat_notification_logo.png | Bin 0 -> 743 bytes .../ic_stat_notification_logo.png | Bin 0 -> 1741 bytes .../ic_stat_notification_logo.png | Bin 0 -> 2519 bytes .../ic_stat_notification_logo.png | Bin 0 -> 3869 bytes .../player/core/audiobook_player.dart | 51 ++- lib/features/player/core/init.dart | 62 ++++ lib/main.dart | 20 +- lib/settings/app_settings_provider.dart | 22 +- lib/settings/app_settings_provider.g.dart | 2 +- lib/settings/models/app_settings.dart | 47 +++ lib/settings/models/app_settings.freezed.dart | 343 +++++++++++++++++- lib/settings/models/app_settings.g.dart | 52 +++ pubspec.lock | 11 +- pubspec.yaml | 8 +- 15 files changed, 574 insertions(+), 44 deletions(-) create mode 100644 android/app/src/main/res/drawable-hdpi/ic_stat_notification_logo.png create mode 100644 android/app/src/main/res/drawable-mdpi/ic_stat_notification_logo.png create mode 100644 android/app/src/main/res/drawable-xhdpi/ic_stat_notification_logo.png create mode 100644 android/app/src/main/res/drawable-xxhdpi/ic_stat_notification_logo.png create mode 100644 android/app/src/main/res/drawable-xxxhdpi/ic_stat_notification_logo.png create mode 100644 lib/features/player/core/init.dart diff --git a/android/app/src/main/res/drawable-hdpi/ic_stat_notification_logo.png b/android/app/src/main/res/drawable-hdpi/ic_stat_notification_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b2ccfefbb76bc96838fa03fd96fd0cab2bab31e4 GIT binary patch literal 1139 zcmV-(1dRKMP)9LGJ|jFL6k3z=jv!;F6q9%+aCBb8eu{f0hFA;0XqUjRS$e zmq@;_OTg}jo1G^h9CgP@F&cowXI6*QrBl9$+EDjCTxZy&Qg;a@WA`MSomZfdI4DAs zakkF}G?em51L%zl&OPatumFvutYgSA zh5hVtkVA`&NF%vx14^Jg7{?{pRUI9?tBR0-tUY4ao3f&@mbyGYK!WXubRf2}0@*t9 z3e-i@hYA526E%>n)7Bn<+G=``*41%3t+e?_q^zx?n7V01Oas=mmr~Yx3#F3q|4RSaL&TO}*c?!?aHN*`Hn4Z*YBh+mm z9eW+l5tC;i1FA7kO`o9Fqj_N#u|DUsNC#&sHn$^^7zR-RnNAtKMy{hEo6dIHJEK0E zcUt^=sn7HZBRju9`i!+5Xv0jR(+X>+0O|isx5}n&`;rr=FCb(3balOq?$c>sOY{oJ z>uQ_Dd+l9OCEq{>)MB2?P$r#+ejtg?4IY0f_lb(cFpd>Sw*Vahx)bS=ubZU6mIJh; z)`5(On0Mu^lr?q9s2M;y+oDb}MaTe>58ZQga4Ho5qiOV&Vg@pxhVeA7D=7fo@?P)vF2gt5 z^SlR&p+A-Cq`>ya9fxl&{q=AY->B=$CUBO(1q`S(f$7w}PzrNkFc3nZn0^~<$M!3! zz-a=D8L%}%pat7Ge0}DdaDnqo+>XG9umJtl$`s!>`Tp4=(rw0m8}`C7*aEe15AMP$ zxB#s-UR#nEY!UTCs6pUOoWN@w7vU@HfsHT>yP+O7z%3I#Os#mGjeiEv1=R@19+|;& z?APEkD2q+-6Li?eOh6gRUI(v20(a;)Bk(e*z%~NE*u-Z-0$TA9JaIBuOuG+(A8`U& zU5|pC3@+0?1W!W(Gw6?iuJBc;U}9yPDN%=9gm)&CNPqzqistZxcTi6E6g1)cZoWm- zx&&(#3i&XAKkW-JIETtVOpKjo)1JEm)9Al|HK6#ECa_rfmcT5~4O6B^@&9%ObW0jQ zw?{WZ-@$QE+5;>i*93;>>l?`w`zm%}&~@&BR$@{L$ZQcoYAaQ3i4N~Q41yk1%KSQq zkMwm@{DOTUm9Dk^pi9v0{zp+~Alq`d54y|RCjV9H!*f46`URM;gATSTSal{fHv`vb zlj*RpKtD8Un<8TLC#`_W@a+j=V}DZov!gtQk0~)w}4{ika{clQ+@c2P=aYJMIh|k^Qs}!aC4jrK_l{ zvX(>{*jDLFeg^s)=EG71)Segt6VdCw0S!#9$2ew5oPi1GHJ^|O0&mBhoq+!mNHSqK Z<}YeL8YrT5KlK0r002ovPDHLkV1ie0Sik@P literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-xhdpi/ic_stat_notification_logo.png b/android/app/src/main/res/drawable-xhdpi/ic_stat_notification_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..753dbe0c3a67ff72d54481eb14abdc23cb329f22 GIT binary patch literal 1741 zcmV;;1~U1HP)4!6^*#WD)`>w=r_Ao~98wv+Ik1rXzv=hRIn-U z?@LXHZ=HZ1?_kgaxfg08K!*~X1Wp8_>0mK+yYQ@=I{=(c-TWZepLaLmcoqfxi6_a2&Xd!6%2=m+0s~U}7Nw>U6cUz(6pU zHvRd2D9^71bafT#MPOo3Tm_tAQ;3H_Z!nI5Cj|o9>ZRZs>L!#Bc#3)>tgh4uJB9B( zczzzd29BsF0e$xs-~n(!1p+toZ4|f?Wg06G7{T|c1q5ED&5;QRoPo@v$QG3(n>Y!9 zyjh(fNd}r;1A)R7xRCaDfm|v9HU;w}2(G2ytLsG|?=8ABzkx`&XqMqlmLNIMla8=ATN6`;9j4 z0nNmA+PwL{jHOD2STe&I2fAkS9!b<(QW40H z5=_9DpqKHIcPxtJ4(JtT-ZAw!dNEz&CA9Ihm#VagKv)euhyl$*x~a!eD3r@#q6(z9 zrrtxoskd3~VB~*Jr9F*WOdyc(u1J&U20UeH&ykrV1U%U5!8}*&iQK1YmA73c1Of@e zzwXdKKinI`OQSE!dri0;nCE!CqA9hgWJVy6IFny(TTj&s1z*f-7dHNVyq^oqX-q-N z&bYO*$(n#sdNLRZR-&M3L9Rf+>v(s5$3 z)Y)JP_!aoiPSw*^5+d~=p!t|VP5`C^eSv3O|3~m(qKOTwl%OS1JTH?ufv^f4`So1u zuN&J8_ATIe9Z}xvluSxp2m}(InY~r>bY)yP9Lzv@)AP(qJqg&iCn?RwV#?rwzUF+V z65F~F2&K41w1yzI)f j)RjP{n#Oe2>IwV@0Tn%7>Ek}!00000NkvXXu0mjfd)6v$ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_stat_notification_logo.png b/android/app/src/main/res/drawable-xxhdpi/ic_stat_notification_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fee76705b0746649b7198162f279ea5314479597 GIT binary patch literal 2519 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 literal 0 HcmV?d00001 diff --git a/lib/features/player/core/audiobook_player.dart b/lib/features/player/core/audiobook_player.dart index 106a547..6fe3bfa 100644 --- a/lib/features/player/core/audiobook_player.dart +++ b/lib/features/player/core/audiobook_player.dart @@ -8,6 +8,9 @@ import 'package:just_audio/just_audio.dart'; import 'package:just_audio_background/just_audio_background.dart'; import 'package:logging/logging.dart'; import 'package:shelfsdk/audiobookshelf_api.dart'; +import 'package:vaani/settings/app_settings_provider.dart'; +import 'package:vaani/settings/models/app_settings.dart'; +import 'package:vaani/shared/extensions/model_conversions.dart'; final _logger = Logger('AudiobookPlayer'); @@ -81,6 +84,7 @@ class AudiobookPlayer extends AudioPlayer { List? downloadedUris, Uri? artworkUri, }) async { + final appSettings = loadOrCreateAppSettings(); // if the book is null, stop the player if (book == null) { _book = null; @@ -128,8 +132,10 @@ class AudiobookPlayer extends AudioPlayer { // Specify a unique ID for each media item: id: book.libraryItemId + track.index.toString(), // Metadata to display in the notification: - album: book.metadata.title, - title: book.metadata.title ?? track.title, + title: appSettings.notificationSettings.primaryTitle + .formatNotificationTitle(book), + album: appSettings.notificationSettings.secondaryTitle + .formatNotificationTitle(book), artUri: artworkUri ?? Uri.parse( '$baseUrl/api/items/${book.libraryItemId}/cover?token=$token&width=800', @@ -198,7 +204,7 @@ class AudiobookPlayer extends AudioPlayer { @override Stream get positionStream { - // return the positioninbook stream + // return the positionInBook stream return super.positionStream.map((position) { if (_book == null) { return Duration.zero; @@ -267,3 +273,42 @@ Uri _getUri( return uri ?? Uri.parse('${baseUrl.toString()}${track.contentUrl}?token=$token'); } + +extension FormatNotificationTitle on String { + String formatNotificationTitle(BookExpanded book) { + return replaceAllMapped( + RegExp(r'\$(\w+)'), + (match) { + final type = match.group(1); + return NotificationTitleType.values + .firstWhere((element) => element.stringValue == type) + .extractFrom(book) ?? + match.group(0) ?? + ''; + }, + ); + } +} + +extension NotificationTitleUtils on NotificationTitleType { + String? extractFrom(BookExpanded book) { + var bookMetadataExpanded = book.metadata.asBookMetadataExpanded; + switch (this) { + case NotificationTitleType.bookTitle: + return bookMetadataExpanded.title; + case NotificationTitleType.chapterTitle: + // TODO: implement chapter title; depends on https://github.com/Dr-Blank/Vaani/issues/2 + return bookMetadataExpanded.title; + case NotificationTitleType.author: + return bookMetadataExpanded.authorName; + case NotificationTitleType.narrator: + return bookMetadataExpanded.narratorName; + case NotificationTitleType.series: + return bookMetadataExpanded.seriesName; + case NotificationTitleType.subtitle: + return bookMetadataExpanded.subtitle; + case NotificationTitleType.year: + return bookMetadataExpanded.publishedYear; + } + } +} diff --git a/lib/features/player/core/init.dart b/lib/features/player/core/init.dart new file mode 100644 index 0000000..c61637b --- /dev/null +++ b/lib/features/player/core/init.dart @@ -0,0 +1,62 @@ +import 'package:audio_service/audio_service.dart'; +import 'package:audio_session/audio_session.dart'; +import 'package:just_audio_background/just_audio_background.dart' + show JustAudioBackground, NotificationConfig; +import 'package:just_audio_media_kit/just_audio_media_kit.dart' + show JustAudioMediaKit; +import 'package:vaani/settings/app_settings_provider.dart'; +import 'package:vaani/settings/models/app_settings.dart'; + +Future configurePlayer() async { + // for playing audio on windows, linux + JustAudioMediaKit.ensureInitialized(); + + // for configuring how this app will interact with other audio apps + final session = await AudioSession.instance; + await session.configure(const AudioSessionConfiguration.speech()); + + final appSettings = loadOrCreateAppSettings(); + + // for playing audio in the background + await JustAudioBackground.init( + androidNotificationChannelId: 'com.vaani.bg_demo.channel.audio', + androidNotificationChannelName: 'Audio playback', + androidNotificationOngoing: true, + androidStopForegroundOnPause: true, + androidNotificationChannelDescription: 'Audio playback in the background', + androidNotificationIcon: 'drawable/ic_stat_notification_logo', + rewindInterval: appSettings.notificationSettings.rewindInterval, + fastForwardInterval: appSettings.notificationSettings.fastForwardInterval, + androidShowNotificationBadge: false, + notificationConfigBuilder: (state) { + final controls = [ + if (appSettings.notificationSettings.mediaControls + .contains(NotificationMediaControl.skipToPreviousChapter) && + state.hasPrevious) + MediaControl.skipToPrevious, + if (appSettings.notificationSettings.mediaControls + .contains(NotificationMediaControl.rewind)) + MediaControl.rewind, + if (state.playing) MediaControl.pause else MediaControl.play, + if (appSettings.notificationSettings.mediaControls + .contains(NotificationMediaControl.fastForward)) + MediaControl.fastForward, + if (appSettings.notificationSettings.mediaControls + .contains(NotificationMediaControl.skipToNextChapter) && + state.hasNext) + MediaControl.skipToNext, + if (appSettings.notificationSettings.mediaControls + .contains(NotificationMediaControl.stop)) + MediaControl.stop, + ]; + return NotificationConfig( + controls: controls, + systemActions: const { + MediaAction.seek, + MediaAction.seekForward, + MediaAction.seekBackward, + }, + ); + }, + ); +} diff --git a/lib/main.dart b/lib/main.dart index 381d27f..cea6f7f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,15 +1,11 @@ -import 'package:audio_session/audio_session.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:just_audio_background/just_audio_background.dart' - show JustAudioBackground; -import 'package:just_audio_media_kit/just_audio_media_kit.dart' - show JustAudioMediaKit; import 'package:logging/logging.dart'; import 'package:vaani/api/server_provider.dart'; import 'package:vaani/db/storage.dart'; import 'package:vaani/features/downloads/providers/download_manager.dart'; import 'package:vaani/features/playback_reporting/providers/playback_reporter_provider.dart'; +import 'package:vaani/features/player/core/init.dart'; import 'package:vaani/features/player/providers/audiobook_player.dart'; import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart'; import 'package:vaani/router/router.dart'; @@ -31,22 +27,12 @@ void main() async { ); }); - // for playing audio on windows, linux - JustAudioMediaKit.ensureInitialized(); - // initialize the storage await initStorage(); - // for configuring how this app will interact with other audio apps - final session = await AudioSession.instance; - await session.configure(const AudioSessionConfiguration.speech()); + // initialize audio player + await configurePlayer(); - // for playing audio in the background - await JustAudioBackground.init( - androidNotificationChannelId: 'com.vaani.bg_demo.channel.audio', - androidNotificationChannelName: 'Audio playback', - androidNotificationOngoing: true, - ); // run the app runApp( diff --git a/lib/settings/app_settings_provider.dart b/lib/settings/app_settings_provider.dart index b23d18d..662a043 100644 --- a/lib/settings/app_settings_provider.dart +++ b/lib/settings/app_settings_provider.dart @@ -11,25 +11,29 @@ final _box = AvailableHiveBoxes.userPrefsBox; final _logger = Logger('AppSettingsProvider'); -model.AppSettings readFromBoxOrCreate() { +model.AppSettings loadOrCreateAppSettings() { // see if the settings are already in the box + model.AppSettings? settings; if (_box.isNotEmpty) { - final foundSettings = _box.getAt(0); - _logger.fine('found settings in box: $foundSettings'); - return foundSettings; + try { + settings = _box.getAt(0); + _logger.fine('found settings in box: $settings'); + } catch (e) { + _logger.warning('error reading settings from box: $e' + '\nclearing box'); + _box.clear(); + } } else { - // create a new settings object - const settings = model.AppSettings(); - _logger.fine('created new settings: $settings'); - return settings; + _logger.fine('no settings found in box, creating new settings'); } + return settings ?? const model.AppSettings(); } @Riverpod(keepAlive: true) class AppSettings extends _$AppSettings { @override model.AppSettings build() { - state = readFromBoxOrCreate(); + state = loadOrCreateAppSettings(); ref.listenSelf((_, __) { writeToBox(); }); diff --git a/lib/settings/app_settings_provider.g.dart b/lib/settings/app_settings_provider.g.dart index 4956783..2f321ee 100644 --- a/lib/settings/app_settings_provider.g.dart +++ b/lib/settings/app_settings_provider.g.dart @@ -6,7 +6,7 @@ part of 'app_settings_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$appSettingsHash() => r'e0e132b782b97f11d9791d4f1e45bf4ee67dd99b'; +String _$appSettingsHash() => r'f51d55f117692d4fb9f4b4febf02906c0953d334'; /// See also [AppSettings]. @ProviderFor(AppSettings) diff --git a/lib/settings/models/app_settings.dart b/lib/settings/models/app_settings.dart index 0591e9f..9dc5eb8 100644 --- a/lib/settings/models/app_settings.dart +++ b/lib/settings/models/app_settings.dart @@ -14,6 +14,7 @@ class AppSettings with _$AppSettings { @Default(ThemeSettings()) ThemeSettings themeSettings, @Default(PlayerSettings()) PlayerSettings playerSettings, @Default(DownloadSettings()) DownloadSettings downloadSettings, + @Default(NotificationSettings()) NotificationSettings notificationSettings, }) = _AppSettings; factory AppSettings.fromJson(Map json) => @@ -133,3 +134,49 @@ class DownloadSettings with _$DownloadSettings { factory DownloadSettings.fromJson(Map json) => _$DownloadSettingsFromJson(json); } + +@freezed +class NotificationSettings with _$NotificationSettings { + const factory NotificationSettings({ + @Default(Duration(seconds: 30)) Duration fastForwardInterval, + @Default(Duration(seconds: 10)) Duration rewindInterval, + @Default(true) bool progressBarIsChapterProgress, + @Default('\$bookTitle') String primaryTitle, + @Default('\$author') String secondaryTitle, + @Default( + [ + NotificationMediaControl.rewind, + NotificationMediaControl.fastForward, + NotificationMediaControl.skipToPreviousChapter, + NotificationMediaControl.skipToNextChapter, + ], + ) + List mediaControls, + }) = _NotificationSettings; + + factory NotificationSettings.fromJson(Map json) => + _$NotificationSettingsFromJson(json); +} + +enum NotificationTitleType { + chapterTitle('chapterTitle'), + bookTitle('bookTitle'), + author('author'), + subtitle('subtitle'), + series('series'), + narrator('narrator'), + year('year'); + + const NotificationTitleType(this.stringValue); + + final String stringValue; +} + +enum NotificationMediaControl { + fastForward, + rewind, + speedToggle, + stop, + skipToNextChapter, + skipToPreviousChapter, +} diff --git a/lib/settings/models/app_settings.freezed.dart b/lib/settings/models/app_settings.freezed.dart index 90d2cd2..204d314 100644 --- a/lib/settings/models/app_settings.freezed.dart +++ b/lib/settings/models/app_settings.freezed.dart @@ -23,6 +23,8 @@ mixin _$AppSettings { ThemeSettings get themeSettings => throw _privateConstructorUsedError; PlayerSettings get playerSettings => throw _privateConstructorUsedError; DownloadSettings get downloadSettings => throw _privateConstructorUsedError; + NotificationSettings get notificationSettings => + throw _privateConstructorUsedError; /// Serializes this AppSettings to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -43,11 +45,13 @@ abstract class $AppSettingsCopyWith<$Res> { $Res call( {ThemeSettings themeSettings, PlayerSettings playerSettings, - DownloadSettings downloadSettings}); + DownloadSettings downloadSettings, + NotificationSettings notificationSettings}); $ThemeSettingsCopyWith<$Res> get themeSettings; $PlayerSettingsCopyWith<$Res> get playerSettings; $DownloadSettingsCopyWith<$Res> get downloadSettings; + $NotificationSettingsCopyWith<$Res> get notificationSettings; } /// @nodoc @@ -68,6 +72,7 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings> Object? themeSettings = null, Object? playerSettings = null, Object? downloadSettings = null, + Object? notificationSettings = null, }) { return _then(_value.copyWith( themeSettings: null == themeSettings @@ -82,6 +87,10 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings> ? _value.downloadSettings : downloadSettings // ignore: cast_nullable_to_non_nullable as DownloadSettings, + notificationSettings: null == notificationSettings + ? _value.notificationSettings + : notificationSettings // ignore: cast_nullable_to_non_nullable + as NotificationSettings, ) as $Val); } @@ -114,6 +123,17 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings> return _then(_value.copyWith(downloadSettings: value) as $Val); }); } + + /// Create a copy of AppSettings + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $NotificationSettingsCopyWith<$Res> get notificationSettings { + return $NotificationSettingsCopyWith<$Res>(_value.notificationSettings, + (value) { + return _then(_value.copyWith(notificationSettings: value) as $Val); + }); + } } /// @nodoc @@ -127,7 +147,8 @@ abstract class _$$AppSettingsImplCopyWith<$Res> $Res call( {ThemeSettings themeSettings, PlayerSettings playerSettings, - DownloadSettings downloadSettings}); + DownloadSettings downloadSettings, + NotificationSettings notificationSettings}); @override $ThemeSettingsCopyWith<$Res> get themeSettings; @@ -135,6 +156,8 @@ abstract class _$$AppSettingsImplCopyWith<$Res> $PlayerSettingsCopyWith<$Res> get playerSettings; @override $DownloadSettingsCopyWith<$Res> get downloadSettings; + @override + $NotificationSettingsCopyWith<$Res> get notificationSettings; } /// @nodoc @@ -153,6 +176,7 @@ class __$$AppSettingsImplCopyWithImpl<$Res> Object? themeSettings = null, Object? playerSettings = null, Object? downloadSettings = null, + Object? notificationSettings = null, }) { return _then(_$AppSettingsImpl( themeSettings: null == themeSettings @@ -167,6 +191,10 @@ class __$$AppSettingsImplCopyWithImpl<$Res> ? _value.downloadSettings : downloadSettings // ignore: cast_nullable_to_non_nullable as DownloadSettings, + notificationSettings: null == notificationSettings + ? _value.notificationSettings + : notificationSettings // ignore: cast_nullable_to_non_nullable + as NotificationSettings, )); } } @@ -177,7 +205,8 @@ class _$AppSettingsImpl implements _AppSettings { const _$AppSettingsImpl( {this.themeSettings = const ThemeSettings(), this.playerSettings = const PlayerSettings(), - this.downloadSettings = const DownloadSettings()}); + this.downloadSettings = const DownloadSettings(), + this.notificationSettings = const NotificationSettings()}); factory _$AppSettingsImpl.fromJson(Map json) => _$$AppSettingsImplFromJson(json); @@ -191,10 +220,13 @@ class _$AppSettingsImpl implements _AppSettings { @override @JsonKey() final DownloadSettings downloadSettings; + @override + @JsonKey() + final NotificationSettings notificationSettings; @override String toString() { - return 'AppSettings(themeSettings: $themeSettings, playerSettings: $playerSettings, downloadSettings: $downloadSettings)'; + return 'AppSettings(themeSettings: $themeSettings, playerSettings: $playerSettings, downloadSettings: $downloadSettings, notificationSettings: $notificationSettings)'; } @override @@ -207,13 +239,15 @@ class _$AppSettingsImpl implements _AppSettings { (identical(other.playerSettings, playerSettings) || other.playerSettings == playerSettings) && (identical(other.downloadSettings, downloadSettings) || - other.downloadSettings == downloadSettings)); + other.downloadSettings == downloadSettings) && + (identical(other.notificationSettings, notificationSettings) || + other.notificationSettings == notificationSettings)); } @JsonKey(includeFromJson: false, includeToJson: false) @override - int get hashCode => - Object.hash(runtimeType, themeSettings, playerSettings, downloadSettings); + int get hashCode => Object.hash(runtimeType, themeSettings, playerSettings, + downloadSettings, notificationSettings); /// Create a copy of AppSettings /// with the given fields replaced by the non-null parameter values. @@ -235,7 +269,8 @@ abstract class _AppSettings implements AppSettings { const factory _AppSettings( {final ThemeSettings themeSettings, final PlayerSettings playerSettings, - final DownloadSettings downloadSettings}) = _$AppSettingsImpl; + final DownloadSettings downloadSettings, + final NotificationSettings notificationSettings}) = _$AppSettingsImpl; factory _AppSettings.fromJson(Map json) = _$AppSettingsImpl.fromJson; @@ -246,6 +281,8 @@ abstract class _AppSettings implements AppSettings { PlayerSettings get playerSettings; @override DownloadSettings get downloadSettings; + @override + NotificationSettings get notificationSettings; /// Create a copy of AppSettings /// with the given fields replaced by the non-null parameter values. @@ -1935,3 +1972,293 @@ abstract class _DownloadSettings implements DownloadSettings { _$$DownloadSettingsImplCopyWith<_$DownloadSettingsImpl> get copyWith => throw _privateConstructorUsedError; } + +NotificationSettings _$NotificationSettingsFromJson(Map json) { + return _NotificationSettings.fromJson(json); +} + +/// @nodoc +mixin _$NotificationSettings { + Duration get fastForwardInterval => throw _privateConstructorUsedError; + Duration get rewindInterval => throw _privateConstructorUsedError; + bool get progressBarIsChapterProgress => throw _privateConstructorUsedError; + String get primaryTitle => throw _privateConstructorUsedError; + String get secondaryTitle => throw _privateConstructorUsedError; + List get mediaControls => + throw _privateConstructorUsedError; + + /// Serializes this NotificationSettings to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of NotificationSettings + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $NotificationSettingsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $NotificationSettingsCopyWith<$Res> { + factory $NotificationSettingsCopyWith(NotificationSettings value, + $Res Function(NotificationSettings) then) = + _$NotificationSettingsCopyWithImpl<$Res, NotificationSettings>; + @useResult + $Res call( + {Duration fastForwardInterval, + Duration rewindInterval, + bool progressBarIsChapterProgress, + String primaryTitle, + String secondaryTitle, + List mediaControls}); +} + +/// @nodoc +class _$NotificationSettingsCopyWithImpl<$Res, + $Val extends NotificationSettings> + implements $NotificationSettingsCopyWith<$Res> { + _$NotificationSettingsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of NotificationSettings + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? fastForwardInterval = null, + Object? rewindInterval = null, + Object? progressBarIsChapterProgress = null, + Object? primaryTitle = null, + Object? secondaryTitle = null, + Object? mediaControls = null, + }) { + return _then(_value.copyWith( + fastForwardInterval: null == fastForwardInterval + ? _value.fastForwardInterval + : fastForwardInterval // ignore: cast_nullable_to_non_nullable + as Duration, + rewindInterval: null == rewindInterval + ? _value.rewindInterval + : rewindInterval // ignore: cast_nullable_to_non_nullable + as Duration, + progressBarIsChapterProgress: null == progressBarIsChapterProgress + ? _value.progressBarIsChapterProgress + : progressBarIsChapterProgress // ignore: cast_nullable_to_non_nullable + as bool, + primaryTitle: null == primaryTitle + ? _value.primaryTitle + : primaryTitle // ignore: cast_nullable_to_non_nullable + as String, + secondaryTitle: null == secondaryTitle + ? _value.secondaryTitle + : secondaryTitle // ignore: cast_nullable_to_non_nullable + as String, + mediaControls: null == mediaControls + ? _value.mediaControls + : mediaControls // ignore: cast_nullable_to_non_nullable + as List, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$NotificationSettingsImplCopyWith<$Res> + implements $NotificationSettingsCopyWith<$Res> { + factory _$$NotificationSettingsImplCopyWith(_$NotificationSettingsImpl value, + $Res Function(_$NotificationSettingsImpl) then) = + __$$NotificationSettingsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {Duration fastForwardInterval, + Duration rewindInterval, + bool progressBarIsChapterProgress, + String primaryTitle, + String secondaryTitle, + List mediaControls}); +} + +/// @nodoc +class __$$NotificationSettingsImplCopyWithImpl<$Res> + extends _$NotificationSettingsCopyWithImpl<$Res, _$NotificationSettingsImpl> + implements _$$NotificationSettingsImplCopyWith<$Res> { + __$$NotificationSettingsImplCopyWithImpl(_$NotificationSettingsImpl _value, + $Res Function(_$NotificationSettingsImpl) _then) + : super(_value, _then); + + /// Create a copy of NotificationSettings + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? fastForwardInterval = null, + Object? rewindInterval = null, + Object? progressBarIsChapterProgress = null, + Object? primaryTitle = null, + Object? secondaryTitle = null, + Object? mediaControls = null, + }) { + return _then(_$NotificationSettingsImpl( + fastForwardInterval: null == fastForwardInterval + ? _value.fastForwardInterval + : fastForwardInterval // ignore: cast_nullable_to_non_nullable + as Duration, + rewindInterval: null == rewindInterval + ? _value.rewindInterval + : rewindInterval // ignore: cast_nullable_to_non_nullable + as Duration, + progressBarIsChapterProgress: null == progressBarIsChapterProgress + ? _value.progressBarIsChapterProgress + : progressBarIsChapterProgress // ignore: cast_nullable_to_non_nullable + as bool, + primaryTitle: null == primaryTitle + ? _value.primaryTitle + : primaryTitle // ignore: cast_nullable_to_non_nullable + as String, + secondaryTitle: null == secondaryTitle + ? _value.secondaryTitle + : secondaryTitle // ignore: cast_nullable_to_non_nullable + as String, + mediaControls: null == mediaControls + ? _value._mediaControls + : mediaControls // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$NotificationSettingsImpl implements _NotificationSettings { + const _$NotificationSettingsImpl( + {this.fastForwardInterval = const Duration(seconds: 30), + this.rewindInterval = const Duration(seconds: 10), + this.progressBarIsChapterProgress = true, + this.primaryTitle = '\$bookTitle', + this.secondaryTitle = '\$author', + final List mediaControls = const [ + NotificationMediaControl.rewind, + NotificationMediaControl.fastForward, + NotificationMediaControl.skipToPreviousChapter, + NotificationMediaControl.skipToNextChapter + ]}) + : _mediaControls = mediaControls; + + factory _$NotificationSettingsImpl.fromJson(Map json) => + _$$NotificationSettingsImplFromJson(json); + + @override + @JsonKey() + final Duration fastForwardInterval; + @override + @JsonKey() + final Duration rewindInterval; + @override + @JsonKey() + final bool progressBarIsChapterProgress; + @override + @JsonKey() + final String primaryTitle; + @override + @JsonKey() + final String secondaryTitle; + final List _mediaControls; + @override + @JsonKey() + List get mediaControls { + if (_mediaControls is EqualUnmodifiableListView) return _mediaControls; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_mediaControls); + } + + @override + String toString() { + return 'NotificationSettings(fastForwardInterval: $fastForwardInterval, rewindInterval: $rewindInterval, progressBarIsChapterProgress: $progressBarIsChapterProgress, primaryTitle: $primaryTitle, secondaryTitle: $secondaryTitle, mediaControls: $mediaControls)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$NotificationSettingsImpl && + (identical(other.fastForwardInterval, fastForwardInterval) || + other.fastForwardInterval == fastForwardInterval) && + (identical(other.rewindInterval, rewindInterval) || + other.rewindInterval == rewindInterval) && + (identical(other.progressBarIsChapterProgress, + progressBarIsChapterProgress) || + other.progressBarIsChapterProgress == + progressBarIsChapterProgress) && + (identical(other.primaryTitle, primaryTitle) || + other.primaryTitle == primaryTitle) && + (identical(other.secondaryTitle, secondaryTitle) || + other.secondaryTitle == secondaryTitle) && + const DeepCollectionEquality() + .equals(other._mediaControls, _mediaControls)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + fastForwardInterval, + rewindInterval, + progressBarIsChapterProgress, + primaryTitle, + secondaryTitle, + const DeepCollectionEquality().hash(_mediaControls)); + + /// Create a copy of NotificationSettings + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$NotificationSettingsImplCopyWith<_$NotificationSettingsImpl> + get copyWith => + __$$NotificationSettingsImplCopyWithImpl<_$NotificationSettingsImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$NotificationSettingsImplToJson( + this, + ); + } +} + +abstract class _NotificationSettings implements NotificationSettings { + const factory _NotificationSettings( + {final Duration fastForwardInterval, + final Duration rewindInterval, + final bool progressBarIsChapterProgress, + final String primaryTitle, + final String secondaryTitle, + final List mediaControls}) = + _$NotificationSettingsImpl; + + factory _NotificationSettings.fromJson(Map json) = + _$NotificationSettingsImpl.fromJson; + + @override + Duration get fastForwardInterval; + @override + Duration get rewindInterval; + @override + bool get progressBarIsChapterProgress; + @override + String get primaryTitle; + @override + String get secondaryTitle; + @override + List get mediaControls; + + /// Create a copy of NotificationSettings + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$NotificationSettingsImplCopyWith<_$NotificationSettingsImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/settings/models/app_settings.g.dart b/lib/settings/models/app_settings.g.dart index 8986989..58aaad4 100644 --- a/lib/settings/models/app_settings.g.dart +++ b/lib/settings/models/app_settings.g.dart @@ -20,6 +20,10 @@ _$AppSettingsImpl _$$AppSettingsImplFromJson(Map json) => ? const DownloadSettings() : DownloadSettings.fromJson( json['downloadSettings'] as Map), + notificationSettings: json['notificationSettings'] == null + ? const NotificationSettings() + : NotificationSettings.fromJson( + json['notificationSettings'] as Map), ); Map _$$AppSettingsImplToJson(_$AppSettingsImpl instance) => @@ -27,6 +31,7 @@ Map _$$AppSettingsImplToJson(_$AppSettingsImpl instance) => 'themeSettings': instance.themeSettings, 'playerSettings': instance.playerSettings, 'downloadSettings': instance.downloadSettings, + 'notificationSettings': instance.notificationSettings, }; _$ThemeSettingsImpl _$$ThemeSettingsImplFromJson(Map json) => @@ -203,3 +208,50 @@ Map _$$DownloadSettingsImplToJson( 'maxConcurrentByHost': instance.maxConcurrentByHost, 'maxConcurrentByGroup': instance.maxConcurrentByGroup, }; + +_$NotificationSettingsImpl _$$NotificationSettingsImplFromJson( + Map json) => + _$NotificationSettingsImpl( + fastForwardInterval: json['fastForwardInterval'] == null + ? const Duration(seconds: 30) + : Duration( + microseconds: (json['fastForwardInterval'] as num).toInt()), + rewindInterval: json['rewindInterval'] == null + ? const Duration(seconds: 10) + : Duration(microseconds: (json['rewindInterval'] as num).toInt()), + progressBarIsChapterProgress: + json['progressBarIsChapterProgress'] as bool? ?? true, + primaryTitle: json['primaryTitle'] as String? ?? '\$bookTitle', + secondaryTitle: json['secondaryTitle'] as String? ?? '\$author', + mediaControls: (json['mediaControls'] as List?) + ?.map((e) => $enumDecode(_$NotificationMediaControlEnumMap, e)) + .toList() ?? + const [ + NotificationMediaControl.rewind, + NotificationMediaControl.fastForward, + NotificationMediaControl.skipToPreviousChapter, + NotificationMediaControl.skipToNextChapter + ], + ); + +Map _$$NotificationSettingsImplToJson( + _$NotificationSettingsImpl instance) => + { + 'fastForwardInterval': instance.fastForwardInterval.inMicroseconds, + 'rewindInterval': instance.rewindInterval.inMicroseconds, + 'progressBarIsChapterProgress': instance.progressBarIsChapterProgress, + 'primaryTitle': instance.primaryTitle, + 'secondaryTitle': instance.secondaryTitle, + 'mediaControls': instance.mediaControls + .map((e) => _$NotificationMediaControlEnumMap[e]!) + .toList(), + }; + +const _$NotificationMediaControlEnumMap = { + NotificationMediaControl.fastForward: 'fastForward', + NotificationMediaControl.rewind: 'rewind', + NotificationMediaControl.speedToggle: 'speedToggle', + NotificationMediaControl.stop: 'stop', + NotificationMediaControl.skipToNextChapter: 'skipToNextChapter', + NotificationMediaControl.skipToPreviousChapter: 'skipToPreviousChapter', +}; diff --git a/pubspec.lock b/pubspec.lock index 2fe54a5..f7059ef 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -71,7 +71,7 @@ packages: source: hosted version: "2.11.0" audio_service: - dependency: transitive + dependency: "direct main" description: name: audio_service sha256: "9dd5ba7e77567b290c35908b1950d61485b4dfdd3a0ac398e98cfeec04651b75" @@ -704,10 +704,11 @@ packages: just_audio_background: dependency: "direct main" description: - name: just_audio_background - sha256: "7547b076d5445431c780b0915707f18baa6d9588077d5d8f811e8a77b4e0bec5" - url: "https://pub.dev" - source: hosted + path: just_audio_background + ref: media-notification-config + resolved-ref: "79ac48a7d322d5b8db8847b35ed0c8555fa249bc" + url: "https://github.com/Dr-Blank/just_audio" + source: git version: "0.0.1-beta.13" just_audio_media_kit: dependency: "direct main" diff --git a/pubspec.yaml b/pubspec.yaml index 17f7b22..888f8b6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,6 +32,7 @@ isar_version: &isar_version ^4.0.0-dev.13 # define the version to be used dependencies: animated_list_plus: ^0.5.2 animated_theme_switcher: ^2.0.10 + audio_service: ^0.18.15 audio_session: ^0.1.19 audio_video_progress_bar: ^2.0.2 auto_scroll_text: ^0.0.7 @@ -59,7 +60,12 @@ dependencies: isar_flutter_libs: ^4.0.0-dev.13 json_annotation: ^4.9.0 just_audio: ^0.9.37 - just_audio_background: ^0.0.1-beta.11 + just_audio_background: + # TODO Remove git dep when https://github.com/ryanheise/just_audio/issues/912 is closed + git: + url: https://github.com/Dr-Blank/just_audio + ref: media-notification-config + path: just_audio_background just_audio_media_kit: ^2.0.4 list_wheel_scroll_view_nls: ^0.0.3 logging: ^1.2.0