├── android ├── res │ ├── drawable │ │ └── icon.png │ └── values │ │ ├── strings.xml │ │ └── libs.xml ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ ├── gradle-wrapper.properties~ │ │ └── gradle-wrapper.properties ├── build.gradle ├── gradlew.bat ├── gradlew └── AndroidManifest.xml ├── qml.qrc ├── source ├── java │ ├── QuickstartPreferences.java │ ├── JavaNatives.java │ ├── MyApplication.java │ ├── MyGcmListenerService.java │ ├── RegistrationIntentService.java │ └── Vibrate.java ├── cpp │ ├── main.cpp │ └── misc │ │ ├── pushnotification.h │ │ ├── ios │ │ └── pushnotification.mm │ │ └── pushnotification.cpp └── qml │ └── main.qml ├── .gitignore ├── ios ├── pushnotifications.entitlements └── Info.plist ├── qtquickcontrols2.conf ├── README.md ├── qt-pushnotifications.pro └── test_pushnotifications.py /android/res/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gympulsr/qt-pushnotifications/HEAD/android/res/drawable/icon.png -------------------------------------------------------------------------------- /qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | source/qml/main.qml 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gympulsr/qt-pushnotifications/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /source/java/QuickstartPreferences.java: -------------------------------------------------------------------------------- 1 | package com.example.example; 2 | 3 | public class QuickstartPreferences { 4 | public static final String GCM_TOKEN = ""; 5 | 6 | } 7 | -------------------------------------------------------------------------------- /android/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | xxxxxxx 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | qt-pushnotifications.pro.user 3 | apns.pem 4 | android/google-services.json 5 | android/.build 6 | android/gradle.properties 7 | android/gradle.properties~ 8 | android/local.properties 9 | android/local.properties~ 10 | -------------------------------------------------------------------------------- /source/java/JavaNatives.java: -------------------------------------------------------------------------------- 1 | //adapt that accordingly 2 | package com.example.example; 3 | 4 | import org.qtproject.qt5.android.QtNative; 5 | import android.content.Intent; 6 | 7 | class JavaNatives{ 8 | public static native void sendGCMToken(String gcmToken); 9 | } 10 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties~: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip 7 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip 7 | -------------------------------------------------------------------------------- /ios/pushnotifications.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | 8 | 9 | -------------------------------------------------------------------------------- /qtquickcontrols2.conf: -------------------------------------------------------------------------------- 1 | ; This file can be edited to change the style of the application 2 | ; See Styling Qt Quick Controls 2 in the documentation for details: 3 | ; http://doc.qt.io/qt-5/qtquickcontrols2-styles.html 4 | 5 | [Controls] 6 | Style=Default 7 | 8 | [Universal] 9 | Theme=Light 10 | ;Accent=Steel 11 | 12 | [Material] 13 | Theme=Light 14 | ;Accent=BlueGrey 15 | ;Primary=BlueGray 16 | -------------------------------------------------------------------------------- /source/java/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.example; 2 | 3 | import android.content.Context; 4 | 5 | public class MyApplication extends org.qtproject.qt5.android.bindings.QtApplication 6 | { 7 | private static Context context; 8 | 9 | public void onCreate() 10 | { 11 | super.onCreate(); 12 | MyApplication.context = getApplicationContext(); 13 | } 14 | 15 | public static Context getAppContext() 16 | { 17 | return MyApplication.context; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android/res/values/libs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | https://download.qt.io/ministro/android/qt5/qt-5.7 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /source/cpp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "misc/pushnotification.h" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 9 | QGuiApplication app(argc, argv); 10 | 11 | qmlRegisterSingletonType("com.example.example", 1, 0, "PushNotificationRegistrationTokenHandler", 12 | PushNotificationRegistrationTokenHandler::pushNotificationRegistrationTokenProvider); 13 | 14 | QQmlApplicationEngine engine; 15 | engine.addImportPath(QStringLiteral("qrc:/")); 16 | engine.load(QUrl(QStringLiteral("qrc:/source/qml/main.qml"))); 17 | 18 | return app.exec(); 19 | } 20 | -------------------------------------------------------------------------------- /source/cpp/misc/pushnotification.h: -------------------------------------------------------------------------------- 1 | #ifndef PUSHNOTIFICATION_H 2 | #define PUSHNOTIFICATION_H 3 | 4 | #include 5 | #include 6 | 7 | class PushNotificationRegistrationTokenHandler : public QObject{ 8 | Q_OBJECT 9 | Q_PROPERTY(QString gcmRegistrationToken READ getGcmRegistrationToken NOTIFY gcmRegistrationTokenChanged) 10 | Q_PROPERTY(QString apnsRegistrationToken READ getAPNSRegistrationToken WRITE setAPNSRegistrationToken NOTIFY apnsRegistrationTokenChanged) 11 | public: 12 | PushNotificationRegistrationTokenHandler(QObject* parent = 0); 13 | //singleton type provider function for Qt Quick 14 | static QObject* pushNotificationRegistrationTokenProvider(QQmlEngine *engine, QJSEngine *scriptEngine); 15 | //singleton object provider for C++ 16 | static PushNotificationRegistrationTokenHandler* instance(); 17 | void setGcmRegistrationToken(const QString& gcmRegistrationToken); 18 | QString getGcmRegistrationToken(); 19 | QString getAPNSRegistrationToken() const; 20 | void setAPNSRegistrationToken(const QString& apnsToken); 21 | ~PushNotificationRegistrationTokenHandler(); 22 | signals: 23 | void gcmRegistrationTokenChanged(); 24 | void apnsRegistrationTokenChanged(); 25 | void registeredChanged(); 26 | private: 27 | QString m_gcmToken; 28 | QString m_apnsToken; 29 | }; 30 | 31 | 32 | #endif // PUSHNOTIFICATION_H 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qt-pushnotifications 2 | 3 | This repository demonstrates how to implement push notifications with Qt on iOs and Android. 4 | 5 | For more informations see the following blog entries: 6 | 7 | [iOs push notifications with Qt](https://gympulsr.com/blog/qt/2017/03/08/push-notification-ios-qt.html) 8 | 9 | [Android push notifications with Qt](https://gympulsr.com/blog/qt/2017/03/26/push-notification-android-qt.html) 10 | 11 | About gympulsr 12 | ============== 13 | 14 | gympulsr is a fun community for fitness enthusiasts to connect with like minded people around the world. Mark your gym on a map to connect with people that are working out nearby. Post motivating pictures, show people your gym transformation or just write about your last workout. 15 | Wanna give it a try? Download gympulsr from the Apple or Google Play Store and say hello. 16 | 17 | Download on the App Store 19 | Get it on Google Play 20 | 21 | -------------------------------------------------------------------------------- /qt-pushnotifications.pro: -------------------------------------------------------------------------------- 1 | QT += qml quick 2 | 3 | android{ 4 | QT += androidextras 5 | } 6 | 7 | CONFIG += c++11 8 | 9 | SOURCES += \ 10 | source/cpp/main.cpp \ 11 | source/cpp/misc/pushnotification.cpp 12 | 13 | RESOURCES += qml.qrc 14 | 15 | 16 | ios{ 17 | ######## adapt the following value to match your TeamID ######## 18 | MY_DEVELOPMENT_TEAM.value = XXXXXXXXX 19 | ################################################################ 20 | 21 | MY_DEVELOPMENT_TEAM.name = DEVELOPMENT_TEAM 22 | QMAKE_MAC_XCODE_SETTINGS += MY_DEVELOPMENT_TEAM 23 | 24 | MY_ENTITLEMENTS.name = CODE_SIGN_ENTITLEMENTS 25 | MY_ENTITLEMENTS.value = $$PWD/ios/pushnotifications.entitlements 26 | QMAKE_MAC_XCODE_SETTINGS += MY_ENTITLEMENTS 27 | 28 | QMAKE_IOS_DEPLOYMENT_TARGET=8.0 29 | 30 | # Note for devices: 1=iPhone, 2=iPad, 1,2=Universal. 31 | QMAKE_IOS_TARGETED_DEVICE_FAMILY = 1 32 | 33 | QMAKE_INFO_PLIST = $$PWD/ios/Info.plist 34 | 35 | OBJECTIVE_SOURCES += \ 36 | $$PWD/source/cpp/misc/ios/pushnotification.mm \ 37 | 38 | CONFIG -= bitcode 39 | 40 | } 41 | 42 | android{ 43 | ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android 44 | 45 | ################# adapt that accordingly ####################### 46 | ANDROID_JAVA_SOURCES.path = /src/com/example/example 47 | ################################################################ 48 | 49 | ANDROID_JAVA_SOURCES.files = $$files($$PWD/source/java/*.java) 50 | INSTALLS += ANDROID_JAVA_SOURCES 51 | } 52 | 53 | HEADERS += \ 54 | source/cpp/misc/pushnotification.h 55 | 56 | DISTFILES += \ 57 | android/AndroidManifest.xml \ 58 | android/gradle/wrapper/gradle-wrapper.jar \ 59 | android/gradlew \ 60 | android/res/values/libs.xml \ 61 | android/build.gradle \ 62 | android/gradle/wrapper/gradle-wrapper.properties \ 63 | android/gradlew.bat 64 | -------------------------------------------------------------------------------- /source/qml/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.0 4 | import com.example.example 1.0 5 | 6 | ApplicationWindow { 7 | visible: true 8 | width: 640 9 | height: 480 10 | title: qsTr("Qt Push Notifications Demo") 11 | 12 | Rectangle{ 13 | anchors.fill: parent 14 | Text{ 15 | id: introduction 16 | anchors.top: parent.top 17 | anchors.horizontalCenter: parent.horizontalCenter 18 | anchors.topMargin: 20 19 | text: "This example demonstrates how to implement push notifications on " + Qt.platform.os; 20 | width: parent.width - 20 21 | wrapMode: Text.WordWrap 22 | } 23 | 24 | Text{ 25 | id: registrationToken 26 | anchors.top: introduction.bottom 27 | anchors.horizontalCenter: parent.horizontalCenter 28 | anchors.topMargin: 20 29 | wrapMode: Text.WrapAtWordBoundaryOrAnywhere 30 | width: parent.width - 20 31 | text: Qt.platform.os === "ios" ? 32 | ("Token: " + PushNotificationRegistrationTokenHandler.apnsRegistrationToken) 33 | : ("Token: " + PushNotificationRegistrationTokenHandler.gcmRegistrationToken) 34 | } 35 | 36 | Text{ 37 | id: info 38 | anchors.top: registrationToken.bottom 39 | anchors.horizontalCenter: parent.horizontalCenter 40 | anchors.topMargin: 30 41 | wrapMode: Text.WordWrap 42 | width: parent.width - 20 43 | text: "Did you find this example useful? If so, it would really mean a lot to me " 44 | + " if you tell your friends who are into fitness about gympulsr (https://www.gympulsr.com)." 45 | + " Gympulsr is a hobby project of mine that connects fitness enthusiasts around the world with each other" 46 | + " (and btw. it's also written in Qt. ;-)). Thanks a lot!" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /source/cpp/misc/ios/pushnotification.mm: -------------------------------------------------------------------------------- 1 | #import "UIKit/UIKit.h" 2 | #include "pushnotification.h" 3 | 4 | @interface QIOSApplicationDelegate 5 | @end 6 | //add a category to QIOSApplicationDelegate 7 | @interface QIOSApplicationDelegate (QPushNotificationDelegate) 8 | @end 9 | 10 | 11 | @implementation QIOSApplicationDelegate (QPushNotificationDelegate) 12 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 13 | 14 | //-- Set Notification 15 | if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) 16 | { 17 | // iOS 8 Notifications 18 | [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]]; 19 | 20 | [application registerForRemoteNotifications]; 21 | } 22 | else 23 | { 24 | // iOS < 8 Notifications 25 | [application registerForRemoteNotificationTypes: 26 | (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)]; 27 | } 28 | 29 | return YES; 30 | } 31 | 32 | - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { 33 | NSLog(@"Did Register for Remote Notifications with Device Token (%@)", deviceToken); 34 | 35 | const unsigned *tokenBytes = (const unsigned*)[deviceToken bytes]; 36 | NSString *tokenStr = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x", 37 | ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]), 38 | ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]), 39 | ntohl(tokenBytes[6]), ntohl(tokenBytes[7])]; 40 | PushNotificationRegistrationTokenHandler::instance()->setAPNSRegistrationToken(QString::fromNSString(tokenStr)); 41 | } 42 | 43 | - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { 44 | NSLog(@"Did Fail to Register for Remote Notifications"); 45 | NSLog(@"%@, %@", error, error.localizedDescription); 46 | 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:1.1.0' 8 | classpath 'com.google.gms:google-services:3.0.0' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | jcenter() 15 | mavenCentral() 16 | } 17 | 18 | gradle.projectsEvaluated { 19 | tasks.withType(JavaCompile) { 20 | options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" 21 | } 22 | } 23 | } 24 | 25 | apply plugin: 'com.android.application' 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | compile "com.google.android.gms:play-services-gcm:9.4.0" 30 | compile "com.android.support:appcompat-v7:22.1.0" 31 | } 32 | 33 | android { 34 | /******************************************************* 35 | * The following variables: 36 | * - androidBuildToolsVersion, 37 | * - androidCompileSdkVersion 38 | * - qt5AndroidDir - holds the path to qt android files 39 | * needed to build any Qt application 40 | * on Android. 41 | * 42 | * are defined in gradle.properties file. This file is 43 | * updated by QtCreator and androiddeployqt tools. 44 | * Changing them manually might break the compilation! 45 | *******************************************************/ 46 | 47 | compileSdkVersion androidCompileSdkVersion.toInteger() 48 | 49 | buildToolsVersion androidBuildToolsVersion 50 | 51 | sourceSets { 52 | main { 53 | manifest.srcFile 'AndroidManifest.xml' 54 | java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] 55 | aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] 56 | res.srcDirs = [qt5AndroidDir + '/res', 'res'] 57 | resources.srcDirs = ['src'] 58 | renderscript.srcDirs = ['src'] 59 | assets.srcDirs = ['assets'] 60 | jniLibs.srcDirs = ['libs'] 61 | } 62 | } 63 | 64 | lintOptions { 65 | abortOnError false 66 | } 67 | 68 | defaultConfig { 69 | //adapt that accordingly 70 | applicationId "com.example.example" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test_pushnotifications.py: -------------------------------------------------------------------------------- 1 | from apns2.client import APNsClient 2 | from apns2.payload import Payload 3 | from gcm import GCM 4 | import os 5 | 6 | ##### 7 | ## set TARGET to 'android' if you want to send push notifications to android devices 8 | ## set TARGET to 'ios' if you want to send push notifications to iOs devices 9 | TARGET = "ios" 10 | 11 | ###### Set your bundle id 12 | BUNDLE_ID = "com.example.example" 13 | 14 | currentDir = os.path.dirname(os.path.realpath(__file__)) 15 | 16 | 17 | if(TARGET == "ios"): 18 | ###### If you are using a production certificate, change this to 'False' 19 | APNS_USE_SANDBOX = True 20 | 21 | ###### set path to APNS certificate 22 | APNS_CERT_PATH = currentDir + os.path.sep + "apns.pem" 23 | 24 | def static_vars(**kwargs): 25 | def decorate(func): 26 | for k in kwargs: 27 | setattr(func, k, kwargs[k]) 28 | return func 29 | return decorate 30 | 31 | #creating a connection to the Apple Push notification service is really expensive and furthermore 32 | #has the disadvantage, that Apple detects multiple connection/disconnection attempts as possible DDos attack! 33 | #so we use a static variable to keep the connection open as long as possible. 34 | @static_vars(client=APNsClient('apns.pem', use_sandbox=APNS_USE_SANDBOX, use_alternative_port=False)) 35 | def sendAPNSNotification(token, message): 36 | payload = Payload(alert=message, sound="default", badge=0) 37 | #client = APNsClient(APNS_CERT_PATH, use_sandbox=True, use_alternative_port=False) 38 | sendAPNSNotification.client.send_notification(token, payload, topic=BUNDLE_ID) 39 | 40 | elif(TARGET == "android"): 41 | GCM_API_KEY = "YOUR_GCM_API_KEY" #adapt that accordingly 42 | 43 | def sendGCMNotification(registrationTokenList, message): 44 | gcm = GCM(GCM_API_KEY) 45 | #print 'send message to gcm registration ids ' + ', '.join(registrationTokenList) 46 | retVal = gcm.json_request(registration_ids=registrationTokenList, data={"message": message}, 47 | delay_while_idle=False) 48 | return retVal 49 | 50 | if __name__ == "__main__": 51 | token = "YOUR_TOKEN" #change that to your token 52 | message = "Hello World" 53 | 54 | print "Sending '%s' to the phone with the token %s" %(message, token,) 55 | 56 | if(TARGET == "ios"): 57 | sendAPNSNotification(token, message) 58 | elif(TARGET == "android"): 59 | sendGCMNotification([token], message) 60 | else: 61 | print "UNKNOWN TARGET" 62 | 63 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /source/java/MyGcmListenerService.java: -------------------------------------------------------------------------------- 1 | //adapt that accordingly 2 | package com.example.example; 3 | import com.example.example.R; 4 | 5 | import android.app.NotificationManager; 6 | import android.app.PendingIntent; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.media.RingtoneManager; 10 | import android.net.Uri; 11 | import android.os.Bundle; 12 | import android.support.v4.app.NotificationCompat; 13 | import android.util.Log; 14 | 15 | import com.google.android.gms.gcm.GcmListenerService; 16 | 17 | public class MyGcmListenerService extends GcmListenerService 18 | { 19 | private static final String TAG = "MyGcmListenerService"; 20 | 21 | /** 22 | * Called when message is received. 23 | * 24 | * @param from SenderID of the sender. 25 | * @param data Data bundle containing message data as key/value pairs. 26 | * For Set of keys use data.keySet(). 27 | */ 28 | @Override 29 | public void onMessageReceived(String from, Bundle data) 30 | { 31 | String message = data.getString("message"); 32 | Log.d(TAG, "From: " + from); 33 | Log.d(TAG, "Message: " + message); 34 | 35 | //adapt that if you want to react to topics 36 | //individually. 37 | if (from.startsWith("/topics/")) { 38 | // message received from some topic. 39 | } else { 40 | // normal downstream message. 41 | } 42 | /** 43 | * Production applications would usually process the message here. 44 | * Eg: - Syncing with server. 45 | * - Store message in local database. 46 | * - Update UI. 47 | */ 48 | 49 | /** 50 | * In some cases it may be useful to show a notification indicating to the user 51 | * that a message was received. 52 | */ 53 | sendNotification(message); 54 | } 55 | 56 | /** 57 | * Create and show a simple notification containing the received GCM message. 58 | * 59 | * @param message GCM message received. 60 | */ 61 | private void sendNotification(String message) 62 | { 63 | Intent intent = new Intent(this, Vibrate.class); 64 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 65 | PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, PendingIntent.FLAG_ONE_SHOT); 66 | 67 | Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); 68 | NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) 69 | .setSmallIcon(R.drawable.icon) 70 | .setContentTitle("example") //adapt that accordingly 71 | .setContentText(message) 72 | .setAutoCancel(true) 73 | .setSound(defaultSoundUri) 74 | .setContentIntent(pendingIntent); 75 | 76 | NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 77 | 78 | notificationManager.notify(0 /* ID of notification */, notificationBuilder.build()); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /source/java/RegistrationIntentService.java: -------------------------------------------------------------------------------- 1 | //adapt that accordingly 2 | package com.example.example; 3 | 4 | import android.app.IntentService; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.preference.PreferenceManager; 8 | import android.support.v4.content.LocalBroadcastManager; 9 | import android.util.Log; 10 | 11 | import com.google.android.gms.gcm.GcmPubSub; 12 | import com.google.android.gms.gcm.GoogleCloudMessaging; 13 | import com.google.android.gms.iid.InstanceID; 14 | 15 | import java.io.IOException; 16 | 17 | public class RegistrationIntentService extends IntentService { 18 | 19 | private static final String TAG = "RegIntentService"; 20 | private static final String[] TOPICS = {"global"}; 21 | 22 | public RegistrationIntentService() { 23 | super(TAG); 24 | } 25 | 26 | @Override 27 | protected void onHandleIntent(Intent intent) { 28 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 29 | 30 | try { 31 | // [START register_for_gcm] 32 | // Initially this call goes out to the network to retrieve the token, subsequent calls 33 | // are local. 34 | // R.string.gcm_defaultSenderId (the Sender ID) is typically derived from google-services.json. 35 | // See https://developers.google.com/cloud-messaging/android/start for details on this file. 36 | // [START get_token] 37 | InstanceID instanceID = InstanceID.getInstance(this); 38 | String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId), 39 | GoogleCloudMessaging.INSTANCE_ID_SCOPE, null); 40 | 41 | // Subscribe to topic channels 42 | subscribeTopics(token); 43 | 44 | sharedPreferences.edit().putString(QuickstartPreferences.GCM_TOKEN, token).apply(); 45 | 46 | // You should store a boolean that indicates whether the generated token has been 47 | // sent to your server. If the boolean is false, send the token to your server, 48 | // otherwise your server should have already received the token. 49 | //sharedPreferences.edit().putBoolean(QuickstartPreferences.SENT_TOKEN_TO_SERVER, true).apply(); 50 | // [END register_for_gcm] 51 | } catch (Exception e) { 52 | Log.d(TAG, "Failed to complete token refresh", e); 53 | // If an exception happens while fetching the new token or updating our registration data 54 | // on a third-party server, this ensures that we'll attempt the update at a later time. 55 | //sharedPreferences.edit().putBoolean(QuickstartPreferences.SENT_TOKEN_TO_SERVER, false).apply(); 56 | } 57 | // Notify UI that registration token has been received 58 | Intent gotToken = new Intent(QuickstartPreferences.GCM_TOKEN); 59 | LocalBroadcastManager.getInstance(this).sendBroadcast(gotToken); 60 | } 61 | 62 | 63 | /** 64 | * Subscribe to any GCM topics of interest, as defined by the TOPICS constant. 65 | * 66 | * @param token GCM token 67 | * @throws IOException if unable to reach the GCM PubSub service 68 | */ 69 | private void subscribeTopics(String token) throws IOException { 70 | GcmPubSub pubSub = GcmPubSub.getInstance(this); 71 | for (String topic : TOPICS) { 72 | pubSub.subscribe(token, "/topics/" + topic, null); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ios/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDisplayName 6 | ${PRODUCT_NAME} 7 | CFBundleExecutable 8 | qt-pushnotifications 9 | CFBundleGetInfoString 10 | Created by Qt/QMake 11 | CFBundleIconFile 12 | 13 | CFBundleIcons 14 | 15 | CFBundlePrimaryIcon 16 | 17 | CFBundleIconFiles 18 | 19 | AppIcon29x29.png 20 | AppIcon29x29@2x.png 21 | AppIcon40x40@2x.png 22 | AppIcon57x57.png 23 | AppIcon57x57@2x.png 24 | AppIcon60x60@2x.png 25 | 26 | 27 | 28 | CFBundleIdentifier 29 | com.example.example 30 | CFBundleName 31 | ${PRODUCT_NAME} 32 | CFBundlePackageType 33 | APPL 34 | CFBundleShortVersionString 35 | 0.1 36 | CFBundleSignature 37 | ???? 38 | CFBundleVersion 39 | 2 40 | ITSAppUsesNonExemptEncryption 41 | 42 | LSRequiresIPhoneOS 43 | 44 | NOTE 45 | This file was generated by Qt/QMake. 46 | UILaunchImageFile 47 | LaunchImage 48 | UILaunchImages 49 | 50 | 51 | UILaunchImageMinimumOSVersion 52 | 7.0 53 | UILaunchImageName 54 | LaunchImage-iOS7 55 | UILaunchImageOrientation 56 | Portrait 57 | UILaunchImageSize 58 | {320, 568} 59 | 60 | 61 | UILaunchImageMinimumOSVersion 62 | 7.0 63 | UILaunchImageName 64 | LaunchImage-iOS7 65 | UILaunchImageOrientation 66 | Portrait 67 | UILaunchImageSize 68 | {320, 480} 69 | 70 | 71 | UILaunchImages~ipad 72 | 73 | 74 | UILaunchImageMinimumOSVersion 75 | 7.0 76 | UILaunchImageName 77 | LaunchImage-iOS7-Portrait 78 | UILaunchImageOrientation 79 | Portrait 80 | UILaunchImageSize 81 | {768, 1024} 82 | 83 | 84 | UILaunchImageMinimumOSVersion 85 | 7.0 86 | UILaunchImageName 87 | LaunchImage-iOS7 88 | UILaunchImageOrientation 89 | Portrait 90 | UILaunchImageSize 91 | {320, 568} 92 | 93 | 94 | UILaunchImageMinimumOSVersion 95 | 7.0 96 | UILaunchImageName 97 | LaunchImage-iOS7 98 | UILaunchImageOrientation 99 | Portrait 100 | UILaunchImageSize 101 | {320, 480} 102 | 103 | 104 | UIRequiresFullScreen 105 | 106 | UISupportedInterfaceOrientations 107 | 108 | UIInterfaceOrientationPortrait 109 | UIInterfaceOrientationPortraitUpsideDown 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /source/cpp/misc/pushnotification.cpp: -------------------------------------------------------------------------------- 1 | #include "pushnotification.h" 2 | #include 3 | 4 | #ifdef Q_OS_ANDROID 5 | #include 6 | #include 7 | #endif 8 | 9 | QString g_gcmRegistrationToken = ""; 10 | std::mutex g_RegistrationTokenMutex; 11 | 12 | PushNotificationRegistrationTokenHandler::PushNotificationRegistrationTokenHandler(QObject *parent) 13 | : QObject(parent), 14 | m_gcmToken(""), 15 | m_apnsToken("") 16 | { 17 | } 18 | 19 | QObject* PushNotificationRegistrationTokenHandler::pushNotificationRegistrationTokenProvider(QQmlEngine *engine, QJSEngine *scriptEngine){ 20 | Q_UNUSED(engine) 21 | Q_UNUSED(scriptEngine) 22 | return PushNotificationRegistrationTokenHandler::instance(); 23 | } 24 | 25 | PushNotificationRegistrationTokenHandler* PushNotificationRegistrationTokenHandler::instance() { 26 | static PushNotificationRegistrationTokenHandler* pushNotificationRegistrationTokenHandler = new PushNotificationRegistrationTokenHandler(); 27 | return pushNotificationRegistrationTokenHandler; 28 | } 29 | 30 | void PushNotificationRegistrationTokenHandler::setGcmRegistrationToken(const QString& gcmToken){ 31 | m_gcmToken = gcmToken; 32 | } 33 | 34 | QString PushNotificationRegistrationTokenHandler::getGcmRegistrationToken(){ 35 | if(g_gcmRegistrationToken != ""){ 36 | g_RegistrationTokenMutex.lock(); 37 | setGcmRegistrationToken(g_gcmRegistrationToken); 38 | g_gcmRegistrationToken = ""; 39 | g_RegistrationTokenMutex.unlock(); 40 | } 41 | return m_gcmToken; 42 | } 43 | 44 | QString PushNotificationRegistrationTokenHandler::getAPNSRegistrationToken() const{ 45 | return m_apnsToken; 46 | } 47 | 48 | void PushNotificationRegistrationTokenHandler::setAPNSRegistrationToken(const QString& apnsToken){ 49 | m_apnsToken = apnsToken; 50 | apnsRegistrationTokenChanged(); //emit signal 51 | } 52 | 53 | PushNotificationRegistrationTokenHandler::~PushNotificationRegistrationTokenHandler(){ 54 | } 55 | 56 | 57 | 58 | 59 | #ifdef Q_OS_ANDROID 60 | static void gcmTokenResult(JNIEnv* /*env*/ env, jobject obj, jstring gcmToken) 61 | { 62 | const char* nativeString = env->GetStringUTFChars(gcmToken, 0); 63 | qDebug() << "GCM Token is:" << nativeString; 64 | 65 | //the following is kind of a "hack". We can't use 66 | // PushNotificationRegistrationTokenHandler::instance()->setGcmRegistrationToken(QString(nativeString)); 67 | //directly, as this function most probably get's called before the GUI application is initialized. Using a 68 | //QObject before the application is initialized results in undefined behavior and might crash the application. 69 | //So in order to avoid that we are storing the gcm registration token in a global variable. The 70 | //PushNotificationRegistrationTokenHandler::getGcmRegistrationToken() method copies the value in it's own 71 | //private member variable, as soon as the gcm registration token is available. 72 | //Just to be on the safe side, we are protecting the variable with a mutex. 73 | g_RegistrationTokenMutex.lock(); 74 | g_gcmRegistrationToken = QString(nativeString); 75 | g_RegistrationTokenMutex.unlock(); 76 | 77 | } 78 | 79 | 80 | // create a vector with all our JNINativeMethod(s) 81 | static JNINativeMethod methods[] = { 82 | { "sendGCMToken", // const char* function name; 83 | "(Ljava/lang/String;)V", 84 | (void *)gcmTokenResult // function pointer 85 | } 86 | }; 87 | 88 | 89 | // this method is called automatically by Java VM 90 | // after the .so file is loaded 91 | JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) 92 | { 93 | JNIEnv* env; 94 | // get the JNIEnv pointer. 95 | if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) 96 | != JNI_OK) { 97 | return JNI_ERR; 98 | } 99 | 100 | // search for Java class which declares the native methods 101 | jclass javaClass = env->FindClass("com/example/example/JavaNatives"); 102 | if (!javaClass) 103 | return JNI_ERR; 104 | 105 | // register our native methods 106 | if (env->RegisterNatives(javaClass, methods, 107 | sizeof(methods) / sizeof(methods[0])) < 0) { 108 | return JNI_ERR; 109 | } 110 | 111 | return JNI_VERSION_1_6; 112 | } 113 | #endif 114 | -------------------------------------------------------------------------------- /source/java/Vibrate.java: -------------------------------------------------------------------------------- 1 | //adapt that accordingly 2 | package com.example.example; 3 | import com.example.example.R; 4 | 5 | import android.content.Context; 6 | import android.os.Vibrator; 7 | import android.app.Activity; 8 | import android.os.Bundle; 9 | 10 | import android.content.BroadcastReceiver; 11 | import android.content.Intent; 12 | import android.content.IntentFilter; 13 | import android.content.SharedPreferences; 14 | import android.preference.PreferenceManager; 15 | import android.support.v4.content.LocalBroadcastManager; 16 | import android.support.v7.app.AppCompatActivity; 17 | import android.util.Log; 18 | //import android.widget.ProgressBar; 19 | //import android.widget.TextView; 20 | 21 | import com.google.android.gms.common.ConnectionResult; 22 | import com.google.android.gms.common.GoogleApiAvailability; 23 | 24 | public class Vibrate extends org.qtproject.qt5.android.bindings.QtActivity 25 | { 26 | // start GCM 27 | private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; 28 | private static final String TAG = "MainActivity"; 29 | 30 | private BroadcastReceiver mRegistrationBroadcastReceiver; 31 | //private KeyStore m_keyStore; 32 | 33 | @Override 34 | public void onCreate(Bundle savedInstanceState) 35 | { 36 | super.onCreate(savedInstanceState); 37 | 38 | mRegistrationBroadcastReceiver = new BroadcastReceiver() 39 | { 40 | @Override 41 | public void onReceive(Context context, Intent intent) 42 | { 43 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 44 | String token = sharedPreferences.getString(QuickstartPreferences.GCM_TOKEN, ""); 45 | Log.i("Activity", token); 46 | JavaNatives.sendGCMToken(token); 47 | } 48 | }; 49 | 50 | if (checkPlayServices()) 51 | { 52 | // Start IntentService to register this application with GCM. 53 | Intent intent = new Intent(this, RegistrationIntentService.class); 54 | startService(intent); 55 | } 56 | } 57 | 58 | @Override 59 | protected void onResume() 60 | { 61 | super.onResume(); 62 | LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver, 63 | new IntentFilter(QuickstartPreferences.GCM_TOKEN)); 64 | } 65 | 66 | @Override 67 | protected void onSaveInstanceState(Bundle outState) { 68 | super.onSaveInstanceState(outState); 69 | } 70 | 71 | @Override 72 | protected void onPause() 73 | { 74 | LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver); 75 | super.onPause(); 76 | } 77 | 78 | // 79 | // Check the device to make sure it has the Google Play Services APK. If 80 | // it doesn't, display a dialog that allows users to download the APK from 81 | // the Google Play Store or enable it in the device's system settings. 82 | // 83 | private boolean checkPlayServices() 84 | { 85 | GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance(); 86 | int resultCode = apiAvailability.isGooglePlayServicesAvailable(this); 87 | if (resultCode != ConnectionResult.SUCCESS) 88 | { 89 | if (apiAvailability.isUserResolvableError(resultCode)) 90 | { 91 | apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST).show(); 92 | } 93 | else 94 | { 95 | Log.i(TAG, "This device is not supported."); 96 | finish(); 97 | } 98 | return false; 99 | } 100 | return true; 101 | } 102 | 103 | 104 | 105 | // start vibrate 106 | public static Vibrator m_vibrator; 107 | public static Vibrate m_istance; 108 | public Vibrate() 109 | { 110 | m_istance = this; 111 | } 112 | public static void start(int x) 113 | { 114 | if (m_vibrator == null) 115 | { 116 | if (m_istance != null) 117 | { 118 | m_vibrator = (Vibrator) m_istance.getSystemService(Context.VIBRATOR_SERVICE); 119 | m_vibrator.vibrate(x); 120 | } 121 | } 122 | else m_vibrator.vibrate(x); 123 | } 124 | // end vibrate 125 | 126 | } 127 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 85 | 86 | 89 | 90 | 91 | 92 | 93 | 94 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 117 | 118 | 119 | 120 | 121 | --------------------------------------------------------------------------------