优化一下通知栏显示

This commit is contained in:
rang 2025-12-07 17:55:07 +08:00
parent d6894c3191
commit f4f860f3ec
12 changed files with 133 additions and 69 deletions

View file

@ -48,6 +48,8 @@ PODS:
- just_audio (0.0.1):
- Flutter
- FlutterMacOS
- media_kit_libs_ios_audio (1.0.4):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
@ -80,6 +82,7 @@ DEPENDENCIES:
- Flutter (from `Flutter`)
- isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`)
- just_audio (from `.symlinks/plugins/just_audio/darwin`)
- media_kit_libs_ios_audio (from `.symlinks/plugins/media_kit_libs_ios_audio/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
@ -113,6 +116,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/isar_flutter_libs/ios"
just_audio:
:path: ".symlinks/plugins/just_audio/darwin"
media_kit_libs_ios_audio:
:path: ".symlinks/plugins/media_kit_libs_ios_audio/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
@ -141,6 +146,7 @@ SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073
just_audio: a42c63806f16995daf5b219ae1d679deb76e6a79
media_kit_libs_ios_audio: 8f39d96a9c630685dfb844c289bd1d114c486fb3
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2

View file

@ -105,7 +105,6 @@
3E1CA9493996109C19FFDD1A /* Pods-RunnerTests.release.xcconfig */,
9830F7B941AA345666516454 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
@ -492,6 +491,7 @@
DEVELOPMENT_TEAM = UV4P38S7RL;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -675,11 +675,12 @@
DEVELOPMENT_TEAM = UV4P38S7RL;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = dr.blank.vaani;
PRODUCT_BUNDLE_IDENTIFIER = dr.blank.vaani.debug;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -698,6 +699,7 @@
DEVELOPMENT_TEAM = UV4P38S7RL;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",

View file

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
@ -24,6 +26,17 @@
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
@ -41,18 +54,5 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
</dict>
</plist>

View file

@ -1,18 +1,45 @@
import 'package:audio_service/audio_service.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:media_kit/media_kit.dart';
import 'package:vaani/features/player/providers/abs_provider.dart';
class AbsAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandler {
final Player player = Player();
AbsAudioHandler() {
AbsAudioHandler(Ref ref) {
playbackState.add(
playbackState.value.copyWith(
controls: [
MediaControl.skipToPrevious,
if (player.state.playing) MediaControl.pause else MediaControl.play,
// MediaControl.rewind,
// MediaControl.fastForward,
MediaControl.skipToNext,
MediaControl.stop,
],
systemActions: {
MediaAction.play,
MediaAction.pause,
MediaAction.seek,
MediaAction.seekForward,
MediaAction.seekBackward,
},
),
);
final absState = ref.read(absStateProvider.notifier);
// 1. /
player.stream.playing.listen((bool playing) {
playbackState.add(playbackState.value.copyWith(
playing: playing,
// playing processingState
processingState:
playing ? AudioProcessingState.ready : AudioProcessingState.idle,
processingState: player.state.completed
? AudioProcessingState.completed
: player.state.buffering
? AudioProcessingState.buffering
: AudioProcessingState.ready,
));
absState.updataPlaying(playing);
});
// 2.
player.stream.position.listen((Duration position) {
@ -76,6 +103,10 @@ class AbsAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandler {
}
Future<void> setVolume(double volume) async {
final state = player.state;
await player.setVolume(volume);
}
PlayerStream get stream => player.stream;
PlayerState get state => player.state;
}

View file

@ -8,22 +8,26 @@ class AbsPlayerState {
final api.BookChapter? currentChapter;
//
final int currentIndex;
final bool playing;
AbsPlayerState({
this.book,
this.currentChapter,
this.currentIndex = 0,
this.playing = false,
});
AbsPlayerState copyWith({
api.BookExpanded? book,
api.BookChapter? currentChapter,
int? currentIndex,
bool? playing,
}) {
return AbsPlayerState(
book: book ?? this.book,
currentChapter: currentChapter ?? this.currentChapter,
currentIndex: currentIndex ?? this.currentIndex,
playing: playing ?? this.playing,
);
}
}

View file

@ -1,30 +0,0 @@
import 'package:audio_service/audio_service.dart';
import 'package:audio_session/audio_session.dart';
import 'package:media_kit/media_kit.dart';
import 'package:vaani/features/player/core/abs_audio_handler.dart' as core;
Future<core.AbsAudioHandler> absAudioHandlerInit() async {
// for playing audio on windows, linux
MediaKit.ensureInitialized();
// for configuring how this app will interact with other audio apps
final session = await AudioSession.instance;
await session.configure(const AudioSessionConfiguration.speech());
final audioService = await AudioService.init(
builder: () => core.AbsAudioHandler(),
config: const AudioServiceConfig(
androidNotificationChannelId: 'dr.blank.vaani.channel.audio',
androidNotificationChannelName: 'ABSPlayback',
androidNotificationChannelDescription:
'Needed to control audio from lock screen',
androidNotificationOngoing: false,
androidStopForegroundOnPause: false,
androidNotificationIcon: 'drawable/ic_stat_logo',
preloadArtwork: true,
// fastForwardInterval: Duration(seconds: 20),
// rewindInterval: Duration(seconds: 20),
),
);
return audioService;
}

View file

@ -30,7 +30,7 @@ Future<core.AbsAudioHandler> absAudioHandlerInit(Ref ref) async {
await session.configure(const AudioSessionConfiguration.speech());
final audioService = await AudioService.init(
builder: () => core.AbsAudioHandler(),
builder: () => core.AbsAudioHandler(ref),
config: const AudioServiceConfig(
androidNotificationChannelId: 'dr.blank.vaani.channel.audio',
androidNotificationChannelName: 'ABSPlayback',
@ -91,9 +91,9 @@ class AbsState extends _$AbsState {
final initialIndex = book.tracks.indexOf(trackToPlay);
final initialPositionInTrack =
currentTime != null ? currentTime - trackToPlay.startOffset : null;
final title = appSettings.notificationSettings.primaryTitle
final album = appSettings.notificationSettings.primaryTitle
.formatNotificationTitle(book);
final album = appSettings.notificationSettings.secondaryTitle
final artlist = appSettings.notificationSettings.secondaryTitle
.formatNotificationTitle(book);
final id = _getUri(trackToPlay, downloadedUris,
@ -101,10 +101,9 @@ class AbsState extends _$AbsState {
.toString();
final item = MediaItem(
id: id,
title: title,
title: chapterToPlay.title,
album: album,
displayTitle: title,
displaySubtitle: album,
artist: artlist,
duration: chapterToPlay.duration,
artUri: Uri.parse(
'${api.baseUrl}/api/items/${book.libraryItemId}/cover?token=${api.token!}',
@ -154,6 +153,20 @@ class AbsState extends _$AbsState {
Future<void> next() async {}
Future<void> previous() async {}
void updataPlaying(bool playing) {
state = state.copyWith(playing: playing);
}
Stream<Duration> get positionStreamInChapter {
final player = ref.read(absPlayerProvider);
return player.stream.position.distinct().map((position) {
return position +
(state.book?.tracks[state.currentIndex].startOffset ??
Duration.zero) -
(state.currentChapter?.start ?? Duration.zero);
});
}
Uri _getUri(
api.AudioTrack track,
@ -172,3 +185,8 @@ class AbsState extends _$AbsState {
Uri.parse('${baseUrl.toString()}${track.contentUrl}?token=$token');
}
}
@riverpod
Stream<Duration> positionChapter(Ref ref) {
return ref.watch(absStateProvider.notifier).positionStreamInChapter;
}

View file

@ -7,7 +7,7 @@ part of 'abs_provider.dart';
// **************************************************************************
String _$absAudioHandlerInitHash() =>
r'8815383b114e5e3da826afdea58bf0a884b1e3f2';
r'bb46f715e9d51bb6269d0d77e314665601a6bdb0';
/// See also [absAudioHandlerInit].
@ProviderFor(absAudioHandlerInit)
@ -25,6 +25,23 @@ final absAudioHandlerInitProvider =
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef AbsAudioHandlerInitRef = FutureProviderRef<core.AbsAudioHandler>;
String _$positionChapterHash() => r'b1d19345bceb2e54399e15fbb16a534f4be5ba43';
/// See also [positionChapter].
@ProviderFor(positionChapter)
final positionChapterProvider = AutoDisposeStreamProvider<Duration>.internal(
positionChapter,
name: r'positionChapterProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$positionChapterHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef PositionChapterRef = AutoDisposeStreamProviderRef<Duration>;
String _$absPlayerHash() => r'c313a2456bb221b83f3cd2142ae63d6463ef304b';
/// See also [AbsPlayer].
@ -40,7 +57,7 @@ final absPlayerProvider =
);
typedef _$AbsPlayer = Notifier<core.AbsAudioHandler>;
String _$absStateHash() => r'fb11d9d970e0de2dfd722c1f0de2a3b9b10f2859';
String _$absStateHash() => r'6b4ca07c7998304a1522a07b23955c3e54a441e3';
/// See also [AbsState].
@ProviderFor(AbsState)

View file

@ -4,10 +4,12 @@ import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/constants/sizes.dart';
import 'package:vaani/features/player/providers/abs_provider.dart';
import 'package:vaani/features/player/providers/audiobook_player.dart';
import 'package:vaani/features/player/providers/currently_playing_provider.dart';
import 'package:vaani/features/player/view/widgets/player_player_pause_button.dart';
import 'package:vaani/router/router.dart';
import 'package:vaani/shared/extensions/chapter.dart';
import 'package:vaani/shared/extensions/model_conversions.dart';
import 'package:vaani/shared/widgets/shelves/book_shelf.dart';
@ -19,11 +21,12 @@ class PlayerMinimized extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentBook = ref.watch(currentBookProvider);
final currentBook = ref.watch(absStateProvider.select((v) => v.book));
if (currentBook == null) {
return SizedBox.shrink();
}
final currentChapter = ref.watch(currentChapterProvider);
final currentChapter =
ref.watch(absStateProvider.select((v) => v.currentChapter));
return PlayerMinimizedFramework(
children: [
@ -112,9 +115,14 @@ class PlayerMinimizedFramework extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final player = ref.watch(playerProvider);
// final player = ref.watch(playerProvider);
final currentChapter =
ref.watch(absStateProvider.select((v) => v.currentChapter));
final progress =
useStream(player.positionStreamInChapter, initialData: Duration.zero);
// useStream(player.positionStreamInChapter, initialData: Duration.zero);
useStream(ref.read(absStateProvider.notifier).positionStreamInChapter,
initialData: Duration.zero);
return GestureDetector(
onTap: () {
if (GoRouterState.of(context).topRoute?.name != Routes.player.name) {
@ -136,7 +144,7 @@ class PlayerMinimizedFramework extends HookConsumerWidget {
height: AppElementSizes.barHeight,
child: LinearProgressIndicator(
value: (progress.data ?? Duration.zero).inSeconds /
(player.chapterDuration?.inSeconds ?? 1),
(currentChapter?.duration.inSeconds ?? 1),
// color: Theme.of(context).colorScheme.onPrimaryContainer,
// backgroundColor: Theme.of(context).colorScheme.primaryContainer,
),

View file

@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/api/library_provider.dart' show currentLibraryProvider;
import 'package:vaani/features/explore/providers/search_controller.dart';
import 'package:vaani/features/player/providers/abs_provider.dart';
import 'package:vaani/features/player/providers/currently_playing_provider.dart';
import 'package:vaani/features/player/view/player_minimized.dart';
import 'package:vaani/features/you/view/widgets/library_switch_chip.dart';
@ -50,7 +51,8 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
Widget buildNavLeft(BuildContext context, WidgetRef ref) {
// final isPlayerActive = ref.watch(isPlayerActiveProvider);
final currentBook = ref.watch(currentBookProvider);
// final currentBook = ref.watch(currentBookProvider);
final currentBook = ref.watch(absStateProvider.select((v) => v.book));
return Padding(
padding:
EdgeInsets.only(bottom: currentBook != null ? playerMinHeight : 0),

View file

@ -216,13 +216,13 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final me = ref.watch(meProvider);
// final player = ref.watch(audiobookPlayerProvider);
final currentBook = ref.watch(currentBookProvider);
final playerStatus = ref.watch(playerStatusProvider);
final isLoading = playerStatus.isLoading(libraryItemId);
final currentBook = ref.watch(absStateProvider.select((v) => v.book));
final playing = ref.watch(absStateProvider.select((v) => v.playing));
// final playerStatus = ref.watch(playerStatusProvider);
// final isLoading = playerStatus.isLoading(libraryItemId);
final isCurrentBookSetInPlayer =
currentBook?.libraryItemId == libraryItemId;
final isPlayingThisBook =
playerStatus.isPlaying() && isCurrentBookSetInPlayer;
final isPlayingThisBook = playing && isCurrentBookSetInPlayer;
final userProgress = me.valueOrNull?.mediaProgress
?.firstWhereOrNull((element) => element.libraryItemId == libraryItemId);
@ -308,7 +308,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
icon: Hero(
tag: HeroTagPrefixes.libraryItemPlayButton + libraryItemId,
child: DynamicItemPlayIcon(
isLoading: isLoading,
// isLoading: isLoading,
isBookCompleted: isBookCompleted,
isPlayingThisBook: isPlayingThisBook,
isCurrentBookSetInPlayer: isCurrentBookSetInPlayer,
@ -355,7 +355,7 @@ class BookCoverWidget extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentBook = ref.watch(currentBookProvider);
final currentBook = ref.watch(absStateProvider.select((v) => v.book));
if (currentBook == null) {
return const BookCoverSkeleton();
}

View file

@ -16,6 +16,8 @@ PODS:
- just_audio (0.0.1):
- Flutter
- FlutterMacOS
- media_kit_libs_macos_audio (1.0.4):
- FlutterMacOS
- package_info_plus (0.0.1):
- FlutterMacOS
- path_provider_foundation (0.0.1):
@ -44,6 +46,7 @@ DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral`)
- isar_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos`)
- just_audio (from `Flutter/ephemeral/.symlinks/plugins/just_audio/darwin`)
- media_kit_libs_macos_audio (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_audio/macos`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`)
@ -70,6 +73,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos
just_audio:
:path: Flutter/ephemeral/.symlinks/plugins/just_audio/darwin
media_kit_libs_macos_audio:
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_audio/macos
package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
path_provider_foundation:
@ -96,6 +101,7 @@ SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
isar_flutter_libs: 43385c99864c168fadba7c9adeddc5d38838ca6a
just_audio: a42c63806f16995daf5b219ae1d679deb76e6a79
media_kit_libs_macos_audio: 3871782a4f3f84c77f04d7666c87800a781c24da
package_info_plus: 12f1c5c2cfe8727ca46cbd0b26677728972d9a5b
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161