Android Notification - First Cut
the build was successful
Details
the build was successful
Details
This commit has the basics of notifications on Android working again, updated to the latest Android SDK way of doing things (with channel IDs and grouping). Android users will get notified when the app is open for new Peer Messages across all profiles. In the future, this should be extended to add notifications for new peer invites, actual have actionable actions (accept/block) and maybe even work when the app isn't open...
This commit is contained in:
parent
de56f8be15
commit
76df3c286d
|
@ -79,4 +79,11 @@ Theoretically speaking it should be possible to use `ANDROID_EXTRA_PLUGINS` to i
|
|||
SVG images on Android. However, we have been unable to make it work. If you would like to try, the following
|
||||
issues might be helpful:
|
||||
|
||||
* https://bugreports.qt.io/browse/QTBUG-60022
|
||||
* https://bugreports.qt.io/browse/QTBUG-60022
|
||||
|
||||
## Notifications
|
||||
|
||||
- Android 8 (API Level 26) forces you to call setChannelId()
|
||||
- Android 9 "Do Not Disturb" mode also hides all notifications
|
||||
- Setting up notification channels only seems possible *once* per install. any changes you need to make
|
||||
require that the app is reinstalled, or the actual channel deleted and changed.
|
|
@ -23,20 +23,13 @@ import static android.app.Notification.CATEGORY_SERVICE;
|
|||
public class CwtchActivity extends org.qtproject.qt5.android.bindings.QtActivity
|
||||
{
|
||||
private static NotificationManager m_notificationManager;
|
||||
private static Notification.Builder m_builder;
|
||||
private static Notification.Builder m_builderOngoing;
|
||||
private static CwtchActivity m_instance;
|
||||
|
||||
private static int PRIORITY_MIN = -2; // From NotificationCompat
|
||||
private static int PRIORITY_DEFAULT = 0; // From NotificationCompat
|
||||
|
||||
private static String NOTIFICATION_CHANNEL_ID = "cwtch_notification_channel";
|
||||
private static int CONTENT_NOTIFICATION_ID = 2;
|
||||
private static String CONTENT_NOTIFICATION_ID_NAME = "Notifications from Peers";
|
||||
|
||||
private static int ONGOING_NOTIFICATION_ID = 0;
|
||||
private static String ONGOING_NOTIFICATION_ID_NAME = "ongoing";
|
||||
|
||||
private static int CONTENT_NOTIFICATION_ID = 1;
|
||||
private static String CONTENT_NOTIFICATION_ID_NAME = "content";
|
||||
|
||||
|
||||
public CwtchActivity() {
|
||||
|
@ -57,66 +50,47 @@ public class CwtchActivity extends org.qtproject.qt5.android.bindings.QtActivity
|
|||
}
|
||||
}
|
||||
|
||||
public static void notify(String s)
|
||||
public static void notify(String s, String o)
|
||||
{
|
||||
if (m_notificationManager == null) {
|
||||
m_notificationManager = (NotificationManager)m_instance.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
createNotificationChannel();
|
||||
}
|
||||
|
||||
if (m_builder == null) {
|
||||
m_builder = new Notification.Builder(m_instance);
|
||||
m_builder.setSmallIcon(R.drawable.ic_launcher);
|
||||
m_builder.setContentTitle("Cwtch");
|
||||
m_builder.setPriority(PRIORITY_DEFAULT);
|
||||
// Apparently thr android documentation is just wrong and we need to provide a setGroupSummary
|
||||
// notification regardless of targetted support version...
|
||||
Notification groupSummary =
|
||||
new Notification.Builder(m_instance)
|
||||
.setContentTitle("Cwtch")
|
||||
.setContentText("New Message from Peer: " + o)
|
||||
.setGroupSummary(true)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setGroup(NOTIFICATION_CHANNEL_ID)
|
||||
.setChannelId(NOTIFICATION_CHANNEL_ID)
|
||||
.build();
|
||||
m_notificationManager.notify(1, groupSummary);
|
||||
|
||||
}
|
||||
Notification.Builder m_builder = new Notification.Builder(m_instance)
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setChannelId(NOTIFICATION_CHANNEL_ID)
|
||||
.setGroup(NOTIFICATION_CHANNEL_ID)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setAutoCancel(true)
|
||||
.setContentTitle("New Message from Peer: " + o)
|
||||
.setContentText("[redacted: Open Cwtch App to see the Message]");
|
||||
m_notificationManager.notify(CONTENT_NOTIFICATION_ID++, m_builder.build());
|
||||
|
||||
m_builder.setContentText(s);
|
||||
m_notificationManager.notify(CONTENT_NOTIFICATION_ID, m_builder.build());
|
||||
}
|
||||
|
||||
public static void ongoingNotify(String s)
|
||||
{
|
||||
if (m_notificationManager == null) {
|
||||
m_notificationManager = (NotificationManager)m_instance.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
createNotificationChannel();
|
||||
}
|
||||
|
||||
if (m_builderOngoing == null) {
|
||||
m_builderOngoing = new Notification.Builder(m_instance);
|
||||
m_builderOngoing.setSmallIcon(R.drawable.ic_launcher);
|
||||
m_builderOngoing.setContentTitle("Cwtch");
|
||||
m_builderOngoing.setPriority(PRIORITY_MIN);
|
||||
|
||||
m_builderOngoing.setWhen(0); // Don't show the time
|
||||
m_builderOngoing.setOngoing(true);
|
||||
if (SDK_INT >= 21) {
|
||||
m_builderOngoing.setCategory(CATEGORY_SERVICE);
|
||||
//m_builder.setVisibility(VISIBILITY_SECRET);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_builderOngoing.setContentText(s);
|
||||
m_notificationManager.notify(ONGOING_NOTIFICATION_ID, m_builderOngoing.build());
|
||||
}
|
||||
|
||||
private static void createNotificationChannel() {
|
||||
// Create the NotificationChannel, but only on API 26+ because
|
||||
// the NotificationChannel class is new and not in the support library
|
||||
if (SDK_INT >= 26) {
|
||||
String description = "Cwtch Ongoing Notification Channel";
|
||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
||||
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, ONGOING_NOTIFICATION_ID_NAME, importance);
|
||||
channel.setDescription(description);
|
||||
// Register the channel with the system; you can't change the importance
|
||||
// or other notification behaviors after this
|
||||
m_notificationManager.createNotificationChannel(channel);
|
||||
|
||||
description = "Cwtch Content Notification Channel";
|
||||
importance = NotificationManager.IMPORTANCE_DEFAULT;
|
||||
channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, CONTENT_NOTIFICATION_ID_NAME, importance);
|
||||
String description = "Cwtch Notification Channel";
|
||||
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, CONTENT_NOTIFICATION_ID_NAME, NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.setDescription(description);
|
||||
// Register the channel with the system; you can't change the importance
|
||||
// or other notification behaviors after this
|
||||
|
|
|
@ -58,7 +58,6 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) {
|
|||
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
|
||||
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampReceived])
|
||||
uiManager.StoreAndNotify(peer, e.Data[event.RemotePeer], e.Data[event.Data], ts, onion)
|
||||
|
||||
case event.PeerAcknowledgement:
|
||||
uiManager.Acknowledge(e.Data[event.RemotePeer], e.Data[event.EventID])
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ type CwtchActivity struct {
|
|||
|
||||
_ func() `constructor:"init"`
|
||||
|
||||
_ string `property:"channel"`
|
||||
_ string `property:"notification"`
|
||||
|
||||
_ func(string) `slot:"updateAndroidNotification"`
|
||||
|
@ -20,7 +21,6 @@ type CwtchActivity struct {
|
|||
|
||||
func (c *CwtchActivity) init() {
|
||||
log.Debugln("CwtchActivity.init()")
|
||||
c.createOngoingNotification()
|
||||
c.ConnectNotificationChanged(c.updateAndroidNotification)
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,8 @@ func (c *CwtchActivity) updateAndroidNotification(n string) {
|
|||
var err = androidextras.QAndroidJniObject_CallStaticMethodVoid2Caught(
|
||||
"ca/openprivacy/cwtch/ui/CwtchActivity",
|
||||
"notify",
|
||||
"(Ljava/lang/String;)V",
|
||||
n,
|
||||
"(Ljava/lang/String;Ljava/lang/String;)V",
|
||||
n, c.Channel(),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
@ -38,20 +38,6 @@ func (c *CwtchActivity) updateAndroidNotification(n string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *CwtchActivity) createOngoingNotification() {
|
||||
|
||||
var err = androidextras.QAndroidJniObject_CallStaticMethodVoid2Caught(
|
||||
"ca/openprivacy/cwtch/ui/CwtchActivity",
|
||||
"ongoingNotify",
|
||||
"(Ljava/lang/String;)V",
|
||||
"Cwtch is running",
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Error calling Java CwtchActivity.ongoingNotify(): %v\n", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CwtchActivity) rootHomeButtonHandle() {
|
||||
log.Infoln("CwtchActivity.rootHomeButtonHandle()!")
|
||||
var err = androidextras.QAndroidJniObject_CallStaticMethodVoid2Caught(
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"cwtch.im/cwtch/protocol/connections"
|
||||
"cwtch.im/ui/go/constants"
|
||||
"cwtch.im/ui/go/features/groups"
|
||||
"cwtch.im/ui/go/ui/android"
|
||||
"github.com/therecipe/qt/qml"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
@ -22,6 +23,7 @@ import (
|
|||
type GrandCentralDispatcher struct {
|
||||
core.QObject
|
||||
|
||||
AndroidCwtchActivity *android.CwtchActivity
|
||||
QMLEngine *qml.QQmlApplicationEngine
|
||||
Translator, OpaqueTranslator *core.QTranslator
|
||||
|
||||
|
@ -58,6 +60,7 @@ type GrandCentralDispatcher struct {
|
|||
_ func() `signal:"ResetProfileList"`
|
||||
_ func(failed bool) `signal:"ChangePasswordResponse"`
|
||||
_ func(onion string, online bool) `signal:"UpdateProfileNetworkStatus"`
|
||||
_ func(onion string) `signal:"Notify"`
|
||||
|
||||
// server management
|
||||
_ func(handle, displayname, image string, status int, autostart bool, bundle string, messages int, key_types []string, keys []string) `signal:"AddServer"`
|
||||
|
@ -147,6 +150,7 @@ func (this *GrandCentralDispatcher) init() {
|
|||
this.SetTheme(this.GlobalSettings.Theme)
|
||||
this.SetExperimentsEnabled(this.GlobalSettings.ExperimentsEnabled)
|
||||
this.SetExperiments(this.GlobalSettings.Experiments)
|
||||
this.AndroidCwtchActivity = android.NewCwtchActivity(nil)
|
||||
}
|
||||
|
||||
// GetUiManager gets (and creates if required) a ui Manager for the supplied profile id
|
||||
|
|
|
@ -301,6 +301,11 @@ func (this *manager) MessageJustAdded() {
|
|||
}
|
||||
|
||||
func (this *manager) StoreAndNotify(pere peer.CwtchPeer, onion string, messageTxt string, sent time.Time, profileOnion string) {
|
||||
|
||||
// Send a New Message from Peer Notification
|
||||
this.gcd.AndroidCwtchActivity.SetChannel(onion)
|
||||
this.gcd.AndroidCwtchActivity.NotificationChanged("New Message from Peer")
|
||||
|
||||
this.gcd.DoIfProfileElse(this.profile, func() {
|
||||
this.gcd.DoIfConversationElse(onion, func() {
|
||||
this.gcd.TimelineInterface.AddMessage(this.gcd.TimelineInterface.num())
|
||||
|
@ -314,6 +319,7 @@ func (this *manager) StoreAndNotify(pere peer.CwtchPeer, onion string, messageTx
|
|||
}, func() {
|
||||
the.CwtchApp.GetPeer(profileOnion).StoreMessage(onion, messageTxt, sent)
|
||||
})
|
||||
this.gcd.Notify(onion)
|
||||
}
|
||||
|
||||
// AddMessage adds a message to the message pane for the supplied conversation if it is active
|
||||
|
@ -331,6 +337,9 @@ func (this *manager) AddMessage(handle string, from string, message string, from
|
|||
})
|
||||
this.gcd.IncContactUnreadCount(handle)
|
||||
})
|
||||
if !fromMe {
|
||||
this.gcd.Notify(handle)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *manager) ReloadProfiles() {
|
||||
|
|
11
main.go
11
main.go
|
@ -11,7 +11,6 @@ import (
|
|||
os2 "cwtch.im/ui/go/os"
|
||||
"cwtch.im/ui/go/the"
|
||||
"cwtch.im/ui/go/ui"
|
||||
"cwtch.im/ui/go/ui/android"
|
||||
"encoding/base64"
|
||||
"flag"
|
||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||
|
@ -161,6 +160,7 @@ func mainUi(flagLocal bool, flagClientUI bool) {
|
|||
log.Errorf("Could not access global ui config: %v\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
gcd := ui.NewGrandCentralDispatcher(nil)
|
||||
gcd.SetOs(runtime.GOOS)
|
||||
dir := core.QCoreApplication_ApplicationDirPath()
|
||||
|
@ -186,8 +186,6 @@ func mainUi(flagLocal bool, flagClientUI bool) {
|
|||
gcd.SetBuildDate("now")
|
||||
}
|
||||
|
||||
|
||||
|
||||
// this is to load local qml files quickly when developing
|
||||
var qmlSource *core.QUrl
|
||||
if flagLocal {
|
||||
|
@ -223,13 +221,13 @@ func mainUi(flagLocal bool, flagClientUI bool) {
|
|||
return nam
|
||||
})
|
||||
engine.SetNetworkAccessManagerFactory(factory)
|
||||
|
||||
engine.RootContext().SetContextProperty("gcd", gcd)
|
||||
gcd.TimelineInterface = ui.NewMessageModel(nil)
|
||||
engine.RootContext().SetContextProperty("mm", gcd.TimelineInterface)
|
||||
|
||||
var androidCwtchActivity = android.NewCwtchActivity(nil)
|
||||
engine.RootContext().SetContextProperty("androidCwtchActivity", androidCwtchActivity)
|
||||
|
||||
engine.RootContext().SetContextProperty("androidCwtchActivity", gcd.AndroidCwtchActivity)
|
||||
|
||||
|
||||
engine.Load(qmlSource)
|
||||
|
||||
|
@ -263,7 +261,6 @@ func loadACN() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// generate a random socks and control port (not real random...these are port numbers...)
|
||||
mrand.Seed(int64(time.Now().Nanosecond()))
|
||||
port := mrand.Intn(1000) + 9600
|
||||
|
|
10
qml/main.qml
10
qml/main.qml
|
@ -419,6 +419,14 @@ ApplicationWindow {
|
|||
parentStack.updateToolbar()
|
||||
statusbar.resetHeight()
|
||||
}
|
||||
|
||||
onNotify: function(onion) {
|
||||
// If we are processing QML it means the app is open, and as such we don't want to
|
||||
// Send a notification - in the future we should probably use an API like this to Cancel notifications
|
||||
// Until then I am leaving this here for documentation.
|
||||
// androidCwtchActivity.channel = onion
|
||||
// androidCwtchActivity.notification = "Message from " + onion;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: Mutant.standard.imagePath = gcd.assetPath;
|
||||
|
@ -433,4 +441,6 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Reference in New Issue