From 97fcf3bc987a18f32233fea550eb4a5a22e2b822 Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Mon, 4 Mar 2013 10:16:42 +0100 Subject: Introducing the Qt Android port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on the Necessitas project by Bogdan Vatra. Contributors to the Qt5 project: BogDan Vatra Eskil Abrahamsen Blomfeldt hjk Oswald Buddenhagen Paul Olav Tvete Robin Burchell Samuel Rødal Yoann Lopes The full history of the Qt5 port can be found in refs/old-heads/android, SHA-1 249ca9ca2c7d876b91b31df9434dde47f9065d0d Change-Id: Iff1a7b2dbb707c986f2639e65e39ed8f22430120 Reviewed-by: Oswald Buddenhagen Reviewed-by: Lars Knoll --- src/android/android.pro | 15 + src/android/jar/AndroidManifest.xml | 4 + src/android/jar/res/values/strings.xml | 4 + .../qtproject/qt5/android/QtActivityDelegate.java | 714 +++++++++ .../src/org/qtproject/qt5/android/QtEditText.java | 97 ++ .../qtproject/qt5/android/QtInputConnection.java | 244 +++ .../src/org/qtproject/qt5/android/QtLayout.java | 201 +++ .../src/org/qtproject/qt5/android/QtNative.java | 611 ++++++++ .../qt5/android/QtNativeLibrariesDir.java | 61 + .../src/org/qtproject/qt5/android/QtSurface.java | 206 +++ src/android/java/AndroidManifest.xml | 25 + src/android/java/res/layout/splash.xml | 13 + src/android/java/res/values-de/strings.xml | 6 + src/android/java/res/values-el/strings.xml | 6 + src/android/java/res/values-es/strings.xml | 6 + src/android/java/res/values-et/strings.xml | 6 + src/android/java/res/values-fa/strings.xml | 6 + src/android/java/res/values-fr/strings.xml | 6 + src/android/java/res/values-id/strings.xml | 6 + src/android/java/res/values-it/strings.xml | 6 + src/android/java/res/values-ja/strings.xml | 6 + src/android/java/res/values-ms/strings.xml | 6 + src/android/java/res/values-nb/strings.xml | 6 + src/android/java/res/values-nl/strings.xml | 6 + src/android/java/res/values-pl/strings.xml | 6 + src/android/java/res/values-pt-rBR/strings.xml | 6 + src/android/java/res/values-ro/strings.xml | 6 + src/android/java/res/values-rs/strings.xml | 6 + src/android/java/res/values-ru/strings.xml | 6 + src/android/java/res/values-zh-rCN/strings.xml | 6 + src/android/java/res/values-zh-rTW/strings.xml | 6 + src/android/java/res/values/libs.xml | 8 + src/android/java/res/values/strings.xml | 7 + .../src/org/kde/necessitas/ministro/IMinistro.aidl | 49 + .../kde/necessitas/ministro/IMinistroCallback.aidl | 53 + .../qtproject/qt5/android/bindings/QtActivity.java | 1278 ++++++++++++++++ .../qt5/android/bindings/QtApplication.java | 149 ++ src/android/java/version.xml | 8 + src/corelib/codecs/qtextcodec.cpp | 2 +- src/corelib/codecs/qtextcodec_p.h | 2 +- src/corelib/corelib.pro | 6 + src/corelib/global/qlogging.cpp | 24 + src/corelib/global/qsystemdetection.h | 4 + src/corelib/io/io.pri | 2 +- src/corelib/io/qfilesystemengine_unix.cpp | 2 +- src/corelib/io/qtemporarydir.cpp | 14 +- src/corelib/kernel/kernel.pri | 2 +- src/corelib/kernel/qsharedmemory_p.h | 2 +- src/corelib/thread/qthread_unix.cpp | 8 +- src/corelib/tools/qstring.h | 2 +- src/gui/kernel/qguiapplication.cpp | 28 +- src/network/access/qnetworkaccessfilebackend.cpp | 18 +- src/network/access/qnetworkaccessmanager.cpp | 6 +- src/network/access/qnetworkreplyfileimpl.cpp | 12 +- src/network/kernel/kernel.pri | 2 +- src/network/kernel/qhostinfo_unix.cpp | 2 +- src/network/kernel/qnetworkinterface_unix.cpp | 2 +- src/network/ssl/qsslsocket_openssl.cpp | 17 +- src/opengl/opengl.pro | 3 + .../devicediscovery/devicediscovery.pri | 2 +- src/plugins/platforms/android/android.pro | 3 + src/plugins/platforms/android/opengl/opengl.pro | 30 + src/plugins/platforms/android/raster/raster.pro | 19 + src/plugins/platforms/android/src/android.json | 3 + .../platforms/android/src/androidjniclipboard.cpp | 120 ++ .../platforms/android/src/androidjniclipboard.h | 61 + .../platforms/android/src/androidjniinput.cpp | 480 ++++++ .../platforms/android/src/androidjniinput.h | 59 + .../platforms/android/src/androidjnimain.cpp | 839 ++++++++++ src/plugins/platforms/android/src/androidjnimain.h | 120 ++ .../platforms/android/src/androidjnimenu.cpp | 405 +++++ src/plugins/platforms/android/src/androidjnimenu.h | 69 + .../android/src/androidplatformplugin.cpp | 66 + .../android/src/opengl/qandroidopenglcontext.cpp | 78 + .../android/src/opengl/qandroidopenglcontext.h | 68 + .../src/opengl/qandroidopenglplatformwindow.cpp | 72 + .../src/opengl/qandroidopenglplatformwindow.h | 73 + .../android/src/opengl/qeglfshooks_android.cpp | 132 ++ .../src/qandroidassetsfileenginehandler.cpp | 288 ++++ .../android/src/qandroidassetsfileenginehandler.h | 60 + .../platforms/android/src/qandroidinputcontext.cpp | 644 ++++++++ .../platforms/android/src/qandroidinputcontext.h | 130 ++ .../android/src/qandroidplatformclipboard.cpp | 79 + .../android/src/qandroidplatformclipboard.h | 63 + .../android/src/qandroidplatformfontdatabase.cpp | 79 + .../android/src/qandroidplatformfontdatabase.h | 58 + .../android/src/qandroidplatformintegration.cpp | 283 ++++ .../android/src/qandroidplatformintegration.h | 158 ++ .../platforms/android/src/qandroidplatformmenu.cpp | 167 ++ .../platforms/android/src/qandroidplatformmenu.h | 91 ++ .../android/src/qandroidplatformmenubar.cpp | 109 ++ .../android/src/qandroidplatformmenubar.h | 74 + .../android/src/qandroidplatformmenuitem.cpp | 180 +++ .../android/src/qandroidplatformmenuitem.h | 99 ++ .../android/src/qandroidplatformservices.cpp | 83 + .../android/src/qandroidplatformservices.h | 61 + .../android/src/qandroidplatformtheme.cpp | 78 + .../platforms/android/src/qandroidplatformtheme.h | 56 + .../android/src/raster/qandroidplatformscreen.cpp | 71 + .../android/src/raster/qandroidplatformscreen.h | 59 + .../android/src/raster/qandroidplatformwindow.cpp | 56 + .../android/src/raster/qandroidplatformwindow.h | 60 + .../platforms/android/src/raster/raster.pri | 7 + src/plugins/platforms/android/src/src.pri | 47 + src/plugins/platforms/eglfs/qeglfsintegration.cpp | 4 +- src/plugins/platforms/platforms.pro | 2 + src/src.pro | 4 + src/widgets/styles/qandroidstyle.cpp | 1601 ++++++++++++++++++++ src/widgets/styles/qandroidstyle_p.h | 382 +++++ src/widgets/styles/qcommonstyle.cpp | 4 + src/widgets/styles/qstylefactory.cpp | 12 + src/widgets/styles/styles.pri | 7 + src/widgets/widgets/qtextbrowser.cpp | 29 +- 113 files changed, 11858 insertions(+), 47 deletions(-) create mode 100644 src/android/android.pro create mode 100644 src/android/jar/AndroidManifest.xml create mode 100644 src/android/jar/res/values/strings.xml create mode 100644 src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java create mode 100644 src/android/jar/src/org/qtproject/qt5/android/QtEditText.java create mode 100644 src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java create mode 100644 src/android/jar/src/org/qtproject/qt5/android/QtLayout.java create mode 100644 src/android/jar/src/org/qtproject/qt5/android/QtNative.java create mode 100644 src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java create mode 100644 src/android/jar/src/org/qtproject/qt5/android/QtSurface.java create mode 100644 src/android/java/AndroidManifest.xml create mode 100644 src/android/java/res/layout/splash.xml create mode 100644 src/android/java/res/values-de/strings.xml create mode 100644 src/android/java/res/values-el/strings.xml create mode 100644 src/android/java/res/values-es/strings.xml create mode 100644 src/android/java/res/values-et/strings.xml create mode 100644 src/android/java/res/values-fa/strings.xml create mode 100644 src/android/java/res/values-fr/strings.xml create mode 100644 src/android/java/res/values-id/strings.xml create mode 100644 src/android/java/res/values-it/strings.xml create mode 100644 src/android/java/res/values-ja/strings.xml create mode 100644 src/android/java/res/values-ms/strings.xml create mode 100644 src/android/java/res/values-nb/strings.xml create mode 100644 src/android/java/res/values-nl/strings.xml create mode 100644 src/android/java/res/values-pl/strings.xml create mode 100644 src/android/java/res/values-pt-rBR/strings.xml create mode 100644 src/android/java/res/values-ro/strings.xml create mode 100644 src/android/java/res/values-rs/strings.xml create mode 100644 src/android/java/res/values-ru/strings.xml create mode 100644 src/android/java/res/values-zh-rCN/strings.xml create mode 100644 src/android/java/res/values-zh-rTW/strings.xml create mode 100644 src/android/java/res/values/libs.xml create mode 100644 src/android/java/res/values/strings.xml create mode 100644 src/android/java/src/org/kde/necessitas/ministro/IMinistro.aidl create mode 100644 src/android/java/src/org/kde/necessitas/ministro/IMinistroCallback.aidl create mode 100644 src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java create mode 100644 src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java create mode 100644 src/android/java/version.xml create mode 100644 src/plugins/platforms/android/android.pro create mode 100644 src/plugins/platforms/android/opengl/opengl.pro create mode 100644 src/plugins/platforms/android/raster/raster.pro create mode 100644 src/plugins/platforms/android/src/android.json create mode 100644 src/plugins/platforms/android/src/androidjniclipboard.cpp create mode 100644 src/plugins/platforms/android/src/androidjniclipboard.h create mode 100644 src/plugins/platforms/android/src/androidjniinput.cpp create mode 100644 src/plugins/platforms/android/src/androidjniinput.h create mode 100644 src/plugins/platforms/android/src/androidjnimain.cpp create mode 100644 src/plugins/platforms/android/src/androidjnimain.h create mode 100644 src/plugins/platforms/android/src/androidjnimenu.cpp create mode 100644 src/plugins/platforms/android/src/androidjnimenu.h create mode 100644 src/plugins/platforms/android/src/androidplatformplugin.cpp create mode 100644 src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp create mode 100644 src/plugins/platforms/android/src/opengl/qandroidopenglcontext.h create mode 100644 src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp create mode 100644 src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h create mode 100644 src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp create mode 100644 src/plugins/platforms/android/src/qandroidassetsfileenginehandler.cpp create mode 100644 src/plugins/platforms/android/src/qandroidassetsfileenginehandler.h create mode 100644 src/plugins/platforms/android/src/qandroidinputcontext.cpp create mode 100644 src/plugins/platforms/android/src/qandroidinputcontext.h create mode 100644 src/plugins/platforms/android/src/qandroidplatformclipboard.cpp create mode 100644 src/plugins/platforms/android/src/qandroidplatformclipboard.h create mode 100644 src/plugins/platforms/android/src/qandroidplatformfontdatabase.cpp create mode 100644 src/plugins/platforms/android/src/qandroidplatformfontdatabase.h create mode 100644 src/plugins/platforms/android/src/qandroidplatformintegration.cpp create mode 100644 src/plugins/platforms/android/src/qandroidplatformintegration.h create mode 100644 src/plugins/platforms/android/src/qandroidplatformmenu.cpp create mode 100644 src/plugins/platforms/android/src/qandroidplatformmenu.h create mode 100644 src/plugins/platforms/android/src/qandroidplatformmenubar.cpp create mode 100644 src/plugins/platforms/android/src/qandroidplatformmenubar.h create mode 100644 src/plugins/platforms/android/src/qandroidplatformmenuitem.cpp create mode 100644 src/plugins/platforms/android/src/qandroidplatformmenuitem.h create mode 100644 src/plugins/platforms/android/src/qandroidplatformservices.cpp create mode 100644 src/plugins/platforms/android/src/qandroidplatformservices.h create mode 100644 src/plugins/platforms/android/src/qandroidplatformtheme.cpp create mode 100644 src/plugins/platforms/android/src/qandroidplatformtheme.h create mode 100644 src/plugins/platforms/android/src/raster/qandroidplatformscreen.cpp create mode 100644 src/plugins/platforms/android/src/raster/qandroidplatformscreen.h create mode 100644 src/plugins/platforms/android/src/raster/qandroidplatformwindow.cpp create mode 100644 src/plugins/platforms/android/src/raster/qandroidplatformwindow.h create mode 100644 src/plugins/platforms/android/src/raster/raster.pri create mode 100644 src/plugins/platforms/android/src/src.pri create mode 100644 src/widgets/styles/qandroidstyle.cpp create mode 100644 src/widgets/styles/qandroidstyle_p.h (limited to 'src') diff --git a/src/android/android.pro b/src/android/android.pro new file mode 100644 index 0000000000..049a51904c --- /dev/null +++ b/src/android/android.pro @@ -0,0 +1,15 @@ +CONFIG += java +TARGET = QtAndroid +DESTDIR = $$[QT_INSTALL_PREFIX/get]/jar + +PATHPREFIX = $$PWD/jar/src/org/qtproject/qt5/android/ + +JAVACLASSPATH += $$PWD/jar/src/ +JAVASOURCES += \ + $$PATHPREFIX/QtActivityDelegate.java \ + $$PATHPREFIX/QtEditText.java \ + $$PATHPREFIX/QtInputConnection.java \ + $$PATHPREFIX/QtLayout.java \ + $$PATHPREFIX/QtNative.java \ + $$PATHPREFIX/QtNativeLibrariesDir.java \ + $$PATHPREFIX/QtSurface.java diff --git a/src/android/jar/AndroidManifest.xml b/src/android/jar/AndroidManifest.xml new file mode 100644 index 0000000000..ebc6fcfea7 --- /dev/null +++ b/src/android/jar/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/android/jar/res/values/strings.xml b/src/android/jar/res/values/strings.xml new file mode 100644 index 0000000000..1021b5478a --- /dev/null +++ b/src/android/jar/res/values/strings.xml @@ -0,0 +1,4 @@ + + + QtJar + diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java new file mode 100644 index 0000000000..b6e6e3397e --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java @@ -0,0 +1,714 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Android port of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt5.android; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Iterator; + +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Build; +import android.os.Bundle; +import android.text.method.MetaKeyKeyListener; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; + +public class QtActivityDelegate +{ + private Activity m_activity = null; + private Method m_super_dispatchKeyEvent = null; + private Method m_super_onRestoreInstanceState = null; + private Method m_super_onRetainNonConfigurationInstance = null; + private Method m_super_onSaveInstanceState = null; + private Method m_super_onKeyDown = null; + private Method m_super_onKeyUp = null; + private Method m_super_onConfigurationChanged = null; + + private static final String NATIVE_LIBRARIES_KEY = "native.libraries"; + private static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries"; + private static final String MAIN_LIBRARY_KEY = "main.library"; + private static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables"; + private static final String APPLICATION_PARAMETERS_KEY = "application.parameters"; + private static final String STATIC_INIT_CLASSES_KEY = "static.init.classes"; + private static final String NECESSITAS_API_LEVEL_KEY = "necessitas.api.level"; + + private static String m_environmentVariables = null; + private static String m_applicationParameters = null; + + private int m_currentOrientation = Configuration.ORIENTATION_UNDEFINED; + + private String m_mainLib; + private long m_metaState; + private int m_lastChar = 0; + private boolean m_fullScreen = false; + private boolean m_started = false; + private QtSurface m_surface = null; + private QtLayout m_layout = null; + private QtEditText m_editText = null; + private InputMethodManager m_imm = null; + private boolean m_quitApp = true; + private Process m_debuggerProcess = null; // debugger process + + public boolean m_keyboardIsVisible = false; + public boolean m_keyboardIsHiding = false; + + public QtLayout getQtLayout() + { + return m_layout; + } + + public QtSurface getQtSurface() + { + return m_surface; + } + + public void redrawWindow(int left, int top, int right, int bottom) + { + m_surface.drawBitmap(new Rect(left, top, right, bottom)); + } + + public void setFullScreen(boolean enterFullScreen) + { + if (m_fullScreen == enterFullScreen) + return; + + if (m_fullScreen = enterFullScreen) { + m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + } else { + m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + } + + // case status + private final int ImhNoAutoUppercase = 0x2; + private final int ImhPreferUppercase = 0x8; + @SuppressWarnings("unused") + private final int ImhPreferLowercase = 0x10; + private final int ImhUppercaseOnly = 0x40000; + private final int ImhLowercaseOnly = 0x80000; + + // options + private final int ImhNoPredictiveText = 0x20; + + // layout + private final int ImhHiddenText = 0x1; + private final int ImhPreferNumbers = 0x4; + private final int ImhMultiLine = 0x400; + private final int ImhDigitsOnly = 0x10000; + private final int ImhFormattedNumbersOnly = 0x20000; + private final int ImhDialableCharactersOnly = 0x100000; + private final int ImhEmailCharactersOnly = 0x200000; + private final int ImhUrlCharactersOnly = 0x400000; + + public void resetSoftwareKeyboard() + { + if (m_imm == null) + return; + m_editText.postDelayed(new Runnable() { + @Override + public void run() { + m_imm.restartInput(m_editText); + } + }, 5); + } + + public void showSoftwareKeyboard(int x, int y, int width, int height, int inputHints) + { + if (m_imm == null) + return; + if (height > m_surface.getHeight()*2/3) + m_activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + else + m_activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + + int initialCapsMode = 0; + int imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_DONE; + int inputType = android.text.InputType.TYPE_CLASS_TEXT; + + if ((inputHints & ImhMultiLine) != 0) { + inputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE; + imeOptions = android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION; + } + + if (((inputHints & ImhNoAutoUppercase) != 0 || (inputHints & ImhPreferUppercase) != 0) + && (inputHints & ImhLowercaseOnly) == 0) { + initialCapsMode = android.text.TextUtils.CAP_MODE_SENTENCES; + } + + if ((inputHints & ImhUppercaseOnly) != 0) + initialCapsMode = android.text.TextUtils.CAP_MODE_CHARACTERS; + + if ((inputHints & ImhHiddenText) != 0) + inputType = android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD; + + if ((inputHints & ImhPreferNumbers) != 0) + inputType = android.text.InputType.TYPE_CLASS_NUMBER; + + if ((inputHints & ImhDigitsOnly) != 0) + inputType = android.text.InputType.TYPE_CLASS_NUMBER; + + if ((inputHints & ImhFormattedNumbersOnly) != 0) { + inputType = android.text.InputType.TYPE_CLASS_NUMBER + | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL + | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED; + } + + if ((inputHints & ImhDialableCharactersOnly) != 0) + inputType = android.text.InputType.TYPE_CLASS_PHONE; + + if ((inputHints & ImhEmailCharactersOnly) != 0) + inputType = android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; + + if ((inputHints & ImhUrlCharactersOnly) != 0) { + inputType = android.text.InputType.TYPE_TEXT_VARIATION_URI; + imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_GO; + } + + if ((inputHints & ImhNoPredictiveText) != 0) { + //android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | android.text.InputType.TYPE_CLASS_TEXT; + inputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; + } + + m_editText.setInitialCapsMode(initialCapsMode); + m_editText.setImeOptions(imeOptions); + m_editText.setInputType(inputType); + + m_layout.removeView(m_editText); + m_layout.addView(m_editText, new QtLayout.LayoutParams(width, height, x, y)); + m_editText.bringToFront(); + m_editText.requestFocus(); + m_editText.postDelayed(new Runnable() { + @Override + public void run() { + m_imm.showSoftInput(m_editText, 0); + m_keyboardIsVisible = true; + m_keyboardIsHiding = false; + m_editText.postDelayed(new Runnable() { + @Override + public void run() { + m_imm.restartInput(m_editText); + } + }, 25); + } + }, 15); + } + + public void hideSoftwareKeyboard() + { + if (m_imm == null) + return; + m_imm.hideSoftInputFromWindow(m_editText.getWindowToken(), 0); + m_keyboardIsVisible = false; + m_keyboardIsHiding = false; + } + + public boolean isSoftwareKeyboardVisible() + { + return m_keyboardIsVisible; + } + + String getAppIconSize(Activity a) + { + int size = a.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size); + if (size < 36 || size > 512) { // check size sanity + DisplayMetrics metrics = new DisplayMetrics(); + a.getWindowManager().getDefaultDisplay().getMetrics(metrics); + size = metrics.densityDpi/10*3; + if (size < 36) + size = 36; + + if (size > 512) + size = 512; + } + return "\tQT_ANDROID_APP_ICON_SIZE=" + size; + } + + public void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd) + { + if (m_imm == null) + return; + + m_imm.updateSelection(m_editText, selStart, selEnd, candidatesStart, candidatesEnd); + } + + public boolean loadApplication(Activity activity, ClassLoader classLoader, Bundle loaderParams) + { + /// check parameters integrity + if (!loaderParams.containsKey(NATIVE_LIBRARIES_KEY) + || !loaderParams.containsKey(BUNDLED_LIBRARIES_KEY) + || !loaderParams.containsKey(ENVIRONMENT_VARIABLES_KEY)) { + return false; + } + + m_activity = activity; + QtNative.setActivity(m_activity, this); + QtNative.setClassLoader(classLoader); + if (loaderParams.containsKey(STATIC_INIT_CLASSES_KEY)) { + for (String className: loaderParams.getStringArray(STATIC_INIT_CLASSES_KEY)) { + if (className.length() == 0) + continue; + + try { + @SuppressWarnings("rawtypes") + Class initClass = classLoader.loadClass(className); + Object staticInitDataObject = initClass.newInstance(); // create an instance + Method m = initClass.getMethod("setActivity", Activity.class, Object.class); + m.invoke(staticInitDataObject, m_activity, this); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + QtNative.loadQtLibraries(loaderParams.getStringArrayList(NATIVE_LIBRARIES_KEY)); + ArrayList libraries = loaderParams.getStringArrayList(BUNDLED_LIBRARIES_KEY); + QtNative.loadBundledLibraries(libraries, QtNativeLibrariesDir.nativeLibrariesDir(m_activity)); + m_mainLib = loaderParams.getString(MAIN_LIBRARY_KEY); + // older apps provide the main library as the last bundled library; look for this if the main library isn't provided + if (null == m_mainLib && libraries.size() > 0) + m_mainLib = libraries.get(libraries.size() - 1); + + try { + m_super_dispatchKeyEvent = m_activity.getClass().getMethod("super_dispatchKeyEvent", KeyEvent.class); + m_super_onRestoreInstanceState = m_activity.getClass().getMethod("super_onRestoreInstanceState", Bundle.class); + m_super_onRetainNonConfigurationInstance = m_activity.getClass().getMethod("super_onRetainNonConfigurationInstance"); + m_super_onSaveInstanceState = m_activity.getClass().getMethod("super_onSaveInstanceState", Bundle.class); + m_super_onKeyDown = m_activity.getClass().getMethod("super_onKeyDown", Integer.TYPE, KeyEvent.class); + m_super_onKeyUp = m_activity.getClass().getMethod("super_onKeyUp", Integer.TYPE, KeyEvent.class); + m_super_onConfigurationChanged = m_activity.getClass().getMethod("super_onConfigurationChanged", Configuration.class); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + int necessitasApiLevel = 1; + if (loaderParams.containsKey(NECESSITAS_API_LEVEL_KEY)) + necessitasApiLevel = loaderParams.getInt(NECESSITAS_API_LEVEL_KEY); + + m_environmentVariables = loaderParams.getString(ENVIRONMENT_VARIABLES_KEY); + String additionalEnvironmentVariables = "QT_ANDROID_FONTS_MONOSPACE=Droid Sans Mono;Droid Sans;Droid Sans Fallback" + + "\tNECESSITAS_API_LEVEL=" + necessitasApiLevel + + "\tHOME=" + m_activity.getFilesDir().getAbsolutePath() + + "\tTMPDIR=" + m_activity.getFilesDir().getAbsolutePath(); + if (android.os.Build.VERSION.SDK_INT < 14) + additionalEnvironmentVariables += "\tQT_ANDROID_FONTS=Droid Sans;Droid Sans Fallback"; + else + additionalEnvironmentVariables += "\tQT_ANDROID_FONTS=Roboto;Droid Sans;Droid Sans Fallback"; + + additionalEnvironmentVariables += getAppIconSize(activity); + + if (m_environmentVariables != null && m_environmentVariables.length() > 0) + m_environmentVariables = additionalEnvironmentVariables + "\t" + m_environmentVariables; + else + m_environmentVariables = additionalEnvironmentVariables; + + if (loaderParams.containsKey(APPLICATION_PARAMETERS_KEY)) + m_applicationParameters = loaderParams.getString(APPLICATION_PARAMETERS_KEY); + else + m_applicationParameters = ""; + + return true; + } + + public boolean startApplication() + { + // start application + try { + // FIXME turn on debuggable check + // if the applications is debuggable and it has a native debug request + Bundle extras = m_activity.getIntent().getExtras(); + if ( /*(ai.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0 + &&*/ extras != null + && extras.containsKey("native_debug") + && extras.getString("native_debug").equals("true")) { + try { + String packagePath = + m_activity.getPackageManager().getApplicationInfo(m_activity.getPackageName(), + PackageManager.GET_CONFIGURATIONS).dataDir + "/"; + String gdbserverPath = + extras.containsKey("gdbserver_path") + ? extras.getString("gdbserver_path") + : packagePath+"lib/gdbserver "; + + String socket = + extras.containsKey("gdbserver_socket") + ? extras.getString("gdbserver_socket") + : "+debug-socket"; + + // start debugger + m_debuggerProcess = Runtime.getRuntime().exec(gdbserverPath + + socket + + " --attach " + + android.os.Process.myPid(), + null, + new File(packagePath)); + } catch (IOException ioe) { + Log.e(QtNative.QtTAG,"Can't start debugger" + ioe.getMessage()); + } catch (SecurityException se) { + Log.e(QtNative.QtTAG,"Can't start debugger" + se.getMessage()); + } catch (NameNotFoundException e) { + Log.e(QtNative.QtTAG,"Can't start debugger" + e.getMessage()); + } + } + + + if ( /*(ai.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0 + &&*/ extras != null + && extras.containsKey("debug_ping") + && extras.getString("debug_ping").equals("true")) { + String packagePath = + m_activity.getPackageManager().getApplicationInfo(m_activity.getPackageName(), + PackageManager.GET_CONFIGURATIONS).dataDir + "/"; + String debugPing = packagePath + "debug_ping"; + int i = 0; + while (true) { + ++i; + Log.i(QtNative.QtTAG, "DEBUGGER: WAITING FOR PING AT " + debugPing + ", ATTEMPT " + i); + File file = new File(debugPing); + if (file.exists()) { + file.delete(); + break; + } + Thread.sleep(1000); + } + + Log.i(QtNative.QtTAG, "DEBUGGER: GOT PING " + debugPing); + } + + + if (/*(ai.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0 + &&*/ extras != null + && extras.containsKey("qml_debug") + && extras.getString("qml_debug").equals("true")) { + String qmljsdebugger; + if (extras.containsKey("qmljsdebugger")) { + qmljsdebugger = extras.getString("qmljsdebugger"); + qmljsdebugger.replaceAll("\\s", ""); // remove whitespace for security + } else { + qmljsdebugger = "port:3768"; + } + m_applicationParameters += "\t-qmljsdebugger=" + qmljsdebugger; + } + + if (null == m_surface) + onCreate(null); + String nativeLibraryDir = QtNativeLibrariesDir.nativeLibrariesDir(m_activity); + m_surface.applicationStarted( QtNative.startApplication(m_applicationParameters, + m_environmentVariables, + m_mainLib, + nativeLibraryDir)); + m_started = true; + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public void onTerminate() + { + QtNative.terminateQt(); + } + + public void onCreate(Bundle savedInstanceState) + { + m_quitApp = true; + if (null == savedInstanceState) { + DisplayMetrics metrics = new DisplayMetrics(); + m_activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); + QtNative.setApplicationDisplayMetrics(metrics.widthPixels, metrics.heightPixels, + metrics.widthPixels, metrics.heightPixels, + metrics.xdpi, metrics.ydpi); + } + m_layout = new QtLayout(m_activity); + m_surface = new QtSurface(m_activity, 0); + m_editText = new QtEditText(m_activity); + m_imm = (InputMethodManager)m_activity.getSystemService(Context.INPUT_METHOD_SERVICE); + m_layout.addView(m_surface,0); + m_activity.setContentView(m_layout, + new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + m_layout.bringChildToFront(m_surface); + m_activity.registerForContextMenu(m_layout); + + m_currentOrientation = m_activity.getResources().getConfiguration().orientation; + } + + public void onConfigurationChanged(Configuration configuration) + { + try { + m_super_onConfigurationChanged.invoke(m_activity, configuration); + } catch (Exception e) { + e.printStackTrace(); + } + + if (configuration.orientation != m_currentOrientation + && m_currentOrientation != Configuration.ORIENTATION_UNDEFINED) { + QtNative.handleOrientationChanged(configuration.orientation); + } + + m_currentOrientation = configuration.orientation; + } + + public void onDestroy() + { + if (m_quitApp) { + if (m_debuggerProcess != null) + m_debuggerProcess.destroy(); + System.exit(0);// FIXME remove it or find a better way + } + } + + public void onRestoreInstanceState(Bundle savedInstanceState) + { + try { + m_super_onRestoreInstanceState.invoke(m_activity, savedInstanceState); + } catch (Exception e) { + e.printStackTrace(); + } +// setFullScreen(savedInstanceState.getBoolean("FullScreen")); + m_started = savedInstanceState.getBoolean("Started"); + if (m_started) + m_surface.applicationStarted(true); + } + + public void onResume() + { + // fire all lostActions + synchronized (QtNative.m_mainActivityMutex) + { + Iterator itr = QtNative.getLostActions().iterator(); + while (itr.hasNext()) + m_activity.runOnUiThread(itr.next()); + + if (m_started) { + QtNative.clearLostActions(); + QtNative.updateWindow(); + } + } + } + + public Object onRetainNonConfigurationInstance() + { + try { + m_super_onRetainNonConfigurationInstance.invoke(m_activity); + } catch (Exception e) { + e.printStackTrace(); + } + m_quitApp = false; + return true; + } + + public void onSaveInstanceState(Bundle outState) { + try { + m_super_onSaveInstanceState.invoke(m_activity, outState); + } catch (Exception e) { + e.printStackTrace(); + } + outState.putBoolean("FullScreen", m_fullScreen); + outState.putBoolean("Started", m_started); + } + + public boolean onKeyDown(int keyCode, KeyEvent event) + { + if (!m_started) + return false; + + if (keyCode == KeyEvent.KEYCODE_MENU) { + try { + return (Boolean)m_super_onKeyDown.invoke(m_activity, keyCode, event); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + m_metaState = MetaKeyKeyListener.handleKeyDown(m_metaState, keyCode, event); + int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(m_metaState)); + int lc = c; + m_metaState = MetaKeyKeyListener.adjustMetaAfterKeypress(m_metaState); + + if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) { + c = c & KeyCharacterMap.COMBINING_ACCENT_MASK; + int composed = KeyEvent.getDeadChar(m_lastChar, c); + c = composed; + } + + m_lastChar = lc; + if (keyCode != KeyEvent.KEYCODE_BACK) + QtNative.keyDown(keyCode, c, event.getMetaState()); + + return true; + } + + public boolean onKeyUp(int keyCode, KeyEvent event) + { + if (!m_started) + return false; + + if (keyCode == KeyEvent.KEYCODE_MENU) { + try { + return (Boolean)m_super_onKeyUp.invoke(m_activity, keyCode, event); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + if (keyCode == KeyEvent.KEYCODE_BACK && m_keyboardIsVisible) + { + if (!m_keyboardIsHiding) + hideSoftwareKeyboard(); + return true; + } + + m_metaState = MetaKeyKeyListener.handleKeyUp(m_metaState, keyCode, event); + QtNative.keyUp(keyCode, event.getUnicodeChar(), event.getMetaState()); + return true; + } + + public boolean dispatchKeyEvent(KeyEvent event) + { + if (m_started + && event.getAction() == KeyEvent.ACTION_MULTIPLE + && event.getCharacters() != null + && event.getCharacters().length() == 1 + && event.getKeyCode() == 0) { + QtNative.keyDown(0, event.getCharacters().charAt(0), event.getMetaState()); + QtNative.keyUp(0, event.getCharacters().charAt(0), event.getMetaState()); + } + + try { + return (Boolean) m_super_dispatchKeyEvent.invoke(m_activity, event); + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + private boolean m_opionsMenuIsVisible = false; + public boolean onCreateOptionsMenu(Menu menu) + { + menu.clear(); + return true; + } + public boolean onPrepareOptionsMenu(Menu menu) + { + m_opionsMenuIsVisible = true; + return QtNative.onPrepareOptionsMenu(menu); + } + + public boolean onOptionsItemSelected(MenuItem item) + { + return QtNative.onOptionsItemSelected(item.getItemId(), item.isChecked()); + } + + public void onOptionsMenuClosed(Menu menu) + { + m_opionsMenuIsVisible = false; + QtNative.onOptionsMenuClosed(menu); + } + + public void resetOptionsMenu() + { + if (m_opionsMenuIsVisible) + m_activity.closeOptionsMenu(); + } + private boolean m_contextMenuVisible = false; + public void onCreateContextMenu(ContextMenu menu, + View v, + ContextMenuInfo menuInfo) + { + menu.clearHeader(); + QtNative.onCreateContextMenu(menu); + m_contextMenuVisible = true; + } + + public void onContextMenuClosed(Menu menu) + { + if (!m_contextMenuVisible) { + Log.e(QtNative.QtTAG, "invalid onContextMenuClosed call"); + return; + } + m_contextMenuVisible = false; + QtNative.onContextMenuClosed(menu); + } + + public boolean onContextItemSelected(MenuItem item) + { + return QtNative.onContextItemSelected(item.getItemId(), item.isChecked()); + } + + public void openContextMenu() + { + m_layout.postDelayed(new Runnable() { + @Override + public void run() { + m_activity.openContextMenu(m_layout); + } + }, 10); + } + + public void closeContextMenu() + { + m_activity.closeContextMenu(); + } +} diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java b/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java new file mode 100644 index 0000000000..b95e0c070c --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Android port of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt5.android; + +import android.content.Context; +import android.text.InputType; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; + +public class QtEditText extends View +{ + QtInputConnection m_inputConnection; + int m_initialCapsMode = 0; + int m_imeOptions = 0; + int m_inputType = InputType.TYPE_CLASS_TEXT; + + public void setImeOptions(int m_imeOptions) + { + this.m_imeOptions = m_imeOptions; + } + + public void setInitialCapsMode(int m_initialCapsMode) + { + this.m_initialCapsMode = m_initialCapsMode; + } + + + public void setInputType(int m_inputType) + { + this.m_inputType = m_inputType; + } + + public QtEditText(Context context) + { + super(context); + setFocusable(true); + setFocusableInTouchMode(true); + m_inputConnection = new QtInputConnection(this); + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) + { + outAttrs.inputType = m_inputType; + outAttrs.imeOptions = m_imeOptions; + outAttrs.initialCapsMode = m_initialCapsMode; + outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI; + return m_inputConnection; + } +// // DEBUG CODE +// @Override +// protected void onDraw(Canvas canvas) { +// canvas.drawARGB(127, 255, 0, 255); +// super.onDraw(canvas); +// } +} diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java b/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java new file mode 100644 index 0000000000..3bcec030b5 --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Android port of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt5.android; + +import android.content.Context; +import android.os.Build; +import android.view.View; +import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputMethodManager; + +class QtExtractedText +{ + public int partialEndOffset; + public int partialStartOffset; + public int selectionEnd; + public int selectionStart; + public int startOffset; + public String text; +} + +class QtNativeInputConnection +{ + static native boolean commitText(String text, int newCursorPosition); + static native boolean commitCompletion(String text, int position); + static native boolean deleteSurroundingText(int leftLength, int rightLength); + static native boolean finishComposingText(); + static native int getCursorCapsMode(int reqModes); + static native QtExtractedText getExtractedText(int hintMaxChars, int hintMaxLines, int flags); + static native String getSelectedText(int flags); + static native String getTextAfterCursor(int length, int flags); + static native String getTextBeforeCursor(int length, int flags); + static native boolean setComposingText(String text, int newCursorPosition); + static native boolean setSelection(int start, int end); + static native boolean selectAll(); + static native boolean cut(); + static native boolean copy(); + static native boolean copyURL(); + static native boolean paste(); +} + +public class QtInputConnection extends BaseInputConnection +{ + private static final int ID_SELECT_ALL = android.R.id.selectAll; + private static final int ID_CUT = android.R.id.cut; + private static final int ID_COPY = android.R.id.copy; + private static final int ID_PASTE = android.R.id.paste; + private static final int ID_COPY_URL = android.R.id.copyUrl; + private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod; + private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary; + + View m_view; + boolean m_closing; + public QtInputConnection(View targetView) + { + super(targetView, true); + m_view = targetView; + m_closing = false; + } + + @Override + public boolean beginBatchEdit() + { + m_closing = false; + return true; + } + + @Override + public boolean endBatchEdit() + { + m_closing = false; + return true; + } + + @Override + public boolean commitCompletion(CompletionInfo text) + { + m_closing = false; + return QtNativeInputConnection.commitCompletion(text.getText().toString(), text.getPosition()); + } + + @Override + public boolean commitText(CharSequence text, int newCursorPosition) + { + m_closing = false; + return QtNativeInputConnection.commitText(text.toString(), newCursorPosition); + } + + @Override + public boolean deleteSurroundingText(int leftLength, int rightLength) + { + m_closing = false; + return QtNativeInputConnection.deleteSurroundingText(leftLength, rightLength); + } + + @Override + public boolean finishComposingText() + { + if (m_closing) { + QtNative.activityDelegate().m_keyboardIsHiding = true; + m_view.postDelayed(new Runnable() { + @Override + public void run() { + if (QtNative.activityDelegate().m_keyboardIsHiding) + QtNative.activityDelegate().m_keyboardIsVisible=false; + } + }, 5000); // it seems finishComposingText comes musch faster than onKeyUp event, + // so we must delay hide notification + m_closing = false; + } else { + m_closing = true; + } + return QtNativeInputConnection.finishComposingText(); + } + + @Override + public int getCursorCapsMode(int reqModes) + { + return QtNativeInputConnection.getCursorCapsMode(reqModes); + } + + @Override + public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) + { + QtExtractedText qExtractedText = QtNativeInputConnection.getExtractedText(request.hintMaxChars, + request.hintMaxLines, + flags); + ExtractedText extractedText = new ExtractedText(); + extractedText.partialEndOffset = qExtractedText.partialEndOffset; + extractedText.partialStartOffset = qExtractedText.partialStartOffset; + extractedText.selectionEnd = qExtractedText.selectionEnd; + extractedText.selectionStart = qExtractedText.selectionStart; + extractedText.startOffset = qExtractedText.startOffset; + extractedText.text = qExtractedText.text; + return extractedText; + } + + public CharSequence getSelectedText(int flags) + { + return QtNativeInputConnection.getSelectedText(flags); + } + + @Override + public CharSequence getTextAfterCursor(int length, int flags) + { + return QtNativeInputConnection.getTextAfterCursor(length, flags); + } + + @Override + public CharSequence getTextBeforeCursor(int length, int flags) + { + return QtNativeInputConnection.getTextBeforeCursor(length, flags); + } + + @Override + public boolean performContextMenuAction(int id) + { + switch (id) { + case ID_SELECT_ALL: + return QtNativeInputConnection.selectAll(); + case ID_COPY: + return QtNativeInputConnection.copy(); + case ID_COPY_URL: + return QtNativeInputConnection.copyURL(); + case ID_CUT: + return QtNativeInputConnection.cut(); + case ID_PASTE: + return QtNativeInputConnection.paste(); + + case ID_SWITCH_INPUT_METHOD: + InputMethodManager imm = (InputMethodManager)m_view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) + imm.showInputMethodPicker(); + + return true; + + case ID_ADD_TO_DICTIONARY: +// TODO +// String word = m_editable.subSequence(0, m_editable.length()).toString(); +// if (word != null) { +// Intent i = new Intent("com.android.settings.USER_DICTIONARY_INSERT"); +// i.putExtra("word", word); +// i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); +// m_view.getContext().startActivity(i); +// } + return true; + } + return super.performContextMenuAction(id); + } + + @Override + public boolean setComposingText(CharSequence text, int newCursorPosition) + { + return QtNativeInputConnection.setComposingText(text.toString(), newCursorPosition); + } + + @Override + public boolean setSelection(int start, int end) + { + return QtNativeInputConnection.setSelection(start, end); + } +} diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtLayout.java b/src/android/jar/src/org/qtproject/qt5/android/QtLayout.java new file mode 100644 index 0000000000..043dab5ce8 --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt5/android/QtLayout.java @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Android port of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt5.android; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +public class QtLayout extends ViewGroup +{ + public QtLayout(Context context) + { + super(context); + } + + public QtLayout(Context context, AttributeSet attrs) + { + super(context, attrs); + } + + public QtLayout(Context context, AttributeSet attrs, int defStyle) + { + super(context, attrs, defStyle); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) + { + int count = getChildCount(); + + int maxHeight = 0; + int maxWidth = 0; + + // Find out how big everyone wants to be + measureChildren(widthMeasureSpec, heightMeasureSpec); + + // Find rightmost and bottom-most child + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + int childRight; + int childBottom; + + QtLayout.LayoutParams lp + = (QtLayout.LayoutParams) child.getLayoutParams(); + + childRight = lp.x + child.getMeasuredWidth(); + childBottom = lp.y + child.getMeasuredHeight(); + + maxWidth = Math.max(maxWidth, childRight); + maxHeight = Math.max(maxHeight, childBottom); + } + } + + // Check against minimum height and width + maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); + maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); + + setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), + resolveSize(maxHeight, heightMeasureSpec)); + } + + /** + * Returns a set of layout parameters with a width of + * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}, + * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} + * and with the coordinates (0, 0). + */ + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() + { + return new LayoutParams(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, + android.view.ViewGroup.LayoutParams.WRAP_CONTENT, + 0, + 0); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) + { + int count = getChildCount(); + + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + QtLayout.LayoutParams lp = + (QtLayout.LayoutParams) child.getLayoutParams(); + + int childLeft = lp.x; + int childTop = lp.y; + child.layout(childLeft, childTop, + childLeft + child.getMeasuredWidth(), + childTop + child.getMeasuredHeight()); + + } + } + } + + // Override to allow type-checking of LayoutParams. + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) + { + return p instanceof QtLayout.LayoutParams; + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) + { + return new LayoutParams(p); + } + + /** + * Per-child layout information associated with AbsoluteLayout. + * See + * {@link android.R.styleable#AbsoluteLayout_Layout Absolute Layout Attributes} + * for a list of all child view attributes that this class supports. + */ + public static class LayoutParams extends ViewGroup.LayoutParams + { + /** + * The horizontal, or X, location of the child within the view group. + */ + public int x; + /** + * The vertical, or Y, location of the child within the view group. + */ + public int y; + + /** + * Creates a new set of layout parameters with the specified width, + * height and location. + * + * @param width the width, either {@link #FILL_PARENT}, + {@link #WRAP_CONTENT} or a fixed size in pixels + * @param height the height, either {@link #FILL_PARENT}, + {@link #WRAP_CONTENT} or a fixed size in pixels + * @param x the X location of the child + * @param y the Y location of the child + */ + public LayoutParams(int width, int height, int x, int y) + { + super(width, height); + this.x = x; + this.y = y; + } + + /** + * {@inheritDoc} + */ + public LayoutParams(ViewGroup.LayoutParams source) + { + super(source); + } + } + + public void bringChildFront(int child) + { + bringChildToFront(getChildAt(child)); + } +} diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java new file mode 100644 index 0000000000..346bc1221a --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java @@ -0,0 +1,611 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Android port of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt5.android; + +import java.io.File; +import java.util.ArrayList; +import java.util.concurrent.Semaphore; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.text.ClipboardManager; +import android.util.Log; +import android.view.ContextMenu; +import android.view.Menu; +import android.view.MotionEvent; + +public class QtNative +{ + private static Activity m_activity = null; + private static QtActivityDelegate m_activityDelegate = null; + public static Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations + + public static final String QtTAG = "Qt JAVA"; // string used for Log.x + private static ArrayList m_lostActions = new ArrayList(); // a list containing all actions which could not be performed (e.g. the main activity is destroyed, etc.) + private static boolean m_started = false; + private static int m_displayMetricsScreenWidthPixels = 0; + private static int m_displayMetricsScreenHeightPixels = 0; + private static int m_displayMetricsDesktopWidthPixels = 0; + private static int m_displayMetricsDesktopHeightPixels = 0; + private static double m_displayMetricsXDpi = .0; + private static double m_displayMetricsYDpi = .0; + private static int m_oldx, m_oldy; + private static final int m_moveThreshold = 0; + private static ClipboardManager m_clipboardManager = null; + + private static ClassLoader m_classLoader = null; + public static ClassLoader classLoader() + { + return m_classLoader; + } + + public static void setClassLoader(ClassLoader classLoader) + { + m_classLoader = classLoader; + } + + public static Activity activity() + { + synchronized (m_mainActivityMutex) { + return m_activity; + } + } + + public static QtActivityDelegate activityDelegate() + { + synchronized (m_mainActivityMutex) { + return m_activityDelegate; + } + } + + public static void openURL(String url) + { + Uri uri = Uri.parse(url); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + activity().startActivity(intent); + } + + // this method loads full path libs + public static void loadQtLibraries(ArrayList libraries) + { + if (libraries == null) + return; + + for (String libName : libraries) { + try { + File f = new File(libName); + if (f.exists()) + System.load(libName); + } catch (SecurityException e) { + Log.i(QtTAG, "Can't load '" + libName + "'", e); + } catch (Exception e) { + Log.i(QtTAG, "Can't load '" + libName + "'", e); + } + } + } + + // this method loads bundled libs by name. + public static void loadBundledLibraries(ArrayList libraries, String nativeLibraryDir) + { + if (libraries == null) + return; + + for (String libName : libraries) { + try { + File f = new File(nativeLibraryDir+"lib"+libName+".so"); + if (f.exists()) + System.load(f.getAbsolutePath()); + else + Log.i(QtTAG, "Can't find '" + f.getAbsolutePath()); + } catch (Exception e) { + Log.i(QtTAG, "Can't load '" + libName + "'", e); + } + } + } + + public static void setActivity(Activity qtMainActivity, QtActivityDelegate qtActivityDelegate) + { + synchronized (m_mainActivityMutex) { + m_activity = qtMainActivity; + m_activityDelegate = qtActivityDelegate; + } + } + + static public ArrayList getLostActions() + { + return m_lostActions; + } + + static public void clearLostActions() + { + m_lostActions.clear(); + } + + private static boolean runAction(Runnable action) + { + synchronized (m_mainActivityMutex) { + if (m_activity == null) + m_lostActions.add(action); + else + m_activity.runOnUiThread(action); + return m_activity != null; + } + } + + public static boolean startApplication(String params, + String environment, + String mainLibrary, + String nativeLibraryDir) throws Exception + { + File f = new File(nativeLibraryDir + "lib" + mainLibrary + ".so"); + if (!f.exists()) + throw new Exception("Can't find main library '" + mainLibrary + "'"); + + if (params == null) + params = "-platform\tandroid"; + + boolean res = false; + synchronized (m_mainActivityMutex) { + res = startQtAndroidPlugin(); + setDisplayMetrics(m_displayMetricsScreenWidthPixels, + m_displayMetricsScreenHeightPixels, + m_displayMetricsDesktopWidthPixels, + m_displayMetricsDesktopHeightPixels, + m_displayMetricsXDpi, + m_displayMetricsYDpi); + if (params.length() > 0) + params = "\t" + params; + startQtApplication(f.getAbsolutePath() + "\t" + params, environment); + m_started = true; + } + return res; + } + + public static void setApplicationDisplayMetrics(int screenWidthPixels, + int screenHeightPixels, + int desktopWidthPixels, + int desktopHeightPixels, + double XDpi, + double YDpi) + { + /* Fix buggy dpi report */ + if (XDpi < android.util.DisplayMetrics.DENSITY_LOW) + XDpi = android.util.DisplayMetrics.DENSITY_LOW; + if (YDpi < android.util.DisplayMetrics.DENSITY_LOW) + YDpi = android.util.DisplayMetrics.DENSITY_LOW; + + synchronized (m_mainActivityMutex) { + if (m_started) { + setDisplayMetrics(screenWidthPixels, + screenHeightPixels, + desktopWidthPixels, + desktopHeightPixels, + XDpi, + YDpi); + } else { + m_displayMetricsScreenWidthPixels = screenWidthPixels; + m_displayMetricsScreenHeightPixels = screenHeightPixels; + m_displayMetricsDesktopWidthPixels = desktopWidthPixels; + m_displayMetricsDesktopHeightPixels = desktopHeightPixels; + m_displayMetricsXDpi = XDpi; + m_displayMetricsYDpi = YDpi; + } + } + } + + public static void pauseApplication() + { + synchronized (m_mainActivityMutex) { + if (m_started) + pauseQtApp(); + } + } + + public static void resumeApplication() + { + synchronized (m_mainActivityMutex) { + if (m_started) { + resumeQtApp(); + updateWindow(); + } + } + } + + // application methods + public static native void startQtApplication(String params, String env); + public static native void startQtApp(String params, String env); + public static native void pauseQtApp(); + public static native void resumeQtApp(); + public static native boolean startQtAndroidPlugin(); + public static native void quitQtAndroidPlugin(); + public static native void terminateQt(); + // application methods + + private static void quitApp() + { + m_activity.finish(); + } + + private static void redrawSurface(final int left, final int top, final int right, final int bottom ) + { + runAction(new Runnable() { + @Override + public void run() { + m_activityDelegate.redrawWindow(left, top, right, bottom); + } + }); + } + + //@ANDROID-5 + static private int getAction(int index, MotionEvent event) + { + int action = event.getAction(); + if (action == MotionEvent.ACTION_MOVE) { + int hsz = event.getHistorySize(); + if (hsz > 0) { + if (Math.abs(event.getX(index) - event.getHistoricalX(index, hsz-1)) > 1 + || Math.abs(event.getY(index) - event.getHistoricalY(index, hsz-1)) > 1) { + return 1; + } else { + return 2; + } + } + return 1; + } + + switch (index) { + case 0: + if (action == MotionEvent.ACTION_DOWN + || action == MotionEvent.ACTION_POINTER_1_DOWN) { + return 0; + } + + if (action == MotionEvent.ACTION_UP + || action == MotionEvent.ACTION_POINTER_1_UP) { + return 3; + } + break; + + case 1: + if (action == MotionEvent.ACTION_POINTER_2_DOWN + || action == MotionEvent.ACTION_POINTER_DOWN) { + return 0; + } + + if (action == MotionEvent.ACTION_POINTER_2_UP + || action == MotionEvent.ACTION_POINTER_UP) { + return 3; + } + break; + + case 2: + if (action == MotionEvent.ACTION_POINTER_3_DOWN + || action == MotionEvent.ACTION_POINTER_DOWN) { + return 0; + } + + if (action == MotionEvent.ACTION_POINTER_3_UP + || action == MotionEvent.ACTION_POINTER_UP) { + return 3; + } + + break; + } + return 2; + } + //@ANDROID-5 + + static public void sendTouchEvent(MotionEvent event, int id) + { + //@ANDROID-5 + touchBegin(id); + for (int i=0;i m_moveThreshold || Math.abs(dy) > m_moveThreshold) { + mouseMove(id, (int) event.getX(), (int) event.getY()); + m_oldx = (int) event.getX(); + m_oldy = (int) event.getY(); + } + break; + } + } + + static public void sendTrackballEvent(MotionEvent event, int id) + { + switch (event.getAction()) { + case MotionEvent.ACTION_UP: + mouseUp(id, (int) event.getX(), (int) event.getY()); + break; + + case MotionEvent.ACTION_DOWN: + mouseDown(id, (int) event.getX(), (int) event.getY()); + m_oldx = (int) event.getX(); + m_oldy = (int) event.getY(); + break; + + case MotionEvent.ACTION_MOVE: + int dx = (int) (event.getX() - m_oldx); + int dy = (int) (event.getY() - m_oldy); + if (Math.abs(dx) > 5 || Math.abs(dy) > 5) { + mouseMove(id, (int) event.getX(), (int) event.getY()); + m_oldx = (int) event.getX(); + m_oldy = (int) event.getY(); + } + break; + } + } + + private static void updateSelection(final int selStart, + final int selEnd, + final int candidatesStart, + final int candidatesEnd) + { + runAction(new Runnable() { + @Override + public void run() { + m_activityDelegate.updateSelection(selStart, selEnd, candidatesStart, candidatesEnd); + } + }); + } + + private static void showSoftwareKeyboard(final int x, + final int y, + final int width, + final int height, + final int inputHints ) + { + runAction(new Runnable() { + @Override + public void run() { + m_activityDelegate.showSoftwareKeyboard(x, y, width, height, inputHints); + } + }); + } + + private static void resetSoftwareKeyboard() + { + runAction(new Runnable() { + @Override + public void run() { + m_activityDelegate.resetSoftwareKeyboard(); + } + }); + } + + private static void hideSoftwareKeyboard() + { + runAction(new Runnable() { + @Override + public void run() { + m_activityDelegate.hideSoftwareKeyboard(); + } + }); + } + + private static boolean isSoftwareKeyboardVisible() + { + Semaphore semaphore = new Semaphore(1); + Boolean ret = false; + class RunnableRes implements Runnable { + @SuppressWarnings("unused") + Boolean returnValue = null; + Semaphore semaphore = null; + RunnableRes(Boolean ret, Semaphore sem) { + semaphore = sem; + returnValue = ret; + } + @Override + public void run() { + returnValue = m_activityDelegate.isSoftwareKeyboardVisible(); + semaphore.release(); + } + } + + runAction(new RunnableRes(ret, semaphore)); + try { + semaphore.acquire(); + } catch (Exception e) { + e.printStackTrace(); + } + return ret; + } + + private static void setFullScreen(final boolean fullScreen) + { + runAction(new Runnable() { + @Override + public void run() { + m_activityDelegate.setFullScreen(fullScreen); + updateWindow(); + } + }); + } + + private static void registerClipboardManager() + { + final Semaphore semaphore = new Semaphore(1); + runAction(new Runnable() { + @Override + public void run() { + m_clipboardManager = (android.text.ClipboardManager) m_activity.getSystemService(Context.CLIPBOARD_SERVICE); + semaphore.release(); + } + }); + try { + semaphore.acquire(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void setClipboardText(String text) + { + m_clipboardManager.setText(text); + } + + private static boolean hasClipboardText() + { + return m_clipboardManager.hasText(); + } + + private static String getClipboardText() + { + return m_clipboardManager.getText().toString(); + } + + private static void openContextMenu() + { + runAction(new Runnable() { + @Override + public void run() { + m_activityDelegate.openContextMenu(); + } + }); + } + + private static void closeContextMenu() + { + runAction(new Runnable() { + @Override + public void run() { + m_activityDelegate.closeContextMenu(); + } + }); + } + + private static void resetOptionsMenu() + { + runAction(new Runnable() { + @Override + public void run() { + m_activityDelegate.resetOptionsMenu(); + } + }); + } + + // screen methods + public static native void setDisplayMetrics(int screenWidthPixels, + int screenHeightPixels, + int desktopWidthPixels, + int desktopHeightPixels, + double XDpi, + double YDpi); + public static native void handleOrientationChanged(int newOrientation); + // screen methods + + // pointer methods + public static native void mouseDown(int winId, int x, int y); + public static native void mouseUp(int winId, int x, int y); + public static native void mouseMove(int winId, int x, int y); + public static native void touchBegin(int winId); + public static native void touchAdd(int winId, int pointerId, int action, boolean primary, int x, int y, float size, float pressure); + public static native void touchEnd(int winId, int action); + public static native void longPress(int winId, int x, int y); + // pointer methods + + // keyboard methods + public static native void keyDown(int key, int unicode, int modifier); + public static native void keyUp(int key, int unicode, int modifier); + // keyboard methods + + // surface methods + public static native void destroySurface(); + public static native void setSurface(Object surface); + public static native void lockSurface(); + public static native void unlockSurface(); + // surface methods + + // window methods + public static native void updateWindow(); + // window methods + + // menu methods + public static native boolean onPrepareOptionsMenu(Menu menu); + public static native boolean onOptionsItemSelected(int itemId, boolean checked); + public static native void onOptionsMenuClosed(Menu menu); + + public static native void onCreateContextMenu(ContextMenu menu); + public static native boolean onContextItemSelected(int itemId, boolean checked); + public static native void onContextMenuClosed(Menu menu); + // menu methods +} diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java b/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java new file mode 100644 index 0000000000..219ea13f1a --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Android port of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt5.android; + +import android.app.Activity; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; + +public class QtNativeLibrariesDir { + public static String nativeLibrariesDir(Activity activity) + { + String m_nativeLibraryDir = null; + try { + ApplicationInfo ai = activity.getPackageManager().getApplicationInfo(activity.getPackageName(), 0); + m_nativeLibraryDir = ai.nativeLibraryDir+"/"; + } catch (NameNotFoundException e) { + e.printStackTrace(); + } + return m_nativeLibraryDir; + } +} diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java b/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java new file mode 100644 index 0000000000..77126ec1c8 --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Android port of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt5.android; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.PixelFormat; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class QtSurface extends SurfaceView implements SurfaceHolder.Callback +{ + private Bitmap m_bitmap = null; + private boolean m_started = false; + private boolean m_usesGL = false; + private GestureDetector m_gestureDetector; + + public QtSurface(Context context, int id) + { + super(context); + setFocusable(true); + getHolder().addCallback(this); + getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU); + setId(id); + m_gestureDetector = + new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { + public void onLongPress(MotionEvent event) { + if (!m_started) + return; + QtNative.longPress(getId(), (int) event.getX(), (int) event.getY()); + } + }); + m_gestureDetector.setIsLongpressEnabled(true); + } + + public void applicationStarted(boolean usesGL) + { + m_started = true; + m_usesGL = usesGL; + if (getWidth() < 1 || getHeight() < 1) + return; + if (m_usesGL) { + QtNative.setSurface(getHolder().getSurface()); + } else { + QtNative.lockSurface(); + QtNative.setSurface(null); + m_bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565); + QtNative.setSurface(m_bitmap); + QtNative.unlockSurface(); + } + } + + @Override + public void surfaceCreated(SurfaceHolder holder) + { + DisplayMetrics metrics = new DisplayMetrics(); + ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics); + QtNative.setApplicationDisplayMetrics(metrics.widthPixels, + metrics.heightPixels, getWidth(), getHeight(), metrics.xdpi, metrics.ydpi); + + if (m_usesGL) + holder.setFormat(PixelFormat.RGBA_8888); + +// if (!m_started) +// return; +// +// if (m_usesGL) +// QtApplication.setSurface(holder.getSurface()); +// else +// { +// QtApplication.lockSurface(); +// QtApplication.setSurface(null); +// m_bitmap=Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565); +// QtApplication.setSurface(m_bitmap); +// QtApplication.unlockSurface(); +// } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) + { + if (width<1 || height<1) + return; + + DisplayMetrics metrics = new DisplayMetrics(); + ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics); + QtNative.setApplicationDisplayMetrics(metrics.widthPixels, + metrics.heightPixels, + width, + height, + metrics.xdpi, + metrics.ydpi); + + if (!m_started) + return; + + if (m_usesGL) { + QtNative.setSurface(holder.getSurface()); + } else { + QtNative.lockSurface(); + QtNative.setSurface(null); + m_bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + QtNative.setSurface(m_bitmap); + QtNative.unlockSurface(); + QtNative.updateWindow(); + } + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) + { + if (m_usesGL) { + QtNative.destroySurface(); + } else { + if (!m_started) + return; + + QtNative.lockSurface(); + QtNative.setSurface(null); + QtNative.unlockSurface(); + } + } + + public void drawBitmap(Rect rect) + { + if (!m_started) + return; + QtNative.lockSurface(); + if (null != m_bitmap) { + try { + Canvas cv = getHolder().lockCanvas(rect); + cv.drawBitmap(m_bitmap, rect, rect, null); + getHolder().unlockCanvasAndPost(cv); + } catch (Exception e) { + Log.e(QtNative.QtTAG, "Can't create main activity", e); + } + } + QtNative.unlockSurface(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) + { + if (!m_started) + return false; + QtNative.sendTouchEvent(event, getId()); + m_gestureDetector.onTouchEvent(event); + return true; + } + + @Override + public boolean onTrackballEvent(MotionEvent event) + { + if (!m_started) + return false; + QtNative.sendTrackballEvent(event, getId()); + return true; + } +} diff --git a/src/android/java/AndroidManifest.xml b/src/android/java/AndroidManifest.xml new file mode 100644 index 0000000000..7a77d6fd83 --- /dev/null +++ b/src/android/java/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/android/java/res/layout/splash.xml b/src/android/java/res/layout/splash.xml new file mode 100644 index 0000000000..6b0d492dd5 --- /dev/null +++ b/src/android/java/res/layout/splash.xml @@ -0,0 +1,13 @@ + + + + diff --git a/src/android/java/res/values-de/strings.xml b/src/android/java/res/values-de/strings.xml new file mode 100644 index 0000000000..320d9ec33f --- /dev/null +++ b/src/android/java/res/values-de/strings.xml @@ -0,0 +1,6 @@ + + + Ministro-Dienst wurde nicht gefunden.\nAnwendung kann nicht gestartet werden + Diese Anwendung benötigt den Ministro-Dienst. Möchten Sie ihn installieren? + In Ihrer Anwendung ist ein schwerwiegender Fehler aufgetreten, sie kann nicht fortgesetzt werden + diff --git a/src/android/java/res/values-el/strings.xml b/src/android/java/res/values-el/strings.xml new file mode 100644 index 0000000000..3cab212f2b --- /dev/null +++ b/src/android/java/res/values-el/strings.xml @@ -0,0 +1,6 @@ + + + Δεν ήταν δυνατή η εύρεση της υπηρεσίας Ministro. Δεν είναι δυνατή η εκκίνηση της εφαρμογής. + Η εφαρμογή απαιτεί την υπηρεσία Ministro. Να εγκατασταθεί η υπηρεσία? + Παρουσιάστηκε ένα κρίσιμο σφάλμα και η εφαρμογή δεν μπορεί να συνεχίσει. + diff --git a/src/android/java/res/values-es/strings.xml b/src/android/java/res/values-es/strings.xml new file mode 100644 index 0000000000..cf0b54d0b0 --- /dev/null +++ b/src/android/java/res/values-es/strings.xml @@ -0,0 +1,6 @@ + + + Servicio Ministro inesistente. Imposible ejecutar la aplicación. + Esta aplicación requiere el servicio Ministro. Instalarlo? + La aplicación ha causado un error grave y no es posible continuar. + diff --git a/src/android/java/res/values-et/strings.xml b/src/android/java/res/values-et/strings.xml new file mode 100644 index 0000000000..d55a3c1471 --- /dev/null +++ b/src/android/java/res/values-et/strings.xml @@ -0,0 +1,6 @@ + + + Ei suuda leida Ministro teenust.\nProgrammi ei saa käivitada. + See programm vajab Ministro teenust.\nKas soovite paigaldada? + Programmiga juhtus fataalne viga.\nKahjuks ei saa jätkata. + diff --git a/src/android/java/res/values-fa/strings.xml b/src/android/java/res/values-fa/strings.xml new file mode 100644 index 0000000000..a8d1b87444 --- /dev/null +++ b/src/android/java/res/values-fa/strings.xml @@ -0,0 +1,6 @@ + + + سرویس Ministro را پیدا نمی‌کند. برنامه نمی‌تواند آغاز شود. + این نرم‌افزار به سرویس Ministro احتیاج دارد. آیا دوست دارید آن را نصب کنید؟ + خطایی اساسی در برنامه‌تان رخ داد و اجرای برنامه نمی‌تواند ادامه یابد. + diff --git a/src/android/java/res/values-fr/strings.xml b/src/android/java/res/values-fr/strings.xml new file mode 100644 index 0000000000..efc0fb6e1e --- /dev/null +++ b/src/android/java/res/values-fr/strings.xml @@ -0,0 +1,6 @@ + + + Le service Ministro est introuvable.\nL\'application ne peut pas démarrer. + Cette application requiert le service Ministro. Voulez-vous l\'installer? + Votre application a rencontré une erreur fatale et ne peut pas continuer. + diff --git a/src/android/java/res/values-id/strings.xml b/src/android/java/res/values-id/strings.xml new file mode 100644 index 0000000000..aaa5bda0de --- /dev/null +++ b/src/android/java/res/values-id/strings.xml @@ -0,0 +1,6 @@ + + + Layanan Ministro tidak bisa ditemukan.\nAplikasi tidak bisa dimulai. + Aplikasi ini membutuhkan layanan Ministro. Apakah Anda ingin menginstalnya? + Aplikasi Anda mengalami kesalahan fatal dan tidak dapat melanjutkan. + diff --git a/src/android/java/res/values-it/strings.xml b/src/android/java/res/values-it/strings.xml new file mode 100644 index 0000000000..4773419c44 --- /dev/null +++ b/src/android/java/res/values-it/strings.xml @@ -0,0 +1,6 @@ + + + Servizio Ministro inesistente. Impossibile eseguire \nl\'applicazione. + Questa applicazione richiede il servizio Ministro.Installarlo? + L\'applicazione ha provocato un errore grave e non puo\' continuare. + diff --git a/src/android/java/res/values-ja/strings.xml b/src/android/java/res/values-ja/strings.xml new file mode 100644 index 0000000000..ba1cfda9ec --- /dev/null +++ b/src/android/java/res/values-ja/strings.xml @@ -0,0 +1,6 @@ + + + Ministroサービスが見つかりません。\nアプリケーションが起動できません。 + このアプリケーションにはMinistroサービスが必要です。 インストールしてもよろしいですか? + アプリケーションで致命的なエラーが発生したため続行できません。 + diff --git a/src/android/java/res/values-ms/strings.xml b/src/android/java/res/values-ms/strings.xml new file mode 100644 index 0000000000..6e3952eaec --- /dev/null +++ b/src/android/java/res/values-ms/strings.xml @@ -0,0 +1,6 @@ + + + Tidak jumpa servis Ministro.\nAplikasi tidak boleh dimulakan. + Aplikasi ini memerlukan servis Ministro. Adakah anda ingin pasang servis itu? + Aplikasi anda menemui ralat muat dan tidak boleh diteruskan. + diff --git a/src/android/java/res/values-nb/strings.xml b/src/android/java/res/values-nb/strings.xml new file mode 100644 index 0000000000..8a550e99a2 --- /dev/null +++ b/src/android/java/res/values-nb/strings.xml @@ -0,0 +1,6 @@ + + + Kan ikke finne tjenesten Ministro. Applikasjonen kan ikke starte. + Denne applikasjonen krever tjenesten Ministro. Vil du installere denne? + Applikasjonen fikk en kritisk feil og kan ikke fortsette + diff --git a/src/android/java/res/values-nl/strings.xml b/src/android/java/res/values-nl/strings.xml new file mode 100644 index 0000000000..8a45a724ff --- /dev/null +++ b/src/android/java/res/values-nl/strings.xml @@ -0,0 +1,6 @@ + + + De Ministro service is niet gevonden.\nDe applicatie kan niet starten. + Deze applicatie maakt gebruik van de Ministro service. Wilt u deze installeren? + Er is een fatale fout in de applicatie opgetreden. De applicatie kan niet verder gaan. + diff --git a/src/android/java/res/values-pl/strings.xml b/src/android/java/res/values-pl/strings.xml new file mode 100644 index 0000000000..9fefc92dcd --- /dev/null +++ b/src/android/java/res/values-pl/strings.xml @@ -0,0 +1,6 @@ + + + Usługa Ministro nie została znaleziona.\nAplikacja nie może zostać uruchomiona. + Aplikacja wymaga usługi Ministro. Czy chcesz ją zainstalować? + Wystąpił błąd krytyczny. Aplikacja zostanie zamknięta. + diff --git a/src/android/java/res/values-pt-rBR/strings.xml b/src/android/java/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000000..67ac3f9f98 --- /dev/null +++ b/src/android/java/res/values-pt-rBR/strings.xml @@ -0,0 +1,6 @@ + + + Não foi possível encontrar o serviço Ministro.\nA aplicação não pode iniciar. + Essa aplicação requer o serviço Ministro. Gostaria de instalá-lo? + Sua aplicação encontrou um erro fatal e não pode continuar. + diff --git a/src/android/java/res/values-ro/strings.xml b/src/android/java/res/values-ro/strings.xml new file mode 100644 index 0000000000..f88a442b35 --- /dev/null +++ b/src/android/java/res/values-ro/strings.xml @@ -0,0 +1,6 @@ + + + Serviciul Ministro nu poate fi găsit.\nAplicaţia nu poate porni. + Această aplicaţie necesită serviciul Ministro.\nDoriţi să-l instalaţi? + Aplicaţia dumneavoastră a întâmpinat o eroare fatală şi nu poate continua. + diff --git a/src/android/java/res/values-rs/strings.xml b/src/android/java/res/values-rs/strings.xml new file mode 100644 index 0000000000..3194ce9022 --- /dev/null +++ b/src/android/java/res/values-rs/strings.xml @@ -0,0 +1,6 @@ + + + Ministro servise nije pronađen. Aplikacija ne može biti pokrenuta. + Ova aplikacija zahteva Ministro servis. Želite li da ga instalirate? + Vaša aplikacija je naišla na fatalnu grešku i ne može nastaviti sa radom. + diff --git a/src/android/java/res/values-ru/strings.xml b/src/android/java/res/values-ru/strings.xml new file mode 100644 index 0000000000..d3cee80f9d --- /dev/null +++ b/src/android/java/res/values-ru/strings.xml @@ -0,0 +1,6 @@ + + + Сервис Ministro не найден.\nПриложение нельзя запустить. + Этому приложению необходим сервис Ministro. Вы хотите его установить? + Ваше приложение столкнулось с фатальной ошибкой и не может более работать. + diff --git a/src/android/java/res/values-zh-rCN/strings.xml b/src/android/java/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..2eb1269880 --- /dev/null +++ b/src/android/java/res/values-zh-rCN/strings.xml @@ -0,0 +1,6 @@ + + + 无法找到Ministro服务。\n应用程序无法启动。 + 此应用程序需要Ministro服务。您想安装它吗? + 您的应用程序遇到一个致命错误导致它无法继续。 + diff --git a/src/android/java/res/values-zh-rTW/strings.xml b/src/android/java/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..f6e68efa52 --- /dev/null +++ b/src/android/java/res/values-zh-rTW/strings.xml @@ -0,0 +1,6 @@ + + + 無法找到Ministro服務。\n應用程序無法啟動。 + 此應用程序需要Ministro服務。您想安裝它嗎? + 您的應用程序遇到一個致命錯誤導致它無法繼續。 + diff --git a/src/android/java/res/values/libs.xml b/src/android/java/res/values/libs.xml new file mode 100644 index 0000000000..92ce09b7c0 --- /dev/null +++ b/src/android/java/res/values/libs.xml @@ -0,0 +1,8 @@ + + + + QtCore + QtGui + + + diff --git a/src/android/java/res/values/strings.xml b/src/android/java/res/values/strings.xml new file mode 100644 index 0000000000..bd6928fe59 --- /dev/null +++ b/src/android/java/res/values/strings.xml @@ -0,0 +1,7 @@ + + + + Can\'t find Ministro service.\nThe application can\'t start. + This application requires Ministro service. Would you like to install it? + Your application encountered a fatal error and cannot continue. + diff --git a/src/android/java/src/org/kde/necessitas/ministro/IMinistro.aidl b/src/android/java/src/org/kde/necessitas/ministro/IMinistro.aidl new file mode 100644 index 0000000000..1c33a77fdb --- /dev/null +++ b/src/android/java/src/org/kde/necessitas/ministro/IMinistro.aidl @@ -0,0 +1,49 @@ +/* + Copyright (c) 2012, BogDan Vatra + Contact: http://www.qt-project.org/legal + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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 AUTHOR ``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 THE AUTHOR 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. +*/ + +package org.kde.necessitas.ministro; + +import org.kde.necessitas.ministro.IMinistroCallback; + +interface IMinistro +{ +/** +* Check/download required libs to run the application +* +* param callback - interface used by Minsitro service to notify the client when the loader is ready +* param parameters +* parameters fields: +* * Key Name Key type Explanations +* "required.modules" StringArray Required modules by your application +* "application.title" String Application name, used to show more informations to user +* "qt.provider" String Qt libs provider, currently only "necessitas" is supported. +* "minimum.ministro.api" Integer Minimum Ministro API level, used to check if Ministro service compatible with your application. Current API Level is 1 ! +* "minimum.qt.version" Integer Minimim Qt version (e.g. 0x040800, which means Qt 4.8.0, check http://doc.trolltech.com/4.8/qtglobal.html#QT_VERSION)! +* "3rd.party.repositories" StringArray 3rd party repositories, which should be downloaded by ministro, needs minimum.ministro.api >= 2 +* Check http://community.kde.org/Necessitas/Ministro for more details. +*/ + void requestLoader(in IMinistroCallback callback, in Bundle parameters); +} diff --git a/src/android/java/src/org/kde/necessitas/ministro/IMinistroCallback.aidl b/src/android/java/src/org/kde/necessitas/ministro/IMinistroCallback.aidl new file mode 100644 index 0000000000..eec270aec1 --- /dev/null +++ b/src/android/java/src/org/kde/necessitas/ministro/IMinistroCallback.aidl @@ -0,0 +1,53 @@ +/* + Copyright (c) 2012, BogDan Vatra + Contact: http://www.qt-project.org/legal + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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 AUTHOR ``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 THE AUTHOR 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. +*/ + +package org.kde.necessitas.ministro; + +oneway interface IMinistroCallback { +/** +* This method is called by the Ministro service back into the application which +* implements this interface. +* +* param in - loaderParams +* loaderParams fields: +* * Key Name Key type Explanations +* * "error.code" Integer See below +* * "error.message" String Missing if no error, otherwise will contain the error message translated into phone language where available. +* * "dex.path" String The list of jar/apk files containing classes and resources, needed to be passed to application DexClassLoader +* * "lib.path" String The list of directories containing native libraries; may be missing, needed to be passed to application DexClassLoader +* * "loader.class.name" String Loader class name. +* +* "error.code" field possible errors: +* - 0 no error. +* - 1 incompatible Ministro version. Ministro needs to be upgraded. +* - 2 not all modules could be satisfy. +* - 3 invalid parameters +* +* This parameter will contain additional fields which are used by the loader to start your application, so it must be passed to loader. +*/ + + void loaderReady(in Bundle loaderParams); +} diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java new file mode 100644 index 0000000000..c3d82bca8d --- /dev/null +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java @@ -0,0 +1,1278 @@ +/* + Copyright (c) 2012, BogDan Vatra + Contact: http://www.qt-project.org/legal + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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 AUTHOR ``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 THE AUTHOR 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. +*/ + +package org.qtproject.qt5.android.bindings; + +import java.io.File; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; + +import org.kde.necessitas.ministro.IMinistro; +import org.kde.necessitas.ministro.IMinistroCallback; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Configuration; +import android.content.res.Resources.Theme; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.net.Uri; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager.LayoutParams; +import android.view.accessibility.AccessibilityEvent; +import dalvik.system.DexClassLoader; + +//@ANDROID-11 +import android.app.Fragment; +import android.view.ActionMode; +import android.view.ActionMode.Callback; +//@ANDROID-11 + +public class QtActivity extends Activity +{ + private final static int MINISTRO_INSTALL_REQUEST_CODE = 0xf3ee; // request code used to know when Ministro instalation is finished + private static final int MINISTRO_API_LEVEL = 2; // Ministro api level (check IMinistro.aidl file) + private static final int NECESSITAS_API_LEVEL = 2; // Necessitas api level used by platform plugin + private static final String QT_PROVIDER = "necessitas"; + private static final int QT_VERSION = 0x050100; // This app requires at least Qt version 5.1.0 + + private static final String ERROR_CODE_KEY = "error.code"; + private static final String ERROR_MESSAGE_KEY = "error.message"; + private static final String DEX_PATH_KEY = "dex.path"; + private static final String LIB_PATH_KEY = "lib.path"; + private static final String LOADER_CLASS_NAME_KEY = "loader.class.name"; + private static final String NATIVE_LIBRARIES_KEY = "native.libraries"; + private static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables"; + private static final String APPLICATION_PARAMETERS_KEY = "application.parameters"; + private static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries"; + private static final String MAIN_LIBRARY_KEY = "main.library"; + private static final String STATIC_INIT_CLASSES_KEY = "static.init.classes"; + private static final String NECESSITAS_API_LEVEL_KEY = "necessitas.api.level"; + + /// Ministro server parameter keys + private static final String REQUIRED_MODULES_KEY = "required.modules"; + private static final String APPLICATION_TITLE_KEY = "application.title"; + private static final String QT_PROVIDER_KEY = "qt.provider"; + private static final String MINIMUM_MINISTRO_API_KEY = "minimum.ministro.api"; + private static final String MINIMUM_QT_VERSION_KEY = "minimum.qt.version"; +// private static final String REPOSITORIES="3rd.party.repositories"; // needs MINISTRO_API_LEVEL >=2 !!! + // Use this key to specify any 3rd party repositories urls + // Ministro will download these repositories into thier + // own folders, check http://community.kde.org/Necessitas/Ministro + // for more details. + + private static final String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application, + // the parameters must not contain any white spaces + // and must be separated with "\t" + // e.g "-param1\t-param2=value2\t-param3\tvalue3" + + private static final String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_STYLE=1\t"; + // use this variable to add any environment variables to your application. + // the env vars must be separated with "\t" + // e.g. "ENV_VAR1=1\tENV_VAR2=2\t" + // Currently the following vars are used by the android plugin: + // * QT_USE_ANDROID_NATIVE_STYLE - 0 if you don't want to use android style plugin, it will save a few ms at startup. + + private static final int INCOMPATIBLE_MINISTRO_VERSION = 1; // Incompatible Ministro version. Ministro needs to be upgraded. + private ActivityInfo m_activityInfo = null; // activity info object, used to access the libs and the strings + private DexClassLoader m_classLoader = null; // loader object + private String[] m_qtLibs = null; // required qt libs + + // this function is used to load and start the loader + private void loadApplication(Bundle loaderParams) + { + try { + final int errorCode = loaderParams.getInt(ERROR_CODE_KEY); + if (errorCode != 0) { + if (errorCode == INCOMPATIBLE_MINISTRO_VERSION) { + downloadUpgradeMinistro(loaderParams.getString(ERROR_MESSAGE_KEY)); + return; + } + + // fatal error, show the error and quit + AlertDialog errorDialog = new AlertDialog.Builder(QtActivity.this).create(); + errorDialog.setMessage(loaderParams.getString(ERROR_MESSAGE_KEY)); + errorDialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + errorDialog.show(); + return; + } + + // add all bundled Qt libs to loader params + ArrayList libs = new ArrayList(); + if ( m_activityInfo.metaData.containsKey("android.app.bundled_libs_resource_id") ) + libs.addAll(Arrays.asList(getResources().getStringArray(m_activityInfo.metaData.getInt("android.app.bundled_libs_resource_id")))); + + String libName = null; + if ( m_activityInfo.metaData.containsKey("android.app.lib_name") ) { + libName = m_activityInfo.metaData.getString("android.app.lib_name"); + loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function + } + + loaderParams.putStringArrayList(BUNDLED_LIBRARIES_KEY, libs); + loaderParams.putInt(NECESSITAS_API_LEVEL_KEY, NECESSITAS_API_LEVEL); + + // load and start QtLoader class + m_classLoader = new DexClassLoader(loaderParams.getString(DEX_PATH_KEY), // .jar/.apk files + getDir("outdex", Context.MODE_PRIVATE).getAbsolutePath(), // directory where optimized DEX files should be written. + loaderParams.containsKey(LIB_PATH_KEY) ? loaderParams.getString(LIB_PATH_KEY) : null, // libs folder (if exists) + getClassLoader()); // parent loader + + @SuppressWarnings("rawtypes") + Class loaderClass = m_classLoader.loadClass(loaderParams.getString(LOADER_CLASS_NAME_KEY)); // load QtLoader class + Object qtLoader = loaderClass.newInstance(); // create an instance + Method perpareAppMethod = qtLoader.getClass().getMethod("loadApplication", + Activity.class, + ClassLoader.class, + Bundle.class); + if (!(Boolean)perpareAppMethod.invoke(qtLoader, this, m_classLoader, loaderParams)) + throw new Exception(""); + + QtApplication.setQtActivityDelegate(qtLoader); + + // now load the application library so it's accessible from this class loader + if (libName != null) + System.loadLibrary(libName); + + Method startAppMethod=qtLoader.getClass().getMethod("startApplication"); + if (!(Boolean)startAppMethod.invoke(qtLoader)) + throw new Exception(""); + + } catch (Exception e) { + e.printStackTrace(); + AlertDialog errorDialog = new AlertDialog.Builder(QtActivity.this).create(); + if (m_activityInfo != null && m_activityInfo.metaData.containsKey("android.app.fatal_error_msg")) + errorDialog.setMessage(m_activityInfo.metaData.getString("android.app.fatal_error_msg")); + else + errorDialog.setMessage("Fatal error, your application can't be started."); + + errorDialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + errorDialog.show(); + } + } + + private ServiceConnection m_ministroConnection=new ServiceConnection() { + private IMinistro m_service = null; + @Override + public void onServiceConnected(ComponentName name, IBinder service) + { + m_service = IMinistro.Stub.asInterface(service); + try { + if (m_service!=null) { + Bundle parameters= new Bundle(); + parameters.putStringArray(REQUIRED_MODULES_KEY, m_qtLibs); + parameters.putString(APPLICATION_TITLE_KEY, (String)QtActivity.this.getTitle()); + parameters.putInt(MINIMUM_MINISTRO_API_KEY, MINISTRO_API_LEVEL); + parameters.putString(QT_PROVIDER_KEY, QT_PROVIDER); + parameters.putInt(MINIMUM_QT_VERSION_KEY, QT_VERSION); + parameters.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES); + if (null!=APPLICATION_PARAMETERS) + parameters.putString(APPLICATION_PARAMETERS_KEY, APPLICATION_PARAMETERS); + // parameters.putStringArray(REPOSITORIES, null); + m_service.requestLoader(m_ministroCallback, parameters); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + private IMinistroCallback m_ministroCallback = new IMinistroCallback.Stub() { + // this function is called back by Ministro. + @Override + public void loaderReady(final Bundle loaderParams) throws RemoteException { + runOnUiThread(new Runnable() { + @Override + public void run() { + unbindService(m_ministroConnection); + loadApplication(loaderParams); + } + }); + } + }; + + @Override + public void onServiceDisconnected(ComponentName name) { + m_service = null; + } + }; + + private void downloadUpgradeMinistro(String msg) + { + AlertDialog.Builder downloadDialog = new AlertDialog.Builder(this); + downloadDialog.setMessage(msg); + downloadDialog.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + try { + Uri uri = Uri.parse("market://search?q=pname:org.kde.necessitas.ministro"); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivityForResult(intent, MINISTRO_INSTALL_REQUEST_CODE); + } catch (Exception e) { + e.printStackTrace(); + ministroNotFound(); + } + } + }); + + downloadDialog.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + QtActivity.this.finish(); + } + }); + downloadDialog.show(); + } + + private void ministroNotFound() + { + AlertDialog errorDialog = new AlertDialog.Builder(QtActivity.this).create(); + + if (m_activityInfo != null && m_activityInfo.metaData.containsKey("android.app.ministro_not_found_msg")) + errorDialog.setMessage(m_activityInfo.metaData.getString("android.app.ministro_not_found_msg")); + else + errorDialog.setMessage("Can't find Ministro service.\nThe application can't start."); + + errorDialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + errorDialog.show(); + } + + private void startApp(final boolean firstStart) + { + try { + ActivityInfo ai=getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); + if (ai.metaData.containsKey("android.app.qt_libs_resource_id")) { + int resourceId = ai.metaData.getInt("android.app.qt_libs_resource_id"); + m_qtLibs = getResources().getStringArray(resourceId); + } + + if (getIntent().getExtras()!= null + && getIntent().getExtras().containsKey("use_local_qt_libs") + && getIntent().getExtras().getString("use_local_qt_libs").equals("true")) { + ArrayList libraryList= new ArrayList(); + + String localPrefix="/data/local/tmp/qt/"; + if (getIntent().getExtras().containsKey("libs_prefix")) + localPrefix=getIntent().getExtras().getString("libs_prefix"); + + if (m_qtLibs != null) { + for (int i=0;i 0) + libraryList.add(localPrefix + lib); + } + } + + String dexPaths = new String(); + String pathSeparator = System.getProperty("path.separator", ":"); + if (getIntent().getExtras().containsKey("load_local_jars")) { + String[] jarFiles = getIntent().getExtras().getString("load_local_jars").split(":"); + for (String jar:jarFiles) { + if (jar.length() > 0) { + if (dexPaths.length() > 0) + dexPaths += pathSeparator; + dexPaths += localPrefix + jar; + } + } + } + + Bundle loaderParams = new Bundle(); + loaderParams.putInt(ERROR_CODE_KEY, 0); + loaderParams.putString(DEX_PATH_KEY, dexPaths); + loaderParams.putString(LOADER_CLASS_NAME_KEY, getIntent().getExtras().containsKey("loader_class_name") + ? getIntent().getExtras().getString("loader_class_name") + : "org.qtproject.qt5.android.QtActivityDelegate"); + if (getIntent().getExtras().containsKey("static_init_classes")) { + loaderParams.putStringArray(STATIC_INIT_CLASSES_KEY, + getIntent().getExtras().getString("static_init_classes").split(":")); + } + loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList); + loaderParams.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES + + "QT_QPA_EGLFS_HIDECURSOR=1" + + "\tQML2_IMPORT_PATH=" + localPrefix + "/qml" + + "\tQML_IMPORT_PATH=" + localPrefix + "/imports" + + "\tQT_PLUGIN_PATH=" + localPrefix + "/plugins"); + loadApplication(loaderParams); + return; + } + + try { + if (!bindService(new Intent(org.kde.necessitas.ministro.IMinistro.class.getCanonicalName()), + m_ministroConnection, + Context.BIND_AUTO_CREATE)) { + throw new SecurityException(""); + } + } catch (Exception e) { + if (firstStart) { + String msg = "This application requires Ministro service. Would you like to install it?"; + if (m_activityInfo != null && m_activityInfo.metaData.containsKey("android.app.ministro_needed_msg")) + msg = m_activityInfo.metaData.getString("android.app.ministro_needed_msg"); + downloadUpgradeMinistro(msg); + } else { + ministroNotFound(); + } + } + } catch (Exception e) { + Log.e(QtApplication.QtTAG, "Can't create main activity", e); + } + } + + + + /////////////////////////// forward all notifications //////////////////////////// + /////////////////////////// Super class calls //////////////////////////////////// + /////////////// PLEASE DO NOT CHANGE THE FOLLOWING CODE ////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + + @Override + public boolean dispatchKeyEvent(KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyEvent, event); + else + return super.dispatchKeyEvent(event); + } + public boolean super_dispatchKeyEvent(KeyEvent event) + { + return super.dispatchKeyEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchPopulateAccessibilityEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchPopulateAccessibilityEvent, event); + else + return super.dispatchPopulateAccessibilityEvent(event); + } + public boolean super_dispatchPopulateAccessibilityEvent(AccessibilityEvent event) + { + return super_dispatchPopulateAccessibilityEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchTouchEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTouchEvent, ev); + else + return super.dispatchTouchEvent(ev); + } + public boolean super_dispatchTouchEvent(MotionEvent event) + { + return super.dispatchTouchEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean dispatchTrackballEvent(MotionEvent ev) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchTrackballEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTrackballEvent, ev); + else + return super.dispatchTrackballEvent(ev); + } + public boolean super_dispatchTrackballEvent(MotionEvent event) + { + return super.dispatchTrackballEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) + { + + if (QtApplication.m_delegateObject != null && QtApplication.onActivityResult != null) { + QtApplication.invokeDelegateMethod(QtApplication.onActivityResult, requestCode, resultCode, data); + return; + } + if (requestCode == MINISTRO_INSTALL_REQUEST_CODE) + startApp(false); + super.onActivityResult(requestCode, resultCode, data); + } + public void super_onActivityResult(int requestCode, int resultCode, Intent data) + { + super.onActivityResult(requestCode, resultCode, data); + } + //--------------------------------------------------------------------------- + + @Override + protected void onApplyThemeResource(Theme theme, int resid, boolean first) + { + if (!QtApplication.invokeDelegate(theme, resid, first).invoked) + super.onApplyThemeResource(theme, resid, first); + } + public void super_onApplyThemeResource(Theme theme, int resid, boolean first) + { + super.onApplyThemeResource(theme, resid, first); + } + //--------------------------------------------------------------------------- + + + @Override + protected void onChildTitleChanged(Activity childActivity, CharSequence title) + { + if (!QtApplication.invokeDelegate(childActivity, title).invoked) + super.onChildTitleChanged(childActivity, title); + } + public void super_onChildTitleChanged(Activity childActivity, CharSequence title) + { + super.onChildTitleChanged(childActivity, title); + } + //--------------------------------------------------------------------------- + + @Override + public void onConfigurationChanged(Configuration newConfig) + { + if (!QtApplication.invokeDelegate(newConfig).invoked) + super.onConfigurationChanged(newConfig); + } + public void super_onConfigurationChanged(Configuration newConfig) + { + super.onConfigurationChanged(newConfig); + } + //--------------------------------------------------------------------------- + + @Override + public void onContentChanged() + { + if (!QtApplication.invokeDelegate().invoked) + super.onContentChanged(); + } + public void super_onContentChanged() + { + super.onContentChanged(); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onContextItemSelected(MenuItem item) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(item); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onContextItemSelected(item); + } + public boolean super_onContextItemSelected(MenuItem item) + { + return super.onContextItemSelected(item); + } + //--------------------------------------------------------------------------- + + @Override + public void onContextMenuClosed(Menu menu) + { + if (!QtApplication.invokeDelegate(menu).invoked) + super.onContextMenuClosed(menu); + } + public void super_onContextMenuClosed(Menu menu) + { + super.onContextMenuClosed(menu); + } + //--------------------------------------------------------------------------- + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + if (QtApplication.m_delegateObject != null && QtApplication.onCreate != null) { + QtApplication.invokeDelegateMethod(QtApplication.onCreate, savedInstanceState); + return; + } + + requestWindowFeature(Window.FEATURE_NO_TITLE); + try { + m_activityInfo = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); + } catch (NameNotFoundException e) { + e.printStackTrace(); + finish(); + return; + } + + if (null == getLastNonConfigurationInstance()) { + // if splash screen is defined, then show it + if (m_activityInfo.metaData.containsKey("android.app.splash_screen") ) + setContentView(m_activityInfo.metaData.getInt("android.app.splash_screen")); + startApp(true); + } + } + //--------------------------------------------------------------------------- + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) + { + if (!QtApplication.invokeDelegate(menu, v, menuInfo).invoked) + super.onCreateContextMenu(menu, v, menuInfo); + } + public void super_onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) + { + super.onCreateContextMenu(menu, v, menuInfo); + } + //--------------------------------------------------------------------------- + + @Override + public CharSequence onCreateDescription() + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(); + if (res.invoked) + return (CharSequence)res.methodReturns; + else + return super.onCreateDescription(); + } + public CharSequence super_onCreateDescription() + { + return super.onCreateDescription(); + } + //--------------------------------------------------------------------------- + + @Override + protected Dialog onCreateDialog(int id) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(id); + if (res.invoked) + return (Dialog)res.methodReturns; + else + return super.onCreateDialog(id); + } + public Dialog super_onCreateDialog(int id) + { + return super.onCreateDialog(id); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onCreateOptionsMenu(Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onCreateOptionsMenu(menu); + } + public boolean super_onCreateOptionsMenu(Menu menu) + { + return super.onCreateOptionsMenu(menu); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onCreatePanelMenu(int featureId, Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onCreatePanelMenu(featureId, menu); + } + public boolean super_onCreatePanelMenu(int featureId, Menu menu) + { + return super.onCreatePanelMenu(featureId, menu); + } + //--------------------------------------------------------------------------- + + + @Override + public View onCreatePanelView(int featureId) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId); + if (res.invoked) + return (View)res.methodReturns; + else + return super.onCreatePanelView(featureId); + } + public View super_onCreatePanelView(int featureId) + { + return super.onCreatePanelView(featureId); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(outBitmap, canvas); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onCreateThumbnail(outBitmap, canvas); + } + public boolean super_onCreateThumbnail(Bitmap outBitmap, Canvas canvas) + { + return super.onCreateThumbnail(outBitmap, canvas); + } + //--------------------------------------------------------------------------- + + @Override + public View onCreateView(String name, Context context, AttributeSet attrs) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(name, context, attrs); + if (res.invoked) + return (View)res.methodReturns; + else + return super.onCreateView(name, context, attrs); + } + public View super_onCreateView(String name, Context context, AttributeSet attrs) + { + return super.onCreateView(name, context, attrs); + } + //--------------------------------------------------------------------------- + + @Override + protected void onDestroy() + { + super.onDestroy(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyDown != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyDown, keyCode, event); + else + return super.onKeyDown(keyCode, event); + } + public boolean super_onKeyDown(int keyCode, KeyEvent event) + { + return super.onKeyDown(keyCode, event); + } + //--------------------------------------------------------------------------- + + + @Override + public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyMultiple != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyMultiple, keyCode, repeatCount, event); + else + return super.onKeyMultiple(keyCode, repeatCount, event); + } + public boolean super_onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) + { + return super.onKeyMultiple(keyCode, repeatCount, event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyDown != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyUp, keyCode, event); + else + return super.onKeyUp(keyCode, event); + } + public boolean super_onKeyUp(int keyCode, KeyEvent event) + { + return super.onKeyUp(keyCode, event); + } + //--------------------------------------------------------------------------- + + @Override + public void onLowMemory() + { + if (!QtApplication.invokeDelegate().invoked) + super.onLowMemory(); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, item); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onMenuItemSelected(featureId, item); + } + public boolean super_onMenuItemSelected(int featureId, MenuItem item) + { + return super.onMenuItemSelected(featureId, item); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onMenuOpened(int featureId, Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onMenuOpened(featureId, menu); + } + public boolean super_onMenuOpened(int featureId, Menu menu) + { + return super.onMenuOpened(featureId, menu); + } + //--------------------------------------------------------------------------- + + @Override + protected void onNewIntent(Intent intent) + { + if (!QtApplication.invokeDelegate(intent).invoked) + super.onNewIntent(intent); + } + public void super_onNewIntent(Intent intent) + { + super.onNewIntent(intent); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(item); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onOptionsItemSelected(item); + } + public boolean super_onOptionsItemSelected(MenuItem item) + { + return super.onOptionsItemSelected(item); + } + //--------------------------------------------------------------------------- + + @Override + public void onOptionsMenuClosed(Menu menu) + { + if (!QtApplication.invokeDelegate(menu).invoked) + super.onOptionsMenuClosed(menu); + } + public void super_onOptionsMenuClosed(Menu menu) + { + super.onOptionsMenuClosed(menu); + } + //--------------------------------------------------------------------------- + + @Override + public void onPanelClosed(int featureId, Menu menu) + { + if (!QtApplication.invokeDelegate(featureId, menu).invoked) + super.onPanelClosed(featureId, menu); + } + public void super_onPanelClosed(int featureId, Menu menu) + { + super.onPanelClosed(featureId, menu); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPause() + { + super.onPause(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPostCreate(Bundle savedInstanceState) + { + super.onPostCreate(savedInstanceState); + QtApplication.invokeDelegate(savedInstanceState); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPostResume() + { + super.onPostResume(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPrepareDialog(int id, Dialog dialog) + { + if (!QtApplication.invokeDelegate(id, dialog).invoked) + super.onPrepareDialog(id, dialog); + } + public void super_onPrepareDialog(int id, Dialog dialog) + { + super.onPrepareDialog(id, dialog); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onPrepareOptionsMenu(Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onPrepareOptionsMenu(menu); + } + public boolean super_onPrepareOptionsMenu(Menu menu) + { + return super.onPrepareOptionsMenu(menu); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onPreparePanel(int featureId, View view, Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, view, menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onPreparePanel(featureId, view, menu); + } + public boolean super_onPreparePanel(int featureId, View view, Menu menu) + { + return super.onPreparePanel(featureId, view, menu); + } + //--------------------------------------------------------------------------- + + @Override + protected void onRestart() + { + super.onRestart(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) + { + if (!QtApplication.invokeDelegate(savedInstanceState).invoked) + super.onRestoreInstanceState(savedInstanceState); + } + public void super_onRestoreInstanceState(Bundle savedInstanceState) + { + super.onRestoreInstanceState(savedInstanceState); + } + //--------------------------------------------------------------------------- + + @Override + protected void onResume() + { + super.onResume(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + public Object onRetainNonConfigurationInstance() + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(); + if (res.invoked) + return res.methodReturns; + else + return super.onRetainNonConfigurationInstance(); + } + public Object super_onRetainNonConfigurationInstance() + { + return super.onRetainNonConfigurationInstance(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onSaveInstanceState(Bundle outState) + { + if (!QtApplication.invokeDelegate(outState).invoked) + super.onSaveInstanceState(outState); + } + public void super_onSaveInstanceState(Bundle outState) + { + super.onSaveInstanceState(outState); + + } + //--------------------------------------------------------------------------- + + @Override + public boolean onSearchRequested() + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onSearchRequested(); + } + public boolean super_onSearchRequested() + { + return super.onSearchRequested(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onStart() + { + super.onStart(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onStop() + { + super.onStop(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onTitleChanged(CharSequence title, int color) + { + if (!QtApplication.invokeDelegate(title, color).invoked) + super.onTitleChanged(title, color); + } + public void super_onTitleChanged(CharSequence title, int color) + { + super.onTitleChanged(title, color); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onTouchEvent(MotionEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onTouchEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTouchEvent, event); + else + return super.onTouchEvent(event); + } + public boolean super_onTouchEvent(MotionEvent event) + { + return super.onTouchEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onTrackballEvent(MotionEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onTrackballEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTrackballEvent, event); + else + return super.onTrackballEvent(event); + } + public boolean super_onTrackballEvent(MotionEvent event) + { + return super.onTrackballEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public void onUserInteraction() + { + if (!QtApplication.invokeDelegate().invoked) + super.onUserInteraction(); + } + public void super_onUserInteraction() + { + super.onUserInteraction(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onUserLeaveHint() + { + if (!QtApplication.invokeDelegate().invoked) + super.onUserLeaveHint(); + } + public void super_onUserLeaveHint() + { + super.onUserLeaveHint(); + } + //--------------------------------------------------------------------------- + + @Override + public void onWindowAttributesChanged(LayoutParams params) + { + if (!QtApplication.invokeDelegate(params).invoked) + super.onWindowAttributesChanged(params); + } + public void super_onWindowAttributesChanged(LayoutParams params) + { + super.onWindowAttributesChanged(params); + } + //--------------------------------------------------------------------------- + + @Override + public void onWindowFocusChanged(boolean hasFocus) + { + if (!QtApplication.invokeDelegate(hasFocus).invoked) + super.onWindowFocusChanged(hasFocus); + } + public void super_onWindowFocusChanged(boolean hasFocus) + { + super.onWindowFocusChanged(hasFocus); + } + //--------------------------------------------------------------------------- + + //////////////// Activity API 5 ///////////// +//@ANDROID-5 + @Override + public void onAttachedToWindow() + { + if (!QtApplication.invokeDelegate().invoked) + super.onAttachedToWindow(); + } + public void super_onAttachedToWindow() + { + super.onAttachedToWindow(); + } + //--------------------------------------------------------------------------- + + @Override + public void onBackPressed() + { + if (!QtApplication.invokeDelegate().invoked) + super.onBackPressed(); + } + public void super_onBackPressed() + { + super.onBackPressed(); + } + //--------------------------------------------------------------------------- + + @Override + public void onDetachedFromWindow() + { + if (!QtApplication.invokeDelegate().invoked) + super.onDetachedFromWindow(); + } + public void super_onDetachedFromWindow() + { + super.onDetachedFromWindow(); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyLongPress != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyLongPress, keyCode, event); + else + return super.onKeyLongPress(keyCode, event); + } + public boolean super_onKeyLongPress(int keyCode, KeyEvent event) + { + return super.onKeyLongPress(keyCode, event); + } + //--------------------------------------------------------------------------- +//@ANDROID-5 + +//////////////// Activity API 8 ///////////// +//@ANDROID-8 +@Override + protected Dialog onCreateDialog(int id, Bundle args) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(id, args); + if (res.invoked) + return (Dialog)res.methodReturns; + else + return super.onCreateDialog(id, args); + } + public Dialog super_onCreateDialog(int id, Bundle args) + { + return super.onCreateDialog(id, args); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPrepareDialog(int id, Dialog dialog, Bundle args) + { + if (!QtApplication.invokeDelegate(id, dialog, args).invoked) + super.onPrepareDialog(id, dialog, args); + } + public void super_onPrepareDialog(int id, Dialog dialog, Bundle args) + { + super.onPrepareDialog(id, dialog, args); + } + //--------------------------------------------------------------------------- +//@ANDROID-8 + //////////////// Activity API 11 ///////////// + +//@ANDROID-11 + @Override + public boolean dispatchKeyShortcutEvent(KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyShortcutEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyShortcutEvent, event); + else + return super.dispatchKeyShortcutEvent(event); + } + public boolean super_dispatchKeyShortcutEvent(KeyEvent event) + { + return super.dispatchKeyShortcutEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public void onActionModeFinished(ActionMode mode) + { + if (!QtApplication.invokeDelegate(mode).invoked) + super.onActionModeFinished(mode); + } + public void super_onActionModeFinished(ActionMode mode) + { + super.onActionModeFinished(mode); + } + //--------------------------------------------------------------------------- + + @Override + public void onActionModeStarted(ActionMode mode) + { + if (!QtApplication.invokeDelegate(mode).invoked) + super.onActionModeStarted(mode); + } + public void super_onActionModeStarted(ActionMode mode) + { + super.onActionModeStarted(mode); + } + //--------------------------------------------------------------------------- + + @Override + public void onAttachFragment(Fragment fragment) + { + if (!QtApplication.invokeDelegate(fragment).invoked) + super.onAttachFragment(fragment); + } + public void super_onAttachFragment(Fragment fragment) + { + super.onAttachFragment(fragment); + } + //--------------------------------------------------------------------------- + + @Override + public View onCreateView(View parent, String name, Context context, AttributeSet attrs) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(parent, name, context, attrs); + if (res.invoked) + return (View)res.methodReturns; + else + return super.onCreateView(parent, name, context, attrs); + } + public View super_onCreateView(View parent, String name, Context context, + AttributeSet attrs) { + return super.onCreateView(parent, name, context, attrs); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onKeyShortcut(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyShortcut != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyShortcut, keyCode,event); + else + return super.onKeyShortcut(keyCode, event); + } + public boolean super_onKeyShortcut(int keyCode, KeyEvent event) + { + return super.onKeyShortcut(keyCode, event); + } + //--------------------------------------------------------------------------- + + @Override + public ActionMode onWindowStartingActionMode(Callback callback) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(callback); + if (res.invoked) + return (ActionMode)res.methodReturns; + else + return super.onWindowStartingActionMode(callback); + } + public ActionMode super_onWindowStartingActionMode(Callback callback) + { + return super.onWindowStartingActionMode(callback); + } + //--------------------------------------------------------------------------- +//@ANDROID-11 + //////////////// Activity API 12 ///////////// + +//@ANDROID-12 + @Override + public boolean dispatchGenericMotionEvent(MotionEvent ev) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchGenericMotionEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchGenericMotionEvent, ev); + else + return super.dispatchGenericMotionEvent(ev); + } + public boolean super_dispatchGenericMotionEvent(MotionEvent event) + { + return super.dispatchGenericMotionEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onGenericMotionEvent(MotionEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onGenericMotionEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onGenericMotionEvent, event); + else + return super.onGenericMotionEvent(event); + } + public boolean super_onGenericMotionEvent(MotionEvent event) + { + return super.onGenericMotionEvent(event); + } + //--------------------------------------------------------------------------- +//@ANDROID-12 + +} diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java new file mode 100644 index 0000000000..a23c3afb4d --- /dev/null +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java @@ -0,0 +1,149 @@ +/* + Copyright (c) 2012, BogDan Vatra + Contact: http://www.qt-project.org/legal + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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 AUTHOR ``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 THE AUTHOR 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. +*/ + +package org.qtproject.qt5.android.bindings; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; + +import android.app.Application; + +public class QtApplication extends Application +{ + public final static String QtTAG = "Qt"; + public static Object m_delegateObject = null; + public static HashMap> m_delegateMethods= new HashMap>(); + public static Method dispatchKeyEvent = null; + public static Method dispatchPopulateAccessibilityEvent = null; + public static Method dispatchTouchEvent = null; + public static Method dispatchTrackballEvent = null; + public static Method onKeyDown = null; + public static Method onKeyMultiple = null; + public static Method onKeyUp = null; + public static Method onTouchEvent = null; + public static Method onTrackballEvent = null; + public static Method onActivityResult = null; + public static Method onCreate = null; + public static Method onKeyLongPress = null; + public static Method dispatchKeyShortcutEvent = null; + public static Method onKeyShortcut = null; + public static Method dispatchGenericMotionEvent = null; + public static Method onGenericMotionEvent = null; + + public static void setQtActivityDelegate(Object listener) + { + QtApplication.m_delegateObject = listener; + + ArrayList delegateMethods = new ArrayList(); + for (Method m : listener.getClass().getMethods()) { + if (m.getDeclaringClass().getName().startsWith("org.qtproject.qt5.android")) + delegateMethods.add(m); + } + + ArrayList applicationFields = new ArrayList(); + for (Field f : QtApplication.class.getFields()) { + if (f.getDeclaringClass().getName().equals(QtApplication.class.getName())) + applicationFields.add(f); + } + + for (Method delegateMethod : delegateMethods) { + try { + QtActivity.class.getDeclaredMethod(delegateMethod.getName(), delegateMethod.getParameterTypes()); + if (QtApplication.m_delegateMethods.containsKey(delegateMethod.getName())) { + QtApplication.m_delegateMethods.get(delegateMethod.getName()).add(delegateMethod); + } else { + ArrayList delegateSet = new ArrayList(); + delegateSet.add(delegateMethod); + QtApplication.m_delegateMethods.put(delegateMethod.getName(), delegateSet); + } + for (Field applicationField:applicationFields) { + if (applicationField.getName().equals(delegateMethod.getName())) { + try { + applicationField.set(null, delegateMethod); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } catch (Exception e) { + } + } + } + + @Override + public void onTerminate() { + if (m_delegateObject != null && m_delegateMethods.containsKey("onTerminate")) + invokeDelegateMethod(m_delegateMethods.get("onTerminate").get(0)); + super.onTerminate(); + } + + public static class InvokeResult + { + public boolean invoked = false; + public Object methodReturns = null; + } + + private static int stackDeep=-1; + public static InvokeResult invokeDelegate(Object... args) + { + InvokeResult result = new InvokeResult(); + if (m_delegateObject == null) + return result; + StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + if (-1 == stackDeep) { + String activityClassName = QtActivity.class.getCanonicalName(); + for (int it=0;it + + AndroidManifest.xml + libs.xml + logo.png + icon.png + + diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp index 511817677c..1a5c9f6766 100644 --- a/src/corelib/codecs/qtextcodec.cpp +++ b/src/corelib/codecs/qtextcodec.cpp @@ -89,7 +89,7 @@ #include #include #include -#if defined (_XOPEN_UNIX) && !defined(Q_OS_QNX) && !defined(Q_OS_OSF) && !defined(Q_OS_LINUX_ANDROID) +#if defined (_XOPEN_UNIX) && !defined(Q_OS_QNX) && !defined(Q_OS_OSF) && !defined(Q_OS_ANDROID) # include #endif diff --git a/src/corelib/codecs/qtextcodec_p.h b/src/corelib/codecs/qtextcodec_p.h index 18629f4bf3..0fec1e80c7 100644 --- a/src/corelib/codecs/qtextcodec_p.h +++ b/src/corelib/codecs/qtextcodec_p.h @@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_TEXTCODEC -#if defined(Q_OS_MAC) || defined(Q_OS_IOS) || defined(Q_OS_LINUX_ANDROID) || defined(Q_OS_QNX) +#if defined(Q_OS_MAC) || defined(Q_OS_IOS) || defined(Q_OS_ANDROID) || defined(Q_OS_QNX) #define QT_LOCALE_IS_UTF8 #endif diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro index cbfb457212..b1f1a60b6c 100644 --- a/src/corelib/corelib.pro +++ b/src/corelib/corelib.pro @@ -16,6 +16,12 @@ win32-g++*:QMAKE_CXXFLAGS_CXX11 = -std=gnu++0x QMAKE_DOCS = $$PWD/doc/qtcore.qdocconf +ANDROID_JAR_DEPENDENCIES = \ + jar/QtAndroid.jar +ANDROID_LIB_DEPENDENCIES = \ + plugins/platforms/android/libqtforandroid.so \ + libs/libgnustl_shared.so + load(qt_module) include(animation/animation.pri) diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index 7204efc752..6a127e1786 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -57,6 +57,10 @@ #include #endif +#ifdef Q_OS_ANDROID +#include +#endif + #include QT_BEGIN_NAMESPACE @@ -835,6 +839,24 @@ Q_CORE_EXPORT QtMsgHandler qInstallMsgHandler(QtMsgHandler); static QtMsgHandler msgHandler = 0; // pointer to debug handler (without context) static QtMessageHandler messageHandler = 0; // pointer to debug handler (with context) +#ifdef Q_OS_ANDROID +static void android_default_message_handler(QtMsgType type, + const QMessageLogContext &context, + const QString &message) +{ + android_LogPriority priority; + switch (type) { + case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break; + case QtWarningMsg: priority = ANDROID_LOG_WARN; break; + case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break; + case QtFatalMsg: priority = ANDROID_LOG_FATAL; break; + }; + + __android_log_print(priority, "Qt", "%s:%d (%s): %s", qPrintable(context.file), context.line, + qPrintable(context.function), qPrintable(message)); +} +#endif //Q_OS_ANDROID + /*! \internal */ @@ -855,6 +877,8 @@ static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &con #if defined(QT_USE_SLOG2) slog2_default_handler(type, logMessage.toLocal8Bit().constData()); +#elif defined(Q_OS_ANDROID) + android_default_message_handler(type, context, logMessage); #else fprintf(stderr, "%s", logMessage.toLocal8Bit().constData()); fflush(stderr); diff --git a/src/corelib/global/qsystemdetection.h b/src/corelib/global/qsystemdetection.h index 6062dc7b7b..0caac3d797 100644 --- a/src/corelib/global/qsystemdetection.h +++ b/src/corelib/global/qsystemdetection.h @@ -80,6 +80,7 @@ LYNX - LynxOS BSD4 - Any BSD 4.4 system UNIX - Any UNIX BSD/SYSV system + ANDROID - Android platform */ #if defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__)) @@ -90,6 +91,9 @@ # else # define Q_OS_DARWIN32 # endif +#elif defined(ANDROID) +# define Q_OS_ANDROID +# define Q_OS_LINUX #elif defined(__CYGWIN__) # define Q_OS_CYGWIN #elif !defined(SAG_COM) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index d4ed8e5362..a52386def1 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -133,7 +133,7 @@ win32 { SOURCES += io/qstandardpaths_unix.cpp } - linux-*|if(qnx:contains(QT_CONFIG, inotify)) { + linux|if(qnx:contains(QT_CONFIG, inotify)) { SOURCES += io/qfilesystemwatcher_inotify.cpp HEADERS += io/qfilesystemwatcher_inotify_p.h } diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp index 9970530827..4bfb03a41e 100644 --- a/src/corelib/io/qfilesystemengine_unix.cpp +++ b/src/corelib/io/qfilesystemengine_unix.cpp @@ -169,7 +169,7 @@ QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, if (entry.isEmpty() || entry.isRoot()) return entry; -#if !defined(Q_OS_MAC) && !defined(Q_OS_QNX) && _POSIX_VERSION < 200809L +#if !defined(Q_OS_MAC) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && _POSIX_VERSION < 200809L // realpath(X,0) is not supported Q_UNUSED(data); return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath())); diff --git a/src/corelib/io/qtemporarydir.cpp b/src/corelib/io/qtemporarydir.cpp index 6782fcb986..755c31f371 100644 --- a/src/corelib/io/qtemporarydir.cpp +++ b/src/corelib/io/qtemporarydir.cpp @@ -52,7 +52,7 @@ #endif #include // mkdtemp -#if defined(Q_OS_QNX) || defined(Q_OS_WIN) +#if defined(Q_OS_QNX) || defined(Q_OS_WIN) || defined(Q_OS_ANDROID) #include #endif @@ -94,9 +94,9 @@ static QString defaultTemplateName() return QDir::tempPath() + QLatin1Char('/') + baseName + QLatin1String("-XXXXXX"); } -#if defined(Q_OS_QNX ) || defined(Q_OS_WIN) -static char *mkdtemp(char *templateName) +static char *q_mkdtemp(char *templateName) { +#if defined(Q_OS_QNX ) || defined(Q_OS_WIN) || defined(Q_OS_ANDROID) static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const size_t length = strlen(templateName); @@ -137,17 +137,17 @@ static char *mkdtemp(char *templateName) } } return 0; -} -#elif defined(Q_OS_LINUX_ANDROID) -extern char *mkdtemp(char *); +#else + return mkdtemp(templateName); #endif +} void QTemporaryDirPrivate::create(const QString &templateName) { QByteArray buffer = QFile::encodeName(templateName); if (!buffer.endsWith("XXXXXX")) buffer += "XXXXXX"; - if (mkdtemp(buffer.data())) { // modifies buffer + if (q_mkdtemp(buffer.data())) { // modifies buffer success = true; path = QFile::decodeName(buffer.constData()); } diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index d2de873cef..d5a6fa6122 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -129,7 +129,7 @@ unix|integrity { contains(QT_CONFIG, clock-gettime):include($$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri) - !linux-android-* { + !android { SOURCES += kernel/qsharedmemory_unix.cpp \ kernel/qsystemsemaphore_unix.cpp } else { diff --git a/src/corelib/kernel/qsharedmemory_p.h b/src/corelib/kernel/qsharedmemory_p.h index dd9856adf5..9f1e6c8aa1 100644 --- a/src/corelib/kernel/qsharedmemory_p.h +++ b/src/corelib/kernel/qsharedmemory_p.h @@ -69,7 +69,7 @@ namespace QSharedMemoryPrivate #include "qsystemsemaphore.h" #include "private/qobject_p.h" -#if !defined(Q_OS_WIN) && !defined(Q_OS_LINUX_ANDROID) +#if !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) # include #endif diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index f7397dd8d4..8104cc8938 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -287,7 +287,7 @@ static void setCurrentThreadName(pthread_t threadId, const char *name) void *QThreadPrivate::start(void *arg) { -#if !defined(Q_OS_LINUX_ANDROID) +#if !defined(Q_OS_ANDROID) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); #endif pthread_cleanup_push(QThreadPrivate::finish, arg); @@ -326,7 +326,7 @@ void *QThreadPrivate::start(void *arg) #endif emit thr->started(QThread::QPrivateSignal()); -#if !defined(Q_OS_LINUX_ANDROID) +#if !defined(Q_OS_ANDROID) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_testcancel(); #endif @@ -631,7 +631,7 @@ void QThread::start(Priority priority) void QThread::terminate() { -#if !defined(Q_OS_LINUX_ANDROID) +#if !defined(Q_OS_ANDROID) Q_D(QThread); QMutexLocker locker(&d->mutex); @@ -673,7 +673,7 @@ void QThread::setTerminationEnabled(bool enabled) "Current thread was not started with QThread."); Q_UNUSED(thr) -#if defined(Q_OS_LINUX_ANDROID) +#if defined(Q_OS_ANDROID) Q_UNUSED(enabled); #else pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, NULL); diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index d152e6795e..ac49bdcdf6 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -49,7 +49,7 @@ #include -#if defined(Q_OS_LINUX_ANDROID) +#if defined(Q_OS_ANDROID) // std::wstring is disabled on android's glibc, as bionic lacks certain features // that libstdc++ checks for (like mbcslen). namespace std diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 1763624b83..7155127f75 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -1507,10 +1507,20 @@ void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyE { QWindow *window = e->window.data(); modifier_buttons = e->modifiers; - if (e->nullWindow) + if (e->nullWindow +#ifdef Q_OS_ANDROID + || (e->keyType == QEvent::KeyRelease && e->key == Qt::Key_Back) || e->key == Qt::Key_Menu +#endif + ) { window = QGuiApplication::focusWindow(); - if (!window) + } + if (!window +#ifdef Q_OS_ANDROID + && e->keyType != QEvent::KeyRelease && e->key != Qt::Key_Back +#endif + ) { return; + } if (window->d_func()->blockedByModalWindow) { // a modal window is blocking this window, don't allow key events through return; @@ -1520,7 +1530,19 @@ void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyE e->nativeScanCode, e->nativeVirtualKey, e->nativeModifiers, e->unicode, e->repeat, e->repeatCount); ev.setTimestamp(e->timestamp); - QGuiApplication::sendSpontaneousEvent(window, &ev); + +#ifdef Q_OS_ANDROID + if (e->keyType == QEvent::KeyRelease && e->key == Qt::Key_Back) { + if (!window) { + qApp->quit(); + } else { + QGuiApplication::sendEvent(window, &ev); + if (!ev.isAccepted() && e->key == Qt::Key_Back) + QWindowSystemInterface::handleCloseEvent(window); + } + } else +#endif + QGuiApplication::sendSpontaneousEvent(window, &ev); } void QGuiApplicationPrivate::processEnterEvent(QWindowSystemInterfacePrivate::EnterEvent *e) diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp index a7f35815f9..13428cc802 100644 --- a/src/network/access/qnetworkaccessfilebackend.cpp +++ b/src/network/access/qnetworkaccessfilebackend.cpp @@ -65,7 +65,11 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op, } QUrl url = request.url(); - if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0 || url.isLocalFile()) { + if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0 +#if defined(Q_OS_ANDROID) + || url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0 +#endif + || url.isLocalFile()) { return new QNetworkAccessFileBackend; } else if (!url.scheme().isEmpty() && url.authority().isEmpty() && (url.scheme().length() > 1)) { // check if QFile could, in theory, open this URL via the file engines @@ -113,10 +117,16 @@ void QNetworkAccessFileBackend::open() QString fileName = url.toLocalFile(); if (fileName.isEmpty()) { - if (url.scheme() == QLatin1String("qrc")) + if (url.scheme() == QLatin1String("qrc")) { fileName = QLatin1Char(':') + url.path(); - else - fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); + } else { +#if defined(Q_OS_ANDROID) + if (url.scheme() == QLatin1String("assets")) + fileName = QLatin1String("assets:") + url.path(); + else +#endif + fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); + } } file.setFileName(fileName); diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index b83b437b2c..123b354131 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -1010,7 +1010,11 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera // fast path for GET on file:// URLs // The QNetworkAccessFileBackend will right now only be used for PUT if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation) - && (isLocalFile || scheme == QLatin1String("qrc"))) { + && (isLocalFile || scheme == QLatin1String("qrc") +#if defined(Q_OS_ANDROID) + || scheme == QLatin1String("assets") +#endif + )) { return new QNetworkReplyFileImpl(this, req, op); } diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp index 8a8f73c24f..f7555f8fc3 100644 --- a/src/network/access/qnetworkreplyfileimpl.cpp +++ b/src/network/access/qnetworkreplyfileimpl.cpp @@ -91,10 +91,16 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QObject *parent, const QNetworkRequ QString fileName = url.toLocalFile(); if (fileName.isEmpty()) { - if (url.scheme() == QLatin1String("qrc")) + if (url.scheme() == QLatin1String("qrc")) { fileName = QLatin1Char(':') + url.path(); - else - fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); + } else { +#if defined(Q_OS_ANDROID) + if (url.scheme() == QLatin1String("assets")) + fileName = QLatin1String("assets:") + url.path(); + else +#endif + fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); + } } QFileInfo fi(fileName); diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri index 580e0b31b3..a4a19988b3 100644 --- a/src/network/kernel/kernel.pri +++ b/src/network/kernel/kernel.pri @@ -27,7 +27,7 @@ SOURCES += kernel/qauthenticator.cpp \ unix:SOURCES += kernel/qdnslookup_unix.cpp kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp -linux-android* { +android { SOURCES -= kernel/qdnslookup_unix.cpp SOURCES += kernel/qdnslookup_android.cpp } diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 0e147c4877..fac83b922b 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -323,7 +323,7 @@ QString QHostInfo::localHostName() QString QHostInfo::localDomainName() { -#if !defined(Q_OS_VXWORKS) && !defined(Q_OS_LINUX_ANDROID) +#if !defined(Q_OS_VXWORKS) && !defined(Q_OS_ANDROID) resolveLibrary(); if (local_res_ninit) { // using thread-safe version diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp index 46eeb46f98..7885d122ea 100644 --- a/src/network/kernel/qnetworkinterface_unix.cpp +++ b/src/network/kernel/qnetworkinterface_unix.cpp @@ -61,7 +61,7 @@ # define QT_NO_GETIFADDRS #endif -#ifdef Q_OS_LINUX_ANDROID +#ifdef Q_OS_ANDROID // android lacks if_nameindex # define QT_NO_IPV6IFNAME #endif diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 6845752d8b..e8f8b294c9 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -681,10 +681,19 @@ QList QSslSocketPrivate::systemCaCertificates() } #elif defined(Q_OS_UNIX) QSet certFiles; +# ifdef Q_OS_ANDROID + QList directories; + directories << qgetenv("MINISTRO_SSL_CERTS_PATH"); // Set by Ministro +# else QList directories = unixRootCertDirectories(); +# endif QDir currentDir; QStringList nameFilters; +# ifdef Q_OS_ANDROID + nameFilters << QLatin1String("*.der"); +#else nameFilters << QLatin1String("*.pem") << QLatin1String("*.crt"); +# endif currentDir.setNameFilters(nameFilters); for (int a = 0; a < directories.count(); a++) { currentDir.setPath(QLatin1String(directories.at(a))); @@ -697,10 +706,16 @@ QList QSslSocketPrivate::systemCaCertificates() } QSetIterator it(certFiles); while(it.hasNext()) { - systemCerts.append(QSslCertificate::fromPath(it.next())); +# ifdef Q_OS_ANDROID + systemCerts.append(QSslCertificate::fromPath(it.next(), QSsl::Der)); +# else + systemCerts.append(QSslCertificate::fromPath(it.next(), QSsl::Pem)); +# endif } +# ifndef Q_OS_ANDROID systemCerts.append(QSslCertificate::fromPath(QLatin1String("/etc/pki/tls/certs/ca-bundle.crt"), QSsl::Pem)); // Fedora, Mandriva systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/local/share/certs/ca-root-nss.crt"), QSsl::Pem)); // FreeBSD's ca_root_nss +# endif #endif #ifdef QSSLSOCKET_DEBUG qDebug() << "systemCaCertificates retrieval time " << timer.elapsed() << "ms"; diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index 4d9208d983..b01ca80829 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -8,6 +8,9 @@ irix-cc*:QMAKE_CXXFLAGS += -no_prelink -ptused QMAKE_DOCS = $$PWD/doc/qtopengl.qdocconf +ANDROID_LIB_DEPENDENCY_REPLACEMENTS = \ + "plugins/platforms/android/libqtforandroid.so:plugins/platforms/android/libqtforandroidGL.so" + load(qt_module) contains(QT_CONFIG, opengl):CONFIG += opengl diff --git a/src/platformsupport/devicediscovery/devicediscovery.pri b/src/platformsupport/devicediscovery/devicediscovery.pri index 530ae3dbb2..9faf6f24dd 100644 --- a/src/platformsupport/devicediscovery/devicediscovery.pri +++ b/src/platformsupport/devicediscovery/devicediscovery.pri @@ -1,4 +1,4 @@ -linux-*:contains(QT_CONFIG, evdev) { +linux:contains(QT_CONFIG, evdev) { HEADERS += $$PWD/qdevicediscovery_p.h contains(QT_CONFIG, libudev) { diff --git a/src/plugins/platforms/android/android.pro b/src/plugins/platforms/android/android.pro new file mode 100644 index 0000000000..aa5ab4ddbd --- /dev/null +++ b/src/plugins/platforms/android/android.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += raster opengl diff --git a/src/plugins/platforms/android/opengl/opengl.pro b/src/plugins/platforms/android/opengl/opengl.pro new file mode 100644 index 0000000000..301c8e6e4c --- /dev/null +++ b/src/plugins/platforms/android/opengl/opengl.pro @@ -0,0 +1,30 @@ +TARGET = qtforandroidGL + +PLUGIN_TYPE = platforms +load(qt_plugin) + +# STATICPLUGIN needed because there's a Q_IMPORT_PLUGIN in androidjnimain.cpp +# Yes, the plugin imports itself statically +DEFINES += QT_STATICPLUGIN ANDROID_PLUGIN_OPENGL + +!equals(ANDROID_PLATFORM, android-9) { + INCLUDEPATH += $$NDK_ROOT/platforms/android-9/arch-$$ANDROID_ARCHITECTURE/usr/include + LIBS += -L$$NDK_ROOT/platforms/android-9/arch-$$ANDROID_ARCHITECTURE/usr/lib -ljnigraphics -landroid +} else { + LIBS += -ljnigraphics -landroid +} + +EGLFS_PLATFORM_HOOKS_SOURCES = $$PWD/../src/opengl/qeglfshooks_android.cpp + +INCLUDEPATH += $$PWD/../src/opengl/ + +HEADERS += \ + $$PWD/../src/opengl/qandroidopenglcontext.h \ + $$PWD/../src/opengl/qandroidopenglplatformwindow.h + +SOURCES += \ + $$PWD/../src/opengl/qandroidopenglcontext.cpp \ + $$PWD/../src/opengl/qandroidopenglplatformwindow.cpp + +include($$PWD/../../eglfs/eglfs.pri) +include($$PWD/../src/src.pri) diff --git a/src/plugins/platforms/android/raster/raster.pro b/src/plugins/platforms/android/raster/raster.pro new file mode 100644 index 0000000000..53d8ee7a2b --- /dev/null +++ b/src/plugins/platforms/android/raster/raster.pro @@ -0,0 +1,19 @@ +TARGET = qtforandroid + +PLUGIN_TYPE = platforms + +# STATICPLUGIN needed because there's a Q_IMPORT_PLUGIN in androidjnimain.cpp +# Yes, the plugin imports itself statically +DEFINES += QT_STATICPLUGIN + +load(qt_plugin) + +!contains(ANDROID_PLATFORM, android-9) { + INCLUDEPATH += $$NDK_ROOT/platforms/android-9/arch-$$ANDROID_ARCHITECTURE/usr/include + LIBS += -L$$NDK_ROOT/platforms/android-9/arch-$$ANDROID_ARCHITECTURE/usr/lib -ljnigraphics -landroid +} else { + LIBS += -ljnigraphics -landroid +} + +include($$PWD/../src/src.pri) +include($$PWD/../src/raster/raster.pri) diff --git a/src/plugins/platforms/android/src/android.json b/src/plugins/platforms/android/src/android.json new file mode 100644 index 0000000000..6843bd3301 --- /dev/null +++ b/src/plugins/platforms/android/src/android.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "android" ] +} diff --git a/src/plugins/platforms/android/src/androidjniclipboard.cpp b/src/plugins/platforms/android/src/androidjniclipboard.cpp new file mode 100644 index 0000000000..05270ac374 --- /dev/null +++ b/src/plugins/platforms/android/src/androidjniclipboard.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "androidjniclipboard.h" +#include "androidjnimain.h" + +using namespace QtAndroid; +namespace QtAndroidClipboard +{ + // Clipboard support + static jmethodID m_registerClipboardManagerMethodID = 0; + static jmethodID m_setClipboardTextMethodID = 0; + static jmethodID m_hasClipboardTextMethodID = 0; + static jmethodID m_getClipboardTextMethodID = 0; + // Clipboard support + + void setClipboardListener(QAndroidPlatformClipboard *listener) + { + Q_UNUSED(listener); + + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + env.jniEnv->CallStaticVoidMethod(applicationClass(), m_registerClipboardManagerMethodID); + } + + void setClipboardText(const QString &text) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + jstring jtext = env.jniEnv->NewString(reinterpret_cast(text.data()), + text.length()); + env.jniEnv->CallStaticVoidMethod(applicationClass(), m_setClipboardTextMethodID, jtext); + env.jniEnv->DeleteLocalRef(jtext); + } + + bool hasClipboardText() + { + AttachedJNIEnv env; + if (!env.jniEnv) + return false; + + return env.jniEnv->CallStaticBooleanMethod(applicationClass(), m_hasClipboardTextMethodID); + } + + QString clipboardText() + { + AttachedJNIEnv env; + if (!env.jniEnv) + return QString(); + + jstring text = reinterpret_cast(env.jniEnv->CallStaticObjectMethod(applicationClass(), + m_getClipboardTextMethodID)); + const jchar *jstr = env.jniEnv->GetStringChars(text, 0); + QString str(reinterpret_cast(jstr), env.jniEnv->GetStringLength(text)); + env.jniEnv->ReleaseStringChars(text, jstr); + return str; + } + + +#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ + VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ + if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), methodErrorMsgFmt(), METHOD_NAME, METHOD_SIGNATURE); \ + return false; \ + } + + bool registerNatives(JNIEnv *env) + { + jclass appClass = QtAndroid::applicationClass(); + + GET_AND_CHECK_STATIC_METHOD(m_registerClipboardManagerMethodID, appClass, "registerClipboardManager", "()V"); + GET_AND_CHECK_STATIC_METHOD(m_setClipboardTextMethodID, appClass, "setClipboardText", "(Ljava/lang/String;)V"); + GET_AND_CHECK_STATIC_METHOD(m_hasClipboardTextMethodID, appClass, "hasClipboardText", "()Z"); + GET_AND_CHECK_STATIC_METHOD(m_getClipboardTextMethodID, appClass, "getClipboardText", "()Ljava/lang/String;"); + + return true; + } +} diff --git a/src/plugins/platforms/android/src/androidjniclipboard.h b/src/plugins/platforms/android/src/androidjniclipboard.h new file mode 100644 index 0000000000..15cd93202e --- /dev/null +++ b/src/plugins/platforms/android/src/androidjniclipboard.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROIDJNICLIPBOARD_H +#define ANDROIDJNICLIPBOARD_H + +#include +#include + +class QAndroidPlatformClipboard; +namespace QtAndroidClipboard +{ + // Clipboard support + void setClipboardListener(QAndroidPlatformClipboard *listener); + void setClipboardText(const QString &text); + bool hasClipboardText(); + QString clipboardText(); + // Clipboard support + + bool registerNatives(JNIEnv *env); +} + +#endif // ANDROIDJNICLIPBOARD_H diff --git a/src/plugins/platforms/android/src/androidjniinput.cpp b/src/plugins/platforms/android/src/androidjniinput.cpp new file mode 100644 index 0000000000..6a3dd1f349 --- /dev/null +++ b/src/plugins/platforms/android/src/androidjniinput.cpp @@ -0,0 +1,480 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "androidjniinput.h" +#include "androidjnimain.h" + +#include +#include +#include + +using namespace QtAndroid; + +namespace QtAndroidInput +{ + static jmethodID m_showSoftwareKeyboardMethodID = 0; + static jmethodID m_resetSoftwareKeyboardMethodID = 0; + static jmethodID m_hideSoftwareKeyboardMethodID = 0; + static jmethodID m_isSoftwareKeyboardVisibleMethodID = 0; + static jmethodID m_updateSelectionMethodID = 0; + + static bool m_ignoreMouseEvents = false; + + static QList m_touchPoints; + + static QPointer m_mouseGrabber; + + void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + env.jniEnv->CallStaticVoidMethod(applicationClass(), m_updateSelectionMethodID, + selStart, selEnd, candidatesStart, candidatesEnd); + } + + void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + env.jniEnv->CallStaticVoidMethod(applicationClass(), + m_showSoftwareKeyboardMethodID, + left, + top, + width, + height, + inputHints); + } + + void resetSoftwareKeyboard() + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + env.jniEnv->CallStaticVoidMethod(applicationClass(), m_resetSoftwareKeyboardMethodID); + } + + void hideSoftwareKeyboard() + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + env.jniEnv->CallStaticVoidMethod(applicationClass(), m_hideSoftwareKeyboardMethodID); + } + + bool isSoftwareKeyboardVisible() + { + AttachedJNIEnv env; + if (!env.jniEnv) + return false; + + return env.jniEnv->CallStaticBooleanMethod(applicationClass(), m_isSoftwareKeyboardVisibleMethodID); + } + + + static void mouseDown(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y) + { + if (m_ignoreMouseEvents) + return; + + QPoint globalPos(x,y); + QWindow *tlw = topLevelWindowAt(globalPos); + m_mouseGrabber = tlw; + QPoint localPos = tlw ? (globalPos - tlw->position()) : globalPos; + QWindowSystemInterface::handleMouseEvent(tlw, + localPos, + globalPos, + Qt::MouseButtons(Qt::LeftButton)); + } + + static void mouseUp(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y) + { + QPoint globalPos(x,y); + QWindow *tlw = m_mouseGrabber.data(); + if (!tlw) + tlw = topLevelWindowAt(globalPos); + QPoint localPos = tlw ? (globalPos -tlw->position()) : globalPos; + QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos + , Qt::MouseButtons(Qt::NoButton)); + m_ignoreMouseEvents = false; + m_mouseGrabber = 0; + } + + static void mouseMove(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y) + { + + if (m_ignoreMouseEvents) + return; + + QPoint globalPos(x,y); + QWindow *tlw = m_mouseGrabber.data(); + if (!tlw) + tlw = topLevelWindowAt(globalPos); + QPoint localPos = tlw ? (globalPos-tlw->position()) : globalPos; + QWindowSystemInterface::handleMouseEvent(tlw, + localPos, + globalPos, + Qt::MouseButtons(Qt::LeftButton)); + } + + static void longPress(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y) + { + m_ignoreMouseEvents = true; + QPoint globalPos(x,y); + QWindow *tlw = topLevelWindowAt(globalPos); + QPoint localPos = tlw ? (globalPos-tlw->position()) : globalPos; + + // Release left button + QWindowSystemInterface::handleMouseEvent(tlw, + localPos, + globalPos, + Qt::MouseButtons(Qt::NoButton)); + + // Press right button + QWindowSystemInterface::handleMouseEvent(tlw, + localPos, + globalPos, + Qt::MouseButtons(Qt::RightButton)); + } + + static void touchBegin(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/) + { + m_touchPoints.clear(); + } + + static void touchAdd(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint id, jint action, jboolean /*primary*/, jint x, jint y, jfloat size, jfloat pressure) + { + Qt::TouchPointState state = Qt::TouchPointStationary; + switch (action) { + case 0: + state = Qt::TouchPointPressed; + break; + case 1: + state = Qt::TouchPointMoved; + break; + case 2: + state = Qt::TouchPointStationary; + break; + case 3: + state = Qt::TouchPointReleased; + break; + } + + const int dw = desktopWidthPixels(); + const int dh = desktopHeightPixels(); + QWindowSystemInterface::TouchPoint touchPoint; + touchPoint.id = id; + touchPoint.pressure = pressure; + touchPoint.normalPosition = QPointF(double(x / dw), double(y / dh)); + touchPoint.state = state; + touchPoint.area = QRectF(x - double(dw*size) / 2.0, + y - double(dh*size) / 2.0, + double(dw*size), + double(dh*size)); + m_touchPoints.push_back(touchPoint); + } + + static void touchEnd(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint action) + { + QEvent::Type eventType = QEvent::None; + switch (action) { + case 0: + eventType = QEvent::TouchBegin; + break; + case 1: + eventType = QEvent::TouchUpdate; + break; + case 2: + eventType = QEvent::TouchEnd; + break; + } + + // FIXME + // QWindowSystemInterface::handleTouchEvent(0, 0, eventType, QTouchEvent::TouchScreen, m_touchPoints); + } + + static int mapAndroidKey(int key) + { + // 0--9 0x00000007 -- 0x00000010 + if (key >= 0x00000007 && key <= 0x00000010) + return Qt::Key_0 + key - 0x00000007; + + // A--Z 0x0000001d -- 0x00000036 + if (key >= 0x0000001d && key <= 0x00000036) + return Qt::Key_A + key - 0x0000001d; + + switch (key) { + case 0x00000039: + case 0x0000003a: + return Qt::Key_Alt; + + case 0x0000004b: + return Qt::Key_Apostrophe; + + case 0x00000004: // KEYCODE_BACK + return Qt::Key_Back; + + case 0x00000049: + return Qt::Key_Backslash; + + case 0x00000005: + return Qt::Key_Call; + + case 0x0000001b: + return Qt::Key_WebCam; + + case 0x0000001c: + return Qt::Key_Clear; + + case 0x00000037: + return Qt::Key_Comma; + + case 0x00000043: + return Qt::Key_Backspace; + + case 0x00000017: // KEYCODE_DPAD_CENTER + return Qt::Key_Enter; + + case 0x00000014: // KEYCODE_DPAD_DOWN + return Qt::Key_Down; + + case 0x00000015: //KEYCODE_DPAD_LEFT + return Qt::Key_Left; + + case 0x00000016: //KEYCODE_DPAD_RIGHT + return Qt::Key_Right; + + case 0x00000013: //KEYCODE_DPAD_UP + return Qt::Key_Up; + + case 0x00000006: //KEYCODE_ENDCALL + return Qt::Key_Hangup; + + case 0x00000042: + return Qt::Key_Return; + + case 0x00000041: //KEYCODE_ENVELOPE + return Qt::Key_LaunchMail; + + case 0x00000046: + return Qt::Key_Equal; + + case 0x00000040: + return Qt::Key_Explorer; + + case 0x00000003: + return Qt::Key_Home; + + case 0x00000047: + return Qt::Key_BracketLeft; + + case 0x0000005a: // KEYCODE_MEDIA_FAST_FORWARD + return Qt::Key_Forward; + + case 0x00000057: + return Qt::Key_MediaNext; + + case 0x00000055: + return Qt::Key_MediaPlay; + + case 0x00000058: + return Qt::Key_MediaPrevious; + + case 0x00000059: + return Qt::Key_AudioRewind; + + case 0x00000056: + return Qt::Key_MediaStop; + + case 0x00000052: //KEYCODE_MENU + return Qt::Key_Menu; + + case 0x00000045: + return Qt::Key_Minus; + + case 0x0000005b: + return Qt::Key_VolumeMute; + + case 0x0000004e: + return Qt::Key_NumLock; + + case 0x00000038: + return Qt::Key_Period; + + case 0x00000051: + return Qt::Key_Plus; + + case 0x0000001a: + return Qt::Key_PowerOff; + + case 0x00000048: + return Qt::Key_BracketRight; + + case 0x00000054: + return Qt::Key_Search; + + case 0x0000004a: + return Qt::Key_Semicolon; + + case 0x0000003b: + case 0x0000003c: + return Qt::Key_Shift; + + case 0x0000004c: + return Qt::Key_Slash; + + case 0x00000001: + return Qt::Key_Left; + + case 0x00000002: + return Qt::Key_Right; + + case 0x0000003e: + return Qt::Key_Space; + + case 0x0000003f: // KEYCODE_SYM + return Qt::Key_Meta; + + case 0x0000003d: + return Qt::Key_Tab; + + case 0x00000019: + return Qt::Key_VolumeDown; + + case 0x00000018: + return Qt::Key_VolumeUp; + + case 0x00000000: // KEYCODE_UNKNOWN + case 0x00000011: // KEYCODE_STAR ?!?!? + case 0x00000012: // KEYCODE_POUND ?!?!? + case 0x00000053: // KEYCODE_NOTIFICATION ?!?!? + case 0x0000004f: // KEYCODE_HEADSETHOOK ?!?!? + case 0x00000044: // KEYCODE_GRAVE ?!?!? + case 0x00000050: // KEYCODE_FOCUS ?!?!? + return Qt::Key_Any; + + default: + return 0; + } + } + + static void keyDown(JNIEnv */*env*/, jobject /*thiz*/, jint key, jint unicode, jint modifier) + { + Qt::KeyboardModifiers modifiers; + if (modifier & 1) + modifiers |= Qt::ShiftModifier; + + if (modifier & 2) + modifiers |= Qt::AltModifier; + + if (modifier & 4) + modifiers |= Qt::MetaModifier; + + QWindowSystemInterface::handleKeyEvent(0, + QEvent::KeyPress, + mapAndroidKey(key), + modifiers, + QChar(unicode), + true); + } + + static void keyUp(JNIEnv */*env*/, jobject /*thiz*/, jint key, jint unicode, jint modifier) + { + Qt::KeyboardModifiers modifiers; + if (modifier & 1) + modifiers |= Qt::ShiftModifier; + + if (modifier & 2) + modifiers |= Qt::AltModifier; + + if (modifier & 4) + modifiers |= Qt::MetaModifier; + + QWindowSystemInterface::handleKeyEvent(0, + QEvent::KeyRelease, + mapAndroidKey(key), + modifiers, + QChar(unicode), + true); + } + + + static JNINativeMethod methods[] = { + {"touchBegin","(I)V",(void*)touchBegin}, + {"touchAdd","(IIIZIIFF)V",(void*)touchAdd}, + {"touchEnd","(II)V",(void*)touchEnd}, + {"mouseDown", "(III)V", (void *)mouseDown}, + {"mouseUp", "(III)V", (void *)mouseUp}, + {"mouseMove", "(III)V", (void *)mouseMove}, + {"longPress", "(III)V", (void *)longPress}, + {"keyDown", "(III)V", (void *)keyDown}, + {"keyUp", "(III)V", (void *)keyUp} + }; + +#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ + VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ + if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), methodErrorMsgFmt(), METHOD_NAME, METHOD_SIGNATURE); \ + return false; \ + } + + bool registerNatives(JNIEnv *env) + { + jclass appClass = QtAndroid::applicationClass(); + + if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed"); + return false; + } + + GET_AND_CHECK_STATIC_METHOD(m_showSoftwareKeyboardMethodID, appClass, "showSoftwareKeyboard", "(IIIII)V"); + GET_AND_CHECK_STATIC_METHOD(m_resetSoftwareKeyboardMethodID, appClass, "resetSoftwareKeyboard", "()V"); + GET_AND_CHECK_STATIC_METHOD(m_hideSoftwareKeyboardMethodID, appClass, "hideSoftwareKeyboard", "()V"); + GET_AND_CHECK_STATIC_METHOD(m_isSoftwareKeyboardVisibleMethodID, appClass, "isSoftwareKeyboardVisible", "()Z"); + GET_AND_CHECK_STATIC_METHOD(m_updateSelectionMethodID, appClass, "updateSelection", "(IIII)V"); + return true; + } +} diff --git a/src/plugins/platforms/android/src/androidjniinput.h b/src/plugins/platforms/android/src/androidjniinput.h new file mode 100644 index 0000000000..a78c7519db --- /dev/null +++ b/src/plugins/platforms/android/src/androidjniinput.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROIDJNIINPUT_H +#define ANDROIDJNIINPUT_H +#include + +namespace QtAndroidInput +{ + // Software keyboard support + void showSoftwareKeyboard(int top, int left, int width, int height, int inputHints); + void resetSoftwareKeyboard(); + void hideSoftwareKeyboard(); + bool isSoftwareKeyboardVisible(); + void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd); + // Software keyboard support + + bool registerNatives(JNIEnv *env); +} + +#endif // ANDROIDJNIINPUT_H diff --git a/src/plugins/platforms/android/src/androidjnimain.cpp b/src/plugins/platforms/android/src/androidjnimain.cpp new file mode 100644 index 0000000000..2a4c48df3c --- /dev/null +++ b/src/plugins/platforms/android/src/androidjnimain.cpp @@ -0,0 +1,839 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "androidjnimain.h" +#include "androidjniinput.h" +#include "androidjniclipboard.h" +#include "androidjnimenu.h" +#include "qandroidplatformintegration.h" +#include + +#include + +#include +#include +#include "qandroidassetsfileenginehandler.h" +#include + +#include + +#ifdef ANDROID_PLUGIN_OPENGL +# include "qandroidopenglplatformwindow.h" +#endif + +#if __ANDROID_API__ > 8 +# include +#endif + +static jmethodID m_redrawSurfaceMethodID = 0; + +Q_IMPORT_PLUGIN(QAndroidPlatformIntegrationPlugin) + +static JavaVM *m_javaVM = NULL; +static jclass m_applicationClass = NULL; +static jobject m_classLoaderObject = NULL; +static jmethodID m_loadClassMethodID = NULL; +static AAssetManager *m_assetManager = NULL; +static jobject m_resourcesObj; +static jobject m_activityObject = NULL; + +static jclass m_bitmapClass = 0; +static jmethodID m_createBitmapMethodID = 0; +static jobject m_ARGB_8888_BitmapConfigValue = 0; +static jobject m_RGB_565_BitmapConfigValue = 0; + +static jclass m_bitmapDrawableClass = 0; +static jmethodID m_bitmapDrawableConstructorMethodID = 0; + +extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application +static Main m_main = NULL; +static void *m_mainLibraryHnd = NULL; +static QList m_applicationParams; + +#ifndef ANDROID_PLUGIN_OPENGL +static jobject m_surface = NULL; +#else +static EGLNativeWindowType m_nativeWindow = 0; +static QSemaphore m_waitForWindowSemaphore; +static bool m_waitForWindow = false; + +static jfieldID m_surfaceFieldID = 0; +#endif + + +static QSemaphore m_quitAppSemaphore; +static QMutex m_surfaceMutex(QMutex::Recursive); +static QSemaphore m_pauseApplicationSemaphore; +static QMutex m_pauseApplicationMutex; + +static QAndroidPlatformIntegration *m_androidPlatformIntegration = 0; + +static int m_desktopWidthPixels = 0; +static int m_desktopHeightPixels = 0; + +static volatile bool m_pauseApplication; + +static jmethodID m_setFullScreenMethodID = 0; + +static AndroidAssetsFileEngineHandler *m_androidAssetsFileEngineHandler = 0; + + + +static const char m_qtTag[] = "Qt"; +static const char m_classErrorMsg[] = "Can't find class \"%s\""; +static const char m_methodErrorMsg[] = "Can't find method \"%s%s\""; + +static inline void checkPauseApplication() +{ + m_pauseApplicationMutex.lock(); + if (m_pauseApplication) { + m_pauseApplicationMutex.unlock(); + m_pauseApplicationSemaphore.acquire(); // wait until surface is created + + m_pauseApplicationMutex.lock(); + m_pauseApplication = false; + m_pauseApplicationMutex.unlock(); + + //FIXME +// QWindowSystemInterface::handleScreenAvailableGeometryChange(0); +// QWindowSystemInterface::handleScreenGeometryChange(0); + } else { + m_pauseApplicationMutex.unlock(); + } +} + +namespace QtAndroid +{ +#ifndef ANDROID_PLUGIN_OPENGL + void flushImage(const QPoint &pos, const QImage &image, const QRect &destinationRect) + { + checkPauseApplication(); + QMutexLocker locker(&m_surfaceMutex); + if (!m_surface) + return; + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + int bpp = 2; + AndroidBitmapInfo info; + int ret; + + if ((ret = AndroidBitmap_getInfo(env.jniEnv, m_surface, &info)) < 0) { + qWarning() << "AndroidBitmap_getInfo() failed ! error=" << ret; + m_javaVM->DetachCurrentThread(); + return; + } + + if (info.format != ANDROID_BITMAP_FORMAT_RGB_565) { + qWarning() << "Bitmap format is not RGB_565!"; + m_javaVM->DetachCurrentThread(); + return; + } + + void *pixels; + unsigned char *screenBits; + if ((ret = AndroidBitmap_lockPixels(env.jniEnv, m_surface, &pixels)) < 0) { + qWarning() << "AndroidBitmap_lockPixels() failed! error=" << ret; + m_javaVM->DetachCurrentThread(); + return; + } + + screenBits = static_cast(pixels); + int sbpl = info.stride; + int swidth = info.width; + int sheight = info.height; + + unsigned sposx = pos.x() + destinationRect.x(); + unsigned sposy = pos.y() + destinationRect.y(); + + screenBits += sposy * sbpl; + + unsigned ibpl = image.bytesPerLine(); + unsigned iposx = destinationRect.x(); + unsigned iposy = destinationRect.y(); + + const unsigned char *imageBits = static_cast(image.bits()); + imageBits += iposy * ibpl; + + unsigned width = swidth - sposx < unsigned(destinationRect.width()) + ? (swidth-sposx) + : destinationRect.width(); + unsigned height = sheight - sposy < unsigned(destinationRect.height()) + ? (sheight - sposy) + : destinationRect.height(); + + for (unsigned y = 0; y < height; y++) { + memcpy(screenBits + y*sbpl + sposx*bpp, + imageBits + y*ibpl + iposx*bpp, + width*bpp); + } + AndroidBitmap_unlockPixels(env.jniEnv, m_surface); + + env.jniEnv->CallStaticVoidMethod(m_applicationClass, + m_redrawSurfaceMethodID, + jint(destinationRect.left()), + jint(destinationRect.top()), + jint(destinationRect.right() + 1), + jint(destinationRect.bottom() + 1)); +#warning FIXME dirty hack, figure out why it needs to add 1 to right and bottom !!!! + } + +#else // for #ifndef ANDROID_PLUGIN_OPENGL + EGLNativeWindowType nativeWindow(bool waitForWindow) + { + m_surfaceMutex.lock(); + if (!m_nativeWindow && waitForWindow) { + m_waitForWindow = true; + m_surfaceMutex.unlock(); + m_waitForWindowSemaphore.acquire(); + m_waitForWindow = false; + return m_nativeWindow; + } + m_surfaceMutex.unlock(); + return m_nativeWindow; + } + + QSize nativeWindowSize() + { + if (m_nativeWindow == 0) + return QAndroidPlatformIntegration::defaultDesktopSize(); + + int width = ANativeWindow_getWidth(m_nativeWindow); + int height = ANativeWindow_getHeight(m_nativeWindow); + + return QSize(width, height); + } +#endif + + void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration) + { + m_surfaceMutex.lock(); + m_androidPlatformIntegration = androidPlatformIntegration; + m_surfaceMutex.unlock(); + } + + void setFullScreen(QWidget *widget) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + bool fullScreen = widget->isFullScreen(); + if (!fullScreen) { + foreach (QWidget *w, qApp->topLevelWidgets()) { + fullScreen |= w->isFullScreen(); + if (fullScreen) + break; + } + } + + env.jniEnv->CallStaticVoidMethod(m_applicationClass, m_setFullScreenMethodID, fullScreen); + } + + QWindow *topLevelWindowAt(const QPoint &globalPos) + { + return m_androidPlatformIntegration + ? m_androidPlatformIntegration->screen()->topLevelAt(globalPos) + : 0; + } + + int desktopWidthPixels() + { + return m_desktopWidthPixels; + } + + int desktopHeightPixels() + { + return m_desktopHeightPixels; + } + + JavaVM *javaVM() + { + return m_javaVM; + } + + jclass findClass(const QString &className, JNIEnv *env) + { + return static_cast(env->CallObjectMethod(m_classLoaderObject, + m_loadClassMethodID, + env->NewString(reinterpret_cast(className.constData()), + jsize(className.length())))); + } + + AAssetManager *assetManager() + { + return m_assetManager; + } + + jclass applicationClass() + { + return m_applicationClass; + } + + jobject activity() + { + return m_activityObject; + } + + jobject createBitmap(QImage img, JNIEnv *env) + { + if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_RGB16) + img = img.convertToFormat(QImage::Format_ARGB32); + + jobject bitmap = env->CallStaticObjectMethod(m_bitmapClass, + m_createBitmapMethodID, + img.width(), + img.height(), + img.format() == QImage::Format_ARGB32 + ? m_ARGB_8888_BitmapConfigValue + : m_RGB_565_BitmapConfigValue); + if (!bitmap) + return 0; + + AndroidBitmapInfo info; + if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) { + env->DeleteLocalRef(bitmap); + return 0; + } + + void *pixels; + if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) { + env->DeleteLocalRef(bitmap); + return 0; + } + + if (info.stride == uint(img.bytesPerLine()) + && info.width == uint(img.width()) + && info.height == uint(img.height())) { + memcpy(pixels, img.constBits(), info.stride * info.height); + } else { + uchar *bmpPtr = static_cast(pixels); + const unsigned width = qMin(info.width, (uint)img.width()); //should be the same + const unsigned height = qMin(info.height, (uint)img.height()); //should be the same + for (unsigned y = 0; y < height; y++, bmpPtr += info.stride) + memcpy(bmpPtr, img.constScanLine(y), width); + } + AndroidBitmap_unlockPixels(env, bitmap); + return bitmap; + } + + jobject createBitmapDrawable(jobject bitmap, JNIEnv *env) + { + if (!bitmap) + return 0; + + return env->NewObject(m_bitmapDrawableClass, + m_bitmapDrawableConstructorMethodID, + m_resourcesObj, + bitmap); + } + + const char *classErrorMsgFmt() + { + return m_classErrorMsg; + } + + const char *methodErrorMsgFmt() + { + return m_methodErrorMsg; + } + + const char *qtTagText() + { + return m_qtTag; + } +} + +static jboolean startQtAndroidPlugin(JNIEnv* /*env*/, jobject /*object*//*, jobject applicationAssetManager*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + m_surface = 0; +#else + m_nativeWindow = 0; + m_waitForWindow = false; +#endif + + m_androidPlatformIntegration = 0; + m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler(); + +#ifdef ANDROID_PLUGIN_OPENGL + return true; +#else + return false; +#endif +} + +static void *startMainMethod(void */*data*/) +{ + char const **params; + params = static_cast(malloc(m_applicationParams.length() * sizeof(char *))); + for (int i = 0; i < m_applicationParams.size(); i++) + params[i] = static_cast(m_applicationParams[i].constData()); + + int ret = m_main(m_applicationParams.length(), const_cast(params)); + + free(params); + Q_UNUSED(ret); + + if (m_mainLibraryHnd) { + int res = dlclose(m_mainLibraryHnd); + if (res < 0) + qWarning() << "dlclose failed:" << dlerror(); + } + + QtAndroid::AttachedJNIEnv env; + if (!env.jniEnv) + return 0; + + if (m_applicationClass) { + jmethodID quitApp = env.jniEnv->GetStaticMethodID(m_applicationClass, "quitApp", "()V"); + env.jniEnv->CallStaticVoidMethod(m_applicationClass, quitApp); + } + + return 0; +} + +static jboolean startQtApplication(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString) +{ + m_mainLibraryHnd = NULL; + const char *nativeString = env->GetStringUTFChars(environmentString, 0); + QByteArray string = nativeString; + env->ReleaseStringUTFChars(environmentString, nativeString); + m_applicationParams=string.split('\t'); + foreach (string, m_applicationParams) { + if (putenv(string.constData())) + qWarning() << "Can't set environment" << string; + } + + nativeString = env->GetStringUTFChars(paramsString, 0); + string = nativeString; + env->ReleaseStringUTFChars(paramsString, nativeString); + + m_applicationParams=string.split('\t'); + + // Go home + QDir::setCurrent(QDir::homePath()); + + //look for main() + if (m_applicationParams.length()) { + // Obtain a handle to the main library (the library that contains the main() function). + // This library should already be loaded, and calling dlopen() will just return a reference to it. + m_mainLibraryHnd = dlopen(m_applicationParams.first().data(), 0); + if (m_mainLibraryHnd == NULL) { + qCritical() << "dlopen failed:" << dlerror(); + return false; + } + m_main = (Main)dlsym(m_mainLibraryHnd, "main"); + } else { + qWarning() << "No main library was specified; searching entire process (this is slow!)"; + m_main = (Main)dlsym(RTLD_DEFAULT, "main"); + } + + if (!m_main) { + qCritical() << "dlsym failed:" << dlerror(); + qCritical() << "Could not find main method"; + return false; + } + + pthread_t appThread; + return pthread_create(&appThread, NULL, startMainMethod, NULL) == 0; +} + +static void pauseQtApp(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.lock(); + m_pauseApplicationMutex.lock(); + + if (m_androidPlatformIntegration) + m_androidPlatformIntegration->pauseApp(); + m_pauseApplication = true; + + m_pauseApplicationMutex.unlock(); + m_surfaceMutex.unlock(); +} + +static void resumeQtApp(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.lock(); + m_pauseApplicationMutex.lock(); + if (m_androidPlatformIntegration) + m_androidPlatformIntegration->resumeApp(); + + if (m_pauseApplication) + m_pauseApplicationSemaphore.release(); + + m_pauseApplicationMutex.unlock(); + m_surfaceMutex.unlock(); +} + +static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) { + env->DeleteGlobalRef(m_surface); + m_surface = 0; + } +#else + Q_UNUSED(env); +#endif + + m_androidPlatformIntegration = 0; + delete m_androidAssetsFileEngineHandler; +} + +static void terminateQt(JNIEnv *env, jclass /*clazz*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) + env->DeleteGlobalRef(m_surface); +#endif + env->DeleteGlobalRef(m_applicationClass); + env->DeleteGlobalRef(m_classLoaderObject); + env->DeleteGlobalRef(m_resourcesObj); + env->DeleteGlobalRef(m_activityObject); + env->DeleteGlobalRef(m_bitmapClass); + env->DeleteGlobalRef(m_ARGB_8888_BitmapConfigValue); + env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue); + env->DeleteGlobalRef(m_bitmapDrawableClass); +} + +#ifdef ANDROID_PLUGIN_OPENGL +#if __ANDROID_API__ < 9 +struct FakeNativeWindow +{ + long long dummyNativeWindow;// force 64 bits alignment +}; + +class FakeSurface: public FakeNativeWindow +{ +public: + virtual void FakeSurfaceMethod() + { + fakeSurface = 0; + } + + int fakeSurface; +}; + +EGLNativeWindowType ANativeWindow_fromSurface(JNIEnv *env, jobject jSurface) +{ + FakeSurface *surface = static_cast(env->GetIntField(jSurface, m_surfaceFieldID)); + return static_cast(static_cast(surface)); +} +#endif // __ANDROID_API__ < 9 +#endif // ANDROID_PLUGIN_OPENGL + +static void setSurface(JNIEnv *env, jobject /*thiz*/, jobject jSurface) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) + env->DeleteGlobalRef(m_surface); + m_surface = env->NewGlobalRef(jSurface); +#else + m_surfaceMutex.lock(); + EGLNativeWindowType nativeWindow = ANativeWindow_fromSurface(env, jSurface); + bool sameNativeWindow = (nativeWindow != 0 && nativeWindow == m_nativeWindow); + + m_nativeWindow = nativeWindow; + if (m_waitForWindow) + m_waitForWindowSemaphore.release(); + if (m_androidPlatformIntegration && !sameNativeWindow) { + m_surfaceMutex.unlock(); + m_androidPlatformIntegration->surfaceChanged(); + } else if (m_androidPlatformIntegration && sameNativeWindow) { + QAndroidOpenGLPlatformWindow *window = m_androidPlatformIntegration->primaryWindow(); + QPlatformScreen *screen = m_androidPlatformIntegration->screen(); + QSize size = QtAndroid::nativeWindowSize(); + + QRect geometry(QPoint(0, 0), size); + QWindowSystemInterface::handleScreenAvailableGeometryChange(screen->screen(), geometry); + QWindowSystemInterface::handleScreenGeometryChange(screen->screen(), geometry); + + if (window != 0) { + window->lock(); + window->scheduleResize(size); + + QWindowSystemInterface::handleExposeEvent(window->window(), + QRegion(window->window()->geometry())); + window->unlock(); + } + + m_surfaceMutex.unlock(); + } else { + m_surfaceMutex.unlock(); + } +#endif // for #ifndef ANDROID_PLUGIN_OPENGL +} + +static void destroySurface(JNIEnv *env, jobject /*thiz*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) { + env->DeleteGlobalRef(m_surface); + m_surface = 0; + } +#else + Q_UNUSED(env); + m_nativeWindow = 0; + if (m_androidPlatformIntegration != 0) + m_androidPlatformIntegration->invalidateNativeSurface(); +#endif +} + +static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/, + jint /*widthPixels*/, jint /*heightPixels*/, + jint desktopWidthPixels, jint desktopHeightPixels, + jdouble xdpi, jdouble ydpi) +{ + m_desktopWidthPixels = desktopWidthPixels; + m_desktopHeightPixels = desktopHeightPixels; + + if (!m_androidPlatformIntegration) { + QAndroidPlatformIntegration::setDefaultDisplayMetrics(desktopWidthPixels,desktopHeightPixels, + qRound(double(desktopWidthPixels) / xdpi * 25.4), + qRound(double(desktopHeightPixels) / ydpi * 25.4)); + } else { + m_androidPlatformIntegration->setDisplayMetrics(qRound(double(desktopWidthPixels) / xdpi * 25.4), + qRound(double(desktopHeightPixels) / ydpi * 25.4)); + m_androidPlatformIntegration->setDesktopSize(desktopWidthPixels, desktopHeightPixels); + } +} + +static void lockSurface(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.lock(); +} + +static void unlockSurface(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.unlock(); +} + +static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidPlatformIntegration) + return; + + if (qApp != 0) { + foreach (QWidget *w, qApp->topLevelWidgets()) + w->update(); + } + +#ifndef ANDROID_PLUGIN_OPENGL + QAndroidPlatformScreen *screen = static_cast(m_androidPlatformIntegration->screen()); + QMetaObject::invokeMethod(screen, "setDirty", Qt::QueuedConnection, Q_ARG(QRect,screen->geometry())); +#else + qWarning("updateWindow: Dirty screen not implemented yet on OpenGL"); +#endif +} + +static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newOrientation) +{ + if (m_androidPlatformIntegration == 0) + return; + + Qt::ScreenOrientation screenOrientation = newOrientation == 1 + ? Qt::PortraitOrientation + : Qt::LandscapeOrientation; + QPlatformScreen *screen = m_androidPlatformIntegration->screen(); + QWindowSystemInterface::handleScreenOrientationChange(screen->screen(), + screenOrientation); +} + +static JNINativeMethod methods[] = { + {"startQtAndroidPlugin", "()Z", (void *)startQtAndroidPlugin}, + {"startQtApplication", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)startQtApplication}, + {"pauseQtApp", "()V", (void *)pauseQtApp}, + {"resumeQtApp", "()V", (void *)resumeQtApp}, + {"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin}, + {"terminateQt", "()V", (void *)terminateQt}, + {"setDisplayMetrics", "(IIIIDD)V", (void *)setDisplayMetrics}, + {"setSurface", "(Ljava/lang/Object;)V", (void *)setSurface}, + {"destroySurface", "()V", (void *)destroySurface}, + {"lockSurface", "()V", (void *)lockSurface}, + {"unlockSurface", "()V", (void *)unlockSurface}, + {"updateWindow", "()V", (void *)updateWindow}, + {"handleOrientationChanged", "(I)V", (void *)handleOrientationChanged} +}; + +#define FIND_AND_CHECK_CLASS(CLASS_NAME) \ +clazz = env->FindClass(CLASS_NAME); \ +if (!clazz) { \ + __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME); \ + return JNI_FALSE; \ +} + +#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ +VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ +if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \ + return JNI_FALSE; \ +} + +#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ +VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ +if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \ + return JNI_FALSE; \ +} + +#define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \ +VAR = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \ +if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \ + return JNI_FALSE; \ +} + +#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \ +VAR = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \ +if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \ + return JNI_FALSE; \ +} + +static int registerNatives(JNIEnv *env) +{ + jclass clazz; + FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/QtNative"); + m_applicationClass = static_cast(env->NewGlobalRef(clazz)); + + if (env->RegisterNatives(m_applicationClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed"); + return JNI_FALSE; + } + + GET_AND_CHECK_STATIC_METHOD(m_redrawSurfaceMethodID, m_applicationClass, "redrawSurface", "(IIII)V"); + GET_AND_CHECK_STATIC_METHOD(m_setFullScreenMethodID, m_applicationClass, "setFullScreen", "(Z)V"); + +#ifdef ANDROID_PLUGIN_OPENGL + FIND_AND_CHECK_CLASS("android/view/Surface"); +#if __ANDROID_API__ < 9 +# define ANDROID_VIEW_SURFACE_JNI_ID "mSurface" +#else +# define ANDROID_VIEW_SURFACE_JNI_ID "mNativeSurface" +#endif + GET_AND_CHECK_FIELD(m_surfaceFieldID, clazz, ANDROID_VIEW_SURFACE_JNI_ID, "I"); +#endif + + jmethodID methodID; + GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;"); + jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID); + m_activityObject = env->NewGlobalRef(activityObject); + GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "classLoader", "()Ljava/lang/ClassLoader;"); + m_classLoaderObject = env->NewGlobalRef(env->CallStaticObjectMethod(m_applicationClass, methodID)); + + clazz = env->GetObjectClass(m_classLoaderObject); + GET_AND_CHECK_METHOD(m_loadClassMethodID, clazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + + FIND_AND_CHECK_CLASS("android/content/ContextWrapper"); + GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;"); + m_assetManager = AAssetManager_fromJava(env, env->CallObjectMethod(activityObject, methodID)); + + GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;"); + m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(activityObject, methodID)); + + FIND_AND_CHECK_CLASS("android/graphics/Bitmap"); + m_bitmapClass = static_cast(env->NewGlobalRef(clazz)); + GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass + , "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); + + FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config"); + jfieldID fieldId; + GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;"); + m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId)); + GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;"); + m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId)); + + FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable"); + m_bitmapDrawableClass = static_cast(env->NewGlobalRef(clazz)); + GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID, + m_bitmapDrawableClass, + "", + "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V"); + + return JNI_TRUE; +} + +Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/) +{ + typedef union { + JNIEnv *nativeEnvironment; + void *venv; + } UnionJNIEnvToVoid; + + __android_log_print(ANDROID_LOG_INFO, "Qt", "qt start"); + UnionJNIEnvToVoid uenv; + uenv.venv = NULL; + m_javaVM = 0; + + if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { + __android_log_print(ANDROID_LOG_FATAL, "Qt", "GetEnv failed"); + return -1; + } + + JNIEnv *env = uenv.nativeEnvironment; + if (!registerNatives(env) + || !QtAndroidInput::registerNatives(env) + || !QtAndroidClipboard::registerNatives(env) + || !QtAndroidMenu::registerNatives(env)) { + __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); + return -1; + } + + m_javaVM = vm; + return JNI_VERSION_1_4; +} diff --git a/src/plugins/platforms/android/src/androidjnimain.h b/src/plugins/platforms/android/src/androidjnimain.h new file mode 100644 index 0000000000..36699f15b8 --- /dev/null +++ b/src/plugins/platforms/android/src/androidjnimain.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROID_APP_H +#define ANDROID_APP_H + +#include + +#ifdef ANDROID_PLUGIN_OPENGL +# include +#endif + +#include + +#include +#include + +class QImage; +class QRect; +class QPoint; +class QThread; +class QAndroidPlatformIntegration; +class QWidget; +class QString; +class QWindow; + +namespace QtAndroid +{ + void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration); + void setQtThread(QThread *thread); + + void setFullScreen(QWidget *widget); + +#ifndef ANDROID_PLUGIN_OPENGL + void flushImage(const QPoint &pos, const QImage &image, const QRect &rect); +#else + EGLNativeWindowType nativeWindow(bool waitToCreate = true); + QSize nativeWindowSize(); +#endif + + QWindow *topLevelWindowAt(const QPoint &globalPos); + int desktopWidthPixels(); + int desktopHeightPixels(); + JavaVM *javaVM(); + jclass findClass(const QString &className, JNIEnv *env); + AAssetManager *assetManager(); + jclass applicationClass(); + jobject activity(); + + jobject createBitmap(QImage img, JNIEnv *env = 0); + jobject createBitmapDrawable(jobject bitmap, JNIEnv *env = 0); + + struct AttachedJNIEnv + { + AttachedJNIEnv() + { + attached = false; + if (QtAndroid::javaVM()->GetEnv((void**)&jniEnv, JNI_VERSION_1_6) < 0) { + if (QtAndroid::javaVM()->AttachCurrentThread(&jniEnv, NULL) < 0) { + __android_log_print(ANDROID_LOG_ERROR, "Qt", "AttachCurrentThread failed"); + jniEnv = 0; + return; + } + attached = true; + } + } + + ~AttachedJNIEnv() + { + if (attached) + QtAndroid::javaVM()->DetachCurrentThread(); + } + bool attached; + JNIEnv *jniEnv; + }; + const char *classErrorMsgFmt(); + const char *methodErrorMsgFmt(); + const char *qtTagText(); + +} +#endif // ANDROID_APP_H diff --git a/src/plugins/platforms/android/src/androidjnimenu.cpp b/src/plugins/platforms/android/src/androidjnimenu.cpp new file mode 100644 index 0000000000..e49af0fdac --- /dev/null +++ b/src/plugins/platforms/android/src/androidjnimenu.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "androidjnimenu.h" +#include "androidjnimain.h" +#include +#include +#include +#include +#include "qandroidplatformmenubar.h" +#include "qandroidplatformmenu.h" +#include + +using namespace QtAndroid; + +namespace QtAndroidMenu +{ + static QQueue pendingContextMenus; + static QAndroidPlatformMenu *visibleMenu = 0; + static QMutex visibleMenuMutex(QMutex::Recursive); + + static QSet menuBars; + static QAndroidPlatformMenuBar *visibleMenuBar = 0; + static QWindow *activeTopLevelWindow = 0; + static QMutex menuBarMutex(QMutex::Recursive); + + static jmethodID openContextMenuMethodID = 0; + static jmethodID closeContextMenuMethodID = 0; + static jmethodID resetOptionsMenuMethodID = 0; + + static jmethodID clearMenuMethodID = 0; + static jmethodID addMenuItemMethodID = 0; + static int menuNoneValue = 0; + static jmethodID setHeaderTitleContextMenuMethodID = 0; + + static jmethodID setCheckableMenuItemMethodID = 0; + static jmethodID setCheckedMenuItemMethodID = 0; + static jmethodID setEnabledMenuItemMethodID = 0; + static jmethodID setIconMenuItemMethodID = 0; + static jmethodID setVisibleMenuItemMethodID = 0; + + void resetMenuBar() + { + AttachedJNIEnv env; + if (env.jniEnv) + env.jniEnv->CallStaticVoidMethod(applicationClass(), resetOptionsMenuMethodID); + } + + void showContextMenu(QAndroidPlatformMenu *menu, JNIEnv *env) + { + QMutexLocker lock(&visibleMenuMutex); + if (visibleMenu) { + pendingContextMenus.enqueue(menu); + } else { + visibleMenu = menu; + menu->aboutToShow(); + if (env) { + env->CallStaticVoidMethod(applicationClass(), openContextMenuMethodID); + } else { + AttachedJNIEnv aenv; + if (aenv.jniEnv) + aenv.jniEnv->CallStaticVoidMethod(applicationClass(), openContextMenuMethodID); + } + } + } + + void hideContextMenu(QAndroidPlatformMenu *menu) + { + QMutexLocker lock(&visibleMenuMutex); + if (visibleMenu == menu) { + AttachedJNIEnv env; + if (env.jniEnv) + env.jniEnv->CallStaticVoidMethod(applicationClass(), openContextMenuMethodID); + } else { + pendingContextMenus.removeOne(menu); + } + } + + void syncMenu(QAndroidPlatformMenu */*menu*/) + { +// QMutexLocker lock(&visibleMenuMutex); +// if (visibleMenu == menu) +// { +// hideContextMenu(menu); +// showContextMenu(menu); +// } + } + + void androidPlatformMenuDestroyed(QAndroidPlatformMenu *menu) + { + QMutexLocker lock(&visibleMenuMutex); + if (visibleMenu == menu) + visibleMenu = 0; + } + + void setMenuBar(QAndroidPlatformMenuBar *menuBar, QWindow *window) + { + if (activeTopLevelWindow == window && visibleMenuBar != menuBar) { + visibleMenuBar = menuBar; + resetMenuBar(); + } + } + + void setActiveTopLevelWindow(QWindow *window) + { + QMutexLocker lock(&menuBarMutex); + if (activeTopLevelWindow == window) + return; + + visibleMenuBar = 0; + activeTopLevelWindow = window; +#ifdef ANDROID_PLUGIN_OPENGL + //only one toplevel window, so the menu bar always belongs to us + if (menuBars.size() == 1) { + visibleMenuBar = *menuBars.constBegin(); //since QSet doesn't have first() + } else +#endif + foreach (QAndroidPlatformMenuBar *menuBar, menuBars) { + if (menuBar->parentWindow() == window) { + visibleMenuBar = menuBar; + break; + } + } + + resetMenuBar(); + } + + void addMenuBar(QAndroidPlatformMenuBar *menuBar) + { + QMutexLocker lock(&menuBarMutex); + menuBars.insert(menuBar); + } + + void removeMenuBar(QAndroidPlatformMenuBar *menuBar) + { + QMutexLocker lock(&menuBarMutex); + menuBars.remove(menuBar); + if (visibleMenuBar == menuBar) + resetMenuBar(); + } + + static void fillMenuItem(JNIEnv *env, jobject menuItem, bool checkable, bool checked, bool enabled, bool visible, const QIcon &icon=QIcon()) + { + env->CallObjectMethod(menuItem, setCheckableMenuItemMethodID, checkable); + env->CallObjectMethod(menuItem, setCheckedMenuItemMethodID, checked); + env->CallObjectMethod(menuItem, setEnabledMenuItemMethodID, enabled); + + if (!icon.isNull()) { + int sz = qMax(36, qgetenv("QT_ANDROID_APP_ICON_SIZE").toInt()); + QImage img = icon.pixmap(QSize(sz,sz), + enabled + ? QIcon::Normal + : QIcon::Disabled, + QIcon::On).toImage(); + env->CallObjectMethod(menuItem, + setIconMenuItemMethodID, + createBitmapDrawable(createBitmap(img, env), env)); + } + + env->CallObjectMethod(menuItem, setVisibleMenuItemMethodID, visible); + } + + static int addAllMenuItemsToMenu(JNIEnv *env, jobject menu, QAndroidPlatformMenu *platformMenu) { + int order = 0; + QMutexLocker lock(platformMenu->menuItemsMutex()); + foreach (QAndroidPlatformMenuItem *item, platformMenu->menuItems()) { + if (item->isSeparator()) + continue; + jstring jtext = env->NewString(reinterpret_cast(item->text().data()), + item->text().length()); + jobject menuItem = env->CallObjectMethod(menu, + addMenuItemMethodID, + menuNoneValue, + int(item->tag()), + order++, + jtext); + env->DeleteLocalRef(jtext); + fillMenuItem(env, + menuItem, + item->isCheckable(), + item->isChecked(), + item->isEnabled(), + item->isVisible(), + item->icon()); + } + + return order; + } + + static jboolean onPrepareOptionsMenu(JNIEnv *env, jobject /*thiz*/, jobject menu) + { + env->CallVoidMethod(menu, clearMenuMethodID); + QMutexLocker lock(&menuBarMutex); + if (!visibleMenuBar) + return JNI_FALSE; + + const QAndroidPlatformMenuBar::PlatformMenusType &menus = visibleMenuBar->menus(); + int order = 0; + QMutexLocker lockMenuBarMutex(visibleMenuBar->menusListMutex()); + if (menus.size() == 1) { // Expand the menu + order = addAllMenuItemsToMenu(env, menu, static_cast(menus.front())); + } else { + foreach (QAndroidPlatformMenu *item, menus) { + jstring jtext = env->NewString(reinterpret_cast(item->text().data()), + item->text().length()); + jobject menuItem = env->CallObjectMethod(menu, + addMenuItemMethodID, + menuNoneValue, + int(item->tag()), + order++, + jtext); + env->DeleteLocalRef(jtext); + + fillMenuItem(env, + menuItem, + false, + false, + item->isEnabled(), + item->isVisible(), + item->icon()); + } + } + return order ? JNI_TRUE : JNI_FALSE; + } + + static jboolean onOptionsItemSelected(JNIEnv *env, jobject /*thiz*/, jint menuId, jboolean checked) + { + QMutexLocker lock(&menuBarMutex); + if (!visibleMenuBar) + return JNI_FALSE; + + const QAndroidPlatformMenuBar::PlatformMenusType &menus = visibleMenuBar->menus(); + if (menus.size() == 1) { // Expanded menu + QAndroidPlatformMenuItem *item = static_cast(menus.front()->menuItemForTag(menuId)); + if (item) { + if (item->menu()) { + showContextMenu(item->menu(), env); + } else { + if (item->isCheckable()) + item->setChecked(checked); + item->activated(); + } + } + } else { + QAndroidPlatformMenu *menu = static_cast(visibleMenuBar->menuForTag(menuId)); + if (menu) + showContextMenu(menu, env); + } + + return JNI_TRUE; + } + + static void onOptionsMenuClosed(JNIEnv */*env*/, jobject /*thiz*/, jobject /*menu*/) + { + } + + static void onCreateContextMenu(JNIEnv *env, jobject /*thiz*/, jobject menu) + { + env->CallVoidMethod(menu, clearMenuMethodID); + QMutexLocker lock(&visibleMenuMutex); + if (!visibleMenu) + return; + + jstring jtext = env->NewString(reinterpret_cast(visibleMenu->text().data()), + visibleMenu->text().length()); + env->CallObjectMethod(menu, setHeaderTitleContextMenuMethodID, jtext); + env->DeleteLocalRef(jtext); + addAllMenuItemsToMenu(env, menu, visibleMenu); + } + + static jboolean onContextItemSelected(JNIEnv *env, jobject /*thiz*/, jint menuId, jboolean checked) + { + QMutexLocker lock(&visibleMenuMutex); + QAndroidPlatformMenuItem * item = static_cast(visibleMenu->menuItemForTag(menuId)); + if (item) { + if (item->menu()) { + showContextMenu(item->menu(), env); + } else { + if (item->isCheckable()) + item->setChecked(checked); + item->activated(); + } + } + return JNI_TRUE; + } + + static void onContextMenuClosed(JNIEnv *env, jobject /*thiz*/, jobject /*menu*/) + { + QMutexLocker lock(&visibleMenuMutex); + if (!visibleMenu) + return; + visibleMenu->aboutToHide(); + visibleMenu = 0; + if (!pendingContextMenus.empty()) + showContextMenu(pendingContextMenus.dequeue(), env); + } + + static JNINativeMethod methods[] = { + {"onPrepareOptionsMenu", "(Landroid/view/Menu;)Z", (void *)onPrepareOptionsMenu}, + {"onOptionsItemSelected", "(IZ)Z", (void *)onOptionsItemSelected}, + {"onOptionsMenuClosed", "(Landroid/view/Menu;)V", (void*)onOptionsMenuClosed}, + {"onCreateContextMenu", "(Landroid/view/ContextMenu;)V", (void *)onCreateContextMenu}, + {"onContextItemSelected", "(IZ)Z", (void *)onContextItemSelected}, + {"onContextMenuClosed", "(Landroid/view/Menu;)V", (void*)onContextMenuClosed}, + }; + +#define FIND_AND_CHECK_CLASS(CLASS_NAME) \ + clazz = env->FindClass(CLASS_NAME); \ + if (!clazz) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), classErrorMsgFmt(), CLASS_NAME); \ + return false; \ + } + +#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ + VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ + if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), methodErrorMsgFmt(), METHOD_NAME, METHOD_SIGNATURE); \ + return false; \ + } + +#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ + VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ + if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), methodErrorMsgFmt(), METHOD_NAME, METHOD_SIGNATURE); \ + return false; \ + } + +#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \ + VAR = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \ + if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), methodErrorMsgFmt(), FIELD_NAME, FIELD_SIGNATURE); \ + return false; \ + } + + bool registerNatives(JNIEnv *env) + { + jclass appClass = applicationClass(); + + if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed"); + return false; + } + + GET_AND_CHECK_STATIC_METHOD(openContextMenuMethodID, appClass, "openContextMenu", "()V"); + GET_AND_CHECK_STATIC_METHOD(closeContextMenuMethodID, appClass, "closeContextMenu", "()V"); + GET_AND_CHECK_STATIC_METHOD(resetOptionsMenuMethodID, appClass, "resetOptionsMenu", "()V"); + + jclass clazz; + FIND_AND_CHECK_CLASS("android/view/Menu"); + GET_AND_CHECK_METHOD(clearMenuMethodID, clazz, "clear", "()V"); + GET_AND_CHECK_METHOD(addMenuItemMethodID, clazz, "add", "(IIILjava/lang/CharSequence;)Landroid/view/MenuItem;"); + jfieldID menuNoneFiledId; + GET_AND_CHECK_STATIC_FIELD(menuNoneFiledId, clazz, "NONE", "I"); + menuNoneValue = env->GetStaticIntField(clazz, menuNoneFiledId); + + FIND_AND_CHECK_CLASS("android/view/ContextMenu"); + GET_AND_CHECK_METHOD(setHeaderTitleContextMenuMethodID, clazz, "setHeaderTitle","(Ljava/lang/CharSequence;)Landroid/view/ContextMenu;"); + + FIND_AND_CHECK_CLASS("android/view/MenuItem"); + GET_AND_CHECK_METHOD(setCheckableMenuItemMethodID, clazz, "setCheckable", "(Z)Landroid/view/MenuItem;"); + GET_AND_CHECK_METHOD(setCheckedMenuItemMethodID, clazz, "setChecked", "(Z)Landroid/view/MenuItem;"); + GET_AND_CHECK_METHOD(setEnabledMenuItemMethodID, clazz, "setEnabled", "(Z)Landroid/view/MenuItem;"); + GET_AND_CHECK_METHOD(setIconMenuItemMethodID, clazz, "setIcon", "(Landroid/graphics/drawable/Drawable;)Landroid/view/MenuItem;"); + GET_AND_CHECK_METHOD(setVisibleMenuItemMethodID, clazz, "setVisible", "(Z)Landroid/view/MenuItem;"); + return true; + } +} diff --git a/src/plugins/platforms/android/src/androidjnimenu.h b/src/plugins/platforms/android/src/androidjnimenu.h new file mode 100644 index 0000000000..7c5422f67b --- /dev/null +++ b/src/plugins/platforms/android/src/androidjnimenu.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROIDJNIMENU_H +#define ANDROIDJNIMENU_H + +#include + +class QAndroidPlatformMenuBar; +class QAndroidPlatformMenu; +class QAndroidPlatformMenuItem; +class QWindow; + +namespace QtAndroidMenu +{ + // Menu support + void showContextMenu(QAndroidPlatformMenu *menu, JNIEnv *env = 0); + void hideContextMenu(QAndroidPlatformMenu *menu); + void syncMenu(QAndroidPlatformMenu *menu); + void androidPlatformMenuDestroyed(QAndroidPlatformMenu *menu); + + void setMenuBar(QAndroidPlatformMenuBar *menuBar, QWindow *window); + void setActiveTopLevelWindow(QWindow *window); + void addMenuBar(QAndroidPlatformMenuBar *menuBar); + void removeMenuBar(QAndroidPlatformMenuBar *menuBar); + + // Menu support + bool registerNatives(JNIEnv *env); +} + +#endif // ANDROIDJNIMENU_H diff --git a/src/plugins/platforms/android/src/androidplatformplugin.cpp b/src/plugins/platforms/android/src/androidplatformplugin.cpp new file mode 100644 index 0000000000..71c5096e16 --- /dev/null +++ b/src/plugins/platforms/android/src/androidplatformplugin.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qandroidplatformintegration.h" + +QT_BEGIN_NAMESPACE + +class QAndroidPlatformIntegrationPlugin: public QPlatformIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.1" FILE "android.json") +public: + QPlatformIntegration *create(const QString &key, const QStringList ¶mList); +}; + + +QPlatformIntegration *QAndroidPlatformIntegrationPlugin::create(const QString &key, const QStringList ¶mList) +{ + Q_UNUSED(paramList); + if (key.toLower() == "android") + return new QAndroidPlatformIntegration(paramList); + return 0; +} + +QT_END_NAMESPACE +#include "androidplatformplugin.moc" + diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp new file mode 100644 index 0000000000..aa8ee57341 --- /dev/null +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidopenglcontext.h" +#include "qandroidopenglplatformwindow.h" +#include "qandroidplatformintegration.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +QAndroidOpenGLContext::QAndroidOpenGLContext(const QAndroidPlatformIntegration *integration, + const QSurfaceFormat &format, + QPlatformOpenGLContext *share, + EGLDisplay display, + EGLenum eglApi) + : QEglFSContext(format, share, display, eglApi) + , m_platformIntegration(integration) +{ +} + +void QAndroidOpenGLContext::swapBuffers(QPlatformSurface *surface) +{ + QEglFSContext::swapBuffers(surface); + + QAndroidOpenGLPlatformWindow *primaryWindow = m_platformIntegration->primaryWindow(); + if (primaryWindow == surface) { + primaryWindow->lock(); + QSize size = primaryWindow->scheduledResize(); + if (size.isValid()) { + QRect geometry(QPoint(0, 0), size); + primaryWindow->setGeometry(geometry); + primaryWindow->scheduleResize(QSize()); + } + primaryWindow->unlock(); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.h b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.h new file mode 100644 index 0000000000..c4c5a430ad --- /dev/null +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDOPENGLCONTEXT_H +#define QANDROIDOPENGLCONTEXT_H + +#include +#include "qeglfscontext.h" + +QT_BEGIN_NAMESPACE + +class QAndroidPlatformIntegration; +class QAndroidOpenGLContext : public QEglFSContext +{ +public: + QAndroidOpenGLContext(const QAndroidPlatformIntegration *integration, + const QSurfaceFormat &format, + QPlatformOpenGLContext *share, + EGLDisplay display, + EGLenum eglApi = EGL_OPENGL_ES_API); + + void swapBuffers(QPlatformSurface *surface); + +private: + const QAndroidPlatformIntegration *m_platformIntegration; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDOPENGLCONTEXT_H diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp new file mode 100644 index 0000000000..15c6559157 --- /dev/null +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidopenglplatformwindow.h" +#include "androidjnimain.h" +#include + +QT_BEGIN_NAMESPACE + +QAndroidOpenGLPlatformWindow::QAndroidOpenGLPlatformWindow(QWindow *window) + : QEglFSWindow(window) +{ +} + +bool QAndroidOpenGLPlatformWindow::isExposed() const +{ + return QtAndroid::nativeWindow(false) != 0 && QEglFSWindow::isExposed(); +} + +void QAndroidOpenGLPlatformWindow::invalidateSurface() +{ + QWindowSystemInterface::handleExposeEvent(window(), QRegion()); // Obscure event + QWindowSystemInterface::flushWindowSystemEvents(); + QEglFSWindow::invalidateSurface(); +} + +void QAndroidOpenGLPlatformWindow::resetSurface() +{ + QEglFSWindow::resetSurface(); + QWindowSystemInterface::handleExposeEvent(window(), QRegion(geometry())); // Expose event + QWindowSystemInterface::flushWindowSystemEvents(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h new file mode 100644 index 0000000000..b835cb3246 --- /dev/null +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDOPENGLPLATFORMWINDOW_H +#define QANDROIDOPENGLPLATFORMWINDOW_H + +#include "qeglfswindow.h" +#include + +QT_BEGIN_NAMESPACE + +class QAndroidOpenGLPlatformWindow : public QEglFSWindow +{ +public: + QAndroidOpenGLPlatformWindow(QWindow *window); + + QSize scheduledResize() const { return m_scheduledResize; } + void scheduleResize(const QSize &size) { m_scheduledResize = size; } + + void lock() { m_lock.lock(); } + void unlock() { m_lock.unlock(); } + + bool isExposed() const; + + void invalidateSurface(); + void resetSurface(); + +private: + QSize m_scheduledResize; + QMutex m_lock; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDOPENGLPLATFORMWINDOW_H diff --git a/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp b/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp new file mode 100644 index 0000000000..cd415843a7 --- /dev/null +++ b/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfshooks.h" +#include "androidjnimain.h" +#include "qandroidplatformintegration.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QEglFSAndroidHooks: public QEglFSHooks +{ +public: + void platformInit(); + void platformDestroy(); + EGLNativeDisplayType platformDisplay() const; + QSize screenSize() const; + QSizeF physicalScreenSize() const; + int screenDepth() const; + QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const; + EGLNativeWindowType createNativeWindow(const QSize &size, const QSurfaceFormat &format); + void destroyNativeWindow(EGLNativeWindowType window); + bool hasCapability(QPlatformIntegration::Capability cap) const; +}; + +void QEglFSAndroidHooks::platformInit() +{ +} + +void QEglFSAndroidHooks::platformDestroy() +{ +} + +EGLNativeDisplayType QEglFSAndroidHooks::platformDisplay() const +{ + return EGL_DEFAULT_DISPLAY; +} + +QSize QEglFSAndroidHooks::screenSize() const +{ + return QtAndroid::nativeWindowSize(); +} + +QSizeF QEglFSAndroidHooks::physicalScreenSize() const +{ + return QSizeF(QAndroidPlatformIntegration::m_defaultPhysicalSizeWidth, QAndroidPlatformIntegration::m_defaultPhysicalSizeHeight); +} + + +EGLNativeWindowType QEglFSAndroidHooks::createNativeWindow(const QSize &size, const QSurfaceFormat &format) +{ + ANativeWindow *window = QtAndroid::nativeWindow(); + if (window != 0) + ANativeWindow_acquire(window); + + return window; +} + +void QEglFSAndroidHooks::destroyNativeWindow(EGLNativeWindowType window) +{ + ANativeWindow_release(window); +} + +bool QEglFSAndroidHooks::hasCapability(QPlatformIntegration::Capability capability) const +{ + switch (capability) { + case QPlatformIntegration::OpenGL: return true; + case QPlatformIntegration::ThreadedOpenGL: return true; + default: return false; + }; +} + +int QEglFSAndroidHooks::screenDepth() const +{ + // ### Hardcoded + return 32; +} + +QSurfaceFormat QEglFSAndroidHooks::surfaceFormatFor(const QSurfaceFormat &inputFormat) const +{ + QSurfaceFormat ret(inputFormat); + ret.setAlphaBufferSize(8); + ret.setRedBufferSize(8); + ret.setGreenBufferSize(8); + ret.setBlueBufferSize(8); + return ret; +} + +static QEglFSAndroidHooks eglFSAndroidHooks; +QEglFSHooks *platformHooks = &eglFSAndroidHooks; + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.cpp new file mode 100644 index 0000000000..f3cb2586cc --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidassetsfileenginehandler.h" +#include "androidjnimain.h" + +#include + +class AndroidAbstractFileEngineIterator: public QAbstractFileEngineIterator +{ +public: + AndroidAbstractFileEngineIterator(QDir::Filters filters, + const QStringList &nameFilters, + AAssetDir *asset, + const QString &path) + : QAbstractFileEngineIterator(filters, nameFilters) + { + AAssetDir_rewind(asset); + const char *fileName; + while ((fileName = AAssetDir_getNextFileName(asset))) + m_items << fileName; + m_index = -1; + m_path = path; + } + + virtual QFileInfo currentFileInfo() const + { + return QFileInfo(currentFilePath()); + } + + virtual QString currentFileName() const + { + if (m_index < 0 || m_index >= m_items.size()) + return QString(); + return m_items[m_index]; + } + + virtual QString currentFilePath() const + { + return m_path + currentFileName(); + } + + virtual bool hasNext() const + { + return m_items.size() && (m_index < m_items.size() - 1); + } + + virtual QString next() + { + if (!hasNext()) + return QString(); + m_index++; + return currentFileName(); + } + +private: + QString m_path; + QStringList m_items; + int m_index; +}; + +class AndroidAbstractFileEngine: public QAbstractFileEngine +{ +public: + explicit AndroidAbstractFileEngine(AAsset *asset, const QString &fileName) + { + m_assetDir = 0; + m_assetFile = asset; + m_fileName = fileName; + } + + explicit AndroidAbstractFileEngine(AAssetDir *asset, const QString &fileName) + { + m_assetFile = 0; + m_assetDir = asset; + m_fileName = fileName; + if (!m_fileName.endsWith(QChar(QLatin1Char('/')))) + m_fileName += "/"; + } + + ~AndroidAbstractFileEngine() + { + close(); + if (m_assetDir) + AAssetDir_close(m_assetDir); + } + + virtual bool open(QIODevice::OpenMode openMode) + { + if (m_assetFile) + return openMode & QIODevice::ReadOnly; + return false; + } + + virtual bool close() + { + if (m_assetFile) { + AAsset_close(m_assetFile); + m_assetFile = 0; + return true; + } + return false; + } + + virtual qint64 size() const + { + if (m_assetFile) + return AAsset_getLength(m_assetFile); + return -1; + } + + virtual qint64 pos() const + { + if (m_assetFile) + return AAsset_seek(m_assetFile, 0, SEEK_CUR); + return -1; + } + + virtual bool seek(qint64 pos) + { + if (m_assetFile) + return pos == AAsset_seek(m_assetFile, pos, SEEK_SET); + return false; + } + + virtual qint64 read(char *data, qint64 maxlen) + { + if (m_assetFile) + return AAsset_read(m_assetFile, data, maxlen); + return -1; + } + + virtual bool isSequential() const + { + return false; + } + + virtual bool caseSensitive() const + { + return true; + } + + virtual bool isRelativePath() const + { + return false; + } + + virtual FileFlags fileFlags(FileFlags type = FileInfoAll) const + { + FileFlags flags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag); + if (m_assetFile) + flags |= FileType; + if (m_assetDir) + flags |= DirectoryType; + + return type & flags; + } + + virtual QString fileName(FileName file = DefaultName) const + { + int pos; + switch (file) { + case DefaultName: + case AbsoluteName: + case CanonicalName: + return m_fileName; + case BaseName: + if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1) + return m_fileName.mid(pos); + else + return m_fileName; + case PathName: + case AbsolutePathName: + case CanonicalPathName: + if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1) + return m_fileName.left(pos); + else + return m_fileName; + default: + return QString(); + } + } + + virtual void setFileName(const QString &file) + { + if (file == m_fileName) + return; + + m_fileName = file; + if (!m_fileName.endsWith(QChar(QLatin1Char('/')))) + m_fileName += "/"; + + close(); + } + + virtual Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) + { + if (m_assetDir) + return new AndroidAbstractFileEngineIterator(filters, filterNames, m_assetDir, m_fileName); + return 0; + } + +private: + AAsset *m_assetFile; + AAssetDir *m_assetDir; + QString m_fileName; +}; + + +AndroidAssetsFileEngineHandler::AndroidAssetsFileEngineHandler() +{ + m_assetManager = QtAndroid::assetManager(); +} + +AndroidAssetsFileEngineHandler::~AndroidAssetsFileEngineHandler() +{ +} + +QAbstractFileEngine * AndroidAssetsFileEngineHandler::create(const QString &fileName) const +{ + if (fileName.isEmpty()) + return 0; + + if (!fileName.startsWith(QLatin1String("assets:/"))) + return 0; + + int prefixSize=8; + + m_path.clear(); + if (!fileName.endsWith(QLatin1Char('/'))) { + m_path = fileName.toUtf8(); + AAsset *asset = AAssetManager_open(m_assetManager, + m_path.constData() + prefixSize, + AASSET_MODE_BUFFER); + if (asset) + return new AndroidAbstractFileEngine(asset, fileName); + } + + if (!m_path.size()) + m_path = fileName.left(fileName.length() - 1).toUtf8(); + + AAssetDir *assetDir = AAssetManager_openDir(m_assetManager, m_path.constData() + prefixSize); + if (assetDir) { + if (AAssetDir_getNextFileName(assetDir)) + return new AndroidAbstractFileEngine(assetDir, fileName); + else + AAssetDir_close(assetDir); + } + return 0; +} diff --git a/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.h b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.h new file mode 100644 index 0000000000..9bff6a012e --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDASSETSFILEENGINEHANDLER_H +#define QANDROIDASSETSFILEENGINEHANDLER_H + +#include +#include + +class AndroidAssetsFileEngineHandler: public QAbstractFileEngineHandler +{ +public: + AndroidAssetsFileEngineHandler(); + virtual ~AndroidAssetsFileEngineHandler(); + QAbstractFileEngine *create(const QString &fileName) const; + +private: + AAssetManager *m_assetManager; + mutable QByteArray m_path; +}; + +#endif // QANDROIDASSETSFILEENGINEHANDLER_H diff --git a/src/plugins/platforms/android/src/qandroidinputcontext.cpp b/src/plugins/platforms/android/src/qandroidinputcontext.cpp new file mode 100644 index 0000000000..37fb605ea8 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidinputcontext.cpp @@ -0,0 +1,644 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qandroidinputcontext.h" +#include "androidjnimain.h" +#include "androidjniinput.h" +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +static QAndroidInputContext *m_androidInputContext = 0; +static char const *const QtNativeInputConnectionClassName = "org/qtproject/qt5/android/QtNativeInputConnection"; +static char const *const QtExtractedTextClassName = "org/qtproject/qt5/android/QtExtractedText"; +static jclass m_extractedTextClass = 0; +static jmethodID m_classConstructorMethodID = 0; +static jfieldID m_partialEndOffsetFieldID = 0; +static jfieldID m_partialStartOffsetFieldID = 0; +static jfieldID m_selectionEndFieldID = 0; +static jfieldID m_selectionStartFieldID = 0; +static jfieldID m_startOffsetFieldID = 0; +static jfieldID m_textFieldID = 0; + +static jboolean commitText(JNIEnv *env, jobject /*thiz*/, jstring text, jint newCursorPosition) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + jboolean isCopy; + const jchar *jstr = env->GetStringChars(text, &isCopy); + QString str(reinterpret_cast(jstr), env->GetStringLength(text)); + env->ReleaseStringChars(text, jstr); + + return m_androidInputContext->commitText(str, newCursorPosition); +} + +static jboolean deleteSurroundingText(JNIEnv */*env*/, jobject /*thiz*/, jint leftLength, jint rightLength) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->deleteSurroundingText(leftLength, rightLength); +} + +static jboolean finishComposingText(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->finishComposingText(); +} + +static jint getCursorCapsMode(JNIEnv */*env*/, jobject /*thiz*/, jint reqModes) +{ + if (!m_androidInputContext) + return 0; + + return m_androidInputContext->getCursorCapsMode(reqModes); +} + +static jobject getExtractedText(JNIEnv *env, jobject /*thiz*/, int hintMaxChars, int hintMaxLines, jint flags) +{ + if (!m_androidInputContext) + return 0; + + const QAndroidInputContext::ExtractedText &extractedText = + m_androidInputContext->getExtractedText(hintMaxChars, hintMaxLines, flags); + + jobject object = env->NewObject(m_extractedTextClass, m_classConstructorMethodID); + env->SetIntField(object, m_partialStartOffsetFieldID, extractedText.partialStartOffset); + env->SetIntField(object, m_partialEndOffsetFieldID, extractedText.partialEndOffset); + env->SetIntField(object, m_selectionStartFieldID, extractedText.selectionStart); + env->SetIntField(object, m_selectionEndFieldID, extractedText.selectionEnd); + env->SetIntField(object, m_startOffsetFieldID, extractedText.startOffset); + env->SetObjectField(object, + m_textFieldID, + env->NewString(reinterpret_cast(extractedText.text.constData()), + jsize(extractedText.text.length()))); + + return object; +} + +static jstring getSelectedText(JNIEnv *env, jobject /*thiz*/, jint flags) +{ + if (!m_androidInputContext) + return 0; + + const QString &text = m_androidInputContext->getSelectedText(flags); + return env->NewString(reinterpret_cast(text.constData()), jsize(text.length())); +} + +static jstring getTextAfterCursor(JNIEnv *env, jobject /*thiz*/, jint length, jint flags) +{ + if (!m_androidInputContext) + return 0; + + const QString &text = m_androidInputContext->getTextAfterCursor(length, flags); + return env->NewString(reinterpret_cast(text.constData()), jsize(text.length())); +} + +static jstring getTextBeforeCursor(JNIEnv *env, jobject /*thiz*/, jint length, jint flags) +{ + if (!m_androidInputContext) + return 0; + + const QString &text = m_androidInputContext->getTextBeforeCursor(length, flags); + return env->NewString(reinterpret_cast(text.constData()), jsize(text.length())); +} + +static jboolean setComposingText(JNIEnv *env, jobject /*thiz*/, jstring text, jint newCursorPosition) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + jboolean isCopy; + const jchar *jstr = env->GetStringChars(text, &isCopy); + QString str(reinterpret_cast(jstr), env->GetStringLength(text)); + env->ReleaseStringChars(text, jstr); + + return m_androidInputContext->setComposingText(str, newCursorPosition); +} + +static jboolean setSelection(JNIEnv */*env*/, jobject /*thiz*/, jint start, jint end) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->setSelection(start, end); +} + +static jboolean selectAll(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->selectAll(); +} + +static jboolean cut(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->cut(); +} + +static jboolean copy(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->copy(); +} + +static jboolean copyURL(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->copyURL(); +} + +static jboolean paste(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->paste(); +} + + +static JNINativeMethod methods[] = { + {"commitText", "(Ljava/lang/String;I)Z", (void *)commitText}, + {"deleteSurroundingText", "(II)Z", (void *)deleteSurroundingText}, + {"finishComposingText", "()Z", (void *)finishComposingText}, + {"getCursorCapsMode", "(I)I", (void *)getCursorCapsMode}, + {"getExtractedText", "(III)Lorg/qtproject/qt5/android/QtExtractedText;", (void *)getExtractedText}, + {"getSelectedText", "(I)Ljava/lang/String;", (void *)getSelectedText}, + {"getTextAfterCursor", "(II)Ljava/lang/String;", (void *)getTextAfterCursor}, + {"getTextBeforeCursor", "(II)Ljava/lang/String;", (void *)getTextBeforeCursor}, + {"setComposingText", "(Ljava/lang/String;I)Z", (void *)setComposingText}, + {"setSelection", "(II)Z", (void *)setSelection}, + {"selectAll", "()Z", (void *)selectAll}, + {"cut", "()Z", (void *)cut}, + {"copy", "()Z", (void *)copy}, + {"copyURL", "()Z", (void *)copyURL}, + {"paste", "()Z", (void *)paste} +}; + + +QAndroidInputContext::QAndroidInputContext():QPlatformInputContext() +{ + JNIEnv *env = 0; + if (QtAndroid::javaVM()->AttachCurrentThread(&env, NULL) < 0) { + qCritical() << "AttachCurrentThread failed"; + return; + } + + jclass clazz = QtAndroid::findClass(QtNativeInputConnectionClassName, env); + if (clazz == NULL) { + qCritical() << "Native registration unable to find class '" + << QtNativeInputConnectionClassName + << "'"; + return; + } + + if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + qCritical() << "RegisterNatives failed for '" + << QtNativeInputConnectionClassName + << "'"; + return; + } + + clazz = QtAndroid::findClass(QtExtractedTextClassName, env); + if (clazz == NULL) { + qCritical() << "Native registration unable to find class '" + << QtExtractedTextClassName + << "'"; + return; + } + + m_extractedTextClass = static_cast(env->NewGlobalRef(clazz)); + m_classConstructorMethodID = env->GetMethodID(m_extractedTextClass, "", "()V"); + if (m_classConstructorMethodID == NULL) { + qCritical() << "GetMethodID failed"; + return; + } + + m_partialEndOffsetFieldID = env->GetFieldID(m_extractedTextClass, "partialEndOffset", "I"); + if (m_partialEndOffsetFieldID == NULL) { + qCritical() << "Can't find field partialEndOffset"; + return; + } + + m_partialStartOffsetFieldID = env->GetFieldID(m_extractedTextClass, "partialStartOffset", "I"); + if (m_partialStartOffsetFieldID == NULL) { + qCritical() << "Can't find field partialStartOffset"; + return; + } + + m_selectionEndFieldID = env->GetFieldID(m_extractedTextClass, "selectionEnd", "I"); + if (m_selectionEndFieldID == NULL) { + qCritical() << "Can't find field selectionEnd"; + return; + } + + m_selectionStartFieldID = env->GetFieldID(m_extractedTextClass, "selectionStart", "I"); + if (m_selectionStartFieldID == NULL) { + qCritical() << "Can't find field selectionStart"; + return; + } + + m_startOffsetFieldID = env->GetFieldID(m_extractedTextClass, "startOffset", "I"); + if (m_startOffsetFieldID == NULL) { + qCritical() << "Can't find field startOffset"; + return; + } + + m_textFieldID = env->GetFieldID(m_extractedTextClass, "text", "Ljava/lang/String;"); + if (m_textFieldID == NULL) { + qCritical() << "Can't find field text"; + return; + } + qRegisterMetaType("QInputMethodEvent*"); + qRegisterMetaType("QInputMethodQueryEvent*"); + m_androidInputContext = this; +} + +QAndroidInputContext::~QAndroidInputContext() +{ + m_androidInputContext = 0; + m_extractedTextClass = 0; + m_partialEndOffsetFieldID = 0; + m_partialStartOffsetFieldID = 0; + m_selectionEndFieldID = 0; + m_selectionStartFieldID = 0; + m_startOffsetFieldID = 0; + m_textFieldID = 0; +} + +void QAndroidInputContext::reset() +{ + clear(); + if (qGuiApp->focusObject()) + QtAndroidInput::resetSoftwareKeyboard(); + else + QtAndroidInput::hideSoftwareKeyboard(); +} + +void QAndroidInputContext::commit() +{ + finishComposingText(); + + QSharedPointer query = focusObjectInputMethodQuery(); + if (!query.isNull()) { + const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + QtAndroidInput::updateSelection(cursorPos, cursorPos, -1, -1); //selection empty and no pre-edit text + } +} + +void QAndroidInputContext::update(Qt::InputMethodQueries queries) +{ + QSharedPointer query = focusObjectInputMethodQuery(queries); + if (query.isNull()) + return; +#warning TODO extract the needed data from query +} + +void QAndroidInputContext::invokeAction(QInputMethod::Action action, int cursorPosition) +{ +#warning TODO Handle at least QInputMethod::ContextMenu action + Q_UNUSED(action) + Q_UNUSED(cursorPosition) + + if (action == QInputMethod::Click) + commit(); +} + +QRectF QAndroidInputContext::keyboardRect() const +{ + return QPlatformInputContext::keyboardRect(); +} + +bool QAndroidInputContext::isAnimating() const +{ + return false; +} + +void QAndroidInputContext::showInputPanel() +{ + QSharedPointer query = focusObjectInputMethodQuery(); + if (query.isNull()) + return; + QRectF itemRect = qGuiApp->inputMethod()->inputItemRectangle(); + QRect rect = qGuiApp->inputMethod()->inputItemTransform().mapRect(itemRect).toRect(); + QWindow *window = qGuiApp->focusWindow(); + if (window) + rect = QRect(window->mapToGlobal(rect.topLeft()), rect.size()); + + QtAndroidInput::showSoftwareKeyboard(rect.left(), + rect.top(), + rect.width(), + rect.height(), + query->value(Qt::ImHints).toUInt()); +} + +void QAndroidInputContext::hideInputPanel() +{ + QtAndroidInput::hideSoftwareKeyboard(); +} + +bool QAndroidInputContext::isInputPanelVisible() const +{ + return QtAndroidInput::isSoftwareKeyboardVisible(); +} + +bool QAndroidInputContext::isComposing() const +{ + return m_composingText.length(); +} + +void QAndroidInputContext::clear() +{ + m_composingText.clear(); + m_extractedText.clear(); +} + +void QAndroidInputContext::sendEvent(QObject *receiver, QInputMethodEvent *event) +{ + QCoreApplication::sendEvent(receiver, event); +} + +void QAndroidInputContext::sendEvent(QObject *receiver, QInputMethodQueryEvent *event) +{ + QCoreApplication::sendEvent(receiver, event); +} + +jboolean QAndroidInputContext::commitText(const QString &text, jint /*newCursorPosition*/) +{ + m_composingText = text; + return finishComposingText(); +} + +jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint rightLength) +{ + QSharedPointer query = focusObjectInputMethodQuery(); + if (query.isNull()) + return JNI_TRUE; + + m_composingText.clear(); + + QInputMethodEvent event; + event.setCommitString(QString(), -leftLength, leftLength+rightLength); + sendInputMethodEvent(&event); + clear(); + + return JNI_TRUE; +} + +jboolean QAndroidInputContext::finishComposingText() +{ + QInputMethodEvent event; + event.setCommitString(m_composingText); + sendInputMethodEvent(&event); + clear(); + + return JNI_TRUE; +} + +jint QAndroidInputContext::getCursorCapsMode(jint /*reqModes*/) +{ + jint res = 0; + QSharedPointer query = focusObjectInputMethodQuery(); + if (query.isNull()) + return res; + + const uint qtInputMethodHints = query->value(Qt::ImHints).toUInt(); + + if (qtInputMethodHints & Qt::ImhPreferUppercase) + res = CAP_MODE_SENTENCES; + + if (qtInputMethodHints & Qt::ImhUppercaseOnly) + res = CAP_MODE_CHARACTERS; + + return res; +} + +const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedText(jint hintMaxChars, jint /*hintMaxLines*/, jint /*flags*/) +{ + QSharedPointer query = focusObjectInputMethodQuery(); + if (query.isNull()) + return m_extractedText; + + if (hintMaxChars) + m_extractedText.text = query->value(Qt::ImSurroundingText).toString().right(hintMaxChars); + + m_extractedText.startOffset = query->value(Qt::ImCursorPosition).toInt(); + const QString &selection = query->value(Qt::ImCurrentSelection).toString(); + const int selLen = selection.length(); + if (selLen) { + m_extractedText.selectionStart = query->value(Qt::ImAnchorPosition).toInt(); + m_extractedText.selectionEnd = m_extractedText.startOffset; + } + + return m_extractedText; +} + +QString QAndroidInputContext::getSelectedText(jint /*flags*/) +{ + QSharedPointer query = focusObjectInputMethodQuery(); + if (query.isNull()) + return QString(); + + return query->value(Qt::ImCurrentSelection).toString(); +} + +QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/) +{ + QSharedPointer query = focusObjectInputMethodQuery(); + if (query.isNull()) + return QString(); + + QString text = query->value(Qt::ImSurroundingText).toString(); + if (!text.length()) + return text; + + int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + return text.mid(cursorPos, length); +} + +QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) +{ + QSharedPointer query = focusObjectInputMethodQuery(); + if (query.isNull()) + return QString(); + + QString text = query->value(Qt::ImSurroundingText).toString(); + if (!text.length()) + return text; + + int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + const int wordLeftPos = cursorPos - length; + return text.mid(wordLeftPos > 0 ? wordLeftPos : 0, cursorPos); +} + +jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCursorPosition) +{ + if (newCursorPosition > 0) + newCursorPosition += text.length() - 1; + m_composingText = text; + QList attributes; + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, + newCursorPosition, + 1, + QVariant())); + // Show compose text underlined + QTextCharFormat underlined; + underlined.setFontUnderline(true); + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,0, text.length(), + QVariant(underlined))); + + QInputMethodEvent event(m_composingText, attributes); + sendInputMethodEvent(&event); + + QSharedPointer query = focusObjectInputMethodQuery(); + if (!query.isNull()) { + int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + int preeditLength = text.length(); + QtAndroidInput::updateSelection(cursorPos+preeditLength, cursorPos+preeditLength, cursorPos, cursorPos+preeditLength); + } + + return JNI_TRUE; +} + +jboolean QAndroidInputContext::setSelection(jint start, jint end) +{ + QList attributes; + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, + start, + end - start, + QVariant())); + + QInputMethodEvent event(QString(), attributes); + sendInputMethodEvent(&event); + return JNI_TRUE; +} + +jboolean QAndroidInputContext::selectAll() +{ +#warning TODO + return JNI_FALSE; +} + +jboolean QAndroidInputContext::cut() +{ +#warning TODO + return JNI_FALSE; +} + +jboolean QAndroidInputContext::copy() +{ +#warning TODO + return JNI_FALSE; +} + +jboolean QAndroidInputContext::copyURL() +{ +#warning TODO + return JNI_FALSE; +} + +jboolean QAndroidInputContext::paste() +{ +#warning TODO + return JNI_FALSE; +} + +QSharedPointer QAndroidInputContext::focusObjectInputMethodQuery(Qt::InputMethodQueries queries) +{ +#warning TODO make qGuiApp->focusObject() thread safe !!! + QObject *focusObject = qGuiApp->focusObject(); + if (!focusObject) + return QSharedPointer(); + + QSharedPointer ret = QSharedPointer(new QInputMethodQueryEvent(queries)); + if (qGuiApp->thread()==QThread::currentThread()) { + QCoreApplication::sendEvent(focusObject, ret.data()); + } else { + QMetaObject::invokeMethod(this, + "sendEvent", + Qt::BlockingQueuedConnection, + Q_ARG(QObject*, focusObject), + Q_ARG(QInputMethodQueryEvent*, ret.data())); + } + + return ret; +} + +void QAndroidInputContext::sendInputMethodEvent(QInputMethodEvent *event) +{ +#warning TODO make qGuiApp->focusObject() thread safe !!! + QObject *focusObject = qGuiApp->focusObject(); + if (!focusObject) + return; + + if (qGuiApp->thread() == QThread::currentThread()) { + QCoreApplication::sendEvent(focusObject, event); + } else { + QMetaObject::invokeMethod(this, + "sendEvent", + Qt::BlockingQueuedConnection, + Q_ARG(QObject*, focusObject), + Q_ARG(QInputMethodEvent*, event)); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/src/qandroidinputcontext.h b/src/plugins/platforms/android/src/qandroidinputcontext.h new file mode 100644 index 0000000000..e2b8107044 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidinputcontext.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROIDINPUTCONTEXT_H +#define ANDROIDINPUTCONTEXT_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QAndroidInputContext: public QPlatformInputContext +{ + Q_OBJECT + enum CapsMode + { + CAP_MODE_CHARACTERS = 0x00001000, + CAP_MODE_SENTENCES = 0x00004000, + CAP_MODE_WORDS = 0x00002000 + }; + +public: + struct ExtractedText + { + ExtractedText() { clear(); } + + void clear() + { + partialEndOffset = partialStartOffset = selectionEnd = selectionStart = startOffset = -1; + text.clear(); + } + + int partialEndOffset; + int partialStartOffset; + int selectionEnd; + int selectionStart; + int startOffset; + QString text; + }; + +public: + QAndroidInputContext(); + ~QAndroidInputContext(); + bool isValid() const { return true; } + + void reset(); + void commit(); + void update(Qt::InputMethodQueries queries); + void invokeAction(QInputMethod::Action action, int cursorPosition); + QRectF keyboardRect() const; + bool isAnimating() const; + void showInputPanel(); + void hideInputPanel(); + bool isInputPanelVisible() const; + + bool isComposing() const; + void clear(); + + //---------------// + jboolean commitText(const QString &text, jint newCursorPosition); + jboolean deleteSurroundingText(jint leftLength, jint rightLength); + jboolean finishComposingText(); + jint getCursorCapsMode(jint reqModes); + const ExtractedText &getExtractedText(jint hintMaxChars, jint hintMaxLines, jint flags); + QString getSelectedText(jint flags); + QString getTextAfterCursor(jint length, jint flags); + QString getTextBeforeCursor(jint length, jint flags); + jboolean setComposingText(const QString &text, jint newCursorPosition); + jboolean setSelection(jint start, jint end); + jboolean selectAll(); + jboolean cut(); + jboolean copy(); + jboolean copyURL(); + jboolean paste(); + +private: + QSharedPointer focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll); + void sendInputMethodEvent(QInputMethodEvent *event); + +private slots: + virtual void sendEvent(QObject *receiver, QInputMethodEvent *event); + virtual void sendEvent(QObject *receiver, QInputMethodQueryEvent *event); + +private: + ExtractedText m_extractedText; + QString m_composingText; +}; + +QT_END_NAMESPACE + +#endif // ANDROIDINPUTCONTEXT_H diff --git a/src/plugins/platforms/android/src/qandroidplatformclipboard.cpp b/src/plugins/platforms/android/src/qandroidplatformclipboard.cpp new file mode 100644 index 0000000000..bc48b4935b --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformclipboard.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidplatformclipboard.h" +#include "androidjniclipboard.h" +#ifndef QT_NO_CLIPBOARD +#include + +QT_BEGIN_NAMESPACE + +QAndroidPlatformClipboard::QAndroidPlatformClipboard() +{ + QtAndroidClipboard::setClipboardListener(this); +} + +QMimeData *QAndroidPlatformClipboard::mimeData(QClipboard::Mode mode) +{ + if (QClipboard::Clipboard != mode || !QtAndroidClipboard::hasClipboardText()) + return 0; + + QMimeData *mimeData = new QMimeData(); + mimeData->setText(QtAndroidClipboard::clipboardText()); + return mimeData; +} + +void QAndroidPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) +{ + if (!data || !data->hasText() || QClipboard::Clipboard != mode) + return; + + QtAndroidClipboard::setClipboardText(data->text()); +} + +bool QAndroidPlatformClipboard::supportsMode(QClipboard::Mode mode) const +{ + return QClipboard::Clipboard == mode; +} + +QT_END_NAMESPACE + +#endif // QT_NO_CLIPBOARD diff --git a/src/plugins/platforms/android/src/qandroidplatformclipboard.h b/src/plugins/platforms/android/src/qandroidplatformclipboard.h new file mode 100644 index 0000000000..644f326934 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformclipboard.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDPLATFORMCLIPBOARD_H +#define QANDROIDPLATFORMCLIPBOARD_H + +#include + +#ifndef QT_NO_CLIPBOARD +QT_BEGIN_NAMESPACE + +class QAndroidPlatformClipboard: public QPlatformClipboard +{ +public: + QAndroidPlatformClipboard(); + + virtual QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard); + virtual void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard); + virtual bool supportsMode(QClipboard::Mode mode) const; +}; + +QT_END_NAMESPACE +#endif // QT_NO_CLIPBOARD + +#endif // QANDROIDPLATFORMCLIPBOARD_H diff --git a/src/plugins/platforms/android/src/qandroidplatformfontdatabase.cpp b/src/plugins/platforms/android/src/qandroidplatformfontdatabase.cpp new file mode 100644 index 0000000000..7f68b44ed8 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformfontdatabase.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qandroidplatformfontdatabase.h" + +QString QAndroidPlatformFontDatabase::fontDir() const +{ + return QLatin1String("/system/fonts"); +} + +void QAndroidPlatformFontDatabase::populateFontDatabase() +{ + QString fontpath = fontDir(); + + if (!QFile::exists(fontpath)) { + qFatal("QFontDatabase: Cannot find font directory %s - is Qt installed correctly?", + qPrintable(fontpath)); + } + + QDir dir(fontpath, QLatin1String("*.ttf")); + for (int i = 0; i < int(dir.count()); ++i) { + const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); + addTTFile(QByteArray(), file); + } +} + +QStringList QAndroidPlatformFontDatabase::fallbacksForFamily(const QString &family, + QFont::Style style, + QFont::StyleHint styleHint, + QChar::Script script) const +{ + Q_UNUSED(family); + Q_UNUSED(style); + Q_UNUSED(script); + if (styleHint == QFont::Monospace) + return QString(qgetenv("QT_ANDROID_FONTS_MONOSPACE")).split(";"); + + return QString(qgetenv("QT_ANDROID_FONTS")).split(";"); +} diff --git a/src/plugins/platforms/android/src/qandroidplatformfontdatabase.h b/src/plugins/platforms/android/src/qandroidplatformfontdatabase.h new file mode 100644 index 0000000000..3cbfe95d36 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformfontdatabase.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDPLATFORMFONTDATABASE_H +#define QANDROIDPLATFORMFONTDATABASE_H + +#include + +class QAndroidPlatformFontDatabase: public QBasicFontDatabase +{ +public: + QString fontDir() const; + void populateFontDatabase(); + QStringList fallbacksForFamily(const QString &family, + QFont::Style style, + QFont::StyleHint styleHint, + QChar::Script script) const; +}; + +#endif // QANDROIDPLATFORMFONTDATABASE_H diff --git a/src/plugins/platforms/android/src/qandroidplatformintegration.cpp b/src/plugins/platforms/android/src/qandroidplatformintegration.cpp new file mode 100644 index 0000000000..1091416ccc --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformintegration.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidplatformintegration.h" +#include "qabstracteventdispatcher.h" +#include "androidjnimain.h" +#include +#include +#include +#include +#include "qandroidplatformservices.h" +#include "qandroidplatformfontdatabase.h" +#include "qandroidplatformclipboard.h" +#include + +#ifndef ANDROID_PLUGIN_OPENGL +# include "qandroidplatformscreen.h" +# include "qandroidplatformwindow.h" +# include +#else +# include "qeglfswindow.h" +# include "androidjnimenu.h" +# include "qandroidopenglcontext.h" +# include "qandroidopenglplatformwindow.h" +# include "qeglfshooks.h" +# include +#endif + +#include "qandroidplatformtheme.h" + +QT_BEGIN_NAMESPACE + +int QAndroidPlatformIntegration::m_defaultGeometryWidth = 320; +int QAndroidPlatformIntegration::m_defaultGeometryHeight = 455; +int QAndroidPlatformIntegration::m_defaultPhysicalSizeWidth = 50; +int QAndroidPlatformIntegration::m_defaultPhysicalSizeHeight = 71; + +void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteArray &resource) +{ + if (resource=="JavaVM") + return QtAndroid::javaVM(); + if (resource == "QtActivity") + return QtAndroid::activity(); + + return 0; +} + +QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶mList) +#ifdef ANDROID_PLUGIN_OPENGL + : m_primaryWindow(0) +#endif +{ + Q_UNUSED(paramList); + +#ifndef ANDROID_PLUGIN_OPENGL + m_eventDispatcher = createUnixEventDispatcher(); +#endif + + m_androidPlatformNativeInterface = new QAndroidPlatformNativeInterface(); + +#ifndef ANDROID_PLUGIN_OPENGL + m_primaryScreen = new QAndroidPlatformScreen(); + screenAdded(m_primaryScreen); + m_primaryScreen->setPhysicalSize(QSize(m_defaultPhysicalSizeWidth, m_defaultPhysicalSizeHeight)); + m_primaryScreen->setGeometry(QRect(0, 0, m_defaultGeometryWidth, m_defaultGeometryHeight)); +#endif + + m_mainThread = QThread::currentThread(); + QtAndroid::setAndroidPlatformIntegration(this); + + m_androidFDB = new QAndroidPlatformFontDatabase(); + m_androidPlatformServices = new QAndroidPlatformServices(); + m_androidPlatformClipboard = new QAndroidPlatformClipboard(); +} + +bool QAndroidPlatformIntegration::hasCapability(Capability cap) const +{ + switch (cap) { + case ThreadedPixmaps: return true; + default: +#ifndef ANDROID_PLUGIN_OPENGL + return QPlatformIntegration::hasCapability(cap); +#else + return QEglFSIntegration::hasCapability(cap); +#endif + } +} + +#ifndef ANDROID_PLUGIN_OPENGL +QPlatformBackingStore *QAndroidPlatformIntegration::createPlatformBackingStore(QWindow *window) const +{ + return new QFbBackingStore(window); +} + +QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *window) const +{ + return new QAndroidPlatformWindow(window); +} + +QAbstractEventDispatcher *QAndroidPlatformIntegration::guiThreadEventDispatcher() const +{ + return m_eventDispatcher; +} +#else // !ANDROID_PLUGIN_OPENGL +QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *window) const +{ + if (m_primaryWindow != 0) { + qWarning("QAndroidPlatformIntegration::createPlatformWindow: Unsupported case: More than " + "one top-level window created."); + } + + m_primaryWindow = new QAndroidOpenGLPlatformWindow(window); + m_primaryWindow->requestActivateWindow(); + QtAndroidMenu::setActiveTopLevelWindow(window); + + return m_primaryWindow; +} + +void QAndroidPlatformIntegration::invalidateNativeSurface() +{ + if (m_primaryWindow != 0) + m_primaryWindow->invalidateSurface(); +} + +void QAndroidPlatformIntegration::surfaceChanged() +{ + if (m_primaryWindow != 0) + m_primaryWindow->resetSurface(); +} + +QPlatformOpenGLContext *QAndroidPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const +{ + return new QAndroidOpenGLContext(this, + QEglFSHooks::hooks()->surfaceFormatFor(context->format()), + context->shareHandle(), + display()); +} +#endif // ANDROID_PLUGIN_OPENGL + +QAndroidPlatformIntegration::~QAndroidPlatformIntegration() +{ + delete m_androidPlatformNativeInterface; + delete m_androidFDB; + QtAndroid::setAndroidPlatformIntegration(NULL); +} +QPlatformFontDatabase *QAndroidPlatformIntegration::fontDatabase() const +{ + return m_androidFDB; +} + +#ifndef QT_NO_CLIPBOARD +QPlatformClipboard *QAndroidPlatformIntegration::clipboard() const +{ +static QAndroidPlatformClipboard *clipboard = 0; + if (!clipboard) + clipboard = new QAndroidPlatformClipboard; + + return clipboard; +} +#endif + +QPlatformInputContext *QAndroidPlatformIntegration::inputContext() const +{ + return &m_platformInputContext; +} + +QPlatformNativeInterface *QAndroidPlatformIntegration::nativeInterface() const +{ + return m_androidPlatformNativeInterface; +} + +QPlatformServices *QAndroidPlatformIntegration::services() const +{ + return m_androidPlatformServices; +} + +static const QLatin1String androidThemeName("android"); +QStringList QAndroidPlatformIntegration::themeNames() const +{ + return QStringList(QString(androidThemeName)); +} + +QPlatformTheme *QAndroidPlatformIntegration::createPlatformTheme(const QString &name) const +{ + if (androidThemeName == name) + return new QAndroidPlatformTheme; + + return 0; +} + +void QAndroidPlatformIntegration::setDefaultDisplayMetrics(int gw, int gh, int sw, int sh) +{ + m_defaultGeometryWidth = gw; + m_defaultGeometryHeight = gh; + m_defaultPhysicalSizeWidth = sw; + m_defaultPhysicalSizeHeight = sh; +} + +void QAndroidPlatformIntegration::setDefaultDesktopSize(int gw, int gh) +{ + m_defaultGeometryWidth = gw; + m_defaultGeometryHeight = gh; +} + + +#ifndef ANDROID_PLUGIN_OPENGL +void QAndroidPlatformIntegration::setDesktopSize(int width, int height) +{ + if (m_primaryScreen) + QMetaObject::invokeMethod(m_primaryScreen, "setGeometry", Qt::AutoConnection, Q_ARG(QRect, QRect(0,0,width, height))); +} + +void QAndroidPlatformIntegration::setDisplayMetrics(int width, int height) +{ + if (m_primaryScreen) + QMetaObject::invokeMethod(m_primaryScreen, "setPhysicalSize", Qt::AutoConnection, Q_ARG(QSize, QSize(width, height))); +} +#else +void QAndroidPlatformIntegration::setDesktopSize(int width, int height) +{ + m_defaultGeometryWidth = width; + m_defaultGeometryHeight = height; +} + +void QAndroidPlatformIntegration::setDisplayMetrics(int width, int height) +{ + m_defaultPhysicalSizeWidth = width; + m_defaultPhysicalSizeHeight = height; +} + +#endif + +void QAndroidPlatformIntegration::pauseApp() +{ + if (QAbstractEventDispatcher::instance(m_mainThread)) + QAbstractEventDispatcher::instance(m_mainThread)->interrupt(); +} + +void QAndroidPlatformIntegration::resumeApp() +{ + if (QAbstractEventDispatcher::instance(m_mainThread)) + QAbstractEventDispatcher::instance(m_mainThread)->wakeUp(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/src/qandroidplatformintegration.h b/src/plugins/platforms/android/src/qandroidplatformintegration.h new file mode 100644 index 0000000000..7dde277d25 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformintegration.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDPLATFORMINTERATION_H +#define QANDROIDPLATFORMINTERATION_H + +#include +#include +#include +#include + +#include +#include "qandroidinputcontext.h" + +#ifndef ANDROID_PLUGIN_OPENGL +# include "qandroidplatformscreen.h" +#else +# include "qeglfsintegration.h" +#endif + +QT_BEGIN_NAMESPACE + +class QDesktopWidget; +class QAndroidPlatformServices; + +#ifdef ANDROID_PLUGIN_OPENGL +class QAndroidOpenGLPlatformWindow; +#endif + +class QAndroidPlatformNativeInterface: public QPlatformNativeInterface +{ +public: + void *nativeResourceForIntegration(const QByteArray &resource); +}; + +class QAndroidPlatformIntegration +#ifndef ANDROID_PLUGIN_OPENGL + : public QPlatformIntegration +#else + : public QEglFSIntegration +#endif +{ + friend class QAndroidPlatformScreen; + +public: + QAndroidPlatformIntegration(const QStringList ¶mList); + ~QAndroidPlatformIntegration(); + + bool hasCapability(QPlatformIntegration::Capability cap) const; + +#ifndef ANDROID_PLUGIN_OPENGL + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + QPlatformWindow *createPlatformWindow(QWindow *window) const; + QAbstractEventDispatcher *guiThreadEventDispatcher() const; + QAndroidPlatformScreen *screen() { return m_primaryScreen; } +#else + QPlatformWindow *createPlatformWindow(QWindow *window) const; + void invalidateNativeSurface(); + void surfaceChanged(); + QAndroidOpenGLPlatformWindow *primaryWindow() const { return m_primaryWindow; } + QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; +#endif + + virtual void setDesktopSize(int width, int height); + virtual void setDisplayMetrics(int width, int height); + bool isVirtualDesktop() { return true; } + + QPlatformFontDatabase *fontDatabase() const; + +#ifndef QT_NO_CLIPBOARD + QPlatformClipboard *clipboard() const; +#endif + + QPlatformInputContext *inputContext() const; + QPlatformNativeInterface *nativeInterface() const; + QPlatformServices *services() const; + + QStringList themeNames() const; + QPlatformTheme *createPlatformTheme(const QString &name) const; + + void pauseApp(); + void resumeApp(); + static void setDefaultDisplayMetrics(int gw, int gh, int sw, int sh); + static void setDefaultDesktopSize(int gw, int gh); + + static QSize defaultDesktopSize() + { + return QSize(m_defaultGeometryWidth, m_defaultGeometryHeight); + } + +private: + + friend class QEglFSAndroidHooks; +#ifndef ANDROID_PLUGIN_OPENGL + QAbstractEventDispatcher *m_eventDispatcher; + QAndroidPlatformScreen *m_primaryScreen; +#else + mutable QAndroidOpenGLPlatformWindow *m_primaryWindow; +#endif + + QThread *m_mainThread; + + static int m_defaultGeometryWidth; + static int m_defaultGeometryHeight; + static int m_defaultPhysicalSizeWidth; + static int m_defaultPhysicalSizeHeight; + + QPlatformFontDatabase *m_androidFDB; + QImage *m_FbScreenImage; + QPainter *m_compositePainter; + QAndroidPlatformNativeInterface *m_androidPlatformNativeInterface; + QAndroidPlatformServices *m_androidPlatformServices; + QPlatformClipboard *m_androidPlatformClipboard; + + mutable QAndroidInputContext m_platformInputContext; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/android/src/qandroidplatformmenu.cpp b/src/plugins/platforms/android/src/qandroidplatformmenu.cpp new file mode 100644 index 0000000000..36247e86f9 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenu.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidplatformmenu.h" +#include "qandroidplatformmenuitem.h" +#include "androidjnimenu.h" + +QAndroidPlatformMenu::QAndroidPlatformMenu() +{ + m_tag = reinterpret_cast(this); // QMenu will overwrite this later, but we need a unique ID for QtQuick + m_enabled = true; + m_isVisible = true; +} + +QAndroidPlatformMenu::~QAndroidPlatformMenu() +{ + QtAndroidMenu::androidPlatformMenuDestroyed(this); +} + +void QAndroidPlatformMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) +{ + QMutexLocker lock(&m_menuItemsMutex); + m_menuItems.insert(qFind(m_menuItems.begin(), + m_menuItems.end(), + static_cast(before)), + static_cast(menuItem)); +} + +void QAndroidPlatformMenu::removeMenuItem(QPlatformMenuItem *menuItem) +{ + QMutexLocker lock(&m_menuItemsMutex); + m_menuItems.erase(qFind(m_menuItems.begin(), + m_menuItems.end(), + static_cast(menuItem))); +} + +void QAndroidPlatformMenu::syncMenuItem(QPlatformMenuItem *menuItem) +{ + PlatformMenuItemsType::iterator it; + for (it = m_menuItems.begin(); it != m_menuItems.end(); ++it) { + if ((*it)->tag() == menuItem->tag()) + break; + } + + if (it != m_menuItems.end()) + QtAndroidMenu::syncMenu(this); +} + +void QAndroidPlatformMenu::syncSeparatorsCollapsible(bool enable) +{ + Q_UNUSED(enable) +} + +void QAndroidPlatformMenu::setTag(quintptr tag) +{ + m_tag = tag; +} + +quintptr QAndroidPlatformMenu::tag() const +{ + return m_tag; +} + +void QAndroidPlatformMenu::setText(const QString &text) +{ + m_text = text; +} + +QString QAndroidPlatformMenu::text() const +{ + return m_text; +} + +void QAndroidPlatformMenu::setIcon(const QIcon &icon) +{ + m_icon = icon; +} + +QIcon QAndroidPlatformMenu::icon() const +{ + return m_icon; +} + +void QAndroidPlatformMenu::setEnabled(bool enabled) +{ + m_enabled = enabled; +} + +bool QAndroidPlatformMenu::isEnabled() const +{ + return m_enabled; +} + +void QAndroidPlatformMenu::setVisible(bool visible) +{ + m_isVisible = visible; +} + +bool QAndroidPlatformMenu::isVisible() const +{ + return m_isVisible; +} + +QPlatformMenuItem *QAndroidPlatformMenu::menuItemAt(int position) const +{ + if (position < m_menuItems.size()) + return m_menuItems[position]; + return 0; +} + +QPlatformMenuItem *QAndroidPlatformMenu::menuItemForTag(quintptr tag) const +{ + foreach (QPlatformMenuItem *menuItem, m_menuItems) { + if (menuItem->tag() == tag) + return menuItem; + } + + return 0; +} + +QAndroidPlatformMenu::PlatformMenuItemsType QAndroidPlatformMenu::menuItems() const +{ + return m_menuItems; +} + +QMutex *QAndroidPlatformMenu::menuItemsMutex() +{ + return &m_menuItemsMutex; +} diff --git a/src/plugins/platforms/android/src/qandroidplatformmenu.h b/src/plugins/platforms/android/src/qandroidplatformmenu.h new file mode 100644 index 0000000000..20236cb636 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenu.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDPLATFORMMENU_H +#define QANDROIDPLATFORMMENU_H + +#include +#include +#include + +class QAndroidPlatformMenuItem; +class QAndroidPlatformMenu: public QPlatformMenu +{ +public: + typedef QVector PlatformMenuItemsType; + +public: + QAndroidPlatformMenu(); + ~QAndroidPlatformMenu(); + + void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before); + void removeMenuItem(QPlatformMenuItem *menuItem); + void syncMenuItem(QPlatformMenuItem *menuItem); + void syncSeparatorsCollapsible(bool enable); + + void setTag(quintptr tag); + quintptr tag() const; + void setText(const QString &text); + QString text() const; + void setIcon(const QIcon &icon); + QIcon icon() const; + void setEnabled(bool enabled); + bool isEnabled() const; + void setVisible(bool visible); + bool isVisible() const; + + QPlatformMenuItem *menuItemAt(int position) const; + QPlatformMenuItem *menuItemForTag(quintptr tag) const; + + PlatformMenuItemsType menuItems() const; + QMutex *menuItemsMutex(); + +private: + PlatformMenuItemsType m_menuItems; + quintptr m_tag; + QString m_text; + QIcon m_icon; + bool m_enabled; + bool m_isVisible; + QMutex m_menuItemsMutex; +}; + +#endif // QANDROIDPLATFORMMENU_H diff --git a/src/plugins/platforms/android/src/qandroidplatformmenubar.cpp b/src/plugins/platforms/android/src/qandroidplatformmenubar.cpp new file mode 100644 index 0000000000..ef1ac61356 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenubar.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidplatformmenubar.h" +#include "qandroidplatformmenu.h" +#include "androidjnimenu.h" + + +QAndroidPlatformMenuBar::QAndroidPlatformMenuBar() +{ + m_parentWindow = 0; + QtAndroidMenu::addMenuBar(this); +} + +QAndroidPlatformMenuBar::~QAndroidPlatformMenuBar() +{ + QtAndroidMenu::removeMenuBar(this); +} + +void QAndroidPlatformMenuBar::insertMenu(QPlatformMenu *menu, QPlatformMenu *before) +{ + QMutexLocker lock(&m_menusListMutex); + m_menus.insert(qFind(m_menus.begin(), + m_menus.end(), + static_cast(before)), + static_cast(menu)); +} + +void QAndroidPlatformMenuBar::removeMenu(QPlatformMenu *menu) +{ + QMutexLocker lock(&m_menusListMutex); + m_menus.erase(qFind(m_menus.begin(), + m_menus.end(), + static_cast(menu))); +} + +void QAndroidPlatformMenuBar::syncMenu(QPlatformMenu *menu) +{ + QtAndroidMenu::syncMenu(static_cast(menu)); +} + +void QAndroidPlatformMenuBar::handleReparent(QWindow *newParentWindow) +{ + m_parentWindow = newParentWindow; + QtAndroidMenu::setMenuBar(this, newParentWindow); +} + +QPlatformMenu *QAndroidPlatformMenuBar::menuForTag(quintptr tag) const +{ + foreach (QPlatformMenu *menu, m_menus) { + if (menu->tag() == tag) + return menu; + } + + return 0; +} + +QWindow *QAndroidPlatformMenuBar::parentWindow() const +{ + return m_parentWindow; +} + +QAndroidPlatformMenuBar::PlatformMenusType QAndroidPlatformMenuBar::menus() const +{ + return m_menus; +} + +QMutex *QAndroidPlatformMenuBar::menusListMutex() +{ + return &m_menusListMutex; +} diff --git a/src/plugins/platforms/android/src/qandroidplatformmenubar.h b/src/plugins/platforms/android/src/qandroidplatformmenubar.h new file mode 100644 index 0000000000..56915335c2 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenubar.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDPLATFORMMENUBAR_H +#define QANDROIDPLATFORMMENUBAR_H + +#include +#include +#include + +class QAndroidPlatformMenu; +class QAndroidPlatformMenuBar: public QPlatformMenuBar +{ +public: + typedef QVector PlatformMenusType; +public: + QAndroidPlatformMenuBar(); + ~QAndroidPlatformMenuBar(); + + void insertMenu(QPlatformMenu *menu, QPlatformMenu *before); + void removeMenu(QPlatformMenu *menu); + void syncMenu(QPlatformMenu *menu); + void handleReparent(QWindow *newParentWindow); + QPlatformMenu *menuForTag(quintptr tag) const; + + QWindow *parentWindow() const; + PlatformMenusType menus() const; + QMutex *menusListMutex(); + +private: + PlatformMenusType m_menus; + QWindow *m_parentWindow; + QMutex m_menusListMutex; +}; + +#endif // QANDROIDPLATFORMMENUBAR_H diff --git a/src/plugins/platforms/android/src/qandroidplatformmenuitem.cpp b/src/plugins/platforms/android/src/qandroidplatformmenuitem.cpp new file mode 100644 index 0000000000..bd37834d2a --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenuitem.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidplatformmenuitem.h" +#include "qandroidplatformmenu.h" + +QAndroidPlatformMenuItem::QAndroidPlatformMenuItem() +{ + m_tag = reinterpret_cast(this); // QMenu will overwrite this later, but we need a unique ID for QtQuick + m_menu = 0; + m_isVisible = true; + m_isSeparator = false; + m_role = NoRole; + m_isCheckable = false; + m_isChecked = false; + m_isEnabled = true; +} + +void QAndroidPlatformMenuItem::setTag(quintptr tag) +{ + m_tag = tag; +} + +quintptr QAndroidPlatformMenuItem::tag() const +{ + return m_tag; +} + +void QAndroidPlatformMenuItem::setText(const QString &text) +{ + m_text = text; + if (m_menu) + m_menu->setText(m_text); +} + +QString QAndroidPlatformMenuItem::text() const +{ + return m_text; +} + +void QAndroidPlatformMenuItem::setIcon(const QIcon &icon) +{ + m_icon = icon; + if (m_menu) + m_menu->setIcon(m_icon); +} + +QIcon QAndroidPlatformMenuItem::icon() const +{ + return m_icon; +} + +void QAndroidPlatformMenuItem::setMenu(QPlatformMenu *menu) +{ + m_menu = static_cast(menu); + if (!m_menu) + return; + + m_menu->setText(m_text); + m_menu->setIcon(m_icon); + m_menu->setVisible(m_isVisible); + m_menu->setEnabled(m_isEnabled); +} + +QAndroidPlatformMenu *QAndroidPlatformMenuItem::menu() const +{ + return m_menu; +} + +void QAndroidPlatformMenuItem::setVisible(bool isVisible) +{ + m_isVisible = isVisible; + if (m_menu) + m_menu->setVisible(m_isVisible); +} + +bool QAndroidPlatformMenuItem::isVisible() const +{ + return m_isVisible; +} + +void QAndroidPlatformMenuItem::setIsSeparator(bool isSeparator) +{ + m_isSeparator = isSeparator; +} + +bool QAndroidPlatformMenuItem::isSeparator() const +{ + return m_isSeparator; +} + +void QAndroidPlatformMenuItem::setFont(const QFont &font) +{ + Q_UNUSED(font) +} + +void QAndroidPlatformMenuItem::setRole(QPlatformMenuItem::MenuRole role) +{ + m_role = role; +} + +QPlatformMenuItem::MenuRole QAndroidPlatformMenuItem::role() const +{ + return m_role; +} + +void QAndroidPlatformMenuItem::setCheckable(bool checkable) +{ + m_isCheckable = checkable; +} + +bool QAndroidPlatformMenuItem::isCheckable() const +{ + return m_isCheckable; +} + +void QAndroidPlatformMenuItem::setChecked(bool isChecked) +{ + m_isChecked = isChecked; +} + +bool QAndroidPlatformMenuItem::isChecked() const +{ + return m_isChecked; +} + +void QAndroidPlatformMenuItem::setShortcut(const QKeySequence &shortcut) +{ + Q_UNUSED(shortcut) +} + +void QAndroidPlatformMenuItem::setEnabled(bool enabled) +{ + m_isEnabled = enabled; + if (m_menu) + m_menu->setEnabled(m_isEnabled); +} + +bool QAndroidPlatformMenuItem::isEnabled() const +{ + return m_isEnabled; +} diff --git a/src/plugins/platforms/android/src/qandroidplatformmenuitem.h b/src/plugins/platforms/android/src/qandroidplatformmenuitem.h new file mode 100644 index 0000000000..5861e8e195 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenuitem.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDPLATFORMMENUITEM_H +#define QANDROIDPLATFORMMENUITEM_H +#include + +class QAndroidPlatformMenu; + +class QAndroidPlatformMenuItem: public QPlatformMenuItem +{ +public: + QAndroidPlatformMenuItem(); + void setTag(quintptr tag); + quintptr tag() const; + + void setText(const QString &text); + QString text() const; + + void setIcon(const QIcon &icon); + QIcon icon() const; + + void setMenu(QPlatformMenu *menu); + QAndroidPlatformMenu *menu() const; + + void setVisible(bool isVisible); + bool isVisible() const; + + void setIsSeparator(bool isSeparator); + bool isSeparator() const; + + void setFont(const QFont &font); + + void setRole(MenuRole role); + MenuRole role() const; + + void setCheckable(bool checkable); + bool isCheckable() const; + + void setChecked(bool isChecked); + bool isChecked() const; + + void setShortcut(const QKeySequence &shortcut); + + void setEnabled(bool enabled); + bool isEnabled() const; + +private: + quintptr m_tag; + QString m_text; + QIcon m_icon; + QAndroidPlatformMenu *m_menu; + bool m_isVisible; + bool m_isSeparator; + MenuRole m_role; + bool m_isCheckable; + bool m_isChecked; + bool m_isEnabled; +}; + +#endif // QANDROIDPLATFORMMENUITEM_H diff --git a/src/plugins/platforms/android/src/qandroidplatformservices.cpp b/src/plugins/platforms/android/src/qandroidplatformservices.cpp new file mode 100644 index 0000000000..841a9d4d51 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformservices.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidplatformservices.h" +#include +#include +#include + +QAndroidPlatformServices::QAndroidPlatformServices() +{ + JNIEnv *env; + if (QtAndroid::javaVM()->AttachCurrentThread(&env, NULL) < 0) { + qCritical() << "AttachCurrentThread failed"; + return; + } + + m_openURIMethodID = env->GetStaticMethodID(QtAndroid::applicationClass(), + "openURL", + "(Ljava/lang/String;)V"); +} + +bool QAndroidPlatformServices::openUrl(const QUrl &url) +{ + JNIEnv *env; + if (QtAndroid::javaVM()->AttachCurrentThread(&env, NULL) < 0) { + qCritical() << "AttachCurrentThread failed"; + return false; + } + + jstring string = env->NewString(reinterpret_cast(url.toString().constData()), + url.toString().length()); + env->CallStaticVoidMethod(QtAndroid::applicationClass(), m_openURIMethodID, string); + env->DeleteLocalRef(string); + return true; +} + +bool QAndroidPlatformServices::openDocument(const QUrl &url) +{ + return openUrl(url); +} + +QByteArray QAndroidPlatformServices::desktopEnvironment() const +{ + return QByteArray("Android"); +} diff --git a/src/plugins/platforms/android/src/qandroidplatformservices.h b/src/plugins/platforms/android/src/qandroidplatformservices.h new file mode 100644 index 0000000000..8368b19043 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformservices.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROIDPLATFORMDESKTOPSERVICE_H +#define ANDROIDPLATFORMDESKTOPSERVICE_H + +#include +#include "androidjnimain.h" +#include + +class QAndroidPlatformServices: public QPlatformServices +{ +public: + QAndroidPlatformServices(); + bool openUrl(const QUrl &url); + bool openDocument(const QUrl &url); + QByteArray desktopEnvironment() const; +private: + jmethodID m_openURIMethodID; + +}; + +#endif // ANDROIDPLATFORMDESKTOPSERVICE_H diff --git a/src/plugins/platforms/android/src/qandroidplatformtheme.cpp b/src/plugins/platforms/android/src/qandroidplatformtheme.cpp new file mode 100644 index 0000000000..25f2ade11a --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformtheme.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidplatformtheme.h" +#include "qandroidplatformmenubar.h" +#include "qandroidplatformmenu.h" +#include "qandroidplatformmenuitem.h" +#include +#include + +QPlatformMenuBar *QAndroidPlatformTheme::createPlatformMenuBar() const +{ + return new QAndroidPlatformMenuBar; +} + +QPlatformMenu *QAndroidPlatformTheme::createPlatformMenu() const +{ + return new QAndroidPlatformMenu; +} + +QPlatformMenuItem *QAndroidPlatformTheme::createPlatformMenuItem() const +{ + return new QAndroidPlatformMenuItem; +} + +QVariant QAndroidPlatformTheme::themeHint(ThemeHint hint) const +{ + switch (hint) { + case StyleNames: + if (qgetenv("QT_USE_ANDROID_NATIVE_STYLE").toInt() + && (!qgetenv("MINISTRO_ANDROID_STYLE_PATH").isEmpty() + || QFileInfo("/data/data/org.kde.necessitas.ministro/files/qt/style/style.json").exists())) { + return QStringList("android"); + } + return QStringList("fusion"); + break; + default: + return QPlatformTheme::themeHint(hint); + } +} diff --git a/src/plugins/platforms/android/src/qandroidplatformtheme.h b/src/plugins/platforms/android/src/qandroidplatformtheme.h new file mode 100644 index 0000000000..263878ee16 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformtheme.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDPLATFORMTHEME_H +#define QANDROIDPLATFORMTHEME_H + +#include + +class QAndroidPlatformTheme: public QPlatformTheme +{ +public: + virtual QPlatformMenuBar *createPlatformMenuBar() const; + virtual QPlatformMenu *createPlatformMenu() const; + virtual QPlatformMenuItem *createPlatformMenuItem() const; + virtual QVariant themeHint(ThemeHint hint) const; +}; + +#endif // QANDROIDPLATFORMTHEME_H diff --git a/src/plugins/platforms/android/src/raster/qandroidplatformscreen.cpp b/src/plugins/platforms/android/src/raster/qandroidplatformscreen.cpp new file mode 100644 index 0000000000..2779d7cffd --- /dev/null +++ b/src/plugins/platforms/android/src/raster/qandroidplatformscreen.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidplatformscreen.h" +#include "qandroidplatformintegration.h" +#include "androidjnimain.h" +#include "androidjnimenu.h" + +QAndroidPlatformScreen::QAndroidPlatformScreen():QFbScreen() +{ + mGeometry = QRect(0, 0, QAndroidPlatformIntegration::m_defaultGeometryWidth, QAndroidPlatformIntegration::m_defaultGeometryHeight); + mFormat = QImage::Format_RGB16; + mDepth = 16; + mPhysicalSize.setHeight(QAndroidPlatformIntegration::m_defaultPhysicalSizeHeight); + mPhysicalSize.setWidth(QAndroidPlatformIntegration::m_defaultPhysicalSizeWidth); + initializeCompositor(); +} + +void QAndroidPlatformScreen::topWindowChanged(QWindow *w) +{ + QtAndroidMenu::setActiveTopLevelWindow(w); +} + +QRegion QAndroidPlatformScreen::doRedraw() +{ + QRegion touched; + touched = QFbScreen::doRedraw(); + if (touched.isEmpty()) + return touched; + + QtAndroid::flushImage(mGeometry.topLeft(), *mScreenImage, touched.boundingRect()); + return touched; +} diff --git a/src/plugins/platforms/android/src/raster/qandroidplatformscreen.h b/src/plugins/platforms/android/src/raster/qandroidplatformscreen.h new file mode 100644 index 0000000000..df08e43af4 --- /dev/null +++ b/src/plugins/platforms/android/src/raster/qandroidplatformscreen.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDPLATFORMSCREEN_H +#define QANDROIDPLATFORMSCREEN_H + +#include + +class QAndroidPlatformScreen: public QFbScreen +{ + Q_OBJECT +public: + QAndroidPlatformScreen(); + void topWindowChanged(QWindow *w); + +public slots: + QRegion doRedraw(); + +}; + +#endif diff --git a/src/plugins/platforms/android/src/raster/qandroidplatformwindow.cpp b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.cpp new file mode 100644 index 0000000000..94a69c10c7 --- /dev/null +++ b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidplatformwindow.h" + +QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window) : QFbWindow(window) +{ +} + +void QAndroidPlatformWindow::setGeometry(const QRect &rect) +{ + QFbWindow::setGeometry(rect); +} + +void QAndroidPlatformWindow::propagateSizeHints() +{ + //shut up warning from default implementation +} diff --git a/src/plugins/platforms/android/src/raster/qandroidplatformwindow.h b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.h new file mode 100644 index 0000000000..3ee815fd69 --- /dev/null +++ b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROIDPLATFORMWINDOW_H +#define ANDROIDPLATFORMWINDOW_H +#include +#include + +class QAndroidPlatformWindow: public QObject, public QFbWindow +{ + Q_OBJECT +public: + explicit QAndroidPlatformWindow(QWindow *window); + + void propagateSizeHints(); + +public slots: + void setGeometry(const QRect &rect); + +}; + +#endif // ANDROIDPLATFORMWINDOW_H diff --git a/src/plugins/platforms/android/src/raster/raster.pri b/src/plugins/platforms/android/src/raster/raster.pri new file mode 100644 index 0000000000..86e5aa235f --- /dev/null +++ b/src/plugins/platforms/android/src/raster/raster.pri @@ -0,0 +1,7 @@ +INCLUDEPATH += $$PWD + +SOURCES += $$PWD/qandroidplatformscreen.cpp \ + $$PWD/qandroidplatformwindow.cpp + +HEADERS += $$PWD/qandroidplatformscreen.h \ + $$PWD/qandroidplatformwindow.h diff --git a/src/plugins/platforms/android/src/src.pri b/src/plugins/platforms/android/src/src.pri new file mode 100644 index 0000000000..9bf36b2337 --- /dev/null +++ b/src/plugins/platforms/android/src/src.pri @@ -0,0 +1,47 @@ +load(qt_plugin) + +QT += core-private gui-private widgets-private platformsupport-private + +CONFIG += qpa/genericunixfontdatabase + +OTHER_FILES += $$PWD/android.json + +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../../../3rdparty/android/src + +SOURCES += $$PWD/androidplatformplugin.cpp \ + $$PWD/androidjnimain.cpp \ + $$PWD/androidjniinput.cpp \ + $$PWD/androidjnimenu.cpp \ + $$PWD/androidjniclipboard.cpp \ + $$PWD/qandroidplatformintegration.cpp \ + $$PWD/qandroidplatformservices.cpp \ + $$PWD/qandroidassetsfileenginehandler.cpp \ + $$PWD/qandroidinputcontext.cpp \ + $$PWD/qandroidplatformfontdatabase.cpp \ + $$PWD/qandroidplatformclipboard.cpp \ + $$PWD/qandroidplatformtheme.cpp \ + $$PWD/qandroidplatformmenubar.cpp \ + $$PWD/qandroidplatformmenu.cpp \ + $$PWD/qandroidplatformmenuitem.cpp + + +HEADERS += $$PWD/qandroidplatformintegration.h \ + $$PWD/androidjnimain.h \ + $$PWD/androidjniinput.h \ + $$PWD/androidjnimenu.h \ + $$PWD/androidjniclipboard.h \ + $$PWD/qandroidplatformservices.h \ + $$PWD/qandroidassetsfileenginehandler.h \ + $$PWD/qandroidinputcontext.h \ + $$PWD/qandroidplatformfontdatabase.h \ + $$PWD/qandroidplatformclipboard.h \ + $$PWD/qandroidplatformtheme.h \ + $$PWD/qandroidplatformmenubar.h \ + $$PWD/qandroidplatformmenu.h \ + $$PWD/qandroidplatformmenuitem.h + + +#Non-standard install directory, QTBUG-29859 +DESTDIR = $$DESTDIR/android +target.path = $${target.path}/android diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/qeglfsintegration.cpp index 0fc4c44629..8cebe16775 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsintegration.cpp @@ -53,7 +53,7 @@ #include #include -#if !defined(QT_NO_EVDEV) +#if !defined(QT_NO_EVDEV) && !defined(Q_OS_ANDROID) #include #include #include @@ -77,7 +77,7 @@ QEglFSIntegration::QEglFSIntegration() { QGuiApplicationPrivate::instance()->setEventDispatcher(mEventDispatcher); -#if !defined(QT_NO_EVDEV) +#if !defined(QT_NO_EVDEV) && !defined(Q_OS_ANDROID) new QEvdevKeyboardManager(QLatin1String("EvdevKeyboard"), QString() /* spec */, this); new QEvdevMouseManager(QLatin1String("EvdevMouse"), QString() /* spec */, this); new QEvdevTouchScreenHandlerThread(QString() /* spec */, this); diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index a60a3626fa..92664826ab 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -1,5 +1,7 @@ TEMPLATE = subdirs +android:!android-no-sdk: SUBDIRS += android + SUBDIRS += minimal offscreen contains(QT_CONFIG, xcb) { diff --git a/src/src.pro b/src/src.pro index 02d4852b60..90f0a35711 100644 --- a/src/src.pro +++ b/src/src.pro @@ -66,6 +66,8 @@ src_plugins.subdir = $$PWD/plugins src_plugins.target = sub-plugins src_plugins.depends = src_sql src_xml src_network +src_android.subdir = $$PWD/android + # this order is important SUBDIRS += src_tools src_corelib win32:SUBDIRS += src_winmain @@ -98,3 +100,5 @@ contains(QT_CONFIG, concurrent):SUBDIRS += src_concurrent SUBDIRS += src_plugins nacl: SUBDIRS -= src_network src_testlib + +android:!android-no-sdk: SUBDIRS += src_android diff --git a/src/widgets/styles/qandroidstyle.cpp b/src/widgets/styles/qandroidstyle.cpp new file mode 100644 index 0000000000..a3c1063b41 --- /dev/null +++ b/src/widgets/styles/qandroidstyle.cpp @@ -0,0 +1,1601 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtWidgets module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidstyle_p.h" + +#if !defined(QT_NO_STYLE_ANDROID) || defined(QT_PLUGIN) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace { + const int textStyle_bold = 1; + const int textStyle_italic = 2; + + const int typeface_sans = 1; + const int typeface_serif = 2; + const int typeface_monospace = 3; + + const quint32 NO_COLOR = 1; + const quint32 TRANSPARENT_COLOR = 0; +} + + +QAndroidStyle::QAndroidStyle() + : QCommonStyle() +{ + QString stylePath(QLatin1String(qgetenv("MINISTRO_ANDROID_STYLE_PATH"))); + + if (stylePath.isEmpty()) + stylePath = QLatin1String("/data/data/org.kde.necessitas.ministro/files/qt/style/"); + Q_ASSERT(!stylePath.isEmpty()); + + QFile f(stylePath + QLatin1String("style.json")); + if (!f.open(QIODevice::ReadOnly)) + return; + + QJsonParseError error; + QJsonDocument document = QJsonDocument::fromJson(f.readAll(), &error); + if (document.isNull()) { + qCritical() << error.errorString(); + return; + } + + if (!document.isObject()) { + qCritical() << "Style.json does not contain a valid style."; + return; + } + + QJsonObject object = document.object(); + for (QJsonObject::const_iterator objectIterator = object.constBegin(); + objectIterator != object.constEnd(); + ++objectIterator) { + QString key = objectIterator.key(); + QJsonValue value = objectIterator.value(); + if (!value.isObject()) { + qWarning("Style.json structure is unrecognized."); + continue; + } + + QJsonObject item = value.toObject(); + QJsonObject::const_iterator attributeIterator = item.find(QLatin1String("qtClass")); + if (attributeIterator != item.constEnd()) { + // The item has palette and font information for a specific Qt Class (e.g. QWidget, QPushButton, etc.) + const QString qtClassName = attributeIterator.value().toString(); + + // Extract font information + QFont font; + + // Font size (in pixels) + attributeIterator = item.find(QLatin1String("TextAppearance_textSize")); + if (attributeIterator != item.constEnd()) + font.setPixelSize(int(attributeIterator.value().toDouble())); + + // Font style + attributeIterator = item.find(QLatin1String("TextAppearance_textStyle")); + if (attributeIterator != item.constEnd()) { + const int style = int(attributeIterator.value().toDouble()); + font.setBold(style & textStyle_bold); + font.setItalic(style & textStyle_italic); + } + + // Font typeface + attributeIterator = item.find(QLatin1String("TextAppearance_typeface")); + if (attributeIterator != item.constEnd()) { + QFont::StyleHint styleHint = QFont::AnyStyle; + switch (int(attributeIterator.value().toDouble())) { + case typeface_sans: + styleHint = QFont::SansSerif; + break; + case typeface_serif: + styleHint = QFont::Serif; + break; + case typeface_monospace: + styleHint = QFont::Monospace; + break; + } + font.setStyleHint(styleHint, QFont::PreferMatch); + } + QApplication::setFont(font, qtClassName.toUtf8()); + // Extract font information + + // Extract palette information + QPalette palette; + attributeIterator = item.find(QLatin1String("TextAppearance_textColor")); + if (attributeIterator != item.constEnd()) + setPaletteColor(attributeIterator.value().toObject().toVariantMap(), palette, QPalette::WindowText); + + attributeIterator = item.find(QLatin1String("TextAppearance_textColorLink")); + if (attributeIterator != item.constEnd()) + setPaletteColor(attributeIterator.value().toObject().toVariantMap(), palette, QPalette::Link); + + attributeIterator = item.find(QLatin1String("TextAppearance_textColorHighlight")); + if (attributeIterator != item.constEnd()) + palette.setColor(QPalette::Highlight, QRgb(int(attributeIterator.value().toDouble()))); + palette.setColor(QPalette::Window, Qt::black); + QApplication::setPalette(palette, qtClassName.toUtf8()); + if (QLatin1String("QWidget") == qtClassName) + m_standardPalette = palette; + // Extract palette information + } + QAndroidStyle::ItemType itemType = qtControl(key); + if (QC_UnknownType == itemType) + continue; + + switch (itemType) { + case QC_Checkbox: + case QC_RadioButton: + m_androidControlsHash[int(itemType)] = new AndroidCompoundButtonControl(item.toVariantMap(), + itemType); + break; + + case QC_ProgressBar: + m_androidControlsHash[int(itemType)] = new AndroidProgressBarControl(item.toVariantMap(), + itemType); + break; + + case QC_Slider: + m_androidControlsHash[int(itemType)] = new AndroidSeekBarControl(item.toVariantMap(), + itemType); + break; + + case QC_Combobox: + m_androidControlsHash[int(itemType)] = new AndroidSpinnerControl(item.toVariantMap(), + itemType); + break; + + default: + m_androidControlsHash[int(itemType)] = new AndroidControl(item.toVariantMap(), + itemType); + break; + } + } + QApplication::setPalette(QApplication::palette("simple_list_item"), "QListView"); + QApplication::setFont(QApplication::font("simple_list_item"), "QListView"); + QApplication::setPalette(QApplication::palette("simple_list_item"), "QAbstractItemView"); + QApplication::setFont(QApplication::font("simple_list_item"), "QAbstractItemView"); +} + +QAndroidStyle::~QAndroidStyle() +{ + qDeleteAll(m_androidControlsHash); +} + + +void QAndroidStyle::setPaletteColor(const QVariantMap &object, + QPalette &palette, + QPalette::ColorRole role) +{ + // QPalette::Active -> ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET + palette.setColor(QPalette::Active, + role, + QRgb(object.value(QLatin1String("ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET")).toInt())); + + // QPalette::Inactive -> ENABLED_STATE_SET + palette.setColor(QPalette::Inactive, + role, + QRgb(object.value(QLatin1String("ENABLED_STATE_SET")).toInt())); + + // QPalette::Disabled -> EMPTY_STATE_SET + palette.setColor(QPalette::Disabled, + role, + QRgb(object.value(QLatin1String("EMPTY_STATE_SET")).toInt())); + + palette.setColor(QPalette::Current, role, palette.color(QPalette::Active, role)); + + if (role == QPalette::WindowText) { + // QPalette::BrightText -> PRESSED + // QPalette::Active -> PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET + palette.setColor(QPalette::Active, + QPalette::BrightText, + QRgb(object.value(QLatin1String("PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET")).toInt())); + + // QPalette::Inactive -> PRESSED_ENABLED_STATE_SET + palette.setColor(QPalette::Inactive, + QPalette::BrightText, + QRgb(object.value(QLatin1String("PRESSED_ENABLED_STATE_SET")).toInt())); + + // QPalette::Disabled -> PRESSED_STATE_SET + palette.setColor(QPalette::Disabled, + QPalette::BrightText, + QRgb(object.value(QLatin1String("PRESSED_STATE_SET")).toInt())); + + palette.setColor(QPalette::Current, QPalette::BrightText, palette.color(QPalette::Active, QPalette::BrightText)); + + // QPalette::HighlightedText -> SELECTED + // QPalette::Active -> ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET + palette.setColor(QPalette::Active, + QPalette::HighlightedText, + QRgb(object.value(QLatin1String("ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET")).toInt())); + + // QPalette::Inactive -> ENABLED_SELECTED_STATE_SET + palette.setColor(QPalette::Inactive, + QPalette::HighlightedText, + QRgb(object.value(QLatin1String("ENABLED_SELECTED_STATE_SET")).toInt())); + + // QPalette::Disabled -> SELECTED_STATE_SET + palette.setColor(QPalette::Disabled, + QPalette::HighlightedText, + QRgb(object.value(QLatin1String("SELECTED_STATE_SET")).toInt())); + + palette.setColor(QPalette::Current, + QPalette::HighlightedText, + palette.color(QPalette::Active, QPalette::HighlightedText)); + + // Same colors for Text + palette.setColor(QPalette::Active, QPalette::Text, palette.color(QPalette::Active, role)); + palette.setColor(QPalette::Inactive, QPalette::Text, palette.color(QPalette::Inactive, role)); + palette.setColor(QPalette::Disabled, QPalette::Text, palette.color(QPalette::Disabled, role)); + palette.setColor(QPalette::Current, QPalette::Text, palette.color(QPalette::Current, role)); + + // And for ButtonText + palette.setColor(QPalette::Active, QPalette::ButtonText, palette.color(QPalette::Active, role)); + palette.setColor(QPalette::Inactive, QPalette::ButtonText, palette.color(QPalette::Inactive, role)); + palette.setColor(QPalette::Disabled, QPalette::ButtonText, palette.color(QPalette::Disabled, role)); + palette.setColor(QPalette::Current, QPalette::ButtonText, palette.color(QPalette::Current, role)); + } +} + +QAndroidStyle::ItemType QAndroidStyle::qtControl(const QString &android) +{ + if (android == QLatin1String("buttonStyle")) + return QC_Button; + if (android == QLatin1String("editTextStyle")) + return QC_EditText; + if (android == QLatin1String("radioButtonStyle")) + return QC_RadioButton; + if (android == QLatin1String("checkboxStyle")) + return QC_Checkbox; + if (android == QLatin1String("textViewStyle")) + return QC_View; + if (android == QLatin1String("buttonStyleToggle")) + return QC_Switch; + if (android == QLatin1String("spinnerStyle")) + return QC_Combobox; + if (android == QLatin1String("progressBarStyleHorizontal")) + return QC_ProgressBar; + if (android == QLatin1String("seekBarStyle")) + return QC_Slider; + + return QC_UnknownType; +} + +QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ComplexControl control) +{ + switch (control) { + case CC_ComboBox: + return QC_Combobox; + case CC_Slider: + return QC_Slider; + case CC_GroupBox: + return QC_View; + default: + return QC_UnknownType; + } +} + +QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ContentsType contentsType) +{ + switch (contentsType) { + case CT_PushButton: + return QC_Button; + case CT_CheckBox: + return QC_Checkbox; + case CT_RadioButton: + return QC_RadioButton; + case CT_ComboBox: + return QC_Combobox; + case CT_ProgressBar: + return QC_ProgressBar; + case CT_Slider: + return QC_Slider; + case CT_TabWidget: + return QC_Tab; + case CT_TabBarTab: + return QC_TabButton; + case CT_LineEdit: + return QC_EditText; + case CT_GroupBox: + return QC_GroupBox; + default: + return QC_UnknownType; + } +} + +QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ControlElement controlElement) +{ + switch (controlElement) { + case CE_PushButton: + case CE_PushButtonBevel: + case CE_PushButtonLabel: + return QC_Button; + + case CE_CheckBox: + case CE_CheckBoxLabel: + return QC_Checkbox; + + case CE_RadioButton: + case CE_RadioButtonLabel: + return QC_RadioButton; + + case CE_TabBarTab: + case CE_TabBarTabShape: + case CE_TabBarTabLabel: + return QC_Tab; + + case CE_ProgressBar: + case CE_ProgressBarGroove: + case CE_ProgressBarContents: + case CE_ProgressBarLabel: + return QC_ProgressBar; + + case CE_ComboBoxLabel: + return QC_Combobox; + + default: + return QC_UnknownType; + } +} + +QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::PrimitiveElement primitiveElement) +{ + switch (primitiveElement) { + case QStyle::PE_PanelLineEdit: + case QStyle::PE_FrameLineEdit: + return QC_EditText; + + case QStyle::PE_FrameWindow: + case QStyle::PE_Widget: + case QStyle::PE_Frame: + case QStyle::PE_FrameFocusRect: + return QC_View; + default: + return QC_UnknownType; + } +} + +QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::SubElement subElement) +{ + switch (subElement) { + case QStyle::SE_LineEditContents: + return QC_EditText; + + case QStyle::SE_PushButtonContents: + case QStyle::SE_PushButtonFocusRect: + return QC_Button; + + case SE_RadioButtonContents: + return QC_RadioButton; + + case SE_CheckBoxContents: + return QC_Checkbox; + + default: + return QC_UnknownType; + } +} + +void QAndroidStyle::drawPrimitive(PrimitiveElement pe, + const QStyleOption *opt, + QPainter *p, + const QWidget *w) const +{ + const ItemType itemType = qtControl(pe); + AndroidControlsHash::const_iterator it = itemType != QC_UnknownType + ? m_androidControlsHash.find(itemType) + : m_androidControlsHash.end(); + if (it != m_androidControlsHash.end()) + it.value()->drawControl(opt, p, w); + else + QCommonStyle::drawPrimitive(pe, opt, p, w); +} + + +void QAndroidStyle::drawControl(QStyle::ControlElement element, + const QStyleOption *opt, + QPainter *p, + const QWidget *w) const +{ + const ItemType itemType = qtControl(element); + AndroidControlsHash::const_iterator it = itemType != QC_UnknownType + ? m_androidControlsHash.find(itemType) + : m_androidControlsHash.end(); + if (it != m_androidControlsHash.end()) { + it.value()->drawControl(opt, p, w); + + switch (itemType) { + case QC_Button: + if (const QStyleOptionButton *buttonOption = + qstyleoption_cast(opt)) { + QMargins padding = it.value()->padding(); + QStyleOptionButton copy (*buttonOption); + copy.rect.adjust(padding.left(), padding.top(), -padding.right(), -padding.bottom()); + QCommonStyle::drawControl(CE_PushButtonLabel, ©, p, w); + } + break; + case QC_Checkbox: + case QC_RadioButton: + if (const QStyleOptionButton *btn = + qstyleoption_cast(opt)) { + const bool isRadio = (element == CE_RadioButton); + QStyleOptionButton subopt(*btn); + subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents + : SE_CheckBoxContents, btn, w); + QCommonStyle::drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, p, w); + } + break; + case QC_Combobox: + if (const QStyleOptionComboBox *comboboxOption = + qstyleoption_cast(opt)) { + QMargins padding = it.value()->padding(); + QStyleOptionComboBox copy (*comboboxOption); + copy.rect.adjust(padding.left(), padding.top(), -padding.right(), -padding.bottom()); + p->setFont(QApplication::font("simple_spinner_item")); + p->setPen(QApplication::palette("QPushButton").color(QPalette::Active, QPalette::Text)); + QCommonStyle::drawControl(CE_ComboBoxLabel, comboboxOption, p, w); + } + break; + default: + break; + } + } + else + QCommonStyle::drawControl(element, opt, p, w); +} + +QRect QAndroidStyle::subElementRect(SubElement subElement, + const QStyleOption *option, + const QWidget *widget) const +{ + const ItemType itemType = qtControl(subElement); + AndroidControlsHash::const_iterator it = itemType != QC_UnknownType + ? m_androidControlsHash.find(itemType) + : m_androidControlsHash.end(); + if (it != m_androidControlsHash.end()) + return it.value()->subElementRect(subElement, option, widget); + return QCommonStyle::subElementRect(subElement, option, widget); +} + +void QAndroidStyle::drawComplexControl(ComplexControl cc, + const QStyleOptionComplex *opt, + QPainter *p, + const QWidget *widget) const +{ + const ItemType itemType = qtControl(cc); + AndroidControlsHash::const_iterator it = itemType != QC_UnknownType + ? m_androidControlsHash.find(itemType) + : m_androidControlsHash.end(); + if (it != m_androidControlsHash.end()) + it.value()->drawControl(opt, p, widget); + else + QCommonStyle::drawComplexControl(cc, opt, p, widget); +} + +QStyle::SubControl QAndroidStyle::hitTestComplexControl(ComplexControl cc, + const QStyleOptionComplex *opt, + const QPoint &pt, + const QWidget *widget) const +{ + const ItemType itemType = qtControl(cc); + AndroidControlsHash::const_iterator it = itemType != QC_UnknownType + ? m_androidControlsHash.find(itemType) + : m_androidControlsHash.end(); + if (it != m_androidControlsHash.end()) { + switch (cc) { + case CC_Slider: + if (const QStyleOptionSlider *slider = qstyleoption_cast(opt)) { + QRect r = it.value()->subControlRect(slider, SC_SliderHandle, widget); + if (r.isValid() && r.contains(pt)) { + return SC_SliderHandle; + } else { + r = it.value()->subControlRect(slider, SC_SliderGroove, widget); + if (r.isValid() && r.contains(pt)) + return SC_SliderGroove; + } + } + break; + default: + break; + } + } + return QCommonStyle::hitTestComplexControl(cc, opt, pt, widget); +} + +QRect QAndroidStyle::subControlRect(ComplexControl cc, + const QStyleOptionComplex *opt, + SubControl sc, + const QWidget *widget) const +{ + const ItemType itemType = qtControl(cc); + AndroidControlsHash::const_iterator it = itemType != QC_UnknownType + ? m_androidControlsHash.find(itemType) + : m_androidControlsHash.end(); + if (it != m_androidControlsHash.end()) + return it.value()->subControlRect(opt, sc, widget); + return QCommonStyle::subControlRect(cc, opt, sc, widget); +} + +int QAndroidStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, + const QWidget *widget) const +{ + switch (metric) { + case PM_ButtonMargin: + case PM_FocusFrameVMargin: + case PM_FocusFrameHMargin: + case PM_ComboBoxFrameWidth: + case PM_SpinBoxFrameWidth: + return 0; + default: + return QCommonStyle::pixelMetric(metric, option, widget); + } + +} + +QSize QAndroidStyle::sizeFromContents(ContentsType ct, + const QStyleOption *opt, + const QSize &contentsSize, + const QWidget *w) const +{ + QSize sz=QCommonStyle::sizeFromContents(ct, opt, contentsSize, w); + const ItemType itemType = qtControl(ct); + AndroidControlsHash::const_iterator it = itemType != QC_UnknownType + ? m_androidControlsHash.find(itemType) + : m_androidControlsHash.end(); + if (it != m_androidControlsHash.end()) + return it.value()->sizeFromContents(opt, sz, w); + return sz; +} + +QPixmap QAndroidStyle::standardPixmap(StandardPixmap standardPixmap, + const QStyleOption *opt, + const QWidget *widget) const +{ + return QCommonStyle::standardPixmap(standardPixmap, opt, widget); +} + +QPixmap QAndroidStyle::generatedIconPixmap(QIcon::Mode iconMode, + const QPixmap &pixmap, + const QStyleOption *opt) const +{ + return QCommonStyle::generatedIconPixmap(iconMode, pixmap, opt); +} + +QPalette QAndroidStyle::standardPalette() const +{ + return m_standardPalette; +} + +QAndroidStyle::AndroidDrawable::AndroidDrawable(const QVariantMap &drawable, + QAndroidStyle::ItemType itemType) +{ + initPadding(drawable); + m_itemType = itemType; +} + +QAndroidStyle::AndroidDrawable::~AndroidDrawable() +{ +} + +void QAndroidStyle::AndroidDrawable::initPadding(const QVariantMap &drawable) +{ + QVariantMap::const_iterator it = drawable.find(QLatin1String("padding")); + if (it != drawable.end()) + m_padding = extractMargins(it.value().toMap()); +} + +const QMargins &QAndroidStyle::AndroidDrawable::padding() const +{ + return m_padding; +} + +QSize QAndroidStyle::AndroidDrawable::size() const +{ + if (type() == Image || type() == NinePatch) + return static_cast(this)->size(); + + return QSize(); +} + +QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidDrawable::fromMap(const QVariantMap &drawable, + ItemType itemType) +{ + const QString type = drawable.value(QLatin1String("type")).toString(); + if (type == QLatin1String("image")) + return new QAndroidStyle::AndroidImageDrawable(drawable, itemType); + if (type == QLatin1String("9patch")) + return new QAndroidStyle::Android9PatchDrawable(drawable, itemType); + if (type == QLatin1String("stateslist")) + return new QAndroidStyle::AndroidStateDrawable(drawable, itemType); + if (type == QLatin1String("layer")) + return new QAndroidStyle::AndroidLayerDrawable(drawable, itemType); + if (type == QLatin1String("gradient")) + return new QAndroidStyle::AndroidGradientDrawable(drawable, itemType); + if (type == QLatin1String("clipDrawable")) + return new QAndroidStyle::AndroidClipDrawable(drawable, itemType); + if (type == QLatin1String("color")) + return new QAndroidStyle::AndroidColorDrawable(drawable, itemType); + return 0; +} + +QMargins QAndroidStyle::AndroidDrawable::extractMargins(const QVariantMap &value) +{ + QMargins m; + m.setLeft(value.value(QLatin1String("left")).toInt()); + m.setRight(value.value(QLatin1String("right")).toInt()); + m.setTop(value.value(QLatin1String("top")).toInt()); + m.setBottom(value.value(QLatin1String("bottom")).toInt()); + return m; +} + + +QAndroidStyle::AndroidImageDrawable::AndroidImageDrawable(const QVariantMap &drawable, + QAndroidStyle::ItemType itemType) + : AndroidDrawable(drawable, itemType) +{ + m_filePath = drawable.value(QLatin1String("path")).toString(); + m_size.setHeight(drawable.value(QLatin1String("height")).toInt()); + m_size.setWidth(drawable.value(QLatin1String("width")).toInt()); +} + +QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidImageDrawable::type() const +{ + return QAndroidStyle::Image; +} + +void QAndroidStyle::AndroidImageDrawable::draw(QPainter *painter, const QStyleOption *opt) const +{ + if (m_hashKey.isEmpty()) + m_hashKey = QFileInfo(m_filePath).fileName(); + + QPixmap pm; + if (!QPixmapCache::find(m_hashKey, &pm)) { + pm.load(m_filePath); + QPixmapCache::insert(m_hashKey, pm); + } + + painter->drawPixmap(opt->rect.x(), (opt->rect.height() - pm.height()) / 2, pm); +} + +QSize QAndroidStyle::AndroidImageDrawable::size() const +{ + return m_size; +} + +QAndroidStyle::AndroidColorDrawable::AndroidColorDrawable(const QVariantMap &drawable, + ItemType itemType) + : AndroidDrawable(drawable, itemType) +{ + m_color.setRgba(QRgb(drawable.value(QLatin1String("color")).toInt())); +} + +QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidColorDrawable::type() const +{ + return QAndroidStyle::Color; +} + +void QAndroidStyle::AndroidColorDrawable::draw(QPainter *painter, const QStyleOption *opt) const +{ + painter->fillRect(opt->rect, m_color); +} + +QAndroidStyle::Android9PatchDrawable::Android9PatchDrawable(const QVariantMap &drawable, + QAndroidStyle::ItemType itemType) + : AndroidImageDrawable(drawable.value(QLatin1String("drawable")).toMap(), itemType) +{ + initPadding(drawable); + QVariantMap chunk = drawable.value(QLatin1String("chunkInfo")).toMap(); + extractIntArray(chunk.value(QLatin1String("xdivs")).toList(), m_chunkData.xDivs); + extractIntArray(chunk.value(QLatin1String("ydivs")).toList(), m_chunkData.yDivs); + extractIntArray(chunk.value(QLatin1String("colors")).toList(), m_chunkData.colors); +} + +QAndroidStyle::AndroidDrawableType QAndroidStyle::Android9PatchDrawable::type() const +{ + return QAndroidStyle::NinePatch; +} + +int QAndroidStyle::Android9PatchDrawable::calculateStretch(int boundsLimit, + int startingPoint, + int srcSpace, + int numStrechyPixelsRemaining, + int numFixedPixelsRemaining) +{ + int spaceRemaining = boundsLimit - startingPoint; + int stretchySpaceRemaining = spaceRemaining - numFixedPixelsRemaining; + return (float(srcSpace) * stretchySpaceRemaining / numStrechyPixelsRemaining + .5); +} + +void QAndroidStyle::Android9PatchDrawable::extractIntArray(const QVariantList &values, + QVector & array) +{ + foreach (QVariant value, values) + array << value.toInt(); +} + + +void QAndroidStyle::Android9PatchDrawable::draw(QPainter * painter, const QStyleOption *opt) const +{ + if (m_hashKey.isEmpty()) + m_hashKey = QFileInfo(m_filePath).fileName(); + + QPixmap pixmap; + if (!QPixmapCache::find(m_hashKey, &pixmap)) { + pixmap.load(m_filePath); + QPixmapCache::insert(m_hashKey, pixmap); + } + + const QRect &bounds=opt->rect; + + // shamelessly stolen from Android's sources (NinepatchImpl.cpp) and adapted for Qt + const int pixmapWidth = pixmap.width(); + const int pixmapHeight = pixmap.height(); + + if (bounds.isNull() || !pixmapWidth || !pixmapHeight) + return; + + QPainter::RenderHints savedHints = painter->renderHints(); + + // The patchs doesn't need smooth transform ! + painter->setRenderHints(QPainter::SmoothPixmapTransform, false); + + QRectF dst; + QRectF src; + + const int32_t x0 = m_chunkData.xDivs[0]; + const int32_t y0 = m_chunkData.yDivs[0]; + const quint8 numXDivs = m_chunkData.xDivs.size(); + const quint8 numYDivs = m_chunkData.yDivs.size(); + int i; + int j; + int colorIndex = 0; + quint32 color; + bool xIsStretchable; + const bool initialXIsStretchable = (x0 == 0); + bool yIsStretchable = (y0 == 0); + const int bitmapWidth = pixmap.width(); + const int bitmapHeight = pixmap.height(); + + int *dstRights = static_cast(alloca((numXDivs + 1) * sizeof(int))); + bool dstRightsHaveBeenCached = false; + + int numStretchyXPixelsRemaining = 0; + for (i = 0; i < numXDivs; i += 2) + numStretchyXPixelsRemaining += m_chunkData.xDivs[i + 1] - m_chunkData.xDivs[i]; + + int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining; + int numStretchyYPixelsRemaining = 0; + for (i = 0; i < numYDivs; i += 2) + numStretchyYPixelsRemaining += m_chunkData.yDivs[i + 1] - m_chunkData.yDivs[i]; + + int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining; + src.setTop(0); + dst.setTop(bounds.top()); + // The first row always starts with the top being at y=0 and the bottom + // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case + // the first row is stretchable along the Y axis, otherwise it is fixed. + // The last row always ends with the bottom being bitmap.height and the top + // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or + // yDivs[numYDivs-1]. In the former case the last row is stretchable along + // the Y axis, otherwise it is fixed. + // + // The first and last columns are similarly treated with respect to the X + // axis. + // + // The above is to help explain some of the special casing that goes on the + // code below. + + // The initial yDiv and whether the first row is considered stretchable or + // not depends on whether yDiv[0] was zero or not. + for (j = yIsStretchable ? 1 : 0; + j <= numYDivs && src.top() < bitmapHeight; + j++, yIsStretchable = !yIsStretchable) { + src.setLeft(0); + dst.setLeft(bounds.left()); + if (j == numYDivs) { + src.setBottom(bitmapHeight); + dst.setBottom(bounds.bottom()); + } else { + src.setBottom(m_chunkData.yDivs[j]); + const int srcYSize = src.bottom() - src.top(); + if (yIsStretchable) { + dst.setBottom(dst.top() + calculateStretch(bounds.bottom(), dst.top(), + srcYSize, + numStretchyYPixelsRemaining, + numFixedYPixelsRemaining)); + numStretchyYPixelsRemaining -= srcYSize; + } else { + dst.setBottom(dst.top() + srcYSize); + numFixedYPixelsRemaining -= srcYSize; + } + } + + xIsStretchable = initialXIsStretchable; + // The initial xDiv and whether the first column is considered + // stretchable or not depends on whether xDiv[0] was zero or not. + for (i = xIsStretchable ? 1 : 0; + i <= numXDivs && src.left() < bitmapWidth; + i++, xIsStretchable = !xIsStretchable) { + color = m_chunkData.colors[colorIndex++]; + if (i == numXDivs) { + src.setRight(bitmapWidth); + dst.setRight(bounds.right()); + } else { + src.setRight(m_chunkData.xDivs[i]); + if (dstRightsHaveBeenCached) { + dst.setRight(dstRights[i]); + } else { + const int srcXSize = src.right() - src.left(); + if (xIsStretchable) { + dst.setRight(dst.left() + calculateStretch(bounds.right(), dst.left(), + srcXSize, + numStretchyXPixelsRemaining, + numFixedXPixelsRemaining)); + numStretchyXPixelsRemaining -= srcXSize; + } else { + dst.setRight(dst.left() + srcXSize); + numFixedXPixelsRemaining -= srcXSize; + } + dstRights[i] = dst.right(); + } + } + // If this horizontal patch is too small to be displayed, leave + // the destination left edge where it is and go on to the next patch + // in the source. + if (src.left() >= src.right()) { + src.setLeft(src.right()); + continue; + } + // Make sure that we actually have room to draw any bits + if (dst.right() <= dst.left() || dst.bottom() <= dst.top()) { + goto nextDiv; + } + // If this patch is transparent, skip and don't draw. + if (color == TRANSPARENT_COLOR) + goto nextDiv; + if (color != NO_COLOR) + painter->fillRect(dst, (QRgb)color); + else + painter->drawPixmap(dst, pixmap, src); +nextDiv: + src.setLeft(src.right()); + dst.setLeft(dst.right()); + } + src.setTop(src.bottom()); + dst.setTop(dst.bottom()); + dstRightsHaveBeenCached = true; + } + painter->setRenderHints(savedHints); +} + +QAndroidStyle::AndroidGradientDrawable::AndroidGradientDrawable(const QVariantMap &drawable, + QAndroidStyle::ItemType itemType) + : AndroidDrawable(drawable, itemType), m_orientation(TOP_BOTTOM) +{ + m_radius = drawable.value(QLatin1String("radius")).toInt(); + if (m_radius < 0) + m_radius = 0; + + QVariantList colors = drawable.value(QLatin1String("colors")).toList(); + QVariantList positions = drawable.value(QLatin1String("positions")).toList(); + int min=colors.size() < positions.size() ? colors.size() : positions.size(); + for (int i = 0; i < min; i++) + m_gradient.setColorAt(positions.at(i).toDouble(), (QRgb)colors.at(i).toInt()); + + QByteArray orientation=drawable.value(QLatin1String("orientation")).toByteArray(); + if (orientation == "TOP_BOTTOM") // draw the gradient from the top to the bottom + m_orientation = TOP_BOTTOM; + else if (orientation == "TR_BL") // draw the gradient from the top-right to the bottom-left + m_orientation = TR_BL; + else if (orientation == "RIGHT_LEFT") // draw the gradient from the right to the left + m_orientation = RIGHT_LEFT; + else if (orientation == "BR_TL") // draw the gradient from the bottom-right to the top-left + m_orientation = BR_TL; + else if (orientation == "BOTTOM_TOP") // draw the gradient from the bottom to the top + m_orientation = BOTTOM_TOP; + else if (orientation == "BL_TR") // draw the gradient from the bottom-left to the top-right + m_orientation = BL_TR; + else if (orientation == "LEFT_RIGHT") // draw the gradient from the left to the right + m_orientation = LEFT_RIGHT; + else if (orientation == "TL_BR") // draw the gradient from the top-left to the bottom-right + m_orientation = TL_BR; + else + qWarning("AndroidGradientDrawable: unknown orientation"); +} + +QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidGradientDrawable::type() const +{ + return QAndroidStyle::Gradient; +} + +void QAndroidStyle::AndroidGradientDrawable::draw(QPainter *painter, const QStyleOption *opt) const +{ + const int width = opt->rect.width(); + const int height = opt->rect.height(); + switch (m_orientation) { + case TOP_BOTTOM: + // draw the gradient from the top to the bottom + m_gradient.setStart(width/2,0); + m_gradient.setFinalStop(width/2,height); + break; + case TR_BL: + // draw the gradient from the top-right to the bottom-left + m_gradient.setStart(width,0); + m_gradient.setFinalStop(0,height); + break; + case RIGHT_LEFT: + // draw the gradient from the right to the left + m_gradient.setStart(width,height/2); + m_gradient.setFinalStop(0,height/2); + break; + case BR_TL: + // draw the gradient from the bottom-right to the top-left + m_gradient.setStart(width,height); + m_gradient.setFinalStop(0,0); + break; + case BOTTOM_TOP: + // draw the gradient from the bottom to the top + m_gradient.setStart(width/2,height); + m_gradient.setFinalStop(width/2,0); + break; + case BL_TR: + // draw the gradient from the bottom-left to the top-right + m_gradient.setStart(0,height); + m_gradient.setFinalStop(width,0); + break; + case LEFT_RIGHT: + // draw the gradient from the left to the right + m_gradient.setStart(0,height/2); + m_gradient.setFinalStop(width,height/2); + break; + case TL_BR: + // draw the gradient from the top-left to the bottom-right + m_gradient.setStart(0,0); + m_gradient.setFinalStop(width,height); + break; + } + + const QBrush &oldBrush = painter->brush(); + const QPen oldPen = painter->pen(); + painter->setPen(Qt::NoPen); + painter->setBrush(m_gradient); + painter->drawRoundedRect(opt->rect, m_radius, m_radius); + painter->setBrush(oldBrush); + painter->setPen(oldPen); +} + +QSize QAndroidStyle::AndroidGradientDrawable::size() const +{ + return QSize(m_radius*2, m_radius*2); +} + +QAndroidStyle::AndroidClipDrawable::AndroidClipDrawable(const QVariantMap &drawable, + QAndroidStyle::ItemType itemType) + : AndroidDrawable(drawable, itemType) +{ + m_drawable = fromMap(drawable.value(QLatin1String("drawable")).toMap(), itemType); + m_factor = 0; + m_orientation = Qt::Horizontal; +} + +QAndroidStyle::AndroidClipDrawable::~AndroidClipDrawable() +{ + delete m_drawable; +} + +QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidClipDrawable::type() const +{ + return QAndroidStyle::Clip; +} + +void QAndroidStyle::AndroidClipDrawable::setFactor(double factor, Qt::Orientation orientation) +{ + m_factor = factor; + m_orientation = orientation; +} + +void QAndroidStyle::AndroidClipDrawable::draw(QPainter *painter, const QStyleOption *opt) const +{ + QStyleOption copy(*opt); + if (m_orientation == Qt::Horizontal) + copy.rect.setWidth(copy.rect.width()*m_factor); + else + copy.rect.setHeight(copy.rect.height()*m_factor); + + m_drawable->draw(painter, ©); +} + +QAndroidStyle::AndroidStateDrawable::AndroidStateDrawable(const QVariantMap &drawable, + QAndroidStyle::ItemType itemType) + : AndroidDrawable(drawable, itemType) +{ + QVariantList states = drawable.value(QLatin1String("stateslist")).toList(); + foreach (QVariant stateVariant, states) { + QVariantMap state = stateVariant.toMap(); + const int s = extractState(state.value(QLatin1String("states")).toMap()); + if (-1 == s) + continue; + const AndroidDrawable *ad = fromMap(state.value(QLatin1String("drawable")).toMap(), itemType); + if (!ad) + continue; + StateType item; + item.first = s; + item.second = ad; + m_states<draw(painter, opt); +} + +const QAndroidStyle::AndroidDrawable* QAndroidStyle::AndroidStateDrawable::bestAndroidStateMatch(const QStyleOption *opt) const +{ + const AndroidDrawable *bestMatch = 0; + if (!opt) { + if (m_states.size()) + return m_states[0].second; + return bestMatch; + } + + uint bestCost=0xffff; + foreach (const StateType & state, m_states) { + if (int(opt->state) == state.first) + return state.second; + uint cost = 0; + + int difference = int(opt->state^state.first); + + if (difference & QStyle::State_Active) + cost += 1000; + + if (difference & QStyle::State_Enabled) + cost += 1000; + + if ((m_itemType == QC_Button || m_itemType == QC_EditText) && (difference & QStyle::State_Raised)) + cost += 1000; + + if ((m_itemType == QC_Button || m_itemType == QC_EditText) && (difference & QStyle::State_Sunken)) + cost += 1000; + + if (difference & QStyle::State_Off) + cost += 1000; + + if (difference & QStyle::State_On) + cost += 1000; + + if (difference & QStyle::State_HasFocus) + cost += 1000; + + if (difference & QStyle::State_Selected) + cost += 1000; + + if (cost < bestCost) { + bestCost = cost; + bestMatch = state.second; + } + } + return bestMatch; +} + +int QAndroidStyle::AndroidStateDrawable::extractState(const QVariantMap &value) +{ + int state = QStyle::State_None; + foreach (const QString key, value.keys()) { + bool val = value.value(key).toString() == QLatin1String("true"); + if (key == QLatin1String("enabled") && val) { + state |= QStyle::State_Enabled; + continue; + } + + if (key == QLatin1String("window_focused") && val) { + state |= QStyle::State_Active; + continue; + } + + if (key == QLatin1String("focused") && val) { + state |= QStyle::State_HasFocus; + continue; + } + + if (key == QLatin1String("checked")) { + state |= val ? QStyle::State_On : QStyle::State_Off; + continue; + } + + if (key == QLatin1String("pressed")) { + state |= val ? QStyle::State_Raised : QStyle::State_Sunken; + state |= QStyle::State_Enabled | QStyle::State_HasFocus; + continue; + } + + if (key == QLatin1String("selected") && val) { + state |= QStyle::State_Selected; + state |= QStyle::State_Enabled | QStyle::State_HasFocus; + continue; + } + + if (key == QLatin1String("active") && val) { + state |= QStyle::State_Active; + continue; + } + + // Keep misspelling for compatibility + if (key == QLatin1String("backgroud") && val) + return -1; + } + return state; +} + +QAndroidStyle::AndroidLayerDrawable::AndroidLayerDrawable(const QVariantMap &drawable, + QAndroidStyle::ItemType itemType) + : AndroidDrawable(drawable, itemType) +{ + QVariantList layers = drawable.value(QLatin1String("layers")).toList(); + foreach (QVariant layer, layers) { + QVariantMap layerMap = layer.toMap(); + AndroidDrawable *ad = fromMap(layerMap, itemType); + if (ad) { + LayerType l; + l.second = ad; + l.first = layerMap.value(QLatin1String("id")).toInt(); + m_layers << l; + } + } +} + +QAndroidStyle::AndroidLayerDrawable::~AndroidLayerDrawable() +{ + foreach (const LayerType &layer, m_layers) + delete layer.second; +} + +QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidLayerDrawable::type() const +{ + return QAndroidStyle::Layer; +} + +void QAndroidStyle::AndroidLayerDrawable::draw(QPainter *painter, const QStyleOption *opt) const +{ + foreach (const LayerType &layer, m_layers) + layer.second->draw(painter, opt); +} + +QAndroidStyle::AndroidDrawable *QAndroidStyle::AndroidLayerDrawable::layer(int id) const +{ + foreach (const LayerType &layer, m_layers) + if (layer.first == id) + return layer.second; + return 0; +} + +QSize QAndroidStyle::AndroidLayerDrawable::size() const +{ + QSize sz; + foreach (const LayerType &layer, m_layers) + sz = sz.expandedTo(layer.second->size()); + return sz; +} + +QAndroidStyle::AndroidControl::AndroidControl(const QVariantMap &control, + QAndroidStyle::ItemType itemType) +{ + QVariantMap::const_iterator it = control.find(QLatin1String("View_background")); + if (it != control.end()) + m_background = AndroidDrawable::fromMap(it.value().toMap(), itemType); + else + m_background = 0; + + it = control.find(QLatin1String("View_minWidth")); + if (it!=control.end()) + m_minSize.setWidth(it.value().toInt()); + + it = control.find(QLatin1String("View_minHeight")); + if (it != control.end()) + m_minSize.setHeight(it.value().toInt()); + + it = control.find(QLatin1String("View_maxWidth")); + if (it != control.end()) + m_maxSize.setWidth(it.value().toInt()); + + it = control.find(QLatin1String("View_maxHeight")); + if (it != control.end()) + m_maxSize.setHeight(it.value().toInt()); +} + +QAndroidStyle::AndroidControl::~AndroidControl() +{ + delete m_background; +} + +void QAndroidStyle::AndroidControl::drawControl(const QStyleOption *opt, QPainter *p, const QWidget */*w*/) +{ + if (m_background) + m_background->draw(p, opt); +} + +QRect QAndroidStyle::AndroidControl::subElementRect(QStyle::SubElement /*subElement*/, + const QStyleOption *option, + const QWidget */*widget*/) const +{ + if (const AndroidDrawable *drawable=m_background) { + if (drawable->type() == State) + drawable = static_cast(m_background)->bestAndroidStateMatch(option); + + const QMargins &padding = drawable->padding(); + + QRect r = option->rect.adjusted(padding.left(), padding.top(), + -padding.right(), -padding.bottom()); + + if (r.width() < m_minSize.width()) + r.setWidth(m_minSize.width()); + + if (r.height() < m_minSize.height()) + r.setHeight(m_minSize.height()); + + return visualRect(option->direction, option->rect, r); + } + return option->rect; + +} + +QRect QAndroidStyle::AndroidControl::subControlRect(const QStyleOptionComplex *option, + QStyle::SubControl /*sc*/, + const QWidget *widget) const +{ + return subElementRect(QStyle::SE_CustomBase, option, widget); +} + +QSize QAndroidStyle::AndroidControl::sizeFromContents(const QStyleOption *opt, + const QSize &contentsSize, + const QWidget */*w*/) const +{ + QSize sz; + if (const AndroidDrawable *drawable=m_background) { + + if (drawable->type() == State) + drawable = static_cast(m_background)->bestAndroidStateMatch(opt); + const QMargins &padding = drawable->padding(); + sz.setWidth(padding.left() + padding.right()); + sz.setHeight(padding.top() + padding.bottom()); + if (sz.isEmpty()) + sz = drawable->size(); + } + sz += contentsSize; + if (contentsSize.height() < opt->fontMetrics.height()) + sz.setHeight(sz.height() + (opt->fontMetrics.height() - contentsSize.height())); + if (sz.height() < m_minSize.height()) + sz.setHeight(m_minSize.height()); + if (sz.width() < m_minSize.width()) + sz.setWidth(m_minSize.width()); + return sz; +} + +QMargins QAndroidStyle::AndroidControl::padding() +{ + if (const AndroidDrawable *drawable = m_background) + { + if (drawable->type() == State) + drawable=static_cast(m_background)->bestAndroidStateMatch(0); + return drawable->padding(); + } + return QMargins(); +} + +QAndroidStyle::AndroidCompoundButtonControl::AndroidCompoundButtonControl(const QVariantMap &control, + ItemType itemType) + : AndroidControl(control, itemType) +{ + QVariantMap::const_iterator it = control.find(QLatin1String("CompoundButton_button")); + if (it != control.end()) + m_button = AndroidDrawable::fromMap(it.value().toMap(), itemType); + else + m_button = 0; +} + +QAndroidStyle::AndroidCompoundButtonControl::~AndroidCompoundButtonControl() +{ + delete m_button; +} + +void QAndroidStyle::AndroidCompoundButtonControl::drawControl(const QStyleOption *opt, + QPainter *p, + const QWidget *w) +{ + AndroidControl::drawControl(opt, p, w); + if (m_button) + m_button->draw(p, opt); +} + +QAndroidStyle::AndroidProgressBarControl::AndroidProgressBarControl(const QVariantMap &control, + ItemType itemType) + : AndroidControl(control, itemType) +{ + QVariantMap::const_iterator it = control.find(QLatin1String("ProgressBar_indeterminateDrawable")); + if (it != control.end()) + m_indeterminateDrawable = AndroidDrawable::fromMap(it.value().toMap(), itemType); + else + m_indeterminateDrawable = 0; + + it = control.find(QLatin1String("ProgressBar_progressDrawable")); + if (it != control.end()) + m_progressDrawable = AndroidDrawable::fromMap(it.value().toMap(), itemType); + else + m_progressDrawable = 0; + + it = control.find(QLatin1String("ProgressBar_progress_id")); + if (it != control.end()) + m_progressId = it.value().toInt(); + + it = control.find(QLatin1String("ProgressBar_secondaryProgress_id")); + if (it != control.end()) + m_secondaryProgress_id = it.value().toInt(); + + it = control.find(QLatin1String("ProgressBar_minWidth")); + if (it != control.end()) + m_minSize.setWidth(it.value().toInt()); + + it = control.find(QLatin1String("ProgressBar_minHeight")); + if (it != control.end()) + m_minSize.setHeight(it.value().toInt()); + + it = control.find(QLatin1String("ProgressBar_maxWidth")); + if (it != control.end()) + m_maxSize.setWidth(it.value().toInt()); + + it = control.find(QLatin1String("ProgressBar_maxHeight")); + if (it != control.end()) + m_maxSize.setHeight(it.value().toInt()); +} + +QAndroidStyle::AndroidProgressBarControl::~AndroidProgressBarControl() +{ + delete m_progressDrawable; + delete m_indeterminateDrawable; +} + +void QAndroidStyle::AndroidProgressBarControl::drawControl(const QStyleOption *option, QPainter *p, const QWidget */*w*/) +{ + if (!m_progressDrawable) + return; + + if (const QStyleOptionProgressBar *progressBarOption = + qstyleoption_cast(option)) { + QStyleOptionProgressBarV2 progressBarV2(*progressBarOption); + if (m_progressDrawable->type() == QAndroidStyle::Layer) { + QAndroidStyle::AndroidDrawable *clipDrawable = static_cast(m_progressDrawable)->layer(m_progressId); + if (clipDrawable->type() == QAndroidStyle::Clip) + static_cast(clipDrawable)->setFactor(double(progressBarV2.progress/(progressBarV2.maximum-progressBarV2.minimum)), + progressBarV2.orientation); + } + m_progressDrawable->draw(p, option); + } +} + +QRect QAndroidStyle::AndroidProgressBarControl::subElementRect(QStyle::SubElement subElement, + const QStyleOption *option, + const QWidget *widget) const +{ + if (const QStyleOptionProgressBar *progressBarOption = + qstyleoption_cast(option)) { + QStyleOptionProgressBarV2 progressBarV2(*progressBarOption); + const bool horizontal = progressBarV2.orientation == Qt::Vertical; + if (!m_background) + return option->rect; + + QMargins padding = m_background->padding(); + QRect p(padding.left(), padding.top(), padding.right()-padding.left(), padding.bottom()-padding.top()); + padding = m_indeterminateDrawable->padding(); + p |= QRect(padding.left(), padding.top(), padding.right()-padding.left(), padding.bottom()-padding.top()); + padding = m_progressDrawable->padding(); + p |= QRect(padding.left(), padding.top(), padding.right()-padding.left(), padding.bottom()-padding.top()); + + QRect r = option->rect.adjusted(p.left(), p.top(), -p.right(), -p.bottom()); + + if (horizontal) { + if (r.height()m_maxSize.height()) + r.setHeight(m_maxSize.height()); + } else { + if (r.width()m_maxSize.width()) + r.setWidth(m_maxSize.width()); + } + return visualRect(option->direction, option->rect, r); + } + return AndroidControl::subElementRect(subElement, option, widget); +} + +QSize QAndroidStyle::AndroidProgressBarControl::sizeFromContents(const QStyleOption *opt, + const QSize &contentsSize, + const QWidget */*w*/) const +{ + QSize sz(contentsSize); + if (sz.height() < m_minSize.height()) + sz.setHeight(m_minSize.height()); + if (sz.width() < m_minSize.width()) + sz.setWidth(m_minSize.width()); + + if (const QStyleOptionProgressBar *progressBarOption = + qstyleoption_cast(opt)) { + QStyleOptionProgressBarV2 progressBarV2(*progressBarOption); + if (progressBarV2.orientation == Qt::Vertical) { + if (sz.height() > m_maxSize.height()) + sz.setHeight(m_maxSize.height()); + } else { + if (sz.width() > m_maxSize.width()) + sz.setWidth(m_maxSize.width()); + } + } + return contentsSize; +} + +QAndroidStyle::AndroidSeekBarControl::AndroidSeekBarControl(const QVariantMap &control, + ItemType itemType) + : AndroidProgressBarControl(control, itemType) +{ + QVariantMap::const_iterator it = control.find(QLatin1String("SeekBar_thumb")); + if (it != control.end()) + m_seekBarThumb = AndroidDrawable::fromMap(it.value().toMap(), itemType); + else + m_seekBarThumb = 0; +} + +QAndroidStyle::AndroidSeekBarControl::~AndroidSeekBarControl() +{ + delete m_seekBarThumb; +} + +void QAndroidStyle::AndroidSeekBarControl::drawControl(const QStyleOption *option, + QPainter *p, + const QWidget */*w*/) +{ + if (!m_seekBarThumb || !m_progressDrawable) + return; + + if (const QStyleOptionSlider *styleOption = + qstyleoption_cast(option)) { + double factor = double(styleOption->sliderPosition/(styleOption->maximum-styleOption->minimum)); + if (m_progressDrawable->type()==QAndroidStyle::Layer) { + QAndroidStyle::AndroidDrawable *clipDrawable = static_cast(m_progressDrawable)->layer(m_progressId); + if (clipDrawable->type() == QAndroidStyle::Clip) + static_cast(clipDrawable)->setFactor(factor, styleOption->orientation); + } + const AndroidDrawable *drawable=m_seekBarThumb; + if (drawable->type() == State) + drawable = static_cast(m_seekBarThumb)->bestAndroidStateMatch(option); + QStyleOption copy(*option); + copy.rect.setY((copy.rect.height()-m_minSize.height())/2); + copy.rect.setHeight(m_minSize.height()); + copy.rect.setWidth(copy.rect.width()-drawable->size().width()); + copy.rect.translate(drawable->size().width()/2,0); + m_progressDrawable->draw(p, ©); + if (styleOption->orientation == Qt::Vertical) + qCritical() << "Vertical slider are not supported"; + int pos = (double(copy.rect.width()*factor - drawable->size().width()) / 2); + copy.rect.translate(pos, 0); + copy.rect.setSize(drawable->size()); + m_seekBarThumb->draw(p, ©); + } +} + +QSize QAndroidStyle::AndroidSeekBarControl::sizeFromContents(const QStyleOption *opt, + const QSize &contentsSize, + const QWidget *w) const +{ + QSize sz = AndroidProgressBarControl::sizeFromContents(opt, contentsSize, w); + if (!m_seekBarThumb) + return sz; + const AndroidDrawable *drawable=m_seekBarThumb; + if (drawable->type() == State) + drawable = static_cast(m_seekBarThumb)->bestAndroidStateMatch(opt); + return sz.expandedTo(drawable->size()); +} + +QRect QAndroidStyle::AndroidSeekBarControl::subControlRect(const QStyleOptionComplex *option, + SubControl sc, + const QWidget */*widget*/) const +{ + const QStyleOptionSlider *styleOption = + qstyleoption_cast(option); + + if (m_seekBarThumb && sc == SC_SliderHandle && styleOption) { + const AndroidDrawable *drawable = m_seekBarThumb; + if (drawable->type() == State) + drawable = static_cast(m_seekBarThumb)->bestAndroidStateMatch(option); + + QRect r(option->rect); + double factor = double(styleOption->sliderPosition/(styleOption->maximum-styleOption->minimum)); + int pos=(double(option->rect.width()*factor - drawable->size().width()) / 2); + r.setX(r.x()+pos); + r.setSize(drawable->size()); + return r; + } + return option->rect; +} + +QAndroidStyle::AndroidSpinnerControl::AndroidSpinnerControl(const QVariantMap &control, + QAndroidStyle::ItemType itemType) + : AndroidControl(control, itemType) +{} + +QRect QAndroidStyle::AndroidSpinnerControl::subControlRect(const QStyleOptionComplex *option, + SubControl sc, + const QWidget *widget) const +{ + if (sc == QStyle::SC_ComboBoxListBoxPopup) + return option->rect; + return AndroidControl::subControlRect(option, sc, widget); +} + +QT_END_NAMESPACE + +#endif // !defined(QT_NO_STYLE_ANDROID) || defined(QT_PLUGIN) diff --git a/src/widgets/styles/qandroidstyle_p.h b/src/widgets/styles/qandroidstyle_p.h new file mode 100644 index 0000000000..35fa61983a --- /dev/null +++ b/src/widgets/styles/qandroidstyle_p.h @@ -0,0 +1,382 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtWidgets module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDSTYLE_P_H +#define QANDROIDSTYLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qstylefactory.cpp. This header may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_STYLE_ANDROID) + +class Q_GUI_EXPORT QAndroidStyle : public QCommonStyle +{ + Q_OBJECT + +public: + enum ItemType + { + QC_UnknownType = -1, + QC_View, + QC_GroupBox, + QC_Button, + QC_Checkbox, + QC_RadioButton, + QC_Slider, + QC_Switch, + QC_EditText, + QC_Combobox, + QC_BusyIndicator, + QC_ProgressBar, + QC_Tab, + QC_TabButton, + QC_RatingIndicator, + QC_SearchBox, + QC_CustomCOntrol=0xf00, + QC_ControlMask=0xfff + }; + + struct Android9PatchChunk + { + QVector xDivs; + QVector yDivs; + QVector colors; + }; + + struct AndroidItemStateInfo + { + AndroidItemStateInfo():state(0){} + int state; + QByteArray filePath; + QByteArray hashKey; + Android9PatchChunk chunkData; + QSize size; + QMargins padding; + }; + + enum AndroidDrawableType + { + Color, + Image, + Clip, + NinePatch, + Gradient, + State, + Layer + }; + + class AndroidDrawable + { + public: + AndroidDrawable(const QVariantMap &drawable, ItemType itemType); + virtual ~AndroidDrawable(); + virtual void initPadding(const QVariantMap &drawable); + virtual AndroidDrawableType type() const = 0; + virtual void draw(QPainter *painter,const QStyleOption *opt) const = 0; + const QMargins &padding() const; + virtual QSize size() const; + static AndroidDrawable *fromMap(const QVariantMap &drawable, ItemType itemType); + static QMargins extractMargins(const QVariantMap &value); + protected: + ItemType m_itemType; + QMargins m_padding; + }; + + class AndroidColorDrawable: public AndroidDrawable + { + public: + AndroidColorDrawable(const QVariantMap &drawable, ItemType itemType); + virtual AndroidDrawableType type() const; + virtual void draw(QPainter *painter,const QStyleOption *opt) const; + + protected: + QColor m_color; + }; + + class AndroidImageDrawable: public AndroidDrawable + { + public: + AndroidImageDrawable(const QVariantMap &drawable, ItemType itemType); + virtual AndroidDrawableType type() const; + virtual void draw(QPainter *painter,const QStyleOption *opt) const; + virtual QSize size() const; + + protected: + QString m_filePath; + mutable QString m_hashKey; + QSize m_size; + }; + + class Android9PatchDrawable: public AndroidImageDrawable + { + public: + Android9PatchDrawable(const QVariantMap &drawable, ItemType itemType); + virtual AndroidDrawableType type() const; + virtual void draw(QPainter *painter, const QStyleOption *opt) const; + private: + static int calculateStretch(int boundsLimit, int startingPoint, + int srcSpace, int numStrechyPixelsRemaining, + int numFixedPixelsRemaining); + void extractIntArray(const QVariantList &values, QVector &array); + private: + Android9PatchChunk m_chunkData; + }; + + class AndroidGradientDrawable: public AndroidDrawable + { + public: + enum GradientOrientation + { + TOP_BOTTOM, + TR_BL, + RIGHT_LEFT, + BR_TL, + BOTTOM_TOP, + BL_TR, + LEFT_RIGHT, + TL_BR + }; + + public: + AndroidGradientDrawable(const QVariantMap &drawable, ItemType itemType); + virtual AndroidDrawableType type() const; + virtual void draw(QPainter *painter, const QStyleOption *opt) const; + QSize size() const; + private: + mutable QLinearGradient m_gradient; + GradientOrientation m_orientation; + int m_radius; + }; + + class AndroidClipDrawable: public AndroidDrawable + { + public: + AndroidClipDrawable(const QVariantMap &drawable, ItemType itemType); + ~AndroidClipDrawable(); + virtual AndroidDrawableType type() const; + virtual void setFactor(double factor, Qt::Orientation orientation); + virtual void draw(QPainter *painter, const QStyleOption *opt) const; + + private: + double m_factor; + Qt::Orientation m_orientation; + const AndroidDrawable *m_drawable; + }; + + class AndroidStateDrawable: public AndroidDrawable + { + public: + AndroidStateDrawable(const QVariantMap &drawable, ItemType itemType); + ~AndroidStateDrawable(); + virtual AndroidDrawableType type() const; + virtual void draw(QPainter *painter, const QStyleOption *opt) const; + inline const AndroidDrawable *bestAndroidStateMatch(const QStyleOption *opt) const; + static int extractState(const QVariantMap &value); + + private: + typedef QPair StateType; + QList m_states; + }; + + class AndroidLayerDrawable: public AndroidDrawable + { + public: + AndroidLayerDrawable(const QVariantMap &drawable, QAndroidStyle::ItemType itemType); + ~AndroidLayerDrawable(); + virtual AndroidDrawableType type() const; + virtual void draw(QPainter *painter, const QStyleOption *opt) const; + AndroidDrawable *layer(int id) const; + QSize size() const; + private: + typedef QPair LayerType; + QList m_layers; + }; + + class AndroidControl + { + public: + AndroidControl(const QVariantMap &control, ItemType itemType); + virtual ~AndroidControl(); + virtual void drawControl(const QStyleOption *opt, QPainter *p, const QWidget *w); + virtual QRect subElementRect(SubElement subElement, + const QStyleOption *option, + const QWidget *widget = 0) const; + virtual QRect subControlRect(const QStyleOptionComplex *option, + SubControl sc, + const QWidget *widget = 0) const; + virtual QSize sizeFromContents(const QStyleOption *opt, + const QSize &contentsSize, + const QWidget *w) const; + virtual QMargins padding(); + protected: + const AndroidDrawable *m_background; + QSize m_minSize; + QSize m_maxSize; + }; + + class AndroidCompoundButtonControl : public AndroidControl + { + public: + AndroidCompoundButtonControl(const QVariantMap &control, ItemType itemType); + virtual ~AndroidCompoundButtonControl(); + virtual void drawControl(const QStyleOption *opt, QPainter *p, const QWidget *w); + + protected: + const AndroidDrawable *m_button; + }; + + class AndroidProgressBarControl : public AndroidControl + { + public: + AndroidProgressBarControl(const QVariantMap &control, ItemType itemType); + virtual ~AndroidProgressBarControl(); + virtual void drawControl(const QStyleOption *option, QPainter *p, const QWidget *w); + virtual QRect subElementRect(SubElement subElement, + const QStyleOption *option, + const QWidget *widget = 0) const; + + QSize sizeFromContents(const QStyleOption *opt, + const QSize &contentsSize, + const QWidget *w) const; + protected: + AndroidDrawable *m_progressDrawable; + AndroidDrawable *m_indeterminateDrawable; + int m_secondaryProgress_id; + int m_progressId; + }; + + class AndroidSeekBarControl : public AndroidProgressBarControl + { + public: + AndroidSeekBarControl(const QVariantMap &control, ItemType itemType); + virtual ~AndroidSeekBarControl(); + virtual void drawControl(const QStyleOption *option, QPainter *p, const QWidget *w); + QSize sizeFromContents(const QStyleOption *opt, + const QSize &contentsSize, const QWidget *w) const; + QRect subControlRect(const QStyleOptionComplex *option, SubControl sc, + const QWidget *widget = 0) const; + private: + AndroidDrawable *m_seekBarThumb; + }; + + class AndroidSpinnerControl : public AndroidControl + { + public: + AndroidSpinnerControl(const QVariantMap &control, ItemType itemType); + virtual ~AndroidSpinnerControl(){} + virtual QRect subControlRect(const QStyleOptionComplex *option, + SubControl sc, + const QWidget *widget = 0) const; + }; + + typedef QList AndroidItemStateInfoList; + +public: + QAndroidStyle(); + ~QAndroidStyle(); + + virtual void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p, + const QWidget *w = 0) const; + + virtual void drawControl(QStyle::ControlElement element, const QStyleOption *opt, QPainter *p, + const QWidget *w = 0) const; + + virtual QRect subElementRect(SubElement subElement, const QStyleOption *option, + const QWidget *widget = 0) const; + virtual void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, + const QWidget *widget = 0) const; + virtual SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, + const QPoint &pt, const QWidget *widget = 0) const; + virtual QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, + SubControl sc, const QWidget *widget = 0) const; + + virtual int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, + const QWidget *widget = 0) const; + + virtual QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, + const QSize &contentsSize, const QWidget *w = 0) const; + + virtual QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt = 0, + const QWidget *widget = 0) const; + + virtual QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, + const QStyleOption *opt) const; + + virtual QPalette standardPalette() const; +private: + Q_DISABLE_COPY(QAndroidStyle) + static ItemType qtControl(QStyle::ComplexControl control); + static ItemType qtControl(QStyle::ContentsType contentsType); + static ItemType qtControl(QStyle::ControlElement controlElement); + static ItemType qtControl(QStyle::PrimitiveElement primitiveElement); + static ItemType qtControl(QStyle::SubElement subElement); + static ItemType qtControl(const QString &android); + + static void setPaletteColor(const QVariantMap &object, + QPalette &palette, + QPalette::ColorRole role); +private: + typedef QHash AndroidControlsHash; + AndroidControlsHash m_androidControlsHash; + QPalette m_standardPalette; +}; + +#endif // QT_NO_STYLE_ANDROID + +QT_END_NAMESPACE + +#endif // QANDROIDSTYLE_P_H diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 1a41b6dbaa..52c733b8ec 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -5100,7 +5100,11 @@ int QCommonStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget ret = theme->themeHint(QPlatformTheme::ToolButtonStyle).toInt(); break; case SH_RequestSoftwareInputPanel: +#ifdef Q_OS_ANDROID + ret = RSIP_OnMouseClick; +#else ret = RSIP_OnMouseClickAndAlreadyFocused; +#endif break; case SH_ScrollBar_Transient: ret = false; diff --git a/src/widgets/styles/qstylefactory.cpp b/src/widgets/styles/qstylefactory.cpp index 471d3b748d..5028371e23 100644 --- a/src/widgets/styles/qstylefactory.cpp +++ b/src/widgets/styles/qstylefactory.cpp @@ -48,6 +48,9 @@ #include "qwindowsstyle_p.h" #ifndef QT_NO_STYLE_FUSION #include "qfusionstyle_p.h" +#ifndef QT_NO_STYLE_ANDROID +#include "qandroidstyle_p.h" +#endif #endif #ifndef QT_NO_STYLE_GTK #include "qgtkstyle_p.h" @@ -143,6 +146,11 @@ QStyle *QStyleFactory::create(const QString& key) ret = new QFusionStyle; else #endif +#ifndef QT_NO_STYLE_ANDROID + if (style == QLatin1String("android")) + ret = new QAndroidStyle; + else +#endif #ifndef QT_NO_STYLE_GTK if (style == QLatin1String("gtk") || style == QLatin1String("gtk+")) ret = new QGtkStyle; @@ -206,6 +214,10 @@ QStringList QStyleFactory::keys() (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based))) list << QLatin1String("WindowsVista"); #endif +#ifndef QT_NO_STYLE_ANDROID + if (!list.contains(QLatin1String("Android"))) + list << QLatin1String("Android"); +#endif #ifndef QT_NO_STYLE_GTK if (!list.contains(QLatin1String("GTK+"))) list << QLatin1String("GTK+"); diff --git a/src/widgets/styles/styles.pri b/src/widgets/styles/styles.pri index b15eb1fa48..9f23fb30cc 100644 --- a/src/widgets/styles/styles.pri +++ b/src/widgets/styles/styles.pri @@ -136,3 +136,10 @@ contains( styles, windowsmobile ) { } else { DEFINES += QT_NO_STYLE_WINDOWSMOBILE } + +contains( styles, android ) { + HEADERS += styles/qandroidstyle_p.h + SOURCES += styles/qandroidstyle.cpp +} else { + DEFINES += QT_NO_STYLE_ANDROID +} diff --git a/src/widgets/widgets/qtextbrowser.cpp b/src/widgets/widgets/qtextbrowser.cpp index 61e7a5fbfb..b4158e3b8d 100644 --- a/src/widgets/widgets/qtextbrowser.cpp +++ b/src/widgets/widgets/qtextbrowser.cpp @@ -151,12 +151,18 @@ public: QString QTextBrowserPrivate::findFile(const QUrl &name) const { QString fileName; - if (name.scheme() == QLatin1String("qrc")) + if (name.scheme() == QLatin1String("qrc")) { fileName = QLatin1String(":/") + name.path(); - else if (name.scheme().isEmpty()) + } else if (name.scheme().isEmpty()) { fileName = name.path(); - else - fileName = name.toLocalFile(); + } else { +#if defined(Q_OS_ANDROID) + if (name.scheme() == QLatin1String("assets")) + fileName = QLatin1String("assets:") + name.path(); + else +#endif + fileName = name.toLocalFile(); + } if (QFileInfo(fileName).isAbsolute()) return fileName; @@ -217,13 +223,14 @@ void QTextBrowserPrivate::_q_activateAnchor(const QString &href) textOrSourceChanged = false; #ifndef QT_NO_DESKTOPSERVICES - if ((openExternalLinks - && url.scheme() != QLatin1String("file") - && url.scheme() != QLatin1String("qrc") - && !url.isRelative()) - || (url.isRelative() && !currentURL.isRelative() - && currentURL.scheme() != QLatin1String("file") - && currentURL.scheme() != QLatin1String("qrc"))) { + bool isFileScheme = + url.scheme() == QLatin1String("file") +#if defined(Q_OS_ANDROID) + || url.scheme() == QLatin1String("assets") +#endif + || url.scheme() == QLatin1String("qrc"); + if ((openExternalLinks && !isFileScheme && !url.isRelative()) + || (url.isRelative() && !currentURL.isRelative() && !isFileScheme)) { QDesktopServices::openUrl(url); return; } -- cgit v1.2.3