From 796c5bb4655a511553970e111c96d90963fb9ae4 Mon Sep 17 00:00:00 2001 From: liyuqian Date: Mon, 9 May 2016 08:49:29 -0700 Subject: [PATCH] Initial commit of our new Android app to demo Skia. Currently, there's only a simple Activity with a title bar and a surface view that connects with Skia viewer native application. Before integrating user action events, I want to make sure that the design and implementation so far are good. Note that the old NativeApplication-based Activity (ViewerActivity) is still there for reference and test purposes. It will be removed eventually. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1952323004 Review-Url: https://codereview.chromium.org/1952323004 --- .../apps/viewer/src/main/AndroidManifest.xml | 9 +- .../main/java/org/skia/viewer/ViewerActivity.java | 52 ++++-- .../java/org/skia/viewer/ViewerApplication.java | 41 +++++ .../viewer/src/main/res/layout/activity_main.xml | 17 ++ tools/viewer/sk_app/VulkanWindowContext.h | 4 +- tools/viewer/sk_app/android/Window_android.cpp | 143 ++--------------- tools/viewer/sk_app/android/Window_android.h | 23 +-- .../viewer/sk_app/android/surface_glue_android.cpp | 174 +++++++++++++++++++++ tools/viewer/sk_app/android/surface_glue_android.h | 55 +++++++ 9 files changed, 350 insertions(+), 168 deletions(-) create mode 100644 platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerApplication.java create mode 100644 platform_tools/android/apps/viewer/src/main/res/layout/activity_main.xml create mode 100644 tools/viewer/sk_app/android/surface_glue_android.cpp create mode 100644 tools/viewer/sk_app/android/surface_glue_android.h diff --git a/platform_tools/android/apps/viewer/src/main/AndroidManifest.xml b/platform_tools/android/apps/viewer/src/main/AndroidManifest.xml index 2ed88e1..a1fd971 100644 --- a/platform_tools/android/apps/viewer/src/main/AndroidManifest.xml +++ b/platform_tools/android/apps/viewer/src/main/AndroidManifest.xml @@ -7,14 +7,11 @@ - - + diff --git a/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerActivity.java b/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerActivity.java index 950d6b2..48cec39 100644 --- a/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerActivity.java +++ b/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerActivity.java @@ -7,22 +7,48 @@ package org.skia.viewer; -import android.app.ActionBar; +import android.app.Activity; import android.os.Bundle; -import android.provider.Settings; -import android.view.View; -import android.view.WindowManager; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class ViewerActivity extends Activity implements SurfaceHolder.Callback { + private SurfaceView mView; + private ViewerApplication mApplication; + + private native void onSurfaceCreated(long handle, Surface surface); + private native void onSurfaceChanged(long handle, Surface surface); + private native void onSurfaceDestroyed(long handle); -public class ViewerActivity extends android.app.NativeActivity { - static { - System.loadLibrary("skia_android"); - } - @Override - public void onCreate(Bundle savedInstanceState) - { + protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ActionBar ab = this.getActionBar(); - ab.hide(); + setContentView(R.layout.activity_main); + + mApplication = (ViewerApplication) getApplication(); + mView = (SurfaceView) findViewById(R.id.surfaceView); + mView.getHolder().addCallback(this); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + if (mApplication.getNativeHandle() != 0) { + onSurfaceCreated(mApplication.getNativeHandle(), holder.getSurface()); + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + if (mApplication.getNativeHandle() != 0) { + onSurfaceChanged(mApplication.getNativeHandle(), holder.getSurface()); + } + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + if (mApplication.getNativeHandle() != 0) { + onSurfaceDestroyed(mApplication.getNativeHandle()); + } } } diff --git a/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerApplication.java b/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerApplication.java new file mode 100644 index 0000000..9389f72 --- /dev/null +++ b/platform_tools/android/apps/viewer/src/main/java/org/skia/viewer/ViewerApplication.java @@ -0,0 +1,41 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +package org.skia.viewer; + +import android.app.Application; + +public class ViewerApplication extends Application { + private long mNativeHandle = 0; + + static { + System.loadLibrary("skia_android"); + System.loadLibrary("viewer"); + } + + private native long createNativeApp(); + private native void destroyNativeApp(long handle); + + @Override + public void onCreate() { + super.onCreate(); + mNativeHandle = createNativeApp(); + } + + @Override + public void onTerminate() { + if (mNativeHandle != 0) { + destroyNativeApp(mNativeHandle); + mNativeHandle = 0; + } + super.onTerminate(); + } + + public long getNativeHandle() { + return mNativeHandle; + } +} diff --git a/platform_tools/android/apps/viewer/src/main/res/layout/activity_main.xml b/platform_tools/android/apps/viewer/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..6597a48 --- /dev/null +++ b/platform_tools/android/apps/viewer/src/main/res/layout/activity_main.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/tools/viewer/sk_app/VulkanWindowContext.h b/tools/viewer/sk_app/VulkanWindowContext.h index e186881..ec80a32 100644 --- a/tools/viewer/sk_app/VulkanWindowContext.h +++ b/tools/viewer/sk_app/VulkanWindowContext.h @@ -46,8 +46,8 @@ public: this->createSwapchain(w, h, fDisplayParams); } - const DisplayParams& getDisplayParams() { return fDisplayParams; } - void setDisplayParams(const DisplayParams& params) { + const DisplayParams& getDisplayParams() override { return fDisplayParams; } + void setDisplayParams(const DisplayParams& params) override { this->createSwapchain(fWidth, fHeight, params); } diff --git a/tools/viewer/sk_app/android/Window_android.cpp b/tools/viewer/sk_app/android/Window_android.cpp index 9def29f..09e7ef1 100644 --- a/tools/viewer/sk_app/android/Window_android.cpp +++ b/tools/viewer/sk_app/android/Window_android.cpp @@ -13,22 +13,17 @@ namespace sk_app { Window* Window::CreateNativeWindow(void* platformData) { Window_android* window = new Window_android(); - if (!window->init((android_app*)platformData)) { + if (!window->init((SkiaAndroidApp*)platformData)) { delete window; return nullptr; } return window; } -static void handle_cmd(struct android_app* app, int32_t cmd); -static int32_t handle_input(struct android_app* app, AInputEvent* event); - -bool Window_android::init(android_app* app) { - SkASSERT(app); - mApp = app; - mApp->userData = this; - mApp->onAppCmd = handle_cmd; - mApp->onInputEvent = handle_input; +bool Window_android::init(SkiaAndroidApp* skiaAndroidApp) { + SkASSERT(skiaAndroidApp); + fSkiaAndroidApp = skiaAndroidApp; + fSkiaAndroidApp->fWindow = this; return true; } @@ -53,131 +48,15 @@ void Window_android::initDisplay(ANativeWindow* window) { SkASSERT(window); ContextPlatformData_android platformData; platformData.fNativeWindow = window; - fWindowContext = VulkanWindowContext::Create((void*)&platformData, mSampleCount, fSRGB); -} - -static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { - if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { - SkDebugf("Failure writing android_app cmd: %s\n", strerror(errno)); - } + fWindowContext = VulkanWindowContext::Create((void*)&platformData, fDisplayParams); + fNativeWindowInitialized = true; } -void Window_android::inval() { - android_app_write_cmd(mApp, APP_CMD_INVAL_WINDOW); -} - -void Window_android::paintIfNeeded() { - if (mApp->window || !mContentRect.isEmpty()) { - this->onPaint(); - } -} - -/** - * Process the next main command. - */ -static void handle_cmd(struct android_app* app, int32_t cmd) { - Window_android* window = (Window_android*)app->userData; - switch (cmd) { - case APP_CMD_INIT_WINDOW: - // The window is being shown, get it ready. - SkASSERT(app->window); - window->initDisplay(app->window); - window->paintIfNeeded(); - break; - case APP_CMD_WINDOW_RESIZED: { - int width = ANativeWindow_getWidth(app->window); - int height = ANativeWindow_getHeight(app->window); - window->onResize(width, height); - break; - } - case APP_CMD_CONTENT_RECT_CHANGED: - window->setContentRect(app->contentRect.left, app->contentRect.top, - app->contentRect.right, app->contentRect.bottom); - window->paintIfNeeded(); - break; - case APP_CMD_TERM_WINDOW: - // The window is being hidden or closed, clean it up. - window->detach(); - break; - case APP_CMD_INVAL_WINDOW: - window->paintIfNeeded(); - break; - } -} - -static Window::Key get_key(int32_t keycode) { - static const struct { - int32_t fAndroidKey; - Window::Key fWindowKey; - } gPair[] = { - { AKEYCODE_BACK, Window::kBack_Key }, - { AKEYCODE_VOLUME_UP, Window::kLeft_Key }, - { AKEYCODE_VOLUME_DOWN, Window::kRight_Key } - }; - for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { - if (gPair[i].fAndroidKey == keycode) { - return gPair[i].fWindowKey; - } - } - return Window::kNONE_Key; +void Window_android::onDisplayDestroyed() { + fNativeWindowInitialized = false; + detach(); } -static Window::InputState get_action(int32_t action) { - static const struct { - int32_t fAndroidAction; - Window::InputState fInputState; - } gPair[] = { - { AKEY_STATE_DOWN, Window::kDown_InputState }, - { AKEY_STATE_UP, Window::kUp_InputState }, - }; - for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { - if (gPair[i].fAndroidAction == action) { - return gPair[i].fInputState; - } - } - return Window::kMove_InputState; -} - -static int32_t get_key_modifiers(AInputEvent* event) { - static const struct { - int32_t fAndroidState; - int32_t fWindowModifier; - } gPair[] = { - { AMETA_SHIFT_ON, Window::kShift_ModifierKey }, - { AMETA_CTRL_ON, Window::kControl_ModifierKey }, - }; - - int32_t metaState = AKeyEvent_getMetaState(event); - int32_t modifiers = 0; - - if (AKeyEvent_getRepeatCount(event) == 0) { - modifiers |= Window::kFirstPress_ModifierKey; - } - - for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { - if (gPair[i].fAndroidState == metaState) { - modifiers |= gPair[i].fWindowModifier; - } - } - return modifiers; -} - -/** - * Process the next input event. - */ -static int32_t handle_input(struct android_app* app, AInputEvent* event) { - Window_android* window = (Window_android*)app->userData; - switch(AInputEvent_getType(event)) { - case AINPUT_EVENT_TYPE_MOTION: - break; - case AINPUT_EVENT_TYPE_KEY: - Window::Key key = get_key(AKeyEvent_getKeyCode(event)); - Window::InputState state = get_action(AKeyEvent_getAction(event)); - int32_t mod = get_key_modifiers(event); - window->onKey(key, state, mod); - return true; // eat all key events - } - return 0; -} +void Window_android::inval() { fSkiaAndroidApp->postMessage(Message(kContentInvalidated)); } } // namespace sk_app diff --git a/tools/viewer/sk_app/android/Window_android.h b/tools/viewer/sk_app/android/Window_android.h index b570615..45e5bbe 100644 --- a/tools/viewer/sk_app/android/Window_android.h +++ b/tools/viewer/sk_app/android/Window_android.h @@ -9,24 +9,18 @@ #define Window_android_DEFINED #include "../Window.h" -#include +#include "surface_glue_android.h" namespace sk_app { -enum { - /** - * Leave plenty of space between this item and the ones defined in the glue layer - */ - APP_CMD_INVAL_WINDOW = 64, -}; - class Window_android : public Window { public: Window_android() : Window() {} ~Window_android() override {} - bool init(android_app* app_state); + bool init(SkiaAndroidApp* skiaAndroidApp); void initDisplay(ANativeWindow* window); + void onDisplayDestroyed(); void setTitle(const char*) override; void show() override {} @@ -34,17 +28,16 @@ public: bool attach(BackEndType attachType, const DisplayParams& params) override; void inval() override; - void paintIfNeeded(); - bool scaleContentToFit() const override { return true; } bool supportsContentRect() const override { return true; } - SkRect getContentRect() override { return mContentRect; } - void setContentRect(int l, int t, int r, int b) { mContentRect.set(l,t,r,b); } + SkRect getContentRect() override { return fContentRect; } + void setContentRect(int l, int t, int r, int b) { fContentRect.set(l,t,r,b); } private: - android_app* mApp = nullptr; - SkRect mContentRect; + SkiaAndroidApp* fSkiaAndroidApp = nullptr; + SkRect fContentRect; DisplayParams fDisplayParams; + bool fNativeWindowInitialized = false; }; } // namespace sk_app diff --git a/tools/viewer/sk_app/android/surface_glue_android.cpp b/tools/viewer/sk_app/android/surface_glue_android.cpp new file mode 100644 index 0000000..b1d0029 --- /dev/null +++ b/tools/viewer/sk_app/android/surface_glue_android.cpp @@ -0,0 +1,174 @@ +/* +* Copyright 2016 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file. +*/ + +#include "surface_glue_android.h" + +#include +#include +#include +#include + +#include +#include + +#include "../Application.h" +#include "SkTypes.h" +#include "SkUtils.h" +#include "Window_android.h" + +namespace sk_app { + +static const int LOOPER_ID_MESSAGEPIPE = 1; + +void* pthread_main(void* arg); + +SkiaAndroidApp::SkiaAndroidApp() { + fNativeWindow = nullptr; + pthread_create(&fThread, nullptr, pthread_main, this); +} + +SkiaAndroidApp::~SkiaAndroidApp() { + if (fWindow) { + fWindow->detach(); + } + if (fNativeWindow) { + ANativeWindow_release(fNativeWindow); + fNativeWindow = nullptr; + } + if (fApp) { + delete fApp; + } +} + +void SkiaAndroidApp::paintIfNeeded() { + if (fNativeWindow && fWindow) { + fWindow->onPaint(); + } +} + +void SkiaAndroidApp::postMessage(const Message& message) { + auto writeSize = write(fPipes[1], &message, sizeof(message)); + SkASSERT(writeSize == sizeof(message)); +} + +void SkiaAndroidApp::readMessage(Message* message) { + auto readSize = read(fPipes[0], message, sizeof(Message)); + SkASSERT(readSize == sizeof(Message)); +} + +static int message_callback(int fd, int events, void* data) { + auto skiaAndroidApp = (SkiaAndroidApp*)data; + Message message; + skiaAndroidApp->readMessage(&message); + SkDebugf("message_callback %d", message.fType); + SkASSERT(message.fType != kUndefined); + + switch (message.fType) { + case kDestroyApp: { + delete skiaAndroidApp; + pthread_exit(nullptr); + return 0; + } + case kContentInvalidated: { + skiaAndroidApp->paintIfNeeded(); + break; + } + case kSurfaceCreated: { + SkASSERT(!skiaAndroidApp->fNativeWindow && message.fNativeWindow); + skiaAndroidApp->fNativeWindow = message.fNativeWindow; + auto window_android = (Window_android*)skiaAndroidApp->fWindow; + window_android->initDisplay(skiaAndroidApp->fNativeWindow); + skiaAndroidApp->paintIfNeeded(); + break; + } + case kSurfaceChanged: { + SkASSERT(message.fNativeWindow == skiaAndroidApp->fNativeWindow && + message.fNativeWindow); + int width = ANativeWindow_getWidth(skiaAndroidApp->fNativeWindow); + int height = ANativeWindow_getHeight(skiaAndroidApp->fNativeWindow); + skiaAndroidApp->fWindow->onResize(width, height); + auto window_android = (Window_android*)skiaAndroidApp->fWindow; + window_android->setContentRect(0, 0, width, height); + skiaAndroidApp->paintIfNeeded(); + break; + } + case kSurfaceDestroyed: { + if (skiaAndroidApp->fNativeWindow) { + auto window_android = (Window_android*)skiaAndroidApp->fWindow; + window_android->onDisplayDestroyed(); + ANativeWindow_release(skiaAndroidApp->fNativeWindow); + skiaAndroidApp->fNativeWindow = nullptr; + } + break; + } + default: { + // do nothing + } + } + + return 1; // continue receiving callbacks +} + +void* pthread_main(void* arg) { + SkDebugf("pthread_main begins"); + + auto skiaAndroidApp = (SkiaAndroidApp*)arg; + + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + pipe(skiaAndroidApp->fPipes); + ALooper_addFd(looper, skiaAndroidApp->fPipes[0], LOOPER_ID_MESSAGEPIPE, ALOOPER_EVENT_INPUT, + message_callback, skiaAndroidApp); + + int ident; + int events; + struct android_poll_source* source; + + skiaAndroidApp->fApp = Application::Create(0, nullptr, skiaAndroidApp); + + while ((ident = ALooper_pollAll(-1, nullptr, &events, (void**)&source)) >= 0) { + SkDebugf("ALooper_pollAll ident=%d", ident); + } + + return nullptr; +} + +extern "C" // extern "C" is needed for JNI (although the method itself is in C++) + JNIEXPORT jlong JNICALL + Java_org_skia_viewer_ViewerApplication_createNativeApp(JNIEnv* env, jobject activity) { + SkiaAndroidApp* skiaAndroidApp = new SkiaAndroidApp; + return (jlong)((size_t)skiaAndroidApp); +} + +extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerApplication_destroyNativeApp( + JNIEnv* env, jobject activity, jlong handle) { + auto skiaAndroidApp = (SkiaAndroidApp*)handle; + skiaAndroidApp->postMessage(Message(kDestroyApp)); +} + +extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceCreated( + JNIEnv* env, jobject activity, jlong handle, jobject surface) { + auto skiaAndroidApp = (SkiaAndroidApp*)handle; + Message message(kSurfaceCreated); + message.fNativeWindow = ANativeWindow_fromSurface(env, surface); + skiaAndroidApp->postMessage(message); +} + +extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceChanged( + JNIEnv* env, jobject activity, jlong handle, jobject surface) { + auto skiaAndroidApp = (SkiaAndroidApp*)handle; + Message message(kSurfaceChanged); + message.fNativeWindow = ANativeWindow_fromSurface(env, surface); + skiaAndroidApp->postMessage(message); +} + +extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceDestroyed( + JNIEnv* env, jobject activity, jlong handle) { + auto skiaAndroidApp = (SkiaAndroidApp*)handle; + skiaAndroidApp->postMessage(Message(kSurfaceDestroyed)); +} + +} // namespace sk_app diff --git a/tools/viewer/sk_app/android/surface_glue_android.h b/tools/viewer/sk_app/android/surface_glue_android.h new file mode 100644 index 0000000..aefe462 --- /dev/null +++ b/tools/viewer/sk_app/android/surface_glue_android.h @@ -0,0 +1,55 @@ +/* +* Copyright 2016 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file. +*/ + +#ifndef surface_glue_android_DEFINED +#define surface_glue_android_DEFINED + +#include + +#include + +#include "../Application.h" +#include "../Window.h" + +namespace sk_app { + +enum MessageType { + kUndefined, + kSurfaceCreated, + kSurfaceChanged, + kSurfaceDestroyed, + kDestroyApp, + kContentInvalidated +}; + +struct Message { + MessageType fType = kUndefined; + ANativeWindow* fNativeWindow = nullptr; + + Message() {} + Message(MessageType t) : fType(t) {} +}; + +struct SkiaAndroidApp { + int fPipes[2]; // 0 is the read message pipe, 1 is the write message pipe + Application* fApp; + Window* fWindow; + ANativeWindow* fNativeWindow; + + SkiaAndroidApp(); + ~SkiaAndroidApp(); + void postMessage(const Message& message); + void readMessage(Message* message); + void paintIfNeeded(); + +private: + pthread_t fThread; +}; + +} // namespace sk_app + +#endif -- 2.7.4