feat: add notification settings customisation options

This commit is contained in:
Dr-Blank 2024-09-24 23:58:52 -04:00
parent 84e5fd9d81
commit e89cda83c2
No known key found for this signature in database
GPG key ID: 7452CC63F210A266
15 changed files with 574 additions and 44 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -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<Uri>? 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<Duration> 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;
}
}
}

View file

@ -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<void> 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,
},
);
},
);
}

View file

@ -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(

View file

@ -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();
});

View file

@ -6,7 +6,7 @@ part of 'app_settings_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$appSettingsHash() => r'e0e132b782b97f11d9791d4f1e45bf4ee67dd99b';
String _$appSettingsHash() => r'f51d55f117692d4fb9f4b4febf02906c0953d334';
/// See also [AppSettings].
@ProviderFor(AppSettings)

View file

@ -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<String, dynamic> json) =>
@ -133,3 +134,49 @@ class DownloadSettings with _$DownloadSettings {
factory DownloadSettings.fromJson(Map<String, dynamic> 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<NotificationMediaControl> mediaControls,
}) = _NotificationSettings;
factory NotificationSettings.fromJson(Map<String, dynamic> 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,
}

View file

@ -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<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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<NotificationMediaControl> get mediaControls =>
throw _privateConstructorUsedError;
/// Serializes this NotificationSettings to a JSON map.
Map<String, dynamic> 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<NotificationSettings> 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<NotificationMediaControl> 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<NotificationMediaControl>,
) 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<NotificationMediaControl> 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<NotificationMediaControl>,
));
}
}
/// @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<NotificationMediaControl> mediaControls = const [
NotificationMediaControl.rewind,
NotificationMediaControl.fastForward,
NotificationMediaControl.skipToPreviousChapter,
NotificationMediaControl.skipToNextChapter
]})
: _mediaControls = mediaControls;
factory _$NotificationSettingsImpl.fromJson(Map<String, dynamic> 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<NotificationMediaControl> _mediaControls;
@override
@JsonKey()
List<NotificationMediaControl> 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<String, dynamic> 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<NotificationMediaControl> mediaControls}) =
_$NotificationSettingsImpl;
factory _NotificationSettings.fromJson(Map<String, dynamic> 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<NotificationMediaControl> 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;
}

View file

@ -20,6 +20,10 @@ _$AppSettingsImpl _$$AppSettingsImplFromJson(Map<String, dynamic> json) =>
? const DownloadSettings()
: DownloadSettings.fromJson(
json['downloadSettings'] as Map<String, dynamic>),
notificationSettings: json['notificationSettings'] == null
? const NotificationSettings()
: NotificationSettings.fromJson(
json['notificationSettings'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$AppSettingsImplToJson(_$AppSettingsImpl instance) =>
@ -27,6 +31,7 @@ Map<String, dynamic> _$$AppSettingsImplToJson(_$AppSettingsImpl instance) =>
'themeSettings': instance.themeSettings,
'playerSettings': instance.playerSettings,
'downloadSettings': instance.downloadSettings,
'notificationSettings': instance.notificationSettings,
};
_$ThemeSettingsImpl _$$ThemeSettingsImplFromJson(Map<String, dynamic> json) =>
@ -203,3 +208,50 @@ Map<String, dynamic> _$$DownloadSettingsImplToJson(
'maxConcurrentByHost': instance.maxConcurrentByHost,
'maxConcurrentByGroup': instance.maxConcurrentByGroup,
};
_$NotificationSettingsImpl _$$NotificationSettingsImplFromJson(
Map<String, dynamic> 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<dynamic>?)
?.map((e) => $enumDecode(_$NotificationMediaControlEnumMap, e))
.toList() ??
const [
NotificationMediaControl.rewind,
NotificationMediaControl.fastForward,
NotificationMediaControl.skipToPreviousChapter,
NotificationMediaControl.skipToNextChapter
],
);
Map<String, dynamic> _$$NotificationSettingsImplToJson(
_$NotificationSettingsImpl instance) =>
<String, dynamic>{
'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',
};

View file

@ -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"

View file

@ -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