mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-04-28 20:27:58 +03:00
notification support using libportal
This commit is contained in:
parent
b14549e639
commit
45de09a191
9 changed files with 286 additions and 19 deletions
|
@ -85,6 +85,7 @@ libtranslationlayer_so = shared_library('translation_layer_main', [
|
|||
'src/api-impl-jni/app/android_app_Activity.c',
|
||||
'src/api-impl-jni/app/android_app_AlertDialog.c',
|
||||
'src/api-impl-jni/app/android_app_Dialog.c',
|
||||
'src/api-impl-jni/app/android_app_NotificationManager.c',
|
||||
'src/api-impl-jni/audio/android_media_AudioTrack.c',
|
||||
'src/api-impl-jni/audio/android_media_SoundPool.c',
|
||||
'src/api-impl-jni/content/android_content_ClipboardManager.c',
|
||||
|
|
98
src/api-impl-jni/app/android_app_NotificationManager.c
Normal file
98
src/api-impl-jni/app/android_app_NotificationManager.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include <libportal/portal.h>
|
||||
|
||||
#include "../defines.h"
|
||||
#include "../util.h"
|
||||
|
||||
#include "../generated_headers/android_app_NotificationManager.h"
|
||||
|
||||
static XdpPortal *portal = NULL;
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_android_app_NotificationManager_nativeInitBuilder(JNIEnv *env, jobject this)
|
||||
{
|
||||
return _INTPTR(g_variant_builder_new(G_VARIANT_TYPE("aa{sv}")));
|
||||
}
|
||||
|
||||
static GVariant *serialize_intent(JNIEnv *env, jint type, jstring action_jstr, jstring className_jstr)
|
||||
{
|
||||
const char *action = action_jstr ? (*env)->GetStringUTFChars(env, action_jstr, NULL) : NULL;
|
||||
const char *className = className_jstr ? (*env)->GetStringUTFChars(env, className_jstr, NULL) : NULL;
|
||||
GVariant *intent = g_variant_new("(iss)", type, action ?: "", className ?: "");
|
||||
if (action_jstr) (*env)->ReleaseStringUTFChars(env, action_jstr, action);
|
||||
if (className_jstr) (*env)->ReleaseStringUTFChars(env, className_jstr, className);
|
||||
return intent;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeAddAction(JNIEnv *env, jobject this, jlong builder_ptr, jstring name_jstr, jint type, jstring action, jstring className)
|
||||
{
|
||||
GVariantBuilder *builder = _PTR(builder_ptr);
|
||||
g_variant_builder_open(builder, G_VARIANT_TYPE("a{sv}"));
|
||||
if (name_jstr) {
|
||||
const char *name = (*env)->GetStringUTFChars(env, name_jstr, NULL);
|
||||
g_variant_builder_add(builder, "{sv}", "label", g_variant_new_string(name));
|
||||
(*env)->ReleaseStringUTFChars(env, name_jstr, name);
|
||||
}
|
||||
g_variant_builder_add(builder, "{sv}", "action", g_variant_new_string("button-action"));
|
||||
g_variant_builder_add(builder, "{sv}", "target", serialize_intent(env, type, action, className));
|
||||
g_variant_builder_close(builder);
|
||||
}
|
||||
|
||||
static void notification_action_invoked(XdpPortal *portal, gchar *id_str, gchar *action, GVariant *parameter, gpointer user_data)
|
||||
{
|
||||
int id = atoi(id_str);
|
||||
int type;
|
||||
const char *actionName;
|
||||
const char *className;
|
||||
GVariant *target;
|
||||
JNIEnv *env = get_jni_env();
|
||||
|
||||
GVariantIter *iter = g_variant_iter_new(parameter);
|
||||
g_variant_iter_next(iter, "v", &target);
|
||||
g_variant_get(target, "(iss)", &type, &actionName, &className);
|
||||
jmethodID notificationActionCallback = _STATIC_METHOD((*env)->FindClass(env, "android/app/NotificationManager"), "notificationActionCallback", "(IILjava/lang/String;Ljava/lang/String;)V");
|
||||
(*env)->CallStaticVoidMethod(env, (*env)->FindClass(env, "android/app/NotificationManager"), notificationActionCallback, id, type, _JSTRING(actionName), _JSTRING(className));
|
||||
g_variant_iter_free(iter);
|
||||
g_variant_unref(target);
|
||||
}
|
||||
|
||||
// gnome session locks up when we send notification update before last update was processed
|
||||
static int callback_pending = 0;
|
||||
static void natification_callback(GObject* source_object, GAsyncResult* res, gpointer data)
|
||||
{
|
||||
callback_pending = 0;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeShowNotification(JNIEnv *env, jobject this, jlong builder_ptr, jint id, jstring title_jstr, jstring text_jstr, jint type, jstring action, jstring className)
|
||||
{
|
||||
if (callback_pending) {
|
||||
return;
|
||||
}
|
||||
if (!portal) {
|
||||
portal = xdp_portal_new();
|
||||
g_signal_connect(portal, "notification-action-invoked", G_CALLBACK(notification_action_invoked), NULL);
|
||||
}
|
||||
|
||||
GVariantBuilder *builder = _PTR(builder_ptr);
|
||||
GVariant *buttons = g_variant_builder_end(builder);
|
||||
|
||||
g_variant_builder_init(builder, G_VARIANT_TYPE("a{sv}"));
|
||||
if (title_jstr) {
|
||||
const char *title = (*env)->GetStringUTFChars(env, title_jstr, NULL);
|
||||
g_variant_builder_add(builder, "{sv}", "title", g_variant_new_string(title));
|
||||
(*env)->ReleaseStringUTFChars(env, title_jstr, title);
|
||||
}
|
||||
if (text_jstr) {
|
||||
const char *text = (*env)->GetStringUTFChars(env, text_jstr, NULL);
|
||||
g_variant_builder_add(builder, "{sv}", "body", g_variant_new_string(text));
|
||||
(*env)->ReleaseStringUTFChars(env, text_jstr, text);
|
||||
}
|
||||
g_variant_builder_add(builder, "{sv}", "default-action", g_variant_new_string("default-action"));
|
||||
g_variant_builder_add(builder, "{sv}", "default-action-target", serialize_intent(env, type, action, className));
|
||||
g_variant_builder_add(builder, "{sv}", "buttons", buttons);
|
||||
GVariant *variant = g_variant_builder_end(builder);
|
||||
g_variant_builder_unref(builder);
|
||||
char *id_string = g_strdup_printf("%d", id);
|
||||
xdp_portal_remove_notification(portal, id_string);
|
||||
callback_pending = 1;
|
||||
xdp_portal_add_notification(portal, id_string, variant, XDP_NOTIFICATION_FLAG_NONE, NULL, natification_callback, NULL);
|
||||
g_free(id_string);
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class android_app_NotificationManager */
|
||||
|
||||
#ifndef _Included_android_app_NotificationManager
|
||||
#define _Included_android_app_NotificationManager
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: android_app_NotificationManager
|
||||
* Method: nativeInitBuilder
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_android_app_NotificationManager_nativeInitBuilder
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
/*
|
||||
* Class: android_app_NotificationManager
|
||||
* Method: nativeAddAction
|
||||
* Signature: (JLjava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeAddAction
|
||||
(JNIEnv *, jobject, jlong, jstring, jint, jstring, jstring);
|
||||
|
||||
/*
|
||||
* Class: android_app_NotificationManager
|
||||
* Method: nativeShowNotification
|
||||
* Signature: (JILjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_android_app_NotificationManager_nativeShowNotification
|
||||
(JNIEnv *, jobject, jlong, jint, jstring, jstring, jint, jstring, jstring);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -1,5 +1,8 @@
|
|||
package android.app;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioAttributes;
|
||||
|
@ -44,8 +47,21 @@ public class Notification {
|
|||
|
||||
public Bundle extras;
|
||||
|
||||
String text;
|
||||
String title;
|
||||
List<Action> actions = new ArrayList<Action>();
|
||||
PendingIntent intent;
|
||||
|
||||
public String toString() {
|
||||
return "Notification [" + title + ", " + text + ", " + actions + "]";
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
public Builder(Context context) {}
|
||||
private Notification notification;
|
||||
|
||||
public Builder(Context context) {
|
||||
notification = new Notification();
|
||||
}
|
||||
|
||||
public Builder setWhen(long when) {return this;}
|
||||
|
||||
|
@ -67,13 +83,22 @@ public class Notification {
|
|||
|
||||
public Builder setDefaults(int defaults) {return this;}
|
||||
|
||||
public Builder setContentTitle(CharSequence title) {return this;}
|
||||
public Builder setContentTitle(CharSequence title) {
|
||||
notification.title = title != null ? title.toString() : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setContentText(CharSequence text) {return this;}
|
||||
public Builder setContentText(CharSequence text) {
|
||||
notification.text = text != null ? text.toString() : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setContentInfo(CharSequence info) {return this;}
|
||||
|
||||
public Builder setContentIntent(PendingIntent intent) {return this;}
|
||||
public Builder setContentIntent(PendingIntent intent) {
|
||||
notification.intent = intent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDeleteIntent(PendingIntent intent) {return this;}
|
||||
|
||||
|
@ -111,26 +136,44 @@ public class Notification {
|
|||
|
||||
public Builder setSound(Uri sound, AudioAttributes audioAttributes) {return this;}
|
||||
|
||||
public Builder addAction(Action action) {return this;}
|
||||
public Builder addAction(Action action) {
|
||||
notification.actions.add(action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setStyle(Style style) {return this;}
|
||||
|
||||
public Builder setExtras(Bundle extras) {return this;}
|
||||
|
||||
public Notification build() {
|
||||
return new Notification();
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Action {
|
||||
|
||||
int icon;
|
||||
String title;
|
||||
PendingIntent intent;
|
||||
|
||||
public String toString() {
|
||||
return "Action [" + icon + ", " + title + ", " + intent + "]";
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
|
||||
public Builder(int icon, CharSequence title, PendingIntent intent) {}
|
||||
private Action action;
|
||||
public Builder(int icon, CharSequence title, PendingIntent intent) {
|
||||
action = new Action();
|
||||
action.icon = icon;
|
||||
action.title = String.valueOf(title);
|
||||
action.intent = intent;
|
||||
}
|
||||
|
||||
public Builder addExtras(Bundle extras) {return this;}
|
||||
|
||||
public Action build() {
|
||||
return new Action();
|
||||
return action;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,59 @@
|
|||
package android.app;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class NotificationManager {
|
||||
public void cancelAll() {}
|
||||
|
||||
public void notify(String tag, int id, Notification notification) {
|
||||
System.out.println("notify(" + tag + ", " + id + ", " + notification + ") called");
|
||||
long builder = nativeInitBuilder();
|
||||
for (Notification.Action action : notification.actions) {
|
||||
int intentType = -1;
|
||||
String actionName = null;
|
||||
String className = 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;
|
||||
}
|
||||
nativeAddAction(builder, action.title, intentType, actionName, className);
|
||||
}
|
||||
int intentType = -1;
|
||||
String actionName = null;
|
||||
String className = 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;
|
||||
}
|
||||
nativeShowNotification(builder, id, notification.title, notification.text, intentType, actionName, className);
|
||||
}
|
||||
|
||||
public void notify(int id, Notification notification) {
|
||||
System.out.println("notify(" + id + ", " + notification + ") called");
|
||||
notify(null, id, notification);
|
||||
}
|
||||
|
||||
public void cancel(String tag, int id) {}
|
||||
|
||||
protected static void notificationActionCallback(int id, int intentType, String action, String className) {
|
||||
Context context = Context.this_application;
|
||||
Intent intent = new Intent(action);
|
||||
if (className != null && !className.isEmpty()) {
|
||||
intent.setComponent(new ComponentName(context, className));
|
||||
}
|
||||
if (intentType == 0) { // type Activity
|
||||
context.startActivity(intent);
|
||||
} else if (intentType == 1) { // type Service
|
||||
context.startService(intent);
|
||||
} else if (intentType == 2) { // type Broadcast
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
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, int intentType, String action, String className);
|
||||
}
|
||||
|
|
|
@ -5,8 +5,18 @@ import android.content.Intent;
|
|||
import android.content.IntentSender;
|
||||
|
||||
public class PendingIntent {
|
||||
|
||||
private int requestCode;
|
||||
Intent intent;
|
||||
int type; // 0: activity, 1: service, 2: broadcast
|
||||
|
||||
private PendingIntent(int requestCode, Intent intent, int type) {
|
||||
this.requestCode = requestCode;
|
||||
this.intent = intent;
|
||||
this.type = type;
|
||||
}
|
||||
public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags) {
|
||||
return new PendingIntent();
|
||||
return new PendingIntent(requestCode, intent, 2);
|
||||
}
|
||||
|
||||
public IntentSender getIntentSender() {
|
||||
|
@ -16,11 +26,16 @@ public class PendingIntent {
|
|||
public void send(Context context, int code, Intent intent) {}
|
||||
|
||||
public static PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags) {
|
||||
return new PendingIntent();
|
||||
return new PendingIntent(requestCode, intent, 0);
|
||||
}
|
||||
|
||||
public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags) {
|
||||
return new PendingIntent();
|
||||
return new PendingIntent(requestCode, intent, 1);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "PendingIntent [requestCode=" + requestCode + ", intent=" + intent + ", type="
|
||||
+ new String[] { "activity", "service", "broadcast" }[type] + "]";
|
||||
}
|
||||
|
||||
public class CanceledException extends Exception {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
package android.content;
|
||||
|
||||
public class BroadcastReceiver {
|
||||
public abstract class BroadcastReceiver {
|
||||
|
||||
public abstract void onReceive(Context context, Intent intent);
|
||||
}
|
||||
|
|
|
@ -82,6 +82,8 @@ public class Context extends Object {
|
|||
File obb_dir = null;
|
||||
File cache_dir = null;
|
||||
|
||||
private static Map<IntentFilter, BroadcastReceiver> receiverMap = new HashMap<IntentFilter, BroadcastReceiver>();
|
||||
|
||||
static {
|
||||
assets = new AssetManager();
|
||||
dm = new DisplayMetrics();
|
||||
|
@ -202,6 +204,7 @@ public class Context extends Object {
|
|||
}
|
||||
|
||||
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
|
||||
receiverMap.put(filter, receiver);
|
||||
return new Intent();
|
||||
}
|
||||
|
||||
|
@ -467,7 +470,13 @@ public class Context extends Object {
|
|||
return new File(databaseDir, dbName);
|
||||
}
|
||||
|
||||
public void sendBroadcast(Intent intent) {}
|
||||
public void sendBroadcast(Intent intent) {
|
||||
for (IntentFilter filter : receiverMap.keySet()) {
|
||||
if (filter.matchAction(intent.getAction())) {
|
||||
receiverMap.get(filter).onReceive(this, intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean stopService(Intent intent) {return false;}
|
||||
|
||||
|
|
|
@ -1,9 +1,25 @@
|
|||
package android.content;
|
||||
|
||||
public class IntentFilter {
|
||||
public IntentFilter() {}
|
||||
public IntentFilter(String dummy) {}
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public void addAction(String action) {}
|
||||
public int countActions() { return 0; /*maybe?*/ }
|
||||
public class IntentFilter {
|
||||
|
||||
private Set<String> actions = new HashSet<>();
|
||||
|
||||
public IntentFilter() {}
|
||||
public IntentFilter(String action) {
|
||||
addAction(action);
|
||||
}
|
||||
|
||||
public void addAction(String action) {
|
||||
actions.add(action);
|
||||
}
|
||||
public int countActions() {
|
||||
return actions.size();
|
||||
}
|
||||
|
||||
public final boolean matchAction(String action) {
|
||||
return actions.contains(action);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue