/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-11 by Raw Material Software Ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License (Version 2), as published by the Free Software Foundation. A copy of the license is included in the JUCE distribution, or can be found online at www.gnu.org/licenses. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ END_JUCE_NAMESPACE extern juce::JUCEApplicationBase* juce_CreateApplication(); // (from START_JUCE_APPLICATION) BEGIN_JUCE_NAMESPACE //============================================================================== JUCE_JNI_CALLBACK (JuceAppActivity, launchApp, void, (JNIEnv* env, jobject activity, jstring appFile, jstring appDataDir)) { android.initialise (env, activity, appFile, appDataDir); DBG (SystemStats::getJUCEVersion()); JUCEApplicationBase::createInstance = &juce_CreateApplication; initialiseJuce_GUI(); JUCEApplication* app = dynamic_cast (JUCEApplicationBase::createInstance()); if (! app->initialiseApp (String::empty)) exit (0); } JUCE_JNI_CALLBACK (JuceAppActivity, quitApp, void, (JNIEnv* env, jobject activity)) { JUCEApplicationBase::appWillTerminateByForce(); android.shutdown (env); } //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ METHOD (drawBitmap, "drawBitmap", "([IIIFFIIZLandroid/graphics/Paint;)V") \ METHOD (getClipBounds, "getClipBounds", "()Landroid/graphics/Rect;") DECLARE_JNI_CLASS (CanvasMinimal, "android/graphics/Canvas"); #undef JNI_CLASS_MEMBERS //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ METHOD (setViewName, "setViewName", "(Ljava/lang/String;)V") \ METHOD (layout, "layout", "(IIII)V") \ METHOD (getLeft, "getLeft", "()I") \ METHOD (getTop, "getTop", "()I") \ METHOD (getWidth, "getWidth", "()I") \ METHOD (getHeight, "getHeight", "()I") \ METHOD (getLocationOnScreen, "getLocationOnScreen", "([I)V") \ METHOD (bringToFront, "bringToFront", "()V") \ METHOD (requestFocus, "requestFocus", "()Z") \ METHOD (setVisible, "setVisible", "(Z)V") \ METHOD (isVisible, "isVisible", "()Z") \ METHOD (hasFocus, "hasFocus", "()Z") \ METHOD (invalidate, "invalidate", "(IIII)V") \ METHOD (containsPoint, "containsPoint", "(II)Z") \ DECLARE_JNI_CLASS (ComponentPeerView, "com/juce/ComponentPeerView"); #undef JNI_CLASS_MEMBERS //============================================================================== class AndroidComponentPeer : public ComponentPeer { public: AndroidComponentPeer (Component* const component, const int windowStyleFlags) : ComponentPeer (component, windowStyleFlags), view (android.activity.callObjectMethod (JuceAppActivity.createNewView, component->isOpaque())), usingAndroidGraphics (false), fullScreen (false), sizeAllocated (0) { if (isFocused()) handleFocusGain(); } ~AndroidComponentPeer() { if (MessageManager::getInstance()->isThisTheMessageThread()) { android.activity.callVoidMethod (JuceAppActivity.deleteView, view.get()); } else { class ViewDeleter : public CallbackMessage { public: ViewDeleter (const GlobalRef& view_) : view (view_) { post(); } void messageCallback() { android.activity.callVoidMethod (JuceAppActivity.deleteView, view.get()); } private: GlobalRef view; }; new ViewDeleter (view); } view.clear(); } void* getNativeHandle() const { return (void*) view.get(); } void setVisible (bool shouldBeVisible) { if (MessageManager::getInstance()->isThisTheMessageThread()) { view.callVoidMethod (ComponentPeerView.setVisible, shouldBeVisible); } else { class VisibilityChanger : public CallbackMessage { public: VisibilityChanger (const GlobalRef& view_, bool shouldBeVisible_) : view (view_), shouldBeVisible (shouldBeVisible_) { post(); } void messageCallback() { view.callVoidMethod (ComponentPeerView.setVisible, shouldBeVisible); } private: GlobalRef view; bool shouldBeVisible; }; new VisibilityChanger (view, shouldBeVisible); } } void setTitle (const String& title) { view.callVoidMethod (ComponentPeerView.setViewName, javaString (title).get()); } void setPosition (int x, int y) { const Rectangle pos (getBounds()); setBounds (x, y, pos.getWidth(), pos.getHeight(), false); } void setSize (int w, int h) { const Rectangle pos (getBounds()); setBounds (pos.getX(), pos.getY(), w, h, false); } void setBounds (int x, int y, int w, int h, bool isNowFullScreen) { if (MessageManager::getInstance()->isThisTheMessageThread()) { fullScreen = isNowFullScreen; w = jmax (0, w); h = jmax (0, h); view.callVoidMethod (ComponentPeerView.layout, x, y, x + w, y + h); } else { class ViewMover : public CallbackMessage { public: ViewMover (const GlobalRef& view_, int x_, int y_, int w_, int h_) : view (view_), x (x_), y (y_), w (w_), h (h_) { post(); } void messageCallback() { view.callVoidMethod (ComponentPeerView.layout, x, y, x + w, y + h); } private: GlobalRef view; int x, y, w, h; }; new ViewMover (view, x, y, w, h); } } const Rectangle getBounds() const { return Rectangle (view.callIntMethod (ComponentPeerView.getLeft), view.callIntMethod (ComponentPeerView.getTop), view.callIntMethod (ComponentPeerView.getWidth), view.callIntMethod (ComponentPeerView.getHeight)); } const Point getScreenPosition() const { return Point (view.callIntMethod (ComponentPeerView.getLeft), view.callIntMethod (ComponentPeerView.getTop)); } const Point localToGlobal (const Point& relativePosition) { return relativePosition + getScreenPosition(); } const Point globalToLocal (const Point& screenPosition) { return screenPosition - getScreenPosition(); } void setMinimised (bool shouldBeMinimised) { // n/a } bool isMinimised() const { return false; } void setFullScreen (bool shouldBeFullScreen) { Rectangle r (shouldBeFullScreen ? Desktop::getInstance().getMainMonitorArea() : lastNonFullscreenBounds); if ((! shouldBeFullScreen) && r.isEmpty()) r = getBounds(); // (can't call the component's setBounds method because that'll reset our fullscreen flag) if (! r.isEmpty()) setBounds (r.getX(), r.getY(), r.getWidth(), r.getHeight(), shouldBeFullScreen); component->repaint(); } bool isFullScreen() const { return fullScreen; } void setIcon (const Image& newIcon) { // n/a } bool contains (const Point& position, bool trueIfInAChildWindow) const { return isPositiveAndBelow (position.getX(), component->getWidth()) && isPositiveAndBelow (position.getY(), component->getHeight()) && ((! trueIfInAChildWindow) || view.callBooleanMethod (ComponentPeerView.containsPoint, position.getX(), position.getY())); } const BorderSize getFrameSize() const { // TODO return BorderSize(); } bool setAlwaysOnTop (bool alwaysOnTop) { // TODO return false; } void toFront (bool makeActive) { view.callVoidMethod (ComponentPeerView.bringToFront); if (makeActive) grabFocus(); handleBroughtToFront(); } void toBehind (ComponentPeer* other) { // TODO } //============================================================================== void handleMouseDownCallback (float x, float y, int64 time) { lastMousePos.setXY ((int) x, (int) y); currentModifiers = currentModifiers.withoutMouseButtons(); handleMouseEvent (0, lastMousePos, currentModifiers, time); currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier); handleMouseEvent (0, lastMousePos, currentModifiers, time); } void handleMouseDragCallback (float x, float y, int64 time) { lastMousePos.setXY ((int) x, (int) y); handleMouseEvent (0, lastMousePos, currentModifiers, time); } void handleMouseUpCallback (float x, float y, int64 time) { lastMousePos.setXY ((int) x, (int) y); currentModifiers = currentModifiers.withoutMouseButtons(); handleMouseEvent (0, lastMousePos, currentModifiers, time); } //============================================================================== bool isFocused() const { return view.callBooleanMethod (ComponentPeerView.hasFocus); } void grabFocus() { view.callBooleanMethod (ComponentPeerView.requestFocus); } void handleFocusChangeCallback (bool hasFocus) { if (hasFocus) handleFocusGain(); else handleFocusLoss(); } void textInputRequired (const Point& position) { // TODO } //============================================================================== void handlePaintCallback (JNIEnv* env, jobject canvas) { #if USE_ANDROID_CANVAS if (usingAndroidGraphics) { AndroidLowLevelGraphicsContext g (canvas); handlePaint (g); } else #endif { jobject rect = env->CallObjectMethod (canvas, CanvasMinimal.getClipBounds); const int left = env->GetIntField (rect, RectClass.left); const int top = env->GetIntField (rect, RectClass.top); const int right = env->GetIntField (rect, RectClass.right); const int bottom = env->GetIntField (rect, RectClass.bottom); env->DeleteLocalRef (rect); const Rectangle clip (left, top, right - left, bottom - top); const int sizeNeeded = clip.getWidth() * clip.getHeight(); if (sizeAllocated < sizeNeeded) { buffer.clear(); sizeAllocated = sizeNeeded; buffer = GlobalRef (env->NewIntArray (sizeNeeded)); } jint* dest = env->GetIntArrayElements ((jintArray) buffer.get(), 0); if (dest != 0) { { Image temp (new PreallocatedImage (clip.getWidth(), clip.getHeight(), dest, ! component->isOpaque())); { JUCE_DEFAULT_SOFTWARE_RENDERER_CLASS g (temp); g.setOrigin (-clip.getX(), -clip.getY()); handlePaint (g); } } env->ReleaseIntArrayElements ((jintArray) buffer.get(), dest, 0); env->CallVoidMethod (canvas, CanvasMinimal.drawBitmap, (jintArray) buffer.get(), 0, clip.getWidth(), (jfloat) clip.getX(), (jfloat) clip.getY(), clip.getWidth(), clip.getHeight(), true, (jobject) 0); } } } void repaint (const Rectangle& area) { if (MessageManager::getInstance()->isThisTheMessageThread()) { view.callVoidMethod (ComponentPeerView.invalidate, area.getX(), area.getY(), area.getRight(), area.getBottom()); } else { class ViewRepainter : public CallbackMessage { public: ViewRepainter (const GlobalRef& view_, const Rectangle& area_) : view (view_), area (area_) { post(); } void messageCallback() { view.callVoidMethod (ComponentPeerView.invalidate, area.getX(), area.getY(), area.getRight(), area.getBottom()); } private: GlobalRef view; const Rectangle& area; }; new ViewRepainter (view, area); } } void performAnyPendingRepaintsNow() { // TODO } void setAlpha (float newAlpha) { // TODO } StringArray getAvailableRenderingEngines() { StringArray s (ComponentPeer::getAvailableRenderingEngines()); s.add ("Android Canvas Renderer"); return s; } #if USE_ANDROID_CANVAS int getCurrentRenderingEngine() const { return usingAndroidGraphics ? 1 : 0; } void setCurrentRenderingEngine (int index) { if (usingAndroidGraphics != (index > 0)) { usingAndroidGraphics = index > 0; component->repaint(); } } #endif //============================================================================== static AndroidComponentPeer* findPeerForJavaView (jobject viewToFind) { for (int i = getNumPeers(); --i >= 0;) { AndroidComponentPeer* const ap = static_cast (getPeer(i)); jassert (dynamic_cast (getPeer(i)) != 0); if (ap->view == viewToFind) return ap; } return nullptr; } static ModifierKeys currentModifiers; static Point lastMousePos; private: //============================================================================== GlobalRef view; GlobalRef buffer; bool usingAndroidGraphics, fullScreen; int sizeAllocated; class PreallocatedImage : public Image::SharedImage { public: //============================================================================== PreallocatedImage (const int width_, const int height_, jint* data_, bool hasAlpha_) : Image::SharedImage (Image::ARGB, width_, height_), data (data_), hasAlpha (hasAlpha_) { if (hasAlpha_) zeromem (data_, width * height * sizeof (jint)); } ~PreallocatedImage() { if (hasAlpha) { PixelARGB* pix = (PixelARGB*) data; for (int i = width * height; --i >= 0;) { pix->unpremultiply(); ++pix; } } } Image::ImageType getType() const { return Image::SoftwareImage; } LowLevelGraphicsContext* createLowLevelContext() { return new JUCE_DEFAULT_SOFTWARE_RENDERER_CLASS (Image (this)); } void initialiseBitmapData (Image::BitmapData& bm, int x, int y, Image::BitmapData::ReadWriteMode mode) { bm.lineStride = width * sizeof (jint); bm.pixelStride = sizeof (jint); bm.pixelFormat = Image::ARGB; bm.data = (uint8*) (data + x + y * width); } SharedImage* clone() { PreallocatedImage* s = new PreallocatedImage (width, height, 0, hasAlpha); s->allocatedData.malloc (sizeof (jint) * width * height); s->data = s->allocatedData; memcpy (s->data, data, sizeof (jint) * width * height); return s; } //============================================================================== private: jint* data; HeapBlock allocatedData; bool hasAlpha; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreallocatedImage); }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidComponentPeer); }; ModifierKeys AndroidComponentPeer::currentModifiers = 0; Point AndroidComponentPeer::lastMousePos; //============================================================================== #define JUCE_VIEW_CALLBACK(returnType, javaMethodName, params, juceMethodInvocation) \ JUCE_JNI_CALLBACK (ComponentPeerView, javaMethodName, returnType, params) \ { \ AndroidComponentPeer* const peer = AndroidComponentPeer::findPeerForJavaView (view); \ if (peer != nullptr) \ peer->juceMethodInvocation; \ } JUCE_VIEW_CALLBACK (void, handlePaint, (JNIEnv* env, jobject view, jobject canvas), handlePaintCallback (env, canvas)) JUCE_VIEW_CALLBACK (void, handleMouseDown, (JNIEnv*, jobject view, jfloat x, jfloat y, jlong time), handleMouseDownCallback ((float) x, (float) y, (int64) time)) JUCE_VIEW_CALLBACK (void, handleMouseDrag, (JNIEnv*, jobject view, jfloat x, jfloat y, jlong time), handleMouseDragCallback ((float) x, (float) y, (int64) time)) JUCE_VIEW_CALLBACK (void, handleMouseUp, (JNIEnv*, jobject view, jfloat x, jfloat y, jlong time), handleMouseUpCallback ((float) x, (float) y, (int64) time)) JUCE_VIEW_CALLBACK (void, viewSizeChanged, (JNIEnv*, jobject view), handleMovedOrResized()) JUCE_VIEW_CALLBACK (void, focusChanged, (JNIEnv*, jobject view, jboolean hasFocus), handleFocusChangeCallback (hasFocus)) //============================================================================== ComponentPeer* Component::createNewPeer (int styleFlags, void*) { return new AndroidComponentPeer (this, styleFlags); } //============================================================================== bool Desktop::canUseSemiTransparentWindows() noexcept { return true; } Desktop::DisplayOrientation Desktop::getCurrentOrientation() const { // TODO return upright; } void Desktop::createMouseInputSources() { // This creates a mouse input source for each possible finger for (int i = 0; i < 10; ++i) mouseSources.add (new MouseInputSource (i, false)); } Point MouseInputSource::getCurrentMousePosition() { return AndroidComponentPeer::lastMousePos; } void Desktop::setMousePosition (const Point& newPosition) { // not needed } //============================================================================== bool KeyPress::isKeyCurrentlyDown (const int keyCode) { // TODO return false; } void ModifierKeys::updateCurrentModifiers() noexcept { currentModifiers = AndroidComponentPeer::currentModifiers; } ModifierKeys ModifierKeys::getCurrentModifiersRealtime() noexcept { return AndroidComponentPeer::currentModifiers; } //============================================================================== bool Process::isForegroundProcess() { return true; // TODO } //============================================================================== void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType, const String& title, const String& message, Component* associatedComponent) { android.activity.callVoidMethod (JuceAppActivity.showMessageBox, javaString (title).get(), javaString (message).get(), (jlong) 0); } bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) { jassert (callback != 0); // on android, all alerts must be non-modal!! android.activity.callVoidMethod (JuceAppActivity.showOkCancelBox, javaString (title).get(), javaString (message).get(), (jlong) (pointer_sized_int) callback); return false; } int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) { jassert (callback != 0); // on android, all alerts must be non-modal!! android.activity.callVoidMethod (JuceAppActivity.showYesNoCancelBox, javaString (title).get(), javaString (message).get(), (jlong) (pointer_sized_int) callback); return 0; } JUCE_JNI_CALLBACK (JuceAppActivity, alertDismissed, void, (JNIEnv* env, jobject activity, jlong callbackAsLong, jint result)) { ModalComponentManager::Callback* callback = (ModalComponentManager::Callback*) callbackAsLong; if (callback != 0) callback->modalStateFinished (result); } //============================================================================== void Desktop::setScreenSaverEnabled (const bool isEnabled) { // TODO } bool Desktop::isScreenSaverEnabled() { return true; } //============================================================================== void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDisable, bool allowMenusAndBars) { // TODO } //============================================================================== void Desktop::getCurrentMonitorPositions (Array >& monitorCoords, const bool clipToWorkArea) { monitorCoords.add (Rectangle (0, 0, android.screenWidth, android.screenHeight)); } JUCE_JNI_CALLBACK (JuceAppActivity, setScreenSize, void, (JNIEnv* env, jobject activity, jint screenWidth, jint screenHeight)) { const bool isSystemInitialised = android.screenWidth != 0; android.screenWidth = screenWidth; android.screenHeight = screenHeight; if (isSystemInitialised) Desktop::getInstance().refreshMonitorSizes(); } //============================================================================== Image juce_createIconForFile (const File& file) { return Image::null; } //============================================================================== void* MouseCursor::createMouseCursorFromImage (const Image&, int, int) { return nullptr; } void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorType) { return nullptr; } void MouseCursor::deleteMouseCursor (void* const /*cursorHandle*/, const bool /*isStandard*/) {} //============================================================================== void MouseCursor::showInWindow (ComponentPeer*) const {} void MouseCursor::showInAllWindows() const {} //============================================================================== bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMove) { return false; } bool DragAndDropContainer::performExternalDragDropOfText (const String& text) { return false; } //============================================================================== void LookAndFeel::playAlertSound() { } //============================================================================== void SystemClipboard::copyTextToClipboard (const String& text) { const LocalRef t (javaString (text)); android.activity.callVoidMethod (JuceAppActivity.setClipboardContent, t.get()); } String SystemClipboard::getTextFromClipboard() { const LocalRef text ((jstring) android.activity.callObjectMethod (JuceAppActivity.getClipboardContent)); return juceString (text); } //============================================================================== const int extendedKeyModifier = 0x10000; const int KeyPress::spaceKey = ' '; const int KeyPress::returnKey = 0x0d; const int KeyPress::escapeKey = 0x1b; const int KeyPress::backspaceKey = 0x7f; const int KeyPress::leftKey = extendedKeyModifier + 1; const int KeyPress::rightKey = extendedKeyModifier + 2; const int KeyPress::upKey = extendedKeyModifier + 3; const int KeyPress::downKey = extendedKeyModifier + 4; const int KeyPress::pageUpKey = extendedKeyModifier + 5; const int KeyPress::pageDownKey = extendedKeyModifier + 6; const int KeyPress::endKey = extendedKeyModifier + 7; const int KeyPress::homeKey = extendedKeyModifier + 8; const int KeyPress::deleteKey = extendedKeyModifier + 9; const int KeyPress::insertKey = -1; const int KeyPress::tabKey = 9; const int KeyPress::F1Key = extendedKeyModifier + 10; const int KeyPress::F2Key = extendedKeyModifier + 11; const int KeyPress::F3Key = extendedKeyModifier + 12; const int KeyPress::F4Key = extendedKeyModifier + 13; const int KeyPress::F5Key = extendedKeyModifier + 14; const int KeyPress::F6Key = extendedKeyModifier + 16; const int KeyPress::F7Key = extendedKeyModifier + 17; const int KeyPress::F8Key = extendedKeyModifier + 18; const int KeyPress::F9Key = extendedKeyModifier + 19; const int KeyPress::F10Key = extendedKeyModifier + 20; const int KeyPress::F11Key = extendedKeyModifier + 21; const int KeyPress::F12Key = extendedKeyModifier + 22; const int KeyPress::F13Key = extendedKeyModifier + 23; const int KeyPress::F14Key = extendedKeyModifier + 24; const int KeyPress::F15Key = extendedKeyModifier + 25; const int KeyPress::F16Key = extendedKeyModifier + 26; const int KeyPress::numberPad0 = extendedKeyModifier + 27; const int KeyPress::numberPad1 = extendedKeyModifier + 28; const int KeyPress::numberPad2 = extendedKeyModifier + 29; const int KeyPress::numberPad3 = extendedKeyModifier + 30; const int KeyPress::numberPad4 = extendedKeyModifier + 31; const int KeyPress::numberPad5 = extendedKeyModifier + 32; const int KeyPress::numberPad6 = extendedKeyModifier + 33; const int KeyPress::numberPad7 = extendedKeyModifier + 34; const int KeyPress::numberPad8 = extendedKeyModifier + 35; const int KeyPress::numberPad9 = extendedKeyModifier + 36; const int KeyPress::numberPadAdd = extendedKeyModifier + 37; const int KeyPress::numberPadSubtract = extendedKeyModifier + 38; const int KeyPress::numberPadMultiply = extendedKeyModifier + 39; const int KeyPress::numberPadDivide = extendedKeyModifier + 40; const int KeyPress::numberPadSeparator = extendedKeyModifier + 41; const int KeyPress::numberPadDecimalPoint = extendedKeyModifier + 42; const int KeyPress::numberPadEquals = extendedKeyModifier + 43; const int KeyPress::numberPadDelete = extendedKeyModifier + 44; const int KeyPress::playKey = extendedKeyModifier + 45; const int KeyPress::stopKey = extendedKeyModifier + 46; const int KeyPress::fastForwardKey = extendedKeyModifier + 47; const int KeyPress::rewindKey = extendedKeyModifier + 48;