Adding a new sample plugin to test text input.
diff --git a/samples/BrowserPlugin/jni/Android.mk b/samples/BrowserPlugin/jni/Android.mk
index 3ff5a62..a5150ba 100644
--- a/samples/BrowserPlugin/jni/Android.mk
+++ b/samples/BrowserPlugin/jni/Android.mk
@@ -32,13 +32,15 @@
PluginObject.cpp \
animation/AnimationPlugin.cpp \
audio/AudioPlugin.cpp \
- background/BackgroundPlugin.cpp
+ background/BackgroundPlugin.cpp \
+ form/FormPlugin.cpp \
LOCAL_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/animation \
$(LOCAL_PATH)/audio \
$(LOCAL_PATH)/background \
+ $(LOCAL_PATH)/form \
external/webkit/WebCore/bridge \
external/webkit/WebCore/plugins \
external/webkit/WebCore/platform/android/JavaVM \
diff --git a/samples/BrowserPlugin/jni/PluginObject.h b/samples/BrowserPlugin/jni/PluginObject.h
index 7c7b18f..8070e20 100644
--- a/samples/BrowserPlugin/jni/PluginObject.h
+++ b/samples/BrowserPlugin/jni/PluginObject.h
@@ -54,11 +54,12 @@
};
enum PluginTypes {
- kBackground_PluginType = 1,
- kAnimation_PluginType = 2,
- kAudio_PluginType = 3,
- kText_PluginType = 4,
- kPaint_PluginType = 5
+ kAnimation_PluginType = 1,
+ kAudio_PluginType = 2,
+ kBackground_PluginType = 3,
+ kForm_PluginType = 4,
+ kText_PluginType = 5,
+ kPaint_PluginType = 6
};
typedef uint32_t PluginType;
diff --git a/samples/BrowserPlugin/jni/form/FormPlugin.cpp b/samples/BrowserPlugin/jni/form/FormPlugin.cpp
new file mode 100644
index 0000000..278645c
--- /dev/null
+++ b/samples/BrowserPlugin/jni/form/FormPlugin.cpp
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "FormPlugin.h"
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+#include <math.h>
+#include <string.h>
+
+extern NPNetscapeFuncs* browser;
+extern ANPLogInterfaceV0 gLogI;
+extern ANPCanvasInterfaceV0 gCanvasI;
+extern ANPPaintInterfaceV0 gPaintI;
+extern ANPTypefaceInterfaceV0 gTypefaceI;
+extern ANPWindowInterfaceV0 gWindowI;
+
+
+static void inval(NPP instance) {
+ browser->invalidaterect(instance, NULL);
+}
+
+static uint16 rnd16(float x, int inset) {
+ int ix = (int)roundf(x) + inset;
+ if (ix < 0) {
+ ix = 0;
+ }
+ return static_cast<uint16>(ix);
+}
+
+static void inval(NPP instance, const ANPRectF& r, bool doAA) {
+ const int inset = doAA ? -1 : 0;
+
+ PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
+ NPRect inval;
+ inval.left = rnd16(r.left, inset);
+ inval.top = rnd16(r.top, inset);
+ inval.right = rnd16(r.right, -inset);
+ inval.bottom = rnd16(r.bottom, -inset);
+ browser->invalidaterect(instance, &inval);
+}
+
+static void drawPlugin(SubPlugin* plugin, const ANPBitmap& bitmap, const ANPRectI& clip) {
+ ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
+
+ ANPRectF clipR;
+ clipR.left = clip.left;
+ clipR.top = clip.top;
+ clipR.right = clip.right;
+ clipR.bottom = clip.bottom;
+ gCanvasI.clipRect(canvas, &clipR);
+
+ plugin->draw(canvas);
+ gCanvasI.deleteCanvas(canvas);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+FormPlugin::FormPlugin(NPP inst) : SubPlugin(inst) {
+
+ m_activeInput = NULL;
+
+ memset(&m_usernameInput, 0, sizeof(m_usernameInput));
+ memset(&m_passwordInput, 0, sizeof(m_passwordInput));
+
+ m_usernameInput.text[0] = '\0';
+ m_usernameInput.charPtr = 0;
+
+ m_passwordInput.text[0] = '\0';
+ m_passwordInput.charPtr = 0;
+
+ m_paintInput = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintInput, gPaintI.getFlags(m_paintInput) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintInput, 0xFFFFFFFF);
+
+ m_paintActive = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintActive, gPaintI.getFlags(m_paintActive) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintActive, 0xFFFFFF00);
+
+ m_paintText = gPaintI.newPaint();
+ gPaintI.setFlags(m_paintText, gPaintI.getFlags(m_paintText) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(m_paintText, 0xFF000000);
+ gPaintI.setTextSize(m_paintText, 18);
+
+ ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
+ gPaintI.setTypeface(m_paintText, tf);
+ gTypefaceI.unref(tf);
+
+ //register for key and visibleRect events
+ ANPEventFlags flags = kKey_ANPEventFlag | kVisibleRect_ANPEventFlag;
+ NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
+ if (err != NPERR_NO_ERROR) {
+ gLogI.log(inst, kError_ANPLogType, "Error selecting input events.");
+ }
+}
+
+FormPlugin::~FormPlugin() {
+ gPaintI.deletePaint(m_paintInput);
+ gPaintI.deletePaint(m_paintActive);
+ gPaintI.deletePaint(m_paintText);
+}
+
+void FormPlugin::draw(ANPCanvas* canvas) {
+ NPP instance = this->inst();
+ PluginObject *obj = (PluginObject*) instance->pdata;
+
+ const float inputWidth = 60;
+ const float inputHeight = 30;
+ const int W = obj->window->width;
+ const int H = obj->window->height;
+
+ // color the plugin canvas
+ gCanvasI.drawColor(canvas, 0xFFCDCDCD);
+
+ // draw the username box (5 px from the top edge)
+ inval(instance, m_usernameInput.rect, true);
+ m_usernameInput.rect.left = 5;
+ m_usernameInput.rect.top = 5;
+ m_usernameInput.rect.right = W - 5;
+ m_usernameInput.rect.bottom = m_usernameInput.rect.top + inputHeight;
+ gCanvasI.drawRect(canvas, &m_usernameInput.rect, getPaint(&m_usernameInput));
+ drawText(canvas, m_usernameInput);
+ inval(instance, m_usernameInput.rect, true);
+
+ // draw the password box (5 px from the bottom edge)
+ inval(instance, m_passwordInput.rect, true);
+ m_passwordInput.rect.left = 5;
+ m_passwordInput.rect.top = H - (inputHeight + 5);
+ m_passwordInput.rect.right = W - 5;
+ m_passwordInput.rect.bottom = m_passwordInput.rect.top + inputHeight;
+ gCanvasI.drawRect(canvas, &m_passwordInput.rect, getPaint(&m_passwordInput));
+ drawPassword(canvas, m_passwordInput);
+ inval(instance, m_passwordInput.rect, true);
+}
+
+ANPPaint* FormPlugin::getPaint(TextInput* input) {
+ return (input == m_activeInput) ? m_paintActive : m_paintInput;
+}
+
+void FormPlugin::drawText(ANPCanvas* canvas, TextInput textInput) {
+
+ // get font metrics
+ ANPFontMetrics fontMetrics;
+ gPaintI.getFontMetrics(m_paintText, &fontMetrics);
+
+ gCanvasI.drawText(canvas, textInput.text, textInput.charPtr,
+ textInput.rect.left + 5,
+ textInput.rect.bottom - fontMetrics.fBottom, m_paintText);
+}
+
+void FormPlugin::drawPassword(ANPCanvas* canvas, TextInput passwordInput) {
+
+ //TODO draw circles instead of the actual text
+}
+
+int16 FormPlugin::handleEvent(const ANPEvent* evt) {
+ NPP instance = this->inst();
+
+ switch (evt->eventType) {
+ case kDraw_ANPEventType:
+ switch (evt->data.draw.model) {
+ case kBitmap_ANPDrawingModel:
+ drawPlugin(this, evt->data.draw.data.bitmap, evt->data.draw.clip);
+ return 1;
+ default:
+ break; // unknown drawing model
+ }
+ break;
+
+ case kLifecycle_ANPEventType:
+ if (evt->data.lifecycle.action == kLooseFocus_ANPLifecycleAction) {
+ if (m_activeInput) {
+
+ // hide the keyboard
+ //gWindowI.showKeyboard(instance, false);
+ gLogI.log(instance, kDebug_ANPLogType, "----%p Hiding Keyboard2", instance);
+
+ //inval the plugin
+ m_activeInput = NULL;
+ inval(instance);
+
+ }
+ return 1;
+ }
+ break;
+
+ case kMouse_ANPEventType: {
+
+ int x = evt->data.mouse.x;
+ int y = evt->data.mouse.y;
+ if (kDown_ANPMouseAction == evt->data.mouse.action) {
+
+ TextInput* currentInput = validTap(x,y);
+
+ if (currentInput)
+ gWindowI.showKeyboard(instance, true);
+ else if (m_activeInput) {
+ gWindowI.showKeyboard(instance, false);
+ gLogI.log(instance, kDebug_ANPLogType, "----%p Hiding Keyboard", instance);
+ }
+
+
+ if (currentInput == m_activeInput)
+ return 1;
+
+ if (m_activeInput)
+ inval(instance, m_activeInput->rect, true); // inval the old
+
+ m_activeInput = currentInput; // set the new active input
+ if (m_activeInput)
+ inval(instance, m_activeInput->rect, true); // inval the new
+
+ return 1;
+ }
+ break;
+ }
+
+ case kKey_ANPEventType:
+ if (evt->data.key.action == kDown_ANPKeyAction) {
+
+ //handle navigation keys
+ if (evt->data.key.nativeCode >= kDpadUp_ANPKeyCode
+ && evt->data.key.nativeCode <= kDpadCenter_ANPKeyCode) {
+ gLogI.log(instance, kDebug_ANPLogType, "----%p Recvd Nav Key", instance);
+ return 0;
+ }
+
+ if (m_activeInput) {
+ handleTextInput(m_activeInput, evt->data.key.nativeCode,
+ evt->data.key.unichar);
+ inval(instance, m_activeInput->rect, true);
+ }
+ return 1;
+ }
+ break;
+
+ case kVisibleRect_ANPEventType: {
+
+ int oldScreenW = m_visibleRect.right - m_visibleRect.left;
+ int oldScreenH = m_visibleRect.bottom - m_visibleRect.top;
+
+ m_visibleRect = evt->data.visibleRect.rect;
+
+ int newScreenW = m_visibleRect.right - m_visibleRect.left;
+ int newScreenH = m_visibleRect.bottom - m_visibleRect.top;
+
+ if (m_activeInput && (oldScreenW != newScreenW || oldScreenH != newScreenH))
+ scrollIntoView(m_activeInput);
+
+ return 1;
+ }
+
+ default:
+ break;
+ }
+ return 0; // unknown or unhandled event
+}
+
+void FormPlugin::handleTextInput(TextInput* input, ANPKeyCode keyCode, int32_t unichar) {
+ NPP instance = this->inst();
+
+ //make sure the input field is in view
+ scrollIntoView(input);
+
+ //handle the delete operation
+ if (keyCode == kDel_ANPKeyCode) {
+ if (input->charPtr > 0) {
+ input->charPtr--;
+ }
+ return;
+ }
+
+ //check to see that the input is not full
+ if (input->charPtr >= (sizeof(input->text) - 1))
+ return;
+
+ //add the character
+ input->text[input->charPtr] = static_cast<char>(unichar);
+ input->charPtr++;
+
+ gLogI.log(instance, kDebug_ANPLogType, "----%p Text: %c", instance, unichar);
+}
+
+void FormPlugin::scrollIntoView(TextInput* input) {
+ NPP instance = this->inst();
+ PluginObject *obj = (PluginObject*) instance->pdata;
+ NPWindow *window = obj->window;
+
+ // find the textInput's global rect coordinates
+ ANPRectI inputRect;
+ inputRect.left = window->x + input->rect.left;
+ inputRect.top = window->y + input->rect.top;
+ inputRect.right = inputRect.left + (input->rect.right - input->rect.left);
+ inputRect.bottom = inputRect.top + (input->rect.bottom - input->rect.top);
+
+ gLogI.log(instance, kDebug_ANPLogType, "----%p Checking Rect Visibility", instance);
+
+ // if the rect is contained within visible window then do nothing
+ if (inputRect.left > m_visibleRect.left
+ && inputRect.right < m_visibleRect.right
+ && inputRect.top > m_visibleRect.top
+ && inputRect.bottom < m_visibleRect.bottom) {
+ return;
+ }
+
+ // find the global (x,y) coordinates for center of the textInput
+ int inputCenterX = inputRect.left + ((inputRect.right - inputRect.left)/2);
+ int inputCenterY = inputRect.top + ((inputRect.bottom - inputRect.top)/2);
+
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p Input Center: %d,%d : %d,%d",
+ instance, inputCenterX, inputCenterY, window->x, window->y);
+
+ //find global (x,y) coordinates for center of the visible screen
+ int screenCenterX = m_visibleRect.left + ((m_visibleRect.right - m_visibleRect.left)/2);
+ int screenCenterY = m_visibleRect.top + ((m_visibleRect.bottom - m_visibleRect.top)/2);
+
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p Screen Center: %d,%d : %d,%d",
+ instance, screenCenterX, screenCenterY, m_visibleRect.left, m_visibleRect.top);
+
+ //compute the delta of the two coordinates
+ int deltaX = inputCenterX - screenCenterX;
+ int deltaY = inputCenterY - screenCenterY;
+
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p Centering: %d,%d : %d,%d",
+ instance, deltaX, deltaY, m_visibleRect.left + deltaX, m_visibleRect.top + deltaY);
+
+ //move the visible screen
+ gWindowI.scrollTo(instance, m_visibleRect.left + deltaX, m_visibleRect.top + deltaY);
+
+}
+
+TextInput* FormPlugin::validTap(int x, int y) {
+
+ if (x > m_usernameInput.rect.left && x < m_usernameInput.rect.right &&
+ y > m_usernameInput.rect.top && y < m_usernameInput.rect.bottom)
+ return &m_usernameInput;
+ else if (x >m_passwordInput.rect.left && x < m_passwordInput.rect.right &&
+ y > m_passwordInput.rect.top && y < m_passwordInput.rect.bottom)
+ return &m_passwordInput;
+ else
+ return NULL;
+}
diff --git a/samples/BrowserPlugin/jni/form/FormPlugin.h b/samples/BrowserPlugin/jni/form/FormPlugin.h
new file mode 100644
index 0000000..71964d5
--- /dev/null
+++ b/samples/BrowserPlugin/jni/form/FormPlugin.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginObject.h"
+#include "android_npapi.h"
+
+#ifndef formPlugin__DEFINED
+#define formPlugin__DEFINED
+
+struct TextInput {
+ ANPRectF rect;
+ char text[30];
+ uint32_t charPtr;
+};
+
+class FormPlugin : public SubPlugin {
+public:
+ FormPlugin(NPP inst);
+ virtual ~FormPlugin();
+ virtual void draw(ANPCanvas*);
+ virtual int16 handleEvent(const ANPEvent* evt);
+private:
+
+ TextInput* m_activeInput;
+ TextInput m_usernameInput;
+ TextInput m_passwordInput;
+
+ ANPPaint* m_paintInput;
+ ANPPaint* m_paintActive;
+ ANPPaint* m_paintText;
+
+ ANPRectI m_visibleRect;
+
+ void drawText(ANPCanvas*, TextInput);
+ void drawPassword(ANPCanvas*, TextInput);
+
+ void handleTextInput(TextInput* input, ANPKeyCode keyCode, int32_t unichar);
+ void scrollIntoView(TextInput* input);
+
+ ANPPaint* getPaint(TextInput*);
+ TextInput* validTap(int x, int y);
+
+};
+
+#endif // formPlugin__DEFINED
diff --git a/samples/BrowserPlugin/jni/main.cpp b/samples/BrowserPlugin/jni/main.cpp
index b73e128..23c7f01 100644
--- a/samples/BrowserPlugin/jni/main.cpp
+++ b/samples/BrowserPlugin/jni/main.cpp
@@ -31,6 +31,7 @@
#include "AnimationPlugin.h"
#include "AudioPlugin.h"
#include "BackgroundPlugin.h"
+#include "FormPlugin.h"
#include "android_npapi.h"
NPNetscapeFuncs* browser;
@@ -190,6 +191,10 @@
obj->pluginType = kBackground_PluginType;
obj->activePlugin = new BackgroundPlugin(instance);
}
+ else if (!strcmp(argv[i], "Form")) {
+ obj->pluginType = kForm_PluginType;
+ obj->activePlugin = new FormPlugin(instance);
+ }
gLogI.log(instance, kDebug_ANPLogType, "------ %p PluginType is %d", instance, obj->pluginType);
break;
}
@@ -302,6 +307,12 @@
evt->data.touch.y);
break;
+ case kMouse_ANPEventType:
+ gLogI.log(instance, kDebug_ANPLogType, "---- %p Mouse action=%d [%d %d]",
+ instance, evt->data.mouse.action, evt->data.mouse.x,
+ evt->data.mouse.y);
+ break;
+
case kVisibleRect_ANPEventType:
gLogI.log(instance, kDebug_ANPLogType, "---- %p VisibleRect [%d %d %d %d]",
instance, evt->data.visibleRect.rect.left, evt->data.visibleRect.rect.top,