1

I'm trying to use a class to manage a global AVAudioPlayer instance, but I can't get the lock screen controls to appear. The class subclasses UIResponder, but no matter what I try lock screen controls don't appear.

Client classes use - (void)setUrl:(NSURL *)url withTitle:(NSString *)title to load audio and playAudio to play. What am I doing wrong here?

I have background-execution permission set to audio & car play, and this is failing even after simulator > Reset Content and Settings.

- (void)playAudio {
    [self.audioPlayer play];
    [self updateNowPlayingInfo];
}

- (void)stopAudio {
    [self.audioPlayer stop];
    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:nil];
}

- (void)pauseAudio {
    [self.audioPlayer pause];
    [self updateNowPlayingInfo];
}

- (void)setupAudioSession {
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    NSError *setCategoryError = nil;
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
    NSError *activationError = nil;
    [audioSession setActive:YES error:&activationError];
}

- (void)setupAudioControls {
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
    [self updateNowPlayingInfo];
}

- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {
    if (receivedEvent.type == UIEventTypeRemoteControl) {
        switch (receivedEvent.subtype) {
            case UIEventSubtypeRemoteControlTogglePlayPause:
                NSLog(@"Play pause");
                if (self.audioPlayer.isPlaying) {
                    [self.audioPlayer pause];
                }
                else {
                    [self.audioPlayer play];
                }
                break;

            case UIEventSubtypeRemoteControlPreviousTrack:
                break;

            case UIEventSubtypeRemoteControlNextTrack:
                break;

            default:
                break;
        }

        [self updateNowPlayingInfo];
    }
}

- (void)updateNowPlayingInfo {
    NSMutableDictionary *albumInfo = [[NSMutableDictionary alloc] init];
    albumInfo[MPMediaItemPropertyTitle] = self.title;
    albumInfo[MPMediaItemPropertyArtist] = @"Fidelity";
    albumInfo[MPMediaItemPropertyAlbumTitle] = self.title;
    albumInfo[MPMediaItemPropertyPlaybackDuration] = @(self.audioPlayer.duration);
    albumInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @(self.audioPlayer.currentTime);
    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:albumInfo];
}

- (void)setUrl:(NSURL *)url withTitle:(NSString *)title {
    self.title = title;
    self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
    [self setupAudioSession];
    [self setupAudioControls];
}
Stefan Kendall
  • 66,414
  • 68
  • 253
  • 406
  • If I had to guess, something else `becomeFirstResponder`s after you `setupAudioControls`. I feel like this wouldn't work if this `UIResponder` weren't the first responder when the app gets sent to the background. – Ian MacDonald Feb 11 '15 at 19:22
  • Out of curiosity, do you expect this code to work even if you're not playing audio? My understanding was that intercepting remote control events only worked if you had actually started playing audio, but I'm unclear how that is supposed to interact with the lock screen remote controls. – Palpatim Feb 11 '15 at 20:37
  • @Palpatim you may be right. I assumed if the music hadn't yet started that the lock screen play button could start the music. That could be wrong. – Stefan Kendall Feb 13 '15 at 03:01

1 Answers1

1

Your -(void)setupAudioControls should be called right after going to background. In my app:

...
  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(appDidEnterBackground:)
                                               name:UIApplicationDidEnterBackgroundNotification
                                             object:nil];
...

- (void)appDidEnterBackground:(NSNotification *)notification {
  [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
  [self becomeFirstResponder];

  [self updateMediaInfo];
}
orkenstein
  • 2,810
  • 3
  • 24
  • 45