Merge pull request #4539 from Matricom/amcodec
[vuplus_xbmc] / xbmc / osx / ios / XBMCController.mm
1 /*
2  *      Copyright (C) 2010-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 //hack around problem with xbmc's typedef int BOOL
22 // and obj-c's typedef unsigned char BOOL
23 #define BOOL XBMC_BOOL 
24 #include <sys/resource.h>
25 #include <signal.h>
26
27 #include "system.h"
28 #include "settings/AdvancedSettings.h"
29 #include "settings/Settings.h"
30 #include "FileItem.h"
31 #include "MusicInfoTag.h"
32 #include "SpecialProtocol.h"
33 #include "PlayList.h"
34 #include "ApplicationMessenger.h"
35 #include "Application.h"
36 #include "interfaces/AnnouncementManager.h"
37 #include "input/touch/generic/GenericTouchActionHandler.h"
38 #include "guilib/GUIControl.h"
39 #include "guilib/Key.h"
40 #include "windowing/WindowingFactory.h"
41 #include "video/VideoReferenceClock.h"
42 #include "utils/log.h"
43 #include "utils/TimeUtils.h"
44 #include "utils/Variant.h"
45 #include "Util.h"
46 #include "threads/Event.h"
47 #define id _id
48 #include "TextureCache.h"
49 #undef id
50 #include <math.h>
51
52 #ifndef M_PI
53 #define M_PI 3.1415926535897932384626433832795028842
54 #endif
55 #define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))
56
57 #undef BOOL
58
59 #import <AVFoundation/AVAudioSession.h>
60 #import <MediaPlayer/MPMediaItem.h>
61 #ifdef __IPHONE_5_0
62 #import <MediaPlayer/MPNowPlayingInfoCenter.h>
63 #else
64 const NSString *MPNowPlayingInfoPropertyElapsedPlaybackTime = @"MPNowPlayingInfoPropertyElapsedPlaybackTime";
65 const NSString *MPNowPlayingInfoPropertyPlaybackRate = @"MPNowPlayingInfoPropertyPlaybackRate";
66 const NSString *MPNowPlayingInfoPropertyPlaybackQueueIndex = @"MPNowPlayingInfoPropertyPlaybackQueueIndex";
67 const NSString *MPNowPlayingInfoPropertyPlaybackQueueCount = @"MPNowPlayingInfoPropertyPlaybackQueueCount";
68 #endif
69 #import "IOSEAGLView.h"
70
71 #import "XBMCController.h"
72 #import "IOSScreenManager.h"
73 #import "XBMCApplication.h"
74 #import "XBMCDebugHelpers.h"
75 #import "AutoPool.h"
76
77 XBMCController *g_xbmcController;
78 static CEvent screenChangeEvent;
79
80
81 // notification messages
82 extern NSString* kBRScreenSaverActivated;
83 extern NSString* kBRScreenSaverDismissed;
84
85 id objectFromVariant(const CVariant &data);
86
87 NSArray *arrayFromVariantArray(const CVariant &data)
88 {
89   if (!data.isArray())
90     return nil;
91   NSMutableArray *array = [[[NSMutableArray alloc] initWithCapacity:data.size()] autorelease];
92   for (CVariant::const_iterator_array itr = data.begin_array(); itr != data.end_array(); ++itr)
93   {
94     [array addObject:objectFromVariant(*itr)];
95   }
96   return array;
97 }
98
99 NSDictionary *dictionaryFromVariantMap(const CVariant &data)
100 {
101   if (!data.isObject())
102     return nil;
103   NSMutableDictionary *dict = [[[NSMutableDictionary alloc] initWithCapacity:data.size()] autorelease];
104   for (CVariant::const_iterator_map itr = data.begin_map(); itr != data.end_map(); ++itr)
105   {
106     [dict setValue:objectFromVariant(itr->second) forKey:[NSString stringWithUTF8String:itr->first.c_str()]];
107   }
108   return dict;
109 }
110
111 id objectFromVariant(const CVariant &data)
112 {
113   if (data.isNull())
114     return nil;
115   if (data.isString())
116     return [NSString stringWithUTF8String:data.asString().c_str()];
117   if (data.isWideString())
118     return [NSString stringWithCString:(const char *)data.asWideString().c_str() encoding:NSUnicodeStringEncoding];
119   if (data.isInteger())
120     return [NSNumber numberWithLongLong:data.asInteger()];
121   if (data.isUnsignedInteger())
122     return [NSNumber numberWithUnsignedLongLong:data.asUnsignedInteger()];
123   if (data.isBoolean())
124     return [NSNumber numberWithInt:data.asBoolean()?1:0];
125   if (data.isDouble())
126     return [NSNumber numberWithDouble:data.asDouble()];
127   if (data.isArray())
128     return arrayFromVariantArray(data);
129   if (data.isObject())
130     return dictionaryFromVariantMap(data);
131   return nil;
132 }
133
134 void AnnounceBridge(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
135 {
136   LOG(@"AnnounceBridge: [%s], [%s], [%s]", ANNOUNCEMENT::AnnouncementFlagToString(flag), sender, message);
137   NSDictionary *dict = dictionaryFromVariantMap(data);
138   LOG(@"data: %@", dict.description);
139   const std::string msg(message);
140   if (msg == "OnPlay")
141   {
142     NSDictionary *item = [dict valueForKey:@"item"];
143     NSDictionary *player = [dict valueForKey:@"player"];
144     [item setValue:[player valueForKey:@"speed"] forKey:@"speed"];
145     std::string thumb = g_application.CurrentFileItem().GetArt("thumb");
146     if (!thumb.empty())
147     {
148       bool needsRecaching;
149       CStdString cachedThumb(CTextureCache::Get().CheckCachedImage(thumb, false, needsRecaching));
150       LOG("thumb: %s, %s", thumb.c_str(), cachedThumb.c_str());
151       if (!cachedThumb.empty())
152       {
153         CStdString thumbRealPath = CSpecialProtocol::TranslatePath(cachedThumb);
154         [item setValue:[NSString stringWithUTF8String:thumbRealPath.c_str()] forKey:@"thumb"];
155       }
156     }
157     double duration = g_application.GetTotalTime();
158     if (duration > 0)
159       [item setValue:[NSNumber numberWithDouble:duration] forKey:@"duration"];
160     [item setValue:[NSNumber numberWithDouble:g_application.GetTime()] forKey:@"elapsed"];
161     int current = g_playlistPlayer.GetCurrentSong();
162     if (current >= 0)
163     {
164       [item setValue:[NSNumber numberWithInt:current] forKey:@"current"];
165       [item setValue:[NSNumber numberWithInt:g_playlistPlayer.GetPlaylist(g_playlistPlayer.GetCurrentPlaylist()).size()] forKey:@"total"];
166     }
167     if (g_application.CurrentFileItem().HasMusicInfoTag())
168     {
169       const std::vector<std::string> &genre = g_application.CurrentFileItem().GetMusicInfoTag()->GetGenre();
170       if (!genre.empty())
171       {
172         NSMutableArray *genreArray = [[NSMutableArray alloc] initWithCapacity:genre.size()];
173         for(std::vector<std::string>::const_iterator it = genre.begin(); it != genre.end(); ++it)
174         {
175           [genreArray addObject:[NSString stringWithUTF8String:it->c_str()]];
176         }
177         [item setValue:genreArray forKey:@"genre"];
178       }
179     }
180     LOG(@"item: %@", item.description);
181     [g_xbmcController performSelectorOnMainThread:@selector(onPlay:) withObject:item  waitUntilDone:NO];
182   }
183   else if (msg == "OnSpeedChanged" || msg == "OnPause")
184   {
185     NSDictionary *item = [dict valueForKey:@"item"];
186     NSDictionary *player = [dict valueForKey:@"player"];
187     [item setValue:[player valueForKey:@"speed"] forKey:@"speed"];
188     [item setValue:[NSNumber numberWithDouble:g_application.GetTime()] forKey:@"elapsed"];
189     LOG(@"item: %@", item.description);
190     [g_xbmcController performSelectorOnMainThread:@selector(OnSpeedChanged:) withObject:item  waitUntilDone:NO];
191     if (msg == "OnPause")
192       [g_xbmcController performSelectorOnMainThread:@selector(onPause:) withObject:[dict valueForKey:@"item"]  waitUntilDone:NO];
193   }
194   else if (msg == "OnStop")
195   {
196     [g_xbmcController performSelectorOnMainThread:@selector(onStop:) withObject:[dict valueForKey:@"item"]  waitUntilDone:NO];
197   }
198 }
199
200 class AnnounceReceiver : public ANNOUNCEMENT::IAnnouncer
201 {
202 public:
203   virtual void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
204   {
205     // not all Announce called from xbmc main thread, we need an auto poll here.
206     CCocoaAutoPool pool;
207     AnnounceBridge(flag, sender, message, data);
208   }
209   virtual ~AnnounceReceiver() {}
210   static void init()
211   {
212     if (NULL==g_announceReceiver) {
213       g_announceReceiver = new AnnounceReceiver();
214       ANNOUNCEMENT::CAnnouncementManager::AddAnnouncer(g_announceReceiver);
215     }
216   }
217   static void dealloc()
218   {
219     ANNOUNCEMENT::CAnnouncementManager::RemoveAnnouncer(g_announceReceiver);
220     delete g_announceReceiver;
221   }
222 private:
223   AnnounceReceiver() {}
224   static AnnounceReceiver *g_announceReceiver;
225 };
226
227 AnnounceReceiver *AnnounceReceiver::g_announceReceiver = NULL;
228
229 //--------------------------------------------------------------
230 //
231
232 @interface XBMCController ()
233 - (void)rescheduleNetworkAutoSuspend;
234 @end
235
236 @interface UIApplication (extended)
237 -(void) terminateWithSuccess;
238 @end
239
240 @implementation XBMCController
241 @synthesize animating;
242 @synthesize lastGesturePoint;
243 @synthesize screenScale;
244 @synthesize touchBeginSignaled;
245 @synthesize m_screenIdx;
246 @synthesize screensize;
247 @synthesize m_networkAutoSuspendTimer;
248 @synthesize nowPlayingInfo;
249 //--------------------------------------------------------------
250 - (void) sendKeypressEvent: (XBMC_Event) event
251 {
252   event.type = XBMC_KEYDOWN;
253   CWinEvents::MessagePush(&event);
254
255   event.type = XBMC_KEYUP;
256   CWinEvents::MessagePush(&event);
257 }
258
259 // START OF UIKeyInput protocol
260 - (BOOL)hasText
261 {
262   return NO;
263 }
264
265 - (void)insertText:(NSString *)text
266 {
267   XBMC_Event newEvent;
268   memset(&newEvent, 0, sizeof(newEvent));
269   unichar currentKey = [text characterAtIndex:0];
270
271   // handle upper case letters
272   if (currentKey >= 'A' && currentKey <= 'Z')
273   {
274     newEvent.key.keysym.mod = XBMCKMOD_LSHIFT;
275     currentKey += 0x20;// convert to lower case
276   }
277
278   // handle return
279   if (currentKey == '\n' || currentKey == '\r')
280     currentKey = XBMCK_RETURN;
281
282   newEvent.key.keysym.sym = (XBMCKey)currentKey;
283   newEvent.key.keysym.unicode = currentKey;
284
285   [self sendKeypressEvent:newEvent];
286 }
287
288 - (void)deleteBackward
289 {
290   [self sendKey:XBMCK_BACKSPACE];
291 }
292 // END OF UIKeyInput protocol
293
294 // - iOS6 rotation API - will be called on iOS7 runtime!--------
295 - (NSUInteger)supportedInterfaceOrientations
296 {
297   //mask defines available as of ios6 sdk
298   //return UIInterfaceOrientationMaskLandscape;
299   return (1 << UIInterfaceOrientationLandscapeLeft) | (1 << UIInterfaceOrientationLandscapeRight);
300 }
301 // - old rotation API will be called on iOS6 and lower - removed in iOS7
302 -(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
303 {  
304   //on external screens somehow the logic is rotated by 90°
305   //so we have to do this with our supported orientations then aswell
306   if([[IOSScreenManager sharedInstance] isExternalScreen])
307   {
308     if(interfaceOrientation == UIInterfaceOrientationPortrait) 
309     {
310       return YES;
311     }
312   }
313   else//internal screen
314   {
315     if(interfaceOrientation == UIInterfaceOrientationLandscapeLeft) 
316     {
317       return YES;
318     }
319     else if(interfaceOrientation == UIInterfaceOrientationLandscapeRight)
320     {
321       return YES;
322     }
323   }
324   return NO;
325 }
326 //--------------------------------------------------------------
327 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
328 {
329   orientation = toInterfaceOrientation;
330   CGRect srect = [IOSScreenManager getLandscapeResolution: [m_glView getCurrentScreen]];
331   CGRect rect = srect;;
332   
333
334   switch(toInterfaceOrientation)
335   {
336     case UIInterfaceOrientationPortrait:  
337     case UIInterfaceOrientationPortraitUpsideDown:
338       if(![[IOSScreenManager sharedInstance] isExternalScreen]) 
339       {
340         rect.size = CGSizeMake( srect.size.height, srect.size.width );    
341       }
342       break;
343     case UIInterfaceOrientationLandscapeLeft:
344     case UIInterfaceOrientationLandscapeRight:
345       break;//just leave the rect as is
346   }  
347         m_glView.frame = rect;
348 }
349
350 - (UIInterfaceOrientation) getOrientation
351 {
352         return orientation;
353 }
354
355 -(void)sendKey:(XBMCKey) key
356 {
357   XBMC_Event newEvent;
358   memset(&newEvent, 0, sizeof(newEvent));
359   
360   //newEvent.key.keysym.unicode = key;
361   newEvent.key.keysym.sym = key;
362   [self sendKeypressEvent:newEvent];
363   
364 }
365 //--------------------------------------------------------------
366 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
367 {
368   if ([gestureRecognizer isKindOfClass:[UIRotationGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]) {
369     return YES;
370   }
371
372   if ([gestureRecognizer isKindOfClass:[UISwipeGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
373     return YES;
374   }
375
376   
377   return NO;
378 }
379 //--------------------------------------------------------------
380 - (void)createGestureRecognizers 
381 {
382   //1 finger single tab
383   UITapGestureRecognizer *singleFingerSingleTap = [[UITapGestureRecognizer alloc]
384                                                    initWithTarget:self action:@selector(handleSingleFingerSingleTap:)];
385
386   singleFingerSingleTap.delaysTouchesBegan = NO;
387   singleFingerSingleTap.numberOfTapsRequired = 1;
388   singleFingerSingleTap.numberOfTouchesRequired = 1;
389
390   [m_glView addGestureRecognizer:singleFingerSingleTap];
391   [singleFingerSingleTap release];
392
393   //2 finger single tab - right mouse
394   //single finger double tab delays single finger single tab - so we
395   //go for 2 fingers here - so single finger single tap is instant
396   UITapGestureRecognizer *doubleFingerSingleTap = [[UITapGestureRecognizer alloc]
397     initWithTarget:self action:@selector(handleDoubleFingerSingleTap:)];  
398
399   doubleFingerSingleTap.delaysTouchesBegan = NO;
400   doubleFingerSingleTap.numberOfTapsRequired = 1;
401   doubleFingerSingleTap.numberOfTouchesRequired = 2;
402   [m_glView addGestureRecognizer:doubleFingerSingleTap];
403   [doubleFingerSingleTap release];
404
405   //1 finger single long tab - right mouse - alernative
406   UILongPressGestureRecognizer *singleFingerSingleLongTap = [[UILongPressGestureRecognizer alloc]
407     initWithTarget:self action:@selector(handleSingleFingerSingleLongTap:)];  
408
409   singleFingerSingleLongTap.delaysTouchesBegan = NO;
410   singleFingerSingleLongTap.delaysTouchesEnded = NO;
411   [m_glView addGestureRecognizer:singleFingerSingleLongTap];
412   [singleFingerSingleLongTap release];
413
414   //double finger swipe left for backspace ... i like this fast backspace feature ;)
415   UISwipeGestureRecognizer *swipeLeft2 = [[UISwipeGestureRecognizer alloc]
416                                             initWithTarget:self action:@selector(handleSwipe:)];
417
418   swipeLeft2.delaysTouchesBegan = NO;
419   swipeLeft2.numberOfTouchesRequired = 2;
420   swipeLeft2.direction = UISwipeGestureRecognizerDirectionLeft;
421   swipeLeft2.delegate = self;
422   [m_glView addGestureRecognizer:swipeLeft2];
423   [swipeLeft2 release];
424
425   //single finger swipe left
426   UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc]
427                                           initWithTarget:self action:@selector(handleSwipe:)];
428
429   swipeLeft.delaysTouchesBegan = NO;
430   swipeLeft.numberOfTouchesRequired = 1;
431   swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
432   swipeLeft.delegate = self;
433   [m_glView addGestureRecognizer:swipeLeft];
434   [swipeLeft release];
435   
436   //single finger swipe right
437   UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc]
438                                          initWithTarget:self action:@selector(handleSwipe:)];
439   
440   swipeRight.delaysTouchesBegan = NO;
441   swipeRight.numberOfTouchesRequired = 1;
442   swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
443   swipeRight.delegate = self;
444   [m_glView addGestureRecognizer:swipeRight];
445   [swipeRight release];
446   
447   //single finger swipe up
448   UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc]
449                                          initWithTarget:self action:@selector(handleSwipe:)];
450   
451   swipeUp.delaysTouchesBegan = NO;
452   swipeUp.numberOfTouchesRequired = 1;
453   swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
454   swipeUp.delegate = self;
455   [m_glView addGestureRecognizer:swipeUp];
456   [swipeUp release];
457
458   //single finger swipe down
459   UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc]
460                                          initWithTarget:self action:@selector(handleSwipe:)];
461   
462   swipeDown.delaysTouchesBegan = NO;
463   swipeDown.numberOfTouchesRequired = 1;
464   swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
465   swipeDown.delegate = self;
466   [m_glView addGestureRecognizer:swipeDown];
467   [swipeDown release];
468   
469   //for pan gestures with one finger
470   UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]
471     initWithTarget:self action:@selector(handlePan:)];
472
473   pan.delaysTouchesBegan = NO;
474   pan.maximumNumberOfTouches = 1;
475   [m_glView addGestureRecognizer:pan];
476   [pan release];
477
478   //for zoom gesture
479   UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]
480     initWithTarget:self action:@selector(handlePinch:)];
481
482   pinch.delaysTouchesBegan = NO;
483   pinch.delegate = self;
484   [m_glView addGestureRecognizer:pinch];
485   [pinch release];
486
487   //for rotate gesture
488   UIRotationGestureRecognizer *rotate = [[UIRotationGestureRecognizer alloc]
489                                          initWithTarget:self action:@selector(handleRotate:)];
490
491   rotate.delaysTouchesBegan = NO;
492   rotate.delegate = self;
493   [m_glView addGestureRecognizer:rotate];
494   [rotate release];
495 }
496 //--------------------------------------------------------------
497 - (void) activateKeyboard:(UIView *)view
498 {
499   [self.view addSubview:view];
500   m_glView.userInteractionEnabled = NO;
501 }
502 //--------------------------------------------------------------
503 - (void) deactivateKeyboard:(UIView *)view
504 {
505   [view removeFromSuperview];
506   m_glView.userInteractionEnabled = YES; 
507   [self becomeFirstResponder];
508 }
509 //--------------------------------------------------------------
510 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
511 {
512   if( [m_glView isXBMCAlive] )//NO GESTURES BEFORE WE ARE UP AND RUNNING
513   {
514     UITouch *touch = (UITouch *)[[touches allObjects] objectAtIndex:0];
515     CGPoint point = [touch locationInView:m_glView];
516     point.x *= screenScale;
517     point.y *= screenScale;
518     CGenericTouchActionHandler::Get().OnSingleTouchStart(point.x, point.y);
519   }
520 }
521 //--------------------------------------------------------------
522 -(void)handlePinch:(UIPinchGestureRecognizer*)sender
523 {
524   if( [m_glView isXBMCAlive] )//NO GESTURES BEFORE WE ARE UP AND RUNNING
525   {
526     CGPoint point = [sender locationOfTouch:0 inView:m_glView];  
527     point.x *= screenScale;
528     point.y *= screenScale;
529
530     switch(sender.state)
531     {
532       case UIGestureRecognizerStateBegan:
533         CGenericTouchActionHandler::Get().OnTouchGestureStart(point.x, point.y);
534         break;
535       case UIGestureRecognizerStateChanged:
536         CGenericTouchActionHandler::Get().OnZoomPinch(point.x, point.y, [sender scale]);
537         break;
538       case UIGestureRecognizerStateEnded:
539       case UIGestureRecognizerStateCancelled:
540         CGenericTouchActionHandler::Get().OnTouchGestureEnd(point.x, point.y, 0, 0, 0, 0);
541         break;
542       default:
543         break;
544     }
545   }
546 }
547 //--------------------------------------------------------------
548 -(void)handleRotate:(UIRotationGestureRecognizer*)sender
549 {
550   if( [m_glView isXBMCAlive] )//NO GESTURES BEFORE WE ARE UP AND RUNNING
551   {
552     CGPoint point = [sender locationOfTouch:0 inView:m_glView];
553     point.x *= screenScale;
554     point.y *= screenScale;
555
556     switch(sender.state)
557     {
558       case UIGestureRecognizerStateBegan:
559         CGenericTouchActionHandler::Get().OnTouchGestureStart(point.x, point.y);
560         break;
561       case UIGestureRecognizerStateChanged:
562         CGenericTouchActionHandler::Get().OnRotate(point.x, point.y, RADIANS_TO_DEGREES([sender rotation]));
563         break;
564       case UIGestureRecognizerStateEnded:
565         CGenericTouchActionHandler::Get().OnTouchGestureEnd(point.x, point.y, 0, 0, 0, 0);
566         break;
567       default:
568         break;
569     }
570   }
571 }
572 //--------------------------------------------------------------
573 - (IBAction)handlePan:(UIPanGestureRecognizer *)sender 
574 {
575   if( [m_glView isXBMCAlive] )//NO GESTURES BEFORE WE ARE UP AND RUNNING
576   { 
577     CGPoint velocity = [sender velocityInView:m_glView];
578
579     if( [sender state] == UIGestureRecognizerStateBegan )
580     {
581       CGPoint point = [sender locationOfTouch:0 inView:m_glView];
582       point.x *= screenScale;
583       point.y *= screenScale;
584       touchBeginSignaled = false;
585       lastGesturePoint = point;
586     }
587
588     if( [sender state] == UIGestureRecognizerStateChanged )
589     {
590       CGPoint point = [sender locationOfTouch:0 inView:m_glView];
591       point.x *= screenScale;
592       point.y *= screenScale;
593       bool bNotify = false;
594       CGFloat yMovement=point.y - lastGesturePoint.y;
595       CGFloat xMovement=point.x - lastGesturePoint.x;
596       
597       if( xMovement )
598       {
599         bNotify = true;
600       }
601       
602       if( yMovement )
603       {
604         bNotify = true;
605       }
606       
607       if( bNotify )
608       {
609         if( !touchBeginSignaled )
610         {
611           CGenericTouchActionHandler::Get().OnTouchGestureStart((float)point.x, (float)point.y);
612           touchBeginSignaled = true;
613         }
614
615         CGenericTouchActionHandler::Get().OnTouchGesturePan((float)point.x, (float)point.y,
616                                                             (float)xMovement, (float)yMovement, 
617                                                             (float)velocity.x, (float)velocity.y);
618         lastGesturePoint = point;
619       }
620     }
621     
622     if( touchBeginSignaled && ([sender state] == UIGestureRecognizerStateEnded || [sender state] == UIGestureRecognizerStateCancelled))
623     {
624       //signal end of pan - this will start inertial scrolling with deacceleration in CApplication
625       CGenericTouchActionHandler::Get().OnTouchGestureEnd((float)lastGesturePoint.x, (float)lastGesturePoint.y,
626                                                              (float)0.0, (float)0.0, 
627                                                              (float)velocity.x, (float)velocity.y);
628
629       touchBeginSignaled = false;
630     }
631   }
632 }
633 //--------------------------------------------------------------
634 - (IBAction)handleSwipe:(UISwipeGestureRecognizer *)sender
635 {
636   if( [m_glView isXBMCAlive] )//NO GESTURES BEFORE WE ARE UP AND RUNNING
637   {
638     
639     
640     if (sender.state == UIGestureRecognizerStateRecognized)
641     {
642       CGPoint point = [sender locationOfTouch:0 inView:m_glView];
643       point.x *= screenScale;
644       point.y *= screenScale;
645
646       TouchMoveDirection direction = TouchMoveDirectionNone;
647       switch ([sender direction])
648       {
649         case UISwipeGestureRecognizerDirectionRight:
650           direction = TouchMoveDirectionRight;
651           break;
652         case UISwipeGestureRecognizerDirectionLeft:
653           direction = TouchMoveDirectionLeft;
654           break;
655         case UISwipeGestureRecognizerDirectionUp:
656           direction = TouchMoveDirectionUp;
657           break;
658         case UISwipeGestureRecognizerDirectionDown:
659           direction = TouchMoveDirectionDown;
660           break;
661       }
662       CGenericTouchActionHandler::Get().OnSwipe(direction,
663                                                 0.0, 0.0,
664                                                 point.x, point.y, 0, 0,
665                                                 [sender numberOfTouches]);
666     }
667   }
668 }
669 //--------------------------------------------------------------
670 - (IBAction)handleSingleFingerSingleTap:(UIGestureRecognizer *)sender 
671 {
672   if( [m_glView isXBMCAlive] )//NO GESTURES BEFORE WE ARE UP AND RUNNING
673   {
674     CGPoint point = [sender locationOfTouch:0 inView:m_glView];
675     point.x *= screenScale;
676     point.y *= screenScale;
677     //NSLog(@"%s singleTap", __PRETTY_FUNCTION__);
678     CGenericTouchActionHandler::Get().OnTap((float)point.x, (float)point.y, [sender numberOfTouches]);
679   }
680 }
681 //--------------------------------------------------------------
682 - (IBAction)handleDoubleFingerSingleTap:(UIGestureRecognizer *)sender
683 {
684   if( [m_glView isXBMCAlive] )//NO GESTURES BEFORE WE ARE UP AND RUNNING
685   {
686     CGPoint point = [sender locationOfTouch:0 inView:m_glView];
687     point.x *= screenScale;
688     point.y *= screenScale;
689     //NSLog(@"%s toubleTap", __PRETTY_FUNCTION__);
690     CGenericTouchActionHandler::Get().OnTap((float)point.x, (float)point.y, [sender numberOfTouches]);
691   }
692 }
693 //--------------------------------------------------------------
694 - (IBAction)handleSingleFingerSingleLongTap:(UIGestureRecognizer *)sender
695 {
696   if( [m_glView isXBMCAlive] )//NO GESTURES BEFORE WE ARE UP AND RUNNING
697   {
698     CGPoint point = [sender locationOfTouch:0 inView:m_glView];
699     point.x *= screenScale;
700     point.y *= screenScale;
701
702     if (sender.state == UIGestureRecognizerStateBegan)
703     {
704       lastGesturePoint = point;
705       // mark the control
706       //CGenericTouchActionHandler::Get().OnSingleTouchStart((float)point.x, (float)point.y);
707     }
708
709     if (sender.state == UIGestureRecognizerStateEnded)
710     {
711       CGenericTouchActionHandler::Get().OnSingleTouchMove((float)point.x, (float)point.y, point.x - lastGesturePoint.x, point.y - lastGesturePoint.y, 0, 0);
712     }
713     
714     if (sender.state == UIGestureRecognizerStateEnded)
715     {   
716       CGenericTouchActionHandler::Get().OnLongPress((float)point.x, (float)point.y);
717     }
718   }
719 }
720 //--------------------------------------------------------------
721 - (id)initWithFrame:(CGRect)frame withScreen:(UIScreen *)screen
722
723   PRINT_SIGNATURE();
724   m_screenIdx = 0;
725   self = [super init];
726   if ( !self )
727     return ( nil );
728
729   m_isPlayingBeforeInactive = NO;
730   m_bgTask = UIBackgroundTaskInvalid;
731   m_playbackState = IOS_PLAYBACK_STOPPED;
732
733   m_window = [[UIWindow alloc] initWithFrame:frame];
734   [m_window setRootViewController:self];  
735   m_window.screen = screen;
736   /* Turn off autoresizing */
737   m_window.autoresizingMask = 0;
738   m_window.autoresizesSubviews = NO;
739   
740   NSNotificationCenter *center;
741   center = [NSNotificationCenter defaultCenter];
742   [center addObserver: self
743              selector: @selector(observeDefaultCenterStuff:)
744                  name: nil
745                object: nil];
746
747   /* We start in landscape mode */
748   CGRect srect = frame;
749   srect.size = CGSizeMake( frame.size.height, frame.size.width );
750   orientation = UIInterfaceOrientationLandscapeLeft;
751   
752   m_glView = [[IOSEAGLView alloc] initWithFrame: srect withScreen:screen];
753   [[IOSScreenManager sharedInstance] setView:m_glView];  
754   [m_glView setMultipleTouchEnabled:YES];
755   
756   /* Check if screen is Retina */
757   screenScale = [m_glView getScreenScale:screen];
758
759   [self.view addSubview: m_glView];
760   
761   [self createGestureRecognizers];
762   [m_window addSubview: self.view];
763   [m_window makeKeyAndVisible];
764   g_xbmcController = self;  
765   
766   AnnounceReceiver::init();
767
768   return self;
769 }
770 //--------------------------------------------------------------
771 -(void)viewDidLoad
772 {
773   [super viewDidLoad];
774 }
775 //--------------------------------------------------------------
776 - (void)dealloc
777 {
778   // stop background task
779   [m_networkAutoSuspendTimer invalidate];
780   [self enableNetworkAutoSuspend:nil];
781
782   AnnounceReceiver::dealloc();
783   [m_glView stopAnimation];
784   [m_glView release];
785   [m_window release];
786
787   NSNotificationCenter *center;
788   // take us off the default center for our app
789   center = [NSNotificationCenter defaultCenter];
790   [center removeObserver: self];
791   
792   [super dealloc];
793 }
794 //--------------------------------------------------------------
795 - (void)viewWillAppear:(BOOL)animated
796 {
797   PRINT_SIGNATURE();
798   
799   // move this later into CocoaPowerSyscall
800   [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
801   
802   [self resumeAnimation];
803   
804   [super viewWillAppear:animated];
805 }
806 //--------------------------------------------------------------
807 -(void) viewDidAppear:(BOOL)animated
808 {
809   [super viewDidAppear:animated];
810
811   [self becomeFirstResponder];
812   [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
813 }
814 //--------------------------------------------------------------
815 - (void)viewWillDisappear:(BOOL)animated
816 {  
817   PRINT_SIGNATURE();
818   
819   [self pauseAnimation];
820   
821   // move this later into CocoaPowerSyscall
822   [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
823         
824   [super viewWillDisappear:animated];
825 }
826 //--------------------------------------------------------------
827 -(UIView *)inputView
828 {
829   // override our input view to an empty view
830   // this prevents the on screen keyboard
831   // which would be shown whenever this UIResponder
832   // becomes the first responder (which is always the case!)
833   // caused by implementing the UIKeyInput protocol
834   return [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
835 }
836 //--------------------------------------------------------------
837 - (BOOL) canBecomeFirstResponder
838 {
839   return YES;
840 }
841 //--------------------------------------------------------------
842 - (void)viewDidUnload
843 {
844   [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
845   [self resignFirstResponder];
846
847         [super viewDidUnload];  
848 }
849 //--------------------------------------------------------------
850 - (void) initDisplayLink
851 {
852         [m_glView initDisplayLink];
853 }
854 //--------------------------------------------------------------
855 - (void) deinitDisplayLink
856 {
857   [m_glView deinitDisplayLink];
858 }
859 //--------------------------------------------------------------
860 - (double) getDisplayLinkFPS;
861 {
862   return [m_glView getDisplayLinkFPS];
863 }
864 //--------------------------------------------------------------
865 - (void) setFramebuffer
866 {
867   [m_glView setFramebuffer];
868 }
869 //--------------------------------------------------------------
870 - (bool) presentFramebuffer
871 {
872   return [m_glView presentFramebuffer];
873 }
874 //--------------------------------------------------------------
875 - (CGSize) getScreenSize
876 {
877   screensize.width  = m_glView.bounds.size.width * screenScale;
878   screensize.height = m_glView.bounds.size.height * screenScale;  
879   return screensize;
880 }
881 //--------------------------------------------------------------
882 - (CGFloat) getScreenScale:(UIScreen *)screen;
883 {
884   return [m_glView getScreenScale:screen];
885 }
886 //--------------------------------------------------------------
887 //--------------------------------------------------------------
888 - (BOOL) recreateOnReselect
889
890   PRINT_SIGNATURE();
891   return YES;
892 }
893 //--------------------------------------------------------------
894 - (void)didReceiveMemoryWarning
895 {
896   // Releases the view if it doesn't have a superview.
897   [super didReceiveMemoryWarning];
898   
899   // Release any cached data, images, etc. that aren't in use.
900 }
901 //--------------------------------------------------------------
902 - (void)disableNetworkAutoSuspend
903 {
904   PRINT_SIGNATURE();
905   if (m_bgTask != UIBackgroundTaskInvalid)
906   {
907     [[UIApplication sharedApplication] endBackgroundTask: m_bgTask];
908     m_bgTask = UIBackgroundTaskInvalid;
909   }
910   // we have to alloc the background task for keep network working after screen lock and dark.
911   UIBackgroundTaskIdentifier newTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
912   m_bgTask = newTask;
913
914   if (m_networkAutoSuspendTimer)
915   {
916     [m_networkAutoSuspendTimer invalidate];
917     self.m_networkAutoSuspendTimer = nil;
918   }
919 }
920 //--------------------------------------------------------------
921 - (void)enableNetworkAutoSuspend:(id)obj
922 {
923   PRINT_SIGNATURE();
924   if (m_bgTask != UIBackgroundTaskInvalid)
925   {
926     [[UIApplication sharedApplication] endBackgroundTask: m_bgTask];
927     m_bgTask = UIBackgroundTaskInvalid;
928   }
929 }
930 //--------------------------------------------------------------
931 - (void) disableSystemSleep
932 {
933 }
934 //--------------------------------------------------------------
935 - (void) enableSystemSleep
936 {
937 }
938 //--------------------------------------------------------------
939 - (void) disableScreenSaver
940 {
941 }
942 //--------------------------------------------------------------
943 - (void) enableScreenSaver
944 {
945 }
946 //--------------------------------------------------------------
947 - (bool) changeScreen: (unsigned int)screenIdx withMode:(UIScreenMode *)mode
948 {
949   bool ret = false;
950
951   ret = [[IOSScreenManager sharedInstance] changeScreen:screenIdx withMode:mode];
952
953   return ret;
954 }
955 //--------------------------------------------------------------
956 - (void) activateScreen: (UIScreen *)screen  withOrientation:(UIInterfaceOrientation)newOrientation
957 {
958   // Since ios7 we have to handle the orientation manually
959   // it differs by 90 degree between internal and external screen
960   float   angle = 0;
961   UIView *view = [m_window.subviews objectAtIndex:0];
962   switch(newOrientation)
963   {
964     case UIInterfaceOrientationPortrait:
965       angle = 0;
966       break;
967     case UIInterfaceOrientationPortraitUpsideDown:
968       angle = M_PI;
969       break;
970     case UIInterfaceOrientationLandscapeLeft:
971       angle = -M_PI_2;
972       break;
973     case UIInterfaceOrientationLandscapeRight:
974       angle = M_PI_2;
975       break;
976   }
977   // reset the rotation of the view
978   view.layer.transform = CATransform3DMakeRotation(angle, 0, 0.0, 1.0);
979   [view setFrame:m_window.frame];
980   m_window.screen = screen;
981 }
982 //--------------------------------------------------------------
983 - (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
984   LOG(@"%s: type %d, subtype: %d", __PRETTY_FUNCTION__, receivedEvent.type, receivedEvent.subtype);
985   if (receivedEvent.type == UIEventTypeRemoteControl)
986   {
987     [self disableNetworkAutoSuspend];
988     switch (receivedEvent.subtype)
989     {
990       case UIEventSubtypeRemoteControlTogglePlayPause:
991         CApplicationMessenger::Get().SendAction(ACTION_PLAYER_PLAYPAUSE);
992         break;
993       case UIEventSubtypeRemoteControlPlay:
994         CApplicationMessenger::Get().SendAction(ACTION_PLAYER_PLAY);
995         break;
996       case UIEventSubtypeRemoteControlPause:
997         // ACTION_PAUSE sometimes cause unpause, use MediaPauseIfPlaying to make sure pause only
998         CApplicationMessenger::Get().MediaPauseIfPlaying();
999         break;
1000       case UIEventSubtypeRemoteControlNextTrack:
1001         CApplicationMessenger::Get().SendAction(ACTION_NEXT_ITEM);
1002         break;
1003       case UIEventSubtypeRemoteControlPreviousTrack:
1004         CApplicationMessenger::Get().SendAction(ACTION_PREV_ITEM);
1005         break;
1006       case UIEventSubtypeRemoteControlBeginSeekingForward:
1007         // use 4X speed forward.
1008         CApplicationMessenger::Get().SendAction(ACTION_PLAYER_FORWARD);
1009         CApplicationMessenger::Get().SendAction(ACTION_PLAYER_FORWARD);
1010         break;
1011       case UIEventSubtypeRemoteControlBeginSeekingBackward:
1012         // use 4X speed rewind.
1013         CApplicationMessenger::Get().SendAction(ACTION_PLAYER_REWIND);
1014         CApplicationMessenger::Get().SendAction(ACTION_PLAYER_REWIND);
1015         break;
1016       case UIEventSubtypeRemoteControlEndSeekingForward:
1017       case UIEventSubtypeRemoteControlEndSeekingBackward:
1018         // restore to normal playback speed.
1019         if (g_application.m_pPlayer->IsPlaying() && !g_application.m_pPlayer->IsPaused())
1020           CApplicationMessenger::Get().SendAction(ACTION_PLAYER_PLAY);
1021         break;
1022       default:
1023         LOG(@"unhandled subtype: %d", receivedEvent.subtype);
1024         break;
1025     }
1026     [self rescheduleNetworkAutoSuspend];
1027   }
1028 }
1029 //--------------------------------------------------------------
1030 - (void)enterBackground
1031 {
1032   PRINT_SIGNATURE();
1033   if (g_application.m_pPlayer->IsPlaying() && !g_application.m_pPlayer->IsPaused())
1034   {
1035     m_isPlayingBeforeInactive = YES;
1036     CApplicationMessenger::Get().MediaPauseIfPlaying();
1037   }
1038   g_Windowing.OnAppFocusChange(false);
1039 }
1040
1041 - (void)enterForeground
1042 {
1043   PRINT_SIGNATURE();
1044   g_Windowing.OnAppFocusChange(true);
1045   // when we come back, restore playing if we were.
1046   if (m_isPlayingBeforeInactive)
1047   {
1048     CApplicationMessenger::Get().MediaUnPause();
1049     m_isPlayingBeforeInactive = NO;
1050   }
1051 }
1052
1053 - (void)becomeInactive
1054 {
1055   // if we were interrupted, already paused here
1056   // else if user background us or lock screen, only pause video here, audio keep playing.
1057   if (g_application.m_pPlayer->IsPlayingVideo() && !g_application.m_pPlayer->IsPaused())
1058   {
1059     m_isPlayingBeforeInactive = YES;
1060     CApplicationMessenger::Get().MediaPauseIfPlaying();
1061   }
1062   // check whether we need disable network auto suspend.
1063   [self rescheduleNetworkAutoSuspend];
1064 }
1065 //--------------------------------------------------------------
1066 - (void)pauseAnimation
1067 {
1068   PRINT_SIGNATURE();
1069   
1070   [m_glView pauseAnimation];
1071 }
1072 //--------------------------------------------------------------
1073 - (void)resumeAnimation
1074 {  
1075   PRINT_SIGNATURE();
1076
1077   [m_glView resumeAnimation];
1078 }
1079 //--------------------------------------------------------------
1080 - (void)startAnimation
1081 {
1082   PRINT_SIGNATURE();
1083
1084   [m_glView startAnimation];
1085 }
1086 //--------------------------------------------------------------
1087 - (void)stopAnimation
1088 {
1089   PRINT_SIGNATURE();
1090
1091   [m_glView stopAnimation];
1092 }
1093 //--------------------------------------------------------------
1094 - (void)setIOSNowPlayingInfo:(NSDictionary *)info
1095 {
1096   self.nowPlayingInfo = info;
1097   // MPNowPlayingInfoCenter is an ios5+ class, following code will work on ios5 even if compiled by xcode3
1098   Class NowPlayingInfoCenter = NSClassFromString(@"MPNowPlayingInfoCenter");
1099   if (NowPlayingInfoCenter)
1100     [[NowPlayingInfoCenter defaultCenter] setNowPlayingInfo:self.nowPlayingInfo];
1101 }
1102 //--------------------------------------------------------------
1103 - (void)onPlay:(NSDictionary *)item
1104 {
1105   PRINT_SIGNATURE();
1106   NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
1107
1108   NSString *title = [item objectForKey:@"title"];
1109   if (title && title.length > 0)
1110     [dict setObject:title forKey:MPMediaItemPropertyTitle];
1111   NSString *album = [item objectForKey:@"album"];
1112   if (album && album.length > 0)
1113     [dict setObject:album forKey:MPMediaItemPropertyAlbumTitle];
1114   NSArray *artists = [item objectForKey:@"artist"];
1115   if (artists && artists.count > 0)
1116     [dict setObject:[artists componentsJoinedByString:@" "] forKey:MPMediaItemPropertyArtist];
1117   NSNumber *track = [item objectForKey:@"track"];
1118   if (track)
1119     [dict setObject:track forKey:MPMediaItemPropertyAlbumTrackNumber];
1120   NSNumber *duration = [item objectForKey:@"duration"];
1121   if (duration)
1122     [dict setObject:duration forKey:MPMediaItemPropertyPlaybackDuration];
1123   NSArray *genres = [item objectForKey:@"genre"];
1124   if (genres && genres.count > 0)
1125     [dict setObject:[genres componentsJoinedByString:@" "] forKey:MPMediaItemPropertyGenre];
1126
1127   if (NSClassFromString(@"MPNowPlayingInfoCenter"))
1128   {
1129     NSString *thumb = [item objectForKey:@"thumb"];
1130     if (thumb && thumb.length > 0)
1131     {
1132       UIImage *image = [UIImage imageWithContentsOfFile:thumb];
1133       if (image)
1134       {
1135         MPMediaItemArtwork *mArt = [[MPMediaItemArtwork alloc] initWithImage:image];
1136         if (mArt)
1137         {
1138           [dict setObject:mArt forKey:MPMediaItemPropertyArtwork];
1139           [mArt release];
1140         }
1141       }
1142     }
1143     // these proprity keys are ios5+ only
1144     NSNumber *elapsed = [item objectForKey:@"elapsed"];
1145     if (elapsed)
1146       [dict setObject:elapsed forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
1147     NSNumber *speed = [item objectForKey:@"speed"];
1148     if (speed)
1149       [dict setObject:speed forKey:MPNowPlayingInfoPropertyPlaybackRate];
1150     NSNumber *current = [item objectForKey:@"current"];
1151     if (current)
1152       [dict setObject:current forKey:MPNowPlayingInfoPropertyPlaybackQueueIndex];
1153     NSNumber *total = [item objectForKey:@"total"];
1154     if (total)
1155       [dict setObject:total forKey:MPNowPlayingInfoPropertyPlaybackQueueCount];
1156   }
1157   /*
1158    other properities can be set:
1159    MPMediaItemPropertyAlbumTrackCount
1160    MPMediaItemPropertyComposer
1161    MPMediaItemPropertyDiscCount
1162    MPMediaItemPropertyDiscNumber
1163    MPMediaItemPropertyPersistentID
1164
1165    Additional metadata properties:
1166    MPNowPlayingInfoPropertyChapterNumber;
1167    MPNowPlayingInfoPropertyChapterCount;
1168    */
1169
1170   [self setIOSNowPlayingInfo:dict];
1171   [dict release];
1172
1173   m_playbackState = IOS_PLAYBACK_PLAYING;
1174   [self disableNetworkAutoSuspend];
1175 }
1176 //--------------------------------------------------------------
1177 - (void)OnSpeedChanged:(NSDictionary *)item
1178 {
1179   PRINT_SIGNATURE();
1180   if (NSClassFromString(@"MPNowPlayingInfoCenter"))
1181   {
1182     NSMutableDictionary *info = [self.nowPlayingInfo mutableCopy];
1183     NSNumber *elapsed = [item objectForKey:@"elapsed"];
1184     if (elapsed)
1185       [info setObject:elapsed forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
1186     NSNumber *speed = [item objectForKey:@"speed"];
1187     if (speed)
1188       [info setObject:speed forKey:MPNowPlayingInfoPropertyPlaybackRate];
1189
1190     [self setIOSNowPlayingInfo:info];
1191   }
1192 }
1193 //--------------------------------------------------------------
1194 - (void)onPause:(NSDictionary *)item
1195 {
1196   PRINT_SIGNATURE();
1197   m_playbackState = IOS_PLAYBACK_PAUSED;
1198   // schedule set network auto suspend state for save power if idle.
1199   [self rescheduleNetworkAutoSuspend];
1200 }
1201 //--------------------------------------------------------------
1202 - (void)onStop:(NSDictionary *)item
1203 {
1204   PRINT_SIGNATURE();
1205   [self setIOSNowPlayingInfo:nil];
1206
1207   m_playbackState = IOS_PLAYBACK_STOPPED;
1208   // delay set network auto suspend state in case we are switching playing item.
1209   [self rescheduleNetworkAutoSuspend];
1210 }
1211 //--------------------------------------------------------------
1212 - (void)rescheduleNetworkAutoSuspend
1213 {
1214   LOG(@"%s: playback state: %d", __PRETTY_FUNCTION__,  m_playbackState);
1215   if (m_playbackState == IOS_PLAYBACK_PLAYING)
1216   {
1217     [self disableNetworkAutoSuspend];
1218     return;
1219   }
1220   if (m_networkAutoSuspendTimer)
1221     [m_networkAutoSuspendTimer invalidate];
1222
1223   int delay = m_playbackState == IOS_PLAYBACK_PAUSED ? 60 : 30;  // wait longer if paused than stopped
1224   self.m_networkAutoSuspendTimer = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:@selector(enableNetworkAutoSuspend:) userInfo:nil repeats:NO];
1225 }
1226
1227 #pragma mark -
1228 #pragma mark private helper methods
1229 //
1230 - (void)observeDefaultCenterStuff: (NSNotification *) notification
1231 {
1232 //  LOG(@"default: %@", [notification name]);
1233 //  LOG(@"userInfo: %@", [notification userInfo]);
1234 }
1235
1236 @end