Skip to content

Conversation

@poneciak57
Copy link
Contributor

@poneciak57 poneciak57 commented Nov 11, 2025

💀
Closes #820
Closes RNAA-310

TODO

Maybe TODO

  • NotificationReceiver for notification dissmised event can be centralized right now there are two receivers (not wanted)

⚠️ Breaking changes ⚠️

  • changed api for playback notification management
  • insted of using AudioManager you should use PlaybackNotificationManager

Introduced changes

  • new RecordingNotificationManager for management of notification dedicated for recording
  • new PlaybackNotificationManager for management of notification dedicated for playback
  • deleted methods for lockscreen info in AudioManager

Checklist

  • Linked relevant issue
  • Updated relevant documentation
  • Added/Conducted relevant tests
  • Performed self-review of the code
  • Updated Web Audio API coverage
  • Added support for web

@poneciak57 poneciak57 marked this pull request as ready for review November 20, 2025 14:39
Copy link
Member

@michalsek michalsek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nitpicks, but overall great job, thanks for this! :)

Copy link
Collaborator

@maciejmakowski2003 maciejmakowski2003 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

beside a few nitpicks, really great work!!! fingers crossed that it works as well as android code looks 🙃

useEffect(() => {
// Create AudioContext in suspended state
// Will be resumed when showing notification
audioContextRef.current = new AudioContext({ initSuspended: true });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do not have that option anymore

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AudioContext is by default suspended. However if for the first time user starts sth, it will resume context.

| 'skipBackward';

/// Event names for playback notification actions.
interface PlaybackNotificationEvent {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is seek event? (change playback position)

}

// Disable all remote commands
MPRemoteCommandCenter *remoteCenter = [MPRemoteCommandCenter sharedCommandCenter];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need all of them? we only support some subset

@michalsek michalsek self-requested a review November 25, 2025 15:42
Comment on lines +35 to +50
registerNotification(
type: string,
key: string
): Promise<{ success: boolean; error?: string }>;
showNotification(
key: string,
options: { [key: string]: string | boolean | number | undefined }
): Promise<{ success: boolean; error?: string }>;
updateNotification(
key: string,
options: { [key: string]: string | boolean | number | undefined }
): Promise<{ success: boolean; error?: string }>;
hideNotification(key: string): Promise<{ success: boolean; error?: string }>;
unregisterNotification(
key: string
): Promise<{ success: boolean; error?: string }>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can extract repeating types to some named interfaces and types

{
if (!type || !key) {
NSLog(@"[NotificationRegistry] Invalid type or key");
return NO;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to agree internally on consistency in objective-c parts
bool vs BOOL
true vs YES
false vs NO
anyway it will require some changes

Comment on lines +280 to +294
if ([control isEqualToString:@"play"]) {
remoteCommandName = @"play";
} else if ([control isEqualToString:@"pause"]) {
remoteCommandName = @"pause";
} else if ([control isEqualToString:@"next"]) {
remoteCommandName = @"next";
} else if ([control isEqualToString:@"previous"]) {
remoteCommandName = @"previous";
} else if ([control isEqualToString:@"skipForward"]) {
remoteCommandName = @"skipForward";
} else if ([control isEqualToString:@"skipBackward"]) {
remoteCommandName = @"skipBackward";
} else if ([control isEqualToString:@"seek"]) {
remoteCommandName = @"seek";
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ([control isEqualToString:@"play"]) {
remoteCommandName = @"play";
} else if ([control isEqualToString:@"pause"]) {
remoteCommandName = @"pause";
} else if ([control isEqualToString:@"next"]) {
remoteCommandName = @"next";
} else if ([control isEqualToString:@"previous"]) {
remoteCommandName = @"previous";
} else if ([control isEqualToString:@"skipForward"]) {
remoteCommandName = @"skipForward";
} else if ([control isEqualToString:@"skipBackward"]) {
remoteCommandName = @"skipBackward";
} else if ([control isEqualToString:@"seek"]) {
remoteCommandName = @"seek";
}
NSSet *validControls = [NSSet setWithObjects:
@"play",
@"pause",
@"next",
@"previous",
@"skipForward",
@"skipBackward",
@"seek"
];
if ([validControls containsObject:control]) {
[self enableRemoteCommand:control enabled:enabled];
}


// New notification system
registerNotification(
type: string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can type it stronger than string

Comment on lines +41 to +42
'recording',
this.notificationKey
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk if its just me, bur registering notification of type 'recording' for 'recording' key seems kind of "strange"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it is intended as only one notification at the time, I think it can be simplified, but if it can display more than one there is no method to change the notificationKey

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why only android lock screen manager is deleted, if ios one is not used either?

Comment on lines +4 to +12
#define NOW_PLAYING_INFO_KEYS \
@{ \
@"title" : MPMediaItemPropertyTitle, \
@"artist" : MPMediaItemPropertyArtist, \
@"album" : MPMediaItemPropertyAlbumTitle, \
@"duration" : MPMediaItemPropertyPlaybackDuration, \
@"elapsedTime" : MPNowPlayingInfoPropertyElapsedPlaybackTime, \
@"speed" : MPNowPlayingInfoPropertyPlaybackRate \
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about artwork?

} else if ([state isEqualToString:@"paused"]) {
playbackState = MPNowPlayingPlaybackStatePaused;
} else if ([state isEqualToString:@"stopped"]) {
playbackState = MPNowPlayingPlaybackStateStopped;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you cannot pass 'stopped' from typescript

self.playingInfoCenter.playbackState = playbackState;

// Handle artwork
NSString *artworkUrl = [self getArtworkUrl:_currentInfo[@"artwork"]];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

artworkUrl will always be null, because there is no entry in map defined above and you put in current info only keys that exist in pre-defined map

Comment on lines +218 to +219
registerNotification : (NSString *)type key : (NSString *)key resolve : (RCTPromiseResolveBlock)
resolve reject : (RCTPromiseRejectBlock)reject)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the method is based on promises and cannot be resolved instantly, invoke methods on seperate queue, similar to getDevicesInfo function

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom Foreground Service Icon for Android

6 participants