diff --git a/samples/BrowserPlugin/jni/Android.mk b/samples/BrowserPlugin/jni/Android.mk index ae35ed45d..3ff5a62e4 100644 --- a/samples/BrowserPlugin/jni/Android.mk +++ b/samples/BrowserPlugin/jni/Android.mk @@ -31,11 +31,13 @@ LOCAL_SRC_FILES := \ main.cpp \ PluginObject.cpp \ animation/AnimationPlugin.cpp \ + audio/AudioPlugin.cpp \ background/BackgroundPlugin.cpp LOCAL_C_INCLUDES += \ $(LOCAL_PATH) \ $(LOCAL_PATH)/animation \ + $(LOCAL_PATH)/audio \ $(LOCAL_PATH)/background \ external/webkit/WebCore/bridge \ external/webkit/WebCore/plugins \ diff --git a/samples/BrowserPlugin/jni/PluginObject.h b/samples/BrowserPlugin/jni/PluginObject.h index 8a9a07985..7c7b18fbc 100644 --- a/samples/BrowserPlugin/jni/PluginObject.h +++ b/samples/BrowserPlugin/jni/PluginObject.h @@ -69,7 +69,6 @@ typedef struct PluginObject { PluginType pluginType; SubPlugin* activePlugin; - ANPAudioTrack* track; } PluginObject; diff --git a/samples/BrowserPlugin/jni/audio/AudioPlugin.cpp b/samples/BrowserPlugin/jni/audio/AudioPlugin.cpp new file mode 100644 index 000000000..872f9795b --- /dev/null +++ b/samples/BrowserPlugin/jni/audio/AudioPlugin.cpp @@ -0,0 +1,338 @@ +/* + * 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 "AudioPlugin.h" + +#include +#include +#include +#include +#include + +extern NPNetscapeFuncs* browser; +extern ANPLogInterfaceV0 gLogI; +extern ANPCanvasInterfaceV0 gCanvasI; +extern ANPPaintInterfaceV0 gPaintI; +extern ANPAudioTrackInterfaceV0 gSoundI; +extern ANPTypefaceInterfaceV0 gTypefaceI; + + +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(ix); +} + +static void inval(NPP instance, const ANPRectF& r, bool doAA) { + const int inset = doAA ? -1 : 0; + + PluginObject *obj = reinterpret_cast(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); +} + +struct SoundPlay { + NPP instance; + ANPAudioTrack* track; + FILE* file; +}; + +static void audioCallback(ANPAudioEvent evt, void* user, ANPAudioBuffer* buffer) { + switch (evt) { + case kMoreData_ANPAudioEvent: { + SoundPlay* play = reinterpret_cast(user); + size_t amount = fread(buffer->bufferData, 1, buffer->size, play->file); + buffer->size = amount; + if (amount == 0) { + gSoundI.stop(play->track); + fclose(play->file); + play->file = NULL; + // TODO need to notify our main thread to delete the track now + } + break; + } + default: + break; + } +} + +static ANPAudioTrack* createTrack(NPP instance, const char path[]) { + FILE* f = fopen(path, "r"); + gLogI.log(instance, kWarning_ANPLogType, "--- path %s FILE %p", path, f); + if (NULL == f) { + return NULL; + } + SoundPlay* play = new SoundPlay; + play->file = f; + play->track = gSoundI.newTrack(44100, kPCM16Bit_ANPSampleFormat, 2, audioCallback, play); + if (NULL == play->track) { + fclose(f); + delete play; + return NULL; + } + return play->track; +} + +/////////////////////////////////////////////////////////////////////////////// + +AudioPlugin::AudioPlugin(NPP inst) : SubPlugin(inst) { + + m_track = NULL; + m_activeTouch = false; + + memset(&m_trackRect, 0, sizeof(m_trackRect)); + memset(&m_playRect, 0, sizeof(m_playRect)); + memset(&m_pauseRect, 0, sizeof(m_pauseRect)); + memset(&m_stopRect, 0, sizeof(m_stopRect)); + + m_paintTrack = gPaintI.newPaint(); + gPaintI.setFlags(m_paintTrack, gPaintI.getFlags(m_paintTrack) | kAntiAlias_ANPPaintFlag); + gPaintI.setColor(m_paintTrack, 0xFFC0C0C0); + + m_paintRect = gPaintI.newPaint(); + gPaintI.setFlags(m_paintRect, gPaintI.getFlags(m_paintRect) | kAntiAlias_ANPPaintFlag); + gPaintI.setColor(m_paintRect, 0xFFA8A8A8); + + m_paintText = gPaintI.newPaint(); + gPaintI.setFlags(m_paintText, gPaintI.getFlags(m_paintText) | kAntiAlias_ANPPaintFlag); + gPaintI.setColor(m_paintText, 0xFF2F4F4F); + gPaintI.setTextSize(m_paintText, 18); + + ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle); + gPaintI.setTypeface(m_paintText, tf); + gTypefaceI.unref(tf); + + //register for touch events + ANPEventFlags flags = kTouch_ANPEventFlag; + NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags); + if (err != NPERR_NO_ERROR) { + gLogI.log(inst, kError_ANPLogType, "Error selecting input events."); + } +} + +AudioPlugin::~AudioPlugin() { + gPaintI.deletePaint(m_paintTrack); + gPaintI.deletePaint(m_paintRect); + gPaintI.deletePaint(m_paintText); + if(m_track) + gSoundI.deleteTrack(m_track); +} + +void AudioPlugin::draw(ANPCanvas* canvas) { + NPP instance = this->inst(); + PluginObject *obj = (PluginObject*) instance->pdata; + + const float trackHeight = 30; + const float buttonWidth = 60; + const float buttonHeight = 30; + const int W = obj->window->width; + const int H = obj->window->height; + + // color the plugin canvas + gCanvasI.drawColor(canvas, 0xFFCDCDCD); + + // get font metrics + ANPFontMetrics fontMetrics; + gPaintI.getFontMetrics(m_paintText, &fontMetrics); + + // draw the track box (1 px from the edge) + inval(instance, m_trackRect, true); + m_trackRect.left = 1; + m_trackRect.top = 1; + m_trackRect.right = W - 2; + m_trackRect.bottom = 1 + trackHeight; + gCanvasI.drawRect(canvas, &m_trackRect, m_paintTrack); + inval(instance, m_trackRect, true); + + // draw the play box (under track box) + inval(instance, m_playRect, true); + m_playRect.left = m_trackRect.left + 5; + m_playRect.top = m_trackRect.bottom + 10; + m_playRect.right = m_playRect.left + buttonWidth; + m_playRect.bottom = m_playRect.top + buttonHeight; + gCanvasI.drawRect(canvas, &m_playRect, m_paintRect); + // draw the play box (under track box) + const char playText[] = "Play"; + gCanvasI.drawText(canvas, playText, sizeof(playText)-1, m_playRect.left + 5, + m_playRect.top - fontMetrics.fTop, m_paintText); + inval(instance, m_playRect, true); + + // draw the pause box (under track box) + inval(instance, m_pauseRect, true); + m_pauseRect.left = m_playRect.right + 20; + m_pauseRect.top = m_trackRect.bottom + 10; + m_pauseRect.right = m_pauseRect.left + buttonWidth; + m_pauseRect.bottom = m_pauseRect.top + buttonHeight; + gCanvasI.drawRect(canvas, &m_pauseRect, m_paintRect); + // draw the text in the pause box + const char pauseText[] = "Pause"; + gCanvasI.drawText(canvas, pauseText, sizeof(pauseText)-1, m_pauseRect.left + 5, + m_pauseRect.top - fontMetrics.fTop, m_paintText); + inval(instance, m_pauseRect, true); + + // draw the stop box (under track box) + inval(instance, m_stopRect, true); + m_stopRect.left = m_pauseRect.right + 20; + m_stopRect.top = m_trackRect.bottom + 10; + m_stopRect.right = m_stopRect.left + buttonWidth; + m_stopRect.bottom = m_stopRect.top + buttonHeight; + gCanvasI.drawRect(canvas, &m_stopRect, m_paintRect); + // draw the text in the pause box + const char stopText[] = "Stop"; + gCanvasI.drawText(canvas, stopText, sizeof(stopText)-1, m_stopRect.left + 5, + m_stopRect.top - fontMetrics.fTop, m_paintText); + inval(instance, m_stopRect, true); + +} + +int16 AudioPlugin::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 + } + + case kTouch_ANPEventType: { + int x = evt->data.touch.x; + int y = evt->data.touch.y; + if (kDown_ANPTouchAction == evt->data.touch.action) { + + if (m_activeTouch) + invalActiveRect(); + + m_activeRect = validTouch(x,y); + if(m_activeRect) { + m_activeTouch = true; + // TODO color the rect + return 1; + } + + } else if (kUp_ANPTouchAction == evt->data.touch.action && m_activeTouch) { + handleTouch(x, y); + invalActiveRect(); + m_activeTouch = false; + return 1; + } else if (kCancel_ANPTouchAction == evt->data.touch.action) { + m_activeTouch = false; + } + break; + } + default: + break; + } + return 0; // unknown or unhandled event +} + +void AudioPlugin::invalActiveRect() { } + +ANPRectF* AudioPlugin::validTouch(int x, int y) { + + if (m_playRect.left && x < m_playRect.right && y > m_playRect.top && y < m_playRect.bottom) + return &m_playRect; + else if (m_pauseRect.left && x < m_pauseRect.right && y > m_pauseRect.top && y < m_pauseRect.bottom) + return &m_pauseRect; + else if (x > m_stopRect.left && x < m_stopRect.right && y > m_stopRect.top && y < m_stopRect.bottom) + return &m_stopRect; + else + return NULL; +} + +void AudioPlugin::handleTouch(int x, int y) { + NPP instance = this->inst(); + + if (NULL == m_track) { + m_track = createTrack(instance, "/sdcard/sample.raw"); + } + + // if the track is still null then return + if(NULL == m_track) { + gLogI.log(instance, kError_ANPLogType, "---- %p unable to create track", + instance); + return; + } + + // check to make sure the currentRect matches the activeRect + ANPRectF* currentRect = validTouch(x,y); + if(m_activeRect != currentRect) + return; + + if (m_activeRect == &m_playRect) { + + gLogI.log(instance, kDebug_ANPLogType, "---- %p starting track (%d)", + m_track, gSoundI.isStopped(m_track)); + + if (gSoundI.isStopped(m_track)) { + gSoundI.start(m_track); + } + } + else if (m_activeRect == &m_pauseRect) { + + gLogI.log(instance, kDebug_ANPLogType, "---- %p pausing track (%d)", + m_track, gSoundI.isStopped(m_track)); + + if (!gSoundI.isStopped(m_track)) { + gSoundI.pause(m_track); + } + } + else if (m_activeRect == &m_stopRect) { + + gLogI.log(instance, kDebug_ANPLogType, "---- %p stopping track (%d)", + m_track, gSoundI.isStopped(m_track)); + + if (!gSoundI.isStopped(m_track)) { + gSoundI.stop(m_track); + } + } +} diff --git a/samples/BrowserPlugin/jni/audio/AudioPlugin.h b/samples/BrowserPlugin/jni/audio/AudioPlugin.h new file mode 100644 index 000000000..c357fd5c7 --- /dev/null +++ b/samples/BrowserPlugin/jni/audio/AudioPlugin.h @@ -0,0 +1,60 @@ +/* + * 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 audioPlugin__DEFINED +#define audioPlugin__DEFINED + +class AudioPlugin : public SubPlugin { +public: + AudioPlugin(NPP inst); + virtual ~AudioPlugin(); + virtual void draw(ANPCanvas*); + virtual int16 handleEvent(const ANPEvent* evt); +private: + + ANPRectF m_trackRect; + ANPRectF m_playRect; + ANPRectF m_pauseRect; + ANPRectF m_stopRect; + + ANPPaint* m_paintTrack; + ANPPaint* m_paintRect; + ANPPaint* m_paintText; + + ANPAudioTrack* m_track; + + bool m_activeTouch; + ANPRectF* m_activeRect; + + ANPRectF* validTouch(int x, int y); + void handleTouch(int x, int y); + void invalActiveRect(); + +}; + +#endif // audioPlugin__DEFINED diff --git a/samples/BrowserPlugin/jni/main.cpp b/samples/BrowserPlugin/jni/main.cpp index 0b7774129..b73e128e8 100644 --- a/samples/BrowserPlugin/jni/main.cpp +++ b/samples/BrowserPlugin/jni/main.cpp @@ -29,6 +29,7 @@ #include "main.h" #include "PluginObject.h" #include "AnimationPlugin.h" +#include "AudioPlugin.h" #include "BackgroundPlugin.h" #include "android_npapi.h" @@ -183,7 +184,7 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, } else if (!strcmp(argv[i], "Audio")) { obj->pluginType = kAudio_PluginType; - //TODO add audio here + obj->activePlugin = new AudioPlugin(instance); } else if (!strcmp(argv[i], "Background")) { obj->pluginType = kBackground_PluginType; @@ -207,7 +208,6 @@ NPError NPP_Destroy(NPP instance, NPSavedData** save) { PluginObject *obj = (PluginObject*) instance->pdata; delete obj->activePlugin; - gSoundI.deleteTrack(obj->track); return NPERR_NO_ERROR; } @@ -255,48 +255,6 @@ void NPP_Print(NPP instance, NPPrint* platformPrint) { } -struct SoundPlay { - NPP instance; - ANPAudioTrack* track; - FILE* file; -}; - -static void audioCallback(ANPAudioEvent evt, void* user, ANPAudioBuffer* buffer) { - switch (evt) { - case kMoreData_ANPAudioEvent: { - SoundPlay* play = reinterpret_cast(user); - size_t amount = fread(buffer->bufferData, 1, buffer->size, play->file); - buffer->size = amount; - if (amount == 0) { - gSoundI.stop(play->track); - fclose(play->file); - play->file = NULL; - // need to notify our main thread to delete the track now - } - break; - } - default: - break; - } -} - -static ANPAudioTrack* createTrack(NPP instance, const char path[]) { - FILE* f = fopen(path, "r"); - gLogI.log(instance, kWarning_ANPLogType, "--- path %s FILE %p", path, f); - if (NULL == f) { - return NULL; - } - SoundPlay* play = new SoundPlay; - play->file = f; - play->track = gSoundI.newTrack(44100, kPCM16Bit_ANPSampleFormat, 2, audioCallback, play); - if (NULL == play->track) { - fclose(f); - delete play; - return NULL; - } - return play->track; -} - int16 NPP_HandleEvent(NPP instance, void* event) { PluginObject *obj = reinterpret_cast(instance->pdata); @@ -342,26 +300,12 @@ int16 NPP_HandleEvent(NPP instance, void* event) gLogI.log(instance, kDebug_ANPLogType, "---- %p Touch action=%d [%d %d]", instance, evt->data.touch.action, evt->data.touch.x, evt->data.touch.y); - if (kUp_ANPTouchAction == evt->data.touch.action) { - if (NULL == obj->track) { - obj->track = createTrack(instance, "/sdcard/sample.snd"); - } - if (obj->track) { - gLogI.log(instance, kDebug_ANPLogType, "track %p %d", - obj->track, gSoundI.isStopped(obj->track)); - if (gSoundI.isStopped(obj->track)) { - gSoundI.start(obj->track); - } else { - gSoundI.pause(obj->track); - } - } - } break; case kVisibleRect_ANPEventType: gLogI.log(instance, kDebug_ANPLogType, "---- %p VisibleRect [%d %d %d %d]", - instance, evt->data.visibleRect.x, evt->data.visibleRect.y, - evt->data.visibleRect.width, evt->data.visibleRect.height); + instance, evt->data.visibleRect.rect.left, evt->data.visibleRect.rect.top, + evt->data.visibleRect.rect.right, evt->data.visibleRect.rect.bottom); break; default: