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;
@ -38,300 +39,270 @@ import io.flutter.plugin.common.JSONMethodCodec;
import kotlin.Unit; import kotlin.Unit;
import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function1;
public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler, ServiceAware, MoxplatformApi { public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler, ServiceAware, MoxplatformApi {
public static final String entrypointKey = "entrypoint_handle"; public static final String entrypointKey = "entrypoint_handle";
public static final String extraDataKey = "extra_data"; public static final String extraDataKey = "extra_data";
private static final String autoStartAtBootKey = "auto_start_at_boot"; private static final String autoStartAtBootKey = "auto_start_at_boot";
private static final String TAG = "moxplatform_android"; private static final String TAG = "moxplatform_android";
public static final String methodChannelKey = "me.polynom.moxplatform_android"; public static final String methodChannelKey = "me.polynom.moxplatform_android";
public static final String dataReceivedMethodName = "dataReceived"; public static final String dataReceivedMethodName = "dataReceived";
private static final List<MoxplatformAndroidPlugin> _instances = new ArrayList<>(); private static final List<MoxplatformAndroidPlugin> _instances = new ArrayList<>();
private BackgroundService service; private BackgroundService service;
private MethodChannel channel; private MethodChannel channel;
private static EventChannel notificationChannel; private static EventChannel notificationChannel;
public static EventSink notificationSink; public static EventSink notificationSink;
private Context context; private Context context;
public MoxplatformAndroidPlugin() { public MoxplatformAndroidPlugin() {
_instances.add(this); _instances.add(this);
}
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), methodChannelKey);
channel.setMethodCallHandler(this);
context = flutterPluginBinding.getApplicationContext();
notificationChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "me.polynom/notification_stream");
notificationChannel.setStreamHandler(
this
);
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
localBroadcastManager.registerReceiver(this, new IntentFilter(methodChannelKey));
MoxplatformApi.setup(flutterPluginBinding.getBinaryMessenger(), this);
Log.d(TAG, "Attached to engine");
}
static void registerWith(Registrar registrar) {
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(registrar.context());
final MoxplatformAndroidPlugin plugin = new MoxplatformAndroidPlugin();
localBroadcastManager.registerReceiver(plugin, new IntentFilter(methodChannelKey));
final MethodChannel channel = new MethodChannel(registrar.messenger(), "me.polynom/background_service_android", JSONMethodCodec.INSTANCE);
channel.setMethodCallHandler(plugin);
plugin.channel = channel;
Log.d(TAG, "Registered against registrar");
}
@Override
public void onCancel(Object arguments) {
Log.d(TAG, "Removed listener");
notificationSink = null;
}
@Override
public void onListen(Object arguments, EventChannel.EventSink eventSink) {
Log.d(TAG, "Attached listener");
notificationSink = eventSink;
}
/// Store the entrypoint handle and extra data for the background service.
private void configure(long entrypointHandle, String extraData) {
SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
prefs.edit()
.putLong(entrypointKey, entrypointHandle)
.putString(extraDataKey, extraData)
.apply();
}
public static long getHandle(Context c) {
return c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getLong(entrypointKey, 0);
}
public static String getExtraData(Context c) {
return c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getString(extraDataKey, "");
}
public static void setStartAtBoot(Context c, boolean value) {
c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
.edit()
.putBoolean(autoStartAtBootKey, value)
.apply();
}
public static boolean getStartAtBoot(Context c) {
return c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getBoolean(autoStartAtBootKey, false);
}
private boolean isRunning() {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo info : manager.getRunningServices(Integer.MAX_VALUE)) {
if (BackgroundService.class.getName().equals(info.service.getClassName())) {
return true;
}
} }
return false; @Override
} public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), methodChannelKey);
channel.setMethodCallHandler(this);
context = flutterPluginBinding.getApplicationContext();
@Override notificationChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "me.polynom/notification_stream");
public void onMethodCall(@NonNull MethodCall call, @NonNull io.flutter.plugin.common.MethodChannel.Result result) { notificationChannel.setStreamHandler(this);
switch (call.method) {
case "configure":
ArrayList args = (ArrayList) call.arguments;
long handle = (long) args.get(0);
String extraData = (String) args.get(1);
configure(handle, extraData);
result.success(true); LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
break; localBroadcastManager.registerReceiver(this, new IntentFilter(methodChannelKey));
case "isRunning":
result.success(isRunning()); MoxplatformApi.setup(flutterPluginBinding.getBinaryMessenger(), this);
break;
case "start": Log.d(TAG, "Attached to engine");
MoxplatformAndroidPlugin.setStartAtBoot(context, true); }
BackgroundService.enqueue(context);
Intent intent = new Intent(context, BackgroundService.class); static void registerWith(Registrar registrar) {
ContextCompat.startForegroundService(context, intent); LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(registrar.context());
Log.d(TAG, "Service started"); final MoxplatformAndroidPlugin plugin = new MoxplatformAndroidPlugin();
result.success(true); localBroadcastManager.registerReceiver(plugin, new IntentFilter(methodChannelKey));
break;
case "sendData": final MethodChannel channel = new MethodChannel(registrar.messenger(), "me.polynom/background_service_android", JSONMethodCodec.INSTANCE);
for (MoxplatformAndroidPlugin plugin : _instances) { channel.setMethodCallHandler(plugin);
if (plugin.service != null) { plugin.channel = channel;
plugin.service.receiveData((String) call.arguments);
break; Log.d(TAG, "Registered against registrar");
} }
@Override
public void onCancel(Object arguments) {
Log.d(TAG, "Removed listener");
notificationSink = null;
}
@Override
public void onListen(Object arguments, EventChannel.EventSink eventSink) {
Log.d(TAG, "Attached listener");
notificationSink = eventSink;
}
/// Store the entrypoint handle and extra data for the background service.
private void configure(long entrypointHandle, String extraData) {
SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
prefs.edit().putLong(entrypointKey, entrypointHandle).putString(extraDataKey, extraData).apply();
}
public static long getHandle(Context c) {
return c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getLong(entrypointKey, 0);
}
public static String getExtraData(Context c) {
return c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getString(extraDataKey, "");
}
public static void setStartAtBoot(Context c, boolean value) {
c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).edit().putBoolean(autoStartAtBootKey, value).apply();
}
public static boolean getStartAtBoot(Context c) {
return c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getBoolean(autoStartAtBootKey, false);
}
private boolean isRunning() {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo info : manager.getRunningServices(Integer.MAX_VALUE)) {
if (BackgroundService.class.getName().equals(info.service.getClassName())) {
return true;
}
} }
result.success(true);
break;
case "encryptFile":
Thread encryptionThread = new Thread(new Runnable() {
@Override
public void run() {
ArrayList args = (ArrayList) call.arguments;
String src = (String) args.get(0);
String dest = (String) args.get(1);
byte[] key = (byte[]) args.get(2);
byte[] iv = (byte[]) args.get(3);
int algorithm = (int) args.get(4);
String hashSpec = (String) args.get(5);
result.success( return false;
encryptAndHash(
src,
dest,
key,
iv,
getCipherSpecFromInteger(algorithm),
hashSpec
)
);
}
});
encryptionThread.start();
break;
case "decryptFile":
Thread decryptionThread = new Thread(new Runnable() {
@Override
public void run() {
ArrayList args = (ArrayList) call.arguments;
String src = (String) args.get(0);
String dest = (String) args.get(1);
byte[] key = (byte[]) args.get(2);
byte[] iv = (byte[]) args.get(3);
int algorithm = (int) args.get(4);
String hashSpec = (String) args.get(5);
result.success(
decryptAndHash(
src,
dest,
key,
iv,
getCipherSpecFromInteger(algorithm),
hashSpec
)
);
}
});
decryptionThread.start();
break;
case "hashFile":
Thread hashingThread = new Thread(new Runnable() {
@Override
public void run() {
ArrayList args = (ArrayList) call.arguments;
String src = (String) args.get(0);
String hashSpec = (String) args.get(1);
result.success(hashFile(src, hashSpec));
}
});
hashingThread.start();
break;
case "recordSentMessage":
ArrayList rargs = (ArrayList) call.arguments;
recordSentMessage(
context,
(String) rargs.get(0),
(String) rargs.get(1),
(String) rargs.get(2),
(int) rargs.get(3)
);
result.success(true);
break;
default:
result.notImplemented();
break;
} }
}
@Override @Override
public void onReceive(Context context, Intent intent) { public void onMethodCall(@NonNull MethodCall call, @NonNull io.flutter.plugin.common.MethodChannel.Result result) {
if (intent.getAction() == null) return; switch (call.method) {
case "configure":
ArrayList args = (ArrayList) call.arguments;
long handle = (long) args.get(0);
String extraData = (String) args.get(1);
if (intent.getAction().equalsIgnoreCase(methodChannelKey)) { configure(handle, extraData);
String data = intent.getStringExtra("data"); result.success(true);
break;
case "isRunning":
result.success(isRunning());
break;
case "start":
MoxplatformAndroidPlugin.setStartAtBoot(context, true);
BackgroundService.enqueue(context);
Intent intent = new Intent(context, BackgroundService.class);
ContextCompat.startForegroundService(context, intent);
Log.d(TAG, "Service started");
result.success(true);
break;
case "sendData":
for (MoxplatformAndroidPlugin plugin : _instances) {
if (plugin.service != null) {
plugin.service.receiveData((String) call.arguments);
break;
}
}
result.success(true);
break;
case "encryptFile":
Thread encryptionThread = new Thread(new Runnable() {
@Override
public void run() {
ArrayList args = (ArrayList) call.arguments;
String src = (String) args.get(0);
String dest = (String) args.get(1);
byte[] key = (byte[]) args.get(2);
byte[] iv = (byte[]) args.get(3);
int algorithm = (int) args.get(4);
String hashSpec = (String) args.get(5);
if (channel != null) { result.success(encryptAndHash(src, dest, key, iv, getCipherSpecFromInteger(algorithm), hashSpec));
channel.invokeMethod(dataReceivedMethodName, data); }
} });
encryptionThread.start();
break;
case "decryptFile":
Thread decryptionThread = new Thread(new Runnable() {
@Override
public void run() {
ArrayList args = (ArrayList) call.arguments;
String src = (String) args.get(0);
String dest = (String) args.get(1);
byte[] key = (byte[]) args.get(2);
byte[] iv = (byte[]) args.get(3);
int algorithm = (int) args.get(4);
String hashSpec = (String) args.get(5);
result.success(decryptAndHash(src, dest, key, iv, getCipherSpecFromInteger(algorithm), hashSpec));
}
});
decryptionThread.start();
break;
case "hashFile":
Thread hashingThread = new Thread(new Runnable() {
@Override
public void run() {
ArrayList args = (ArrayList) call.arguments;
String src = (String) args.get(0);
String hashSpec = (String) args.get(1);
result.success(hashFile(src, hashSpec));
}
});
hashingThread.start();
break;
case "recordSentMessage":
ArrayList rargs = (ArrayList) call.arguments;
recordSentMessage(context, (String) rargs.get(0), (String) rargs.get(1), (String) rargs.get(2), (int) rargs.get(3));
result.success(true);
break;
default:
result.notImplemented();
break;
}
} }
}
@Override @Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { public void onReceive(Context context, Intent intent) {
channel.setMethodCallHandler(null); if (intent.getAction() == null) return;
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
localBroadcastManager.unregisterReceiver(this);
Log.d(TAG, "Detached from engine"); if (intent.getAction().equalsIgnoreCase(methodChannelKey)) {
} String data = intent.getStringExtra("data");
@Override if (channel != null) {
public void onAttachedToService(@NonNull ServicePluginBinding binding) { channel.invokeMethod(dataReceivedMethodName, data);
Log.d(TAG, "Attached to service"); }
this.service = (BackgroundService) binding.getService(); }
} }
@Override @Override
public void onDetachedFromService() { public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
Log.d(TAG, "Detached from service"); channel.setMethodCallHandler(null);
this.service = null; LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
} localBroadcastManager.unregisterReceiver(this);
@Override Log.d(TAG, "Detached from engine");
public void createNotificationChannel(@NonNull String title, @NonNull String id, @NonNull Boolean urgent) { }
final NotificationChannel channel = new NotificationChannel(
id,
title,
urgent ? NotificationManager.IMPORTANCE_HIGH : NotificationManager.IMPORTANCE_DEFAULT
);
channel.enableVibration(true);
channel.enableLights(true);
final NotificationManager manager = getSystemService(context, NotificationManager.class);
manager.createNotificationChannel(channel);
}
@Override @Override
public void showMessagingNotification(@NonNull MessagingNotification notification) { public void onAttachedToService(@NonNull ServicePluginBinding binding) {
NotificationsKt.showMessagingNotification(context, notification); Log.d(TAG, "Attached to service");
} this.service = (BackgroundService) binding.getService();
}
@Override
public void onDetachedFromService() {
Log.d(TAG, "Detached from service");
this.service = null;
}
@Override
public void createNotificationChannel(@NonNull String title, @NonNull String id, @NonNull Boolean urgent) {
final NotificationChannel channel = new NotificationChannel(id, title, urgent ? NotificationManager.IMPORTANCE_HIGH : NotificationManager.IMPORTANCE_DEFAULT);
channel.enableVibration(true);
channel.enableLights(true);
final NotificationManager manager = getSystemService(context, NotificationManager.class);
manager.createNotificationChannel(channel);
}
@Override
public void showMessagingNotification(@NonNull MessagingNotification 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);
} }
@Override @Override
public void setNotificationI18n(@NonNull NotificationI18nData data) { public void setNotificationI18n(@NonNull NotificationI18nData data) {
// Configure i18n // Configure i18n
NotificationDataManager.INSTANCE.setYou(context, data.getYou()); NotificationDataManager.INSTANCE.setYou(context, data.getYou());
NotificationDataManager.INSTANCE.setReply(context, data.getReply()); NotificationDataManager.INSTANCE.setReply(context, data.getReply());
NotificationDataManager.INSTANCE.setMarkAsRead(context, data.getMarkAsRead()); NotificationDataManager.INSTANCE.setMarkAsRead(context, data.getMarkAsRead());
} }
@NonNull @NonNull
@Override @Override
public String getPersistentDataPath() { public String getPersistentDataPath() {
return context.getFilesDir().getPath(); return context.getFilesDir().getPath();
} }
@NonNull @NonNull
@Override @Override
public String getCacheDataPath() { public String getCacheDataPath() {
return context.getCacheDir().getPath(); return context.getCacheDir().getPath();
} }
@Override @Override
public void eventStub(@NonNull NotificationEvent event) { public void eventStub(@NonNull NotificationEvent event) {
// Stub to trick pigeon into // Stub to trick pigeon into
} }
} }

View File

@ -211,4 +211,20 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
notification.id.toInt(), notification.id.toInt(),
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();