working edition of push notifications on android, to be cleaned up still

This commit is contained in:
Michael Zanetti 2018-09-18 23:20:22 +02:00
parent dd2051ec30
commit 9279db9608
24 changed files with 1294 additions and 18 deletions

6
.gitmodules vendored
View File

@ -4,3 +4,9 @@
[submodule "nymea-remoteproxy"]
path = nymea-remoteproxy
url = https://github.com/guh/nymea-remoteproxy.git
[submodule "qtcloudmessaging"]
path = qtcloudmessaging
url = https://github.com/qt/qtcloudmessaging.git
[submodule "QtFirebase"]
path = QtFirebase
url = https://github.com/Larpon/QtFirebase.git

1
QtFirebase Submodule

@ -0,0 +1 @@
Subproject commit 593032a7c613d40c55212ec10790400fd4a30c8f

View File

@ -605,6 +605,61 @@ void AWSClient::getId()
});
}
void AWSClient::registerPushNotificationEndpoint(const QString &registrationId)
{
if (!isLoggedIn()) {
qWarning() << "Not logged in at AWS. Can't register push endpoint";
return;
}
if (tokensExpired()) {
qDebug() << "Cannot register push endpoint. Need to refresh our tokens";
refreshAccessToken();
m_callQueue.append(QueuedCall("registerPushNotificationEndpoint", registrationId));
return;
}
qDebug() << "Registering push notification endpoint.";
QUrl url(QString("https://%1/notifications/endpoints/%2").arg(m_configs.at(m_usedConfigIndex).apiEndpoint).arg(m_userId));
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("x-api-idToken", m_idToken);
qDebug() << "POST" << url.toString();
qDebug() << "HEADERS:";
foreach (const QByteArray &hdr, request.rawHeaderList()) {
qDebug() << hdr << ":" << request.rawHeader(hdr);
}
QVariantMap payload;
payload.insert("registrationId", registrationId);
payload.insert("channel", "GCM");
payload.insert("mobileDeviceDisplayName", "test device");
payload.insert("mobileDeviceUuid", "12345678");
QJsonDocument jsonDoc = QJsonDocument::fromVariant(payload);
QNetworkReply *reply = m_nam->post(request, jsonDoc.toJson(QJsonDocument::Compact));
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
QByteArray data = reply->readAll();
if (reply->error() != QNetworkReply::NoError) {
qWarning() << "Error registering push notification endpoint:" << reply->error() << reply->errorString() << qUtf8Printable(data);
// emit deleteAccountResult(LoginErrorUnknownError);
return;
}
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
qWarning() << "Failed to parse JSON from server" << error.errorString() << qUtf8Printable(data);
// emit deleteAccountResult(LoginErrorUnknownError);
return;
}
// emit deleteAccountResult(LoginErrorNoError);
// logout();
qDebug() << "Push notification endpoint registered" << data;
});
}
QByteArray AWSClient::idToken() const
{
return m_idToken;

View File

@ -118,6 +118,8 @@ public:
Q_INVOKABLE bool postToMQTT(const QString &boxId, const QString &timestamp, std::function<void(bool)> callback);
Q_INVOKABLE void getId();
Q_INVOKABLE void registerPushNotificationEndpoint(const QString &registrationId);
bool tokensExpired() const;
QByteArray idToken() const;
QString cognitoIdentityId() const;

View File

@ -95,13 +95,13 @@ NymeaTransportInterface::ConnectionState CloudTransport::connectionState() const
void CloudTransport::sendData(const QByteArray &data)
{
qDebug() << "should send" << data;
// qDebug() << "Cloud transport: Sending data:" << data;
m_remoteproxyConnection->sendData(data);
}
void CloudTransport::ignoreSslErrors(const QList<QSslError> &errors)
{
qDebug() << "Ignoring SSL errors" << errors;
qDebug() << "CloudTransport: Ignoring SSL errors" << errors;
m_remoteproxyConnection->ignoreSslErrors(errors);
}

View File

@ -13,6 +13,7 @@ include(../config.pri)
include(../nymea-remoteproxy/libnymea-remoteproxyclient/libnymea-remoteproxyclient.pri)
QT -= gui
QT += network websockets bluetooth

View File

@ -26,6 +26,8 @@
#include <QSysInfo>
#ifdef Q_OS_ANDROID
#include <QtCloudMessaging>
#include <QtCloudMessagingFirebase>
#include <QtAndroidExtras/QtAndroid>
#endif
@ -69,6 +71,22 @@ int main(int argc, char *argv[])
Engine::instance();
QQmlApplicationEngine *engine = new QQmlApplicationEngine();
#ifdef Q_OS_ANDROID
QCloudMessaging *pushServices = new QCloudMessaging();
QCloudMessagingFirebaseProvider *m_firebaseService = new QCloudMessagingFirebaseProvider();
QVariantMap provider_params;
provider_params["SERVER_API_KEY"] = "AIzaSyAvKQXY4-kZw9Y7MTqVDoF2XCvC7fnhKUs";
pushServices->registerProvider("GoogleFireBase", m_firebaseService, provider_params);
pushServices->connectClient("GoogleFireBase", "nymea:app", QVariantMap());
pushServices->subscribeToChannel("ChatRoom", "GoogleFireBase", "nymea:app");
engine->rootContext()->setContextProperty("pushServices", pushServices);
#endif
#ifdef BRANDING
engine->rootContext()->setContextProperty("appBranding", BRANDING);
#else

View File

@ -2,7 +2,7 @@ TEMPLATE=app
TARGET=nymea-app
include(../config.pri)
QT += network qml quick quickcontrols2 svg websockets bluetooth
QT += network qml quick quickcontrols2 svg websockets bluetooth #cloudmessaging
INCLUDEPATH += $$top_srcdir/libnymea-common \
$$top_srcdir/libnymea-app-core
@ -32,26 +32,34 @@ equals(STYLES_PATH, "") {
RESOURCES += $${STYLES_PATH}/styles.qrc
}
contains(ANDROID_TARGET_ARCH,armeabi-v7a) {
ANDROID_EXTRA_LIBS = \
/opt/android-openssl/prebuilt/armeabi-v7a/libcrypto.so \
/opt/android-openssl/prebuilt/armeabi-v7a/libssl.so
}
android {
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/../packaging/android
QT += androidextras
# QTFIREBASE_CONFIG+=messaging
# QTFIREBASE_SDK_PATH=/opt/firebase_cpp_sdk/
# include(../QtFirebase/qtfirebase.pri)
INCLUDEPATH += /opt/firebase_cpp_sdk/include
QT += androidextras cloudmessagingfirebase
DISTFILES += \
$$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml \
$$ANDROID_PACKAGE_SOURCE_DIR/google-services.json \
$$ANDROID_PACKAGE_SOURCE_DIR/gradle/wrapper/gradle-wrapper.jar \
$$ANDROID_PACKAGE_SOURCE_DIR/gradlew \
$$ANDROID_PACKAGE_SOURCE_DIR/res/values/libs.xml \
$$ANDROID_PACKAGE_SOURCE_DIR/build.gradle \
$$ANDROID_PACKAGE_SOURCE_DIR/gradle/wrapper/gradle-wrapper.properties \
$$ANDROID_PACKAGE_SOURCE_DIR/gradlew.bat \
$$ANDROID_PACKAGE_SOURCE_DIR/src/io/guh/nymeaapp/NymeaAppActivity.java \
$$ANDROID_PACKAGE_SOURCE_DIR/src/io/guh/nymeaapp/NymeaAppNotificationService.java \
$$ANDROID_PACKAGE_SOURCE_DIR/LICENSE
ANDROID_EXTRA_LIBS = \
/opt/android-openssl/prebuilt/armeabi-v7a/libcrypto.so \
/opt/android-openssl/prebuilt/armeabi-v7a/libssl.so
}
macx: {

View File

@ -160,7 +160,7 @@ Page {
spacing: app.margins
visible: Engine.deviceManager.fetchingData
BusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter
Layout.alignment: Qt.AlignHCenter
running: parent.visible
}
Label {

View File

@ -6,6 +6,8 @@ import Qt.labs.settings 1.0
import QtQuick.Window 2.3
import Nymea 1.0
//import QtFirebase 1.0
ApplicationWindow {
id: app
visible: true
@ -51,6 +53,10 @@ ApplicationWindow {
Component.onCompleted: {
pageStack.push(Qt.resolvedUrl("connection/ConnectPage.qml"))
var clientUuid = pushServices.clientToken("GoogleFireBase", "nymea:app");
print("Messaging client uuid:", clientUuid)
Engine.awsClient.registerPushNotificationEndpoint(clientUuid);
}
Connections {
@ -375,6 +381,58 @@ ApplicationWindow {
}
}
Connections {
target:pushServices
onMessageReceived:{
console.log("Message to " + providerId + " service to " + clientId + " client.")
console.log("Message: " + message)
var msg_in_json = JSON.parse(message);
// Example to respond to embedded system request:
if (msg_in_json.command === "REQUESTING_TEMPERATURE")
embeddedPublishTemperatureToServer(msg_in_json.serverID, mydevicecommand.getTemperature());
// Or firebase the message itself is a container of the info.
updateGameNotification(message);
}
onServiceStateUpdated: {
print("push service state updated", state)
}
// Own Uuid to be used or broadcasted to server.
onClientTokenReceived: {
console.log("MY Uuid:"+rid)
// Id this is server code:
serverUuid = rid;
// Id this is client code:
clientUuid = rid;
}
}
// Messaging {
// id: messaging
// onReadyChanged: {
// App.log("Messaging.ready", ready)
// }
// onTokenChanged: {
// App.log("Messaging.token", token)
// }
// onDataChanged: {
// App.log("Messaging.data", JSON.stringify(data))
// }
// onMessageReceived: {
// App.log("onMessageReceived","Messaging.data", JSON.stringify(data))
// }
// }
KeyboardLoader {
id: keyboardRect
anchors { left: parent.left; bottom: parent.bottom; right: parent.right }

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<manifest package="io.guh.nymeaapp" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="nymea:app" android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="nymea:app" android:screenOrientation="unspecified" android:launchMode="singleTop">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="io.guh.nymeaapp.NymeaAppActivity" android:label="nymea:app" android:screenOrientation="unspecified" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@ -32,6 +32,8 @@
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notifyicon" />
<!-- Splash screen -->
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splash"/>
<meta-data android:name="android.app.splash_screen_sticky" android:value="true"/>
@ -55,15 +57,24 @@
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
* none - useful for apps that don't use any of the above Qt modules
-->
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
<meta-data android:name="android.app.extract_android_style" android:value="minimal"/>
<!-- extract android style -->
</activity>
</activity>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
<service android:name="com.google.firebase.messaging.MessageForwardingService" android:exported="false">
</service>
<service android:name="io.guh.nymeaapp.NymeaAppNotificationService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
</application>
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16"/>
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="26"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.

View File

@ -1,16 +1,25 @@
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.1.0'
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.google.gms:google-services:3.2.0'
}
}
allprojects {
repositories {
google()
jcenter()
maven {
url "https://maven.google.com"
}
flatDir {
dirs "/opt/firebase_cpp_sdk/libs/android"
}
}
}
@ -18,8 +27,14 @@ apply plugin: 'com.android.application'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.android.gms:play-services-base:15.0.1'
compile 'com.google.firebase:firebase-core:16.0.1'
compile 'com.google.firebase:firebase-messaging:17.3.0'
compile 'com.google.firebase.messaging.cpp:firebase_messaging_cpp@aar'
}
apply plugin: 'com.google.gms.google-services'
android {
/*******************************************************
* The following variables:

View File

@ -0,0 +1,42 @@
{
"project_info": {
"project_number": "484445295371",
"firebase_url": "https://nymea-app.firebaseio.com",
"project_id": "nymea-app",
"storage_bucket": "nymea-app.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:484445295371:android:c90f21beb977a174",
"android_client_info": {
"package_name": "io.guh.nymeaapp"
}
},
"oauth_client": [
{
"client_id": "484445295371-3tu9tfct101uf8hrqt782fb4kfidvhmq.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAvKQXY4-kZw9Y7MTqVDoF2XCvC7fnhKUs"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,44 @@
package io.guh.nymeaapp;
import android.util.Log;
import android.content.Intent;
import android.os.Bundle;
import com.google.firebase.messaging.MessageForwardingService;
public class NymeaAppActivity extends org.qtproject.qt5.android.bindings.QtActivity
{
// The key in the intent's extras that maps to the incoming message's message ID. Only sent by
// the server, GmsCore sends EXTRA_MESSAGE_ID_KEY below. Server can't send that as it would get
// stripped by the client.
private static final String EXTRA_MESSAGE_ID_KEY_SERVER = "message_id";
// An alternate key value in the intent's extras that also maps to the incoming message's message
// ID. Used by upstream, and set by GmsCore.
private static final String EXTRA_MESSAGE_ID_KEY = "google.message_id";
// The key in the intent's extras that maps to the incoming message's sender value.
private static final String EXTRA_FROM = "google.message_id";
@Override
protected void onNewIntent(Intent intent)
{
// Bundle extras = intent.getExtras();
// String from = extras.getString(EXTRA_FROM);
// String messageId = extras.getString(EXTRA_MESSAGE_ID_KEY);
// Log.d("*************** messageid", messageId);
//// Log.d("Bundle", extras);
// if (messageId == null) {
// messageId = extras.getString(EXTRA_MESSAGE_ID_KEY_SERVER);
// }
// // if (from != null && messageId != null) {
// Intent message = new Intent(this, MessageForwardingService.class);
// message.setAction(MessageForwardingService.ACTION_REMOTE_INTENT);
// message.putExtras(intent);
// message.setData(intent.getData());
// startService(message);
// // }
setIntent(intent);
}
}

View File

@ -0,0 +1,75 @@
package io.guh.nymeaapp;
import com.google.firebase.messaging.RemoteMessage;
import com.google.firebase.messaging.FirebaseMessagingService;
import android.util.Log;
import android.content.Intent;
import android.app.PendingIntent;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.net.Uri;
import android.content.Context;
import android.provider.Settings.System;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import java.util.Random;
public class NymeaAppNotificationService extends FirebaseMessagingService {
/**
* Called when message is received.
*
* @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
// [START receive_message]
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// If the application is in the foreground handle both data and notification messages here.
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
sendNotification(remoteMessage);
}
// [END receive_message]
/**
* Create and show a simple notification containing the received FCM message.
*
* @param remoteMessage FCM RemoteMessage received.
*/
private void sendNotification(RemoteMessage remoteMessage) {
Intent intent = new Intent(this, NymeaAppActivity.class);
// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, PendingIntent.FLAG_ONE_SHOT);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, 0);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "notify_001")
.setSmallIcon(R.drawable.ic_stat_notificationicon)
.setColor(0xFF57BAAE)
.setContentTitle(remoteMessage.getData().get("title"))
.setContentText(remoteMessage.getData().get("body"))
.setAutoCancel(true)
.setSound(android.provider.Settings.System.DEFAULT_RINGTONE_URI)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("notify_001", "Channel human readable title", NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
}
int notificationId = new Random().nextInt(60000);
Log.d("Posting Notification", remoteMessage.getMessageId());
notificationManager.notify(notificationId, notificationBuilder.build());
}
}

1
qtcloudmessaging Submodule

@ -0,0 +1 @@
Subproject commit bf62d04b3d6aad292b7dab46d7c4567965450537