Use existing callback ID for recurring callbacks
[platform/core/uifw/dali-adaptor.git] / dali / internal / adaptor / android / framework-android.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/adaptor/common/framework.h>
20
21 // EXTERNAL INCLUDES
22 #include <unistd.h>
23 #include <queue>
24 #include <unordered_set>
25 #include <android_native_app_glue.h>
26
27 #include <dali/integration-api/debug.h>
28 #include <dali/integration-api/adaptor-framework/adaptor.h>
29 #include <dali/integration-api/adaptor-framework/android/android-framework.h>
30 #include <dali/devel-api/events/touch-point.h>
31 #include <dali/public-api/events/key-event.h>
32 #include <dali/devel-api/events/key-event-devel.h>
33
34 // INTERNAL INCLUDES
35 #include <dali/internal/system/common/callback-manager.h>
36 #include <dali/internal/adaptor/android/android-framework-impl.h>
37
38 namespace Dali
39 {
40
41 namespace Internal
42 {
43
44 namespace Adaptor
45 {
46
47 namespace
48 {
49
50 // Copied from x server
51 static unsigned int GetCurrentMilliSeconds(void)
52 {
53   struct timeval tv;
54
55   struct timespec tp;
56   static clockid_t clockid;
57
58   if (!clockid)
59   {
60 #ifdef CLOCK_MONOTONIC_COARSE
61     if (clock_getres(CLOCK_MONOTONIC_COARSE, &tp) == 0 &&
62       (tp.tv_nsec / 1000) <= 1000 && clock_gettime(CLOCK_MONOTONIC_COARSE, &tp) == 0)
63     {
64       clockid = CLOCK_MONOTONIC_COARSE;
65     }
66     else
67 #endif
68     if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
69     {
70       clockid = CLOCK_MONOTONIC;
71     }
72     else
73     {
74       clockid = ~0L;
75     }
76   }
77   if (clockid != ~0L && clock_gettime(clockid, &tp) == 0)
78   {
79     return (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000L);
80   }
81
82   gettimeofday(&tv, NULL);
83   return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
84 }
85
86 } // Unnamed namespace
87
88 /**
89  * Impl to hide android data members
90  */
91 struct Framework::Impl
92 {
93
94   struct IdleCallback
95   {
96     int timestamp;
97     int timeout;
98     int id;
99     void* data;
100     bool ( *callback )( void *data );
101
102     IdleCallback( int timeout, int id, void* data, bool ( *callback )( void *data ) )
103     : timestamp( GetCurrentMilliSeconds() + timeout ),
104       timeout( timeout ),
105       id ( id ),
106       data( data ),
107       callback( callback )
108     {
109     }
110
111     bool operator()()
112     {
113       return callback( data );
114     }
115
116     bool operator<( const IdleCallback& rhs ) const
117     {
118       return timestamp > rhs.timestamp;
119     }
120   };
121
122   // Constructor
123
124   Impl( Framework* framework )
125   : mAbortCallBack( nullptr ),
126     mCallbackManager( CallbackManager::New() ),
127     mLanguage( "NOT_SUPPORTED" ),
128     mRegion( "NOT_SUPPORTED" ),
129     mFinishRequested( false ),
130     mIdleId( 0 ),
131     mIdleReadPipe( -1 ),
132     mIdleWritePipe( -1 )
133
134
135   {
136     AndroidFramework::GetImplementation( AndroidFramework::Get() ).SetFramework( framework );
137   }
138
139   ~Impl()
140   {
141     AndroidFramework::GetImplementation( AndroidFramework::Get() ).SetFramework( nullptr );
142
143     delete mAbortCallBack;
144     mAbortCallBack = nullptr;
145
146     // we're quiting the main loop so
147     // mCallbackManager->RemoveAllCallBacks() does not need to be called
148     // to delete our abort handler
149     delete mCallbackManager;
150     mCallbackManager = nullptr;
151   }
152
153   std::string GetLanguage() const
154   {
155     return mLanguage;
156   }
157
158   std::string GetRegion() const
159   {
160     return mRegion;
161   }
162
163   void OnIdle()
164   {
165     // Dequeue the pipe
166     int8_t msg = -1;
167     read( mIdleReadPipe, &msg, sizeof( msg ) );
168
169     unsigned int ts = GetCurrentMilliSeconds();
170
171     if ( !mIdleCallbacks.empty() )
172     {
173       IdleCallback callback = mIdleCallbacks.top();
174       if( callback.timestamp <= ts )
175       {
176         mIdleCallbacks.pop();
177
178         // Callback wasn't removed
179         if( mRemovedIdleCallbacks.find( callback.id ) == mRemovedIdleCallbacks.end() )
180         {
181           if ( callback() ) // keep the callback
182           {
183             AddIdle( callback.timeout, callback.data, callback.callback, callback.id );
184           }
185         }
186
187         // Callback cane be also removed during the callback call
188         auto i = mRemovedIdleCallbacks.find( callback.id );
189         if( i != mRemovedIdleCallbacks.end() )
190         {
191           mRemovedIdleCallbacks.erase( i );
192         }
193       }
194     }
195
196     if( mIdleCallbacks.empty() )
197     {
198       mRemovedIdleCallbacks.clear();
199     }
200   }
201
202   unsigned int AddIdle( int timeout, void* data, bool ( *callback )( void *data ) , unsigned int existingId = 0 )
203   {
204     unsigned int chosenId;
205     if (existingId)
206     {
207       chosenId = existingId;
208     }
209     else
210     {
211       ++mIdleId;
212       if( mIdleId == 0 )
213       {
214         ++mIdleId;
215       }
216       chosenId = mIdleId;
217     }
218
219     mIdleCallbacks.push( IdleCallback( timeout, chosenId, data, callback ) );
220
221     // To wake up the idle pipe and to trigger OnIdle
222     int8_t msg = 1;
223     write( mIdleWritePipe, &msg, sizeof( msg ) );
224
225     return chosenId;
226   }
227
228   void RemoveIdle( unsigned int id )
229   {
230     if( id != 0 )
231     {
232       mRemovedIdleCallbacks.insert( id );
233     }
234   }
235
236   int GetIdleTimeout()
237   {
238     int timeout = -1;
239
240     if( !mIdleCallbacks.empty() )
241     {
242       IdleCallback idleTimeout = mIdleCallbacks.top();
243       timeout = idleTimeout.timestamp - GetCurrentMilliSeconds();
244       if( timeout < 0 )
245       {
246          timeout = 0;
247       }
248     }
249
250     return timeout;
251   }
252
253   // Data
254   CallbackBase* mAbortCallBack;
255   CallbackManager* mCallbackManager;
256   std::string mLanguage;
257   std::string mRegion;
258   bool mFinishRequested;
259
260   int mIdleReadPipe;
261   int mIdleWritePipe;
262   unsigned int mIdleId;
263   std::priority_queue<IdleCallback> mIdleCallbacks;
264   std::unordered_set<int> mRemovedIdleCallbacks;
265
266   // Static methods
267
268   /**
269    * Called by the native activity loop when the application APP_CMD_INIT_WINDOW event is processed.
270    */
271   static void NativeWindowCreated( Framework* framework, ANativeWindow* window )
272   {
273     if( framework )
274     {
275       framework->AppStatusHandler( APP_WINDOW_CREATED, window );
276     }
277   }
278
279   /**
280    * Called by the native activity loop when the application APP_CMD_DESTROY event is processed.
281    */
282   static void NativeWindowDestroyed( Framework* framework, ANativeWindow* window )
283   {
284     if( framework )
285     {
286       framework->AppStatusHandler( APP_WINDOW_DESTROYED, window );
287     }
288   }
289
290   /**
291    * Called by the native activity loop when the application APP_CMD_INIT_WINDOW event is processed.
292    */
293   static void NativeAppPaused( Framework* framework )
294   {
295     if( framework )
296     {
297       framework->AppStatusHandler( APP_PAUSE, nullptr );
298     }
299   }
300
301   /**
302    * Called by the native activity loop when the application APP_CMD_TERM_WINDOW event is processed.
303    */
304   static void NativeAppResumed( Framework* framework )
305   {
306     if( framework )
307     {
308       framework->AppStatusHandler( APP_RESUME, nullptr );
309     }
310   }
311
312   /**
313    * Called by the native activity loop when the application input touch event is processed.
314    */
315   static void NativeAppTouchEvent( Framework* framework, Dali::TouchPoint& touchPoint, int64_t timeStamp )
316   {
317     Dali::Adaptor::Get().FeedTouchPoint( touchPoint, timeStamp );
318   }
319
320   /**
321    * Called by the native activity loop when the application input key event is processed.
322    */
323   static void NativeAppKeyEvent( Framework* framework, Dali::KeyEvent& keyEvent )
324   {
325     Dali::Adaptor::Get().FeedKeyEvent( keyEvent );
326   }
327
328   /**
329    * Called by the native activity loop when the application APP_CMD_DESTROY event is processed.
330    */
331   static void NativeAppDestroyed( Framework* framework )
332   {
333     if( framework )
334     {
335       framework->AppStatusHandler( APP_DESTROYED, nullptr );
336     }
337   }
338
339 /*
340   Order of events:
341
342   APP_CMD_START
343   APP_CMD_RESUME
344   APP_CMD_INIT_WINDOW
345   APP_CMD_GAINED_FOCUS
346
347   APP_CMD_PAUSE
348   APP_CMD_LOST_FOCUS
349   APP_CMD_SAVE_STATE
350   APP_CMD_STOP
351   APP_CMD_TERM_WINDOW
352 */
353
354   static void HandleAppCmd(struct android_app* app, int32_t cmd)
355   {
356     Framework* framework = AndroidFramework::GetImplementation( AndroidFramework::Get() ).GetFramework();
357     switch( cmd )
358     {
359       case APP_CMD_SAVE_STATE:
360         break;
361       case APP_CMD_START:
362         break;
363       case APP_CMD_STOP:
364         break;
365       case APP_CMD_RESUME:
366         break;
367       case APP_CMD_PAUSE:
368         break;
369       case APP_CMD_INIT_WINDOW:
370         // The window is being shown, get it ready.
371         AndroidFramework::Get().SetApplicationWindow( app->window );
372         Dali::Internal::Adaptor::Framework::Impl::NativeWindowCreated( framework, app->window );
373         Dali::Internal::Adaptor::Framework::Impl::NativeAppResumed( framework );
374         break;
375       case APP_CMD_TERM_WINDOW:
376         // The window is being hidden or closed, clean it up.
377         AndroidFramework::Get().SetApplicationWindow( nullptr );
378         Dali::Internal::Adaptor::Framework::Impl::NativeAppPaused( framework );
379         Dali::Internal::Adaptor::Framework::Impl::NativeWindowDestroyed( framework, app->window );
380         break;
381       case APP_CMD_GAINED_FOCUS:
382         break;
383       case APP_CMD_LOST_FOCUS:
384         break;
385       case APP_CMD_DESTROY:
386         Dali::Internal::Adaptor::Framework::Impl::NativeAppPaused( framework );
387         Dali::Internal::Adaptor::Framework::Impl::NativeAppDestroyed( framework );
388         break;
389     }
390   }
391
392   static int32_t HandleAppInput(struct android_app* app, AInputEvent* event)
393   {
394     Framework* framework = AndroidFramework::GetImplementation( AndroidFramework::Get() ).GetFramework();
395
396     if( AInputEvent_getType( event ) == AINPUT_EVENT_TYPE_MOTION )
397     {
398       int32_t deviceId = AInputEvent_getDeviceId( event );
399       float x = AMotionEvent_getX( event, 0 );
400       float y = AMotionEvent_getY( event, 0 );
401       Dali::PointState::Type state = Dali::PointState::DOWN;
402       int32_t action = AMotionEvent_getAction( event );
403       int64_t timeStamp = AMotionEvent_getEventTime( event );
404
405       switch ( action & AMOTION_EVENT_ACTION_MASK )
406       {
407       case AMOTION_EVENT_ACTION_DOWN:
408         break;
409       case AMOTION_EVENT_ACTION_UP:
410         state = Dali::PointState::UP;
411         break;
412       case AMOTION_EVENT_ACTION_MOVE:
413         state = Dali::PointState::MOTION;
414         break;
415       case AMOTION_EVENT_ACTION_CANCEL:
416         state = Dali::PointState::INTERRUPTED;
417         break;
418       case AMOTION_EVENT_ACTION_OUTSIDE:
419         state = Dali::PointState::LEAVE;
420         break;
421       }
422
423       Dali::TouchPoint point( deviceId, state, x, y );
424       Dali::Internal::Adaptor::Framework::Impl::NativeAppTouchEvent( framework, point, timeStamp );
425       return 1;
426     }
427     else if ( AInputEvent_getType( event ) == AINPUT_EVENT_TYPE_KEY )
428     {
429       int32_t deviceId = AInputEvent_getDeviceId( event );
430       int32_t keyCode = AKeyEvent_getKeyCode( event );
431       int32_t action = AKeyEvent_getAction( event );
432       int64_t timeStamp = AKeyEvent_getEventTime( event );
433
434       Dali::KeyEvent::State state = Dali::KeyEvent::DOWN;
435       switch ( action )
436       {
437       case AKEY_EVENT_ACTION_DOWN:
438         break;
439       case AKEY_EVENT_ACTION_UP:
440         state = Dali::KeyEvent::UP;
441         break;
442       }
443
444       std::string keyName = "";
445       switch( keyCode )
446       {
447       case 4:
448         keyName = "XF86Back";
449         break;
450       default:
451         break;
452       }
453       Dali::KeyEvent keyEvent = Dali::DevelKeyEvent::New( keyName, "", "", keyCode, 0, timeStamp, state, "", "", Device::Class::NONE, Device::Subclass::NONE );
454       Dali::Internal::Adaptor::Framework::Impl::NativeAppKeyEvent( framework, keyEvent );
455       return 1;
456     }
457
458     return 0;
459   }
460
461   static void HandleAppIdle(struct android_app* app, struct android_poll_source* source) {
462     Framework* framework = AndroidFramework::GetImplementation( AndroidFramework::Get() ).GetFramework();
463     if( framework && framework->mImpl )
464     {
465       framework->mImpl->OnIdle();
466     }
467   }
468
469 };
470
471 Framework::Framework( Framework::Observer& observer, int *argc, char ***argv, Type type )
472 : mObserver( observer ),
473   mInitialised( false ),
474   mPaused(false),
475   mRunning( false ),
476   mArgc( argc ),
477   mArgv( argv ),
478   mBundleName( "" ),
479   mBundleId( "" ),
480   mAbortHandler( MakeCallback( this, &Framework::AbortCallback ) ),
481   mImpl( NULL )
482 {
483   mImpl = new Impl( this );
484 }
485
486 Framework::~Framework()
487 {
488   if( mRunning )
489   {
490     Quit();
491   }
492
493   delete mImpl;
494   mImpl = nullptr;
495 }
496
497 void Framework::Run()
498 {
499   struct android_app* app = AndroidFramework::Get().GetNativeApplication();
500   app->onAppCmd = Framework::Impl::HandleAppCmd;
501   app->onInputEvent = Framework::Impl::HandleAppInput;
502
503   struct android_poll_source* source;
504   struct android_poll_source idlePollSource;
505   idlePollSource.id = LOOPER_ID_USER;
506   idlePollSource.app = app;
507   idlePollSource.process = Impl::HandleAppIdle;
508
509   int idlePipe[2];
510   if( pipe( idlePipe ) )
511   {
512     DALI_LOG_ERROR( "Failed to open idle pipe\n" );
513     return;
514   }
515
516   mImpl->mIdleReadPipe = idlePipe[0];
517   mImpl->mIdleWritePipe = idlePipe[1];
518   ALooper_addFd( app->looper,
519       idlePipe[0], LOOPER_ID_USER, ALOOPER_EVENT_INPUT, NULL, &idlePollSource );
520
521   mRunning = true;
522
523   // Read all pending events.
524   int events;
525   int idleTimeout = -1;
526
527   while( true )
528   {
529     if ( mImpl )
530     {
531       idleTimeout = mImpl->GetIdleTimeout();
532     }
533
534     int id = ALooper_pollAll( idleTimeout, NULL, &events, (void**)&source );
535
536     // Process the error.
537     if( id == ALOOPER_POLL_ERROR )
538     {
539        DALI_LOG_ERROR( "ALooper error\n" );
540        Quit();
541        std::abort();
542     }
543
544     // Process the timeout, trigger OnIdle.
545     if( id == ALOOPER_POLL_TIMEOUT )
546     {
547       int8_t msg = 1;
548       write( mImpl->mIdleWritePipe, &msg, sizeof( msg ) );
549     }
550
551     // Process the application event.
552     if( id >= 0 && source != NULL )
553     {
554       source->process( app, source );
555     }
556
557     // Check if we are exiting.
558     if( app->destroyRequested )
559     {
560       break;
561     }
562   }
563
564   while (!mImpl->mIdleCallbacks.empty())
565   {
566     mImpl->mIdleCallbacks.pop();
567   }
568
569   mImpl->mRemovedIdleCallbacks.clear();
570   mImpl->mIdleId = 0;
571
572   ALooper_removeFd( app->looper, idlePipe[0] );
573   if ( mImpl )
574   {
575     mImpl->mIdleReadPipe = -1;
576     mImpl->mIdleWritePipe = -1;
577   }
578   close( idlePipe[0] );
579   close( idlePipe[1] );
580
581   mRunning = false;
582 }
583
584 unsigned int Framework::AddIdle( int timeout, void* data, bool ( *callback )( void *data ) )
585 {
586   if( mImpl )
587   {
588     return mImpl->AddIdle( timeout, data, callback );
589   }
590
591   return -1;
592 }
593 void Framework::RemoveIdle( unsigned int id )
594 {
595   if( mImpl )
596   {
597     mImpl->RemoveIdle( id );
598   }
599 }
600
601 void Framework::Quit()
602 {
603   struct android_app* app = AndroidFramework::Get().GetNativeApplication();
604   if( app && !app->destroyRequested && !mImpl->mFinishRequested )
605   {
606     mImpl->mFinishRequested = true;
607     ANativeActivity_finish( app->activity );
608   }
609 }
610
611 bool Framework::IsMainLoopRunning()
612 {
613   return mRunning;
614 }
615
616 void Framework::AddAbortCallback( CallbackBase* callback )
617 {
618   mImpl->mAbortCallBack = callback;
619 }
620
621 std::string Framework::GetBundleName() const
622 {
623   return mBundleName;
624 }
625
626 void Framework::SetBundleName(const std::string& name)
627 {
628   mBundleName = name;
629 }
630
631 std::string Framework::GetBundleId() const
632 {
633   return mBundleId;
634 }
635
636 std::string Framework::GetResourcePath()
637 {
638   return DALI_DATA_RO_DIR;
639 }
640
641 std::string Framework::GetDataPath()
642 {
643   return "";
644 }
645
646 void Framework::SetBundleId(const std::string& id)
647 {
648   mBundleId = id;
649 }
650
651 void Framework::AbortCallback( )
652 {
653   // if an abort call back has been installed run it.
654   if( mImpl->mAbortCallBack )
655   {
656     CallbackBase::Execute( *mImpl->mAbortCallBack );
657   }
658   else
659   {
660     Quit();
661   }
662 }
663
664 bool Framework::AppStatusHandler(int type, void* data)
665 {
666   switch (type)
667   {
668     case APP_WINDOW_CREATED:
669       if( !mInitialised )
670       {
671         mObserver.OnInit();
672         mInitialised = true;
673       }
674
675       mObserver.OnSurfaceCreated( data );
676       break;
677
678     case APP_RESET:
679       mObserver.OnReset();
680       break;
681
682     case APP_RESUME:
683       mObserver.OnResume();
684       break;
685
686     case APP_WINDOW_DESTROYED:
687       mObserver.OnSurfaceDestroyed( data );
688       break;
689
690     case APP_PAUSE:
691       mObserver.OnPause();
692       break;
693
694     case APP_LANGUAGE_CHANGE:
695       mObserver.OnLanguageChanged();
696       break;
697
698     case APP_DESTROYED:
699       mObserver.OnTerminate();
700       mInitialised = false;
701       break;
702
703     default:
704       break;
705   }
706
707   return true;
708 }
709
710 void Framework::InitThreads()
711 {
712 }
713
714 std::string Framework::GetLanguage() const
715 {
716   return mImpl->GetLanguage();
717 }
718
719 std::string Framework::GetRegion() const
720 {
721   return mImpl->GetRegion();
722 }
723
724 } // namespace Adaptor
725
726 } // namespace Internal
727
728 } // namespace Dali