From 6449040e618629f8db345408725ee497582416bf Mon Sep 17 00:00:00 2001 From: Chris Waldon Date: Sat, 20 Jun 2020 10:17:40 -0400 Subject: [PATCH] feat: implement android notification cancellation --- android/NotificationHelper.java | 4 ++++ android/notify_android.go | 36 ++++++++++++++++++++++++++++++--- example/hello.go | 5 +++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/android/NotificationHelper.java b/android/NotificationHelper.java index cfd9874..6bac5dd 100644 --- a/android/NotificationHelper.java +++ b/android/NotificationHelper.java @@ -31,4 +31,8 @@ public class NotificationHelper { NotificationManager notificationManager = ctx.getSystemService(NotificationManager.class); notificationManager.notify(notificationID, builder.build()); } + public static void cancelNotification(Context ctx, int notificationID) { + NotificationManager notificationManager = ctx.getSystemService(NotificationManager.class); + notificationManager.cancel(notificationID); + } } diff --git a/android/notify_android.go b/android/notify_android.go index 2c12ea8..cb3be29 100644 --- a/android/notify_android.go +++ b/android/notify_android.go @@ -8,6 +8,10 @@ import ( "github.com/tailscale/tailscale-android/jni" ) +const ( + helperClass = "ht/sr/git/whereswaldon/niotify/NotificationHelper" +) + var ( idlock sync.Mutex nextNotificationID int32 @@ -21,15 +25,20 @@ func nextID() int32 { return id } +// NotificationChannel represents a stream of notifications that an application +// provisions on android. Such streams can be selectively enabled and disabled +// by the user, and should be used for different purposes. type NotificationChannel struct { id string } +// NewChannel creates a new notification channel identified by the provided id +// and with the given user-visible name and description. func NewChannel(id, name, description string) (*NotificationChannel, error) { if err := jni.Do(jni.JVMFor(app.JavaVM()), func(env jni.Env) error { appCtx := jni.Object(app.AppContext()) classLoader := jni.ClassLoaderFor(env, appCtx) - notifyClass, err := jni.LoadClass(env, classLoader, "ht/sr/git/whereswaldon/niotify/NotificationHelper") + notifyClass, err := jni.LoadClass(env, classLoader, helperClass) if err != nil { return err } @@ -51,16 +60,19 @@ func NewChannel(id, name, description string) (*NotificationChannel, error) { return nc, nil } +// Notification represents a notification that has been requested to be shown to the user. +// This type provides methods to cancel or update the contents of the notification. type Notification struct { id int32 } +// Send creates a new Notification and requests that it be displayed on this channel. func (nc *NotificationChannel) Send(title, text string) (*Notification, error) { notificationID := nextID() if err := jni.Do(jni.JVMFor(app.JavaVM()), func(env jni.Env) error { appCtx := jni.Object(app.AppContext()) classLoader := jni.ClassLoaderFor(env, appCtx) - notifyClass, err := jni.LoadClass(env, classLoader, "ht/sr/git/whereswaldon/niotify/NotificationHelper") + notifyClass, err := jni.LoadClass(env, classLoader, helperClass) if err != nil { return err } @@ -74,9 +86,27 @@ func (nc *NotificationChannel) Send(title, text string) (*Notification, error) { } return nil }); err != nil { - return nil, fmt.Errorf("failed creating notification channel: %w", err) + return nil, fmt.Errorf("failed sending notification: %w", err) } return &Notification{ id: notificationID, }, nil } + +// Cancel removes a previously created notification from display. +func (n *Notification) Cancel() error { + notificationID := n.id + if err := jni.Do(jni.JVMFor(app.JavaVM()), func(env jni.Env) error { + appCtx := jni.Object(app.AppContext()) + classLoader := jni.ClassLoaderFor(env, appCtx) + notifyClass, err := jni.LoadClass(env, classLoader, helperClass) + if err != nil { + return err + } + newChannelMethod := jni.GetStaticMethodID(env, notifyClass, "cancelNotification", "(Landroid/content/Context;I)V") + return jni.CallStaticVoidMethod(env, notifyClass, newChannelMethod, jni.Value(app.AppContext()), jni.Value(notificationID)) + }); err != nil { + return fmt.Errorf("failed cancelling notification: %w", err) + } + return nil +} diff --git a/example/hello.go b/example/hello.go index 3dab691..df94c23 100644 --- a/example/hello.go +++ b/example/hello.go @@ -7,6 +7,7 @@ package main import ( "image/color" "log" + "time" "gioui.org/app" "gioui.org/io/system" @@ -62,6 +63,10 @@ func loop(w *app.Window) error { log.Printf("notification send failed: %v", err) } log.Println(notif) + time.Sleep(time.Second * 10) + if err := notif.Cancel(); err != nil { + log.Printf("failed cancelling: %v", err) + } }() } }