NotificationManager: use GIO instead of libportal

GIO's notification implementation makes the code more readable and has
the advantage of supporting multiple notification specifications.

By default we will now use freedesktop notifications when running
without `.desktop` file and XDG-portal notifications when running
with `.desktop` file.

To prevent dynamic notification updates from arriving in wrong order at
the desktop environment, we need to manually queue them up and make sure
that there is at least 200ms delay between updates.
This commit is contained in:
Julian Winkler
2025-07-04 16:41:48 +02:00
parent 9de08ab922
commit 3708cc990e
4 changed files with 135 additions and 97 deletions

View File

@@ -1,15 +1,13 @@
package android.app;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import android.app.Notification.MediaStyle;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -17,9 +15,6 @@ public class NotificationManager {
private static int mpris_notification_id = -1;
// store Intents in map, as long as Parcelable serialization is not yet implemented
private static Map<Integer, Intent> intents = new HashMap<Integer, Intent>();
public void cancelAll() {}
public void notify(String tag, int id, Notification notification) {
@@ -37,23 +32,26 @@ public class NotificationManager {
int intentType = -1;
String actionName = null;
String className = null;
String data = null;
if (action.intent != null) {
intentType = action.intent.type;
actionName = action.intent.intent.getAction();
className = action.intent.intent.getComponent() != null ? action.intent.intent.getComponent().getClassName() : null;
data = action.intent.intent.getData() != null ? action.intent.intent.getData().toString() : null;
}
nativeAddAction(builder, action.title, intentType, actionName, className);
nativeAddAction(builder, action.title, intentType, actionName, className, data);
}
int intentType = -1;
String actionName = null;
String className = null;
String data = null;
if (notification.intent != null) {
intentType = notification.intent.type;
actionName = notification.intent.intent.getAction();
className = notification.intent.intent.getComponent() != null ? notification.intent.intent.getComponent().getClassName() : null;
intents.put(id, notification.intent.intent);
data = notification.intent.intent.getData() != null ? notification.intent.intent.getData().toString() : null;
}
nativeShowNotification(builder, id, notification.title, notification.text, notification.iconPath, notification.ongoing, intentType, actionName, className);
nativeShowNotification(builder, id, notification.title, notification.text, notification.iconPath, notification.ongoing, intentType, actionName, className, data);
}
public void notify(int id, Notification notification) {
@@ -80,16 +78,14 @@ public class NotificationManager {
cancel(null, id);
}
protected static void notificationActionCallback(int id, int intentType, String action, String className) {
protected static void notificationActionCallback(int intentType, String action, String className, String data) {
Context context = Context.this_application;
action = "".equals(action) ? null : action;
className = "".equals(className) ? null : className;
Intent intent = intents.remove(id);
if (intent == null || !Objects.equals(action, intent.getAction()) || !Objects.equals(className, intent.getComponent() == null ? null : intent.getComponent().getClassName())) {
intent = new Intent(action);
if (className != null) {
intent.setComponent(new ComponentName(context, className));
}
data = "".equals(data) ? null : data;
Intent intent = new Intent(action, data != null ? Uri.parse(data) : null);
if (className != null) {
intent.setComponent(new ComponentName(context, className));
}
if (intentType == 0) { // type Activity
context.startActivity(intent);
@@ -103,8 +99,8 @@ public class NotificationManager {
public void createNotificationChannel(NotificationChannel channel) {}
protected native long nativeInitBuilder();
protected native void nativeAddAction(long builder, String title, int intentType, String action, String className);
protected native void nativeShowNotification(long builder, int id, String title, String text, String iconPath, boolean ongoing, int intentType, String action, String className);
protected native void nativeAddAction(long builder, String title, int intentType, String action, String className, String data);
protected native void nativeShowNotification(long builder, int id, String title, String text, String iconPath, boolean ongoing, int intentType, String action, String className, String data);
protected native void nativeShowMPRIS(String packageName, String identiy);
protected native void nativeCancel(int id);
protected native void nativeCancelMPRIS();