blob: 91122bdc4105e0906095365b7258807a724cab2c [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtWebView module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qandroidwebview_p.h"
#include <private/qwebview_p.h>
#include <private/qwebviewloadrequest_p.h>
#include <QtCore/private/qjnihelpers_p.h>
#include <QtCore/private/qjni_p.h>
#include <QtCore/qmap.h>
#include <android/bitmap.h>
#include <QtGui/qguiapplication.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qurl.h>
#include <QtCore/qdebug.h>
QT_BEGIN_NAMESPACE
static const char qtAndroidWebViewControllerClass[] = "org/qtproject/qt5/android/view/QtAndroidWebViewController";
//static bool favIcon(JNIEnv *env, jobject icon, QImage *image)
//{
// // TODO:
// AndroidBitmapInfo bitmapInfo;
// if (AndroidBitmap_getInfo(env, icon, &bitmapInfo) != ANDROID_BITMAP_RESULT_SUCCESS)
// return false;
// void *pixelData;
// if (AndroidBitmap_lockPixels(env, icon, &pixelData) != ANDROID_BITMAP_RESULT_SUCCESS)
// return false;
// *image = QImage::fromData(static_cast<const uchar *>(pixelData), bitmapInfo.width * bitmapInfo.height);
// AndroidBitmap_unlockPixels(env, icon);
// return true;
//}
typedef QMap<quintptr, QAndroidWebViewPrivate *> WebViews;
Q_GLOBAL_STATIC(WebViews, g_webViews)
QAndroidWebViewPrivate::QAndroidWebViewPrivate(QObject *p)
: QAbstractWebView(p)
, m_id(reinterpret_cast<quintptr>(this))
, m_callbackId(0)
, m_window(0)
{
m_viewController = QJNIObjectPrivate(qtAndroidWebViewControllerClass,
"(Landroid/app/Activity;J)V",
QtAndroidPrivate::activity(),
m_id);
m_webView = m_viewController.callObjectMethod("getWebView",
"()Landroid/webkit/WebView;");
m_window = QWindow::fromWinId(reinterpret_cast<WId>(m_webView.object()));
g_webViews->insert(m_id, this);
connect(qApp, &QGuiApplication::applicationStateChanged,
this, &QAndroidWebViewPrivate::onApplicationStateChanged);
}
QAndroidWebViewPrivate::~QAndroidWebViewPrivate()
{
g_webViews->take(m_id);
if (m_window != 0) {
m_window->setVisible(false);
m_window->setParent(0);
delete m_window;
}
m_viewController.callMethod<void>("destroy");
}
QString QAndroidWebViewPrivate::httpUserAgent() const
{
return QString( m_viewController.callObjectMethod<jstring>("getUserAgent").toString());
}
void QAndroidWebViewPrivate::setHttpUserAgent(const QString &userAgent)
{
m_viewController.callMethod<void>("setUserAgent",
"(Ljava/lang/String;)V",
QJNIObjectPrivate::fromString(userAgent).object());
Q_EMIT httpUserAgentChanged(userAgent);
}
QUrl QAndroidWebViewPrivate::url() const
{
return QUrl::fromUserInput(m_viewController.callObjectMethod<jstring>("getUrl").toString());
}
void QAndroidWebViewPrivate::setUrl(const QUrl &url)
{
m_viewController.callMethod<void>("loadUrl",
"(Ljava/lang/String;)V",
QJNIObjectPrivate::fromString(url.toString()).object());
}
void QAndroidWebViewPrivate::loadHtml(const QString &html, const QUrl &baseUrl)
{
const QJNIObjectPrivate &htmlString = QJNIObjectPrivate::fromString(html);
const QJNIObjectPrivate &mimeTypeString = QJNIObjectPrivate::fromString(QLatin1String("text/html;charset=UTF-8"));
baseUrl.isEmpty() ? m_viewController.callMethod<void>("loadData",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
htmlString.object(),
mimeTypeString.object(),
0)
: m_viewController.callMethod<void>("loadDataWithBaseURL",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
QJNIObjectPrivate::fromString(baseUrl.toString()).object(),
htmlString.object(),
mimeTypeString.object(),
0,
0);
}
bool QAndroidWebViewPrivate::canGoBack() const
{
return m_viewController.callMethod<jboolean>("canGoBack");
}
void QAndroidWebViewPrivate::goBack()
{
m_viewController.callMethod<void>("goBack");
}
bool QAndroidWebViewPrivate::canGoForward() const
{
return m_viewController.callMethod<jboolean>("canGoForward");
}
void QAndroidWebViewPrivate::goForward()
{
m_viewController.callMethod<void>("goForward");
}
void QAndroidWebViewPrivate::reload()
{
m_viewController.callMethod<void>("reload");
}
QString QAndroidWebViewPrivate::title() const
{
return m_viewController.callObjectMethod<jstring>("getTitle").toString();
}
void QAndroidWebViewPrivate::setGeometry(const QRect &geometry)
{
if (m_window == 0)
return;
m_window->setGeometry(geometry);
}
void QAndroidWebViewPrivate::setVisibility(QWindow::Visibility visibility)
{
m_window->setVisibility(visibility);
}
void QAndroidWebViewPrivate::runJavaScriptPrivate(const QString &script,
int callbackId)
{
if (QtAndroidPrivate::androidSdkVersion() < 19) {
qWarning("runJavaScript() requires API level 19 or higher.");
if (callbackId == -1)
return;
// Emit signal here to remove the callback.
Q_EMIT javaScriptResult(callbackId, QVariant());
}
m_viewController.callMethod<void>("runJavaScript",
"(Ljava/lang/String;J)V",
static_cast<jstring>(QJNIObjectPrivate::fromString(script).object()),
callbackId);
}
void QAndroidWebViewPrivate::setVisible(bool visible)
{
m_window->setVisible(visible);
}
int QAndroidWebViewPrivate::loadProgress() const
{
return m_viewController.callMethod<jint>("getProgress");
}
bool QAndroidWebViewPrivate::isLoading() const
{
return m_viewController.callMethod<jboolean>("isLoading");
}
void QAndroidWebViewPrivate::setParentView(QObject *view)
{
m_window->setParent(qobject_cast<QWindow *>(view));
}
QObject *QAndroidWebViewPrivate::parentView() const
{
return m_window->parent();
}
void QAndroidWebViewPrivate::stop()
{
m_viewController.callMethod<void>("stopLoading");
}
//void QAndroidWebViewPrivate::initialize()
//{
// // TODO:
//}
void QAndroidWebViewPrivate::onApplicationStateChanged(Qt::ApplicationState state)
{
if (QtAndroidPrivate::androidSdkVersion() < 11)
return;
if (state == Qt::ApplicationActive)
m_viewController.callMethod<void>("onResume");
else
m_viewController.callMethod<void>("onPause");
}
QT_END_NAMESPACE
static void c_onRunJavaScriptResult(JNIEnv *env,
jobject thiz,
jlong id,
jlong callbackId,
jstring result)
{
Q_UNUSED(env)
Q_UNUSED(thiz)
const WebViews &wv = (*g_webViews);
QAndroidWebViewPrivate *wc = static_cast<QAndroidWebViewPrivate *>(wv[id]);
if (!wc)
return;
const QString &resultString = QJNIObjectPrivate(result).toString();
// The result string is in JSON format, lets parse it to see what we got.
QJsonValue jsonValue;
const QByteArray &jsonData = "{ \"data\": " + resultString.toUtf8() + " }";
QJsonParseError error;
const QJsonDocument &jsonDoc = QJsonDocument::fromJson(jsonData, &error);
if (error.error == QJsonParseError::NoError && jsonDoc.isObject()) {
const QJsonObject &object = jsonDoc.object();
jsonValue = object.value(QStringLiteral("data"));
}
Q_EMIT wc->javaScriptResult(int(callbackId),
jsonValue.isNull() ? resultString
: jsonValue.toVariant());
}
static void c_onPageFinished(JNIEnv *env,
jobject thiz,
jlong id,
jstring url)
{
Q_UNUSED(env)
Q_UNUSED(thiz)
const WebViews &wv = (*g_webViews);
QAndroidWebViewPrivate *wc = wv[id];
if (!wc)
return;
QWebViewLoadRequestPrivate loadRequest(QUrl(QJNIObjectPrivate(url).toString()),
QWebView::LoadSucceededStatus,
QString());
Q_EMIT wc->loadingChanged(loadRequest);
}
static void c_onPageStarted(JNIEnv *env,
jobject thiz,
jlong id,
jstring url,
jobject icon)
{
Q_UNUSED(env)
Q_UNUSED(thiz)
Q_UNUSED(icon)
const WebViews &wv = (*g_webViews);
QAndroidWebViewPrivate *wc = wv[id];
if (!wc)
return;
QWebViewLoadRequestPrivate loadRequest(QUrl(QJNIObjectPrivate(url).toString()),
QWebView::LoadStartedStatus,
QString());
Q_EMIT wc->loadingChanged(loadRequest);
// if (!icon)
// return;
// QImage image;
// if (favIcon(env, icon, &image))
// Q_EMIT wc->iconChanged(image);
}
static void c_onProgressChanged(JNIEnv *env,
jobject thiz,
jlong id,
jint newProgress)
{
Q_UNUSED(env)
Q_UNUSED(thiz)
const WebViews &wv = (*g_webViews);
QAndroidWebViewPrivate *wc = wv[id];
if (!wc)
return;
Q_EMIT wc->loadProgressChanged(newProgress);
}
static void c_onReceivedIcon(JNIEnv *env,
jobject thiz,
jlong id,
jobject icon)
{
Q_UNUSED(env)
Q_UNUSED(thiz)
Q_UNUSED(id)
Q_UNUSED(icon)
const WebViews &wv = (*g_webViews);
QAndroidWebViewPrivate *wc = wv[id];
if (!wc)
return;
if (!icon)
return;
// QImage image;
// if (favIcon(env, icon, &image))
// Q_EMIT wc->iconChanged(image);
}
static void c_onReceivedTitle(JNIEnv *env,
jobject thiz,
jlong id,
jstring title)
{
Q_UNUSED(env)
Q_UNUSED(thiz)
const WebViews &wv = (*g_webViews);
QAndroidWebViewPrivate *wc = wv[id];
if (!wc)
return;
const QString &qTitle = QJNIObjectPrivate(title).toString();
Q_EMIT wc->titleChanged(qTitle);
}
static void c_onReceivedError(JNIEnv *env,
jobject thiz,
jlong id,
jint errorCode,
jstring description,
jstring url)
{
Q_UNUSED(env)
Q_UNUSED(thiz)
Q_UNUSED(errorCode)
const WebViews &wv = (*g_webViews);
QAndroidWebViewPrivate *wc = wv[id];
if (!wc)
return;
QWebViewLoadRequestPrivate loadRequest(QUrl(QJNIObjectPrivate(url).toString()),
QWebView::LoadFailedStatus,
QJNIObjectPrivate(description).toString());
Q_EMIT wc->loadingChanged(loadRequest);
}
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
{
static bool initialized = false;
if (initialized)
return JNI_VERSION_1_6;
initialized = true;
typedef union {
JNIEnv *nativeEnvironment;
void *venv;
} UnionJNIEnvToVoid;
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
JNIEnv *env = uenv.nativeEnvironment;
jclass clazz = QJNIEnvironmentPrivate::findClass(qtAndroidWebViewControllerClass, env);
if (!clazz)
return JNI_ERR;
JNINativeMethod methods[] = {
{"c_onPageFinished", "(JLjava/lang/String;)V", reinterpret_cast<void *>(c_onPageFinished)},
{"c_onPageStarted", "(JLjava/lang/String;Landroid/graphics/Bitmap;)V", reinterpret_cast<void *>(c_onPageStarted)},
{"c_onProgressChanged", "(JI)V", reinterpret_cast<void *>(c_onProgressChanged)},
{"c_onReceivedIcon", "(JLandroid/graphics/Bitmap;)V", reinterpret_cast<void *>(c_onReceivedIcon)},
{"c_onReceivedTitle", "(JLjava/lang/String;)V", reinterpret_cast<void *>(c_onReceivedTitle)},
{"c_onRunJavaScriptResult", "(JJLjava/lang/String;)V", reinterpret_cast<void *>(c_onRunJavaScriptResult)},
{"c_onReceivedError", "(JILjava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void *>(c_onReceivedError)}
};
const int nMethods = sizeof(methods) / sizeof(methods[0]);
if (env->RegisterNatives(clazz, methods, nMethods) != JNI_OK)
return JNI_ERR;
return JNI_VERSION_1_6;
}