feat: Allow showing regular notifications

This commit is contained in:
PapaTutuWawa 2023-07-29 13:12:41 +02:00
parent 30ef477999
commit 6da35cd0ba
11 changed files with 604 additions and 263 deletions

View File

@ -10,6 +10,7 @@ import 'package:file_picker/file_picker.dart';
/// The id of the notification channel. /// The id of the notification channel.
const channelId = "me.polynom.moxplatform.testing3"; const channelId = "me.polynom.moxplatform.testing3";
const otherChannelId = "me.polynom.moxplatform.testing4";
void main() { void main() {
runApp(const MyApp()); runApp(const MyApp());
@ -46,6 +47,11 @@ class MyAppState extends State<MyApp> {
channelId, channelId,
false, false,
); );
await MoxplatformPlugin.notifications.createNotificationChannel(
"Test notification channel for warnings",
otherChannelId,
false,
);
await MoxplatformPlugin.notifications.setI18n( await MoxplatformPlugin.notifications.setI18n(
NotificationI18nData( NotificationI18nData(
reply: "答える", reply: "答える",
@ -190,6 +196,34 @@ class MyHomePage extends StatelessWidget {
}, },
child: const Text('Show messaging notification'), child: const Text('Show messaging notification'),
), ),
ElevatedButton(
onPressed: () {
MoxplatformPlugin.notifications.showNotification(
RegularNotification(
id: 4384,
title: 'Warning',
body: 'Something brokey',
channelId: otherChannelId,
icon: NotificationIcon.warning,
),
);
},
child: const Text('Show warning notification'),
),
ElevatedButton(
onPressed: () {
MoxplatformPlugin.notifications.showNotification(
RegularNotification(
id: 4384,
title: 'Error',
body: "Lol, you're on your own",
channelId: otherChannelId,
icon: NotificationIcon.error,
),
);
},
child: const Text('Show error notification'),
),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () async {
final result = await FilePicker.platform.pickFiles( final result = await FilePicker.platform.pickFiles(

View File

@ -57,6 +57,18 @@ public class Api {
return errorList; return errorList;
} }
public enum NotificationIcon {
WARNING(0),
ERROR(1),
NONE(2);
final int index;
private NotificationIcon(final int index) {
this.index = index;
}
}
public enum NotificationEventType { public enum NotificationEventType {
MARK_AS_READ(0), MARK_AS_READ(0),
REPLY(1), REPLY(1),
@ -453,6 +465,156 @@ public class Api {
} }
} }
/** Generated class from Pigeon that represents data sent in messages. */
public static final class RegularNotification {
/** The title of the notification. */
private @NonNull String title;
public @NonNull String getTitle() {
return title;
}
public void setTitle(@NonNull String setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"title\" is null.");
}
this.title = setterArg;
}
/** The body of the notification. */
private @NonNull String body;
public @NonNull String getBody() {
return body;
}
public void setBody(@NonNull String setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"body\" is null.");
}
this.body = setterArg;
}
/** The id of the channel to show the notification on. */
private @NonNull String channelId;
public @NonNull String getChannelId() {
return channelId;
}
public void setChannelId(@NonNull String setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"channelId\" is null.");
}
this.channelId = setterArg;
}
/** The id of the notification. */
private @NonNull Long id;
public @NonNull Long getId() {
return id;
}
public void setId(@NonNull Long setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"id\" is null.");
}
this.id = setterArg;
}
/** The icon to use. */
private @NonNull NotificationIcon icon;
public @NonNull NotificationIcon getIcon() {
return icon;
}
public void setIcon(@NonNull NotificationIcon setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"icon\" is null.");
}
this.icon = setterArg;
}
/** Constructor is non-public to enforce null safety; use Builder. */
RegularNotification() {}
public static final class Builder {
private @Nullable String title;
public @NonNull Builder setTitle(@NonNull String setterArg) {
this.title = setterArg;
return this;
}
private @Nullable String body;
public @NonNull Builder setBody(@NonNull String setterArg) {
this.body = setterArg;
return this;
}
private @Nullable String channelId;
public @NonNull Builder setChannelId(@NonNull String setterArg) {
this.channelId = setterArg;
return this;
}
private @Nullable Long id;
public @NonNull Builder setId(@NonNull Long setterArg) {
this.id = setterArg;
return this;
}
private @Nullable NotificationIcon icon;
public @NonNull Builder setIcon(@NonNull NotificationIcon setterArg) {
this.icon = setterArg;
return this;
}
public @NonNull RegularNotification build() {
RegularNotification pigeonReturn = new RegularNotification();
pigeonReturn.setTitle(title);
pigeonReturn.setBody(body);
pigeonReturn.setChannelId(channelId);
pigeonReturn.setId(id);
pigeonReturn.setIcon(icon);
return pigeonReturn;
}
}
@NonNull
ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(5);
toListResult.add(title);
toListResult.add(body);
toListResult.add(channelId);
toListResult.add(id);
toListResult.add(icon == null ? null : icon.index);
return toListResult;
}
static @NonNull RegularNotification fromList(@NonNull ArrayList<Object> list) {
RegularNotification pigeonResult = new RegularNotification();
Object title = list.get(0);
pigeonResult.setTitle((String) title);
Object body = list.get(1);
pigeonResult.setBody((String) body);
Object channelId = list.get(2);
pigeonResult.setChannelId((String) channelId);
Object id = list.get(3);
pigeonResult.setId((id == null) ? null : ((id instanceof Integer) ? (Integer) id : (Long) id));
Object icon = list.get(4);
pigeonResult.setIcon(icon == null ? null : NotificationIcon.values()[(int) icon]);
return pigeonResult;
}
}
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
public static final class NotificationEvent { public static final class NotificationEvent {
/** The JID the notification was for. */ /** The JID the notification was for. */
@ -672,6 +834,8 @@ public class Api {
return NotificationMessage.fromList((ArrayList<Object>) readValue(buffer)); return NotificationMessage.fromList((ArrayList<Object>) readValue(buffer));
case (byte) 132: case (byte) 132:
return NotificationMessageContent.fromList((ArrayList<Object>) readValue(buffer)); return NotificationMessageContent.fromList((ArrayList<Object>) readValue(buffer));
case (byte) 133:
return RegularNotification.fromList((ArrayList<Object>) readValue(buffer));
default: default:
return super.readValueOfType(type, buffer); return super.readValueOfType(type, buffer);
} }
@ -694,6 +858,9 @@ public class Api {
} else if (value instanceof NotificationMessageContent) { } else if (value instanceof NotificationMessageContent) {
stream.write(132); stream.write(132);
writeValue(stream, ((NotificationMessageContent) value).toList()); writeValue(stream, ((NotificationMessageContent) value).toList());
} else if (value instanceof RegularNotification) {
stream.write(133);
writeValue(stream, ((RegularNotification) value).toList());
} else { } else {
super.writeValue(stream, value); super.writeValue(stream, value);
} }
@ -707,6 +874,8 @@ public class Api {
void showMessagingNotification(@NonNull MessagingNotification notification); void showMessagingNotification(@NonNull MessagingNotification notification);
void showNotification(@NonNull RegularNotification notification);
void setNotificationSelfAvatar(@NonNull String path); void setNotificationSelfAvatar(@NonNull String path);
void setNotificationI18n(@NonNull NotificationI18nData data); void setNotificationI18n(@NonNull NotificationI18nData data);
@ -765,6 +934,30 @@ public class Api {
api.showMessagingNotification(notificationArg); api.showMessagingNotification(notificationArg);
wrapped.add(0, null); wrapped.add(0, null);
} }
catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.showNotification", getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
RegularNotification notificationArg = (RegularNotification) args.get(0);
try {
api.showNotification(notificationArg);
wrapped.add(0, null);
}
catch (Throwable exception) { catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception); ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError; wrapped = wrappedError;

View File

@ -4,6 +4,7 @@ import static androidx.core.content.ContextCompat.getSystemService;
import static me.polynom.moxplatform_android.ConstantsKt.SHARED_PREFERENCES_KEY; import static me.polynom.moxplatform_android.ConstantsKt.SHARED_PREFERENCES_KEY;
import static me.polynom.moxplatform_android.RecordSentMessageKt.recordSentMessage; import static me.polynom.moxplatform_android.RecordSentMessageKt.recordSentMessage;
import static me.polynom.moxplatform_android.CryptoKt.*; import static me.polynom.moxplatform_android.CryptoKt.*;
import me.polynom.moxplatform_android.Api.*; import me.polynom.moxplatform_android.Api.*;
import android.app.ActivityManager; import android.app.ActivityManager;
@ -65,9 +66,7 @@ import kotlin.jvm.functions.Function1;
context = flutterPluginBinding.getApplicationContext(); context = flutterPluginBinding.getApplicationContext();
notificationChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "me.polynom/notification_stream"); notificationChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "me.polynom/notification_stream");
notificationChannel.setStreamHandler( notificationChannel.setStreamHandler(this);
this
);
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context); LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
@ -105,10 +104,7 @@ import kotlin.jvm.functions.Function1;
/// Store the entrypoint handle and extra data for the background service. /// Store the entrypoint handle and extra data for the background service.
private void configure(long entrypointHandle, String extraData) { private void configure(long entrypointHandle, String extraData) {
SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE); SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
prefs.edit() prefs.edit().putLong(entrypointKey, entrypointHandle).putString(extraDataKey, extraData).apply();
.putLong(entrypointKey, entrypointHandle)
.putString(extraDataKey, extraData)
.apply();
} }
public static long getHandle(Context c) { public static long getHandle(Context c) {
@ -120,11 +116,9 @@ import kotlin.jvm.functions.Function1;
} }
public static void setStartAtBoot(Context c, boolean value) { public static void setStartAtBoot(Context c, boolean value) {
c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE) c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).edit().putBoolean(autoStartAtBootKey, value).apply();
.edit()
.putBoolean(autoStartAtBootKey, value)
.apply();
} }
public static boolean getStartAtBoot(Context c) { public static boolean getStartAtBoot(Context c) {
return c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getBoolean(autoStartAtBootKey, false); return c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getBoolean(autoStartAtBootKey, false);
} }
@ -183,16 +177,7 @@ import kotlin.jvm.functions.Function1;
int algorithm = (int) args.get(4); int algorithm = (int) args.get(4);
String hashSpec = (String) args.get(5); String hashSpec = (String) args.get(5);
result.success( result.success(encryptAndHash(src, dest, key, iv, getCipherSpecFromInteger(algorithm), hashSpec));
encryptAndHash(
src,
dest,
key,
iv,
getCipherSpecFromInteger(algorithm),
hashSpec
)
);
} }
}); });
encryptionThread.start(); encryptionThread.start();
@ -209,16 +194,7 @@ import kotlin.jvm.functions.Function1;
int algorithm = (int) args.get(4); int algorithm = (int) args.get(4);
String hashSpec = (String) args.get(5); String hashSpec = (String) args.get(5);
result.success( result.success(decryptAndHash(src, dest, key, iv, getCipherSpecFromInteger(algorithm), hashSpec));
decryptAndHash(
src,
dest,
key,
iv,
getCipherSpecFromInteger(algorithm),
hashSpec
)
);
} }
}); });
decryptionThread.start(); decryptionThread.start();
@ -238,13 +214,7 @@ import kotlin.jvm.functions.Function1;
break; break;
case "recordSentMessage": case "recordSentMessage":
ArrayList rargs = (ArrayList) call.arguments; ArrayList rargs = (ArrayList) call.arguments;
recordSentMessage( recordSentMessage(context, (String) rargs.get(0), (String) rargs.get(1), (String) rargs.get(2), (int) rargs.get(3));
context,
(String) rargs.get(0),
(String) rargs.get(1),
(String) rargs.get(2),
(int) rargs.get(3)
);
result.success(true); result.success(true);
break; break;
default: default:
@ -289,11 +259,7 @@ import kotlin.jvm.functions.Function1;
@Override @Override
public void createNotificationChannel(@NonNull String title, @NonNull String id, @NonNull Boolean urgent) { public void createNotificationChannel(@NonNull String title, @NonNull String id, @NonNull Boolean urgent) {
final NotificationChannel channel = new NotificationChannel( final NotificationChannel channel = new NotificationChannel(id, title, urgent ? NotificationManager.IMPORTANCE_HIGH : NotificationManager.IMPORTANCE_DEFAULT);
id,
title,
urgent ? NotificationManager.IMPORTANCE_HIGH : NotificationManager.IMPORTANCE_DEFAULT
);
channel.enableVibration(true); channel.enableVibration(true);
channel.enableLights(true); channel.enableLights(true);
final NotificationManager manager = getSystemService(context, NotificationManager.class); final NotificationManager manager = getSystemService(context, NotificationManager.class);
@ -305,6 +271,11 @@ import kotlin.jvm.functions.Function1;
NotificationsKt.showMessagingNotification(context, notification); NotificationsKt.showMessagingNotification(context, notification);
} }
@Override
public void showNotification(@NonNull RegularNotification notification) {
NotificationsKt.showNotification(context, notification);
}
@Override @Override
public void setNotificationSelfAvatar(@NonNull String path) { public void setNotificationSelfAvatar(@NonNull String path) {
NotificationDataManager.INSTANCE.setAvatarPath(path); NotificationDataManager.INSTANCE.setAvatarPath(path);

View File

@ -212,3 +212,19 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
finalNotification, finalNotification,
) )
} }
fun showNotification(context: Context, notification: Api.RegularNotification) {
val builtNotification = NotificationCompat.Builder(context, notification.channelId).apply {
setContentTitle(notification.title)
setContentText(notification.body)
when (notification.icon) {
Api.NotificationIcon.ERROR -> setSmallIcon(R.drawable.error)
Api.NotificationIcon.WARNING -> setSmallIcon(R.drawable.warning)
Api.NotificationIcon.NONE -> {}
}
}.build()
// Post the notification
NotificationManagerCompat.from(context).notify(notification.id.toInt(), builtNotification)
}

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
</vector>

View File

@ -24,6 +24,11 @@ class AndroidNotificationsImplementation extends NotificationsImplementation {
return _api.showMessagingNotification(notification); return _api.showMessagingNotification(notification);
} }
@override
Future<void> showNotification(RegularNotification notification) async {
return _api.showNotification(notification);
}
@override @override
Future<void> setNotificationSelfAvatar(String path) async { Future<void> setNotificationSelfAvatar(String path) async {
return _api.setNotificationSelfAvatar(path); return _api.setNotificationSelfAvatar(path);

View File

@ -8,6 +8,12 @@ import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
enum NotificationIcon {
warning,
error,
none,
}
enum NotificationEventType { enum NotificationEventType {
markAsRead, markAsRead,
reply, reply,
@ -139,6 +145,52 @@ class MessagingNotification {
} }
} }
class RegularNotification {
RegularNotification({
required this.title,
required this.body,
required this.channelId,
required this.id,
required this.icon,
});
/// The title of the notification.
String title;
/// The body of the notification.
String body;
/// The id of the channel to show the notification on.
String channelId;
/// The id of the notification.
int id;
/// The icon to use.
NotificationIcon icon;
Object encode() {
return <Object?>[
title,
body,
channelId,
id,
icon.index,
];
}
static RegularNotification decode(Object result) {
result as List<Object?>;
return RegularNotification(
title: result[0]! as String,
body: result[1]! as String,
channelId: result[2]! as String,
id: result[3]! as int,
icon: NotificationIcon.values[result[4]! as int],
);
}
}
class NotificationEvent { class NotificationEvent {
NotificationEvent({ NotificationEvent({
required this.jid, required this.jid,
@ -228,6 +280,9 @@ class _MoxplatformApiCodec extends StandardMessageCodec {
} else if (value is NotificationMessageContent) { } else if (value is NotificationMessageContent) {
buffer.putUint8(132); buffer.putUint8(132);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is RegularNotification) {
buffer.putUint8(133);
writeValue(buffer, value.encode());
} else { } else {
super.writeValue(buffer, value); super.writeValue(buffer, value);
} }
@ -246,6 +301,8 @@ class _MoxplatformApiCodec extends StandardMessageCodec {
return NotificationMessage.decode(readValue(buffer)!); return NotificationMessage.decode(readValue(buffer)!);
case 132: case 132:
return NotificationMessageContent.decode(readValue(buffer)!); return NotificationMessageContent.decode(readValue(buffer)!);
case 133:
return RegularNotification.decode(readValue(buffer)!);
default: default:
return super.readValueOfType(type, buffer); return super.readValueOfType(type, buffer);
} }
@ -306,6 +363,28 @@ class MoxplatformApi {
} }
} }
Future<void> showNotification(RegularNotification arg_notification) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.showNotification', codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList =
await channel.send(<Object?>[arg_notification]) as List<Object?>?;
if (replyList == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
);
} else if (replyList.length > 1) {
throw PlatformException(
code: replyList[0]! as String,
message: replyList[1] as String?,
details: replyList[2],
);
} else {
return;
}
}
Future<void> setNotificationSelfAvatar(String arg_path) async { Future<void> setNotificationSelfAvatar(String arg_path) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.setNotificationSelfAvatar', codec, 'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.setNotificationSelfAvatar', codec,

View File

@ -9,6 +9,9 @@ abstract class NotificationsImplementation {
/// Shows a notification [notification] in the messaging style with everyting it needs. /// Shows a notification [notification] in the messaging style with everyting it needs.
Future<void> showMessagingNotification(MessagingNotification notification); Future<void> showMessagingNotification(MessagingNotification notification);
/// Shows a regular notification [notification].
Future<void> showNotification(RegularNotification notification);
/// Sets the path to the self-avatar for in-notification replies. /// Sets the path to the self-avatar for in-notification replies.
Future<void> setNotificationSelfAvatar(String path); Future<void> setNotificationSelfAvatar(String path);

View File

@ -9,6 +9,10 @@ class StubNotificationsImplementation extends NotificationsImplementation {
@override @override
Future<void> showMessagingNotification(MessagingNotification notification) async {} Future<void> showMessagingNotification(MessagingNotification notification) async {}
@override
Future<void> showNotification(RegularNotification notification) async {}
@override @override
Future<void> setNotificationSelfAvatar(String path) async {} Future<void> setNotificationSelfAvatar(String path) async {}

View File

@ -72,6 +72,31 @@ class MessagingNotification {
final List<NotificationMessage?> messages; final List<NotificationMessage?> messages;
} }
enum NotificationIcon {
warning,
error,
none,
}
class RegularNotification {
const RegularNotification(this.title, this.body, this.channelId, this.id, this.icon);
/// The title of the notification.
final String title;
/// The body of the notification.
final String body;
/// The id of the channel to show the notification on.
final String channelId;
/// The id of the notification.
final int id;
/// The icon to use.
final NotificationIcon icon;
}
enum NotificationEventType { enum NotificationEventType {
markAsRead, markAsRead,
reply, reply,
@ -114,6 +139,7 @@ class NotificationI18nData {
abstract class MoxplatformApi { abstract class MoxplatformApi {
void createNotificationChannel(String title, String id, bool urgent); void createNotificationChannel(String title, String id, bool urgent);
void showMessagingNotification(MessagingNotification notification); void showMessagingNotification(MessagingNotification notification);
void showNotification(RegularNotification notification);
void setNotificationSelfAvatar(String path); void setNotificationSelfAvatar(String path);
void setNotificationI18n(NotificationI18nData data); void setNotificationI18n(NotificationI18nData data);
String getPersistentDataPath(); String getPersistentDataPath();