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