Android Notification - First Cut #391
|
@ -80,3 +80,10 @@ SVG images on Android. However, we have been unable to make it work. If you woul
|
|||
issues might be helpful:
|
||||
|
||||
* 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";
|
||||
dan
commented
Review
* string valueify as per below comment
|
||||
|
||||
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)
|
||||
dan
commented
for strings, can you add these to I think that gives us a translatable path forward for Java, amazingly for strings, can you add these to `android/res/values/strings.xml` and load them here with `getString(R.string.idName);`
I think that gives us a translatable path forward for Java, amazingly
|
||||
.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)
|
||||
dan
commented
* string valueify
|
||||
.setContentText("[redacted: Open Cwtch App to see the Message]");
|
||||
dan
commented
* string valueify
|
||||
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";
|
||||
dan
commented
* string valueify
|
||||
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