How to pause/resume audio when iphone is locked/unlocked - ios

Here's what currently happens in my app:
Locking: iPhone Lock Button Pressed -> Audio Paused
Unlocking: iPhoneUnlocked Button Pressed -> Audio Resumed -> Slide to Unlock -> App Appears
I want the Unlocking sequence to be:
Unlocking: iPhoneUnlock Button Pressed -> Slide to Unlock -> Audio Resumed -> App Appears
I am using OS 2.2.1 and AVFoundation Framework, and here's what I have already tried:
I tried to use applicationWillResignActive and applicationDidBecomeActive callbacks of the AppDelegate, it doesn't work. Although applicationDidBecomeActive is called at the correct time (i.e. when user has slided to unlock the device), the audio gets resumed as soon as unlock button is pressed (I guess it is because of the audio sessions), so I tried:
I implemented audioPlayerBeginInterruption and audioPlayerEndInterruption selectors of the AVAudioPlayerDelegate, it looks like these methods never get called (I have placed log calls). I'm sure I am setting the delegate properly since audioPlayerDidFinishPlaying is correctly called when an audio file has finished playing.
P.S: I am testing on device if that wasn't obvious

They apparently only get called when an Audio session was interrupted, that's to say if you were playing audio and it is being interrupted by other audio which requests an active session.
In short: they dont get called when the phone becomes inactive, but they get called when you receive a call for example.
But even then they only get called when the AVAudioplayer was active at the time.
I'm still wrestling with that crappy system...

Slight abuse of the system, but you can observe the file protection notifications to find out when the device was unlocked. You could use that to decide when to play the audio again.

Related

Play a sound on my backgrounded app when the phone is in silent mode

I've built a timer app and one problem I have is when it's backgrounded, I'm unable to ring the timer if the user has volume off. Turning the volume or sound off also mutes notifications, which is the method I was using for ringing the timer in the background.
I just bought a tile and discovered it can ring your phone even on silent. I have tested this and it works in iOS 9, but I'm not sure how to duplicate this behavior.
How is ringing the iPhone in silent mode accomplished? Background refresh? Motion and activity? Something else?
Prior art:
this answer plays in silent mode, but does not address when the app is closed. same here
First of all, in order to play a sound in background your application must be able to launch the related code responding to a callback event when in the background. Only specific app types are allowed to respond to callbacks events in the background. For example, the "Tile" application can play sound in background responding to BLE-related callbacks events which are fired when the application is in background (e.g., BLE peripheral connections and disconnections) . Conversely, a simple timer is suspended when application goes in background thus the timer-expiration callback won't be triggered and there is no way to launch any code to play sound in this case. (You can read here for some details and a possibile approach to build alarm clock in iOS)
If your application type is within the set of special background-mode applications you can play a sound responding to events in background even when the phone is in silent mode by using AVAudioPlayer. In your view controller import the AVFoundation framework:
import AVFoundation
than declare an AVAudioPlayer variable
var player: AVAudioPlayer?;
in order to play an mp3 file placed in the app bundle:
func playSound() {
guard let url = Bundle.main.url(forResource: "MY_MP3_FILE", withExtension: "mp3") else {
print("error");
return;
}
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: AVAudioSessionCategoryOptions.mixWithOthers);
player = try AVAudioPlayer(contentsOf: url);
guard let player = player else {
print("error");
return;
}
player.play();
} catch let error {
print(error.localizedDescription);
}
}
Important: in Xcode in the "Capabilities" tab you also have to enable the "Audio, AirPlay and Picture in Picture" feature in the "Background Modes" section.
(Swift 4 solution tested in iOS 11 and 12 with different iPhone models).
Warning: Following this approach the sound will play at the current volume set in the phone, thus if the sound volume is equal to zero the sound will not be played. You can force the volume sound to a given value programmatically in this way:
let volumeView = MPVolumeView();
if let view = volumeView.subviews.first as? UISlider{
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
view.value = 0.5; //Set the volume level between 0 and 1
}
}
however, take into account that this approach shows the system sound volume bar and it could lead to a bad user experience.
First thing first I think we need to clear up a few things (correct me if I'm wrong):
You are scheduling a local notification when your timer is expired
Your app isn't running in the background at all
You want your local notification to play a sound even if the phone is on mute.
I'm not actually sure this is possible, you mentioned the Tile app, and the important distinction is that the Tile app actually does run in the background where your app doesn't.
Apple defines specific use cases that are allowed to run in the background. One of those allowed modes are to communicate with a Bluetooth LE device. So the way that works is the iPhone identifies the tile device, and send a notification to the tile app, at that point the app is actually executing. See more about background execution here
I don't believe any of the approved backgrounds modes can be used by you to implement a solution that will allow you to workaround this.
My suggestion is that when the user sets a timer in your app, check if the phone is on silent (see here), and if it is, alert the user that the app won't play a sound when the timer completes.
Also download a few other timer apps and see how they deal with it.

No sound after waking up

We've been using the AIR sound API, building for iOS, with no problems till now, the problem is that the sound works until iPad goes to sleep and there's no sound from our app when iPad is woken back up.
Note: we have the app set to exit on suspend, and we don't have this problem when user suspends -- i.e, hits the button and goes back to desktop.
We only have it when iPad goes to sleep (screen goes dark but app is still running). As far as I can tell our app is not receiving any events from iOS when the iPad goes to sleep or wakes back up. Once this has all happened and the game has no sound, but exiting and restarting makes it work again.
We are getting an activate event from the native application object when iPad is woken up, I'm also checking (just because it sounds useful) the value of SoundMixer.areSoundsInaccessible, but that's way off base. I do get a soundchannel object back when I play a sound while the problem is in effect, I just can't hear it.
We were having a similar problem with audio not playing (roughly half the time the device had been asleep), and fixed it with this:
// AudioPlaybackMode.MEDIA is the default value
SoundMixer.audioPlaybackMode = AudioPlaybackMode.AMBIENT;
Once that was done, audio came back immediately after the app woke up.
Note that AMBIENT's behaviour depends on whether the microphone is being used.

stop audio while in background

There is an app called IHeartRadio which lets you set a sleep timer which will shut off audio after a specified interval.
You first choose a station to listen to and then select an amount of time to sleep after which the radio station will stop playing. The app does not need to be in the foreground for this to happen.
How is it possible for an app to stop audio while it is in the background?
One theory was to set a UILocalNotification with silent audio. Then the UILocalNotification would take over the audio of the device, in effect silencing whatever audio was playing. That didn't work.
Timers don't work in the background, which doesn't leave much in terms of time-based behavior in the background.
When the UIBackgroundModes key contains the audio value, the system’s media frameworks automatically prevent the corresponding app from being suspended when it moves to the background. As long as it is playing audio or video content, the app continues to run in the background. However, if the app stops playing the audio or video, the system suspends it.
From iOS App Programming Guide.
You can use applicationDidEnterBackground event in your AppDelegate to handle application going to background and stop audio in this method.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
// add your audio stopping code ..
}

Keep app running while iOS device locked?

I have an app that makes heavy use of video out. In a typical use-case, I'll have an iPad connected to an external monitor. I just want the external monitor on; the iPad display does not need to stay on.
The ideal case would be for someone to connect to an external monitor, then lock their iPad. But that pauses my app. (Currently, I'm calling setIdleTimerDisabled to keep the iPad from locking up and pausing my app.)
I'd like to give the user the option of locking the iPad, but still having my app running and sending images to video out. (Note: I'm not talking about keeping my app running when it's not in the foreground. I just want to keep it running while it's in the foreground, but the device is locked.)
Is this possible?
I would say no, it is not possible. Here's why:
The docs read:
Pressing the Sleep/Wake button is another type of interruption that causes your app to be deactivated temporarily. When the user presses this button, the system disables touch events, moves the app to the background but sets the value of the app’s applicationState property to UIApplicationStateInactive (as opposed to UIApplicationStateBackground), and finally locks the screen.
Something interesting to note in the docs above is that a bit further down under "What to do when an interruption occurs" Apple recommends that you stop doing certain tasks.
In response to this change, your app should do the following in its applicationWillResignActive: method:
Stop timers and other periodic tasks.
Stop any running metadata queries.
Do not initiate any new tasks.
Pause movie playback (except when playing back over AirPlay).
Enter into a pause state if your app is a game.
Throttle back OpenGL ES frame rates.
Suspend any dispatch queues or operation queues executing non-critical code. (You can continue processing network requests and other time-sensitive background tasks while inactive.)
This tells me that Apple doesn't want or expect your app to be doing much of anything in this state, other than preparing to be fully backgrounded.
On a related note here's a thread that shows how to determine whether you've hit the Sleep/Wake button or not:
Is it possible to distinguish between locking the device and sending an app to background?

UILocalNotification how to call certain method if application is in background

I am creating player application, which is playing audio streams through internet. I want to add alarm functionality in my app - in particular time my player begins to play audio stream, I am trying to use UILocalNotification mechanism. But I've difficulties with it when my application in background mode, I can't call 'play' method, when notification is receiced (can't without user interaction). May be it is impossible?
But I bought this application:
http://itunes.apple.com/us/app/radio-alarm-clock-mp3-radio/id380271167?mt=8
And it seems like radio can start playing when local notification is received. Alarm can start playing radio when my app is in background mode.
Earlier I was trying to use NSTimer for this, but when my app goes to background, timer stops. If I use beginBackgroundTaskWithExpirationHandler: it works only 10 minutes. My app has special flag in plist, what is is audio application, and can playing music in background. In this case timers are working. But if I stop playing and go to background, timer is not working.
When I use \Radio Alarm Clock' application, I hear 'white noise' from dinamic, when music in not playing. May be it is the secret of this application?
Can you help me with my problem? Thanks.
maybe it's too late.
I had a look to the app you've mentioned at http://itunes.apple.com/us/app/radio-alarm-clock-mp3-radio/id380271167?mt=8 and yes, I think you are absolutely right, the only way to achieve that the application remains active while in background is to play a fake sound while it is in the background, which should be prohibited by Apple.
I've also seen that they don't use the remote iPod control, and this was strange at a first look.
At the end my opinion is that they do the following:
Avoid the call to beginReceivingRemoteControlEvents which allows to activate the iPod controls while in background (in fact they don't have it)
In this way, the status bar doesn't show the play icon while
the app plays audio
When the app goes in background, it probably plays a no sound periodically (once every 10 secs for example), in this way the app remains active
I saw that they also avoided to manage interruptions, for example in case another app is in foreground and plays music. Again Apple should have rejected the app for that reason, cos it is against the rules to follow while in background, but maybe they didn't see it during the acceptance tests.
So my interpretation is that they have intentionally missed to activate the iPod controls, just to avoid to show the play icon in the status bar while in background. In this way, the users are unaware that the app is active and is doing something strange after they close it.
In addition you can see that the app doesn't interrupt when another app plays in foreground a sound or audio, because otherwise they risk that the app doesn't restart on time when the alarm shpould fire.
That's just my idea of how they do that, and I think this is the only way for an audio app on iOS to remain active while it is in background and is supposed to be halted (well, in case Apple doesn't see the trick).
Have you tried adding this to appdelegate.m
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
// Call your method in here.
}
if you have can you add code for us to see what your doing.

Resources