1 /*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "tools/sk_app/android/surface_glue_android.h"
9
10 #include <jni.h>
11 #include <pthread.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <unordered_map>
15
16 #include <android/asset_manager.h>
17 #include <android/asset_manager_jni.h>
18 #include <android/input.h>
19 #include <android/keycodes.h>
20 #include <android/looper.h>
21 #include <android/native_window_jni.h>
22
23 #include "include/core/SkTypes.h"
24 #include "include/private/base/SkTo.h"
25 #include "src/base/SkUTF.h"
26 #include "tools/ResourceFactory.h"
27 #include "tools/sk_app/Application.h"
28 #include "tools/sk_app/android/Window_android.h"
29
30
31 namespace sk_app {
32
config_resource_mgr(JNIEnv * env,jobject assetManager)33 static void config_resource_mgr(JNIEnv* env, jobject assetManager) {
34 static AAssetManager* gAAssetManager = nullptr;
35 SkASSERT(assetManager);
36 gAAssetManager = AAssetManager_fromJava(env, assetManager);
37 SkASSERT(gAAssetManager);
38 gResourceFactory = [](const char* resource) -> sk_sp<SkData> {
39 if (!gAAssetManager) {
40 return nullptr;
41 }
42 SkString path = SkStringPrintf("resources/%s", resource);
43 AAsset* asset = AAssetManager_open(gAAssetManager, path.c_str(), AASSET_MODE_STREAMING);
44 if (!asset) {
45 return nullptr;
46 }
47 size_t size = SkToSizeT(AAsset_getLength(asset));
48 sk_sp<SkData> data = SkData::MakeUninitialized(size);
49 (void)AAsset_read(asset, data->writable_data(), size);
50 AAsset_close(asset);
51 return data;
52 };
53 }
54
55 static const int LOOPER_ID_MESSAGEPIPE = 1;
56
57 static const std::unordered_map<int, skui::Key> ANDROID_TO_WINDOW_KEYMAP({
58 {AKEYCODE_SOFT_LEFT, skui::Key::kLeft },
59 {AKEYCODE_SOFT_RIGHT, skui::Key::kRight}
60 });
61
62 static const std::unordered_map<int, skui::InputState> ANDROID_TO_WINDOW_STATEMAP({
63 {AMOTION_EVENT_ACTION_DOWN, skui::InputState::kDown },
64 {AMOTION_EVENT_ACTION_POINTER_DOWN, skui::InputState::kDown },
65 {AMOTION_EVENT_ACTION_UP, skui::InputState::kUp },
66 {AMOTION_EVENT_ACTION_POINTER_UP, skui::InputState::kUp },
67 {AMOTION_EVENT_ACTION_MOVE, skui::InputState::kMove },
68 {AMOTION_EVENT_ACTION_CANCEL, skui::InputState::kUp },
69 });
70
SkiaAndroidApp(JNIEnv * env,jobject androidApp,jobjectArray args)71 SkiaAndroidApp::SkiaAndroidApp(JNIEnv* env, jobject androidApp, jobjectArray args) {
72 env->GetJavaVM(&fJavaVM);
73 fAndroidApp = env->NewGlobalRef(androidApp);
74 fArgs = static_cast<jobjectArray>(env->NewGlobalRef(args));
75 jclass cls = env->GetObjectClass(fAndroidApp);
76 fSetTitleMethodID = env->GetMethodID(cls, "setTitle", "(Ljava/lang/String;)V");
77 fSetStateMethodID = env->GetMethodID(cls, "setState", "(Ljava/lang/String;)V");
78 fNativeWindow = nullptr;
79 pthread_create(&fThread, nullptr, pthread_main, this);
80 }
81
~SkiaAndroidApp()82 SkiaAndroidApp::~SkiaAndroidApp() {
83 fPThreadEnv->DeleteGlobalRef(fAndroidApp);
84 fPThreadEnv->DeleteGlobalRef(fArgs);
85 if (fWindow) {
86 fWindow->detach();
87 }
88 if (fNativeWindow) {
89 ANativeWindow_release(fNativeWindow);
90 fNativeWindow = nullptr;
91 }
92 if (fApp) {
93 delete fApp;
94 }
95 }
96
setTitle(const char * title) const97 void SkiaAndroidApp::setTitle(const char* title) const {
98 jstring titleString = fPThreadEnv->NewStringUTF(title);
99 fPThreadEnv->CallVoidMethod(fAndroidApp, fSetTitleMethodID, titleString);
100 fPThreadEnv->DeleteLocalRef(titleString);
101 }
102
setUIState(const char * state) const103 void SkiaAndroidApp::setUIState(const char* state) const {
104 jstring jstr = fPThreadEnv->NewStringUTF(state);
105 fPThreadEnv->CallVoidMethod(fAndroidApp, fSetStateMethodID, jstr);
106 fPThreadEnv->DeleteLocalRef(jstr);
107 }
108
postMessage(const Message & message) const109 void SkiaAndroidApp::postMessage(const Message& message) const {
110 SkDEBUGCODE(auto writeSize =) write(fPipes[1], &message, sizeof(message));
111 SkASSERT(writeSize == sizeof(message));
112 }
113
readMessage(Message * message) const114 void SkiaAndroidApp::readMessage(Message* message) const {
115 SkDEBUGCODE(auto readSize =) read(fPipes[0], message, sizeof(Message));
116 SkASSERT(readSize == sizeof(Message));
117 }
118
message_callback(int fd,int events,void * data)119 int SkiaAndroidApp::message_callback(int fd, int events, void* data) {
120 auto skiaAndroidApp = (SkiaAndroidApp*)data;
121 Message message;
122 skiaAndroidApp->readMessage(&message);
123 SkASSERT(message.fType != kUndefined);
124
125 switch (message.fType) {
126 case kDestroyApp: {
127 delete skiaAndroidApp;
128 pthread_exit(nullptr);
129 }
130 case kContentInvalidated: {
131 ((Window_android*)skiaAndroidApp->fWindow)->paintIfNeeded();
132 break;
133 }
134 case kSurfaceCreated: {
135 SkASSERT(!skiaAndroidApp->fNativeWindow && message.fNativeWindow);
136 skiaAndroidApp->fNativeWindow = message.fNativeWindow;
137 auto window_android = (Window_android*)skiaAndroidApp->fWindow;
138 window_android->initDisplay(skiaAndroidApp->fNativeWindow);
139 ((Window_android*)skiaAndroidApp->fWindow)->paintIfNeeded();
140 break;
141 }
142 case kSurfaceChanged: {
143 SkASSERT(message.fNativeWindow);
144 int width = ANativeWindow_getWidth(skiaAndroidApp->fNativeWindow);
145 int height = ANativeWindow_getHeight(skiaAndroidApp->fNativeWindow);
146 auto window_android = (Window_android*)skiaAndroidApp->fWindow;
147 if (message.fNativeWindow != skiaAndroidApp->fNativeWindow) {
148 window_android->onDisplayDestroyed();
149 ANativeWindow_release(skiaAndroidApp->fNativeWindow);
150 skiaAndroidApp->fNativeWindow = message.fNativeWindow;
151 window_android->initDisplay(skiaAndroidApp->fNativeWindow);
152 }
153 window_android->onResize(width, height);
154 window_android->paintIfNeeded();
155 break;
156 }
157 case kSurfaceDestroyed: {
158 if (skiaAndroidApp->fNativeWindow) {
159 auto window_android = (Window_android*)skiaAndroidApp->fWindow;
160 window_android->onDisplayDestroyed();
161 ANativeWindow_release(skiaAndroidApp->fNativeWindow);
162 skiaAndroidApp->fNativeWindow = nullptr;
163 }
164 break;
165 }
166 case kKeyPressed: {
167 auto it = ANDROID_TO_WINDOW_KEYMAP.find(message.fKeycode);
168 SkASSERT(it != ANDROID_TO_WINDOW_KEYMAP.end());
169 // No modifier is supported so far
170 skiaAndroidApp->fWindow->onKey(it->second, skui::InputState::kDown, skui::ModifierKey::kNone);
171 skiaAndroidApp->fWindow->onKey(it->second, skui::InputState::kUp, skui::ModifierKey::kNone);
172 break;
173 }
174 case kTouched: {
175 auto it = ANDROID_TO_WINDOW_STATEMAP.find(message.fTouchState);
176 if (it != ANDROID_TO_WINDOW_STATEMAP.end()) {
177 skiaAndroidApp->fWindow->onTouch(message.fTouchOwner, it->second, message.fTouchX,
178 message.fTouchY);
179 } else {
180 SkDebugf("Unknown Touch State: %d\n", message.fTouchState);
181 }
182 break;
183 }
184 case kUIStateChanged: {
185 skiaAndroidApp->fWindow->onUIStateChanged(*message.stateName, *message.stateValue);
186 delete message.stateName;
187 delete message.stateValue;
188 break;
189 }
190 default: {
191 // do nothing
192 }
193 }
194
195 return 1; // continue receiving callbacks
196 }
197
pthread_main(void * arg)198 void* SkiaAndroidApp::pthread_main(void* arg) {
199 SkDebugf("pthread_main begins");
200
201 auto skiaAndroidApp = (SkiaAndroidApp*)arg;
202
203 // Because JNIEnv is thread sensitive, we need AttachCurrentThread to set our fPThreadEnv
204 skiaAndroidApp->fJavaVM->AttachCurrentThread(&(skiaAndroidApp->fPThreadEnv), nullptr);
205 JNIEnv* env = skiaAndroidApp->fPThreadEnv;
206
207 ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
208 pipe(skiaAndroidApp->fPipes);
209 ALooper_addFd(looper, skiaAndroidApp->fPipes[0], LOOPER_ID_MESSAGEPIPE, ALOOPER_EVENT_INPUT,
210 message_callback, skiaAndroidApp);
211
212 jsize argc = env->GetArrayLength(skiaAndroidApp->fArgs);
213 std::unique_ptr<const char*[]> argv = std::unique_ptr<const char*[]>(new const char*[argc+1]);
214 argv[0] = "viewer";
215 for (jsize i = 0; i < argc; ++i) {
216 jobject argBuffer = env->GetObjectArrayElement(skiaAndroidApp->fArgs, i);
217 void* argBytes = env->GetDirectBufferAddress(argBuffer);
218 argv[i+1] = static_cast<const char*>(argBytes);
219 }
220
221 skiaAndroidApp->fApp = Application::Create(argc+1,
222 const_cast<char**>(argv.get()),
223 skiaAndroidApp);
224
225 while (true) {
226 int ident = ALOOPER_POLL_CALLBACK;
227 while (ident == ALOOPER_POLL_CALLBACK) {
228 ident = ALooper_pollOnce(0, nullptr, nullptr, nullptr);
229 }
230
231 if (ident >= 0) {
232 SkDebugf("Unhandled ALooper_pollOnce ident=%d !", ident);
233 } else {
234 skiaAndroidApp->fApp->onIdle();
235 }
236 }
237 }
238
239 extern "C" // extern "C" is needed for JNI (although the method itself is in C++)
240 JNIEXPORT jlong JNICALL
Java_org_skia_viewer_ViewerApplication_createNativeApp(JNIEnv * env,jobject application,jobject assetManager,jobjectArray args)241 Java_org_skia_viewer_ViewerApplication_createNativeApp(JNIEnv* env,
242 jobject application,
243 jobject assetManager,
244 jobjectArray args) {
245 config_resource_mgr(env, assetManager);
246 SkiaAndroidApp* skiaAndroidApp = new SkiaAndroidApp(env, application, args);
247 return (jlong)((size_t)skiaAndroidApp);
248 }
249
Java_org_skia_viewer_ViewerApplication_destroyNativeApp(JNIEnv * env,jobject application,jlong handle)250 extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerApplication_destroyNativeApp(
251 JNIEnv* env, jobject application, jlong handle) {
252 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
253 skiaAndroidApp->postMessage(Message(kDestroyApp));
254 }
255
Java_org_skia_viewer_ViewerActivity_onSurfaceCreated(JNIEnv * env,jobject activity,jlong handle,jobject surface)256 extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceCreated(
257 JNIEnv* env, jobject activity, jlong handle, jobject surface) {
258 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
259 Message message(kSurfaceCreated);
260 message.fNativeWindow = ANativeWindow_fromSurface(env, surface);
261 skiaAndroidApp->postMessage(message);
262 }
263
Java_org_skia_viewer_ViewerActivity_onSurfaceChanged(JNIEnv * env,jobject activity,jlong handle,jobject surface)264 extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceChanged(
265 JNIEnv* env, jobject activity, jlong handle, jobject surface) {
266 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
267 Message message(kSurfaceChanged);
268 message.fNativeWindow = ANativeWindow_fromSurface(env, surface);
269 skiaAndroidApp->postMessage(message);
270 }
271
Java_org_skia_viewer_ViewerActivity_onSurfaceDestroyed(JNIEnv * env,jobject activity,jlong handle)272 extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceDestroyed(
273 JNIEnv* env, jobject activity, jlong handle) {
274 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
275 skiaAndroidApp->postMessage(Message(kSurfaceDestroyed));
276 }
277
Java_org_skia_viewer_ViewerActivity_onKeyPressed(JNIEnv * env,jobject activity,jlong handle,jint keycode)278 extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onKeyPressed(JNIEnv* env,
279 jobject activity,
280 jlong handle,
281 jint keycode) {
282 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
283 Message message(kKeyPressed);
284 message.fKeycode = keycode;
285 skiaAndroidApp->postMessage(message);
286 }
287
Java_org_skia_viewer_ViewerActivity_onTouched(JNIEnv * env,jobject activity,jlong handle,jint owner,jint state,jfloat x,jfloat y)288 extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onTouched(
289 JNIEnv* env, jobject activity, jlong handle, jint owner, jint state, jfloat x, jfloat y) {
290 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
291 Message message(kTouched);
292 message.fTouchOwner = owner;
293 message.fTouchState = state;
294 message.fTouchX = x;
295 message.fTouchY = y;
296 skiaAndroidApp->postMessage(message);
297 }
298
Java_org_skia_viewer_ViewerActivity_onUIStateChanged(JNIEnv * env,jobject activity,jlong handle,jstring stateName,jstring stateValue)299 extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onUIStateChanged(
300 JNIEnv* env, jobject activity, jlong handle, jstring stateName, jstring stateValue) {
301 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
302 Message message(kUIStateChanged);
303 const char* nameChars = env->GetStringUTFChars(stateName, nullptr);
304 const char* valueChars = env->GetStringUTFChars(stateValue, nullptr);
305 message.stateName = new SkString(nameChars);
306 message.stateValue = new SkString(valueChars);
307 skiaAndroidApp->postMessage(message);
308 env->ReleaseStringUTFChars(stateName, nameChars);
309 env->ReleaseStringUTFChars(stateValue, valueChars);
310 }
311
312 } // namespace sk_app
313