feat: Take care of i18n

This commit is contained in:
PapaTutuWawa 2023-07-28 17:32:14 +02:00
parent f90b3866ab
commit adb8ee88d1
12 changed files with 277 additions and 52 deletions

View File

@ -41,7 +41,16 @@ class MyAppState extends State<MyApp> {
Future<void> initStateAsync() async { Future<void> initStateAsync() async {
await Permission.notification.request(); await Permission.notification.request();
await MoxplatformPlugin.notifications.createNotificationChannel("Test notification channel", channelId, false); await MoxplatformPlugin.notifications.createNotificationChannel(
"Test notification channel",
channelId,
false,
NotificationI18nData(
reply: "答える",
markAsRead: "読みた",
you: "あなた",
),
);
MoxplatformPlugin.notifications.getEventStream().listen((event) { MoxplatformPlugin.notifications.getEventStream().listen((event) {
print('NotificationEvent(type: ${event.type}, jid: ${event.jid}, payload: ${event.payload})'); print('NotificationEvent(type: ${event.type}, jid: ${event.jid}, payload: ${event.payload})');

View File

@ -554,6 +554,106 @@ public class Api {
} }
} }
/** Generated class from Pigeon that represents data sent in messages. */
public static final class NotificationI18nData {
/** The content of the reply button. */
private @NonNull String reply;
public @NonNull String getReply() {
return reply;
}
public void setReply(@NonNull String setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"reply\" is null.");
}
this.reply = setterArg;
}
/** The content of the "mark as read" button. */
private @NonNull String markAsRead;
public @NonNull String getMarkAsRead() {
return markAsRead;
}
public void setMarkAsRead(@NonNull String setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"markAsRead\" is null.");
}
this.markAsRead = setterArg;
}
/** The text to show when *you* reply. */
private @NonNull String you;
public @NonNull String getYou() {
return you;
}
public void setYou(@NonNull String setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"you\" is null.");
}
this.you = setterArg;
}
/** Constructor is non-public to enforce null safety; use Builder. */
NotificationI18nData() {}
public static final class Builder {
private @Nullable String reply;
public @NonNull Builder setReply(@NonNull String setterArg) {
this.reply = setterArg;
return this;
}
private @Nullable String markAsRead;
public @NonNull Builder setMarkAsRead(@NonNull String setterArg) {
this.markAsRead = setterArg;
return this;
}
private @Nullable String you;
public @NonNull Builder setYou(@NonNull String setterArg) {
this.you = setterArg;
return this;
}
public @NonNull NotificationI18nData build() {
NotificationI18nData pigeonReturn = new NotificationI18nData();
pigeonReturn.setReply(reply);
pigeonReturn.setMarkAsRead(markAsRead);
pigeonReturn.setYou(you);
return pigeonReturn;
}
}
@NonNull
ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(3);
toListResult.add(reply);
toListResult.add(markAsRead);
toListResult.add(you);
return toListResult;
}
static @NonNull NotificationI18nData fromList(@NonNull ArrayList<Object> list) {
NotificationI18nData pigeonResult = new NotificationI18nData();
Object reply = list.get(0);
pigeonResult.setReply((String) reply);
Object markAsRead = list.get(1);
pigeonResult.setMarkAsRead((String) markAsRead);
Object you = list.get(2);
pigeonResult.setYou((String) you);
return pigeonResult;
}
}
private static class MoxplatformApiCodec extends StandardMessageCodec { private static class MoxplatformApiCodec extends StandardMessageCodec {
public static final MoxplatformApiCodec INSTANCE = new MoxplatformApiCodec(); public static final MoxplatformApiCodec INSTANCE = new MoxplatformApiCodec();
@ -567,8 +667,10 @@ public class Api {
case (byte) 129: case (byte) 129:
return NotificationEvent.fromList((ArrayList<Object>) readValue(buffer)); return NotificationEvent.fromList((ArrayList<Object>) readValue(buffer));
case (byte) 130: case (byte) 130:
return NotificationMessage.fromList((ArrayList<Object>) readValue(buffer)); return NotificationI18nData.fromList((ArrayList<Object>) readValue(buffer));
case (byte) 131: case (byte) 131:
return NotificationMessage.fromList((ArrayList<Object>) readValue(buffer));
case (byte) 132:
return NotificationMessageContent.fromList((ArrayList<Object>) readValue(buffer)); return NotificationMessageContent.fromList((ArrayList<Object>) readValue(buffer));
default: default:
return super.readValueOfType(type, buffer); return super.readValueOfType(type, buffer);
@ -583,11 +685,14 @@ public class Api {
} else if (value instanceof NotificationEvent) { } else if (value instanceof NotificationEvent) {
stream.write(129); stream.write(129);
writeValue(stream, ((NotificationEvent) value).toList()); writeValue(stream, ((NotificationEvent) value).toList());
} else if (value instanceof NotificationMessage) { } else if (value instanceof NotificationI18nData) {
stream.write(130); stream.write(130);
writeValue(stream, ((NotificationI18nData) value).toList());
} else if (value instanceof NotificationMessage) {
stream.write(131);
writeValue(stream, ((NotificationMessage) value).toList()); writeValue(stream, ((NotificationMessage) value).toList());
} else if (value instanceof NotificationMessageContent) { } else if (value instanceof NotificationMessageContent) {
stream.write(131); stream.write(132);
writeValue(stream, ((NotificationMessageContent) value).toList()); writeValue(stream, ((NotificationMessageContent) value).toList());
} else { } else {
super.writeValue(stream, value); super.writeValue(stream, value);
@ -598,7 +703,7 @@ public class Api {
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
public interface MoxplatformApi { public interface MoxplatformApi {
void createNotificationChannel(@NonNull String title, @NonNull String id, @NonNull Boolean urgent); void createNotificationChannel(@NonNull String title, @NonNull String id, @NonNull Boolean urgent, @NonNull NotificationI18nData i18n);
void showMessagingNotification(@NonNull MessagingNotification notification); void showMessagingNotification(@NonNull MessagingNotification notification);
@ -628,8 +733,9 @@ public class Api {
String titleArg = (String) args.get(0); String titleArg = (String) args.get(0);
String idArg = (String) args.get(1); String idArg = (String) args.get(1);
Boolean urgentArg = (Boolean) args.get(2); Boolean urgentArg = (Boolean) args.get(2);
NotificationI18nData i18nArg = (NotificationI18nData) args.get(3);
try { try {
api.createNotificationChannel(titleArg, idArg, urgentArg); api.createNotificationChannel(titleArg, idArg, urgentArg, i18nArg);
wrapped.add(0, null); wrapped.add(0, null);
} }
catch (Throwable exception) { catch (Throwable exception) {

View File

@ -6,17 +6,21 @@ const val TAG = "Moxplatform"
// The size of the buffer to hashing, encryption, and decryption in bytes. // The size of the buffer to hashing, encryption, and decryption in bytes.
const val BUFFER_SIZE = 8096 const val BUFFER_SIZE = 8096
const val REPLY_ACTION = "reply";
// The data key for text entered in the notification's reply field // The data key for text entered in the notification's reply field
const val REPLY_TEXT_KEY = "key_reply_text" const val REPLY_TEXT_KEY = "key_reply_text"
// The action for pressing the "Mark as read" button on a notification
const val MARK_AS_READ_ACTION = "mark_as_read"
// The key for the notification id to mark as read // The key for the notification id to mark as read
const val MARK_AS_READ_ID_KEY = "notification_id" const val MARK_AS_READ_ID_KEY = "notification_id"
// Values for actions performed through the notification
const val REPLY_ACTION = "reply";
const val MARK_AS_READ_ACTION = "mark_as_read"
const val TAP_ACTION = "tap"; const val TAP_ACTION = "tap";
// Extra data keys for the intents that reach the NotificationReceiver
const val NOTIFICATION_EXTRA_JID_KEY = "jid";
const val NOTIFICATION_EXTRA_ID_KEY = "notification_id";
// TODO: Maybe try again to rewrite the entire plugin in Kotlin // TODO: Maybe try again to rewrite the entire plugin in Kotlin
//const val METHOD_CHANNEL_KEY = "me.polynom.moxplatform_android" //const val METHOD_CHANNEL_KEY = "me.polynom.moxplatform_android"
//const val BACKGROUND_METHOD_CHANNEL_KEY = METHOD_CHANNEL_KEY + "_bg" //const val BACKGROUND_METHOD_CHANNEL_KEY = METHOD_CHANNEL_KEY + "_bg"

View File

@ -288,7 +288,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, @NonNull NotificationI18nData i18n) {
final NotificationChannel channel = new NotificationChannel( final NotificationChannel channel = new NotificationChannel(
id, id,
title, title,
@ -298,6 +298,11 @@ import kotlin.jvm.functions.Function1;
channel.enableLights(true); channel.enableLights(true);
final NotificationManager manager = getSystemService(context, NotificationManager.class); final NotificationManager manager = getSystemService(context, NotificationManager.class);
manager.createNotificationChannel(channel); manager.createNotificationChannel(channel);
// Configure i18n
NotificationI18nManager.INSTANCE.setYou(i18n.getYou());
NotificationI18nManager.INSTANCE.setReply(i18n.getReply());
NotificationI18nManager.INSTANCE.setMarkAsRead(i18n.getMarkAsRead());
} }
@Override @Override

View File

@ -1,19 +1,27 @@
package me.polynom.moxplatform_android package me.polynom.moxplatform_android
import android.app.Notification
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build
import android.util.Log import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.app.Person
import androidx.core.app.RemoteInput import androidx.core.app.RemoteInput
import me.polynom.moxplatform_android.Api.NotificationEvent import me.polynom.moxplatform_android.Api.NotificationEvent
import java.time.Instant
class NotificationReceiver : BroadcastReceiver() { class NotificationReceiver : BroadcastReceiver() {
/*
* Dismisses the notification through which we received @intent.
* */
private fun dismissNotification(context: Context, intent: Intent) { private fun dismissNotification(context: Context, intent: Intent) {
// Dismiss the notification // Dismiss the notification
val notificationId = intent.getLongExtra("notification_id", -1).toInt() val notificationId = intent.getLongExtra(NOTIFICATION_EXTRA_ID_KEY, -1).toInt()
if (notificationId != -1) { if (notificationId != -1) {
NotificationManagerCompat.from(context).cancel( NotificationManagerCompat.from(context).cancel(
notificationId, notificationId,
@ -23,15 +31,19 @@ class NotificationReceiver : BroadcastReceiver() {
} }
} }
private fun findActiveNotification(context: Context, id: Int): Notification? {
return (context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
.activeNotifications
.find { it.id == id }?.notification
}
private fun handleMarkAsRead(context: Context, intent: Intent) { private fun handleMarkAsRead(context: Context, intent: Intent) {
Log.d("NotificationReceiver", "Marking ${intent.getStringExtra("jid")} as read")
val jidWrapper = intent.getStringExtra("jid") ?: ""
NotificationManagerCompat.from(context).cancel(intent.getLongExtra(MARK_AS_READ_ID_KEY, -1).toInt()) NotificationManagerCompat.from(context).cancel(intent.getLongExtra(MARK_AS_READ_ID_KEY, -1).toInt())
MoxplatformAndroidPlugin.notificationSink?.success( MoxplatformAndroidPlugin.notificationSink?.success(
NotificationEvent().apply { NotificationEvent().apply {
// TODO: Use constant for key // TODO: Use constant for key
// TODO: Fix // TODO: Fix
jid = jidWrapper jid = intent.getStringExtra(NOTIFICATION_EXTRA_JID_KEY)!!
type = Api.NotificationEventType.MARK_AS_READ type = Api.NotificationEventType.MARK_AS_READ
payload = null payload = null
}.toList() }.toList()
@ -41,28 +53,60 @@ class NotificationReceiver : BroadcastReceiver() {
} }
private fun handleReply(context: Context, intent: Intent) { private fun handleReply(context: Context, intent: Intent) {
val jidWrapper = intent.getStringExtra("jid") ?: ""
val remoteInput = RemoteInput.getResultsFromIntent(intent) ?: return val remoteInput = RemoteInput.getResultsFromIntent(intent) ?: return
Log.d("NotificationReceiver", "Got a reply for ${jidWrapper}") val replyPayload = remoteInput.getCharSequence(REPLY_TEXT_KEY)
// TODO: Notify app
MoxplatformAndroidPlugin.notificationSink?.success( MoxplatformAndroidPlugin.notificationSink?.success(
NotificationEvent().apply { NotificationEvent().apply {
// TODO: Use constant for key // TODO: Use constant for key
jid = jidWrapper jid = intent.getStringExtra(NOTIFICATION_EXTRA_JID_KEY)!!
type = Api.NotificationEventType.REPLY type = Api.NotificationEventType.REPLY
payload = remoteInput.getCharSequence(REPLY_TEXT_KEY).toString() payload = replyPayload.toString()
}.toList() }.toList()
) )
// TODO: Update the notification to prevent showing the spinner val id = intent.getLongExtra(NOTIFICATION_EXTRA_ID_KEY, -1).toInt()
if (id == -1) {
Log.e(TAG, "Failed to find notification id for reply")
return;
}
val notification = findActiveNotification(context, id)
if (notification == null) {
Log.e(TAG, "Failed to find notification for id ${id}")
return
}
// Thanks https://medium.com/@sidorovroman3/android-how-to-use-messagingstyle-for-notifications-without-caching-messages-c414ef2b816c
val recoveredStyle = NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(notification)!!
// TODO: Use a person and cache this data somewhere
val newStyle = Notification.MessagingStyle(NotificationI18nManager.you).apply {
conversationTitle = recoveredStyle.conversationTitle
// TODO: Use person
recoveredStyle.messages.forEach {
addMessage(Notification.MessagingStyle.Message(it.text, it.timestamp, it.sender))
}
}
// TODO: Images get lost here? Do we have to request a new content URI?
newStyle.addMessage(
Notification.MessagingStyle.Message(
replyPayload!!,
Instant.now().toEpochMilli(),
null as CharSequence?
)
)
val recoveredBuilder = Notification.Builder.recoverBuilder(context, notification).apply {
style = newStyle
setOnlyAlertOnce(true)
}
NotificationManagerCompat.from(context).notify(id, recoveredBuilder.build())
} }
private fun handleTap(context: Context, intent: Intent) { private fun handleTap(context: Context, intent: Intent) {
Log.d("NotificationReceiver", "Received a tap")
MoxplatformAndroidPlugin.notificationSink?.success( MoxplatformAndroidPlugin.notificationSink?.success(
NotificationEvent().apply { NotificationEvent().apply {
jid = intent.getStringExtra("jid")!! jid = intent.getStringExtra(NOTIFICATION_EXTRA_JID_KEY)!!
type = Api.NotificationEventType.OPEN type = Api.NotificationEventType.OPEN
payload = null payload = null
}.toList() }.toList()

View File

@ -12,18 +12,23 @@ import androidx.core.content.FileProvider
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import java.io.File import java.io.File
object NotificationI18nManager {
var you: String = "You"
var markAsRead: String = "Mark as read"
var reply: String = "Reply"
}
/// Show a messaging style notification described by @notification. /// Show a messaging style notification described by @notification.
fun showMessagingNotification(context: Context, notification: Api.MessagingNotification) { fun showMessagingNotification(context: Context, notification: Api.MessagingNotification) {
// Build the actions // Build the actions
// -> Reply action // -> Reply action
val remoteInput = RemoteInput.Builder(REPLY_TEXT_KEY).apply { val remoteInput = RemoteInput.Builder(REPLY_TEXT_KEY).apply {
// TODO: i18n setLabel(NotificationI18nManager.reply)
setLabel("Reply")
}.build() }.build()
val replyIntent = Intent(context, NotificationReceiver::class.java).apply { val replyIntent = Intent(context, NotificationReceiver::class.java).apply {
action = REPLY_ACTION action = REPLY_ACTION
// TODO: Use a constant putExtra(NOTIFICATION_EXTRA_JID_KEY, notification.jid)
putExtra("jid", notification.jid) putExtra(NOTIFICATION_EXTRA_ID_KEY, notification.id)
} }
val replyPendingIntent = PendingIntent.getBroadcast( val replyPendingIntent = PendingIntent.getBroadcast(
context.applicationContext, context.applicationContext,
@ -34,8 +39,7 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
val replyAction = NotificationCompat.Action.Builder( val replyAction = NotificationCompat.Action.Builder(
// TODO: Wrong icon? // TODO: Wrong icon?
R.drawable.ic_service_icon, R.drawable.ic_service_icon,
// TODO: i18n NotificationI18nManager.reply,
"Reply",
replyPendingIntent, replyPendingIntent,
).apply { ).apply {
addRemoteInput(remoteInput) addRemoteInput(remoteInput)
@ -45,9 +49,8 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
// -> Mark as read action // -> Mark as read action
val markAsReadIntent = Intent(context, NotificationReceiver::class.java).apply { val markAsReadIntent = Intent(context, NotificationReceiver::class.java).apply {
action = MARK_AS_READ_ACTION action = MARK_AS_READ_ACTION
// TODO: Use a constant putExtra(NOTIFICATION_EXTRA_JID_KEY, notification.jid)
putExtra("jid", notification.jid) putExtra(NOTIFICATION_EXTRA_ID_KEY, notification.id)
putExtra("notification_id", notification.id)
} }
val markAsReadPendingIntent = PendingIntent.getBroadcast( val markAsReadPendingIntent = PendingIntent.getBroadcast(
context.applicationContext, context.applicationContext,
@ -59,7 +62,7 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
// TODO: Wrong icon // TODO: Wrong icon
R.drawable.ic_service_icon, R.drawable.ic_service_icon,
// TODO: i18n // TODO: i18n
"Mark as read", NotificationI18nManager.markAsRead,
markAsReadPendingIntent, markAsReadPendingIntent,
).build() ).build()
@ -67,9 +70,8 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
// Thanks https://github.com/MaikuB/flutter_local_notifications/blob/master/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java#L246 // Thanks https://github.com/MaikuB/flutter_local_notifications/blob/master/flutter_local_notifications/android/src/main/java/com/dexterous/flutterlocalnotifications/FlutterLocalNotificationsPlugin.java#L246
val tapIntent = Intent(context, NotificationReceiver::class.java).apply { val tapIntent = Intent(context, NotificationReceiver::class.java).apply {
action = TAP_ACTION action = TAP_ACTION
// TODO: Use constants putExtra(NOTIFICATION_EXTRA_JID_KEY, notification.jid)
putExtra("jid", notification.jid) putExtra(NOTIFICATION_EXTRA_ID_KEY, notification.id)
putExtra("notification_id", notification.id)
} }
val tapPendingIntent = PendingIntent.getBroadcast( val tapPendingIntent = PendingIntent.getBroadcast(
context, context,
@ -80,8 +82,7 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
// Build the notification // Build the notification
// TODO: Use a person // TODO: Use a person
// TODO: i18n val style = NotificationCompat.MessagingStyle(NotificationI18nManager.you);
val style = NotificationCompat.MessagingStyle("Me");
for (message in notification.messages) { for (message in notification.messages) {
// Build the sender // Build the sender
val sender = Person.Builder().apply { val sender = Person.Builder().apply {
@ -131,6 +132,9 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
// Notification actions // Notification actions
addAction(replyAction) addAction(replyAction)
addAction(markAsReadAction) addAction(markAsReadAction)
// Prevent no notification when we replied before
setOnlyAlertOnce(false)
}.build() }.build()
// Post the notification // Post the notification

View File

@ -13,8 +13,9 @@ class AndroidNotificationsImplementation extends NotificationsImplementation {
String title, String title,
String id, String id,
bool urgent, bool urgent,
NotificationI18nData i18n,
) async { ) async {
return _api.createNotificationChannel(title, id, urgent); return _api.createNotificationChannel(title, id, urgent, i18n);
} }
@override @override

View File

@ -175,6 +175,40 @@ class NotificationEvent {
} }
} }
class NotificationI18nData {
NotificationI18nData({
required this.reply,
required this.markAsRead,
required this.you,
});
/// The content of the reply button.
String reply;
/// The content of the "mark as read" button.
String markAsRead;
/// The text to show when *you* reply.
String you;
Object encode() {
return <Object?>[
reply,
markAsRead,
you,
];
}
static NotificationI18nData decode(Object result) {
result as List<Object?>;
return NotificationI18nData(
reply: result[0]! as String,
markAsRead: result[1]! as String,
you: result[2]! as String,
);
}
}
class _MoxplatformApiCodec extends StandardMessageCodec { class _MoxplatformApiCodec extends StandardMessageCodec {
const _MoxplatformApiCodec(); const _MoxplatformApiCodec();
@override @override
@ -185,12 +219,15 @@ class _MoxplatformApiCodec extends StandardMessageCodec {
} else if (value is NotificationEvent) { } else if (value is NotificationEvent) {
buffer.putUint8(129); buffer.putUint8(129);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is NotificationMessage) { } else if (value is NotificationI18nData) {
buffer.putUint8(130); buffer.putUint8(130);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is NotificationMessageContent) { } else if (value is NotificationMessage) {
buffer.putUint8(131); buffer.putUint8(131);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is NotificationMessageContent) {
buffer.putUint8(132);
writeValue(buffer, value.encode());
} else { } else {
super.writeValue(buffer, value); super.writeValue(buffer, value);
} }
@ -204,8 +241,10 @@ class _MoxplatformApiCodec extends StandardMessageCodec {
case 129: case 129:
return NotificationEvent.decode(readValue(buffer)!); return NotificationEvent.decode(readValue(buffer)!);
case 130: case 130:
return NotificationMessage.decode(readValue(buffer)!); return NotificationI18nData.decode(readValue(buffer)!);
case 131: case 131:
return NotificationMessage.decode(readValue(buffer)!);
case 132:
return NotificationMessageContent.decode(readValue(buffer)!); return NotificationMessageContent.decode(readValue(buffer)!);
default: default:
return super.readValueOfType(type, buffer); return super.readValueOfType(type, buffer);
@ -223,12 +262,12 @@ class MoxplatformApi {
static const MessageCodec<Object?> codec = _MoxplatformApiCodec(); static const MessageCodec<Object?> codec = _MoxplatformApiCodec();
Future<void> createNotificationChannel(String arg_title, String arg_id, bool arg_urgent) async { Future<void> createNotificationChannel(String arg_title, String arg_id, bool arg_urgent, NotificationI18nData arg_i18n) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.createNotificationChannel', codec, 'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.createNotificationChannel', codec,
binaryMessenger: _binaryMessenger); binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = final List<Object?>? replyList =
await channel.send(<Object?>[arg_title, arg_id, arg_urgent]) as List<Object?>?; await channel.send(<Object?>[arg_title, arg_id, arg_urgent, arg_i18n]) as List<Object?>?;
if (replyList == null) { if (replyList == null) {
throw PlatformException( throw PlatformException(
code: 'channel-error', code: 'channel-error',

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:moxplatform_platform_interface/src/api.g.dart'; import 'package:moxplatform_platform_interface/src/api.g.dart';
abstract class NotificationsImplementation { abstract class NotificationsImplementation {
Future<void> createNotificationChannel(String title, String id, bool urgent); Future<void> createNotificationChannel(String title, String id, bool urgent, NotificationI18nData i18n);
Future<void> showMessagingNotification(MessagingNotification notification); Future<void> showMessagingNotification(MessagingNotification notification);

View File

@ -4,7 +4,7 @@ import 'package:moxplatform_platform_interface/src/notifications.dart';
class StubNotificationsImplementation extends NotificationsImplementation { class StubNotificationsImplementation extends NotificationsImplementation {
@override @override
Future<void> createNotificationChannel(String title, String id, bool urgent) async {} Future<void> createNotificationChannel(String title, String id, bool urgent, NotificationI18nData i18n) async {}
@override @override
Future<void> showMessagingNotification(MessagingNotification notification) async {} Future<void> showMessagingNotification(MessagingNotification notification) async {}

View File

@ -97,9 +97,22 @@ class NotificationEvent {
final String? payload; final String? payload;
} }
class NotificationI18nData {
const NotificationI18nData(this.reply, this.markAsRead, this.you);
/// The content of the reply button.
final String reply;
/// The content of the "mark as read" button.
final String markAsRead;
/// The text to show when *you* reply.
final String you;
}
@HostApi() @HostApi()
abstract class MoxplatformApi { abstract class MoxplatformApi {
void createNotificationChannel(String title, String id, bool urgent); void createNotificationChannel(String title, String id, bool urgent, NotificationI18nData i18n);
void showMessagingNotification(MessagingNotification notification); void showMessagingNotification(MessagingNotification notification);