changed: Improve (fallback) logic for subtitle downloading
[vuplus_xbmc] / xbmc / osx / ios / XBMCApplication.m
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 #import <UIKit/UIKit.h>
22 #import <AVFoundation/AVAudioSession.h>
23
24 #import "XBMCApplication.h"
25 #import "XBMCController.h"
26 #import "IOSScreenManager.h"
27 #import "XBMCDebugHelpers.h"
28 #import <objc/runtime.h>
29
30 @implementation XBMCApplicationDelegate
31 XBMCController *m_xbmcController;  
32
33 // - iOS6 rotation API - will be called on iOS7 runtime!--------
34 // - on iOS7 first application is asked for supported orientation
35 // - then the controller of the current view is asked for supported orientation
36 // - if both say OK - rotation is allowed
37 - (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
38 {
39   if ([[window rootViewController] respondsToSelector:@selector(supportedInterfaceOrientations)])
40     return [[window rootViewController] supportedInterfaceOrientations];
41   else
42     return (1 << UIInterfaceOrientationLandscapeRight) | (1 << UIInterfaceOrientationLandscapeLeft);
43 }
44
45 - (void)applicationWillResignActive:(UIApplication *)application
46 {
47   PRINT_SIGNATURE();
48
49   [m_xbmcController pauseAnimation];
50   [m_xbmcController becomeInactive];
51 }
52
53 - (void)applicationDidBecomeActive:(UIApplication *)application
54 {
55   PRINT_SIGNATURE();
56
57   [m_xbmcController resumeAnimation];
58   [m_xbmcController enterForeground];
59 }
60
61 - (void)applicationDidEnterBackground:(UIApplication *)application
62 {
63   PRINT_SIGNATURE();
64
65   if (application.applicationState == UIApplicationStateBackground)
66   {
67     // the app is turn into background, not in by screen lock which has app state inactive.
68     [m_xbmcController enterBackground];
69   }
70 }
71
72 - (void)applicationWillTerminate:(UIApplication *)application
73 {
74   PRINT_SIGNATURE();
75
76   [m_xbmcController stopAnimation];
77 }
78
79 - (void)applicationWillEnterForeground:(UIApplication *)application
80 {
81   PRINT_SIGNATURE();
82 }
83
84 - (void)screenDidConnect:(NSNotification *)aNotification
85 {
86   [IOSScreenManager updateResolutions];
87 }
88
89 - (void)screenDidDisconnect:(NSNotification *)aNotification
90 {
91   [IOSScreenManager updateResolutions];
92 }
93
94 - (void)registerScreenNotifications:(BOOL)bRegister
95 {
96   NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];  
97   
98   if( bRegister )
99   {
100     //register to screen notifications
101     [nc addObserver:self selector:@selector(screenDidConnect:) name:UIScreenDidConnectNotification object:nil]; 
102     [nc addObserver:self selector:@selector(screenDidDisconnect:) name:UIScreenDidDisconnectNotification object:nil]; 
103   }
104   else
105   {
106     //deregister from screen notifications
107     [nc removeObserver:self name:UIScreenDidConnectNotification object:nil];
108     [nc removeObserver:self name:UIScreenDidDisconnectNotification object:nil];
109   }
110 }
111
112 - (void)applicationDidFinishLaunching:(UIApplication *)application 
113 {
114   PRINT_SIGNATURE();
115
116   [[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
117   UIScreen *currentScreen = [UIScreen mainScreen];
118
119   m_xbmcController = [[XBMCController alloc] initWithFrame: [currentScreen bounds] withScreen:currentScreen];  
120   m_xbmcController.wantsFullScreenLayout = YES;  
121   [m_xbmcController startAnimation];
122   [self registerScreenNotifications:YES];
123
124   NSError *err = nil;
125   if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&err])
126   {
127     ELOG(@"AVAudioSession setCategory failed: %@", err);
128   }
129   err = nil;
130   if (![[AVAudioSession sharedInstance] setActive: YES error: &err])
131   {
132     ELOG(@"AVAudioSession setActive failed: %@", err);
133   }
134   [[AVAudioSession sharedInstance] setDelegate:self];
135 }
136
137 - (void)beginInterruption
138 {
139   PRINT_SIGNATURE();
140   [m_xbmcController beginInterruption];
141 }
142 - (void)endInterruptionWithFlags:(NSUInteger)flags
143 {
144   LOG(@"%s: %d", __PRETTY_FUNCTION__, flags);
145   if (flags & AVAudioSessionInterruptionFlags_ShouldResume)
146   {
147     NSError *err = nil;
148     if (![[AVAudioSession sharedInstance] setActive: YES error: &err])
149     {
150       ELOG(@"AVAudioSession::endInterruption setActive failed: %@", err);
151     }
152     [m_xbmcController endInterruption];
153   }
154 }
155
156 - (void)dealloc
157 {
158   [self registerScreenNotifications:NO];
159   [m_xbmcController stopAnimation];
160   [m_xbmcController release];
161
162   [super dealloc];
163 }
164 @end
165
166 //---------------- HOOK FOR BT KEYBOARD CURSORS KEYS START----------------
167 #define GSEVENT_TYPE 2
168 #define GSEVENT_FLAGS 12
169 #define GSEVENTKEY_KEYCODE 15
170 #define GSEVENTKEY_KEYCODE_IOS7 17
171 #define GSEVENTKEY_KEYCODE_64_BIT 19
172 #define GSEVENT_TYPE_KEYUP 11
173
174 #define MSHookMessageEx(class, selector, replacement, result) \
175 (*(result) = method_setImplementation(class_getInstanceMethod((class), (selector)), (replacement)))
176
177 static UniChar kGKKeyboardDirectionRight = 79;
178 static UniChar kGKKeyboardDirectionLeft = 80;
179 static UniChar kGKKeyboardDirectionDown = 81;
180 static UniChar kGKKeyboardDirectionUp = 82;
181
182 // pointer to the original hooked method
183 static void (*UIApplication$sendEvent$Orig)(id, SEL, UIEvent*);
184
185 static bool ios7Detected = false;
186
187 void handleKeyCode(UniChar keyCode)
188 {
189   XBMCKey key = XBMCK_UNKNOWN;
190   //LOG(@"%s: tmp key %x", __PRETTY_FUNCTION__, keyCode);
191   if      (keyCode == kGKKeyboardDirectionRight)
192     key = XBMCK_RIGHT;
193   else if (keyCode == kGKKeyboardDirectionLeft)
194     key = XBMCK_LEFT;
195   else if (keyCode == kGKKeyboardDirectionDown)
196     key = XBMCK_DOWN;
197   else if (keyCode == kGKKeyboardDirectionUp)
198     key = XBMCK_UP;
199   else
200   {
201     //LOG(@"%s: tmp key unsupported :(", __PRETTY_FUNCTION__);
202     return; // not supported by us - return...
203   }
204   
205   [g_xbmcController sendKey:key];
206 }
207
208 static void XBMCsendEvent(id _self, SEL _cmd, UIEvent *event)
209
210   // call super implementation
211   UIApplication$sendEvent$Orig(_self, _cmd, event);
212
213   if ([event respondsToSelector:@selector(_gsEvent)]) 
214   {
215     // Key events come in form of UIInternalEvents.
216     // They contain a GSEvent object which contains 
217     // a GSEventRecord among other things
218 #pragma clang diagnostic push
219 #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
220     int *eventMem = (int *)[event performSelector:@selector(_gsEvent)];
221 #pragma clang diagnostic pop
222
223     if (eventMem) 
224     {
225       // So far we got a GSEvent :)
226       int eventType = eventMem[GSEVENT_TYPE];
227       if (eventType == GSEVENT_TYPE_KEYUP) 
228       {
229         // support 32 and 64bit arm here...
230         int idx = GSEVENTKEY_KEYCODE;
231         if (sizeof(NSInteger) == 8)
232           idx = GSEVENTKEY_KEYCODE_64_BIT;
233         else if (ios7Detected)
234           idx = GSEVENTKEY_KEYCODE_IOS7;
235
236         // Now we got a GSEventKey!
237         
238         // Read flags from GSEvent
239         // for modifier keys if we want to use them somehow at a later time
240         //int eventFlags = eventMem[GSEVENT_FLAGS];
241         // Read keycode from GSEventKey
242         UniChar tmp = (UniChar)eventMem[idx];
243         handleKeyCode(tmp);
244       }
245     }
246   }
247 }
248
249 // implicit called constructor for hooking into the sendEvent (ios < 7) or handleKeyUiEvent (ios 7 and later)
250 // this one hooks us into the keyboard events
251 __attribute__((constructor)) static void HookKeyboard(void)
252 {
253   if (sizeof(NSUInteger) == 8)
254   {
255     kGKKeyboardDirectionDown = 31;
256     kGKKeyboardDirectionUp = 30;
257     kGKKeyboardDirectionRight = 29;
258     kGKKeyboardDirectionLeft = 28;
259     LOG(@"Detected 64bit system!!!");
260   }
261   else
262     LOG(@"Detected 32bit system!!!");
263   
264   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
265   {
266     // Hook into sendEvent: to get keyboard events.
267 #pragma clang diagnostic push
268 #pragma clang diagnostic ignored "-Wundeclared-selector"
269     if ([UIApplication  instancesRespondToSelector:@selector(handleKeyUIEvent:)])
270     {
271       ios7Detected  = true;
272       MSHookMessageEx(objc_getClass("UIApplication"), @selector(handleKeyUIEvent:), (IMP)&XBMCsendEvent, (IMP*)&UIApplication$sendEvent$Orig);
273     }
274     else if ([UIApplication  instancesRespondToSelector:@selector(sendEvent:)])
275       MSHookMessageEx(objc_getClass("UIApplication"), @selector(sendEvent:), (IMP)&XBMCsendEvent, (IMP*)&UIApplication$sendEvent$Orig);
276     else
277       ELOG(@"HookKeyboard: Couldn't hook any of the 2 known keyboard hooks (sendEvent or handleKeyUIEvent - cursor keys on btkeyboards won't work!");
278 #pragma clang diagnostic pop
279   }
280   [pool release];
281 }
282 //---------------- HOOK FOR BT KEYBOARD CURSORS KEYS END----------------
283
284 int main(int argc, char *argv[]) {
285   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];  
286   int retVal = 0;
287   
288   // Block SIGPIPE
289   // SIGPIPE repeatably kills us, turn it off
290   {
291     sigset_t set;
292     sigemptyset(&set);
293     sigaddset(&set, SIGPIPE);
294     sigprocmask(SIG_BLOCK, &set, NULL);
295   }
296   
297   @try
298   {
299     retVal = UIApplicationMain(argc,argv,@"UIApplication",@"XBMCApplicationDelegate");
300     //UIApplicationMain(argc, argv, nil, nil);
301   } 
302   @catch (id theException) 
303   {
304     ELOG(@"%@", theException);
305   }
306   @finally 
307   {
308     ILOG(@"This always happens.");
309   }
310     
311   [pool release];
312         
313   return retVal;
314
315 }