Port the SampleApp (raster) to Android.


git-svn-id: http://skia.googlecode.com/svn/trunk@1452 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/android_sample/SampleApp/Android.mk b/android_sample/SampleApp/Android.mk
new file mode 100644
index 0000000..4df3def
--- /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 0000000..e323246
--- /dev/null
+++ b/android_sample/SampleApp/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.skia.sampleapp"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <uses-sdk android:minSdkVersion="3" />
+    <application android:label="@string/app_name"
+                 android:debuggable="true">
+        <activity android:name=".SampleApp"
+                  android:theme="@android:style/Theme.Holo.NoActionBar"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest> 
diff --git a/android_sample/SampleApp/README.txt b/android_sample/SampleApp/README.txt
new file mode 100644
index 0000000..a7d8e54
--- /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 0000000..f8490b8
--- /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 <jni.h>
+
+#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 0000000..d71116b
--- /dev/null
+++ b/android_sample/SampleApp/res/layout/layout.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/holder"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+    <TextView android:id="@+id/title_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        />
+</LinearLayout>
+
diff --git a/android_sample/SampleApp/res/values/strings.xml b/android_sample/SampleApp/res/values/strings.xml
new file mode 100644
index 0000000..72d3bc8
--- /dev/null
+++ b/android_sample/SampleApp/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+    <string name="app_name">SampleApp</string>
+</resources>
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 0000000..b02c4d7
--- /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 0000000..7b0a035
--- /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 0000000..38a4cf8
--- /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 7b37779..fd4ce0a 100644
--- a/include/views/SkWindow.h
+++ b/include/views/SkWindow.h
@@ -111,6 +111,8 @@
     #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)