Tizen 2.1 base
[framework/osp/uifw.git] / src / ui / FUi_TouchFlickGestureDetector.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Flora License, Version 1.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://floralicense.org/license/
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an AS IS BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 /**
18  * @file                FUi_TouchFlickGestureDetector.cpp
19  * @brief               This is the implementation file for %_TouchFlickGestureDetector class
20  * @version     2.0
21  *
22  * This file contains the implementation of %_TouchFlickGestureDetector class.
23  *
24  */
25
26 #include <FBaseUtilMath.h>
27 #include <FBaseColIEnumeratorT.h>
28 #include <FUiITouchFlickGestureEventListener.h>
29 #include "FUi_TouchManager.h"
30 #include "FUi_TouchFlickGestureDetector.h"
31 #include "FUi_ITouchFlickGestureEventListener.h"
32 #include "FUi_TouchFlickGestureDetectorImpl.h"
33
34 using namespace Tizen::Base::Utility;
35 using namespace Tizen::System;
36 using namespace Tizen::Base::Collection;
37
38 namespace Tizen { namespace Ui
39 {
40
41 namespace{
42 const int MAX_VALUE = -10000;
43 const int MIN_VALUE = 10000;
44 const int IGNORE_OVERSHOOT_TIME = 80;
45 const int IGNORE_OVERSHOOT_MM = 5;
46 const int DIRECT_MOVE_TIME = 80;;
47 const int DIRECT_MOVE_MM = 3;
48 const int MAX_TIME_WND = 200;
49 const int MIN_TIME_WND = 30;
50 const int FIRST_MOVE_BOUND_MM = 2;
51 const int MM_H = 72;
52 const int RES_H = 800;
53 const int IGNORE_OVERSHOOT_PXL = IGNORE_OVERSHOOT_MM * RES_H / MM_H;
54 const int DIRECT_MOVE_PXL = DIRECT_MOVE_MM * RES_H / MM_H;
55 const int MIN_MOVE_PXL = FIRST_MOVE_BOUND_MM * RES_H / MM_H;
56 const unsigned int FIRST_POINT_ID = 0;
57 }
58
59 /* lcd size 480x800 DEVICE_SIZE_VERTICAL        72
60  * lcd size 240x400 DEVICE_SIZE_VERTICAL                66
61  * lcd size 320x480 DEVICE_SIZE_VERTICAL                66
62  *
63  * RES_H = lcd size(height)
64  * */
65 _TouchFlickGestureDetector::_TouchFlickGestureDetector(void)
66         : __xDistance(0)
67         , __yDistance(0)
68         , __duration(0)
69         , __direction(_FLICK_DIRECTION_NONE)
70         , __maxMoveX(MAX_VALUE)
71         , __minMoveX(MIN_VALUE)
72         , __maxMoveY(MAX_VALUE)
73         , __minMoveY(MIN_VALUE)
74         , __pFlickInfoList(null)
75 {
76         result r = GetLastResult();
77         SysTryReturnVoidResult(NID_UI, r == E_SUCCESS, r, GetErrorMessage(r));
78
79         SetDetectorType(_TOUCH_GESTURE_DETECTOR_TYPE_FLICK);
80
81         __pFlickInfoList = new (std::nothrow) ArrayListT<_FlickInfo*>;
82         SysTryReturnVoidResult(NID_UI, __pFlickInfoList, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Memory allocation failed.");
83
84         return;
85 }
86
87 _TouchFlickGestureDetector::~_TouchFlickGestureDetector(void)
88 {
89         ClearVariables();
90
91         delete __pFlickInfoList;
92         __pFlickInfoList = null;
93 }
94
95 void
96 _TouchFlickGestureDetector::CheckMoveMinMax(const int x, const int y)
97 {
98         if (x > __maxMoveX)
99         {
100                 __maxMoveX = x;
101         }
102
103         if (x < __minMoveX)
104         {
105                 __minMoveX = x;
106         }
107
108         if (y > __maxMoveY)
109         {
110                 __maxMoveY = y;
111         }
112
113         if (y < __minMoveY)
114         {
115                 __minMoveY = y;
116         }
117 }
118
119 void
120 _TouchFlickGestureDetector::ClearVariables(void)
121 {
122         __maxMoveX = -10000;
123         __minMoveX = 10000;
124         __maxMoveY = -10000;
125         __minMoveY = 10000;
126
127         for (int i = 0; i < __pFlickInfoList->GetCount(); i++)
128         {
129                 _FlickInfo* pFlickInfo = null;
130                 __pFlickInfoList->GetAt(i, pFlickInfo);
131
132                 if (pFlickInfo)
133                 {
134                         delete pFlickInfo;
135                 }
136         }
137
138         __pFlickInfoList->RemoveAll();
139 }
140
141 bool
142 _TouchFlickGestureDetector::FindFlickWindow(const ArrayListT<_FlickInfo*>*  pList, const _FlickInfo* pFlickInfo, int* pStartIndex, int* pEndIndex)
143 {
144         int pointCount = pList->GetCount();
145         int endIdx = pointCount - 1;
146
147         enum
148         {
149                 FD_NONE = 0, FD_RIGHT = 0x1, FD_UP = 0x2, FD_LEFT = 0x4, FD_DOWN = 0x8
150         };
151
152         int dirOpp = FD_NONE;
153         int dir = FD_NONE;
154         int deltaX = 0, deltaY = 0;
155
156         int i = 0;
157         for (i = endIdx - 1; i > 0; i--)
158         {
159                 bool isDirChanged = false;
160                 int distanceX = 0;
161                 int distanceY = 0;
162                 _FlickInfo* pVecPoints = null;
163                 _FlickInfo* pPrevVecPoints = null;
164
165                 pList->GetAt(i, pVecPoints);
166                 pList->GetAt(i - 1, pPrevVecPoints);
167
168                 if (pVecPoints && pPrevVecPoints)
169                 {
170                         distanceX = pVecPoints->x - pPrevVecPoints->x;
171                         distanceY = pVecPoints->y - pPrevVecPoints->y;
172                 }
173
174                 if (distanceX == 0 && distanceY == 0)
175                 {
176                         continue;
177                 }
178                 else if (distanceX > 0 && distanceX > Math::Abs(distanceY))   // Right
179                 {
180                         dirOpp |= FD_LEFT;
181
182                         if (dirOpp & FD_RIGHT)
183                         {
184                                 isDirChanged = true;
185                         }
186                         else
187                         {
188                                 dir = FD_RIGHT;
189                         }
190                 }
191                 else if (distanceX < 0 && distanceX < -Math::Abs(distanceY))  // Left
192                 {
193                         dirOpp |= FD_RIGHT;
194
195                         if (dirOpp & FD_LEFT)
196                         {
197                                 isDirChanged = true;
198                         }
199                         else
200                         {
201                                 dir = FD_LEFT;
202                         }
203                 }
204                 else if (distanceY > 0 && distanceY >= Math::Abs(distanceX))  // Down
205                 {
206                         dirOpp |= FD_UP;
207
208                         if (dirOpp & FD_DOWN)
209                         {
210                                 isDirChanged = true;
211                         }
212                         else
213                         {
214                                 dir = FD_DOWN;
215                         }
216                 }
217                 else if (distanceY < 0 && distanceY <= -Math::Abs(distanceX)) // Up
218                 {
219                         dirOpp |= FD_DOWN;
220
221                         if (dirOpp & FD_UP)
222                         {
223                                 isDirChanged = true;
224                         }
225                         else
226                         {
227                                 dir = FD_UP;
228                         }
229                 }
230
231                 if (isDirChanged)
232                 {
233                         if (pFlickInfo->time - pVecPoints->time < IGNORE_OVERSHOOT_TIME
234                            && Math::Abs(deltaX) < IGNORE_OVERSHOOT_PXL && Math::Abs(deltaY) < IGNORE_OVERSHOOT_PXL)
235                         {
236                                 endIdx = i;
237                                 dirOpp = FD_NONE;
238                                 dir = FD_NONE;
239                                 deltaX = distanceX;
240                                 deltaY = distanceY;
241                                 continue;
242                         }
243                         break;
244                 }
245                 else
246                 {
247                         deltaX += distanceX;
248                         deltaY += distanceY;
249                 }
250
251                 if (pFlickInfo->time - pPrevVecPoints->time > DIRECT_MOVE_TIME
252                    && Math::Abs(deltaX) < DIRECT_MOVE_PXL && Math::Abs(deltaY) < DIRECT_MOVE_PXL)
253                 {
254                         --i;
255                         *pStartIndex = i;
256                         *pEndIndex = endIdx;
257                         return false;
258                 }
259
260                 if (pFlickInfo->time - pVecPoints->time > MAX_TIME_WND)
261                 {
262                         break;
263                 }
264         }
265
266         *pStartIndex = i;
267         *pEndIndex = endIdx;
268
269         switch(dir)
270         {
271         case FD_NONE:
272                 __direction = _FLICK_DIRECTION_NONE;
273                 break;
274
275         case FD_RIGHT:
276                 __direction = _FLICK_DIRECTION_RIGHT;
277                 break;
278
279         case FD_LEFT:
280                 __direction = _FLICK_DIRECTION_LEFT;
281                 break;
282
283         case FD_UP:
284                 __direction = _FLICK_DIRECTION_UP;
285                 break;
286
287         case FD_DOWN:
288                 __direction = _FLICK_DIRECTION_DOWN;
289                 break;
290
291         default:
292                 break;
293         }
294         return true;
295 }
296
297 bool
298 _TouchFlickGestureDetector::CalculateFlick(const ArrayListT<_FlickInfo*>*  pList, const _FlickInfo* pFlickInfo, _FlickInfo* pReturnFlick)
299 {
300         enum
301         {
302                 ITEM_EXECUTION, DIRECT_MOVE, FLICK_START
303         }
304
305         flickStatus = FLICK_START;
306
307         if ((__maxMoveX - __minMoveX) < MIN_MOVE_PXL
308            && (__maxMoveY - __minMoveY) < MIN_MOVE_PXL)
309         {
310                 flickStatus = ITEM_EXECUTION;
311
312                 pReturnFlick->x = pReturnFlick->y = 0;
313                 pReturnFlick->time = MIN_TIME_WND;
314                 ClearVariables();
315                 return (flickStatus != ITEM_EXECUTION);
316         }
317
318         int startIdx = 0;
319         int endIdx = 0;
320         int endPosX = 0;
321         int endPosY = 0;
322         int endTime = 0;
323         int startPosX = 0;
324         int startPosY = 0;
325         int startTime = 0;
326         int distanceX = 0;
327         int distanceY = 0;
328         int time = 0;
329
330         if (!FindFlickWindow(pList, pFlickInfo, &startIdx, &endIdx))
331         {
332                 flickStatus = DIRECT_MOVE;
333         }
334
335         _FlickInfo* pEndVecPoint = null;
336         _FlickInfo* pStartVecPoint = null;
337
338         pList->GetAt(endIdx, pEndVecPoint);
339         pList->GetAt(startIdx, pStartVecPoint);
340
341         if (pEndVecPoint)
342         {
343                 endPosX = pEndVecPoint->x;
344                 endPosY = pEndVecPoint->y;
345         }
346         endTime = pFlickInfo->time;
347
348         if (pStartVecPoint)
349         {
350                 startPosX = pStartVecPoint->x;
351                 startPosY = pStartVecPoint->y;
352                 startTime = pStartVecPoint->time;
353         }
354
355         distanceX = endPosX - startPosX;
356         distanceY = endPosY - startPosY;
357         time = endTime - startTime;
358
359         if (time < MIN_TIME_WND)
360         {
361                 time = MIN_TIME_WND;
362         }
363
364         pReturnFlick->x = distanceX;
365         pReturnFlick->y = distanceY;
366         pReturnFlick->time = time;
367
368         if (flickStatus == DIRECT_MOVE)
369         {
370                 pReturnFlick->x = pReturnFlick->y = 0;
371         }
372
373         ClearVariables();
374         return true;
375 }
376
377 result
378 _TouchFlickGestureDetector::OnTouchDown(ArrayListT<_FlickInfo*>*  pList, const _FlickInfo* pFlickInfo)
379 {
380         result r = pList->Add(const_cast<_FlickInfo*>(pFlickInfo));
381         SysTryReturnResult(NID_UI, r == E_SUCCESS, r, "%s", GetErrorMessage(r));
382
383         CheckMoveMinMax(pFlickInfo->x, pFlickInfo->y);
384
385         return E_SUCCESS;
386 }
387
388 result
389 _TouchFlickGestureDetector::OnTouchMove(ArrayListT<_FlickInfo*>*  pList, const _FlickInfo* pFlickInfo)
390 {
391         result r = pList->Add(const_cast<_FlickInfo*>(pFlickInfo));
392         SysTryReturnResult(NID_UI, r == E_SUCCESS, r, "%s", GetErrorMessage(r));
393
394         CheckMoveMinMax(pFlickInfo->x, pFlickInfo->y);
395
396         return E_SUCCESS;
397 }
398
399 result
400 _TouchFlickGestureDetector::OnTouchUp(ArrayListT<_FlickInfo*>*  pList, const _FlickInfo* pFlickInfo, _FlickInfo* pReturnFlick, bool& isFlicked)
401 {
402         result r = pList->Add(const_cast<_FlickInfo*>(pFlickInfo));
403         SysTryReturnResult(NID_UI, r == E_SUCCESS, r, "%s", GetErrorMessage(r));
404
405         CheckMoveMinMax(pFlickInfo->x, pFlickInfo->y);
406
407         _FlickInfo* pTempVecPoint = null;
408         bool flickEnable = false;
409
410         int count = pList->GetCount();
411         for(int i=0; i < count; i++)
412         {
413                 pList->GetAt(i, pTempVecPoint);
414                 if(pTempVecPoint->touchStatus == _TOUCH_MOVED)
415                 {
416                         flickEnable = true;
417                         break;
418                 }
419         }
420
421         isFlicked = flickEnable?(CalculateFlick(pList, pFlickInfo, pReturnFlick)):false;
422
423         return E_SUCCESS;
424 }
425
426 bool
427 _TouchFlickGestureDetector::OnTouchPressed(const _Control& source, const _TouchInfo& touchinfo)
428 {
429         if (touchinfo.GetPointId() != FIRST_POINT_ID)
430         {
431                 return false;
432         }
433
434         _FlickInfo* pFlickInfo = new (std::nothrow) _FlickInfo;
435         SysTryReturn(NID_UI, pFlickInfo, false, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Memory allocation failed.");
436
437         pFlickInfo->time = touchinfo.GetTimeStamp();
438         pFlickInfo->x = touchinfo.GetCurrentPosition().x;
439         pFlickInfo->y = touchinfo.GetCurrentPosition().y;
440         pFlickInfo->touchStatus = touchinfo.GetTouchStatus();
441
442         __xDistance = 0;
443         __yDistance = 0;
444         __duration = 0;
445         __direction = _FLICK_DIRECTION_NONE;
446
447         SetDetectorState(_TOUCH_GESTURE_DETECTOR_STATE_READY);
448         SetGestureStart(true);
449
450         result r = OnTouchDown(__pFlickInfoList, pFlickInfo);
451         if (r != E_SUCCESS)
452         {
453                 delete pFlickInfo;
454         }
455
456         return false;
457 }
458
459 bool
460 _TouchFlickGestureDetector::OnTouchMoved(const _Control& source, const _TouchInfo& touchinfo)
461 {
462         if (touchinfo.GetPointId() != FIRST_POINT_ID)
463         {
464                 return false;
465         }
466
467         _FlickInfo* pFlickInfo = new (std::nothrow) _FlickInfo;
468         SysTryReturn(NID_UI, pFlickInfo, false, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Memory allocation failed.");
469
470         pFlickInfo->time = touchinfo.GetTimeStamp();
471         pFlickInfo->x = touchinfo.GetCurrentPosition().x;
472         pFlickInfo->y = touchinfo.GetCurrentPosition().y;
473         pFlickInfo->touchStatus = touchinfo.GetTouchStatus();
474
475         result r = OnTouchMove(__pFlickInfoList, pFlickInfo);
476         if (r != E_SUCCESS)
477         {
478                 delete pFlickInfo;
479         }
480
481         return false;
482 }
483
484 bool
485 _TouchFlickGestureDetector::OnTouchReleased(const _Control& source, const _TouchInfo& touchinfo)
486 {
487         if (touchinfo.GetPointId() != FIRST_POINT_ID)
488         {
489                 return false;
490         }
491
492         _TouchManager* pTouchManager = _TouchManager::GetInstance();
493         SysTryReturn(NID_UI, pTouchManager, false, E_SYSTEM, "[E_SYSTEM] _TouchManager does not exist.");
494
495         _FlickInfo returnedFlickInfo = {0, };
496
497         _FlickInfo* pFlickInfo = new (std::nothrow) _FlickInfo;
498         SysTryReturn(NID_UI, pFlickInfo, false, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] Memory allocation failed.");
499
500         pFlickInfo->time = touchinfo.GetTimeStamp();
501         pFlickInfo->x = touchinfo.GetCurrentPosition().x;
502         pFlickInfo->y = touchinfo.GetCurrentPosition().y;
503         pFlickInfo->touchStatus = touchinfo.GetTouchStatus();
504
505         bool isFlicked = false;
506
507         if (pTouchManager->GetPointCount() > 1) //supports only one finger count
508         {
509                 delete pFlickInfo;
510                 ClearVariables();
511         }
512         else
513         {
514                 result r = OnTouchUp(__pFlickInfoList, pFlickInfo, &returnedFlickInfo, isFlicked);
515                 if (r != E_SUCCESS)
516                 {
517                         delete pFlickInfo;
518                 }
519         }
520
521         SetGestureStart(false);
522         if (isFlicked && (returnedFlickInfo.x != 0 || returnedFlickInfo.y != 0))
523         {
524                 __xDistance = returnedFlickInfo.x;
525                 __yDistance = returnedFlickInfo.y;
526                 __duration = returnedFlickInfo.time;
527
528                 SetDetectorState(_TOUCH_GESTURE_DETECTOR_STATE_SUCCESS);
529         }
530         else
531         {
532                 __xDistance = 0;
533                 __yDistance = 0;
534                 __duration = 0;
535
536                 SetDetectorState(_TOUCH_GESTURE_DETECTOR_STATE_FAILED);
537         }
538
539         return false;
540 }
541
542 bool
543 _TouchFlickGestureDetector::OnTouchCanceled(const _Control& source, const _TouchInfo& touchinfo)
544 {
545         ClearVariables();
546
547         return false;
548 }
549
550 void
551 _TouchFlickGestureDetector::GetDistance(int& x, int &y) const
552 {
553         x = __xDistance;
554         y = __yDistance;
555 }
556
557 int
558 _TouchFlickGestureDetector::GetDuration(void) const
559 {
560         return __duration;
561 }
562
563 _FlickDirection
564 _TouchFlickGestureDetector::GetDirection(void) const
565 {
566         return __direction;
567 }
568
569 }} // Tizen::Ui