Android: Fix push notifications and permission handling
This commit is contained in:
parent
8f6ed7c8a0
commit
48bed7bf3e
@ -26,6 +26,8 @@ import java.util.Random;
|
|||||||
public class NymeaAppNotificationService extends FirebaseMessagingService {
|
public class NymeaAppNotificationService extends FirebaseMessagingService {
|
||||||
|
|
||||||
private static final String TAG = "nymea-app: NymeaAppNotificationService";
|
private static final String TAG = "nymea-app: NymeaAppNotificationService";
|
||||||
|
private static final String DEFAULT_CHANNEL_ID = "default-channel";
|
||||||
|
private static final String DEFAULT_CHANNEL_NAME = "nymea notifications";
|
||||||
|
|
||||||
private int hashId(String id) {
|
private int hashId(String id) {
|
||||||
int hash = 7;
|
int hash = 7;
|
||||||
@ -59,13 +61,28 @@ public class NymeaAppNotificationService extends FirebaseMessagingService {
|
|||||||
|
|
||||||
super.onMessageReceived(remoteMessage);
|
super.onMessageReceived(remoteMessage);
|
||||||
|
|
||||||
|
RemoteMessage.Notification notification = remoteMessage.getNotification();
|
||||||
|
String title = notification != null ? notification.getTitle() : null;
|
||||||
|
String body = notification != null ? notification.getBody() : null;
|
||||||
|
if (title == null) {
|
||||||
|
title = remoteMessage.getData().get("title");
|
||||||
|
}
|
||||||
|
if (body == null) {
|
||||||
|
body = remoteMessage.getData().get("body");
|
||||||
|
}
|
||||||
|
|
||||||
Log.d(TAG, "Notification from: " + remoteMessage.getFrom());
|
Log.d(TAG, "Notification from: " + remoteMessage.getFrom());
|
||||||
Log.d(TAG, "Notification title: " + remoteMessage.getNotification().getTitle());
|
Log.d(TAG, "Notification title: " + title);
|
||||||
Log.d(TAG, "Notification body: " + remoteMessage.getNotification().getBody());
|
Log.d(TAG, "Notification body: " + body);
|
||||||
Log.d(TAG, "Notification priority: " + remoteMessage.getPriority());
|
Log.d(TAG, "Notification priority: " + remoteMessage.getPriority());
|
||||||
Log.d(TAG, "Notification data: " + remoteMessage.getData());
|
Log.d(TAG, "Notification data: " + remoteMessage.getData());
|
||||||
Log.d(TAG, "Notification message ID: " + remoteMessage.getMessageId());
|
Log.d(TAG, "Notification message ID: " + remoteMessage.getMessageId());
|
||||||
|
|
||||||
|
if (title == null && body == null && remoteMessage.getData().isEmpty()) {
|
||||||
|
Log.w(TAG, "No notification payload received, skipping notification creation.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Intent intent = new Intent(this, NymeaAppActivity.class);
|
Intent intent = new Intent(this, NymeaAppActivity.class);
|
||||||
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
intent.setAction(Intent.ACTION_SEND);
|
intent.setAction(Intent.ACTION_SEND);
|
||||||
@ -79,21 +96,36 @@ public class NymeaAppNotificationService extends FirebaseMessagingService {
|
|||||||
// Because of this, we need to dynamically fetch the resource from the package resources
|
// Because of this, we need to dynamically fetch the resource from the package resources
|
||||||
int resId = getResources().getIdentifier("notificationicon", "drawable", getPackageName());
|
int resId = getResources().getIdentifier("notificationicon", "drawable", getPackageName());
|
||||||
Log.d(TAG, "Notification icon resource: " + resId + " Package:" + getPackageName());
|
Log.d(TAG, "Notification icon resource: " + resId + " Package:" + getPackageName());
|
||||||
|
if (resId == 0) {
|
||||||
|
resId = getApplicationInfo().icon;
|
||||||
|
Log.w(TAG, "Notification icon resource missing, using application icon: " + resId);
|
||||||
|
}
|
||||||
|
|
||||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
if (notificationManager == null) {
|
||||||
|
Log.w(TAG, "NotificationManager not available, cannot display notification.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String channelId = resolveStringResource("notification_channel_id", DEFAULT_CHANNEL_ID);
|
||||||
|
String channelName = resolveStringResource("notification_channel_name", DEFAULT_CHANNEL_NAME);
|
||||||
|
|
||||||
// Since android Oreo notification channel is needed.
|
// Since android Oreo notification channel is needed.
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
NotificationChannel channel = new NotificationChannel("default-channel", "Default notification channel for nymea-app", NotificationManager.IMPORTANCE_HIGH);
|
NotificationChannel existingChannel = notificationManager.getNotificationChannel(channelId);
|
||||||
|
if (existingChannel == null) {
|
||||||
|
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
|
||||||
notificationManager.createNotificationChannel(channel);
|
notificationManager.createNotificationChannel(channel);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
|
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId)
|
||||||
.setContentTitle(remoteMessage.getNotification().getTitle())
|
.setContentTitle(title)
|
||||||
.setContentText(remoteMessage.getNotification().getBody())
|
.setContentText(body)
|
||||||
.setSmallIcon(resId)
|
.setSmallIcon(resId)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setContentIntent(pendingIntent);
|
.setContentIntent(pendingIntent)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||||
|
|
||||||
boolean sound = remoteMessage.getData().get("sound") == null || remoteMessage.getData().get("sound").equals("true");
|
boolean sound = remoteMessage.getData().get("sound") == null || remoteMessage.getData().get("sound").equals("true");
|
||||||
Log.d(TAG, "Notification sound enabled: " + (sound ? "true" : "false"));
|
Log.d(TAG, "Notification sound enabled: " + (sound ? "true" : "false"));
|
||||||
@ -114,4 +146,19 @@ public class NymeaAppNotificationService extends FirebaseMessagingService {
|
|||||||
Log.d(TAG, "Posting Notification: " + remoteMessage.getMessageId());
|
Log.d(TAG, "Posting Notification: " + remoteMessage.getMessageId());
|
||||||
notificationManager.notify(0, notificationBuilder.build());
|
notificationManager.notify(0, notificationBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String resolveStringResource(String resourceName, String fallback) {
|
||||||
|
int resId = getResources().getIdentifier(resourceName, "string", getPackageName());
|
||||||
|
if (resId != 0) {
|
||||||
|
try {
|
||||||
|
String resolved = getString(resId);
|
||||||
|
if (resolved != null && !resolved.isEmpty()) {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
} catch (Resources.NotFoundException e) {
|
||||||
|
Log.w(TAG, "String resource not found for " + resourceName + ", using fallback");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,8 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QPermission>
|
#include <QPermission>
|
||||||
#include <QOperatingSystemVersion>
|
#include <QOperatingSystemVersion>
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QtCore/private/qandroidextras_p.h>
|
||||||
|
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
NYMEA_LOGGING_CATEGORY(dcPlatformPermissions, "PlatformPermissions")
|
NYMEA_LOGGING_CATEGORY(dcPlatformPermissions, "PlatformPermissions")
|
||||||
@ -108,8 +110,30 @@ PlatformPermissions::PermissionStatus PlatformPermissionsAndroid::checkPermissio
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case PlatformPermissions::PermissionNotifications: {
|
case PlatformPermissions::PermissionNotifications: {
|
||||||
|
if (QOperatingSystemVersion::current() < QOperatingSystemVersion(QOperatingSystemVersion::Android, 13)) {
|
||||||
|
status = PermissionStatusGranted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto futureResult = QtAndroidPrivate::checkPermission("android.permission.POST_NOTIFICATIONS");
|
||||||
|
QtAndroidPrivate::PermissionResult result = futureResult.result();
|
||||||
|
switch (result) {
|
||||||
|
case QtAndroidPrivate::Authorized:
|
||||||
|
qCDebug(dcPlatformPermissions()) << "Notifications permission already granted.";
|
||||||
|
status = PermissionStatusGranted;
|
||||||
|
break;
|
||||||
|
case QtAndroidPrivate::Denied:
|
||||||
|
qCDebug(dcPlatformPermissions()) << "Notifications permission denied.";
|
||||||
|
status = PermissionStatusDenied;
|
||||||
|
break;
|
||||||
|
case QtAndroidPrivate::Undetermined:
|
||||||
|
qCDebug(dcPlatformPermissions()) << "Notifications permission not yet requested. Requesting...";
|
||||||
|
status = PermissionStatusNotDetermined;
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -154,8 +178,7 @@ void PlatformPermissionsAndroid::requestPermission(PlatformPermissions::Permissi
|
|||||||
}
|
}
|
||||||
case PlatformPermissions::PermissionLocalNetwork: {
|
case PlatformPermissions::PermissionLocalNetwork: {
|
||||||
QFuture permission_request = QtAndroidPrivate::requestPermission("android.permission.POST_NOTIFICATIONS");
|
QFuture permission_request = QtAndroidPrivate::requestPermission("android.permission.POST_NOTIFICATIONS");
|
||||||
switch(permission_request.result())
|
switch(permission_request.result()) {
|
||||||
{
|
|
||||||
case QtAndroidPrivate::Undetermined:
|
case QtAndroidPrivate::Undetermined:
|
||||||
qWarning() << "Permission for posting notifications undetermined!";
|
qWarning() << "Permission for posting notifications undetermined!";
|
||||||
break;
|
break;
|
||||||
@ -169,6 +192,31 @@ void PlatformPermissionsAndroid::requestPermission(PlatformPermissions::Permissi
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PlatformPermissions::PermissionNotifications: {
|
||||||
|
if (QOperatingSystemVersion::current() < QOperatingSystemVersion(QOperatingSystemVersion::Android, 13)) {
|
||||||
|
qCDebug(dcPlatformPermissions()) << "Notifications permission implicitly granted on Android < 13.";
|
||||||
|
emit s_instance->notificationsPermissionChanged();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture permission_request = QtAndroidPrivate::requestPermission("android.permission.POST_NOTIFICATIONS");
|
||||||
|
auto result = permission_request.result();
|
||||||
|
switch(result) {
|
||||||
|
case QtAndroidPrivate::Undetermined:
|
||||||
|
qWarning() << "Permission for posting notifications undetermined!";
|
||||||
|
s_instance->m_requestedButDeniedPermissions.append(platformPermission);
|
||||||
|
break;
|
||||||
|
case QtAndroidPrivate::Authorized:
|
||||||
|
qDebug() << "Permission for posting notifications authorized";
|
||||||
|
break;
|
||||||
|
case QtAndroidPrivate::Denied:
|
||||||
|
qWarning() << "Permission for posting notifications denied!";
|
||||||
|
s_instance->m_requestedButDeniedPermissions.append(platformPermission);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
emit s_instance->notificationsPermissionChanged();
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
qCWarning(dcPlatformPermissions()) << "Requested platform permission" << platformPermission << "but is not implemented yet.";
|
qCWarning(dcPlatformPermissions()) << "Requested platform permission" << platformPermission << "but is not implemented yet.";
|
||||||
break;
|
break;
|
||||||
@ -276,4 +324,3 @@ void PlatformPermissionsAndroid::requestPermission(PlatformPermissions::Permissi
|
|||||||
// emit s_instance->backgroundLocationPermissionChanged();
|
// emit s_instance->backgroundLocationPermissionChanged();
|
||||||
// emit s_instance->notificationsPermissionChanged();
|
// emit s_instance->notificationsPermissionChanged();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|||||||
@ -33,11 +33,14 @@
|
|||||||
PlatformPermissions *PlatformPermissions::instance()
|
PlatformPermissions *PlatformPermissions::instance()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
return new PlatformPermissionsAndroid();
|
static PlatformPermissionsAndroid instance;
|
||||||
|
return &instance;
|
||||||
#elif defined Q_OS_IOS
|
#elif defined Q_OS_IOS
|
||||||
return new PlatformPermissionsIOS();
|
static PlatformPermissionsIOS instance;
|
||||||
|
return &instance;
|
||||||
#else
|
#else
|
||||||
return new PlatformPermissions();
|
static PlatformPermissions instance;
|
||||||
|
return &instance;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,4 +88,3 @@ PlatformPermissions::PermissionStatus PlatformPermissions::checkPermission(Permi
|
|||||||
Q_UNUSED(permission)
|
Q_UNUSED(permission)
|
||||||
return PermissionStatusGranted;
|
return PermissionStatusGranted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include "pushnotifications.h"
|
#include "pushnotifications.h"
|
||||||
#include "platformhelper.h"
|
#include "platformhelper.h"
|
||||||
|
#include "platformintegration/platformpermissions.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
@ -86,6 +87,9 @@ void PushNotifications::setEnabled(bool enabled)
|
|||||||
void PushNotifications::registerForPush()
|
void PushNotifications::registerForPush()
|
||||||
{
|
{
|
||||||
#if defined Q_OS_ANDROID && defined WITH_FIREBASE
|
#if defined Q_OS_ANDROID && defined WITH_FIREBASE
|
||||||
|
// Ensure we have runtime permission to post notifications (Android 13+).
|
||||||
|
PlatformPermissions::instance()->requestPermission(PlatformPermissions::PermissionNotifications);
|
||||||
|
|
||||||
qDebug() << "Checking for play services";
|
qDebug() << "Checking for play services";
|
||||||
jboolean playServicesAvailable = QJniObject::callStaticMethod<jboolean>("io.guh.nymeaapp.NymeaAppNotificationService", "checkPlayServices", "()Z");
|
jboolean playServicesAvailable = QJniObject::callStaticMethod<jboolean>("io.guh.nymeaapp.NymeaAppNotificationService", "checkPlayServices", "()Z");
|
||||||
if (playServicesAvailable) {
|
if (playServicesAvailable) {
|
||||||
@ -102,8 +106,9 @@ void PushNotifications::registerForPush()
|
|||||||
firebase::messaging::Initialize(*m_firebaseApp, this);
|
firebase::messaging::Initialize(*m_firebaseApp, this);
|
||||||
firebase::messaging::SetListener(this);
|
firebase::messaging::SetListener(this);
|
||||||
|
|
||||||
// (Optional, Android 13+): Benachrichtigungs-Erlaubnis anfragen
|
// Android 13+ requires the POST_NOTIFICATIONS runtime permission. Request it here so
|
||||||
// firebase::messaging::RequestPermission();
|
// Firebase is allowed to show notifications when the app is backgrounded or closed.
|
||||||
|
firebase::messaging::RequestPermission();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -89,6 +89,7 @@
|
|||||||
|
|
||||||
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notificationicon"/>
|
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notificationicon"/>
|
||||||
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/notification_icon_color"/>
|
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/notification_icon_color"/>
|
||||||
|
<meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="@string/notification_channel_id"/>
|
||||||
|
|
||||||
<service android:name="com.google.firebase.messaging.MessageForwardingService" android:exported="false">
|
<service android:name="com.google.firebase.messaging.MessageForwardingService" android:exported="false">
|
||||||
</service>
|
</service>
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">nymea:app</string>
|
<string name="app_name">nymea:app</string>
|
||||||
|
<string name="notification_channel_id">default-channel</string>
|
||||||
|
<string name="notification_channel_name">nymea notifications</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user