Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS: destroy() doesn't remove the media controls #93

Open
ddurham2 opened this issue Dec 27, 2022 · 6 comments
Open

iOS: destroy() doesn't remove the media controls #93

ddurham2 opened this issue Dec 27, 2022 · 6 comments

Comments

@ddurham2
Copy link

ddurham2 commented Dec 27, 2022

Possibly related to #90 but not sure.

My app has a stop button (in the app itself) where the selected media is not resumable.. it's now deselected. Upon the internal stop event, I call destroy() on the musicControls object, but the media controls within iOS (not within the app itself) incorrectly still show the media information.

On Android, the notification correctly disappears when destroy() is called.

I tried #90's suggestion of calling destroy twice, but that did not work either.

I tried digging into the Obj-C code, but am a little lost. I was confused not to see MPNowPlayingInfoCenter.playbackState ever altered. It appears that setting the playback rate is done when updating the playing state from within js. I didn't see anything that attempts to remove the controls when destroy is called. Nor, am I sure how that should be done with iOS's API.

Is this behavior expected?

Incidentally, when destroy() is called, I'm seeing this in the log:
[error] - Unhandled Promise rejection: overlay does not exist ; Zone: <root> ; Task: jo.addEventListener:click ; Value: overlay does not exist undefined But it does still hit a breakpoint in the destroy method of MusicControls.m

@woutersnoei
Copy link

From what I gather by studying this a bit is that the destroy() method actually only seems to destroy the notification from the controller to the app, but doesn't do anything to the controller itself. What it should do is setting the MPNowPlayingInfoCenter : nowPlayingInfo to nil or an empty dictionary, i.e. remove all info from the player. I tried this myself but I'm not an apt enough Obj-C programmer to make it work.. @ghenry22 could you please have a look at this? Should be quite simple I suppose..

@woutersnoei
Copy link

Ok, this seems to fix it for iOS: in the file MusicControls.m the deregisterMusicControlsEventListener void (line 314) should become like below, adding the removeTarget:self for pauseCommand, playCommand, stopCommand and togglePlayPauseCommand. Not sure if this has ever worked before because as far as I can tell these are and have always been essential steps in making the controller disappear when playback is done..

- (void) deregisterMusicControlsEventListener {
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"receivedEvent" object:nil];
    
    MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
    [commandCenter.nextTrackCommand removeTarget:self];
    [commandCenter.previousTrackCommand removeTarget:self];
    [commandCenter.pauseCommand removeTarget:self];
    [commandCenter.playCommand removeTarget:self];
    [commandCenter.stopCommand removeTarget:self];
    [commandCenter.togglePlayPauseCommand removeTarget:self];
    
    if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_0) {
        [commandCenter.changePlaybackPositionCommand setEnabled:false];
        [commandCenter.changePlaybackPositionCommand removeTarget:self action:NULL];
        [commandCenter.skipForwardCommand removeTarget:self];
        [commandCenter.skipBackwardCommand removeTarget:self];
    }
    
    [self setLatestEventCallbackId:nil];
}

@ddurham2
Copy link
Author

@woutersnoei - thanks for looking into this. I made the change to suggested, and it does in fact cause the external media controls to properly disappear when destroy() is called now.

However, after they've been destroyed, if I play something else in the app (which requires calling create() again), the external controls show again but won't control playback any more. When I press the pause button in the external controls, I see this logged:
"Invalid callback id received by sendPluginResult"

So, my guess is that something im deregister is now undoing something that the constructor did-- something that create() always assumes is still active.

Thoughts?

@woutersnoei
Copy link

Hmm, interesting, can't seem to replicate that here (i.e. when I re-enable by playing new track in my app it controls it perfectly again). I do remember this error message though from before I came up with my solution: it would give this message after calling destroy() and then hitting the still-existing play/pause buttons. I'm wondering; in my app after I call create() I always define the events and call subscribe(events) and listen() again too. Are you doing that as well in your app? Or are you only calling create()? (Btw testing on iPhone 12 mini and iPad Air 4 both with the latest iOS versions)

@ghenry22
Copy link
Owner

@woutersnoei thanks for the suggested changes, I will add these to a dev branch and try them out.
@ddurham2 I will see if I can reproduce your issue after destroying and creating again. It may be related to where the commandCenter listeners are registered. If they are not registered again after being removed then the controls won't be connected to anything.

@ddurham2
Copy link
Author

@woutersnoei - been a while, but back looking at this. Thanks for you suggestion. It does make things better. I also did have to change to re-creating, subscribing and listening each time playing starts.

Nevertheless, it's still not quite thorough. If you stop the media within the app, the external controls do start saying "Not Playing" and leaves the playing state. However, you can hit the play button and it'll turn to a pause button and flash something on the playback screen. It's like deregister needs to more fully disconnect from the external controls. I'm ignorant of all the details and so can't make any suggestion.

But it's much better than the default. I incorporated your changes here. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants