Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #2130 from ulion/ios_play_background

ios support play background and native media control bar
  • Loading branch information...
commit 643ba7a8d7c875115ea5576021f3d78e4b72f45e 2 parents 2eed73d + ad19633
Memphiz Memphiz authored
8 XBMC-IOS.xcodeproj/project.pbxproj
View
@@ -52,6 +52,8 @@
7CC3106F162AAFE3003E7579 /* EdenVideoArtUpdater.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CC3106D162AAFE3003E7579 /* EdenVideoArtUpdater.cpp */; };
7CCFD9991514950700211D82 /* PCMCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CCFD9971514950700211D82 /* PCMCodec.cpp */; };
7CEE2E7F13D6B7D4000ABF2A /* TimeSmoother.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CEE2E7D13D6B7D4000ABF2A /* TimeSmoother.cpp */; };
+ 8253940516B25C2C00A85509 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8253940416B25C2C00A85509 /* AVFoundation.framework */; };
+ 8253940716B25C2C00A85509 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8253940616B25C2C00A85509 /* MediaPlayer.framework */; };
C80711AD135DB85F002F601B /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C80711AB135DB85F002F601B /* InputOperations.cpp */; };
C8B929D01573557B00284190 /* Epg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929C31573557B00284190 /* Epg.cpp */; };
C8B929D11573557B00284190 /* EpgContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929C51573557B00284190 /* EpgContainer.cpp */; };
@@ -1132,6 +1134,8 @@
7CCFD9981514950700211D82 /* PCMCodec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCMCodec.h; sourceTree = "<group>"; };
7CEE2E7D13D6B7D4000ABF2A /* TimeSmoother.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeSmoother.cpp; sourceTree = "<group>"; };
7CEE2E7E13D6B7D4000ABF2A /* TimeSmoother.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimeSmoother.h; sourceTree = "<group>"; };
+ 8253940416B25C2C00A85509 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
+ 8253940616B25C2C00A85509 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
83D619BB13C0D25300418A0F /* README.ios */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.ios; sourceTree = "<group>"; };
8D576316048677EA00EA77CD /* XBMC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XBMC.app; sourceTree = BUILT_PRODUCTS_DIR; };
C80711AB135DB85F002F601B /* InputOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InputOperations.cpp; sourceTree = "<group>"; };
@@ -3285,6 +3289,8 @@
F56C8C12131F4811000AD0F6 /* librtv.a in Frameworks */,
F56C8C14131F4811000AD0F6 /* libxdaap.a in Frameworks */,
18404DD31396C3D200863BBA /* SlingboxLib.a in Frameworks */,
+ 8253940516B25C2C00A85509 /* AVFoundation.framework in Frameworks */,
+ 8253940716B25C2C00A85509 /* MediaPlayer.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -6435,6 +6441,7 @@
F589AE481288E07D00D8079E /* libxml2.dylib */,
F589AE461288E07400D8079E /* libz.dylib */,
F56B14A412CAF523009B4C96 /* AudioToolbox.framework */,
+ 8253940416B25C2C00A85509 /* AVFoundation.framework */,
4D5D2E1D1301758F006ABC13 /* CFNetwork.framework */,
3255316512B2D02400837CD2 /* CoreAudio.framework */,
F56B143312CAF279009B4C96 /* CoreVideo.framework */,
@@ -6442,6 +6449,7 @@
F56B15D412CD67A9009B4C96 /* CoreGraphics.framework */,
F5899DCD1287212700D8079E /* Foundation.framework */,
F56B160712CD6999009B4C96 /* ImageIO.framework */,
+ 8253940616B25C2C00A85509 /* MediaPlayer.framework */,
F5899DCB1287212700D8079E /* OpenGLES.framework */,
F5899DCA1287212700D8079E /* QuartzCore.framework */,
F5899DCC1287212700D8079E /* UIKit.framework */,
18 xbmc/Application.cpp
View
@@ -404,6 +404,9 @@ CApplication::CApplication(void)
XInitThreads();
#endif
+ // we start in frontend
+ m_bInBackground = false;
+
/* for now always keep this around */
#ifdef HAS_KARAOKE
m_pKaraokeMgr = new CKaraokeLyricsManager();
@@ -2260,8 +2263,8 @@ void CApplication::NewFrame()
void CApplication::Render()
{
- // do not render if we are stopped
- if (m_bStop)
+ // do not render if we are stopped or in background
+ if (m_bStop || m_bInBackground)
return;
if (!m_AppActive && !m_bStop && (!IsPlayingVideo() || IsPaused()))
@@ -4610,6 +4613,8 @@ bool CApplication::WakeUpScreenSaver(bool bPowerOffKeyPressed /* = false */)
void CApplication::CheckScreenSaverAndDPMS()
{
+ if (m_bInBackground)
+ return;
if (!m_dpmsIsActive)
g_Windowing.ResetOSScreensaver();
@@ -4702,6 +4707,15 @@ void CApplication::ActivateScreenSaver(bool forceType /*= false */)
g_windowManager.ActivateWindow(WINDOW_SCREENSAVER);
}
+void CApplication::SetInBackground(bool background)
+{
+ if (!background)
+ {
+ ResetScreenSaverTimer();
+ }
+ m_bInBackground = background;
+}
+
void CApplication::CheckShutdown()
{
// first check if we should reset the timer
4 xbmc/Application.h
View
@@ -298,6 +298,10 @@ class CApplication : public CXBApplicationEx, public IPlayerCallback, public IMs
bool m_bPlaybackStarting;
+ bool m_bInBackground;
+ inline bool IsInBackground() { return m_bInBackground; };
+ void SetInBackground(bool background);
+
CKaraokeLyricsManager* m_pKaraokeMgr;
PLAYERCOREID m_eForcedNextPlayer;
21 xbmc/ApplicationMessenger.cpp
View
@@ -512,6 +512,15 @@ void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg)
}
break;
+ case TMSG_MEDIA_PAUSE_IF_PLAYING:
+ if (g_application.IsPlaying() && !g_application.IsPaused())
+ {
+ g_application.ResetScreenSaver();
+ g_application.WakeUpScreenSaverAndDPMS();
+ g_application.m_pPlayer->Pause();
+ }
+ break;
+
case TMSG_SWITCHTOFULLSCREEN:
if( g_windowManager.GetActiveWindow() != WINDOW_FULLSCREEN_VIDEO )
g_application.SwitchToFullScreen();
@@ -929,6 +938,18 @@ void CApplicationMessenger::MediaPause()
SendMessage(tMsg, true);
}
+void CApplicationMessenger::MediaUnPause()
+{
+ ThreadMessage tMsg = {TMSG_MEDIA_UNPAUSE};
+ SendMessage(tMsg, true);
+}
+
+void CApplicationMessenger::MediaPauseIfPlaying()
+{
+ ThreadMessage tMsg = {TMSG_MEDIA_PAUSE_IF_PLAYING};
+ SendMessage(tMsg, true);
+}
+
void CApplicationMessenger::MediaRestart(bool bWait)
{
ThreadMessage tMsg = {TMSG_MEDIA_RESTART};
4 xbmc/ApplicationMessenger.h
View
@@ -52,9 +52,11 @@ namespace MUSIC_INFO
#define TMSG_MEDIA_PLAY 200
#define TMSG_MEDIA_STOP 201
+// the PAUSE is indeed a PLAYPAUSE
#define TMSG_MEDIA_PAUSE 202
#define TMSG_MEDIA_RESTART 203
#define TMSG_MEDIA_UNPAUSE 204
+#define TMSG_MEDIA_PAUSE_IF_PLAYING 205
#define TMSG_PLAYLISTPLAYER_PLAY 210
#define TMSG_PLAYLISTPLAYER_NEXT 211
@@ -168,6 +170,8 @@ class CApplicationMessenger
void MediaPlay(int playlistid, int song = -1);
void MediaStop(bool bWait = true, int playlistid = -1);
void MediaPause();
+ void MediaUnPause();
+ void MediaPauseIfPlaying();
void MediaRestart(bool bWait);
void PlayListPlayerPlay();
4 xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAE.cpp
View
@@ -488,7 +488,11 @@ IAEStream* CCoreAudioAE::MakeStream(enum AEDataFormat dataFormat,
// if we are suspended we don't
// want anyone to mess with us
if (m_isSuspended && !m_softSuspend)
+#if defined(TARGET_DARWIN_IOS) && !defined(TARGET_DARWIN_IOS_ATV)
+ Resume();
+#else
return NULL;
+#endif
CAEChannelInfo channelInfo(channelLayout);
CLog::Log(LOGINFO, "CCoreAudioAE::MakeStream - %s, %u, %u, %s",
19 xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEHALIOS.cpp
View
@@ -32,7 +32,8 @@
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#define BUFFERED_FRAMES 1024
+// use the maximum frames per slice allows audio play when the screen is locked
+#define BUFFERED_FRAMES 4096
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CIOSCoreAudioHardware
@@ -634,6 +635,10 @@ bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, boo
if (!m_audioUnit->Open(m_audioGraph, kAudioUnitType_Output, kAudioUnitSubType_RemoteIO, kAudioUnitManufacturer_Apple))
return false;
+ UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize();
+
+ m_audioUnit->SetMaxFramesPerSlice(bufferFrames);
+
if (!m_audioUnit->EnableInputOuput())
return false;
@@ -662,6 +667,8 @@ bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, boo
if (!m_mixerUnit->Open(m_audioGraph, kAudioUnitType_Mixer, kAudioUnitSubType_MultiChannelMixer, kAudioUnitManufacturer_Apple))
return false;
+ m_mixerUnit->SetMaxFramesPerSlice(bufferFrames);
+
// set number of input buses
if (!m_mixerUnit->SetInputBusCount(MAX_CONNECTION_LIMIT))
return false;
@@ -698,6 +705,8 @@ bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, boo
if (!m_inputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
return false;
+ m_inputUnit->SetMaxFramesPerSlice(bufferFrames);
+
if (!m_inputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
return false;
@@ -773,12 +782,6 @@ bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format, boo
SetCurrentVolume(initVolume);
- UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize();
-
- m_audioUnit->SetMaxFramesPerSlice(bufferFrames);
- if (m_inputUnit)
- m_inputUnit->SetMaxFramesPerSlice(bufferFrames);
-
SetInputSource(pSource);
ShowGraph();
@@ -985,6 +988,8 @@ CAUOutputDevice *CCoreAudioGraph::CreateUnit(AEAudioFormat &format)
CAUOutputDevice *outputUnit = new CAUOutputDevice();
if (!outputUnit->Open(m_audioGraph, kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
goto error;
+
+ outputUnit->SetMaxFramesPerSlice(m_audioUnit->GetBufferFrameSize());
m_audioUnit->GetFormatDesc(format, &inputFormat);
4 xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEStream.cpp
View
@@ -596,6 +596,10 @@ void CCoreAudioAEStream::Pause()
void CCoreAudioAEStream::Resume()
{
+#if defined(TARGET_DARWIN_IOS) && !defined(TARGET_DARWIN_IOS_ATV)
+ if (CAEFactory::IsSuspended())
+ CAEFactory::Resume();
+#endif
m_paused = false;
}
2  xbmc/cores/paplayer/PAPlayer.cpp
View
@@ -91,8 +91,8 @@ void PAPlayer::SoftStart(bool wait/* = false */)
if (si->m_fadeOutTriggered)
continue;
- si->m_stream->FadeVolume(0.0f, 1.0f, FAST_XFADE_TIME);
si->m_stream->Resume();
+ si->m_stream->FadeVolume(0.0f, 1.0f, FAST_XFADE_TIME);
}
if (wait)
31 xbmc/osx/IOSEAGLView.mm
View
@@ -46,6 +46,7 @@
#import "IOSScreenManager.h"
#import "AutoPool.h"
#import "DarwinUtils.h"
+#import "XBMCDebugHelpers.h"
//--------------------------------------------------------------
@interface IOSEAGLView (PrivateMethods)
@@ -145,7 +146,7 @@ - (void) setScreen:(UIScreen *)screen withFrameBufferResize:(BOOL)resize;
//--------------------------------------------------------------
- (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
{
- //NSLog(@"%s", __PRETTY_FUNCTION__);
+ //PRINT_SIGNATURE();
framebufferResizeRequested = FALSE;
if ((self = [super initWithFrame:frame]))
{
@@ -165,9 +166,9 @@ - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!aContext)
- NSLog(@"Failed to create ES context");
+ ELOG(@"Failed to create ES context");
else if (![EAGLContext setCurrentContext:aContext])
- NSLog(@"Failed to set ES context current");
+ ELOG(@"Failed to set ES context current");
self.context = aContext;
[aContext release];
@@ -188,7 +189,7 @@ - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
//--------------------------------------------------------------
- (void) dealloc
{
- //NSLog(@"%s", __PRETTY_FUNCTION__);
+ //PRINT_SIGNATURE();
[self deleteFramebuffer];
[context release];
@@ -203,7 +204,7 @@ - (EAGLContext *)context
//--------------------------------------------------------------
- (void)setContext:(EAGLContext *)newContext
{
- //NSLog(@"%s", __PRETTY_FUNCTION__);
+ PRINT_SIGNATURE();
if (context != newContext)
{
[self deleteFramebuffer];
@@ -220,7 +221,7 @@ - (void)createFramebuffer
{
if (context && !defaultFramebuffer)
{
- //NSLog(@"%s", __PRETTY_FUNCTION__);
+ //PRINT_SIGNATURE();
[EAGLContext setCurrentContext:context];
// Create default framebuffer object.
@@ -241,7 +242,7 @@ - (void)createFramebuffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
- NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+ ELOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
}
//--------------------------------------------------------------
@@ -249,7 +250,7 @@ - (void) deleteFramebuffer
{
if (context && !pause)
{
- //NSLog(@"%s", __PRETTY_FUNCTION__);
+ PRINT_SIGNATURE();
[EAGLContext setCurrentContext:context];
if (defaultFramebuffer)
@@ -311,16 +312,21 @@ - (bool) presentFramebuffer
//--------------------------------------------------------------
- (void) pauseAnimation
{
+ PRINT_SIGNATURE();
pause = TRUE;
+ g_application.SetInBackground(true);
}
//--------------------------------------------------------------
- (void) resumeAnimation
{
+ PRINT_SIGNATURE();
pause = FALSE;
+ g_application.SetInBackground(false);
}
//--------------------------------------------------------------
- (void) startAnimation
{
+ PRINT_SIGNATURE();
if (!animating && context)
{
animating = TRUE;
@@ -338,6 +344,7 @@ - (void) startAnimation
//--------------------------------------------------------------
- (void) stopAnimation
{
+ PRINT_SIGNATURE();
if (animating && context)
{
[self deinitDisplayLink];
@@ -385,19 +392,19 @@ - (void) runAnimation:(id) arg
if (!g_application.Create())
{
readyToRun = false;
- NSLog(@"%sUnable to create application", __PRETTY_FUNCTION__);
+ ELOG(@"%sUnable to create application", __PRETTY_FUNCTION__);
}
if (!g_application.CreateGUI())
{
readyToRun = false;
- NSLog(@"%sUnable to create GUI", __PRETTY_FUNCTION__);
+ ELOG(@"%sUnable to create GUI", __PRETTY_FUNCTION__);
}
if (!g_application.Initialize())
{
readyToRun = false;
- NSLog(@"%sUnable to initialize application", __PRETTY_FUNCTION__);
+ ELOG(@"%sUnable to initialize application", __PRETTY_FUNCTION__);
}
if (readyToRun)
@@ -412,7 +419,7 @@ - (void) runAnimation:(id) arg
}
catch(...)
{
- NSLog(@"%sException caught on main loop. Exiting", __PRETTY_FUNCTION__);
+ ELOG(@"%sException caught on main loop. Exiting", __PRETTY_FUNCTION__);
}
}
3  xbmc/osx/ios/XBMCApplication.h
View
@@ -19,7 +19,8 @@
*/
#import <UIKit/UIKit.h>
+#import <AVFoundation/AVFoundation.h>
-@interface XBMCApplicationDelegate : NSObject <UIApplicationDelegate> {
+@interface XBMCApplicationDelegate : NSObject <UIApplicationDelegate, AVAudioSessionDelegate> {
}
@end
59 xbmc/osx/ios/XBMCApplication.m
View
@@ -19,37 +19,53 @@
*/
#import <UIKit/UIKit.h>
+#import <AVFoundation/AVAudioSession.h>
#import "XBMCApplication.h"
#import "XBMCController.h"
#import "IOSScreenManager.h"
+#import "XBMCDebugHelpers.h"
@implementation XBMCApplicationDelegate
XBMCController *m_xbmcController;
- (void)applicationWillResignActive:(UIApplication *)application
{
+ PRINT_SIGNATURE();
+
[m_xbmcController pauseAnimation];
+ [m_xbmcController becomeInactive];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
+ PRINT_SIGNATURE();
+
[m_xbmcController resumeAnimation];
+ [m_xbmcController enterForeground];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
- [m_xbmcController pauseAnimation];
+ PRINT_SIGNATURE();
+
+ if (application.applicationState == UIApplicationStateBackground)
+ {
+ // the app is turn into background, not in by screen lock which has app state inactive.
+ [m_xbmcController enterBackground];
+ }
}
- (void)applicationWillTerminate:(UIApplication *)application
{
- [m_xbmcController pauseAnimation];
+ PRINT_SIGNATURE();
+
+ [m_xbmcController stopAnimation];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
- [m_xbmcController resumeAnimation];
+ PRINT_SIGNATURE();
}
- (void)screenDidConnect:(NSNotification *)aNotification
@@ -84,6 +100,8 @@ - (void)registerScreenNotifications:(BOOL)bRegister
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
+ PRINT_SIGNATURE();
+
[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
UIScreen *currentScreen = [UIScreen mainScreen];
@@ -91,6 +109,37 @@ - (void)applicationDidFinishLaunching:(UIApplication *)application
m_xbmcController.wantsFullScreenLayout = YES;
[m_xbmcController startAnimation];
[self registerScreenNotifications:YES];
+
+ NSError *err = nil;
+ if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&err])
+ {
+ ELOG(@"AVAudioSession setCategory failed: %@", err);
+ }
+ err = nil;
+ if (![[AVAudioSession sharedInstance] setActive: YES error: &err])
+ {
+ ELOG(@"AVAudioSession setActive failed: %@", err);
+ }
+ [[AVAudioSession sharedInstance] setDelegate:self];
+}
+
+- (void)beginInterruption
+{
+ PRINT_SIGNATURE();
+ [m_xbmcController beginInterruption];
+}
+- (void)endInterruptionWithFlags:(NSUInteger)flags
+{
+ LOG(@"%s: %d", __PRETTY_FUNCTION__, flags);
+ if (flags & AVAudioSessionInterruptionFlags_ShouldResume)
+ {
+ NSError *err = nil;
+ if (![[AVAudioSession sharedInstance] setActive: YES error: &err])
+ {
+ ELOG(@"AVAudioSession::endInterruption setActive failed: %@", err);
+ }
+ [m_xbmcController endInterruption];
+ }
}
- (void)dealloc
@@ -123,11 +172,11 @@ int main(int argc, char *argv[]) {
}
@catch (id theException)
{
- NSLog(@"%@", theException);
+ ELOG(@"%@", theException);
}
@finally
{
- NSLog(@"This always happens.");
+ ILOG(@"This always happens.");
}
[pool release];
21 xbmc/osx/ios/XBMCController.h
View
@@ -28,6 +28,13 @@
@class IOSEAGLView;
+typedef enum
+{
+ IOS_PLAYBACK_STOPPED,
+ IOS_PLAYBACK_PAUSED,
+ IOS_PLAYBACK_PLAYING
+} IOSPlaybackState;
+
@interface XBMCController : UIViewController <UIGestureRecognizerDelegate>
{
UIWindow *m_window;
@@ -42,6 +49,12 @@
int m_screenIdx;
UIInterfaceOrientation orientation;
+
+ bool m_isPlayingBeforeInactive;
+ bool m_isInterrupted;
+ UIBackgroundTaskIdentifier m_bgTask;
+ NSTimer *m_networkAutoSuspendTimer;
+ IOSPlaybackState m_playbackState;
}
@property (readonly, nonatomic, getter=isAnimating) BOOL animating;
@property CGPoint lastGesturePoint;
@@ -49,12 +62,18 @@
@property bool touchBeginSignaled;
@property int m_screenIdx;
@property CGSize screensize;
+@property (nonatomic, retain) NSTimer *m_networkAutoSuspendTimer;
// message from which our instance is obtained
- (void) pauseAnimation;
- (void) resumeAnimation;
- (void) startAnimation;
- (void) stopAnimation;
+- (void) enterBackground;
+- (void) enterForeground;
+- (void) becomeInactive;
+- (void) beginInterruption;
+- (void) endInterruption;
- (void) sendKey: (XBMCKey) key;
- (void) observeDefaultCenterStuff: (NSNotification *) notification;
- (void) initDisplayLink;
@@ -69,6 +88,8 @@
- (void) activateKeyboard:(UIView *)view;
- (void) deactivateKeyboard:(UIView *)view;
+- (void) disableNetworkAutoSuspend;
+- (void) enableNetworkAutoSuspend:(id)obj;
- (void) disableSystemSleep;
- (void) enableSystemSleep;
- (void) disableScreenSaver;
454 xbmc/osx/ios/XBMCController.mm
View
@@ -28,16 +28,25 @@
#include "settings/AdvancedSettings.h"
#include "settings/Settings.h"
#include "FileItem.h"
+#include "MusicInfoTag.h"
+#include "SpecialProtocol.h"
+#include "PlayList.h"
+#include "AEFactory.h"
#include "ApplicationMessenger.h"
+#include "Application.h"
+#include "interfaces/AnnouncementManager.h"
#include "input/touch/generic/GenericTouchActionHandler.h"
#include "guilib/GUIControl.h"
-
#include "windowing/WindowingFactory.h"
#include "video/VideoReferenceClock.h"
#include "utils/log.h"
#include "utils/TimeUtils.h"
+#include "utils/Variant.h"
#include "Util.h"
#include "threads/Event.h"
+#define id _id
+#include "TextureCache.h"
+#undef id
#include <math.h>
#ifndef M_PI
@@ -47,12 +56,23 @@
#undef BOOL
+#import <AVFoundation/AVAudioSession.h>
+#import <MediaPlayer/MPMediaItem.h>
+#ifdef __IPHONE_5_0
+#import <MediaPlayer/MPNowPlayingInfoCenter.h>
+#else
+const NSString *MPNowPlayingInfoPropertyElapsedPlaybackTime = @"MPNowPlayingInfoPropertyElapsedPlaybackTime";
+const NSString *MPNowPlayingInfoPropertyPlaybackRate = @"MPNowPlayingInfoPropertyPlaybackRate";
+const NSString *MPNowPlayingInfoPropertyPlaybackQueueIndex = @"MPNowPlayingInfoPropertyPlaybackQueueIndex";
+const NSString *MPNowPlayingInfoPropertyPlaybackQueueCount = @"MPNowPlayingInfoPropertyPlaybackQueueCount";
+#endif
#import "IOSEAGLView.h"
#import "XBMCController.h"
#import "IOSScreenManager.h"
#import "XBMCApplication.h"
#import "XBMCDebugHelpers.h"
+#import "AutoPool.h"
XBMCController *g_xbmcController;
static CEvent screenChangeEvent;
@@ -62,6 +82,146 @@
extern NSString* kBRScreenSaverActivated;
extern NSString* kBRScreenSaverDismissed;
+id objectFromVariant(const CVariant &data);
+
+NSArray *arrayFromVariantArray(const CVariant &data)
+{
+ if (!data.isArray())
+ return nil;
+ NSMutableArray *array = [[[NSMutableArray alloc] initWithCapacity:data.size()] autorelease];
+ for (CVariant::const_iterator_array itr = data.begin_array(); itr != data.end_array(); ++itr)
+ {
+ [array addObject:objectFromVariant(*itr)];
+ }
+ return array;
+}
+
+NSDictionary *dictionaryFromVariantMap(const CVariant &data)
+{
+ if (!data.isObject())
+ return nil;
+ NSMutableDictionary *dict = [[[NSMutableDictionary alloc] initWithCapacity:data.size()] autorelease];
+ for (CVariant::const_iterator_map itr = data.begin_map(); itr != data.end_map(); ++itr)
+ {
+ [dict setValue:objectFromVariant(itr->second) forKey:[NSString stringWithUTF8String:itr->first.c_str()]];
+ }
+ return dict;
+}
+
+id objectFromVariant(const CVariant &data)
+{
+ if (data.isNull())
+ return nil;
+ if (data.isString())
+ return [NSString stringWithUTF8String:data.asString().c_str()];
+ if (data.isWideString())
+ return [NSString stringWithCString:(const char *)data.asWideString().c_str() encoding:NSUnicodeStringEncoding];
+ if (data.isInteger())
+ return [NSNumber numberWithLongLong:data.asInteger()];
+ if (data.isUnsignedInteger())
+ return [NSNumber numberWithUnsignedLongLong:data.asUnsignedInteger()];
+ if (data.isBoolean())
+ return [NSNumber numberWithInt:data.asBoolean()?1:0];
+ if (data.isDouble())
+ return [NSNumber numberWithDouble:data.asDouble()];
+ if (data.isArray())
+ return arrayFromVariantArray(data);
+ if (data.isObject())
+ return dictionaryFromVariantMap(data);
+ return nil;
+}
+
+void AnnounceBridge(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
+{
+ LOG(@"AnnounceBridge: [%s], [%s], [%s]", ANNOUNCEMENT::AnnouncementFlagToString(flag), sender, message);
+ const std::string msg(message);
+ if (msg == "OnPlay")
+ {
+ NSDictionary *item = dictionaryFromVariantMap(data["item"]);
+ std::string thumb = g_application.CurrentFileItem().GetArt("thumb");
+ if (!thumb.empty())
+ {
+ bool needsRecaching;
+ CStdString cachedThumb(CTextureCache::Get().CheckCachedImage(thumb, false, needsRecaching));
+ LOG("thumb: %s, %s", thumb.c_str(), cachedThumb.c_str());
+ if (!cachedThumb.empty())
+ {
+ CStdString thumbRealPath = CSpecialProtocol::TranslatePath(cachedThumb);
+ [item setValue:[NSString stringWithUTF8String:thumbRealPath.c_str()] forKey:@"thumb"];
+ }
+ }
+ double duration = g_application.GetTotalTime();
+ if (duration > 0)
+ [item setValue:[NSNumber numberWithDouble:duration] forKey:@"duration"];
+ [item setValue:[NSNumber numberWithInt:g_application.GetPlaySpeed()] forKey:@"speed"];
+ [item setValue:[NSNumber numberWithDouble:g_application.GetTime()] forKey:@"elapsed"];
+ int current = g_playlistPlayer.GetCurrentSong();
+ if (current >= 0)
+ {
+ [item setValue:[NSNumber numberWithInt:current] forKey:@"current"];
+ [item setValue:[NSNumber numberWithInt:g_playlistPlayer.GetPlaylist(g_playlistPlayer.GetCurrentPlaylist()).size()] forKey:@"total"];
+ }
+ if (g_application.CurrentFileItem().HasMusicInfoTag())
+ {
+ const std::vector<std::string> &genre = g_application.CurrentFileItem().GetMusicInfoTag()->GetGenre();
+ if (!genre.empty())
+ {
+ NSMutableArray *genreArray = [[NSMutableArray alloc] initWithCapacity:genre.size()];
+ for(std::vector<std::string>::const_iterator it = genre.begin(); it != genre.end(); ++it)
+ {
+ [genreArray addObject:[NSString stringWithUTF8String:it->c_str()]];
+ }
+ [item setValue:genreArray forKey:@"genre"];
+ }
+ }
+ LOG(@"data: %@", item.description);
+ [g_xbmcController performSelectorOnMainThread:@selector(onPlay:) withObject:item waitUntilDone:NO];
+ }
+ else if (msg == "OnPause")
+ {
+ CAEFactory::Suspend();
+ NSDictionary *item = dictionaryFromVariantMap(data["item"]);
+ LOG(@"data: %@", item.description);
+ [g_xbmcController performSelectorOnMainThread:@selector(onPause:) withObject:item waitUntilDone:NO];
+ }
+ else if (msg == "OnStop")
+ {
+ CAEFactory::Suspend();
+ NSDictionary *item = dictionaryFromVariantMap(data["item"]);
+ LOG(@"data: %@", item.description);
+ [g_xbmcController performSelectorOnMainThread:@selector(onStop:) withObject:item waitUntilDone:NO];
+ }
+}
+
+class AnnounceReceiver : public ANNOUNCEMENT::IAnnouncer
+{
+public:
+ virtual void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
+ {
+ // not all Announce called from xbmc main thread, we need an auto poll here.
+ CCocoaAutoPool pool;
+ AnnounceBridge(flag, sender, message, data);
+ }
+ virtual ~AnnounceReceiver() {}
+ static void init()
+ {
+ if (NULL==g_announceReceiver) {
+ g_announceReceiver = new AnnounceReceiver();
+ ANNOUNCEMENT::CAnnouncementManager::AddAnnouncer(g_announceReceiver);
+ }
+ }
+ static void dealloc()
+ {
+ ANNOUNCEMENT::CAnnouncementManager::RemoveAnnouncer(g_announceReceiver);
+ delete g_announceReceiver;
+ }
+private:
+ AnnounceReceiver() {}
+ static AnnounceReceiver *g_announceReceiver;
+};
+
+AnnounceReceiver *AnnounceReceiver::g_announceReceiver = NULL;
+
//--------------------------------------------------------------
//
@@ -80,6 +240,7 @@ @implementation XBMCController
@synthesize touchBeginSignaled;
@synthesize m_screenIdx;
@synthesize screensize;
+@synthesize m_networkAutoSuspendTimer;
//--------------------------------------------------------------
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
@@ -290,6 +451,7 @@ - (void) deactivateKeyboard:(UIView *)view
{
[view removeFromSuperview];
m_glView.userInteractionEnabled = YES;
+ [self becomeFirstResponder];
}
//--------------------------------------------------------------
-(void)handlePinch:(UIPinchGestureRecognizer*)sender
@@ -528,12 +690,17 @@ - (IBAction)handleSingleFingerSingleLongTap:(UIGestureRecognizer *)sender
//--------------------------------------------------------------
- (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
{
- //NSLog(@"%s", __PRETTY_FUNCTION__);
+ PRINT_SIGNATURE();
m_screenIdx = 0;
self = [super init];
if ( !self )
return ( nil );
+ m_isPlayingBeforeInactive = NO;
+ m_isInterrupted = NO;
+ m_bgTask = UIBackgroundTaskInvalid;
+ m_playbackState = IOS_PLAYBACK_STOPPED;
+
m_window = [[UIWindow alloc] initWithFrame:frame];
[m_window setRootViewController:self];
m_window.screen = screen;
@@ -567,6 +734,8 @@ - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
[m_window makeKeyAndVisible];
g_xbmcController = self;
+ AnnounceReceiver::init();
+
return self;
}
//--------------------------------------------------------------
@@ -577,6 +746,11 @@ -(void)viewDidLoad
//--------------------------------------------------------------
- (void)dealloc
{
+ // stop background task
+ [m_networkAutoSuspendTimer invalidate];
+ [self enableNetworkAutoSuspend:nil];
+
+ AnnounceReceiver::dealloc();
[m_glView stopAnimation];
[m_glView release];
[m_window release];
@@ -591,7 +765,7 @@ - (void)dealloc
//--------------------------------------------------------------
- (void)viewWillAppear:(BOOL)animated
{
- //NSLog(@"%s", __PRETTY_FUNCTION__);
+ PRINT_SIGNATURE();
// move this later into CocoaPowerSyscall
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
@@ -601,9 +775,17 @@ - (void)viewWillAppear:(BOOL)animated
[super viewWillAppear:animated];
}
//--------------------------------------------------------------
+-(void) viewDidAppear:(BOOL)animated
+{
+ [super viewDidAppear:animated];
+
+ [self becomeFirstResponder];
+ [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
+}
+//--------------------------------------------------------------
- (void)viewWillDisappear:(BOOL)animated
{
- //NSLog(@"%s", __PRETTY_FUNCTION__);
+ PRINT_SIGNATURE();
[self pauseAnimation];
@@ -613,8 +795,16 @@ - (void)viewWillDisappear:(BOOL)animated
[super viewWillDisappear:animated];
}
//--------------------------------------------------------------
+- (BOOL) canBecomeFirstResponder
+{
+ return YES;
+}
+//--------------------------------------------------------------
- (void)viewDidUnload
{
+ [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
+ [self resignFirstResponder];
+
[super viewDidUnload];
}
//--------------------------------------------------------------
@@ -658,7 +848,7 @@ - (CGFloat) getScreenScale:(UIScreen *)screen;
//--------------------------------------------------------------
- (BOOL) recreateOnReselect
{
- //NSLog(@"%s", __PRETTY_FUNCTION__);
+ PRINT_SIGNATURE();
return YES;
}
//--------------------------------------------------------------
@@ -670,6 +860,35 @@ - (void)didReceiveMemoryWarning
// Release any cached data, images, etc. that aren't in use.
}
//--------------------------------------------------------------
+- (void)disableNetworkAutoSuspend
+{
+ PRINT_SIGNATURE();
+ if (m_bgTask != UIBackgroundTaskInvalid)
+ {
+ [[UIApplication sharedApplication] endBackgroundTask: m_bgTask];
+ m_bgTask = UIBackgroundTaskInvalid;
+ }
+ // we have to alloc the background task for keep network working after screen lock and dark.
+ UIBackgroundTaskIdentifier newTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
+ m_bgTask = newTask;
+
+ if (m_networkAutoSuspendTimer)
+ {
+ [m_networkAutoSuspendTimer invalidate];
+ self.m_networkAutoSuspendTimer = nil;
+ }
+}
+//--------------------------------------------------------------
+- (void)enableNetworkAutoSuspend:(id)obj
+{
+ PRINT_SIGNATURE();
+ if (m_bgTask != UIBackgroundTaskInvalid)
+ {
+ [[UIApplication sharedApplication] endBackgroundTask: m_bgTask];
+ m_bgTask = UIBackgroundTaskInvalid;
+ }
+}
+//--------------------------------------------------------------
- (void) disableSystemSleep
{
}
@@ -709,48 +928,239 @@ - (void) activateScreen: (UIScreen *)screen
m_window.screen = screen;
}
//--------------------------------------------------------------
+- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
+ LOG(@"%s: type %d, subtype: %d", __PRETTY_FUNCTION__, receivedEvent.type, receivedEvent.subtype);
+ if (receivedEvent.type == UIEventTypeRemoteControl)
+ {
+ [self disableNetworkAutoSuspend];
+ switch (receivedEvent.subtype)
+ {
+ case UIEventSubtypeRemoteControlTogglePlayPause:
+ CApplicationMessenger::Get().SendAction(ACTION_PLAYER_PLAYPAUSE);
+ break;
+ case UIEventSubtypeRemoteControlPlay:
+ CApplicationMessenger::Get().SendAction(ACTION_PLAYER_PLAY);
+ break;
+ case UIEventSubtypeRemoteControlPause:
+ // ACTION_PAUSE sometimes cause unpause, use MediaPauseIfPlaying to make sure pause only
+ CApplicationMessenger::Get().MediaPauseIfPlaying();
+ break;
+ case UIEventSubtypeRemoteControlNextTrack:
+ CApplicationMessenger::Get().SendAction(ACTION_NEXT_ITEM);
+ break;
+ case UIEventSubtypeRemoteControlPreviousTrack:
+ CApplicationMessenger::Get().SendAction(ACTION_PREV_ITEM);
+ break;
+ case UIEventSubtypeRemoteControlBeginSeekingForward:
+ // use 4X speed forward.
+ CApplicationMessenger::Get().SendAction(ACTION_PLAYER_FORWARD);
+ CApplicationMessenger::Get().SendAction(ACTION_PLAYER_FORWARD);
+ break;
+ case UIEventSubtypeRemoteControlBeginSeekingBackward:
+ // use 4X speed rewind.
+ CApplicationMessenger::Get().SendAction(ACTION_PLAYER_REWIND);
+ CApplicationMessenger::Get().SendAction(ACTION_PLAYER_REWIND);
+ break;
+ case UIEventSubtypeRemoteControlEndSeekingForward:
+ case UIEventSubtypeRemoteControlEndSeekingBackward:
+ // restore to normal playback speed.
+ if (g_application.IsPlaying() && !g_application.IsPaused())
+ CApplicationMessenger::Get().SendAction(ACTION_PLAYER_PLAY);
+ break;
+ default:
+ LOG(@"unhandled subtype: %d", receivedEvent.subtype);
+ break;
+ }
+ [self rescheduleNetworkAutoSuspend];
+ }
+}
+//--------------------------------------------------------------
+- (void)enterBackground
+{
+ PRINT_SIGNATURE();
+ if (g_application.IsPlaying() && !g_application.IsPaused())
+ {
+ m_isPlayingBeforeInactive = YES;
+ CApplicationMessenger::Get().MediaPauseIfPlaying();
+ }
+}
+
+- (void)enterForeground
+{
+ PRINT_SIGNATURE();
+ // when we come back, restore playing if we were.
+ if (m_isPlayingBeforeInactive)
+ {
+ CApplicationMessenger::Get().MediaUnPause();
+ m_isPlayingBeforeInactive = NO;
+ }
+ m_isInterrupted = NO;
+}
+
+- (void)becomeInactive
+{
+ LOG(@"%s: was interrupted: %d", __PRETTY_FUNCTION__, m_isInterrupted);
+ // if we were interrupted, already paused here
+ // else if user background us or lock screen, only pause video here, audio keep playing.
+ if (g_application.IsPlayingVideo() && !g_application.IsPaused())
+ {
+ m_isPlayingBeforeInactive = YES;
+ CApplicationMessenger::Get().MediaPauseIfPlaying();
+ }
+ // check whether we need disable network auto suspend.
+ [self rescheduleNetworkAutoSuspend];
+}
+
+- (void)beginInterruption
+{
+ PRINT_SIGNATURE();
+ m_isInterrupted = YES;
+ CAEFactory::Suspend();
+}
+
+- (void)endInterruption
+{
+ PRINT_SIGNATURE();
+ if (CAEFactory::IsSuspended())
+ CAEFactory::Resume();
+}
+//--------------------------------------------------------------
- (void)pauseAnimation
{
- XBMC_Event newEvent;
-
- newEvent.appcommand.type = XBMC_APPCOMMAND;
- newEvent.appcommand.action = ACTION_PLAYER_PLAYPAUSE;
- CWinEventsIOS::MessagePush(&newEvent);
-
- /* Give player time to pause */
- Sleep(2000);
- //NSLog(@"%s", __PRETTY_FUNCTION__);
+ PRINT_SIGNATURE();
[m_glView pauseAnimation];
-
}
//--------------------------------------------------------------
- (void)resumeAnimation
{
- XBMC_Event newEvent;
-
- newEvent.appcommand.type = XBMC_APPCOMMAND;
- newEvent.appcommand.action = ACTION_PLAYER_PLAY;
- CWinEventsIOS::MessagePush(&newEvent);
-
+ PRINT_SIGNATURE();
+
[m_glView resumeAnimation];
}
//--------------------------------------------------------------
- (void)startAnimation
{
+ PRINT_SIGNATURE();
+
[m_glView startAnimation];
}
//--------------------------------------------------------------
- (void)stopAnimation
{
+ PRINT_SIGNATURE();
+
[m_glView stopAnimation];
}
+//--------------------------------------------------------------
+- (void)onPlay:(NSDictionary *)item
+{
+ PRINT_SIGNATURE();
+ // MPNowPlayingInfoCenter is an ios5+ class, following code will work on ios5 even if compiled by xcode3
+ Class NowPlayingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
+ if (NowPlayingInfoCenter)
+ {
+ NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
+
+ NSString *title = [item objectForKey:@"title"];
+ if (title && title.length > 0)
+ [dict setObject:title forKey:MPMediaItemPropertyTitle];
+ NSString *album = [item objectForKey:@"album"];
+ if (album && album.length > 0)
+ [dict setObject:album forKey:MPMediaItemPropertyAlbumTitle];
+ NSArray *artists = [item objectForKey:@"artist"];
+ if (artists && artists.count > 0)
+ [dict setObject:[artists componentsJoinedByString:@" "] forKey:MPMediaItemPropertyArtist];
+ NSNumber *track = [item objectForKey:@"track"];
+ if (track)
+ [dict setObject:track forKey:MPMediaItemPropertyAlbumTrackNumber];
+ NSNumber *duration = [item objectForKey:@"duration"];
+ if (duration)
+ [dict setObject:duration forKey:MPMediaItemPropertyPlaybackDuration];
+ NSArray *genres = [item objectForKey:@"genre"];
+ if (genres && genres.count > 0)
+ [dict setObject:[genres componentsJoinedByString:@" "] forKey:MPMediaItemPropertyGenre];
+ NSString *thumb = [item objectForKey:@"thumb"];
+ if (thumb && thumb.length > 0)
+ {
+ MPMediaItemArtwork *mArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageWithContentsOfFile:thumb]];
+ [dict setObject:mArt forKey:MPMediaItemPropertyArtwork];
+ [mArt release];
+ }
+ // these proprity keys are ios5+ only
+ NSNumber *elapsed = [item objectForKey:@"elapsed"];
+ if (elapsed)
+ [dict setObject:elapsed forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
+ NSNumber *speed = [item objectForKey:@"speed"];
+ if (speed)
+ [dict setObject:speed forKey:MPNowPlayingInfoPropertyPlaybackRate];
+ NSNumber *current = [item objectForKey:@"current"];
+ if (current)
+ [dict setObject:current forKey:MPNowPlayingInfoPropertyPlaybackQueueIndex];
+ NSNumber *total = [item objectForKey:@"total"];
+ if (total)
+ [dict setObject:total forKey:MPNowPlayingInfoPropertyPlaybackQueueCount];
+ /*
+ other properities can be set:
+ MPMediaItemPropertyAlbumTrackCount
+ MPMediaItemPropertyComposer
+ MPMediaItemPropertyDiscCount
+ MPMediaItemPropertyDiscNumber
+ MPMediaItemPropertyPersistentID
+
+ Additional metadata properties:
+ MPNowPlayingInfoPropertyChapterNumber;
+ MPNowPlayingInfoPropertyChapterCount;
+ */
+
+ [[NowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
+ [dict release];
+ }
+ m_playbackState = IOS_PLAYBACK_PLAYING;
+ [self disableNetworkAutoSuspend];
+}
+//--------------------------------------------------------------
+- (void)onPause:(NSDictionary *)item
+{
+ PRINT_SIGNATURE();
+ m_playbackState = IOS_PLAYBACK_PAUSED;
+ // schedule set network auto suspend state for save power if idle.
+ [self rescheduleNetworkAutoSuspend];
+}
+//--------------------------------------------------------------
+- (void)onStop:(NSDictionary *)item
+{
+ PRINT_SIGNATURE();
+ Class NowPlayingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
+ if (NowPlayingInfoCenter)
+ [[NowPlayingInfoCenter defaultCenter] setNowPlayingInfo:nil];
+ m_playbackState = IOS_PLAYBACK_STOPPED;
+ // delay set network auto suspend state in case we are switching playing item.
+ [self rescheduleNetworkAutoSuspend];
+}
+//--------------------------------------------------------------
+- (void)rescheduleNetworkAutoSuspend
+{
+ LOG(@"%s: playback state: %d", __PRETTY_FUNCTION__, m_playbackState);
+ if (m_playbackState == IOS_PLAYBACK_PLAYING)
+ {
+ [self disableNetworkAutoSuspend];
+ return;
+ }
+ if (m_networkAutoSuspendTimer)
+ [m_networkAutoSuspendTimer invalidate];
+
+ int delay = m_playbackState == IOS_PLAYBACK_PAUSED ? 60 : 30; // wait longer if paused than stopped
+ self.m_networkAutoSuspendTimer = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:@selector(enableNetworkAutoSuspend:) userInfo:nil repeats:NO];
+}
+
#pragma mark -
#pragma mark private helper methods
//
- (void)observeDefaultCenterStuff: (NSNotification *) notification
{
- //NSLog(@"default: %@", [notification name]);
+// LOG(@"default: %@", [notification name]);
+// LOG(@"userInfo: %@", [notification userInfo]);
}
@end
4 xbmc/osx/ios/XBMCIOS-Info.plist
View
@@ -36,6 +36,10 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<key>UIPrerenderedIcon</key>
<true/>
+ <key>UIBackgroundModes</key>
+ <array>
+ <string>audio</string>
+ </array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
Please sign in to comment.
Something went wrong with that request. Please try again.