mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2026-02-16 14:29:35 +00:00
aaa
This commit is contained in:
parent
07aea41c6e
commit
4304af26dc
4 changed files with 433 additions and 438 deletions
|
|
@ -31,8 +31,7 @@ class AudiobookPlayer extends HookConsumerWidget {
|
||||||
if (currentBook == null) {
|
if (currentBook == null) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
final itemBeingPlayed =
|
final itemBeingPlayed = ref.watch(libraryItemProvider(currentBook.libraryItemId));
|
||||||
ref.watch(libraryItemProvider(currentBook.libraryItemId));
|
|
||||||
final player = ref.watch(audiobookPlayerProvider);
|
final player = ref.watch(audiobookPlayerProvider);
|
||||||
final imageOfItemBeingPlayed = itemBeingPlayed.valueOrNull != null
|
final imageOfItemBeingPlayed = itemBeingPlayed.valueOrNull != null
|
||||||
? ref.watch(
|
? ref.watch(
|
||||||
|
|
@ -65,8 +64,7 @@ class AudiobookPlayer extends HookConsumerWidget {
|
||||||
themeOfLibraryItemProvider(
|
themeOfLibraryItemProvider(
|
||||||
itemBeingPlayed.valueOrNull?.id,
|
itemBeingPlayed.valueOrNull?.id,
|
||||||
brightness: Theme.of(context).brightness,
|
brightness: Theme.of(context).brightness,
|
||||||
highContrast: appSettings.themeSettings.highContrast ||
|
highContrast: appSettings.themeSettings.highContrast || MediaQuery.of(context).highContrast,
|
||||||
MediaQuery.of(context).highContrast,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -88,14 +86,14 @@ class AudiobookPlayer extends HookConsumerWidget {
|
||||||
onDragDown: (percentage) async {
|
onDragDown: (percentage) async {
|
||||||
// preferred volume
|
// preferred volume
|
||||||
// set volume to 0 when dragging down
|
// set volume to 0 when dragging down
|
||||||
await player
|
await player.setVolume(preferredVolume * (1 - percentage.clamp(0, .75)));
|
||||||
.setVolume(preferredVolume * (1 - percentage.clamp(0, .75)));
|
|
||||||
},
|
},
|
||||||
minHeight: playerMinHeight,
|
minHeight: playerMinHeight,
|
||||||
// subtract the height of notches and other system UI
|
// subtract the height of notches and other system UI
|
||||||
maxHeight: playerMaxHeight,
|
maxHeight: playerMaxHeight,
|
||||||
controller: audioBookMiniplayerController,
|
controller: audioBookMiniplayerController,
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
|
duration: Duration(seconds: 3),
|
||||||
onDismissed: () {
|
onDismissed: () {
|
||||||
// add a delay before closing the player
|
// add a delay before closing the player
|
||||||
// to allow the user to see the player closing
|
// to allow the user to see the player closing
|
||||||
|
|
@ -103,15 +101,13 @@ class AudiobookPlayer extends HookConsumerWidget {
|
||||||
player.setSourceAudiobook(null);
|
player.setSourceAudiobook(null);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
curve: Curves.easeOut,
|
curve: Curves.linear,
|
||||||
builder: (height, percentage) {
|
builder: (height, percentage) {
|
||||||
// at what point should the player switch from miniplayer to expanded player
|
// at what point should the player switch from miniplayer to expanded player
|
||||||
// also at this point the image should be at its max size and in the center of the player
|
// also at this point the image should be at its max size and in the center of the player
|
||||||
final miniplayerPercentageDeclaration =
|
final miniplayerPercentageDeclaration =
|
||||||
(maxImgSize - playerMinHeight) /
|
(maxImgSize - playerMinHeight) / (playerMaxHeight - playerMinHeight);
|
||||||
(playerMaxHeight - playerMinHeight);
|
final bool isFormMiniplayer = percentage < miniplayerPercentageDeclaration;
|
||||||
final bool isFormMiniplayer =
|
|
||||||
percentage < miniplayerPercentageDeclaration;
|
|
||||||
|
|
||||||
if (!isFormMiniplayer) {
|
if (!isFormMiniplayer) {
|
||||||
// this calculation needs a refactor
|
// this calculation needs a refactor
|
||||||
|
|
@ -211,17 +207,14 @@ class AudiobookChapterProgressBar extends HookConsumerWidget {
|
||||||
|
|
||||||
// now find the chapter that corresponds to the current time
|
// now find the chapter that corresponds to the current time
|
||||||
// and calculate the progress of the current chapter
|
// and calculate the progress of the current chapter
|
||||||
final currentChapterProgress = currentChapter == null
|
final currentChapterProgress =
|
||||||
? null
|
currentChapter == null ? null : (player.positionInBook - currentChapter.start);
|
||||||
: (player.positionInBook - currentChapter.start);
|
|
||||||
|
|
||||||
final currentChapterBuffered = currentChapter == null
|
final currentChapterBuffered =
|
||||||
? null
|
currentChapter == null ? null : (player.bufferedPositionInBook - currentChapter.start);
|
||||||
: (player.bufferedPositionInBook - currentChapter.start);
|
|
||||||
|
|
||||||
return ProgressBar(
|
return ProgressBar(
|
||||||
progress:
|
progress: currentChapterProgress ?? position.data ?? const Duration(seconds: 0),
|
||||||
currentChapterProgress ?? position.data ?? const Duration(seconds: 0),
|
|
||||||
total: currentChapter == null
|
total: currentChapter == null
|
||||||
? player.book?.duration ?? const Duration(seconds: 0)
|
? player.book?.duration ?? const Duration(seconds: 0)
|
||||||
: currentChapter.end - currentChapter.start,
|
: currentChapter.end - currentChapter.start,
|
||||||
|
|
@ -232,8 +225,7 @@ class AudiobookChapterProgressBar extends HookConsumerWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
thumbRadius: 8,
|
thumbRadius: 8,
|
||||||
buffered:
|
buffered: currentChapterBuffered ?? buffered.data ?? const Duration(seconds: 0),
|
||||||
currentChapterBuffered ?? buffered.data ?? const Duration(seconds: 0),
|
|
||||||
bufferedBarColor: Theme.of(context).colorScheme.secondary,
|
bufferedBarColor: Theme.of(context).colorScheme.secondary,
|
||||||
timeLabelType: TimeLabelType.remainingTime,
|
timeLabelType: TimeLabelType.remainingTime,
|
||||||
timeLabelLocation: TimeLabelLocation.below,
|
timeLabelLocation: TimeLabelLocation.below,
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,9 @@ class PlayerWhenExpanded extends HookConsumerWidget {
|
||||||
earlyEnd,
|
earlyEnd,
|
||||||
)
|
)
|
||||||
.clamp(0.0, 1.0);
|
.clamp(0.0, 1.0);
|
||||||
|
|
||||||
final currentChapter = ref.watch(currentPlayingChapterProvider);
|
final currentChapter = ref.watch(currentPlayingChapterProvider);
|
||||||
final currentBookMetadata = ref.watch(currentBookMetadataProvider);
|
final currentBookMetadata = ref.watch(currentBookMetadataProvider);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
// sized box for system status bar; not needed as not full screen
|
// sized box for system status bar; not needed as not full screen
|
||||||
|
|
@ -104,10 +104,7 @@ class PlayerWhenExpanded extends HookConsumerWidget {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.1),
|
||||||
.colorScheme
|
|
||||||
.primary
|
|
||||||
.withValues(alpha: 0.1),
|
|
||||||
blurRadius: 32 * earlyPercentage,
|
blurRadius: 32 * earlyPercentage,
|
||||||
spreadRadius: 8 * earlyPercentage,
|
spreadRadius: 8 * earlyPercentage,
|
||||||
// offset: Offset(0, 16 * earlyPercentage),
|
// offset: Offset(0, 16 * earlyPercentage),
|
||||||
|
|
@ -131,128 +128,130 @@ class PlayerWhenExpanded extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
|
|
||||||
// the chapter title
|
// the chapter title
|
||||||
Opacity(
|
if (earlyPercentage > 0.4)
|
||||||
opacity: earlyPercentage,
|
Opacity(
|
||||||
child: Padding(
|
opacity: earlyPercentage,
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: AppElementSizes.paddingRegular * 4 * earlyPercentage,
|
|
||||||
// horizontal: 16.0,
|
|
||||||
),
|
|
||||||
// child: SizedBox(
|
|
||||||
// same as the image width
|
|
||||||
// width: imageSize,
|
|
||||||
child: currentChapter == null
|
|
||||||
? const SizedBox()
|
|
||||||
: Text(
|
|
||||||
currentChapter.title,
|
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
// ),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// the book name and author
|
|
||||||
Opacity(
|
|
||||||
opacity: earlyPercentage,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: AppElementSizes.paddingRegular * earlyPercentage,
|
|
||||||
// horizontal: 16.0,
|
|
||||||
),
|
|
||||||
// child: SizedBox(
|
|
||||||
// same as the image width
|
|
||||||
// width: imageSize,
|
|
||||||
child: Text(
|
|
||||||
[
|
|
||||||
currentBookMetadata?.title ?? '',
|
|
||||||
currentBookMetadata?.authorName ?? '',
|
|
||||||
].join(' - '),
|
|
||||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onSurface
|
|
||||||
.withValues(alpha: 0.7),
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
// ),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const Spacer(),
|
|
||||||
// the progress bar
|
|
||||||
Opacity(
|
|
||||||
opacity: earlyPercentage,
|
|
||||||
child: SizedBox(
|
|
||||||
width: imageSize,
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
top: AppElementSizes.paddingRegular * earlyPercentage,
|
top: AppElementSizes.paddingRegular * 4 * earlyPercentage,
|
||||||
left: AppElementSizes.paddingRegular * earlyPercentage,
|
// horizontal: 16.0,
|
||||||
right: AppElementSizes.paddingRegular * earlyPercentage,
|
|
||||||
),
|
),
|
||||||
child: const AudiobookChapterProgressBar(),
|
// child: SizedBox(
|
||||||
|
// same as the image width
|
||||||
|
// width: imageSize,
|
||||||
|
child: currentChapter == null
|
||||||
|
? const SizedBox()
|
||||||
|
: Text(
|
||||||
|
currentChapter.title,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
const Spacer(),
|
// the book name and author
|
||||||
|
if (earlyPercentage > 0.5)
|
||||||
|
Opacity(
|
||||||
|
opacity: earlyPercentage,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: AppElementSizes.paddingRegular * earlyPercentage,
|
||||||
|
// horizontal: 16.0,
|
||||||
|
),
|
||||||
|
// child: SizedBox(
|
||||||
|
// same as the image width
|
||||||
|
// width: imageSize,
|
||||||
|
child: Text(
|
||||||
|
[
|
||||||
|
currentBookMetadata?.title ?? '',
|
||||||
|
currentBookMetadata?.authorName ?? '',
|
||||||
|
].join(' - '),
|
||||||
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7),
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
// ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
if (earlyPercentage > 0.5) const Spacer(),
|
||||||
|
// the progress bar
|
||||||
|
if (earlyPercentage > 0.6)
|
||||||
|
Opacity(
|
||||||
|
opacity: earlyPercentage,
|
||||||
|
child: SizedBox(
|
||||||
|
width: imageSize,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: AppElementSizes.paddingRegular * earlyPercentage,
|
||||||
|
left: AppElementSizes.paddingRegular * earlyPercentage,
|
||||||
|
right: AppElementSizes.paddingRegular * earlyPercentage,
|
||||||
|
),
|
||||||
|
child: const AudiobookChapterProgressBar(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (earlyPercentage > 0.6) const Spacer(),
|
||||||
|
|
||||||
// the chapter skip buttons, seek 30 seconds back and forward, and play/pause button
|
// the chapter skip buttons, seek 30 seconds back and forward, and play/pause button
|
||||||
Opacity(
|
if (earlyPercentage > 0.8)
|
||||||
opacity: earlyPercentage,
|
Opacity(
|
||||||
child: SizedBox(
|
opacity: earlyPercentage,
|
||||||
width: imageSize,
|
child: SizedBox(
|
||||||
child: Row(
|
width: imageSize,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
child: Row(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
// previous chapter
|
children: [
|
||||||
const AudiobookPlayerSeekChapterButton(isForward: false),
|
// previous chapter
|
||||||
// buttonSkipBackwards
|
const AudiobookPlayerSeekChapterButton(isForward: false),
|
||||||
const AudiobookPlayerSeekButton(isForward: false),
|
// buttonSkipBackwards
|
||||||
AudiobookPlayerPlayPauseButton(
|
const AudiobookPlayerSeekButton(isForward: false),
|
||||||
playPauseController: playPauseController,
|
AudiobookPlayerPlayPauseButton(
|
||||||
),
|
playPauseController: playPauseController,
|
||||||
// buttonSkipForwards
|
),
|
||||||
const AudiobookPlayerSeekButton(isForward: true),
|
// buttonSkipForwards
|
||||||
// next chapter
|
const AudiobookPlayerSeekButton(isForward: true),
|
||||||
const AudiobookPlayerSeekChapterButton(isForward: true),
|
// next chapter
|
||||||
],
|
const AudiobookPlayerSeekChapterButton(isForward: true),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (earlyPercentage > 0.8) const Spacer(),
|
||||||
const Spacer(),
|
|
||||||
|
|
||||||
// speed control, sleep timer, chapter list, and settings
|
// speed control, sleep timer, chapter list, and settings
|
||||||
Opacity(
|
if (earlyPercentage > 0.9)
|
||||||
opacity: earlyPercentage,
|
Opacity(
|
||||||
child: Padding(
|
opacity: earlyPercentage,
|
||||||
padding: EdgeInsets.only(
|
child: Padding(
|
||||||
bottom: AppElementSizes.paddingRegular * 4 * earlyPercentage,
|
padding: EdgeInsets.only(
|
||||||
),
|
bottom: AppElementSizes.paddingRegular * 4 * earlyPercentage,
|
||||||
child: Row(
|
),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
child: Row(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
// speed control
|
children: [
|
||||||
const PlayerSpeedAdjustButton(),
|
// speed control
|
||||||
// sleep timer
|
const PlayerSpeedAdjustButton(),
|
||||||
const SleepTimerButton(),
|
// sleep timer
|
||||||
// chapter list
|
const SleepTimerButton(),
|
||||||
const ChapterSelectionButton(),
|
// chapter list
|
||||||
// settings
|
const ChapterSelectionButton(),
|
||||||
// IconButton(
|
// settings
|
||||||
// icon: const Icon(Icons.more_horiz),
|
// IconButton(
|
||||||
// onPressed: () {
|
// icon: const Icon(Icons.more_horiz),
|
||||||
// // show toast
|
// onPressed: () {
|
||||||
// showNotImplementedToast(context);
|
// // show toast
|
||||||
// },
|
// showNotImplementedToast(context);
|
||||||
// ),
|
// },
|
||||||
],
|
// ),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,7 @@ class PlayerWhenMinimized extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final player = ref.watch(audiobookPlayerProvider);
|
final player = ref.watch(audiobookPlayerProvider);
|
||||||
final vanishingPercentage = 1 - percentageMiniplayer;
|
final vanishingPercentage = 1 - percentageMiniplayer;
|
||||||
final progress =
|
final progress = useStream(player.slowPositionStream, initialData: Duration.zero);
|
||||||
useStream(player.slowPositionStream, initialData: Duration.zero);
|
|
||||||
|
|
||||||
final bookMetaExpanded = ref.watch(currentBookMetadataProvider);
|
final bookMetaExpanded = ref.watch(currentBookMetadataProvider);
|
||||||
|
|
||||||
|
|
@ -46,8 +45,7 @@ class PlayerWhenMinimized extends HookConsumerWidget {
|
||||||
// image
|
// image
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal:
|
horizontal: ((availWidth - maxImgSize) / 2) * percentageMiniplayer,
|
||||||
((availWidth - maxImgSize) / 2) * percentageMiniplayer,
|
|
||||||
),
|
),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|
@ -55,8 +53,7 @@ class PlayerWhenMinimized extends HookConsumerWidget {
|
||||||
context.pushNamed(
|
context.pushNamed(
|
||||||
Routes.libraryItem.name,
|
Routes.libraryItem.name,
|
||||||
pathParameters: {
|
pathParameters: {
|
||||||
Routes.libraryItem.pathParamName!:
|
Routes.libraryItem.pathParamName!: player.book!.libraryItemId,
|
||||||
player.book!.libraryItemId,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -90,10 +87,7 @@ class PlayerWhenMinimized extends HookConsumerWidget {
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7),
|
||||||
.colorScheme
|
|
||||||
.onSurface
|
|
||||||
.withValues(alpha: 0.7),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -106,37 +100,39 @@ class PlayerWhenMinimized extends HookConsumerWidget {
|
||||||
// controller.animateToHeight(state: PanelState.MAX);
|
// controller.animateToHeight(state: PanelState.MAX);
|
||||||
// },
|
// },
|
||||||
// ),
|
// ),
|
||||||
|
|
||||||
// rewind button
|
// rewind button
|
||||||
Opacity(
|
if (vanishingPercentage > 0.6)
|
||||||
opacity: vanishingPercentage,
|
Opacity(
|
||||||
child: Padding(
|
opacity: vanishingPercentage,
|
||||||
padding: const EdgeInsets.only(left: 8),
|
child: Padding(
|
||||||
child: IconButton(
|
padding: const EdgeInsets.only(left: 8),
|
||||||
icon: const Icon(
|
child: IconButton(
|
||||||
Icons.replay_30,
|
icon: const Icon(
|
||||||
size: AppElementSizes.iconSizeSmall,
|
Icons.replay_30,
|
||||||
|
size: AppElementSizes.iconSizeSmall,
|
||||||
|
),
|
||||||
|
onPressed: () {},
|
||||||
),
|
),
|
||||||
onPressed: () {},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
// play/pause button
|
// play/pause button
|
||||||
Opacity(
|
if (vanishingPercentage > 0.8)
|
||||||
opacity: vanishingPercentage,
|
Opacity(
|
||||||
child: Padding(
|
opacity: vanishingPercentage,
|
||||||
padding: const EdgeInsets.only(right: 8),
|
child: Padding(
|
||||||
child: AudiobookPlayerPlayPauseButton(
|
padding: const EdgeInsets.only(right: 8),
|
||||||
playPauseController: playPauseController,
|
child: AudiobookPlayerPlayPauseButton(
|
||||||
|
playPauseController: playPauseController,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: barHeight,
|
height: barHeight,
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
value: (progress.data ?? Duration.zero).inSeconds /
|
value: (progress.data ?? Duration.zero).inSeconds / player.book!.duration.inSeconds,
|
||||||
player.book!.duration.inSeconds,
|
|
||||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||||
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
|
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
556
pubspec.lock
556
pubspec.lock
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue