feat: Take care of i18n
This commit is contained in:
parent
f90b3866ab
commit
adb8ee88d1
@ -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})');
|
||||||
|
@ -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) {
|
||||||
|
@ -16,11 +16,11 @@ public class BootReceiver extends BroadcastReceiver {
|
|||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (MoxplatformAndroidPlugin.getStartAtBoot(context)) {
|
if (MoxplatformAndroidPlugin.getStartAtBoot(context)) {
|
||||||
if (BackgroundService.wakeLock == null) {
|
if (BackgroundService.wakeLock == null) {
|
||||||
Log.d(TAG, "Wakelock is null. Acquiring it...");
|
Log.d(TAG, "Wakelock is null. Acquiring it...");
|
||||||
BackgroundService.getLock(context).acquire(MoxplatformConstants.WAKE_LOCK_DURATION);
|
BackgroundService.getLock(context).acquire(MoxplatformConstants.WAKE_LOCK_DURATION);
|
||||||
Log.d(TAG, "Wakelock acquired...");
|
Log.d(TAG, "Wakelock acquired...");
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextCompat.startForegroundService(context, new Intent(context, BackgroundService.class));
|
ContextCompat.startForegroundService(context, new Intent(context, BackgroundService.class));
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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',
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 {}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user