How to receive MPMusicPlayerControllerNowPlayingItemDidChangeNotification while in background? - iphone

I'm doing this test app where I want to receive notification when the iPod changes the now playing item (song), the test is working nice while app is in foreground but as soon as the app goes to the background it stop getting notifications which is OK, when I tap on the app again (comes to foreground) I get all notifications according to all the times the now playing changed while the app was in background but everytime I'm getting the same song information, so how can I get the correct song information for each notification?
This is the test I did, in the AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
MPMusicPlayerController *player = [MPMusicPlayerController iPodMusicPlayer];
[notificationCenter addObserver:self
selector:#selector(nowPlayingItemChanged:)
name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification
object:player];
[player beginGeneratingPlaybackNotifications];
return YES;
}
-(void) nowPlayingItemChanged:(NSNotification *)notification {
MPMusicPlayerController *player = (MPMusicPlayerController *)notification.object;
MPMediaItem *song = [player nowPlayingItem];
if (song) {
NSString *title = [song valueForProperty:MPMediaItemPropertyTitle];
NSString *album = [song valueForProperty:MPMediaItemPropertyAlbumTitle];
NSString *artist = [song valueForProperty:MPMediaItemPropertyArtist];
NSString *playCount = [song valueForProperty:MPMediaItemPropertyPlayCount];
NSLog(#"title: %#", title);
NSLog(#"album: %#", album);
NSLog(#"artist: %#", artist);
NSLog(#"playCount: %#", playCount);
}
}

See this post your options in the background are pretty restricted:
StackOverFlow Post
And the Apple Docs regarding that state it is not really possible:
Apple Documentation on Background states

Be sure to remove the observer when going into the background:
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification object:musicPlayer];[player endGeneratingPlaybackNotifications];
Add it again when entering the foreground.

Related

iOS 6: UserInfo returns nil for Pasteboard change Notification

Here's the Notification Observer for the Pasteboard change event, I need it to handle the copied selected text
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveClipBoardNotification:)
name:#"UIPasteboardChangedNotification"
object:nil];
And here's the implementation for the selector method receiveClipBoardNotification:
- (void) receiveClipBoardNotification:(NSNotification *) notification {
// NSMutableArray *typesAddedKey = [[notification userInfo] objectForKey:#"UIPasteboardChangedTypesAddedKey"];
// pasteBoardItemType = [typesAddedKey objectAtIndex:0];
NSLog(#"%#", [notification userInfo]);
}
The problem is that [notification userInfo] returns nil on iOS 6, although it works perfectly on iOS 5.
Well, This worked on iOS 6
NSLog(#"%#", [[UIPasteboard generalPasteboard] string]);

connectionDidFinishLoading:connect is never called when debugging on device

I am debugging my IOS5 application on the device for the first time and it is behaving very strangely. My application makes asynchronous http calls. When these calls complete I am posting notifications to handle the returned values. This all works great on the simulator. However on the device, it appears that the connectionDidFinishLoading:connection NSURLConnection delegate method is never called.
What is even stranger is the application then runs the incorrectly notification handler.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
SBJsonParser *jsonParser = [[SBJsonParser alloc]init];
NSError *error = nil;
id jsonObject = [jsonParser objectWithString:responseString error:&error];
switch (currentRequestType) {
case USER_EXISTS:{
NSNumber *userExists =[jsonObject objectForKey:#"exists"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"userExistsNote" object:userExists];
}
break;
case REGISTER_USER:{
NSNumber *success =[jsonObject objectForKey:#"success"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"registerUserNote" object:success];
}
case AUTHENTICATE_USER:{
NSNumber *success =[jsonObject objectForKey:#"success"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"authenticateUserNote" object:success];
}
default:
break;
}
}
The above switch statement delegates which notification should be posted. This peice of code never seems to run when debugging on the device however the method listening for the "registerUserNote" notification runs.. though the "authenticateUserNote" should have posted.
Again.. this all works great in the simulator.
Any ideas?
Thanks!

How to resume a recording using AVAudioRecorder?

I am writing an application that uses the AVAudioRecorder class. It works great except for when a phone call comes in. I am handling this per apple's guidelines of using the AVAudioRecorderDelegate methods
– (void) audioRecorderBeginInterruption:
– (void) audioRecorderEndInterruption:
It works great until the interruption ends and I attempt to "resume" the recording by calling the record method again (per the documentation). However it does not resume my recording but instead throws out the old one and starts up an entirely new one in its place. I have not been able to find a solution to this problem, if anyone has figured this out, or if it is a bug with apple's AVAudioRecorder please let me know. I really hope I do not have to write this using AudioQueues.
thanks
Looks like its a bug with apple's API. Great fun....
This was the response we received from a support ticket.
"The behavior you described is a bug and unfortunately there's nothing in the API that you can change to work around to actually append to the original recording. The interruption is resulting in capturing only the audio recorded after the interruption. You could try and stop the recording after the interruption then creating a new file after which would at least not cause the user to loose any information, but the result would be two separate files.
Please file a bug report at for this issue since bugs filed by external developers are critical when iOS engineering is evaluating critical features of fixes to address. It's easily reproducible but if you have a test app you can include please do, iOS engineering like having apps that show the bug directly.
"
My solution was:
Start record on temp file
Watch for AVAudioSessionInterruptionNotificatio
On AVAudioSessionInterruptionTypeBegan - stop the recording.
On AVAudioSessionInterruptionTypeEnded - Start new recording.
When the user stops - Marge the files.
Full Code
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(audioSessionInterruptionNotification:)
name:AVAudioSessionInterruptionNotification
object:audioSession];
-(void)audioSessionInterruptionNotification:(NSNotification*)notification {
NSString* seccReason = #"";
//Check the type of notification, especially if you are sending multiple AVAudioSession events here
NSLog(#"Interruption notification name %#", notification.name);
NSError *err = noErr;
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
seccReason = #"Interruption notification received";
//Check to see if it was a Begin interruption
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
seccReason = #"Interruption began";
NSLog(#"Interruption notification name %# audio pause", notification.name);
dispatch_time_t restartTime = dispatch_time(DISPATCH_TIME_NOW,
0.01 * NSEC_PER_SEC);
dispatch_after(restartTime, dispatch_get_global_queue(0, 0), ^{
AVAudioRecorder *recorder = [[self recorderPool] objectForKey:lastRecID];
if (recorder) {
if(recorder.isRecording) {
[recorder stop];
NSLog(#"Interruption notification name Pauseing recording %#", lastRecID);
} else {
NSLog(#"Interruption notification name Already Paused %#", lastRecID);
}
}else {
NSLog(#"Interruption notification name recording %# not found", lastRecID);
}
NSLog(#"Interruption notification Pauseing recording status %d",recorder.isRecording);
});
} else if([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]){
seccReason = #"Interruption ended!";
NSLog(#"Interruption notification name %# audio resume", notification.name);
//Start New Recording
dispatch_time_t restartTime = dispatch_time(DISPATCH_TIME_NOW,
0.1 * NSEC_PER_SEC);
dispatch_after(restartTime, dispatch_get_global_queue(0, 0), ^{
AVAudioRecorder *recorder = [[self recorderPool] objectForKey:lastRecID];
NSLog(#"Interruption notification Resumeing recording status %d",recorder.isRecording);
if (recorder) {
if(!recorder.isRecording) {
NSString *filePath = [[self orgFileNames] objectForKey:lastRecID];
NSArray * fileNames =[[self fileNames] objectForKey:lastRecID];
NSString *tmpFileName = [self gnrTempFileName:filePath AndNumber:fileNames.count];
[[[self fileNames] objectForKey:lastRecID] addObject:tmpFileName];
NSURL *url = [NSURL fileURLWithPath:tmpFileName];
NSError *error = nil;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:recordSetting error:&error];
if (![recorder record]) {
NSLog(#"Interruption notification Error Resumeing recording %#",tempRecorder);
return;
}
[[self recorderPool] setObject:recorder forKey:lastRecID];
NSLog(#"Interruption notification nameResumeing recording %#",lastRecID);
}else {
NSLog(#"Interruption notification Already Recording %d",recorder.isRecording);
}
}else {
NSLog(#"Interruption notification name recording %# not found",lastRecID);
}
});
}
}
}
You will try by using this piece of code
-(IBAction)pauseandplay:(id)sender
{
BOOL status= [player isPlaying];
if(status)
{
[pauseplay setImage:[UIImage imageNamed:#"play.png"]];
[player pause];
}
else
{
[pauseplay setImage:[UIImage imageNamed:#"icon-pause.png"]];
[player play];
updateTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:#selector(updateCurrentTime) userInfo:player repeats:YES];
}
}

How to play animation/movie instead of iPhone splash screen?

I want to play an animation(Gif) or a movie instead of Default.png. Is that possible? I tried what is described in this blog :http://www.cuppadev.co.uk/iphone/playing-animated-gifs-on-the-iphone/ but I don't know how to play a Gif animation instead of Default.png
If you're asking about a sanctioned App Store app, then no, you can't use anything but a static Default.png.
If you're writing an app for jailbroken phone, this may be possible (but I don't know).
The Following code should come in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSBundle *bundle = [NSBundle mainBundle];
if(bundle != nil)
{
NSString *videoPath = [bundle pathForResource:#"trail_video" ofType:#"mp4"];
if (moviePath)
{
videoURL = [NSURL fileURLWithPath:moviePath];
}
}
theMovie = [[MPMoviePlayerViewController alloc] initWithContentURL:videoURL];
theMovie.moviePlayer.controlStyle = MPMovieControlStyleNone;
theMovie.moviePlayer.scalingMode = MPMovieScalingModeFill;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:theMovie.moviePlayer];
[theMovie.moviePlayer setFullscreen:YES animated:NO];
[theMovie.moviePlayer prepareToPlay];
[theMovie.moviePlayer play];
window.rootViewController = theMovie;
[self.window.rootViewController.view bringSubviewToFront:mMoviePlayer.moviePlayer.view];
In the moviePlayerDidFinish method load the screen which you desire to load.
Unfortunately, it's impossible to do it on the latest iPhone OS. A trick using symbolic link is currently blocked by OS.

playing background audio on iphone

How can I play a background audio while my application is running?
Thanks.
Okay. This is a solution for background sound on iOS4 & iOS5 (definitely works up to iOS 5.0.1), and I have tested it only with AVPlayer. It should probably work for MPMusicPlayerController too.
Required frameworks:
AVFoundation.framework
AudioToolbox.framework
In your Info.plist, for the key UIBackgroundModes, add audio.
In MyAppDelegate.h:
reference <AVFoundation/AVFoundation.h> & <AudioToolbox/AudioToolbox.h>
implement the protocol AVAudioSessionDelegate:
#interface MyAppDelegate : NSObject <UIApplicationDelegate, AVAudioSessionDelegate>
define a method ensureAudio:
// Ensures the audio routes are setup correctly
- (BOOL) ensureAudio;
In MyAppDelegate.m:
implement the ensureAudio method:
- (BOOL) ensureAudio
{
// Registers this class as the delegate of the audio session (to get background sound)
[[AVAudioSession sharedInstance] setDelegate: self];
// Set category
NSError *categoryError = nil;
if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&categoryError]) {
NSLog(#"Audio session category could not be set");
return NO;
}
// Activate session
NSError *activationError = nil;
if (![[AVAudioSession sharedInstance] setActive: YES error: &activationError]) {
NSLog(#"Audio session could not be activated");
return NO;
}
// Allow the audio to mix with other apps (necessary for background sound)
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
return YES;
}
in the application:didFinishLaunchingWithOptions: method, before you assign the root view controller, run [self ensureAudio]:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Configure audio session
[self ensureAudio];
// Add the navigation controller's view to the window and display.
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
implement the AVAudioSessionDelegate methods like this:
#pragma mark - AVAudioSessionDelegate
- (void) beginInterruption
{
}
- (void) endInterruption
{
// Sometimes the audio session will be reset/stopped by an interruption
[self ensureAudio];
}
- (void) inputIsAvailableChanged:(BOOL)isInputAvailable
{
}
ensure that your app continues to run in the background. You can use the ol' [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler] if you want, but I think there are better ways.
play the actual audio (note I'm using ARC, that's why there are no release calls):
NSURL * file = [[NSBundle mainBundle] URLForResource:#"beep" withExtension:#"aif"];
AVURLAsset * asset = [[AVURLAsset alloc] initWithURL:file options:nil];
AVPlayerItem * item = [[AVPlayerItem alloc] initWithAsset:asset];
__block AVPlayer * player = [[AVPlayer alloc]initWithPlayerItem:item];
__block id finishObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
object:player.currentItem
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
[[NSNotificationCenter defaultCenter] removeObserver:finishObserver];
// Reference the 'player' variable so ARC doesn't release it until it's
// finished playing.
player = nil;
}];
// Trigger asynchronous load
[asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:#"tracks"] completionHandler:^{
// Start playing the beep (watch out - we're not on the main thread here)!
[player play];
}];
And it shooooooooooooould work!
If you are using your app also for recording - then don't forget to change setCategory to AVAudioSessionCategoryPlayAndRecord. In other case you won't be able to record
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error:&setCategoryErr];