├── qtandroidrunner ├── qtandroidrunner64.png ├── qtandroidrunner80.png ├── qml │ └── qtandroidrunner │ │ └── main.qml ├── README.md ├── qtandroidrunner.pro ├── android │ └── src │ │ └── com │ │ └── github │ │ └── qt │ │ └── QtRunner.java ├── qtquick2applicationviewer │ ├── qtquick2applicationviewer.h │ ├── qtquick2applicationviewer.cpp │ └── qtquick2applicationviewer.pri ├── qtandroidrunner.h ├── main.cpp └── qtandroidrunner.cpp ├── androidmetricdemo ├── androidmetricdemo.png ├── androidmetricdemo80.png ├── qml │ └── androidmetricdemo │ │ ├── android.js │ │ ├── splash.qml │ │ └── main.qml ├── README.md ├── androidmetricdemo.pro ├── qtquick2applicationviewer │ ├── qtquick2applicationviewer.h │ ├── qtquick2applicationviewer.cpp │ └── qtquick2applicationviewer.pri └── main.cpp ├── README.md └── .travis.yml /qtandroidrunner/qtandroidrunner64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benlau/qtandroidexamplecode/HEAD/qtandroidrunner/qtandroidrunner64.png -------------------------------------------------------------------------------- /qtandroidrunner/qtandroidrunner80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benlau/qtandroidexamplecode/HEAD/qtandroidrunner/qtandroidrunner80.png -------------------------------------------------------------------------------- /androidmetricdemo/androidmetricdemo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benlau/qtandroidexamplecode/HEAD/androidmetricdemo/androidmetricdemo.png -------------------------------------------------------------------------------- /androidmetricdemo/androidmetricdemo80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benlau/qtandroidexamplecode/HEAD/androidmetricdemo/androidmetricdemo80.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Example code of Qt for Android 2 | ============================== 3 | 4 | androidmetricdemo - Using DP in QML 5 | qtandroidrunner - Run specific code on UI thread 6 | -------------------------------------------------------------------------------- /androidmetricdemo/qml/androidmetricdemo/android.js: -------------------------------------------------------------------------------- 1 | .pragma library 2 | 3 | /* Android Context - A helper library for Android 4 | */ 5 | 6 | /* Density-independent pixel (dp) */ 7 | 8 | var dp = 1; 9 | 10 | function init(context) { 11 | dp = context.density 12 | } 13 | 14 | -------------------------------------------------------------------------------- /androidmetricdemo/README.md: -------------------------------------------------------------------------------- 1 | An example Qt program of using DP for Android platform 2 | ------------------------------------------------------ 3 | 4 | 5 | This example project will not be maintained any more. It is moved to [benlau/quickandroid](https://github.com/benlau/quickandroid) 6 | 7 | -------------------------------------------------------------------------------- /qtandroidrunner/qml/qtandroidrunner/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Rectangle { 4 | width: 360 5 | height: 360 6 | Text { 7 | text: qsTr("Hello World") 8 | anchors.centerIn: parent 9 | } 10 | MouseArea { 11 | anchors.fill: parent 12 | onClicked: { 13 | Qt.quit(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /qtandroidrunner/README.md: -------------------------------------------------------------------------------- 1 | Example code of QtAndroidRunner + WebView 2 | ========================================= 3 | 4 | It is an example code to demonstrate how to run C++ code 5 | on Android application UI thread. It has implemented a 6 | class called QtAndroidRunner to make it easy. 7 | 8 | The example code will create a native WebView on top 9 | of QtSurface 10 | 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language : cpp 2 | env: 3 | - DISPLAY=:99.0 4 | compiler: 5 | - gcc 6 | sudo: false 7 | addons: 8 | apt: 9 | packages: 10 | - wget 11 | before_install: 12 | - sh -e /etc/init.d/xvfb start 13 | script: 14 | - wget --no-check-certificate https://download.qt.io/archive/qt/5.5/5.5.1/qt-opensource-linux-x64-android-5.5.1.run 15 | - chmod u+x qt-opensource-linux-x64-android-5.5.1.run 16 | - ./qt-opensource-linux-x64-android-5.5.1.run --dump-binary-data -o QT 17 | - ls QT 18 | 19 | -------------------------------------------------------------------------------- /androidmetricdemo/androidmetricdemo.pro: -------------------------------------------------------------------------------- 1 | # Add more folders to ship with the application, here 2 | folder_01.source = qml/androidmetricdemo 3 | folder_01.target = qml 4 | DEPLOYMENTFOLDERS = folder_01 5 | 6 | # Additional import path used to resolve QML modules in Creator's code model 7 | QML_IMPORT_PATH = 8 | 9 | # The .cpp file which was generated for your project. Feel free to hack it. 10 | SOURCES += main.cpp 11 | 12 | android { 13 | QT += androidextras 14 | } 15 | 16 | # Installation path 17 | # target.path = 18 | 19 | # Please do not modify the following two lines. Required for deployment. 20 | include(qtquick2applicationviewer/qtquick2applicationviewer.pri) 21 | qtcAddDeployment() 22 | 23 | OTHER_FILES += \ 24 | qml/androidmetricdemo/android.js \ 25 | qml/androidmetricdemo/main.qml 26 | -------------------------------------------------------------------------------- /androidmetricdemo/qml/androidmetricdemo/splash.qml: -------------------------------------------------------------------------------- 1 | /* Author: Ben Lau (https://github.com/benlau) */ 2 | import QtQuick 2.0 3 | import "android.js" as Android 4 | 5 | Rectangle { 6 | id: splash 7 | width: 360 8 | height: 360 9 | color: "#000000" 10 | 11 | onWidthChanged: mainLoader.item.width = splash.width 12 | onHeightChanged: mainLoader.item.height = splash.height 13 | 14 | function init(context) { 15 | Android.init(context); 16 | mainLoader.source = "main.qml" 17 | } 18 | 19 | Loader { // this component performs deferred loading. 20 | id: mainLoader 21 | visible: status == Loader.Ready 22 | onLoaded: { 23 | mainLoader.item.width = splash.width 24 | mainLoader.item.height = splash.height 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /qtandroidrunner/qtandroidrunner.pro: -------------------------------------------------------------------------------- 1 | # Add more folders to ship with the application, here 2 | folder_01.source = qml/qtandroidrunner 3 | folder_01.target = qml 4 | DEPLOYMENTFOLDERS = folder_01 5 | 6 | # Additional import path used to resolve QML modules in Creator's code model 7 | QML_IMPORT_PATH = 8 | 9 | # The .cpp file which was generated for your project. Feel free to hack it. 10 | SOURCES += main.cpp \ 11 | qtandroidrunner.cpp 12 | 13 | QT += androidextras 14 | ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android 15 | 16 | # Installation path 17 | # target.path = 18 | 19 | # Please do not modify the following two lines. Required for deployment. 20 | include(qtquick2applicationviewer/qtquick2applicationviewer.pri) 21 | qtcAddDeployment() 22 | 23 | OTHER_FILES += \ 24 | README.md \ 25 | android/src/com/github/qt/QtRunner.java 26 | 27 | HEADERS += \ 28 | qtandroidrunner.h 29 | 30 | -------------------------------------------------------------------------------- /qtandroidrunner/android/src/com/github/qt/QtRunner.java: -------------------------------------------------------------------------------- 1 | package com.github.qt; 2 | 3 | import android.view.View; 4 | import android.widget.FrameLayout; 5 | import android.widget.RelativeLayout; 6 | import android.widget.FrameLayout; 7 | import android.widget.RelativeLayout; 8 | import android.content.Context; 9 | import android.app.Activity; 10 | import org.qtproject.qt5.android.QtNative; 11 | import java.lang.String; 12 | 13 | /* QtRunner - A helper class for QtAndroidRunner to execute action on UI thread 14 | */ 15 | 16 | public class QtRunner { 17 | 18 | public QtRunner() { 19 | } 20 | 21 | /* Post a request to invoke an action on UI thread 22 | * 23 | */ 24 | 25 | public static void post() { 26 | Activity activity = QtNative.activity(); 27 | Runnable runnable = new Runnable () { 28 | public void run() { 29 | invoke(); 30 | }; 31 | }; 32 | 33 | activity.runOnUiThread(runnable); 34 | } 35 | 36 | private static native void invoke(); 37 | } 38 | -------------------------------------------------------------------------------- /androidmetricdemo/qtquick2applicationviewer/qtquick2applicationviewer.h: -------------------------------------------------------------------------------- 1 | // checksum 0xfde6 version 0x90005 2 | /* 3 | This file was generated by the Qt Quick 2 Application wizard of Qt Creator. 4 | QtQuick2ApplicationViewer is a convenience class containing mobile device specific 5 | code such as screen orientation handling. Also QML paths and debugging are 6 | handled here. 7 | It is recommended not to modify this file, since newer versions of Qt Creator 8 | may offer an updated version of it. 9 | */ 10 | 11 | #ifndef QTQUICK2APPLICATIONVIEWER_H 12 | #define QTQUICK2APPLICATIONVIEWER_H 13 | 14 | #include 15 | 16 | class QtQuick2ApplicationViewer : public QQuickView 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | explicit QtQuick2ApplicationViewer(QWindow *parent = 0); 22 | virtual ~QtQuick2ApplicationViewer(); 23 | 24 | void setMainQmlFile(const QString &file); 25 | void addImportPath(const QString &path); 26 | 27 | void showExpanded(); 28 | 29 | private: 30 | class QtQuick2ApplicationViewerPrivate *d; 31 | }; 32 | 33 | #endif // QTQUICK2APPLICATIONVIEWER_H 34 | -------------------------------------------------------------------------------- /qtandroidrunner/qtquick2applicationviewer/qtquick2applicationviewer.h: -------------------------------------------------------------------------------- 1 | // checksum 0xfde6 version 0x90005 2 | /* 3 | This file was generated by the Qt Quick 2 Application wizard of Qt Creator. 4 | QtQuick2ApplicationViewer is a convenience class containing mobile device specific 5 | code such as screen orientation handling. Also QML paths and debugging are 6 | handled here. 7 | It is recommended not to modify this file, since newer versions of Qt Creator 8 | may offer an updated version of it. 9 | */ 10 | 11 | #ifndef QTQUICK2APPLICATIONVIEWER_H 12 | #define QTQUICK2APPLICATIONVIEWER_H 13 | 14 | #include 15 | 16 | class QtQuick2ApplicationViewer : public QQuickView 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | explicit QtQuick2ApplicationViewer(QWindow *parent = 0); 22 | virtual ~QtQuick2ApplicationViewer(); 23 | 24 | void setMainQmlFile(const QString &file); 25 | void addImportPath(const QString &path); 26 | 27 | void showExpanded(); 28 | 29 | private: 30 | class QtQuick2ApplicationViewerPrivate *d; 31 | }; 32 | 33 | #endif // QTQUICK2APPLICATIONVIEWER_H 34 | -------------------------------------------------------------------------------- /androidmetricdemo/main.cpp: -------------------------------------------------------------------------------- 1 | /* Author: Ben Lau (https://github.com/benlau) */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "qtquick2applicationviewer.h" 9 | #ifdef Q_OS_ANDROID 10 | #include 11 | #endif 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | QGuiApplication app(argc, argv); 16 | QtQuick2ApplicationViewer viewer; 17 | 18 | QVariantMap androidContext; 19 | androidContext["density"] = 1.0; 20 | 21 | #ifdef Q_OS_ANDROID 22 | QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); 23 | QAndroidJniObject resource = activity.callObjectMethod("getResources","()Landroid/content/res/Resources;"); 24 | QAndroidJniObject metrics = resource.callObjectMethod("getDisplayMetrics","()Landroid/util/DisplayMetrics;"); 25 | androidContext["density"] = metrics.getField("density"); 26 | #endif 27 | 28 | viewer.setMainQmlFile(QStringLiteral("qml/androidmetricdemo/splash.qml")); 29 | viewer.showExpanded(); 30 | 31 | QQuickItem *root = viewer.rootObject(); 32 | QMetaObject::invokeMethod(root,"init",Qt::QueuedConnection, 33 | Q_ARG(QVariant,androidContext)); 34 | 35 | return app.exec(); 36 | } 37 | -------------------------------------------------------------------------------- /qtandroidrunner/qtandroidrunner.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author Ben Lau 3 | * Website: https://github.com/benlau 4 | */ 5 | 6 | #ifndef QTANDROIDRUNNER_H 7 | #define QTANDROIDRUNNER_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class QtAndroidRunnerPriv; 15 | 16 | /// Schedule to execute code on Android application UI Thread 17 | /** 18 | * @brief The QtAndroidRunner class 19 | */ 20 | class QtAndroidRunner : public QObject 21 | { 22 | Q_OBJECT 23 | 24 | public: 25 | ~QtAndroidRunner(); 26 | 27 | /// Initialize the QAndroidRunner 28 | /** 29 | * @remark It should be called within JNI_OnLoad 30 | */ 31 | static void init(); 32 | 33 | /// Returns the global QAndroidRunner instance. 34 | static QtAndroidRunner* instance(); 35 | 36 | public slots: 37 | /// Schedule to execute QRunnable on Android application UI thread 38 | /** 39 | * @param runnable 40 | * @remarks If the current thread is the UI thread, then the action is executed immediately. 41 | * 42 | * Note that the runner takes ownership of the runnable if runnable->autoDelete() returns true, and the runnable will be deleted automatically by the runner after the runnable->run() returns. 43 | */ 44 | void start(QRunnable * runnable); 45 | 46 | private: 47 | explicit QtAndroidRunner(QObject *parent = 0); 48 | 49 | QtAndroidRunnerPriv* d; 50 | friend class QtAndroidRunnerPriv; 51 | }; 52 | 53 | #endif // QTANDROIDRUNNER_H 54 | -------------------------------------------------------------------------------- /androidmetricdemo/qml/androidmetricdemo/main.qml: -------------------------------------------------------------------------------- 1 | /* Author: Ben Lau (https://github.com/benlau) */ 2 | import QtQuick 2.0 3 | import "android.js" as Android 4 | 5 | Rectangle { 6 | width: 400 7 | height: 400 8 | 9 | Text { 10 | text: "Forumla: 1 dp = " + Android.dp + " pixel." 11 | anchors.centerIn: parent 12 | font.pixelSize : 18 * Android.dp 13 | } 14 | 15 | MouseArea { 16 | id: mouseArea1 17 | anchors.bottomMargin: 0 18 | anchors.top: actionBar.bottom 19 | anchors.right: parent.right 20 | anchors.bottom: navBar.top 21 | anchors.left: parent.left 22 | anchors.topMargin: 0 23 | onClicked: { 24 | Qt.quit(); 25 | } 26 | } 27 | 28 | Rectangle { 29 | id: actionBar 30 | height: 48 * Android.dp 31 | color: "#3498db" 32 | anchors.right: parent.right 33 | anchors.rightMargin: 0 34 | anchors.left: parent.left 35 | anchors.leftMargin: 0 36 | anchors.top: parent.top 37 | anchors.topMargin: 0 38 | 39 | Text { 40 | id: text1 41 | x: 144 42 | y: 17 43 | color: "#ffffff" 44 | text: "Action Bar - 48DP" 45 | anchors.horizontalCenter: parent.horizontalCenter 46 | anchors.verticalCenter: parent.verticalCenter 47 | horizontalAlignment: Text.AlignHCenter 48 | verticalAlignment: Text.AlignVCenter 49 | font.pixelSize : 22 * Android.dp; 50 | } 51 | } 52 | 53 | Rectangle { 54 | id: navBar 55 | x: 0 56 | y: 360 57 | height: 48 * Android.dp 58 | color: "#3498db" 59 | anchors.right: parent.right 60 | anchors.rightMargin: 0 61 | anchors.left: parent.left 62 | anchors.leftMargin: 0 63 | anchors.bottom: parent.bottom 64 | anchors.bottomMargin: 0 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /androidmetricdemo/qtquick2applicationviewer/qtquick2applicationviewer.cpp: -------------------------------------------------------------------------------- 1 | // checksum 0xc01f version 0x90005 2 | /* 3 | This file was generated by the Qt Quick 2 Application wizard of Qt Creator. 4 | QtQuick2ApplicationViewer is a convenience class containing mobile device specific 5 | code such as screen orientation handling. Also QML paths and debugging are 6 | handled here. 7 | It is recommended not to modify this file, since newer versions of Qt Creator 8 | may offer an updated version of it. 9 | */ 10 | 11 | #include "qtquick2applicationviewer.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | class QtQuick2ApplicationViewerPrivate 18 | { 19 | QString mainQmlFile; 20 | friend class QtQuick2ApplicationViewer; 21 | static QString adjustPath(const QString &path); 22 | }; 23 | 24 | QString QtQuick2ApplicationViewerPrivate::adjustPath(const QString &path) 25 | { 26 | #if defined(Q_OS_IOS) 27 | if (!QDir::isAbsolutePath(path)) 28 | return QString::fromLatin1("%1/%2") 29 | .arg(QCoreApplication::applicationDirPath(), path); 30 | #elif defined(Q_OS_MAC) 31 | if (!QDir::isAbsolutePath(path)) 32 | return QString::fromLatin1("%1/../Resources/%2") 33 | .arg(QCoreApplication::applicationDirPath(), path); 34 | #elif defined(Q_OS_BLACKBERRY) 35 | if (!QDir::isAbsolutePath(path)) 36 | return QString::fromLatin1("app/native/%1").arg(path); 37 | #elif !defined(Q_OS_ANDROID) 38 | QString pathInInstallDir = 39 | QString::fromLatin1("%1/../%2").arg(QCoreApplication::applicationDirPath(), path); 40 | if (QFileInfo(pathInInstallDir).exists()) 41 | return pathInInstallDir; 42 | pathInInstallDir = 43 | QString::fromLatin1("%1/%2").arg(QCoreApplication::applicationDirPath(), path); 44 | if (QFileInfo(pathInInstallDir).exists()) 45 | return pathInInstallDir; 46 | #elif defined(Q_OS_ANDROID_NO_SDK) 47 | return QLatin1String("/data/user/qt/") + path; 48 | #endif 49 | return path; 50 | } 51 | 52 | QtQuick2ApplicationViewer::QtQuick2ApplicationViewer(QWindow *parent) 53 | : QQuickView(parent) 54 | , d(new QtQuick2ApplicationViewerPrivate()) 55 | { 56 | connect(engine(), SIGNAL(quit()), SLOT(close())); 57 | setResizeMode(QQuickView::SizeRootObjectToView); 58 | } 59 | 60 | QtQuick2ApplicationViewer::~QtQuick2ApplicationViewer() 61 | { 62 | delete d; 63 | } 64 | 65 | void QtQuick2ApplicationViewer::setMainQmlFile(const QString &file) 66 | { 67 | d->mainQmlFile = QtQuick2ApplicationViewerPrivate::adjustPath(file); 68 | #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK) 69 | setSource(QUrl(QLatin1String("assets:/")+d->mainQmlFile)); 70 | #else 71 | setSource(QUrl::fromLocalFile(d->mainQmlFile)); 72 | #endif 73 | } 74 | 75 | void QtQuick2ApplicationViewer::addImportPath(const QString &path) 76 | { 77 | engine()->addImportPath(QtQuick2ApplicationViewerPrivate::adjustPath(path)); 78 | } 79 | 80 | void QtQuick2ApplicationViewer::showExpanded() 81 | { 82 | #if defined(Q_WS_SIMULATOR) || defined(Q_OS_QNX) 83 | showFullScreen(); 84 | #else 85 | show(); 86 | #endif 87 | } 88 | -------------------------------------------------------------------------------- /qtandroidrunner/qtquick2applicationviewer/qtquick2applicationviewer.cpp: -------------------------------------------------------------------------------- 1 | // checksum 0xc01f version 0x90005 2 | /* 3 | This file was generated by the Qt Quick 2 Application wizard of Qt Creator. 4 | QtQuick2ApplicationViewer is a convenience class containing mobile device specific 5 | code such as screen orientation handling. Also QML paths and debugging are 6 | handled here. 7 | It is recommended not to modify this file, since newer versions of Qt Creator 8 | may offer an updated version of it. 9 | */ 10 | 11 | #include "qtquick2applicationviewer.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | class QtQuick2ApplicationViewerPrivate 18 | { 19 | QString mainQmlFile; 20 | friend class QtQuick2ApplicationViewer; 21 | static QString adjustPath(const QString &path); 22 | }; 23 | 24 | QString QtQuick2ApplicationViewerPrivate::adjustPath(const QString &path) 25 | { 26 | #if defined(Q_OS_IOS) 27 | if (!QDir::isAbsolutePath(path)) 28 | return QString::fromLatin1("%1/%2") 29 | .arg(QCoreApplication::applicationDirPath(), path); 30 | #elif defined(Q_OS_MAC) 31 | if (!QDir::isAbsolutePath(path)) 32 | return QString::fromLatin1("%1/../Resources/%2") 33 | .arg(QCoreApplication::applicationDirPath(), path); 34 | #elif defined(Q_OS_BLACKBERRY) 35 | if (!QDir::isAbsolutePath(path)) 36 | return QString::fromLatin1("app/native/%1").arg(path); 37 | #elif !defined(Q_OS_ANDROID) 38 | QString pathInInstallDir = 39 | QString::fromLatin1("%1/../%2").arg(QCoreApplication::applicationDirPath(), path); 40 | if (QFileInfo(pathInInstallDir).exists()) 41 | return pathInInstallDir; 42 | pathInInstallDir = 43 | QString::fromLatin1("%1/%2").arg(QCoreApplication::applicationDirPath(), path); 44 | if (QFileInfo(pathInInstallDir).exists()) 45 | return pathInInstallDir; 46 | #elif defined(Q_OS_ANDROID_NO_SDK) 47 | return QLatin1String("/data/user/qt/") + path; 48 | #endif 49 | return path; 50 | } 51 | 52 | QtQuick2ApplicationViewer::QtQuick2ApplicationViewer(QWindow *parent) 53 | : QQuickView(parent) 54 | , d(new QtQuick2ApplicationViewerPrivate()) 55 | { 56 | connect(engine(), SIGNAL(quit()), SLOT(close())); 57 | setResizeMode(QQuickView::SizeRootObjectToView); 58 | } 59 | 60 | QtQuick2ApplicationViewer::~QtQuick2ApplicationViewer() 61 | { 62 | delete d; 63 | } 64 | 65 | void QtQuick2ApplicationViewer::setMainQmlFile(const QString &file) 66 | { 67 | d->mainQmlFile = QtQuick2ApplicationViewerPrivate::adjustPath(file); 68 | #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK) 69 | setSource(QUrl(QLatin1String("assets:/")+d->mainQmlFile)); 70 | #else 71 | setSource(QUrl::fromLocalFile(d->mainQmlFile)); 72 | #endif 73 | } 74 | 75 | void QtQuick2ApplicationViewer::addImportPath(const QString &path) 76 | { 77 | engine()->addImportPath(QtQuick2ApplicationViewerPrivate::adjustPath(path)); 78 | } 79 | 80 | void QtQuick2ApplicationViewer::showExpanded() 81 | { 82 | #if defined(Q_WS_SIMULATOR) || defined(Q_OS_QNX) 83 | showFullScreen(); 84 | #else 85 | show(); 86 | #endif 87 | } 88 | -------------------------------------------------------------------------------- /qtandroidrunner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "qtandroidrunner.h" 6 | #include "qtquick2applicationviewer.h" 7 | 8 | /// Create webview on top of Qt surface 9 | class WebViewInitializer : public QRunnable { 10 | 11 | public: 12 | void run() { 13 | // Example code to create WebView using C++ method. 14 | // However, it is not recommended to construct 15 | // everything using C++. It is very troublesome. 16 | // It just show how can you execute code with 17 | // Android application UI thread 18 | QAndroidJniEnvironment env; 19 | 20 | QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", 21 | "activity", "()Landroid/app/Activity;"); 22 | QAndroidJniObject webView("android/webkit/WebView", 23 | "(Landroid/content/Context;)V", 24 | activity.object()); 25 | 26 | QAndroidJniObject frameLayout = activity.callObjectMethod("findViewById","(I)Landroid/view/View;", 27 | 0x01020002); // Hard coded value of android.R.id.content 28 | QAndroidJniObject layout("android/widget/RelativeLayout", 29 | "(Landroid/content/Context;)V", 30 | activity.object()); 31 | 32 | QAndroidJniObject params = QAndroidJniObject("android/view/ViewGroup$LayoutParams", 33 | "(II)V", 34 | (int) 0xffffffff, 35 | (int) 0xffffffff); 36 | 37 | layout.callMethod("addView", 38 | "(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V", 39 | webView.object(), 40 | params.object()); 41 | 42 | frameLayout.callMethod("addView", 43 | "(Landroid/view/View;Landroid/view/ViewGroup$LayoutParams;)V", 44 | layout.object(), 45 | params.object()); 46 | 47 | QAndroidJniObject url = QAndroidJniObject::fromString("http://qt-project.org"); 48 | 49 | webView.callMethod("loadUrl","(Ljava/lang/String;)V",url.object()); 50 | 51 | if (env->ExceptionOccurred()) { 52 | env->ExceptionDescribe(); 53 | env->ExceptionClear(); 54 | } 55 | 56 | } 57 | }; 58 | 59 | JNIEXPORT 60 | jint 61 | JNI_OnLoad(JavaVM* vm, void*) { 62 | Q_UNUSED(vm); 63 | QtAndroidRunner::init(); 64 | 65 | return JNI_VERSION_1_6; 66 | } 67 | 68 | int main(int argc, char *argv[]) 69 | { 70 | QGuiApplication app(argc, argv); 71 | 72 | QtQuick2ApplicationViewer viewer; 73 | viewer.setMainQmlFile(QStringLiteral("qml/qtandroidrunner/main.qml")); 74 | viewer.showExpanded(); 75 | 76 | QtAndroidRunner *runner = QtAndroidRunner::instance(); 77 | runner->start(new WebViewInitializer()); 78 | 79 | return app.exec(); 80 | } 81 | -------------------------------------------------------------------------------- /qtandroidrunner/qtandroidrunner.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Author Ben Lau 3 | * Website: https://github.com/benlau 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "qtandroidrunner.h" 11 | 12 | #define JCLASS_Name "com/github/qt/QtRunner" 13 | 14 | static QtAndroidRunner *m_instance = 0; 15 | 16 | class QtAndroidRunnerPriv { 17 | public: 18 | QMutex mutex; 19 | QQueue queue; 20 | jclass clazz; 21 | jmethodID tick; 22 | 23 | QtAndroidRunnerPriv() { 24 | QAndroidJniEnvironment env; 25 | 26 | clazz = env->FindClass(JCLASS_Name); 27 | 28 | /* QAndroidJniObject only works for 29 | * core library and cached classes like 30 | * QtActivity / QtApplication. 31 | * 32 | * Therefore, it need to use the raw API 33 | * and must be executed within the JNI_onLoad() 34 | * call. 35 | * 36 | */ 37 | 38 | if (!clazz) 39 | { 40 | qCritical() << "Can't find class : " << JCLASS_Name << ". Did init() be called within JNI_onLoad?"; 41 | } else { 42 | 43 | tick = env->GetStaticMethodID(clazz,"post","()V"); 44 | if (tick ==0) { 45 | qCritical() << "Failed to obtain the method : tick"; 46 | } 47 | 48 | JNINativeMethod methods[] = 49 | { 50 | {"invoke", "()V", (void *)&QtAndroidRunnerPriv::invoke}, 51 | }; 52 | 53 | // Register the native methods. 54 | int numMethods = sizeof(methods) / sizeof(methods[0]); 55 | if (env->RegisterNatives(clazz, methods, numMethods) < 0) { 56 | if (env->ExceptionOccurred()) { 57 | env->ExceptionDescribe(); 58 | env->ExceptionClear(); 59 | qCritical() << "Exception in native method registration"; 60 | } 61 | } 62 | 63 | } 64 | } 65 | 66 | 67 | static void invoke() { 68 | QRunnable *runnable = 0; 69 | 70 | m_instance->d->mutex.lock(); 71 | if (m_instance->d->queue.size() > 0 ) { 72 | runnable = m_instance->d->queue.dequeue(); 73 | } 74 | m_instance->d->mutex.unlock(); 75 | 76 | if (runnable) { 77 | runnable->run(); 78 | if (runnable->autoDelete()) { 79 | delete runnable; 80 | } 81 | } 82 | 83 | } 84 | 85 | }; 86 | 87 | void QtAndroidRunner::init(){ 88 | QtAndroidRunner::instance(); 89 | } 90 | 91 | QtAndroidRunner *QtAndroidRunner::instance() 92 | { 93 | if (m_instance == 0) { 94 | QCoreApplication* app = QCoreApplication::instance(); 95 | m_instance = new QtAndroidRunner(app); 96 | } 97 | 98 | return m_instance; 99 | } 100 | 101 | void QtAndroidRunner::start(QRunnable *runnable) 102 | { 103 | d->mutex.lock(); 104 | d->queue.append(runnable); 105 | d->mutex.unlock(); 106 | 107 | QAndroidJniEnvironment env; 108 | env->CallStaticVoidMethod(d->clazz,d->tick); 109 | } 110 | 111 | QtAndroidRunner::QtAndroidRunner(QObject *parent) : 112 | QObject(parent) 113 | { 114 | d = new QtAndroidRunnerPriv; 115 | } 116 | 117 | QtAndroidRunner::~QtAndroidRunner(){ 118 | delete d; 119 | } 120 | -------------------------------------------------------------------------------- /androidmetricdemo/qtquick2applicationviewer/qtquick2applicationviewer.pri: -------------------------------------------------------------------------------- 1 | # checksum 0x21c9 version 0x90005 2 | # This file was generated by the Qt Quick 2 Application wizard of Qt Creator. 3 | # The code below adds the QtQuick2ApplicationViewer to the project and handles 4 | # the activation of QML debugging. 5 | # It is recommended not to modify this file, since newer versions of Qt Creator 6 | # may offer an updated version of it. 7 | 8 | QT += qml quick 9 | 10 | SOURCES += $$PWD/qtquick2applicationviewer.cpp 11 | HEADERS += $$PWD/qtquick2applicationviewer.h 12 | INCLUDEPATH += $$PWD 13 | # This file was generated by an application wizard of Qt Creator. 14 | # The code below handles deployment to Android and Maemo, aswell as copying 15 | # of the application data to shadow build directories on desktop. 16 | # It is recommended not to modify this file, since newer versions of Qt Creator 17 | # may offer an updated version of it. 18 | 19 | defineTest(qtcAddDeployment) { 20 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 21 | item = item$${deploymentfolder} 22 | greaterThan(QT_MAJOR_VERSION, 4) { 23 | itemsources = $${item}.files 24 | } else { 25 | itemsources = $${item}.sources 26 | } 27 | $$itemsources = $$eval($${deploymentfolder}.source) 28 | itempath = $${item}.path 29 | $$itempath= $$eval($${deploymentfolder}.target) 30 | export($$itemsources) 31 | export($$itempath) 32 | DEPLOYMENT += $$item 33 | } 34 | 35 | MAINPROFILEPWD = $$PWD 36 | 37 | android-no-sdk { 38 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 39 | item = item$${deploymentfolder} 40 | itemfiles = $${item}.files 41 | $$itemfiles = $$eval($${deploymentfolder}.source) 42 | itempath = $${item}.path 43 | $$itempath = /data/user/qt/$$eval($${deploymentfolder}.target) 44 | export($$itemfiles) 45 | export($$itempath) 46 | INSTALLS += $$item 47 | } 48 | 49 | target.path = /data/user/qt 50 | 51 | export(target.path) 52 | INSTALLS += target 53 | } else:android { 54 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 55 | item = item$${deploymentfolder} 56 | itemfiles = $${item}.files 57 | $$itemfiles = $$eval($${deploymentfolder}.source) 58 | itempath = $${item}.path 59 | $$itempath = /assets/$$eval($${deploymentfolder}.target) 60 | export($$itemfiles) 61 | export($$itempath) 62 | INSTALLS += $$item 63 | } 64 | 65 | x86 { 66 | target.path = /libs/x86 67 | } else: armeabi-v7a { 68 | target.path = /libs/armeabi-v7a 69 | } else { 70 | target.path = /libs/armeabi 71 | } 72 | 73 | export(target.path) 74 | INSTALLS += target 75 | } else:win32 { 76 | copyCommand = 77 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 78 | source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) 79 | source = $$replace(source, /, \\) 80 | sourcePathSegments = $$split(source, \\) 81 | target = $$OUT_PWD/$$eval($${deploymentfolder}.target)/$$last(sourcePathSegments) 82 | target = $$replace(target, /, \\) 83 | target ~= s,\\\\\\.?\\\\,\\, 84 | !isEqual(source,$$target) { 85 | !isEmpty(copyCommand):copyCommand += && 86 | isEqual(QMAKE_DIR_SEP, \\) { 87 | copyCommand += $(COPY_DIR) \"$$source\" \"$$target\" 88 | } else { 89 | source = $$replace(source, \\\\, /) 90 | target = $$OUT_PWD/$$eval($${deploymentfolder}.target) 91 | target = $$replace(target, \\\\, /) 92 | copyCommand += test -d \"$$target\" || mkdir -p \"$$target\" && cp -r \"$$source\" \"$$target\" 93 | } 94 | } 95 | } 96 | !isEmpty(copyCommand) { 97 | copyCommand = @echo Copying application data... && $$copyCommand 98 | copydeploymentfolders.commands = $$copyCommand 99 | first.depends = $(first) copydeploymentfolders 100 | export(first.depends) 101 | export(copydeploymentfolders.commands) 102 | QMAKE_EXTRA_TARGETS += first copydeploymentfolders 103 | } 104 | } else:ios { 105 | copyCommand = 106 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 107 | source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) 108 | source = $$replace(source, \\\\, /) 109 | target = $CODESIGNING_FOLDER_PATH/$$eval($${deploymentfolder}.target) 110 | target = $$replace(target, \\\\, /) 111 | sourcePathSegments = $$split(source, /) 112 | targetFullPath = $$target/$$last(sourcePathSegments) 113 | targetFullPath ~= s,/\\.?/,/, 114 | !isEqual(source,$$targetFullPath) { 115 | !isEmpty(copyCommand):copyCommand += && 116 | copyCommand += mkdir -p \"$$target\" 117 | copyCommand += && cp -r \"$$source\" \"$$target\" 118 | } 119 | } 120 | !isEmpty(copyCommand) { 121 | copyCommand = echo Copying application data... && $$copyCommand 122 | !isEmpty(QMAKE_POST_LINK): QMAKE_POST_LINK += ";" 123 | QMAKE_POST_LINK += "$$copyCommand" 124 | export(QMAKE_POST_LINK) 125 | } 126 | } else:unix { 127 | maemo5 { 128 | desktopfile.files = $${TARGET}.desktop 129 | desktopfile.path = /usr/share/applications/hildon 130 | icon.files = $${TARGET}64.png 131 | icon.path = /usr/share/icons/hicolor/64x64/apps 132 | } else:!isEmpty(MEEGO_VERSION_MAJOR) { 133 | desktopfile.files = $${TARGET}_harmattan.desktop 134 | desktopfile.path = /usr/share/applications 135 | icon.files = $${TARGET}80.png 136 | icon.path = /usr/share/icons/hicolor/80x80/apps 137 | } else { # Assumed to be a Desktop Unix 138 | copyCommand = 139 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 140 | source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) 141 | source = $$replace(source, \\\\, /) 142 | macx { 143 | target = $$OUT_PWD/$${TARGET}.app/Contents/Resources/$$eval($${deploymentfolder}.target) 144 | } else { 145 | target = $$OUT_PWD/$$eval($${deploymentfolder}.target) 146 | } 147 | target = $$replace(target, \\\\, /) 148 | sourcePathSegments = $$split(source, /) 149 | targetFullPath = $$target/$$last(sourcePathSegments) 150 | targetFullPath ~= s,/\\.?/,/, 151 | !isEqual(source,$$targetFullPath) { 152 | !isEmpty(copyCommand):copyCommand += && 153 | copyCommand += $(MKDIR) \"$$target\" 154 | copyCommand += && $(COPY_DIR) \"$$source\" \"$$target\" 155 | } 156 | } 157 | !isEmpty(copyCommand) { 158 | copyCommand = @echo Copying application data... && $$copyCommand 159 | copydeploymentfolders.commands = $$copyCommand 160 | first.depends = $(first) copydeploymentfolders 161 | export(first.depends) 162 | export(copydeploymentfolders.commands) 163 | QMAKE_EXTRA_TARGETS += first copydeploymentfolders 164 | } 165 | } 166 | !isEmpty(target.path) { 167 | installPrefix = $${target.path} 168 | } else { 169 | installPrefix = /opt/$${TARGET} 170 | } 171 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 172 | item = item$${deploymentfolder} 173 | itemfiles = $${item}.files 174 | $$itemfiles = $$eval($${deploymentfolder}.source) 175 | itempath = $${item}.path 176 | $$itempath = $${installPrefix}/$$eval($${deploymentfolder}.target) 177 | export($$itemfiles) 178 | export($$itempath) 179 | INSTALLS += $$item 180 | } 181 | 182 | !isEmpty(desktopfile.path) { 183 | export(icon.files) 184 | export(icon.path) 185 | export(desktopfile.files) 186 | export(desktopfile.path) 187 | INSTALLS += icon desktopfile 188 | } 189 | 190 | isEmpty(target.path) { 191 | target.path = $${installPrefix}/bin 192 | export(target.path) 193 | } 194 | INSTALLS += target 195 | } 196 | 197 | export (ICON) 198 | export (INSTALLS) 199 | export (DEPLOYMENT) 200 | export (LIBS) 201 | export (QMAKE_EXTRA_TARGETS) 202 | } 203 | -------------------------------------------------------------------------------- /qtandroidrunner/qtquick2applicationviewer/qtquick2applicationviewer.pri: -------------------------------------------------------------------------------- 1 | # checksum 0x21c9 version 0x90005 2 | # This file was generated by the Qt Quick 2 Application wizard of Qt Creator. 3 | # The code below adds the QtQuick2ApplicationViewer to the project and handles 4 | # the activation of QML debugging. 5 | # It is recommended not to modify this file, since newer versions of Qt Creator 6 | # may offer an updated version of it. 7 | 8 | QT += qml quick 9 | 10 | SOURCES += $$PWD/qtquick2applicationviewer.cpp 11 | HEADERS += $$PWD/qtquick2applicationviewer.h 12 | INCLUDEPATH += $$PWD 13 | # This file was generated by an application wizard of Qt Creator. 14 | # The code below handles deployment to Android and Maemo, aswell as copying 15 | # of the application data to shadow build directories on desktop. 16 | # It is recommended not to modify this file, since newer versions of Qt Creator 17 | # may offer an updated version of it. 18 | 19 | defineTest(qtcAddDeployment) { 20 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 21 | item = item$${deploymentfolder} 22 | greaterThan(QT_MAJOR_VERSION, 4) { 23 | itemsources = $${item}.files 24 | } else { 25 | itemsources = $${item}.sources 26 | } 27 | $$itemsources = $$eval($${deploymentfolder}.source) 28 | itempath = $${item}.path 29 | $$itempath= $$eval($${deploymentfolder}.target) 30 | export($$itemsources) 31 | export($$itempath) 32 | DEPLOYMENT += $$item 33 | } 34 | 35 | MAINPROFILEPWD = $$PWD 36 | 37 | android-no-sdk { 38 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 39 | item = item$${deploymentfolder} 40 | itemfiles = $${item}.files 41 | $$itemfiles = $$eval($${deploymentfolder}.source) 42 | itempath = $${item}.path 43 | $$itempath = /data/user/qt/$$eval($${deploymentfolder}.target) 44 | export($$itemfiles) 45 | export($$itempath) 46 | INSTALLS += $$item 47 | } 48 | 49 | target.path = /data/user/qt 50 | 51 | export(target.path) 52 | INSTALLS += target 53 | } else:android { 54 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 55 | item = item$${deploymentfolder} 56 | itemfiles = $${item}.files 57 | $$itemfiles = $$eval($${deploymentfolder}.source) 58 | itempath = $${item}.path 59 | $$itempath = /assets/$$eval($${deploymentfolder}.target) 60 | export($$itemfiles) 61 | export($$itempath) 62 | INSTALLS += $$item 63 | } 64 | 65 | x86 { 66 | target.path = /libs/x86 67 | } else: armeabi-v7a { 68 | target.path = /libs/armeabi-v7a 69 | } else { 70 | target.path = /libs/armeabi 71 | } 72 | 73 | export(target.path) 74 | INSTALLS += target 75 | } else:win32 { 76 | copyCommand = 77 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 78 | source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) 79 | source = $$replace(source, /, \\) 80 | sourcePathSegments = $$split(source, \\) 81 | target = $$OUT_PWD/$$eval($${deploymentfolder}.target)/$$last(sourcePathSegments) 82 | target = $$replace(target, /, \\) 83 | target ~= s,\\\\\\.?\\\\,\\, 84 | !isEqual(source,$$target) { 85 | !isEmpty(copyCommand):copyCommand += && 86 | isEqual(QMAKE_DIR_SEP, \\) { 87 | copyCommand += $(COPY_DIR) \"$$source\" \"$$target\" 88 | } else { 89 | source = $$replace(source, \\\\, /) 90 | target = $$OUT_PWD/$$eval($${deploymentfolder}.target) 91 | target = $$replace(target, \\\\, /) 92 | copyCommand += test -d \"$$target\" || mkdir -p \"$$target\" && cp -r \"$$source\" \"$$target\" 93 | } 94 | } 95 | } 96 | !isEmpty(copyCommand) { 97 | copyCommand = @echo Copying application data... && $$copyCommand 98 | copydeploymentfolders.commands = $$copyCommand 99 | first.depends = $(first) copydeploymentfolders 100 | export(first.depends) 101 | export(copydeploymentfolders.commands) 102 | QMAKE_EXTRA_TARGETS += first copydeploymentfolders 103 | } 104 | } else:ios { 105 | copyCommand = 106 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 107 | source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) 108 | source = $$replace(source, \\\\, /) 109 | target = $CODESIGNING_FOLDER_PATH/$$eval($${deploymentfolder}.target) 110 | target = $$replace(target, \\\\, /) 111 | sourcePathSegments = $$split(source, /) 112 | targetFullPath = $$target/$$last(sourcePathSegments) 113 | targetFullPath ~= s,/\\.?/,/, 114 | !isEqual(source,$$targetFullPath) { 115 | !isEmpty(copyCommand):copyCommand += && 116 | copyCommand += mkdir -p \"$$target\" 117 | copyCommand += && cp -r \"$$source\" \"$$target\" 118 | } 119 | } 120 | !isEmpty(copyCommand) { 121 | copyCommand = echo Copying application data... && $$copyCommand 122 | !isEmpty(QMAKE_POST_LINK): QMAKE_POST_LINK += ";" 123 | QMAKE_POST_LINK += "$$copyCommand" 124 | export(QMAKE_POST_LINK) 125 | } 126 | } else:unix { 127 | maemo5 { 128 | desktopfile.files = $${TARGET}.desktop 129 | desktopfile.path = /usr/share/applications/hildon 130 | icon.files = $${TARGET}64.png 131 | icon.path = /usr/share/icons/hicolor/64x64/apps 132 | } else:!isEmpty(MEEGO_VERSION_MAJOR) { 133 | desktopfile.files = $${TARGET}_harmattan.desktop 134 | desktopfile.path = /usr/share/applications 135 | icon.files = $${TARGET}80.png 136 | icon.path = /usr/share/icons/hicolor/80x80/apps 137 | } else { # Assumed to be a Desktop Unix 138 | copyCommand = 139 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 140 | source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) 141 | source = $$replace(source, \\\\, /) 142 | macx { 143 | target = $$OUT_PWD/$${TARGET}.app/Contents/Resources/$$eval($${deploymentfolder}.target) 144 | } else { 145 | target = $$OUT_PWD/$$eval($${deploymentfolder}.target) 146 | } 147 | target = $$replace(target, \\\\, /) 148 | sourcePathSegments = $$split(source, /) 149 | targetFullPath = $$target/$$last(sourcePathSegments) 150 | targetFullPath ~= s,/\\.?/,/, 151 | !isEqual(source,$$targetFullPath) { 152 | !isEmpty(copyCommand):copyCommand += && 153 | copyCommand += $(MKDIR) \"$$target\" 154 | copyCommand += && $(COPY_DIR) \"$$source\" \"$$target\" 155 | } 156 | } 157 | !isEmpty(copyCommand) { 158 | copyCommand = @echo Copying application data... && $$copyCommand 159 | copydeploymentfolders.commands = $$copyCommand 160 | first.depends = $(first) copydeploymentfolders 161 | export(first.depends) 162 | export(copydeploymentfolders.commands) 163 | QMAKE_EXTRA_TARGETS += first copydeploymentfolders 164 | } 165 | } 166 | !isEmpty(target.path) { 167 | installPrefix = $${target.path} 168 | } else { 169 | installPrefix = /opt/$${TARGET} 170 | } 171 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 172 | item = item$${deploymentfolder} 173 | itemfiles = $${item}.files 174 | $$itemfiles = $$eval($${deploymentfolder}.source) 175 | itempath = $${item}.path 176 | $$itempath = $${installPrefix}/$$eval($${deploymentfolder}.target) 177 | export($$itemfiles) 178 | export($$itempath) 179 | INSTALLS += $$item 180 | } 181 | 182 | !isEmpty(desktopfile.path) { 183 | export(icon.files) 184 | export(icon.path) 185 | export(desktopfile.files) 186 | export(desktopfile.path) 187 | INSTALLS += icon desktopfile 188 | } 189 | 190 | isEmpty(target.path) { 191 | target.path = $${installPrefix}/bin 192 | export(target.path) 193 | } 194 | INSTALLS += target 195 | } 196 | 197 | export (ICON) 198 | export (INSTALLS) 199 | export (DEPLOYMENT) 200 | export (LIBS) 201 | export (QMAKE_EXTRA_TARGETS) 202 | } 203 | --------------------------------------------------------------------------------