mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Android: add WebBrowserComponent implementation.
This commit is contained in:
parent
e2ae08e111
commit
073921445c
16 changed files with 1014 additions and 80 deletions
|
|
@ -1007,6 +1007,51 @@ private:
|
|||
.replace ("JuceAppActivity", className);
|
||||
}
|
||||
|
||||
String juceWebViewImports, juceWebViewCodeNative, juceWebViewCode;
|
||||
|
||||
if (androidMinimumSDK.get().getIntValue() >= 23)
|
||||
juceWebViewImports << "import android.webkit.WebResourceError;" << newLine;
|
||||
|
||||
if (androidMinimumSDK.get().getIntValue() >= 21)
|
||||
juceWebViewImports << "import android.webkit.WebResourceRequest;" << newLine;
|
||||
|
||||
if (androidMinimumSDK.get().getIntValue() >= 11)
|
||||
juceWebViewImports << "import android.webkit.WebResourceResponse;" << newLine;
|
||||
|
||||
auto javaWebViewFile = javaSourceFolder.getChildFile ("AndroidWebView.java");
|
||||
auto juceWebViewCodeAll = javaWebViewFile.loadFileAsString();
|
||||
|
||||
if (androidMinimumSDK.get().getIntValue() <= 10)
|
||||
{
|
||||
juceWebViewCode << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewApi1_10", false, false)
|
||||
.upToFirstOccurrenceOf ("WebViewApi1_10$$", false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (androidMinimumSDK.get().getIntValue() >= 23)
|
||||
{
|
||||
juceWebViewCodeNative << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewNativeApi23", false, false)
|
||||
.upToFirstOccurrenceOf ("WebViewNativeApi23$$", false, false);
|
||||
|
||||
juceWebViewCode << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewApi23", false, false)
|
||||
.upToFirstOccurrenceOf ("WebViewApi23$$", false, false);
|
||||
}
|
||||
|
||||
if (androidMinimumSDK.get().getIntValue() >= 21)
|
||||
{
|
||||
juceWebViewCodeNative << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewNativeApi21", false, false)
|
||||
.upToFirstOccurrenceOf ("WebViewNativeApi21$$", false, false);
|
||||
|
||||
juceWebViewCode << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewApi21", false, false)
|
||||
.upToFirstOccurrenceOf ("WebViewApi21$$", false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
juceWebViewCode << juceWebViewCodeAll.fromFirstOccurrenceOf ("$$WebViewApi11_20", false, false)
|
||||
.upToFirstOccurrenceOf ("WebViewApi11_20$$", false, false);
|
||||
}
|
||||
}
|
||||
|
||||
auto javaSourceFile = javaSourceFolder.getChildFile ("JuceAppActivity.java");
|
||||
auto javaSourceLines = StringArray::fromLines (javaSourceFile.loadFileAsString());
|
||||
|
||||
|
|
@ -1021,6 +1066,12 @@ private:
|
|||
newFile << juceMidiCode;
|
||||
else if (line.contains ("$$JuceAndroidRuntimePermissionsCode$$"))
|
||||
newFile << juceRuntimePermissionsCode;
|
||||
else if (line.contains ("$$JuceAndroidWebViewImports$$"))
|
||||
newFile << juceWebViewImports;
|
||||
else if (line.contains ("$$JuceAndroidWebViewNativeCode$$"))
|
||||
newFile << juceWebViewCodeNative;
|
||||
else if (line.contains ("$$JuceAndroidWebViewCode$$"))
|
||||
newFile << juceWebViewCode;
|
||||
else
|
||||
newFile << line.replace ("JuceAppActivity", className)
|
||||
.replace ("package com.juce;", "package " + package + ";") << newLine;
|
||||
|
|
|
|||
69
modules/juce_core/native/java/AndroidWebView.java
Normal file
69
modules/juce_core/native/java/AndroidWebView.java
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
$$WebViewNativeApi23 private native void webViewReceivedError (long host, WebView view, WebResourceRequest request, WebResourceError error);WebViewNativeApi23$$
|
||||
$$WebViewNativeApi21 private native void webViewReceivedHttpError (long host, WebView view, WebResourceRequest request, WebResourceResponse errorResponse);WebViewNativeApi21$$
|
||||
|
||||
$$WebViewApi1_10
|
||||
@Override
|
||||
public void onPageStarted (WebView view, String url, Bitmap favicon)
|
||||
{
|
||||
if (host != 0)
|
||||
webViewPageLoadStarted (host, view, url);
|
||||
}
|
||||
WebViewApi1_10$$
|
||||
|
||||
$$WebViewApi11_20
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest (WebView view, String url)
|
||||
{
|
||||
synchronized (hostLock)
|
||||
{
|
||||
if (host != 0)
|
||||
{
|
||||
boolean shouldLoad = webViewPageLoadStarted (host, view, url);
|
||||
|
||||
if (shouldLoad)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return new WebResourceResponse ("text/html", null, null);
|
||||
}
|
||||
WebViewApi11_20$$
|
||||
|
||||
$$WebViewApi21
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)
|
||||
{
|
||||
synchronized (hostLock)
|
||||
{
|
||||
if (host != 0)
|
||||
{
|
||||
boolean shouldLoad = webViewPageLoadStarted (host, view, request.getUrl().toString());
|
||||
|
||||
if (shouldLoad)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return new WebResourceResponse ("text/html", null, null);
|
||||
}
|
||||
WebViewApi21$$
|
||||
|
||||
$$WebViewApi23
|
||||
@Override
|
||||
public void onReceivedError (WebView view, WebResourceRequest request, WebResourceError error)
|
||||
{
|
||||
if (host == 0)
|
||||
return;
|
||||
|
||||
webViewReceivedError (host, view, request, error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedHttpError (WebView view, WebResourceRequest request, WebResourceResponse errorResponse)
|
||||
{
|
||||
if (host == 0)
|
||||
return;
|
||||
|
||||
webViewReceivedHttpError (host, view, request, errorResponse);
|
||||
}
|
||||
WebViewApi23$$
|
||||
|
|
@ -30,10 +30,12 @@ import android.content.Intent;
|
|||
import android.content.res.Configuration;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.http.SslError;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.Environment;
|
||||
import android.view.*;
|
||||
|
|
@ -47,6 +49,11 @@ import android.text.InputType;
|
|||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.webkit.SslErrorHandler;
|
||||
import android.webkit.WebChromeClient;
|
||||
$$JuceAndroidWebViewImports$$ // If you get an error here, you need to re-save your project with the Projucer!
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import java.lang.Runnable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.*;
|
||||
|
|
@ -1336,6 +1343,78 @@ public class JuceAppActivity extends Activity
|
|||
startActivity (new Intent (Intent.ACTION_VIEW, Uri.parse (url)));
|
||||
}
|
||||
|
||||
private native boolean webViewPageLoadStarted (long host, WebView view, String url);
|
||||
private native void webViewPageLoadFinished (long host, WebView view, String url);
|
||||
$$JuceAndroidWebViewNativeCode$$ // If you get an error here, you need to re-save your project with the Projucer!
|
||||
private native void webViewReceivedSslError (long host, WebView view, SslErrorHandler handler, SslError error);
|
||||
private native void webViewCloseWindowRequest (long host, WebView view);
|
||||
private native void webViewCreateWindowRequest (long host, WebView view);
|
||||
|
||||
//==============================================================================
|
||||
public class JuceWebViewClient extends WebViewClient
|
||||
{
|
||||
public JuceWebViewClient (long hostToUse)
|
||||
{
|
||||
host = hostToUse;
|
||||
}
|
||||
|
||||
public void hostDeleted()
|
||||
{
|
||||
synchronized (hostLock)
|
||||
{
|
||||
host = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished (WebView view, String url)
|
||||
{
|
||||
if (host == 0)
|
||||
return;
|
||||
|
||||
webViewPageLoadFinished (host, view, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error)
|
||||
{
|
||||
if (host == 0)
|
||||
return;
|
||||
|
||||
webViewReceivedSslError (host, view, handler, error);
|
||||
}
|
||||
$$JuceAndroidWebViewCode$$ // If you get an error here, you need to re-save your project with the Projucer!
|
||||
|
||||
private long host;
|
||||
private final Object hostLock = new Object();
|
||||
}
|
||||
|
||||
public class JuceWebChromeClient extends WebChromeClient
|
||||
{
|
||||
public JuceWebChromeClient (long hostToUse)
|
||||
{
|
||||
host = hostToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseWindow (WebView window)
|
||||
{
|
||||
webViewCloseWindowRequest (host, window);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateWindow (WebView view, boolean isDialog,
|
||||
boolean isUserGesture, Message resultMsg)
|
||||
{
|
||||
webViewCreateWindowRequest (host, view);
|
||||
return false;
|
||||
}
|
||||
|
||||
private long host;
|
||||
private final Object hostLock = new Object();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
public static final String getLocaleValue (boolean isRegion)
|
||||
{
|
||||
java.util.Locale locale = java.util.Locale.getDefault();
|
||||
|
|
|
|||
|
|
@ -421,6 +421,58 @@ DECLARE_JNI_CLASS (JavaObject, "java/lang/Object");
|
|||
DECLARE_JNI_CLASS (Intent, "android/content/Intent");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
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 (getParent, "getParent", "()Landroid/view/ViewParent;") \
|
||||
METHOD (bringToFront, "bringToFront", "()V") \
|
||||
METHOD (requestFocus, "requestFocus", "()Z") \
|
||||
METHOD (hasFocus, "hasFocus", "()Z") \
|
||||
METHOD (invalidate, "invalidate", "(IIII)V") \
|
||||
METHOD (setVisibility, "setVisibility", "(I)V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidView, "android/view/View");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (addView, "addView", "(Landroid/view/View;)V") \
|
||||
METHOD (removeView, "removeView", "(Landroid/view/View;)V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidViewGroup, "android/view/ViewGroup")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (toString, "toString", "()Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (JavaCharSequence, "java/lang/CharSequence");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (get, "get", "(Ljava/lang/Object;)Ljava/lang/Object;") \
|
||||
METHOD (keySet, "keySet", "()Ljava/util/Set;") \
|
||||
METHOD (put, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")
|
||||
|
||||
DECLARE_JNI_CLASS (JavaMap, "java/util/Map");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (constructor, "<init>", "()V") \
|
||||
METHOD (constructorWithCapacity, "<init>", "(I)V")
|
||||
|
||||
DECLARE_JNI_CLASS (JavaHashMap, "java/util/HashMap");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (concat, "concat", "(Ljava/lang/String;)Ljava/lang/String;") \
|
||||
METHOD (getBytes, "getBytes", "()[B")
|
||||
|
||||
DECLARE_JNI_CLASS (JavaString, "java/lang/String");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
class AndroidInterfaceImplementer;
|
||||
|
||||
|
|
|
|||
|
|
@ -188,21 +188,11 @@ DECLARE_JNI_CLASS (CanvasMinimal, "android/graphics/Canvas");
|
|||
//==============================================================================
|
||||
#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") \
|
||||
METHOD (showKeyboard, "showKeyboard", "(Ljava/lang/String;)V") \
|
||||
METHOD (setSystemUiVisibility, "setSystemUiVisibilityCompat", "(I)V") \
|
||||
METHOD (setSystemUiVisibilityCompat, "setSystemUiVisibilityCompat", "(I)V") \
|
||||
|
||||
DECLARE_JNI_CLASS (ComponentPeerView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
|
@ -302,7 +292,7 @@ public:
|
|||
if (MessageManager::getInstance()->isThisTheMessageThread())
|
||||
{
|
||||
fullScreen = isNowFullScreen;
|
||||
view.callVoidMethod (ComponentPeerView.layout,
|
||||
view.callVoidMethod (AndroidView.layout,
|
||||
r.getX(), r.getY(), r.getRight(), r.getBottom());
|
||||
}
|
||||
else
|
||||
|
|
@ -314,7 +304,7 @@ public:
|
|||
|
||||
void messageCallback() override
|
||||
{
|
||||
view.callVoidMethod (ComponentPeerView.layout,
|
||||
view.callVoidMethod (AndroidView.layout,
|
||||
bounds.getX(), bounds.getY(), bounds.getRight(), bounds.getBottom());
|
||||
}
|
||||
|
||||
|
|
@ -329,10 +319,10 @@ public:
|
|||
|
||||
Rectangle<int> getBounds() const override
|
||||
{
|
||||
return (Rectangle<float> (view.callIntMethod (ComponentPeerView.getLeft),
|
||||
view.callIntMethod (ComponentPeerView.getTop),
|
||||
view.callIntMethod (ComponentPeerView.getWidth),
|
||||
view.callIntMethod (ComponentPeerView.getHeight)) / scale).toNearestInt();
|
||||
return (Rectangle<float> (view.callIntMethod (AndroidView.getLeft),
|
||||
view.callIntMethod (AndroidView.getTop),
|
||||
view.callIntMethod (AndroidView.getWidth),
|
||||
view.callIntMethod (AndroidView.getHeight)) / scale).toNearestInt();
|
||||
}
|
||||
|
||||
void handleScreenSizeChange() override
|
||||
|
|
@ -345,8 +335,8 @@ public:
|
|||
|
||||
Point<float> getScreenPosition() const
|
||||
{
|
||||
return Point<float> (view.callIntMethod (ComponentPeerView.getLeft),
|
||||
view.callIntMethod (ComponentPeerView.getTop)) / scale;
|
||||
return Point<float> (view.callIntMethod (AndroidView.getLeft),
|
||||
view.callIntMethod (AndroidView.getTop)) / scale;
|
||||
}
|
||||
|
||||
Point<float> localToGlobal (Point<float> relativePosition) override
|
||||
|
|
@ -393,7 +383,7 @@ public:
|
|||
SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096
|
||||
};
|
||||
|
||||
view.callVoidMethod (ComponentPeerView.setSystemUiVisibility,
|
||||
view.callVoidMethod (ComponentPeerView.setSystemUiVisibilityCompat,
|
||||
hidden ? (jint) (SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
|
||||
: (jint) (SYSTEM_UI_FLAG_VISIBLE));
|
||||
|
||||
|
|
@ -471,7 +461,7 @@ public:
|
|||
// Avoid calling bringToFront excessively: it's very slow
|
||||
if (frontWindow != this)
|
||||
{
|
||||
view.callVoidMethod (ComponentPeerView.bringToFront);
|
||||
view.callVoidMethod (AndroidView.bringToFront);
|
||||
|
||||
frontWindow = this;
|
||||
}
|
||||
|
|
@ -547,7 +537,7 @@ public:
|
|||
bool isFocused() const override
|
||||
{
|
||||
if (view != nullptr)
|
||||
return view.callBooleanMethod (ComponentPeerView.hasFocus);
|
||||
return view.callBooleanMethod (AndroidView.hasFocus);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -555,7 +545,7 @@ public:
|
|||
void grabFocus() override
|
||||
{
|
||||
if (view != nullptr)
|
||||
view.callBooleanMethod (ComponentPeerView.requestFocus);
|
||||
view.callBooleanMethod (AndroidView.requestFocus);
|
||||
}
|
||||
|
||||
void handleFocusChangeCallback (bool hasFocus)
|
||||
|
|
@ -645,7 +635,7 @@ public:
|
|||
|
||||
if (MessageManager::getInstance()->isThisTheMessageThread())
|
||||
{
|
||||
view.callVoidMethod (ComponentPeerView.invalidate, area.getX(), area.getY(), area.getRight(), area.getBottom());
|
||||
view.callVoidMethod (AndroidView.invalidate, area.getX(), area.getY(), area.getRight(), area.getBottom());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -656,7 +646,7 @@ public:
|
|||
|
||||
void messageCallback() override
|
||||
{
|
||||
view.callVoidMethod (ComponentPeerView.invalidate, area.getX(), area.getY(),
|
||||
view.callVoidMethod (AndroidView.invalidate, area.getX(), area.getY(),
|
||||
area.getRight(), area.getBottom());
|
||||
}
|
||||
|
||||
|
|
|
|||
79
modules/juce_gui_extra/embedding/juce_AndroidViewComponent.h
Normal file
79
modules/juce_gui_extra/embedding/juce_AndroidViewComponent.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_ANDROID || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An Android-specific class that can create and embed a View inside itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use setView() to assign a View to it. The view will then be
|
||||
moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the view is a native object, it'll obliterate any
|
||||
juce components that may overlap this component, but that's life.
|
||||
*/
|
||||
class JUCE_API AndroidViewComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
AndroidViewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~AndroidViewComponent();
|
||||
|
||||
/** Assigns a View to this peer.
|
||||
|
||||
The view will be retained and released by this component for as long as
|
||||
it is needed. To remove the current view, just call setView (nullptr).
|
||||
*/
|
||||
void setView (void* uiView);
|
||||
|
||||
/** Returns the current View. */
|
||||
void* getView() const;
|
||||
|
||||
/** Resizes this component to fit the view that it contains. */
|
||||
void resizeToFitView();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
ScopedPointer<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidViewComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -163,6 +163,8 @@
|
|||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
#include "native/juce_AndroidViewComponent.cpp"
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_android_WebBrowserComponent.cpp"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@
|
|||
#include "code_editor/juce_XMLCodeTokeniser.h"
|
||||
#include "code_editor/juce_LuaCodeTokeniser.h"
|
||||
#include "embedding/juce_ActiveXControlComponent.h"
|
||||
#include "embedding/juce_AndroidViewComponent.h"
|
||||
#include "embedding/juce_NSViewComponent.h"
|
||||
#include "embedding/juce_UIViewComponent.h"
|
||||
#include "embedding/juce_XEmbedComponent.h"
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ public:
|
|||
private:
|
||||
//==============================================================================
|
||||
class Pimpl;
|
||||
Pimpl* browser;
|
||||
ScopedPointer<Pimpl> browser;
|
||||
bool blankPageShown, unloadPageWhenBrowserIsHidden;
|
||||
String lastURL;
|
||||
StringArray lastHeaders;
|
||||
|
|
@ -145,6 +145,10 @@ private:
|
|||
void reloadLastURL();
|
||||
void checkWindowAssociation();
|
||||
|
||||
#if JUCE_ANDROID
|
||||
friend bool juce_webViewPageLoadStarted (WebBrowserComponent*, const String&);
|
||||
#endif
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserComponent)
|
||||
};
|
||||
|
||||
|
|
|
|||
162
modules/juce_gui_extra/native/juce_AndroidViewComponent.cpp
Normal file
162
modules/juce_gui_extra/native/juce_AndroidViewComponent.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class AndroidViewComponent::Pimpl : public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
Pimpl (jobject v, Component& comp)
|
||||
: ComponentMovementWatcher (&comp),
|
||||
view (v),
|
||||
owner (comp)
|
||||
{
|
||||
if (owner.isShowing())
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
removeFromParent();
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
|
||||
{
|
||||
auto* topComp = owner.getTopLevelComponent();
|
||||
|
||||
if (topComp->getPeer() != nullptr)
|
||||
{
|
||||
auto pos = topComp->getLocalPoint (&owner, Point<int>());
|
||||
|
||||
Rectangle<int> r (pos.x, pos.y, owner.getWidth(), owner.getHeight());
|
||||
r *= Desktop::getInstance().getDisplays().getMainDisplay().scale;
|
||||
|
||||
getEnv()->CallVoidMethod (view, AndroidView.layout, r.getX(), r.getY(),
|
||||
r.getRight(), r.getBottom());
|
||||
}
|
||||
}
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
auto* peer = owner.getPeer();
|
||||
|
||||
if (currentPeer != peer)
|
||||
{
|
||||
removeFromParent();
|
||||
|
||||
currentPeer = peer;
|
||||
|
||||
addToParent();
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
VISIBLE = 0,
|
||||
INVISIBLE = 4
|
||||
};
|
||||
|
||||
getEnv()->CallVoidMethod (view, AndroidView.setVisibility, owner.isShowing() ? VISIBLE : INVISIBLE);
|
||||
}
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
Rectangle<int> getViewBounds() const
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
int width = env->CallIntMethod (view, AndroidView.getWidth);
|
||||
int height = env->CallIntMethod (view, AndroidView.getHeight);
|
||||
|
||||
return Rectangle<int> (width, height);
|
||||
}
|
||||
|
||||
GlobalRef view;
|
||||
|
||||
private:
|
||||
void addToParent()
|
||||
{
|
||||
if (currentPeer != nullptr)
|
||||
{
|
||||
jobject peerView = (jobject) currentPeer->getNativeHandle();
|
||||
|
||||
// Assuming a parent is always of ViewGroup type
|
||||
getEnv()->CallVoidMethod (peerView, AndroidViewGroup.addView, view.get());
|
||||
|
||||
componentMovedOrResized (false, false);
|
||||
}
|
||||
}
|
||||
|
||||
void removeFromParent()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
auto parentView = env->CallObjectMethod (view, AndroidView.getParent);
|
||||
|
||||
if (parentView != 0)
|
||||
{
|
||||
// Assuming a parent is always of ViewGroup type
|
||||
env->CallVoidMethod (parentView, AndroidViewGroup.removeView, view.get());
|
||||
}
|
||||
}
|
||||
|
||||
Component& owner;
|
||||
ComponentPeer* currentPeer = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AndroidViewComponent::AndroidViewComponent() {}
|
||||
AndroidViewComponent::~AndroidViewComponent() {}
|
||||
|
||||
void AndroidViewComponent::setView (void* const view)
|
||||
{
|
||||
if (view != getView())
|
||||
{
|
||||
pimpl = nullptr;
|
||||
|
||||
if (view != nullptr)
|
||||
pimpl = new Pimpl ((jobject) view, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void* AndroidViewComponent::getView() const
|
||||
{
|
||||
return pimpl == nullptr ? nullptr : (void*) pimpl->view;
|
||||
}
|
||||
|
||||
void AndroidViewComponent::resizeToFitView()
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
setBounds (pimpl->getViewBounds());
|
||||
}
|
||||
|
||||
void AndroidViewComponent::paint (Graphics&) {}
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -73,12 +73,6 @@ DECLARE_JNI_CLASS (BitmapConfig, "android/graphics/Bitmap$Config");
|
|||
DECLARE_JNI_CLASS (Bundle, "android/os/Bundle");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (toString, "toString", "()Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (CharSequence, "java/lang/CharSequence");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (hasNext, "hasNext", "()Z") \
|
||||
METHOD (next, "next", "()Ljava/lang/Object;")
|
||||
|
|
@ -86,19 +80,6 @@ DECLARE_JNI_CLASS (CharSequence, "java/lang/CharSequence");
|
|||
DECLARE_JNI_CLASS (Iterator, "java/util/Iterator");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (get, "get", "(Ljava/lang/Object;)Ljava/lang/Object;") \
|
||||
METHOD (keySet, "keySet", "()Ljava/util/Set;")
|
||||
|
||||
DECLARE_JNI_CLASS (JavaMap, "java/util/Map");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (concat, "concat", "(Ljava/lang/String;)Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (JavaString, "java/lang/String");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#if __ANDROID_API__ >= 26
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/CharSequence;I)V") \
|
||||
|
|
@ -529,7 +510,7 @@ struct PushNotifications::Pimpl
|
|||
if (remoteInputResult.get() != 0)
|
||||
{
|
||||
auto charSequence = LocalRef<jobject> (env->CallObjectMethod (remoteInputResult, Bundle.getCharSequence, resultKeyString.get()));
|
||||
auto responseString = LocalRef<jstring> ((jstring) env->CallObjectMethod (charSequence, CharSequence.toString));
|
||||
auto responseString = LocalRef<jstring> ((jstring) env->CallObjectMethod (charSequence, JavaCharSequence.toString));
|
||||
|
||||
owner.listeners.call (&PushNotifications::Listener::handleNotificationAction,
|
||||
true,
|
||||
|
|
|
|||
|
|
@ -27,16 +27,360 @@
|
|||
namespace juce
|
||||
{
|
||||
|
||||
WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
|
||||
: browser (nullptr),
|
||||
blankPageShown (false),
|
||||
unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
|
||||
{
|
||||
// Unfortunately, WebBrowserComponent is not implemented for Android yet!
|
||||
// This is just a stub implementation without any useful functionality.
|
||||
jassertfalse;
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (constructor, "<init>", "(Landroid/content/Context;)V") \
|
||||
METHOD (getSettings, "getSettings", "()Landroid/webkit/WebSettings;") \
|
||||
METHOD (goBack, "goBack", "()V") \
|
||||
METHOD (goForward, "goForward", "()V") \
|
||||
METHOD (loadDataWithBaseURL, "loadDataWithBaseURL", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V") \
|
||||
METHOD (loadUrl, "loadUrl", "(Ljava/lang/String;Ljava/util/Map;)V") \
|
||||
METHOD (postUrl, "postUrl", "(Ljava/lang/String;[B)V") \
|
||||
METHOD (reload, "reload", "()V") \
|
||||
METHOD (setWebChromeClient, "setWebChromeClient", "(Landroid/webkit/WebChromeClient;)V") \
|
||||
METHOD (setWebViewClient, "setWebViewClient", "(Landroid/webkit/WebViewClient;)V") \
|
||||
METHOD (stopLoading, "stopLoading", "()V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidWebView, "android/webkit/WebView")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (constructor, "<init>", "()V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidWebChromeClient, "android/webkit/WebChromeClient");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (constructor, "<init>", "()V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidWebViewClient, "android/webkit/WebViewClient");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
STATICMETHOD (getInstance, "getInstance", "()Landroid/webkit/CookieManager;")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidCookieManager, "android/webkit/CookieManager");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (constructor, "<init>", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH ";J)V")
|
||||
|
||||
DECLARE_JNI_CLASS (JuceWebChromeClient, JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceWebChromeClient");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (constructor, "<init>", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH ";J)V") \
|
||||
METHOD (hostDeleted, "hostDeleted", "()V")
|
||||
|
||||
DECLARE_JNI_CLASS (JuceWebViewClient, JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceWebViewClient");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (setBuiltInZoomControls, "setBuiltInZoomControls", "(Z)V") \
|
||||
METHOD (setDisplayZoomControls, "setDisplayZoomControls", "(Z)V") \
|
||||
METHOD (setJavaScriptEnabled, "setJavaScriptEnabled", "(Z)V") \
|
||||
METHOD (setSupportMultipleWindows, "setSupportMultipleWindows", "(Z)V")
|
||||
|
||||
DECLARE_JNI_CLASS (WebSettings, "android/webkit/WebSettings");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (toString, "toString", "()Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (SslError, "android/net/http/SslError")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
STATICMETHOD (encode, "encode", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (URLEncoder, "java/net/URLEncoder")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
class WebBrowserComponent::Pimpl : public AndroidViewComponent,
|
||||
public AsyncUpdater
|
||||
{
|
||||
public:
|
||||
Pimpl (WebBrowserComponent& o)
|
||||
: owner (o)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
setView (env->NewObject (AndroidWebView, AndroidWebView.constructor, android.activity.get()));
|
||||
|
||||
auto settings = LocalRef<jobject> (env->CallObjectMethod ((jobject) getView(), AndroidWebView.getSettings));
|
||||
env->CallVoidMethod (settings, WebSettings.setJavaScriptEnabled, true);
|
||||
env->CallVoidMethod (settings, WebSettings.setBuiltInZoomControls, true);
|
||||
env->CallVoidMethod (settings, WebSettings.setDisplayZoomControls, false);
|
||||
env->CallVoidMethod (settings, WebSettings.setSupportMultipleWindows, true);
|
||||
|
||||
juceWebChromeClient = GlobalRef (LocalRef<jobject> (env->NewObject (JuceWebChromeClient, JuceWebChromeClient.constructor,
|
||||
android.activity.get(),
|
||||
reinterpret_cast<jlong>(&owner))));
|
||||
env->CallVoidMethod ((jobject) getView(), AndroidWebView.setWebChromeClient, juceWebChromeClient.get());
|
||||
|
||||
juceWebViewClient = GlobalRef (LocalRef<jobject> (env->NewObject (JuceWebViewClient, JuceWebViewClient.constructor,
|
||||
android.activity.get(),
|
||||
reinterpret_cast<jlong>(&owner))));
|
||||
env->CallVoidMethod ((jobject) getView(), AndroidWebView.setWebViewClient, juceWebViewClient.get());
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
env->CallVoidMethod ((jobject) getView(), AndroidWebView.stopLoading);
|
||||
|
||||
auto defaultChromeClient = LocalRef<jobject> (env->NewObject (AndroidWebChromeClient, AndroidWebChromeClient.constructor));
|
||||
auto defaultViewClient = LocalRef<jobject> (env->NewObject (AndroidWebViewClient, AndroidWebViewClient .constructor));
|
||||
|
||||
env->CallVoidMethod ((jobject) getView(), AndroidWebView.setWebChromeClient, defaultChromeClient.get());
|
||||
env->CallVoidMethod ((jobject) getView(), AndroidWebView.setWebViewClient, defaultViewClient .get());
|
||||
|
||||
masterReference.clear();
|
||||
|
||||
// if other Java thread is waiting for us to respond to page load request
|
||||
// wake it up immediately (false answer will be sent), so that it releases
|
||||
// the lock we need when calling hostDeleted.
|
||||
responseReadyEvent.signal();
|
||||
|
||||
env->CallVoidMethod (juceWebViewClient, JuceWebViewClient.hostDeleted);
|
||||
}
|
||||
|
||||
void goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
if (headers == nullptr && postData == nullptr)
|
||||
{
|
||||
env->CallVoidMethod ((jobject) getView(), AndroidWebView.loadUrl, javaString (url).get(), 0);
|
||||
}
|
||||
else if (headers != nullptr && postData == nullptr)
|
||||
{
|
||||
auto headersMap = LocalRef<jobject> (env->NewObject (JavaHashMap,
|
||||
JavaHashMap.constructorWithCapacity,
|
||||
headers->size()));
|
||||
|
||||
for (const auto& header : *headers)
|
||||
{
|
||||
auto name = header.upToFirstOccurrenceOf (":", false, false).trim();
|
||||
auto value = header.fromFirstOccurrenceOf (":", false, false).trim();
|
||||
|
||||
env->CallObjectMethod (headersMap, JavaMap.put,
|
||||
javaString (name).get(),
|
||||
javaString (value).get());
|
||||
}
|
||||
|
||||
env->CallVoidMethod ((jobject) getView(), AndroidWebView.loadUrl,
|
||||
javaString (url).get(), headersMap.get());
|
||||
}
|
||||
else if (headers == nullptr && postData != nullptr)
|
||||
{
|
||||
auto dataStringJuce = postData->toString();
|
||||
auto dataStringJava = javaString (dataStringJuce);
|
||||
auto encodingString = LocalRef<jobject> (env->CallStaticObjectMethod (URLEncoder, URLEncoder.encode,
|
||||
dataStringJava.get(), javaString ("utf-8").get()));
|
||||
|
||||
auto bytes = LocalRef<jbyteArray> ((jbyteArray) env->CallObjectMethod (encodingString, JavaString.getBytes));
|
||||
|
||||
env->CallVoidMethod ((jobject) getView(), AndroidWebView.postUrl,
|
||||
javaString (url).get(), bytes.get());
|
||||
}
|
||||
else if (headers != nullptr && postData != nullptr)
|
||||
{
|
||||
// There is no support for both extra headers and post data in Android WebView, so
|
||||
// we need to open URL manually.
|
||||
|
||||
URL urlToUse = URL (url).withPOSTData (*postData);
|
||||
|
||||
connectionThread = nullptr;
|
||||
connectionThread = new ConnectionThread (*this, urlToUse, *headers);
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
connectionThread = nullptr;
|
||||
|
||||
getEnv()->CallVoidMethod ((jobject) getView(), AndroidWebView.stopLoading);
|
||||
}
|
||||
|
||||
void goBack()
|
||||
{
|
||||
connectionThread = nullptr;
|
||||
|
||||
getEnv()->CallVoidMethod ((jobject) getView(), AndroidWebView.goBack);
|
||||
}
|
||||
|
||||
void goForward()
|
||||
{
|
||||
connectionThread = nullptr;
|
||||
|
||||
getEnv()->CallVoidMethod ((jobject) getView(), AndroidWebView.goForward);
|
||||
}
|
||||
|
||||
void refresh()
|
||||
{
|
||||
connectionThread = nullptr;
|
||||
|
||||
getEnv()->CallVoidMethod ((jobject) getView(), AndroidWebView.reload);
|
||||
}
|
||||
|
||||
void handleAsyncUpdate()
|
||||
{
|
||||
jassert (connectionThread != nullptr);
|
||||
|
||||
if (connectionThread == nullptr)
|
||||
return;
|
||||
|
||||
auto& result = connectionThread->getResult();
|
||||
|
||||
if (result.statusCode >= 200 && result.statusCode < 300)
|
||||
{
|
||||
auto url = javaString (result.url);
|
||||
auto data = javaString (result.data);
|
||||
auto mimeType = javaString ("text/html");
|
||||
auto encoding = javaString ("utf-8");
|
||||
|
||||
getEnv()->CallVoidMethod ((jobject) getView(), AndroidWebView.loadDataWithBaseURL,
|
||||
url.get(), data.get(), mimeType.get(),
|
||||
encoding.get(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
owner.pageLoadHadNetworkError (result.description);
|
||||
}
|
||||
}
|
||||
|
||||
bool handlePageAboutToLoad (const String& url)
|
||||
{
|
||||
if (MessageManager::getInstance()->isThisTheMessageThread())
|
||||
return owner.pageAboutToLoad (url);
|
||||
|
||||
WeakReference<Pimpl> weakRef (this);
|
||||
|
||||
if (weakRef == nullptr)
|
||||
return false;
|
||||
|
||||
responseReadyEvent.reset();
|
||||
|
||||
bool shouldLoad = false;
|
||||
|
||||
MessageManager::callAsync ([weakRef, url, &shouldLoad]
|
||||
{
|
||||
if (weakRef == nullptr)
|
||||
return;
|
||||
|
||||
shouldLoad = weakRef->owner.pageAboutToLoad (url);
|
||||
|
||||
weakRef->responseReadyEvent.signal();
|
||||
});
|
||||
|
||||
responseReadyEvent.wait (-1);
|
||||
|
||||
return shouldLoad;
|
||||
}
|
||||
|
||||
private:
|
||||
class ConnectionThread : private Thread
|
||||
{
|
||||
public:
|
||||
struct Result
|
||||
{
|
||||
String url;
|
||||
int statusCode = 0;
|
||||
String description;
|
||||
String data;
|
||||
};
|
||||
|
||||
ConnectionThread (Pimpl& ownerToUse,
|
||||
URL& url,
|
||||
const StringArray& headers)
|
||||
: Thread ("WebBrowserComponent::Pimpl::ConnectionThread"),
|
||||
owner (ownerToUse),
|
||||
webInputStream (new WebInputStream (url, true))
|
||||
{
|
||||
webInputStream->withExtraHeaders (headers.joinIntoString ("\n"));
|
||||
webInputStream->withConnectionTimeout (10000);
|
||||
|
||||
result.url = url.toString (true);
|
||||
|
||||
startThread();
|
||||
}
|
||||
|
||||
~ConnectionThread()
|
||||
{
|
||||
webInputStream->cancel();
|
||||
signalThreadShouldExit();
|
||||
waitForThreadToExit (10000);
|
||||
|
||||
webInputStream = nullptr;
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (! webInputStream->connect (nullptr))
|
||||
{
|
||||
result.description = "Could not establish connection";
|
||||
owner.triggerAsyncUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
result.statusCode = webInputStream->getStatusCode();
|
||||
result.description = "Status code: " + String (result.statusCode);
|
||||
readFromInputStream();
|
||||
owner.triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
const Result& getResult() { return result; }
|
||||
|
||||
private:
|
||||
void readFromInputStream()
|
||||
{
|
||||
MemoryOutputStream ostream;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (threadShouldExit())
|
||||
return;
|
||||
|
||||
char buffer [8192];
|
||||
const int num = webInputStream->read (buffer, sizeof (buffer));
|
||||
|
||||
if (num <= 0)
|
||||
break;
|
||||
|
||||
ostream.write (buffer, (size_t) num);
|
||||
}
|
||||
|
||||
result.data = ostream.toUTF8();
|
||||
}
|
||||
|
||||
Pimpl& owner;
|
||||
ScopedPointer<WebInputStream> webInputStream;
|
||||
Result result;
|
||||
};
|
||||
|
||||
|
||||
WebBrowserComponent& owner;
|
||||
GlobalRef juceWebChromeClient;
|
||||
GlobalRef juceWebViewClient;
|
||||
ScopedPointer<ConnectionThread> connectionThread;
|
||||
WaitableEvent responseReadyEvent;
|
||||
|
||||
WeakReference<Pimpl>::Master masterReference;
|
||||
friend class WeakReference<Pimpl>;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
WebBrowserComponent::WebBrowserComponent (const bool unloadWhenHidden)
|
||||
: blankPageShown (false),
|
||||
unloadPageWhenBrowserIsHidden (unloadWhenHidden)
|
||||
{
|
||||
setOpaque (true);
|
||||
|
||||
addAndMakeVisible (browser = new Pimpl (*this));
|
||||
}
|
||||
|
||||
WebBrowserComponent::~WebBrowserComponent()
|
||||
|
|
@ -61,10 +405,13 @@ void WebBrowserComponent::goToURL (const String& url,
|
|||
lastPostData.reset();
|
||||
|
||||
blankPageShown = false;
|
||||
|
||||
browser->goToURL (url, headers, postData);
|
||||
}
|
||||
|
||||
void WebBrowserComponent::stop()
|
||||
{
|
||||
browser->stop();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goBack()
|
||||
|
|
@ -72,16 +419,19 @@ void WebBrowserComponent::goBack()
|
|||
lastURL.clear();
|
||||
blankPageShown = false;
|
||||
|
||||
browser->goBack();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goForward()
|
||||
{
|
||||
lastURL.clear();
|
||||
|
||||
browser->goForward();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::refresh()
|
||||
{
|
||||
browser->refresh();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -92,13 +442,30 @@ void WebBrowserComponent::paint (Graphics& g)
|
|||
|
||||
void WebBrowserComponent::checkWindowAssociation()
|
||||
{
|
||||
if (isShowing())
|
||||
{
|
||||
if (blankPageShown)
|
||||
goBack();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unloadPageWhenBrowserIsHidden && ! blankPageShown)
|
||||
{
|
||||
// when the component becomes invisible, some stuff like flash
|
||||
// carries on playing audio, so we need to force it onto a blank
|
||||
// page to avoid this, (and send it back when it's made visible again).
|
||||
|
||||
blankPageShown = true;
|
||||
browser->goToURL ("about:blank", 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebBrowserComponent::reloadLastURL()
|
||||
{
|
||||
if (lastURL.isNotEmpty())
|
||||
{
|
||||
goToURL (lastURL, &lastHeaders, &lastPostData);
|
||||
goToURL (lastURL, &lastHeaders, lastPostData.getSize() == 0 ? nullptr : &lastPostData);
|
||||
lastURL.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -110,6 +477,7 @@ void WebBrowserComponent::parentHierarchyChanged()
|
|||
|
||||
void WebBrowserComponent::resized()
|
||||
{
|
||||
browser->setSize (getWidth(), getHeight());
|
||||
}
|
||||
|
||||
void WebBrowserComponent::visibilityChanged()
|
||||
|
|
@ -123,6 +491,118 @@ void WebBrowserComponent::focusGained (FocusChangeType)
|
|||
|
||||
void WebBrowserComponent::clearCookies()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
auto cookieManager = LocalRef<jobject> (env->CallStaticObjectMethod (AndroidCookieManager,
|
||||
AndroidCookieManager.getInstance));
|
||||
|
||||
const bool apiAtLeast21 = env->CallStaticIntMethod (JuceAppActivity, JuceAppActivity.getAndroidSDKVersion) >= 21;
|
||||
|
||||
jmethodID clearCookiesMethod = 0;
|
||||
|
||||
if (apiAtLeast21)
|
||||
{
|
||||
clearCookiesMethod = env->GetMethodID (AndroidCookieManager, "removeAllCookies", "(Landroid/webkit/ValueCallback;)V");
|
||||
env->CallVoidMethod (cookieManager, clearCookiesMethod, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
clearCookiesMethod = env->GetMethodID (AndroidCookieManager, "removeAllCookie", "()V");
|
||||
env->CallVoidMethod (cookieManager, clearCookiesMethod);
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewPageLoadStarted, bool, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/, jobject url))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
return juce_webViewPageLoadStarted (reinterpret_cast<WebBrowserComponent*> (host),
|
||||
juceString (static_cast<jstring> (url)));
|
||||
}
|
||||
|
||||
bool juce_webViewPageLoadStarted (WebBrowserComponent* browserComponent, const String& url)
|
||||
{
|
||||
return browserComponent->browser->handlePageAboutToLoad (url);
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewPageLoadFinished, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/, jobject url))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
reinterpret_cast<WebBrowserComponent*> (host)->pageFinishedLoading (juceString (static_cast<jstring> (url)));
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewReceivedError, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/, jobject /*request*/, jobject error))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
jclass errorClass = env->FindClass ("android/webkit/WebResourceError");
|
||||
|
||||
if (errorClass != 0)
|
||||
{
|
||||
jmethodID method = env->GetMethodID (errorClass, "getDescription", "()Ljava/lang/CharSequence;");
|
||||
|
||||
if (method != 0)
|
||||
{
|
||||
auto sequence = LocalRef<jobject> (env->CallObjectMethod (error, method));
|
||||
auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (sequence, JavaCharSequence.toString));
|
||||
|
||||
reinterpret_cast<WebBrowserComponent*> (host)->pageLoadHadNetworkError (juceString (errorString));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Should never get here!
|
||||
jassertfalse;
|
||||
reinterpret_cast<WebBrowserComponent*> (host)->pageLoadHadNetworkError ({});
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewReceivedHttpError, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/, jobject /*request*/, jobject errorResponse))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
jclass responseClass = env->FindClass ("android/webkit/WebResourceResponse");
|
||||
|
||||
if (responseClass != 0)
|
||||
{
|
||||
jmethodID method = env->GetMethodID (responseClass, "getReasonPhrase", "()Ljava/lang/String;");
|
||||
|
||||
if (method != 0)
|
||||
{
|
||||
auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (errorResponse, method));
|
||||
|
||||
reinterpret_cast<WebBrowserComponent*> (host)->pageLoadHadNetworkError (juceString (errorString));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Should never get here!
|
||||
jassertfalse;
|
||||
reinterpret_cast<WebBrowserComponent*> (host)->pageLoadHadNetworkError ({});
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewReceivedSslError, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/, jobject /*sslErrorHandler*/, jobject sslError))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
auto errorString = LocalRef<jstring> ((jstring) env->CallObjectMethod (sslError, SslError.toString));
|
||||
|
||||
reinterpret_cast<WebBrowserComponent*> (host)->pageLoadHadNetworkError (juceString (errorString));
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewCloseWindowRequest, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
reinterpret_cast<WebBrowserComponent*> (host)->windowCloseRequest();
|
||||
}
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, webViewCreateWindowRequest, void, (JNIEnv* env, jobject /*activity*/, jlong host, jobject /*webView*/))
|
||||
{
|
||||
setEnv (env);
|
||||
|
||||
reinterpret_cast<WebBrowserComponent*> (host)->newWindowAttemptingToLoad ({});
|
||||
}
|
||||
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -715,11 +715,6 @@ WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidd
|
|||
|
||||
WebBrowserComponent::~WebBrowserComponent()
|
||||
{
|
||||
if (browser != nullptr)
|
||||
{
|
||||
delete browser;
|
||||
browser = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -358,8 +358,7 @@ private:
|
|||
|
||||
//==============================================================================
|
||||
WebBrowserComponent::WebBrowserComponent (const bool unloadWhenHidden)
|
||||
: browser (nullptr),
|
||||
blankPageShown (false),
|
||||
: blankPageShown (false),
|
||||
unloadPageWhenBrowserIsHidden (unloadWhenHidden)
|
||||
{
|
||||
setOpaque (true);
|
||||
|
|
@ -369,7 +368,6 @@ WebBrowserComponent::WebBrowserComponent (const bool unloadWhenHidden)
|
|||
|
||||
WebBrowserComponent::~WebBrowserComponent()
|
||||
{
|
||||
deleteAndZero (browser);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -241,17 +241,15 @@ private:
|
|||
|
||||
//==============================================================================
|
||||
WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
|
||||
: browser (nullptr),
|
||||
: browser (new Pimpl()),
|
||||
blankPageShown (false),
|
||||
unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
|
||||
{
|
||||
setOpaque (true);
|
||||
addAndMakeVisible (browser = new Pimpl());
|
||||
}
|
||||
|
||||
WebBrowserComponent::~WebBrowserComponent()
|
||||
{
|
||||
delete browser;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -35,13 +35,6 @@ namespace juce
|
|||
DECLARE_JNI_CLASS (NativeSurfaceView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (addView, "addView", "(Landroid/view/View;)V") \
|
||||
METHOD (removeView, "removeView", "(Landroid/view/View;)V") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidViewGroup, "android/view/ViewGroup")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
class OpenGLContext::NativeContext
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue