How to control Next Track Item on MPMusicPlayer - iphone

In MPMusicPlayer i want to display MPMediaPickerView if the Next Track item are finished....
How this is possible on phone....Please help me....

Watch for MPMusicPlayerControllerNowPlayingItemDidChangeNotification, and in your selector method, check the playbackState for MPMusicPlaybackStateStopped, do other checking (maybe nowPlayingItem?), and present your view.
NSNotificationCenter * notifications = [NSNotificationCenter defaultCenter];
[notifications addObserver:self selector:#selector (trackDidChangeWithNotification:)
name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification
object:self.iPodController];
.....
-(void)handleNextTrackNotification {
if ([self.iPodController playbackState] == MPMusicPlaybackStateStopped) {
// do whatever logic checks
// present view
}
}

Related

Get Notification when a video starts or stops in UIWebView

Hello i am new to objective - c
I'm having a problem with the UIWebView and MPMoviePlayerController: My UIWebView has a movie inside the html (it's a local html file), I'm using html5 and a video tag for the video.
I want a notification when video starts or stops in UIWebView....
I have tried using MPMoviePlayerPlaybackDidFinishNotification, but it doesnt fire ...
I have also tried to make the my main UIViewController's view a view of my own, and intercept -didAddSubview: and -willRemoveSubview:. but with no sucess...
Does any body know how to get notification from uiwebview??
You can observe #"MPAVControllerPlaybackStateChangedNotification" (use nil for the object). This notification isn't documented so I don't know if the App Store will approve your app.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackStateDidChange:)
name:#"MPAVControllerPlaybackStateChangedNotification"
object:nil];
The notification has the key MPAVControllerNewStateParameter in its userInfo. The value seems to be 0 before playback starts, 1 when it is paused, 2 when it is playing, and 3 (momentarily) when you are dragging the playback slider.
- (void)playbackStateDidChange:(NSNotification *)note
{
NSLog(#"note.name=%# state=%d", note.name, [[note.userInfo objectForKey:#"MPAVControllerNewStateParameter"] intValue]);
}
I searched alot about this..Here is the solution that I have found for getting the playback end notification call. Tested code on iOS6.0 and above. All thanks to #Morten.
In viewDidLoad add observer
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackDidEnd:)
name:#"MPAVControllerItemPlaybackDidEndNotification"//#"MPAVControllerPlaybackStateChangedNotification"
object:nil];
Then simply add following javascript code webViewDidFinishLoad delegate as below
- (void)webViewDidFinishLoad:(UIWebView *)webView {
//http://stackoverflow.com/a/12504918/860488
[videoView stringByEvaluatingJavaScriptFromString:#"\
var intervalId = setInterval(function() { \
var vph5 = document.getElementById(\"video-player\");\
if (vph5) {\
vph5.playVideo();\
clearInterval(intervalId);\
} \
}, 100);"];
}
- (void)playbackDidEnd:(NSNotification *)note
{
//do your stuff here
[videoView removeFromSuperview];
videoView.delegate = nil;
videoView = nil;
}
You will get playbackDid End call in the above selected and can do whatever is your requirement.
Happy Coding !!

Unhiding a view is very slow in CTCallCenter callEventHandler

Reposting with more concise and focused question after original question went unanswered. Also adding more insight into the problem after another day of research:
In my app delegate (didFinishLaunching), I set up a callEventHandler on CTCallCenter.
The idea is that when a callState changes, I post a notification with a userInfo dict
containing the call.callState. In my view, I observe this notification, and when the
userInfo dict contains a value of CTCallDisconnected, I want to unhide a view.
The problem I'm having is that the unhiding aspect is taking, almost consistenly, ~ 7 seconds.
Everything else is working fine, and I know this because I NSLog before and after the unhiding,
and those logs appear immediately, but the darned view still lags for 7 seconds.
Here's my code:
appDidFinishLaunching:
self.callCenter = [[CTCallCenter alloc] init];
self.callCenter.callEventHandler = ^(CTCall* call) {
// anounce that we've had a state change in our call center
NSDictionary *dict = [NSDictionary dictionaryWithObject:call.callState forKey:#"callState"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CTCallStateDidChange" object:self userInfo:dict];
};
I then listen for this notification when a user taps a button that dials a phone number:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ctCallStateDidChange:) name:#"CTCallStateDidChange" object:nil];
Then, in ctCallStateDidChange:
- (void)ctCallStateDidChange:(NSNotification *)notification
{
NSLog(#"121");
NSString *callInfo = [[notification userInfo] objectForKey:#"callState"];
if ([callInfo isEqualToString:CTCallStateDisconnected]) {
NSLog(#"before show");
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
NSLog(#"after show");
}
}
I've tracked the problem down to the if condition in the above code sample:
if ([[userInfo valueForKey:#"userInfo"] valueForKey:#"callState"] == CTCallStateDisconnected) {
If I simply replace that with:
if (1 == 1) {
Then the view appears immediately!
The thing is, those NSLog statements are logging immediately, but the view is
lagging in it's unhiding. How could that condition cause only part of it's block
to execute immediately, and the rest to wait ~ 7 seconds?
Thanks!
Try changing your code to this:
- (void)ctCallStateDidChange:(NSNotification *)notification
{
NSLog(#"121");
NSString *callInfo = [[notification userInfo] objectForKey:#"callState"];
if ([callInfo isEqualToString:CTCallStateDisconnected]) {
NSLog(#"before show");
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
NSLog(#"after show");
}
}
Note:
The parameter is an NSNotification, not an NSDictionary
I would not compare strings with ==
No need to cast the view to change the hidden property
Use NO instead of false
Update: Got an idea: Could you try the following, please, in between the NSLogs?
dispatch_async(dispatch_get_main_queue(), ^{
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
});
Reading the CTCallCenter doc, it seems the callEventHandler is dispatched on "the default priority global dispatch queue", which is not the main queue where all the UI stuff happens.
Looks like there is no problem with your hidden code. If I were you, I would comment out all the code after the call ends, and uncomment them one by one to see what is the problem.
Hm... try to call [yourViewController.view setNeedsDisplay] after you change hidden property. Or avoid hidden, use alpha or addSubview: and removeFromSuperview methods instead.
djibouti33,
Where you put this sentence to listen when a user taps a button that dials a phone number?on WillResignActive function?
this sentence --> [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ctCallStateDidChange:) name:#"CTCallStateDidChange" object:nil];
Thanks for your time,
Willy.

Specific expression in if condition causes 7 second delay in execution [duplicate]

Reposting with more concise and focused question after original question went unanswered. Also adding more insight into the problem after another day of research:
In my app delegate (didFinishLaunching), I set up a callEventHandler on CTCallCenter.
The idea is that when a callState changes, I post a notification with a userInfo dict
containing the call.callState. In my view, I observe this notification, and when the
userInfo dict contains a value of CTCallDisconnected, I want to unhide a view.
The problem I'm having is that the unhiding aspect is taking, almost consistenly, ~ 7 seconds.
Everything else is working fine, and I know this because I NSLog before and after the unhiding,
and those logs appear immediately, but the darned view still lags for 7 seconds.
Here's my code:
appDidFinishLaunching:
self.callCenter = [[CTCallCenter alloc] init];
self.callCenter.callEventHandler = ^(CTCall* call) {
// anounce that we've had a state change in our call center
NSDictionary *dict = [NSDictionary dictionaryWithObject:call.callState forKey:#"callState"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CTCallStateDidChange" object:self userInfo:dict];
};
I then listen for this notification when a user taps a button that dials a phone number:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ctCallStateDidChange:) name:#"CTCallStateDidChange" object:nil];
Then, in ctCallStateDidChange:
- (void)ctCallStateDidChange:(NSNotification *)notification
{
NSLog(#"121");
NSString *callInfo = [[notification userInfo] objectForKey:#"callState"];
if ([callInfo isEqualToString:CTCallStateDisconnected]) {
NSLog(#"before show");
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
NSLog(#"after show");
}
}
I've tracked the problem down to the if condition in the above code sample:
if ([[userInfo valueForKey:#"userInfo"] valueForKey:#"callState"] == CTCallStateDisconnected) {
If I simply replace that with:
if (1 == 1) {
Then the view appears immediately!
The thing is, those NSLog statements are logging immediately, but the view is
lagging in it's unhiding. How could that condition cause only part of it's block
to execute immediately, and the rest to wait ~ 7 seconds?
Thanks!
Try changing your code to this:
- (void)ctCallStateDidChange:(NSNotification *)notification
{
NSLog(#"121");
NSString *callInfo = [[notification userInfo] objectForKey:#"callState"];
if ([callInfo isEqualToString:CTCallStateDisconnected]) {
NSLog(#"before show");
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
NSLog(#"after show");
}
}
Note:
The parameter is an NSNotification, not an NSDictionary
I would not compare strings with ==
No need to cast the view to change the hidden property
Use NO instead of false
Update: Got an idea: Could you try the following, please, in between the NSLogs?
dispatch_async(dispatch_get_main_queue(), ^{
[self.view viewWithTag:kNONEMERGENCYCALLSAVEDTOLOG_TAG].hidden = NO;
});
Reading the CTCallCenter doc, it seems the callEventHandler is dispatched on "the default priority global dispatch queue", which is not the main queue where all the UI stuff happens.
Looks like there is no problem with your hidden code. If I were you, I would comment out all the code after the call ends, and uncomment them one by one to see what is the problem.
Hm... try to call [yourViewController.view setNeedsDisplay] after you change hidden property. Or avoid hidden, use alpha or addSubview: and removeFromSuperview methods instead.
djibouti33,
Where you put this sentence to listen when a user taps a button that dials a phone number?on WillResignActive function?
this sentence --> [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ctCallStateDidChange:) name:#"CTCallStateDidChange" object:nil];
Thanks for your time,
Willy.

MPMoviePlayerPlaybackDidFinishNotification gets called when it shouldn't

According to Apple's MPMoviePlayerController doc:
MPMoviePlayerPlaybackDidFinishNotification -
This notification is not sent in cases where the movie player is displaying in fullscreen mode and the user taps the Done button.
Seems to me this is dead wrong. Using the code below, playerPlaybackDidFinish gets called when I tap the done button.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playerPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.player];
- (void) playerPlaybackDidFinish:(NSNotification*)notification
{
NSLog(#"WHY?");
self.player.fullscreen = NO;
}
I need to distinguish between the user tapping the done button and the movie finishing all the way through playback. playerPlaybackDidFinish does get called when the movie ends, but like I said it also gets called when you tap Done.
Here is how you check the MPMoviePlayerPlaybackDidFinishReasonUserInfoKey which is part of the notification of MPMoviePlayerPlaybackDidFinishNotification
- (void) playbackDidFinish:(NSNotification*)notification {
int reason = [[[notification userInfo] valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
if (reason == MPMovieFinishReasonPlaybackEnded) {
//movie finished playin
}else if (reason == MPMovieFinishReasonUserExited) {
//user hit the done button
}else if (reason == MPMovieFinishReasonPlaybackError) {
//error
}
}
I am using the following to do something when a movie is played all the way to the end:
- (void)playbackDidFinish:(NSNotification*)notification
{
BOOL playbackEnded = ([[[notification userInfo] valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue] == MPMovieFinishReasonPlaybackEnded);
BOOL endReached = (self.player.currentPlaybackTime == self.player.playableDuration);
if (playbackEnded && endReached) {
// Movie Ended
}
}
When you get the notification you can check the player's endPlaybackTime. If it's -1 then the movie finished all the way back naturally.
For streamed content, you can check the MPMoviePlayerPlaybackDidFinishReasonUserInfoKey inside the userInfo on the MPMoviePlayerPlaybackDidFinishNotification.
If it's equal to MPMovieFinishReasonUserExited then it's the user stopped playing the content.
Make sure for
moviePlayer.repeatMode = MPMovieRepeatModeNone;

How does overlayViewTouched notification work in the MoviePlayer sample code

I have a question regarding the MoviePlayer sample code provided by apple.
I don't understand how the overlayViewTouch notification works. The NSlog message I added to it does not get sent when I touch the view (not button).
// post the "overlayViewTouch" notification and will send
// the overlayViewTouches: message
- (void)overlayViewTouches:(NSNotification *)notification
{
NSLog(#"overlay view touched");
// Handle touches to the overlay view (MyOverlayView) here...
}
I can, however, get the NSlog notification if I place it in -(void)touchesBegan in "MyOverlayView.m". Which makes me think it is recognizing touches but not sending a notification.
// Handle any touches to the overlay view
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
if (touch.phase == UITouchPhaseBegan)
{
NSLog(#"overlay touched(from touchesBegan")
// IMPORTANT:
// Touches to the overlay view are being handled using
// two different techniques as described here:
//
// 1. Touches to the overlay view (not in the button)
//
// On touches to the view we will post a notification
// "overlayViewTouch". MyMovieViewController is registered
// as an observer for this notification, and the
// overlayViewTouches: method in MyMovieViewController
// will be called.
//
// 2. Touches to the button
//
// Touches to the button in this same view will
// trigger the MyMovieViewController overlayViewButtonPress:
// action method instead.
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:OverlayViewTouchNotification object:nil];
}
}
Can anyone shed light on what I am missing or doing wrong?
Thank you.
As it seems to me the sample code is missing the addObserver selector call to the Notification. An example of the registration can be found in the AppDelegate:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePreloadDidFinish:)
name:MPMoviePlayerContentPreloadDidFinishNotification
object:nil];
As in NSNotificationCenter Documentation
When an object (known as the notification sender) posts a notification, it sends an NSNotification object to the notification center. The notification center then notifies any observers for which the notification meets the criteria specified on registration by sending them the specified notification message, passing the notification as the sole argument.
If there are no observers no one will be informed by NSNotificationCenter.
Just add the appropriate register in init for example.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(overlayViewTouches:)
name:OverlayViewTouchNotification
object:nil];
It's because the overlay view is small. You can see the area covered by the overlay view by changing the background color of the overlay view. The notification will be delivered when you touch the area.