Rewrite the notification code in Kotlin
This commit is contained in:
@@ -21,7 +21,7 @@ import java.util.Map;
|
||||
|
||||
/** Generated class from Pigeon. */
|
||||
@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"})
|
||||
public class Notifications {
|
||||
public class Api {
|
||||
|
||||
/** Error class for passing custom error details to Flutter via a thrown PlatformException. */
|
||||
public static class FlutterError extends RuntimeException {
|
||||
@@ -416,10 +416,10 @@ public class Notifications {
|
||||
}
|
||||
}
|
||||
|
||||
private static class NotificationsImplementationApiCodec extends StandardMessageCodec {
|
||||
public static final NotificationsImplementationApiCodec INSTANCE = new NotificationsImplementationApiCodec();
|
||||
private static class MoxplatformApiCodec extends StandardMessageCodec {
|
||||
public static final MoxplatformApiCodec INSTANCE = new MoxplatformApiCodec();
|
||||
|
||||
private NotificationsImplementationApiCodec() {}
|
||||
private MoxplatformApiCodec() {}
|
||||
|
||||
@Override
|
||||
protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
|
||||
@@ -453,22 +453,28 @@ public class Notifications {
|
||||
}
|
||||
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
public interface NotificationsImplementationApi {
|
||||
public interface MoxplatformApi {
|
||||
|
||||
void createNotificationChannel(@NonNull String title, @NonNull String id, @NonNull Boolean urgent);
|
||||
|
||||
void showMessagingNotification(@NonNull MessagingNotification notification);
|
||||
|
||||
/** The codec used by NotificationsImplementationApi. */
|
||||
@NonNull
|
||||
String getPersistentDataPath();
|
||||
|
||||
@NonNull
|
||||
String getCacheDataPath();
|
||||
|
||||
/** The codec used by MoxplatformApi. */
|
||||
static @NonNull MessageCodec<Object> getCodec() {
|
||||
return NotificationsImplementationApiCodec.INSTANCE;
|
||||
return MoxplatformApiCodec.INSTANCE;
|
||||
}
|
||||
/**Sets up an instance of `NotificationsImplementationApi` to handle messages through the `binaryMessenger`. */
|
||||
static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable NotificationsImplementationApi api) {
|
||||
/**Sets up an instance of `MoxplatformApi` to handle messages through the `binaryMessenger`. */
|
||||
static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable MoxplatformApi api) {
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.NotificationsImplementationApi.createNotificationChannel", getCodec());
|
||||
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.createNotificationChannel", getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
@@ -494,7 +500,7 @@ public class Notifications {
|
||||
{
|
||||
BasicMessageChannel<Object> channel =
|
||||
new BasicMessageChannel<>(
|
||||
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.NotificationsImplementationApi.showMessagingNotification", getCodec());
|
||||
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.showMessagingNotification", getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
@@ -505,6 +511,50 @@ public class Notifications {
|
||||
api.showMessagingNotification(notificationArg);
|
||||
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.getPersistentDataPath", getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
try {
|
||||
String output = api.getPersistentDataPath();
|
||||
wrapped.add(0, output);
|
||||
}
|
||||
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.getCacheDataPath", getCodec());
|
||||
if (api != null) {
|
||||
channel.setMessageHandler(
|
||||
(message, reply) -> {
|
||||
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||
try {
|
||||
String output = api.getCacheDataPath();
|
||||
wrapped.add(0, output);
|
||||
}
|
||||
catch (Throwable exception) {
|
||||
ArrayList<Object> wrappedError = wrapError(exception);
|
||||
wrapped = wrappedError;
|
||||
@@ -1,36 +1,24 @@
|
||||
package me.polynom.moxplatform_android;
|
||||
|
||||
import static androidx.core.content.ContextCompat.getSystemService;
|
||||
import static me.polynom.moxplatform_android.ConstantsKt.MARK_AS_READ_ACTION;
|
||||
import static me.polynom.moxplatform_android.ConstantsKt.MARK_AS_READ_ID_KEY;
|
||||
import static me.polynom.moxplatform_android.ConstantsKt.REPLY_TEXT_KEY;
|
||||
import static me.polynom.moxplatform_android.RecordSentMessageKt.recordSentMessage;
|
||||
import static me.polynom.moxplatform_android.CryptoKt.*;
|
||||
import me.polynom.moxplatform_android.Notifications.*;
|
||||
import me.polynom.moxplatform_android.Api.*;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.RemoteInput;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.app.Person;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -46,7 +34,7 @@ import io.flutter.plugin.common.JSONMethodCodec;
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
|
||||
public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, ServiceAware, NotificationsImplementationApi {
|
||||
public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, ServiceAware, MoxplatformApi {
|
||||
public static final String entrypointKey = "entrypoint_handle";
|
||||
public static final String extraDataKey = "extra_data";
|
||||
private static final String autoStartAtBootKey = "auto_start_at_boot";
|
||||
@@ -60,8 +48,6 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
||||
private MethodChannel channel;
|
||||
private Context context;
|
||||
|
||||
private FileProvider provider = new FileProvider();
|
||||
|
||||
public MoxplatformAndroidPlugin() {
|
||||
_instances.add(this);
|
||||
}
|
||||
@@ -75,7 +61,7 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
|
||||
localBroadcastManager.registerReceiver(this, new IntentFilter(methodChannelKey));
|
||||
|
||||
NotificationsImplementationApi.setup(flutterPluginBinding.getBinaryMessenger(), this);
|
||||
MoxplatformApi.setup(flutterPluginBinding.getBinaryMessenger(), this);
|
||||
|
||||
Log.d(TAG, "Attached to engine");
|
||||
}
|
||||
@@ -292,62 +278,18 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
||||
|
||||
@Override
|
||||
public void showMessagingNotification(@NonNull MessagingNotification notification) {
|
||||
// Create a reply button
|
||||
// TODO: i18n
|
||||
RemoteInput remoteInput = new RemoteInput.Builder(REPLY_TEXT_KEY).setLabel("Reply").build();
|
||||
final Intent replyIntent = new Intent(context, NotificationReceiver.class);
|
||||
final PendingIntent replyPendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 0, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
// TODO: i18n
|
||||
// TODO: Correct icon
|
||||
final NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_service_icon, "Reply", replyPendingIntent)
|
||||
.addRemoteInput(remoteInput)
|
||||
.build();
|
||||
NotificationsKt.showMessagingNotification(context, notification);
|
||||
}
|
||||
|
||||
// Create the "mark as read" button
|
||||
final Intent markAsReadIntent = new Intent(context, NotificationReceiver.class);
|
||||
markAsReadIntent.setAction(MARK_AS_READ_ACTION);
|
||||
markAsReadIntent.putExtra(MARK_AS_READ_ID_KEY, notification.getId());
|
||||
// TODO: Replace with something more useful
|
||||
markAsReadIntent.putExtra("title", notification.getTitle());
|
||||
final PendingIntent markAsReadPendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 0, readIntent,PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
@NonNull
|
||||
@Override
|
||||
public String getPersistentDataPath() {
|
||||
return context.getFilesDir().getPath();
|
||||
}
|
||||
|
||||
final NotificationCompat.MessagingStyle style = new NotificationCompat.MessagingStyle("Me")
|
||||
.setConversationTitle(notification.getTitle());
|
||||
for (final NotificationMessage message : notification.getMessages()) {
|
||||
// Build the sender of the message
|
||||
final Person.Builder personBuilder = new Person.Builder()
|
||||
.setName(message.getSender())
|
||||
.setKey(message.getJid());
|
||||
if (message.getAvatarPath() != null) {
|
||||
final IconCompat icon = IconCompat.createWithAdaptiveBitmap(
|
||||
BitmapFactory.decodeFile(message.getAvatarPath())
|
||||
);
|
||||
personBuilder.setIcon(icon);
|
||||
}
|
||||
|
||||
// Build the message
|
||||
final String content = message.getContent().getBody() == null ? "" : message.getContent().getBody();
|
||||
final NotificationCompat.MessagingStyle.Message msg = new NotificationCompat.MessagingStyle.Message(
|
||||
content,
|
||||
message.getTimestamp(),
|
||||
personBuilder.build()
|
||||
);
|
||||
// Turn the image path to a content Uri, if a media file was specified
|
||||
if (message.getContent().getMime() != null && message.getContent().getPath() != null) {
|
||||
final Uri fileUri = androidx.core.content.FileProvider.getUriForFile(context, "me.polynom.moxplatform_android.fileprovider", new File(message.getContent().getPath()));
|
||||
msg.setData(message.getContent().getMime(), fileUri);
|
||||
}
|
||||
|
||||
style.addMessage(msg);
|
||||
}
|
||||
|
||||
// Build the notification and send it
|
||||
final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, notification.getChannelId())
|
||||
.setStyle(style)
|
||||
// TODO: This is wrong
|
||||
.setSmallIcon(R.drawable.ic_service_icon)
|
||||
.addAction(action)
|
||||
.addAction(R.drawable.ic_service_icon, "Mark as read", markAsReadPendingIntent);
|
||||
NotificationManagerCompat.from(context).notify(notification.getId().intValue(), notificationBuilder.build());
|
||||
@NonNull
|
||||
@Override
|
||||
public String getCacheDataPath() {
|
||||
return context.getCacheDir().getPath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package me.polynom.moxplatform_android
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.app.Person
|
||||
import androidx.core.app.RemoteInput
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import java.io.File
|
||||
|
||||
/// Show a messaging style notification described by @notification.
|
||||
fun showMessagingNotification(context: Context, notification: Api.MessagingNotification) {
|
||||
// Build the actions
|
||||
// -> Reply action
|
||||
val remoteInput = RemoteInput.Builder(REPLY_TEXT_KEY).apply {
|
||||
// TODO: i18n
|
||||
setLabel("Reply")
|
||||
}.build()
|
||||
val replyIntent = Intent(context, NotificationReceiver::class.java)
|
||||
val replyPendingIntent = PendingIntent.getBroadcast(
|
||||
context.applicationContext,
|
||||
0,
|
||||
replyIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT,
|
||||
)
|
||||
val replyAction = NotificationCompat.Action.Builder(
|
||||
// TODO: Wrong icon?
|
||||
R.drawable.ic_service_icon,
|
||||
// TODO: i18n
|
||||
"Reply",
|
||||
replyPendingIntent,
|
||||
).apply {
|
||||
addRemoteInput(remoteInput)
|
||||
}.build()
|
||||
|
||||
// -> Mark as read action
|
||||
val markAsReadIntent = Intent(context, NotificationReceiver::class.java).apply {
|
||||
action = MARK_AS_READ_ACTION
|
||||
// TODO: Put the JID here
|
||||
putExtra("title", notification.title)
|
||||
}
|
||||
val markAsReadPendingIntent = PendingIntent.getBroadcast(
|
||||
context.applicationContext,
|
||||
0,
|
||||
markAsReadIntent,
|
||||
0,
|
||||
)
|
||||
|
||||
// Build the notification
|
||||
// TODO: Use a person
|
||||
// TODO: i18n
|
||||
val style = NotificationCompat.MessagingStyle("Me");
|
||||
for (message in notification.messages) {
|
||||
// Build the sender
|
||||
val sender = Person.Builder().apply {
|
||||
setName(message.sender)
|
||||
setKey(message.jid)
|
||||
|
||||
// Set the avatar, if available
|
||||
if (message.avatarPath != null) {
|
||||
setIcon(
|
||||
IconCompat.createWithAdaptiveBitmap(
|
||||
BitmapFactory.decodeFile(message.avatarPath),
|
||||
),
|
||||
)
|
||||
}
|
||||
}.build()
|
||||
|
||||
// Build the message
|
||||
val body = message.content.body ?: ""
|
||||
val msg = NotificationCompat.MessagingStyle.Message(
|
||||
body,
|
||||
message.timestamp,
|
||||
sender,
|
||||
)
|
||||
// If we got an image, turn it into a content URI and set it
|
||||
if (message.content.mime != null && message.content.path != null) {
|
||||
val fileUri = FileProvider.getUriForFile(
|
||||
context,
|
||||
"me.polynom.moxplatform_android.fileprovider",
|
||||
File(message.content.path),
|
||||
)
|
||||
msg.setData(message.content.mime, fileUri)
|
||||
}
|
||||
|
||||
// Append the message
|
||||
style.addMessage(msg)
|
||||
}
|
||||
|
||||
// Assemble the notification
|
||||
val finalNotification = NotificationCompat.Builder(context, notification.channelId).apply {
|
||||
setStyle(style)
|
||||
// TODO: I think this is wrong
|
||||
setSmallIcon(R.drawable.ic_service_icon)
|
||||
|
||||
addAction(replyAction)
|
||||
addAction(
|
||||
// TODO: Wrong icon
|
||||
R.drawable.ic_service_icon,
|
||||
// TODO: i18n
|
||||
"Mark as read",
|
||||
markAsReadPendingIntent,
|
||||
)
|
||||
}.build()
|
||||
|
||||
// Post the notification
|
||||
NotificationManagerCompat.from(context).notify(
|
||||
notification.id.toInt(),
|
||||
finalNotification,
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- For testing -->
|
||||
<cache-path name="file_picker" path="file_picker/"/>
|
||||
|
||||
<!-- Moxxy -->
|
||||
<files-path name="media" path="media/" />
|
||||
</paths>
|
||||
Reference in New Issue
Block a user