02fb4c4e23a2b7fdfc4732086093f975ad73925a
[platform/core/uifw/dali-demo.git] / build / android / app / src / main / cpp / main.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 // EXTERNAL INCLUDES
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22
23 #include <android/log.h>
24 #include <android_native_app_glue.h>
25 #include <dali/devel-api/adaptor-framework/application-devel.h>
26 #include <dali/integration-api/debug.h>
27 #include <dali/integration-api/adaptor-framework/android/android-framework.h>
28 #include <dlfcn.h>
29
30 // from android_native_app_glue.c
31 #ifndef NDEBUG
32 #define LOGV(...)  ((void)__android_log_print(ANDROID_LOG_VERBOSE, "dalidemo", __VA_ARGS__))
33 #else
34 #define LOGV(...)  ((void)0)
35 #endif
36
37 namespace
38 {
39
40 void free_saved_state(struct android_app *android_app)
41 {
42     pthread_mutex_lock(&android_app->mutex);
43
44     if (android_app->savedState != NULL)
45     {
46       free(android_app->savedState);
47       android_app->savedState = NULL;
48       android_app->savedStateSize = 0;
49     }
50
51     pthread_mutex_unlock(&android_app->mutex);
52 }
53
54 void android_app_destroy(struct android_app *android_app)
55 {
56   LOGV("android_app_destroy");
57   free_saved_state(android_app);
58   pthread_mutex_lock(&android_app->mutex);
59
60   if (android_app->inputQueue != NULL)
61   {
62     AInputQueue_detachLooper(android_app->inputQueue);
63   }
64
65   AConfiguration_delete(android_app->config);
66   android_app->destroyed = 1;
67
68   pthread_cond_broadcast(&android_app->cond);
69   pthread_mutex_unlock(&android_app->mutex);
70
71   // Can't touch android_app object after this.
72 }
73
74 }
75
76 void ExtractAsset(struct android_app* state, const std::string& assetPath, const std::string& filePath)
77 {
78   AAsset* asset = AAssetManager_open(state->activity->assetManager, assetPath.c_str(), AASSET_MODE_BUFFER);
79   if (asset)
80   {
81     size_t length = AAsset_getLength(asset) + 1;
82
83     char* buffer = new char[length];
84     length = AAsset_read(asset, buffer, length);
85
86     FILE* file = fopen(filePath.c_str(), "wb");
87     if (file)
88     {
89       fwrite(buffer, 1, length, file);
90       fclose(file);
91     }
92
93     delete[] buffer;
94     AAsset_close(asset);
95   }
96 }
97
98 void ExtractAssets(struct android_app* state, const std::string& assetDirPath, const std::string& filesDirPath)
99 {
100   AAssetDir* assetDir = AAssetManager_openDir(state->activity->assetManager, assetDirPath.c_str());
101   if (assetDir)
102   {
103     if (mkdir(filesDirPath.c_str(), S_IRWXU) != -1)
104     {
105       const char *filename = NULL;
106       std::string assetPath = assetDirPath + "/";
107       while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL)
108       {
109         ExtractAsset(state, assetPath + filename, filesDirPath + "/" + filename);
110       }
111     }
112
113     AAssetDir_close(assetDir);
114   }
115 }
116
117 void ExtractFontConfig(struct android_app* state, std::string assetFontConfig, std::string fontsPath)
118 {
119   AAsset* asset = AAssetManager_open(state->activity->assetManager, assetFontConfig.c_str(), AASSET_MODE_BUFFER);
120   if (asset)
121   {
122     size_t length = AAsset_getLength(asset) + 1;
123
124     char* buffer = new char[length];
125     length = AAsset_read(asset, buffer, length);
126
127     std::string fontConfig = std::string(buffer, length);
128     int i = fontConfig.find("~");
129     if (i != std::string::npos)
130     {
131       std::string filesDir = state->activity->internalDataPath;
132       fontConfig.replace(i, 1, filesDir);
133     }
134
135     std::string fontsFontConfig = fontsPath;
136     FILE* file = fopen(fontsFontConfig.c_str(), "wb");
137     if (file)
138     {
139       fwrite(fontConfig.c_str(), 1, fontConfig.size(), file);
140       fclose(file);
141     }
142
143     delete[] buffer;
144     AAsset_close(asset);
145   }
146 }
147
148 class DaliDemoNativeActivity
149 {
150 public:
151   ANativeActivity* activity;
152   DaliDemoNativeActivity(ANativeActivity* activity)
153   : activity(activity)
154   {
155   }
156
157   class NativeActivityJNI
158   {
159   public:
160     ANativeActivity* activity;
161     JNIEnv* env;
162     jclass clazz;
163
164     NativeActivityJNI(ANativeActivity* activity)
165     : activity(activity)
166     {
167       activity->vm->AttachCurrentThread(&env, NULL);
168       clazz = env->GetObjectClass(activity->clazz);
169     }
170
171     ~NativeActivityJNI()
172     {
173       activity->vm->DetachCurrentThread();
174     }
175
176     jstring toJString(const std::string& str)
177     {
178       return env->NewStringUTF(str.c_str());
179     }
180
181     std::string toString(jstring jstr)
182     {
183       std::string out;
184       if (jstr)
185       {
186         const char* utf = env->GetStringUTFChars(jstr, 0);
187         out = std::string(utf);
188         env->ReleaseStringUTFChars(jstr, utf);
189       }
190
191       return out;
192     }
193
194     std::string callStringMethod(const std::string& name, const std::string& arg)
195     {
196       jmethodID methodID = env->GetMethodID(clazz, name.c_str(), "(Ljava/lang/String;)Ljava/lang/String;");
197       jstring jstr = (jstring)env->CallObjectMethod(activity->clazz, methodID, toJString(arg));
198       return toString(jstr);
199     }
200   };
201
202   std::string getMetaData(const std::string& key)
203   {
204     NativeActivityJNI nativeActivityJNI(activity);
205     return nativeActivityJNI.callStringMethod("getMetaData", key);
206   }
207
208   std::string getIntentStringExtra(const std::string& key)
209   {
210     NativeActivityJNI nativeActivityJNI(activity);
211     return nativeActivityJNI.callStringMethod("getIntentStringExtra", key);
212   }
213 };
214
215 extern "C" void FcConfigPathInit(const char* path, const char* file);
216
217 void android_main( struct android_app* state )
218 {
219   std::string filesDir = state->activity->internalDataPath;
220
221   std::string fontconfigPath = filesDir + "/fonts";
222   setenv("FONTCONFIG_PATH", fontconfigPath.c_str(), 1);
223
224   std::string fontconfigFile = fontconfigPath + "/fonts.conf";
225   setenv("FONTCONFIG_FILE", fontconfigFile.c_str(), 1);
226
227   struct stat st = { 0 };
228   FcConfigPathInit( fontconfigPath.c_str(), fontconfigFile.c_str() );
229
230   if (stat(fontconfigPath.c_str(), &st) == -1)
231   {
232     mkdir(fontconfigPath.c_str(), S_IRWXU);
233     ExtractFontConfig(state, "fonts/fonts.conf", fontconfigPath + "/fonts.conf");
234     ExtractFontConfig(state, "fonts/fonts.dtd", fontconfigPath + "/fonts.dtd" );
235     ExtractFontConfig(state, "fonts/local.conf", fontconfigPath + "/local.conf");
236     ExtractAssets(state, "fonts/dejavu", fontconfigPath + "/dejavu");
237     ExtractAssets(state, "fonts/tizen", fontconfigPath + "/tizen");
238     ExtractAssets(state, "fonts/bitmap", fontconfigPath + "/bitmap");
239   }
240
241   Dali::Integration::AndroidFramework::New();
242   Dali::Integration::AndroidFramework::Get().SetNativeApplication( state );
243   Dali::Integration::AndroidFramework::Get().SetApplicationConfiguration( state->config );
244   Dali::Integration::AndroidFramework::Get().SetApplicationAssets( state->activity->assetManager );
245   Dali::Integration::AndroidFramework::Get().SetInternalDataPath( filesDir );
246
247   DaliDemoNativeActivity nativeActivity(state->activity);
248
249   int status = 0;
250   std::string libpath = "/data/data/com.sec.dalidemo/lib/libdali-demo.so";
251   std::string callParam = nativeActivity.getIntentStringExtra("start");
252   if (callParam.empty())
253   {
254     callParam = nativeActivity.getMetaData("start");
255   }
256
257   if (!callParam.empty())
258   {
259     libpath = "/data/data/com.sec.dalidemo/lib/lib" +  callParam + ".so";
260   }
261
262   void* handle = dlopen( libpath.c_str(), RTLD_LAZY );
263   if (!handle)
264   {
265     std::exit(status);
266   }
267
268   dlerror();    /* Clear any existing error */
269
270   int (*main)(int, char**) = (int(*)(int, char**))dlsym(handle, "main");
271   if (main)
272   {
273     status = main( 0, nullptr );
274   }
275
276   if (handle)
277   {
278     dlclose(handle);
279     handle = nullptr;
280   }
281
282   android_app_destroy(state);
283
284   Dali::Integration::AndroidFramework::Get().SetNativeApplication(nullptr);
285   Dali::Integration::AndroidFramework::Get().SetApplicationConfiguration(nullptr);
286   Dali::Integration::AndroidFramework::Get().SetApplicationAssets(nullptr);
287   Dali::Integration::AndroidFramework::Delete();
288
289   // We need to kill the application process manually, DALi cannot restart in the same process due to memory leaks
290   std::exit(status);
291 }
292
293 //END_INCLUDE(all)