1/*
2 *  Copyright (C) 2012-2018 Team Kodi
3 *  This file is part of Kodi - https://kodi.tv
4 *
5 *  SPDX-License-Identifier: GPL-2.0-or-later
6 *  See LICENSES/README.md for more information.
7 */
8
9#import "IOSExternalTouchController.h"
10
11#include "Util.h"
12#import "XBMCController.h"
13#include "filesystem/SpecialProtocol.h"
14#include "guilib/LocalizeStrings.h"
15#include "input/mouse/MouseStat.h"
16
17//dim the touchscreen after 15 secs without touch event
18const CGFloat touchScreenDimTimeoutSecs       = 15.0;
19const CGFloat timeFadeSecs                    = 2.0;
20
21@implementation IOSExternalTouchController
22//--------------------------------------------------------------
23- (id)init
24{
25  CGRect frame = [[UIScreen mainScreen] bounds];
26
27  self = [super init];
28  if (self)
29  {
30    UIImage       *xbmcLogo;
31    UIImageView   *xbmcLogoView;
32    UILabel       *descriptionLabel;
33
34    _internalWindow = [[UIWindow alloc] initWithFrame:frame];
35    _touchView = [[UIView alloc] initWithFrame:frame];
36    /* Turn on autoresizing for the whole hierarchy*/
37    [_touchView setAutoresizesSubviews:YES];
38    [_touchView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
39    [_touchView setAlpha:0.0];//start with alpha 0 and fade in with animation below
40    [_touchView setMultipleTouchEnabled:YES];
41    [_touchView setContentMode:UIViewContentModeCenter];
42
43    //load the splash image
44    std::string strUserSplash = CUtil::GetSplashPath();
45    xbmcLogo = [UIImage imageWithContentsOfFile:[NSString stringWithUTF8String:strUserSplash.c_str()]];
46
47    //make a view with the image
48    xbmcLogoView = [[UIImageView alloc] initWithImage:xbmcLogo];
49    //center the image and add it to the view
50    [xbmcLogoView setFrame:frame];
51    [xbmcLogoView setContentMode:UIViewContentModeScaleAspectFill];
52    //autoresize the image frame
53    [xbmcLogoView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
54    [xbmcLogoView setAutoresizesSubviews:YES];
55    [_touchView addSubview:xbmcLogoView];
56    //send the image to the background
57    [_touchView sendSubviewToBack:xbmcLogoView];
58
59    CGRect labelRect = frame;
60    labelRect.size.height/=2;
61    //uilabel with the description
62    descriptionLabel = [[UILabel alloc] initWithFrame:labelRect];
63    //gray textcolor on transparent background
64    [descriptionLabel setTextColor:[UIColor grayColor]];
65    [descriptionLabel setBackgroundColor:[UIColor clearColor]];
66    //text is aligned left in its frame
67    [descriptionLabel setTextAlignment:NSTextAlignmentCenter];
68    [descriptionLabel setContentMode:UIViewContentModeCenter];
69    //setup multiline behaviour
70    [descriptionLabel setLineBreakMode:(NSLineBreakMode)NSLineBreakByTruncatingTail];
71
72    [descriptionLabel setNumberOfLines:5];
73    std::string descText    = g_localizeStrings.Get(34404) + "\n";
74    descText              += g_localizeStrings.Get(34405) + "\n";
75    descText              += g_localizeStrings.Get(34406) + "\n";
76    descText              += g_localizeStrings.Get(34407) + "\n";
77    descText              += g_localizeStrings.Get(34408) + "\n";
78
79    NSString *stringFromUTFString = [[NSString alloc] initWithUTF8String:descText.c_str()];
80
81    [descriptionLabel setText:stringFromUTFString];
82
83    //resize it to full view
84    [descriptionLabel setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
85    [descriptionLabel setAutoresizesSubviews:YES];
86    [_touchView addSubview:descriptionLabel];
87
88    [[self view] addSubview: _touchView];
89
90    [self createGestureRecognizers];
91
92    [_internalWindow addSubview:[self view]];
93    [_internalWindow setBackgroundColor:[UIColor blackColor]];
94    [_internalWindow setScreen:[UIScreen mainScreen]];
95    [_internalWindow makeKeyAndVisible];
96    [_internalWindow setRootViewController:self];
97
98    [self startSleepTimer];//will fade from black too
99  }
100  return self;
101}
102//--------------------------------------------------------------
103- (UIInterfaceOrientationMask)supportedInterfaceOrientations
104{
105  return UIInterfaceOrientationMaskLandscape;
106}
107//--------------------------------------------------------------
108- (void)startSleepTimer
109{
110  NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:touchScreenDimTimeoutSecs];
111
112  //schedule sleep timer to fire once in touchScreenDimTimeoutSecs if not reset
113  _sleepTimer       = [[NSTimer alloc] initWithFireDate:fireDate
114                                      interval:1
115                                      target:self
116                                      selector:@selector(sleepTimerCallback:)
117                                      userInfo:nil
118                                      repeats:NO];
119  //schedule the timer to the runloop
120  NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
121  [self fadeFromBlack];
122  [runLoop addTimer:_sleepTimer forMode:NSDefaultRunLoopMode];
123}
124//--------------------------------------------------------------
125- (void)stopSleepTimer
126{
127  if(_sleepTimer != nil)
128  {
129    [_sleepTimer invalidate];
130    _sleepTimer = nil;
131  }
132}
133//--------------------------------------------------------------
134- (void)sleepTimerCallback:(NSTimer*)theTimer
135{
136  [self fadeToBlack];
137  [self stopSleepTimer];
138}
139//--------------------------------------------------------------
140- (bool)wakeUpFromSleep//returns false if we where dimmed, true if not
141{
142  if(_sleepTimer == nil)
143  {
144    [self startSleepTimer];
145    return false;
146  }
147  else
148  {
149    NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:touchScreenDimTimeoutSecs];
150    [_sleepTimer setFireDate:fireDate];
151    return true;
152  }
153}
154//--------------------------------------------------------------
155- (void)fadeToBlack
156{
157    [UIView animateWithDuration:timeFadeSecs delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
158    [_touchView setAlpha:0.01];//fade to black (don't fade to 0.0 else we don't get gesture callbacks)
159    }
160    completion:^(BOOL finished){}];
161}
162//--------------------------------------------------------------
163- (void)fadeFromBlack
164{
165    [UIView animateWithDuration:timeFadeSecs delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
166    [_touchView setAlpha:1.0];//fade in to full alpha
167    }
168    completion:^(BOOL finished){}];
169}
170//--------------------------------------------------------------
171- (void)createGestureRecognizers
172{
173  //2 finger single tab - right mouse
174  //single finger double tab delays single finger single tab - so we
175  //go for 2 fingers here - so single finger single tap is instant
176  UITapGestureRecognizer *doubleFingerSingleTap = [[UITapGestureRecognizer alloc]
177                                                    initWithTarget:self action:@selector(handleDoubleFingerSingleTap:)];
178  [doubleFingerSingleTap setNumberOfTapsRequired:1];
179  [doubleFingerSingleTap setNumberOfTouchesRequired:2];
180  [[self view] addGestureRecognizer:doubleFingerSingleTap];
181
182  //1 finger single long tab - right mouse - alternative
183  UITapGestureRecognizer *singleFingerSingleLongTap = (UITapGestureRecognizer*)[[UILongPressGestureRecognizer alloc]
184                                                        initWithTarget:self action:@selector(handleSingleFingerSingleLongTap:)];
185  singleFingerSingleLongTap.delaysTouchesBegan = YES;
186  singleFingerSingleLongTap.delaysTouchesEnded = YES;
187  singleFingerSingleLongTap.numberOfTouchesRequired = 1;
188  [self.view addGestureRecognizer:singleFingerSingleLongTap];
189
190  //1 finger single tab - left mouse
191  UITapGestureRecognizer *singleFingerSingleTap = [[UITapGestureRecognizer alloc]
192                                                    initWithTarget:self action:@selector(handleSingleFingerSingleTap:)];
193  [singleFingerSingleTap setDelaysTouchesBegan:NO];
194  [[self view] addGestureRecognizer:singleFingerSingleTap];
195
196  //double finger swipe left for backspace ... i like this fast backspace feature ;)
197  UISwipeGestureRecognizer *swipeDoubleLeft = [[UISwipeGestureRecognizer alloc]
198                                          initWithTarget:self action:@selector(handleDoubleSwipeLeft:)];
199  [swipeDoubleLeft setNumberOfTouchesRequired:2];
200  [swipeDoubleLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
201  [[self view] addGestureRecognizer:swipeDoubleLeft];
202
203  //single finger swipe left for left
204  UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc]
205                                      initWithTarget:self action:@selector(handleSwipeLeft:)];
206  [swipeLeft setNumberOfTouchesRequired:1];
207  [swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
208  [[self view] addGestureRecognizer:swipeLeft];
209
210  //single finger swipe right for right
211  UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc]
212                                      initWithTarget:self action:@selector(handleSwipeRight:)];
213  [swipeRight setNumberOfTouchesRequired:1];
214  [swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
215  [[self view] addGestureRecognizer:swipeRight];
216
217  //single finger swipe up for up
218  UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc]
219                                      initWithTarget:self action:@selector(handleSwipeUp:)];
220  [swipeUp setNumberOfTouchesRequired:1];
221  [swipeUp setDirection:UISwipeGestureRecognizerDirectionUp];
222  [[self view] addGestureRecognizer:swipeUp];
223
224  //single finger swipe down for down
225  UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc]
226                                      initWithTarget:self action:@selector(handleSwipeDown:)];
227  [swipeDown setNumberOfTouchesRequired:1];
228  [swipeDown setDirection:UISwipeGestureRecognizerDirectionDown];
229  [[self view] addGestureRecognizer:swipeDown];
230
231}
232//--------------------------------------------------------------
233- (IBAction)handleDoubleSwipeLeft:(UISwipeGestureRecognizer *)sender
234{
235  if([self wakeUpFromSleep])
236  {
237    [g_xbmcController sendKey:XBMCK_BACKSPACE];
238  }
239}
240//--------------------------------------------------------------
241- (IBAction)handleSwipeLeft:(UISwipeGestureRecognizer *)sender
242{
243  if([self wakeUpFromSleep])
244  {
245    [g_xbmcController sendKey:XBMCK_LEFT];
246  }
247}
248//--------------------------------------------------------------
249- (IBAction)handleSwipeRight:(UISwipeGestureRecognizer *)sender
250{
251  if([self wakeUpFromSleep])
252  {
253    [g_xbmcController sendKey:XBMCK_RIGHT];
254  }
255}
256//--------------------------------------------------------------
257- (IBAction)handleSwipeUp:(UISwipeGestureRecognizer *)sender
258{
259  if([self wakeUpFromSleep])
260  {
261    [g_xbmcController sendKey:XBMCK_UP];
262  }
263}
264//--------------------------------------------------------------
265- (IBAction)handleSwipeDown:(UISwipeGestureRecognizer *)sender
266{
267  if([self wakeUpFromSleep])
268  {
269    [g_xbmcController sendKey:XBMCK_DOWN];
270  }
271}
272//--------------------------------------------------------------
273- (IBAction)handleDoubleFingerSingleTap:(UIGestureRecognizer *)sender
274{
275  if([self wakeUpFromSleep])
276  {
277    [g_xbmcController sendKey:XBMCK_c];
278  }
279}
280//--------------------------------------------------------------
281- (IBAction)handleSingleFingerSingleLongTap:(UIGestureRecognizer *)sender
282{
283  if([self wakeUpFromSleep])
284  {
285    if (sender.state == UIGestureRecognizerStateEnded)
286    {
287      [self handleDoubleFingerSingleTap:sender];
288    }
289  }
290}
291//--------------------------------------------------------------
292- (IBAction)handleSingleFingerSingleTap:(UIGestureRecognizer *)sender
293{
294  if([self wakeUpFromSleep])
295  {
296    [g_xbmcController sendKey:XBMCK_RETURN];
297  }
298}
299//--------------------------------------------------------------
300- (void)viewWillAppear:(BOOL)animated
301{
302  [super viewWillAppear:animated];
303}
304//--------------------------------------------------------------
305- (void)dealloc
306{
307  [self stopSleepTimer];
308}
309//--------------------------------------------------------------
310@end
311