feat: Allow setting the self-avatar

This commit is contained in:
PapaTutuWawa 2023-07-28 21:46:47 +02:00
parent e975e749e4
commit daf40aed0b
10 changed files with 119 additions and 17 deletions

View File

@ -189,6 +189,17 @@ class MyHomePage extends StatelessWidget {
},
child: const Text('Show messaging notification'),
),
ElevatedButton(
onPressed: () async {
final result = await FilePicker.platform.pickFiles(
type: FileType.image,
);
if (result == null) return;
MoxplatformPlugin.notifications.setNotificationSelfAvatar(result.files.single.path!);
},
child: const Text('Set notification self-avatar'),
),
ElevatedButton(
onPressed: () async {
print(await MoxplatformPlugin.platform.getPersistentDataPath());

View File

@ -707,6 +707,8 @@ public class Api {
void showMessagingNotification(@NonNull MessagingNotification notification);
void setNotificationSelfAvatar(@NonNull String path);
@NonNull
String getPersistentDataPath();
@ -762,6 +764,30 @@ public class Api {
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.setNotificationSelfAvatar", getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
String pathArg = (String) args.get(0);
try {
api.setNotificationSelfAvatar(pathArg);
wrapped.add(0, null);
}
catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;

View File

@ -300,9 +300,9 @@ import kotlin.jvm.functions.Function1;
manager.createNotificationChannel(channel);
// Configure i18n
NotificationI18nManager.INSTANCE.setYou(i18n.getYou());
NotificationI18nManager.INSTANCE.setReply(i18n.getReply());
NotificationI18nManager.INSTANCE.setMarkAsRead(i18n.getMarkAsRead());
NotificationDataManager.INSTANCE.setYou(i18n.getYou());
NotificationDataManager.INSTANCE.setReply(i18n.getReply());
NotificationDataManager.INSTANCE.setMarkAsRead(i18n.getMarkAsRead());
}
@Override
@ -310,7 +310,12 @@ import kotlin.jvm.functions.Function1;
NotificationsKt.showMessagingNotification(context, notification);
}
@NonNull
@Override
public void setNotificationSelfAvatar(@NonNull String path) {
NotificationDataManager.INSTANCE.setAvatarPath(path);
}
@NonNull
@Override
public String getPersistentDataPath() {
return context.getFilesDir().getPath();

View File

@ -6,6 +6,8 @@ import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.drawable.Icon
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat
@ -13,6 +15,7 @@ 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 me.polynom.moxplatform_android.Api.NotificationEvent
import java.io.File
import java.time.Instant
@ -43,8 +46,6 @@ class NotificationReceiver : BroadcastReceiver() {
NotificationManagerCompat.from(context).cancel(intent.getLongExtra(MARK_AS_READ_ID_KEY, -1).toInt())
MoxplatformAndroidPlugin.notificationSink?.success(
NotificationEvent().apply {
// TODO: Use constant for key
// TODO: Fix
jid = intent.getStringExtra(NOTIFICATION_EXTRA_JID_KEY)!!
type = Api.NotificationEventType.MARK_AS_READ
payload = null
@ -59,7 +60,6 @@ class NotificationReceiver : BroadcastReceiver() {
val replyPayload = remoteInput.getCharSequence(REPLY_TEXT_KEY)
MoxplatformAndroidPlugin.notificationSink?.success(
NotificationEvent().apply {
// TODO: Use constant for key
jid = intent.getStringExtra(NOTIFICATION_EXTRA_JID_KEY)!!
type = Api.NotificationEventType.REPLY
payload = replyPayload.toString()
@ -80,10 +80,25 @@ class NotificationReceiver : BroadcastReceiver() {
// 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 {
val newStyle = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
Notification.MessagingStyle(
android.app.Person.Builder().apply {
setName(NotificationDataManager.you)
// Set an avatar, if we have one
if (NotificationDataManager.avatarPath != null) {
setIcon(
Icon.createWithAdaptiveBitmap(
BitmapFactory.decodeFile(NotificationDataManager.avatarPath)
)
)
}
}.build()
)
else Notification.MessagingStyle(NotificationDataManager.you)
newStyle.apply {
conversationTitle = recoveredStyle.conversationTitle
// TODO: Use person
recoveredStyle.messages.forEach {
// Check if we have to request (or refresh) the content URI to be able to still
// see the embedded image.

View File

@ -12,10 +12,11 @@ import androidx.core.content.FileProvider
import androidx.core.graphics.drawable.IconCompat
import java.io.File
object NotificationI18nManager {
object NotificationDataManager {
var you: String = "You"
var markAsRead: String = "Mark as read"
var reply: String = "Reply"
var avatarPath: String? = null
}
/// Show a messaging style notification described by @notification.
@ -23,7 +24,7 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
// Build the actions
// -> Reply action
val remoteInput = RemoteInput.Builder(REPLY_TEXT_KEY).apply {
setLabel(NotificationI18nManager.reply)
setLabel(NotificationDataManager.reply)
}.build()
val replyIntent = Intent(context, NotificationReceiver::class.java).apply {
action = REPLY_ACTION
@ -39,7 +40,7 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
val replyAction = NotificationCompat.Action.Builder(
// TODO: Wrong icon?
R.drawable.ic_service_icon,
NotificationI18nManager.reply,
NotificationDataManager.reply,
replyPendingIntent,
).apply {
addRemoteInput(remoteInput)
@ -61,8 +62,7 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
val markAsReadAction = NotificationCompat.Action.Builder(
// TODO: Wrong icon
R.drawable.ic_service_icon,
// TODO: i18n
NotificationI18nManager.markAsRead,
NotificationDataManager.markAsRead,
markAsReadPendingIntent,
).build()
@ -81,8 +81,19 @@ fun showMessagingNotification(context: Context, notification: Api.MessagingNotif
)
// Build the notification
// TODO: Use a person
val style = NotificationCompat.MessagingStyle(NotificationI18nManager.you);
val selfPerson = Person.Builder().apply {
setName(NotificationDataManager.you)
// Set an avatar, if we have one
if (NotificationDataManager.avatarPath != null) {
setIcon(
IconCompat.createWithAdaptiveBitmap(
BitmapFactory.decodeFile(NotificationDataManager.avatarPath),
),
)
}
}.build()
val style = NotificationCompat.MessagingStyle(selfPerson);
for (message in notification.messages) {
// Build the sender
val sender = Person.Builder().apply {

View File

@ -25,6 +25,11 @@ class AndroidNotificationsImplementation extends NotificationsImplementation {
return _api.showMessagingNotification(notification);
}
@override
Future<void> setNotificationSelfAvatar(String path) async {
return _api.setNotificationSelfAvatar(path);
}
@override
Stream<NotificationEvent> getEventStream() => _channel
.receiveBroadcastStream()

View File

@ -306,6 +306,28 @@ class MoxplatformApi {
}
}
Future<void> setNotificationSelfAvatar(String arg_path) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.setNotificationSelfAvatar', codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList =
await channel.send(<Object?>[arg_path]) 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<String> getPersistentDataPath() async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.getPersistentDataPath', codec,

View File

@ -6,5 +6,7 @@ abstract class NotificationsImplementation {
Future<void> showMessagingNotification(MessagingNotification notification);
Future<void> setNotificationSelfAvatar(String path);
Stream<NotificationEvent> getEventStream();
}

View File

@ -9,6 +9,9 @@ class StubNotificationsImplementation extends NotificationsImplementation {
@override
Future<void> showMessagingNotification(MessagingNotification notification) async {}
@override
Future<void> setNotificationSelfAvatar(String path) async {}
@override
Stream<NotificationEvent> getEventStream() {
return StreamController<NotificationEvent>().stream;

View File

@ -116,6 +116,8 @@ abstract class MoxplatformApi {
void showMessagingNotification(MessagingNotification notification);
void setNotificationSelfAvatar(String path);
String getPersistentDataPath();
String getCacheDataPath();