From f33d1537326af59b7948d2fa92db0b0190f0d99d Mon Sep 17 00:00:00 2001 From: Scroggo Date: Tue, 31 May 2011 17:10:21 +0000 Subject: [PATCH] Port the SampleApp (raster) to Android. git-svn-id: http://skia.googlecode.com/svn/trunk@1452 2bbb7eff-a529-9590-31e7-b0007b416f81 --- android_sample/SampleApp/Android.mk | 70 ++++++ android_sample/SampleApp/AndroidManifest.xml | 32 +++ android_sample/SampleApp/README.txt | 25 +++ android_sample/SampleApp/jni/sample-jni.cpp | 208 ++++++++++++++++++ .../SampleApp/res/layout/layout.xml | 26 +++ .../SampleApp/res/values/strings.xml | 18 ++ .../src/com/skia/sampleapp/SampleApp.java | 114 ++++++++++ include/utils/android/AndroidKeyToSkKey.h | 40 ++++ include/views/SkOSWindow_Android.h | 41 ++++ include/views/SkWindow.h | 2 + 10 files changed, 576 insertions(+) create mode 100644 android_sample/SampleApp/Android.mk create mode 100644 android_sample/SampleApp/AndroidManifest.xml create mode 100644 android_sample/SampleApp/README.txt create mode 100644 android_sample/SampleApp/jni/sample-jni.cpp create mode 100644 android_sample/SampleApp/res/layout/layout.xml create mode 100644 android_sample/SampleApp/res/values/strings.xml create mode 100644 android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java create mode 100644 include/utils/android/AndroidKeyToSkKey.h create mode 100644 include/views/SkOSWindow_Android.h diff --git a/android_sample/SampleApp/Android.mk b/android_sample/SampleApp/Android.mk new file mode 100644 index 000000000..4df3defe4 --- /dev/null +++ b/android_sample/SampleApp/Android.mk @@ -0,0 +1,70 @@ +###################################### +# Build the app. +###################################### + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := SampleApp + +LOCAL_JNI_SHARED_LIBRARIES := libskia-sample + +include $(BUILD_PACKAGE) + +###################################### +# Build the shared library. +###################################### + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_C_INCLUDES += \ + external/skia/include/core \ + external/skia/include/config \ + external/skia/include/effects \ + external/skia/include/images \ + external/skia/include/utils \ + $(LOCAL_PATH)/skia_extra/include/views \ + $(LOCAL_PATH)/skia_extra/samplecode \ + $(LOCAL_PATH)/skia_extra/include/xml \ + external/skia/include/gpu \ + external/skia/src/core \ + external/skia/gpu/include \ + frameworks/base/core/jni/android/graphics \ + frameworks/base/native/include/android \ + $(LOCAL_PATH)/jni + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libskia \ + libandroid_runtime \ + libGLESv2 + +LOCAL_STATIC_LIBRARIES := \ + libskiagpu + +LOCAL_PRELINK_MODULE := false + +LOCAL_MODULE := libskia-sample + +LOCAL_SRC_FILES := \ + skia_extra/src/ports/SkXMLParser_empty.cpp \ + jni/sample-jni.cpp + +include $(LOCAL_PATH)/skia_extra/src/views/views_files.mk +LOCAL_SRC_FILES += $(addprefix skia_extra/src/views/, $(SOURCE)) + +include $(LOCAL_PATH)/skia_extra/src/xml/xml_files.mk +LOCAL_SRC_FILES += $(addprefix skia_extra/src/xml/, $(SOURCE)) + +include $(LOCAL_PATH)/skia_extra/samplecode/samplecode_files.mk +LOCAL_SRC_FILES += $(addprefix skia_extra/samplecode/, $(SOURCE)) + +include $(BUILD_SHARED_LIBRARY) diff --git a/android_sample/SampleApp/AndroidManifest.xml b/android_sample/SampleApp/AndroidManifest.xml new file mode 100644 index 000000000..e3232468f --- /dev/null +++ b/android_sample/SampleApp/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + diff --git a/android_sample/SampleApp/README.txt b/android_sample/SampleApp/README.txt new file mode 100644 index 000000000..a7d8e54d2 --- /dev/null +++ b/android_sample/SampleApp/README.txt @@ -0,0 +1,25 @@ +Building the sample app for Android using an Android tree: + +Copy this folder into an Android tree in packages/apps. In addition to jni, +res, and src, there needs to be a fourth folder named "skia_extra". This +will include the skia files which are not part of an Android checkout. It +should have three folders: include, samplecode, and src. + +skia/trunk/include/views -> skia_extra/include/views +skia/trunk/include/xml -> skia_extra/include/xml + +skia/trunk/samplecode -> skia_extra/samplecode + +skia/trunk/src/views -> skia_extra/src/views +skia/trunk/src/ports/SkXMLParser_empty.cpp -> skia_extra/src/ports/ +skia/trunk/src/xml -> skia_extra/src/xml + +skia/trunk/include/utils/android/AndroidKeyToSkKey.h -> jni/ + +From packages/apps/SampleApp, type "mm" to build, and install the +resulting apk. + +(It may be necessary to remove samples that do not build from +skia_extra/samplecode/samplecode_files.mk) + +TODO: Instructions for building from SDK/NDK diff --git a/android_sample/SampleApp/jni/sample-jni.cpp b/android_sample/SampleApp/jni/sample-jni.cpp new file mode 100644 index 000000000..f8490b879 --- /dev/null +++ b/android_sample/SampleApp/jni/sample-jni.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2011 Skia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "SkCanvas.h" +#include "GraphicsJNI.h" +#include "SkEvent.h" +#include "SkWindow.h" +#include "SkApplication.h" +#include "AndroidKeyToSkKey.h" + +/////////////////////////////////////////// +///////////////// Globals ///////////////// +/////////////////////////////////////////// + +struct ActivityGlue { + JNIEnv* m_env; + jweak m_obj; + jmethodID m_setTitle; + ActivityGlue() { + m_env = NULL; + m_obj = NULL; + m_setTitle = NULL; + } +} gActivityGlue; + +struct WindowGlue { + jweak m_obj; + jmethodID m_inval; + WindowGlue() { + m_obj = NULL; + m_inval = NULL; + } +} gWindowGlue; + +SkOSWindow* gWindow; + +/////////////////////////////////////////// +///////////// SkOSWindow impl ///////////// +/////////////////////////////////////////// + +void SkOSWindow::onSetTitle(const char title[]) +{ + if (gActivityGlue.m_env) { + JNIEnv* env = gActivityGlue.m_env; + jstring string = env->NewStringUTF(title); + env->CallVoidMethod(gActivityGlue.m_obj, gActivityGlue.m_setTitle, + string); + env->DeleteLocalRef(string); + } +} + +void SkOSWindow::onHandleInval(const SkIRect& rect) +{ + if (!gActivityGlue.m_env || !gWindowGlue.m_inval || !gWindowGlue.m_obj) { + return; + } + gActivityGlue.m_env->CallVoidMethod(gWindowGlue.m_obj, gWindowGlue.m_inval, + rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); +} + +/////////////////////////////////////////// +/////////////// SkEvent impl ////////////// +/////////////////////////////////////////// + +void SkEvent::SignalQueueTimer(SkMSec) {} + +void SkEvent::SignalNonEmptyQueue() {} + +/////////////////////////////////////////// +////////////////// JNI //////////////////// +/////////////////////////////////////////// + +static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], + const char signature[]) +{ + jmethodID m = env->GetMethodID(clazz, name, signature); + if (!m) SkDebugf("Could not find Java method %s\n", name); + return m; +} + +extern "C" { +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_drawToCanvas( + JNIEnv* env, jobject thiz, jobject jcanvas); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize( + JNIEnv* env, jobject thiz, jint w, jint h); +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown( + JNIEnv* env, jobject thiz, jint keyCode, jint uni); +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp( + JNIEnv* env, jobject thiz, jint keyCode); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick( + JNIEnv* env, jobject thiz, jint x, jint y, jint state); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow( + JNIEnv* env, jobject thiz, jobject jsampleView); +}; + +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown( + JNIEnv* env, jobject thiz, jint keyCode, jint uni) +{ + bool handled = gWindow->handleKey(AndroidKeycodeToSkKey(keyCode)); + handled |= gWindow->handleChar((SkUnichar) uni); + return handled; +} + +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp(JNIEnv* env, + jobject thiz, jint keyCode) +{ + return gWindow->handleKeyUp(AndroidKeycodeToSkKey(keyCode)); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick(JNIEnv* env, + jobject thiz, jint x, jint y, jint jstate) +{ + SkView::Click::State state; + switch(jstate) { + case 0: // MotionEvent.ACTION_DOWN + state = SkView::Click::kDown_State; + break; + case 1: // MotionEvent.ACTION_UP + case 3: // MotionEvent.ACTION_CANCEL + state = SkView::Click::kUp_State; + break; + case 2: // MotionEvent.ACTION_MOVE + state = SkView::Click::kMoved_State; + break; + default: + SkDebugf("motion event ignored\n"); + return; + } + gWindow->handleClick(x, y, state); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize(JNIEnv* env, + jobject thiz, jint w, jint h) +{ + gWindow->resize(w, h); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow( + JNIEnv* env, jobject thiz, jobject jsampleView) +{ + gWindow = create_sk_window(NULL); + // Only using a method on View. + jclass clazz = gActivityGlue.m_env->FindClass("android/view/View"); + gWindowGlue.m_obj = gActivityGlue.m_env->NewWeakGlobalRef(jsampleView); + gWindowGlue.m_inval = GetJMethod(gActivityGlue.m_env, clazz, "invalidate", + "(IIII)V"); + gActivityGlue.m_env->DeleteLocalRef(clazz); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init(JNIEnv* env, + jobject thiz) +{ + gActivityGlue.m_env = env; + // Only using a method on Activity. + jclass clazz = env->FindClass("android/app/Activity"); + gActivityGlue.m_obj = env->NewWeakGlobalRef(thiz); + gActivityGlue.m_setTitle = GetJMethod(env, clazz, "setTitle", + "(Ljava/lang/CharSequence;)V"); + env->DeleteLocalRef(clazz); + + application_init(); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term(JNIEnv* env, + jobject thiz) +{ + application_term(); + if (gWindowGlue.m_obj) { + env->DeleteWeakGlobalRef(gWindowGlue.m_obj); + gWindowGlue.m_obj = NULL; + } + if (gActivityGlue.m_obj) { + env->DeleteWeakGlobalRef(gActivityGlue.m_obj); + gActivityGlue.m_obj = NULL; + } + delete gWindow; + gWindow = NULL; +} + + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_drawToCanvas( + JNIEnv* env, jobject thiz, jobject jcanvas) +{ + if (!gWindow) return; + gWindow->update(NULL); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas); + canvas->drawBitmap(gWindow->getBitmap(), 0, 0); +} diff --git a/android_sample/SampleApp/res/layout/layout.xml b/android_sample/SampleApp/res/layout/layout.xml new file mode 100644 index 000000000..d71116bae --- /dev/null +++ b/android_sample/SampleApp/res/layout/layout.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/android_sample/SampleApp/res/values/strings.xml b/android_sample/SampleApp/res/values/strings.xml new file mode 100644 index 000000000..72d3bc859 --- /dev/null +++ b/android_sample/SampleApp/res/values/strings.xml @@ -0,0 +1,18 @@ + + + + SampleApp + diff --git a/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java b/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java new file mode 100644 index 000000000..b02c4d7fe --- /dev/null +++ b/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2011 Skia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.skia.sampleapp; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class SampleApp extends Activity +{ + private TextView mTitle; + + public class SampleView extends View { + public SampleView(Context context) { + super(context); + createOSWindow(this); + } + + @Override + protected void onDraw(Canvas canvas) { + drawToCanvas(canvas); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + updateSize(w, h); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + final int x = (int) event.getX(); + final int y = (int) event.getY(); + final int action = event.getAction(); + handleClick(x, y, action); + return true; + } + } + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + init(); + setContentView(R.layout.layout); + mTitle = (TextView) findViewById(R.id.title_view); + LinearLayout holder = (LinearLayout) findViewById(R.id.holder); + View view = new SampleView(this); + holder.addView(view, new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + } + + @Override + public void onDestroy() + { + term(); + super.onDestroy(); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + switch (event.getAction()) { + case KeyEvent.ACTION_DOWN: + int uni = event.getUnicodeChar(event.getMetaState()); + return handleKeyDown(event.getKeyCode(), uni); + case KeyEvent.ACTION_UP: + return handleKeyUp(event.getKeyCode()); + default: + return false; + } + } + + @Override + public void setTitle(CharSequence title) { + mTitle.setText(title); + } + + private native void drawToCanvas(Canvas canvas); + private native void init(); + private native void term(); + // Currently depends on init having already been called. + private native void createOSWindow(SampleView view); + private native void updateSize(int w, int h); + private native void handleClick(int x, int y, int state); + private native boolean handleKeyDown(int key, int uni); + private native boolean handleKeyUp(int key); + + static { + System.loadLibrary("skia-sample"); + } +} diff --git a/include/utils/android/AndroidKeyToSkKey.h b/include/utils/android/AndroidKeyToSkKey.h new file mode 100644 index 000000000..7b0a03570 --- /dev/null +++ b/include/utils/android/AndroidKeyToSkKey.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 Skia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_TO_SKIA_KEYCODES_H +#define _ANDROID_TO_SKIA_KEYCODES_H + +#include "keycodes.h" +#include "SkKey.h" + +// Convert an Android keycode to an SkKey. This is an incomplete list, only +// including keys used by the sample app. +SkKey AndroidKeycodeToSkKey(int keycode) { + switch (keycode) { + case AKEYCODE_DPAD_LEFT: + return kLeft_SkKey; + case AKEYCODE_DPAD_RIGHT: + return kRight_SkKey; + case AKEYCODE_DPAD_UP: + return kUp_SkKey; + case AKEYCODE_DPAD_DOWN: + return kDown_SkKey; + default: + return kNONE_SkKey; + } +} + +#endif diff --git a/include/views/SkOSWindow_Android.h b/include/views/SkOSWindow_Android.h new file mode 100644 index 000000000..38a4cf8f5 --- /dev/null +++ b/include/views/SkOSWindow_Android.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011 Skia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkOSWindow_Android_DEFINED +#define SkOSWindow_Android_DEFINED + +#include "SkWindow.h" +#include "SkEvent.h" + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void*) {} + ~SkOSWindow() {} + bool attachGL() { return false; } + void detachGL() {} + void presentGL() {} + +protected: + // overrides from SkWindow + virtual void onHandleInval(const SkIRect&); + virtual void onSetTitle(const char title[]); + +private: + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/include/views/SkWindow.h b/include/views/SkWindow.h index 7b3777966..fd4ce0a91 100644 --- a/include/views/SkWindow.h +++ b/include/views/SkWindow.h @@ -111,6 +111,8 @@ private: #include "SkOSWindow_Mac.h" #elif defined(SK_BUILD_FOR_WIN) #include "SkOSWindow_Win.h" +#elif defined(ANDROID) + #include "SkOSWindow_Android.h" #elif defined(SK_BUILD_FOR_UNIX) #include "SkOSWindow_Unix.h" #elif defined(SK_BUILD_FOR_SDL)