Basic stuff
This commit is contained in:
parent
17642f9fab
commit
1771c0e1b6
@ -47,7 +47,7 @@ android {
|
|||||||
applicationId "com.example.example"
|
applicationId "com.example.example"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion 26
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
@ -34,5 +34,5 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -1,17 +1,49 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:moxplatform/moxplatform.dart';
|
import 'package:moxplatform/moxplatform.dart';
|
||||||
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
|
||||||
|
/// The id of the notification channel.
|
||||||
|
const channelId = "me.polynom.moxplatform.testing3";
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class Sender {
|
||||||
|
const Sender(this.name, this.jid);
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
final String jid;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatefulWidget {
|
||||||
const MyApp({Key? key}) : super(key: key);
|
const MyApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
MyAppState createState() => MyAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyAppState extends State<MyApp> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
initStateAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initStateAsync() async {
|
||||||
|
await Permission.notification.request();
|
||||||
|
|
||||||
|
await MoxplatformPlugin.notifications.createNotificationChannel("Test notification channel", channelId, false);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
@ -19,13 +51,23 @@ class MyApp extends StatelessWidget {
|
|||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
),
|
),
|
||||||
home: const MyHomePage(),
|
home: MyHomePage(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyHomePage extends StatelessWidget {
|
class MyHomePage extends StatelessWidget {
|
||||||
const MyHomePage({super.key});
|
MyHomePage({super.key});
|
||||||
|
|
||||||
|
/// List of "Message senders".
|
||||||
|
final List<Sender> senders = const [
|
||||||
|
Sender('Mash Kyrielight', 'mash@example.org'),
|
||||||
|
Sender('Rio Tsukatsuki', 'rio@millenium'),
|
||||||
|
Sender('Raiden Shogun', 'raiden@tevhat'),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// List of sent messages.
|
||||||
|
List<NotificationMessage> messages = List<NotificationMessage>.empty(growable: true);
|
||||||
|
|
||||||
Future<void> _cryptoTest() async {
|
Future<void> _cryptoTest() async {
|
||||||
final result = await FilePicker.platform.pickFiles();
|
final result = await FilePicker.platform.pickFiles();
|
||||||
@ -98,6 +140,41 @@ class MyHomePage extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
child: const Text('Test recordSentMessage (notes fallback)'),
|
child: const Text('Test recordSentMessage (notes fallback)'),
|
||||||
),
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await FilePicker.platform.pickFiles(
|
||||||
|
type: FileType.image,
|
||||||
|
);
|
||||||
|
print('Picked file: ${result?.files.single.path}');
|
||||||
|
|
||||||
|
// Create a new message.
|
||||||
|
final senderIndex = Random().nextInt(senders.length);
|
||||||
|
final time = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
messages.add(
|
||||||
|
NotificationMessage(
|
||||||
|
jid: senders[senderIndex].jid,
|
||||||
|
sender: senders[senderIndex].name,
|
||||||
|
content: NotificationMessageContent(
|
||||||
|
body: result != null ? null : 'Message #${messages.length}',
|
||||||
|
mime: 'image/jpeg',
|
||||||
|
path: result?.files.single.path,
|
||||||
|
),
|
||||||
|
timestamp: time,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
await Future<void>.delayed(const Duration(seconds: 4));
|
||||||
|
await MoxplatformPlugin.notifications.showMessagingNotification(
|
||||||
|
MessagingNotification(
|
||||||
|
id: 2343,
|
||||||
|
title: 'Test conversation',
|
||||||
|
messages: messages,
|
||||||
|
channelId: channelId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Show messaging notification'),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -39,6 +39,8 @@ dependencies:
|
|||||||
|
|
||||||
file_picker: 5.2.0+1
|
file_picker: 5.2.0+1
|
||||||
|
|
||||||
|
permission_handler: 10.4.3
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
|
@ -5,4 +5,5 @@ class MoxplatformPlugin {
|
|||||||
static MediaScannerImplementation get media => MoxplatformInterface.media;
|
static MediaScannerImplementation get media => MoxplatformInterface.media;
|
||||||
static CryptographyImplementation get crypto => MoxplatformInterface.crypto;
|
static CryptographyImplementation get crypto => MoxplatformInterface.crypto;
|
||||||
static ContactsImplementation get contacts => MoxplatformInterface.contacts;
|
static ContactsImplementation get contacts => MoxplatformInterface.contacts;
|
||||||
|
static NotificationsImplementation get notifications => MoxplatformInterface.notifications;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,8 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
// What Moxxy currently uses
|
||||||
|
minSdkVersion 26
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="me.polynom.moxplatform_android">
|
package="me.polynom.moxplatform_android">
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
|
|
||||||
|
|
||||||
<application>
|
<application>
|
||||||
|
<provider
|
||||||
|
android:name="me.polynom.moxplatform_android.MoxplatformFileProvider"
|
||||||
|
android:authorities="me.polynom.moxplatform_android.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@ -27,5 +38,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver android:name=".NotificationReceiver" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -6,6 +6,14 @@ 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
|
||||||
|
|
||||||
|
// The data key for text entered in the notification's reply field
|
||||||
|
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
|
||||||
|
const val MARK_AS_READ_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"
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
package me.polynom.moxplatform_android
|
||||||
|
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
|
||||||
|
class MoxplatformFileProvider : FileProvider(R.xml.file_paths) {
|
||||||
|
}
|
@ -1,31 +1,39 @@
|
|||||||
package me.polynom.moxplatform_android;
|
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.RecordSentMessageKt.recordSentMessage;
|
||||||
import static me.polynom.moxplatform_android.CryptoKt.*;
|
import static me.polynom.moxplatform_android.CryptoKt.*;
|
||||||
|
import me.polynom.moxplatform_android.Notifications.*;
|
||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
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.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
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.content.ContextCompat;
|
||||||
|
import androidx.core.graphics.drawable.IconCompat;
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.File;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.CipherOutputStream;
|
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||||
import io.flutter.embedding.engine.plugins.service.ServiceAware;
|
import io.flutter.embedding.engine.plugins.service.ServiceAware;
|
||||||
import io.flutter.embedding.engine.plugins.service.ServicePluginBinding;
|
import io.flutter.embedding.engine.plugins.service.ServicePluginBinding;
|
||||||
@ -35,8 +43,10 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
|||||||
import io.flutter.plugin.common.MethodChannel.Result;
|
import io.flutter.plugin.common.MethodChannel.Result;
|
||||||
import io.flutter.plugin.common.PluginRegistry.Registrar;
|
import io.flutter.plugin.common.PluginRegistry.Registrar;
|
||||||
import io.flutter.plugin.common.JSONMethodCodec;
|
import io.flutter.plugin.common.JSONMethodCodec;
|
||||||
|
import kotlin.Unit;
|
||||||
|
import kotlin.jvm.functions.Function1;
|
||||||
|
|
||||||
public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, ServiceAware {
|
public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, ServiceAware, NotificationsImplementationApi {
|
||||||
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";
|
||||||
@ -50,6 +60,8 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
private MethodChannel channel;
|
private MethodChannel channel;
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
|
private FileProvider provider = new FileProvider();
|
||||||
|
|
||||||
public MoxplatformAndroidPlugin() {
|
public MoxplatformAndroidPlugin() {
|
||||||
_instances.add(this);
|
_instances.add(this);
|
||||||
}
|
}
|
||||||
@ -63,6 +75,8 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
|
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
|
||||||
localBroadcastManager.registerReceiver(this, new IntentFilter(methodChannelKey));
|
localBroadcastManager.registerReceiver(this, new IntentFilter(methodChannelKey));
|
||||||
|
|
||||||
|
NotificationsImplementationApi.setup(flutterPluginBinding.getBinaryMessenger(), this);
|
||||||
|
|
||||||
Log.d(TAG, "Attached to engine");
|
Log.d(TAG, "Attached to engine");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +131,7 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
public void onMethodCall(@NonNull MethodCall call, @NonNull io.flutter.plugin.common.MethodChannel.Result result) {
|
||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
case "configure":
|
case "configure":
|
||||||
ArrayList args = (ArrayList) call.arguments;
|
ArrayList args = (ArrayList) call.arguments;
|
||||||
@ -262,4 +276,78 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
Log.d(TAG, "Detached from service");
|
Log.d(TAG, "Detached from service");
|
||||||
this.service = null;
|
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) {
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package me.polynom.moxplatform_android
|
||||||
|
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import androidx.core.app.RemoteInput
|
||||||
|
|
||||||
|
class NotificationReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
// If it is a mark as read, dismiss the entire notification and
|
||||||
|
// send a notification to the app.
|
||||||
|
// TODO: Notify app
|
||||||
|
if (intent.action == MARK_AS_READ_ACTION) {
|
||||||
|
Log.d("NotificationReceiver", "Marking ${intent.getStringExtra("title")} as read")
|
||||||
|
NotificationManagerCompat.from(context).cancel(intent.getLongExtra(MARK_AS_READ_ID_KEY, -1).toInt())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val remoteInput = RemoteInput.getResultsFromIntent(intent) ?: return
|
||||||
|
|
||||||
|
val title = remoteInput.getCharSequence(REPLY_TEXT_KEY).toString()
|
||||||
|
Log.d("NotificationReceiver", title)
|
||||||
|
// TODO: Notify app
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,520 @@
|
|||||||
|
// Autogenerated from Pigeon (v10.1.4), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
|
||||||
|
package me.polynom.moxplatform_android;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import io.flutter.plugin.common.BasicMessageChannel;
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger;
|
||||||
|
import io.flutter.plugin.common.MessageCodec;
|
||||||
|
import io.flutter.plugin.common.StandardMessageCodec;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/** Generated class from Pigeon. */
|
||||||
|
@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"})
|
||||||
|
public class Notifications {
|
||||||
|
|
||||||
|
/** Error class for passing custom error details to Flutter via a thrown PlatformException. */
|
||||||
|
public static class FlutterError extends RuntimeException {
|
||||||
|
|
||||||
|
/** The error code. */
|
||||||
|
public final String code;
|
||||||
|
|
||||||
|
/** The error details. Must be a datatype supported by the api codec. */
|
||||||
|
public final Object details;
|
||||||
|
|
||||||
|
public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected static ArrayList<Object> wrapError(@NonNull Throwable exception) {
|
||||||
|
ArrayList<Object> errorList = new ArrayList<Object>(3);
|
||||||
|
if (exception instanceof FlutterError) {
|
||||||
|
FlutterError error = (FlutterError) exception;
|
||||||
|
errorList.add(error.code);
|
||||||
|
errorList.add(error.getMessage());
|
||||||
|
errorList.add(error.details);
|
||||||
|
} else {
|
||||||
|
errorList.add(exception.toString());
|
||||||
|
errorList.add(exception.getClass().getSimpleName());
|
||||||
|
errorList.add(
|
||||||
|
"Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception));
|
||||||
|
}
|
||||||
|
return errorList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generated class from Pigeon that represents data sent in messages. */
|
||||||
|
public static final class NotificationMessageContent {
|
||||||
|
/** The textual body of the message. */
|
||||||
|
private @Nullable String body;
|
||||||
|
|
||||||
|
public @Nullable String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBody(@Nullable String setterArg) {
|
||||||
|
this.body = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The path and mime type of the media to show. */
|
||||||
|
private @Nullable String mime;
|
||||||
|
|
||||||
|
public @Nullable String getMime() {
|
||||||
|
return mime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMime(@Nullable String setterArg) {
|
||||||
|
this.mime = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String path;
|
||||||
|
|
||||||
|
public @Nullable String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(@Nullable String setterArg) {
|
||||||
|
this.path = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private @Nullable String body;
|
||||||
|
|
||||||
|
public @NonNull Builder setBody(@Nullable String setterArg) {
|
||||||
|
this.body = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String mime;
|
||||||
|
|
||||||
|
public @NonNull Builder setMime(@Nullable String setterArg) {
|
||||||
|
this.mime = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String path;
|
||||||
|
|
||||||
|
public @NonNull Builder setPath(@Nullable String setterArg) {
|
||||||
|
this.path = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull NotificationMessageContent build() {
|
||||||
|
NotificationMessageContent pigeonReturn = new NotificationMessageContent();
|
||||||
|
pigeonReturn.setBody(body);
|
||||||
|
pigeonReturn.setMime(mime);
|
||||||
|
pigeonReturn.setPath(path);
|
||||||
|
return pigeonReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
ArrayList<Object> toList() {
|
||||||
|
ArrayList<Object> toListResult = new ArrayList<Object>(3);
|
||||||
|
toListResult.add(body);
|
||||||
|
toListResult.add(mime);
|
||||||
|
toListResult.add(path);
|
||||||
|
return toListResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NonNull NotificationMessageContent fromList(@NonNull ArrayList<Object> list) {
|
||||||
|
NotificationMessageContent pigeonResult = new NotificationMessageContent();
|
||||||
|
Object body = list.get(0);
|
||||||
|
pigeonResult.setBody((String) body);
|
||||||
|
Object mime = list.get(1);
|
||||||
|
pigeonResult.setMime((String) mime);
|
||||||
|
Object path = list.get(2);
|
||||||
|
pigeonResult.setPath((String) path);
|
||||||
|
return pigeonResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generated class from Pigeon that represents data sent in messages. */
|
||||||
|
public static final class NotificationMessage {
|
||||||
|
/** The sender of the message. */
|
||||||
|
private @NonNull String sender;
|
||||||
|
|
||||||
|
public @NonNull String getSender() {
|
||||||
|
return sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSender(@NonNull String setterArg) {
|
||||||
|
if (setterArg == null) {
|
||||||
|
throw new IllegalStateException("Nonnull field \"sender\" is null.");
|
||||||
|
}
|
||||||
|
this.sender = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The jid of the sender. */
|
||||||
|
private @NonNull String jid;
|
||||||
|
|
||||||
|
public @NonNull String getJid() {
|
||||||
|
return jid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJid(@NonNull String setterArg) {
|
||||||
|
if (setterArg == null) {
|
||||||
|
throw new IllegalStateException("Nonnull field \"jid\" is null.");
|
||||||
|
}
|
||||||
|
this.jid = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The body of the message. */
|
||||||
|
private @NonNull NotificationMessageContent content;
|
||||||
|
|
||||||
|
public @NonNull NotificationMessageContent getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(@NonNull NotificationMessageContent setterArg) {
|
||||||
|
if (setterArg == null) {
|
||||||
|
throw new IllegalStateException("Nonnull field \"content\" is null.");
|
||||||
|
}
|
||||||
|
this.content = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Milliseconds since epoch. */
|
||||||
|
private @NonNull Long timestamp;
|
||||||
|
|
||||||
|
public @NonNull Long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(@NonNull Long setterArg) {
|
||||||
|
if (setterArg == null) {
|
||||||
|
throw new IllegalStateException("Nonnull field \"timestamp\" is null.");
|
||||||
|
}
|
||||||
|
this.timestamp = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The path to the avatar to use */
|
||||||
|
private @Nullable String avatarPath;
|
||||||
|
|
||||||
|
public @Nullable String getAvatarPath() {
|
||||||
|
return avatarPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvatarPath(@Nullable String setterArg) {
|
||||||
|
this.avatarPath = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor is non-public to enforce null safety; use Builder. */
|
||||||
|
NotificationMessage() {}
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private @Nullable String sender;
|
||||||
|
|
||||||
|
public @NonNull Builder setSender(@NonNull String setterArg) {
|
||||||
|
this.sender = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String jid;
|
||||||
|
|
||||||
|
public @NonNull Builder setJid(@NonNull String setterArg) {
|
||||||
|
this.jid = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable NotificationMessageContent content;
|
||||||
|
|
||||||
|
public @NonNull Builder setContent(@NonNull NotificationMessageContent setterArg) {
|
||||||
|
this.content = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable Long timestamp;
|
||||||
|
|
||||||
|
public @NonNull Builder setTimestamp(@NonNull Long setterArg) {
|
||||||
|
this.timestamp = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String avatarPath;
|
||||||
|
|
||||||
|
public @NonNull Builder setAvatarPath(@Nullable String setterArg) {
|
||||||
|
this.avatarPath = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull NotificationMessage build() {
|
||||||
|
NotificationMessage pigeonReturn = new NotificationMessage();
|
||||||
|
pigeonReturn.setSender(sender);
|
||||||
|
pigeonReturn.setJid(jid);
|
||||||
|
pigeonReturn.setContent(content);
|
||||||
|
pigeonReturn.setTimestamp(timestamp);
|
||||||
|
pigeonReturn.setAvatarPath(avatarPath);
|
||||||
|
return pigeonReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
ArrayList<Object> toList() {
|
||||||
|
ArrayList<Object> toListResult = new ArrayList<Object>(5);
|
||||||
|
toListResult.add(sender);
|
||||||
|
toListResult.add(jid);
|
||||||
|
toListResult.add((content == null) ? null : content.toList());
|
||||||
|
toListResult.add(timestamp);
|
||||||
|
toListResult.add(avatarPath);
|
||||||
|
return toListResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NonNull NotificationMessage fromList(@NonNull ArrayList<Object> list) {
|
||||||
|
NotificationMessage pigeonResult = new NotificationMessage();
|
||||||
|
Object sender = list.get(0);
|
||||||
|
pigeonResult.setSender((String) sender);
|
||||||
|
Object jid = list.get(1);
|
||||||
|
pigeonResult.setJid((String) jid);
|
||||||
|
Object content = list.get(2);
|
||||||
|
pigeonResult.setContent((content == null) ? null : NotificationMessageContent.fromList((ArrayList<Object>) content));
|
||||||
|
Object timestamp = list.get(3);
|
||||||
|
pigeonResult.setTimestamp((timestamp == null) ? null : ((timestamp instanceof Integer) ? (Integer) timestamp : (Long) timestamp));
|
||||||
|
Object avatarPath = list.get(4);
|
||||||
|
pigeonResult.setAvatarPath((String) avatarPath);
|
||||||
|
return pigeonResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generated class from Pigeon that represents data sent in messages. */
|
||||||
|
public static final class MessagingNotification {
|
||||||
|
/** The title of the conversation. */
|
||||||
|
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 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 id of the notification channel the notification should appear 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Messages to show. */
|
||||||
|
private @NonNull List<NotificationMessage> messages;
|
||||||
|
|
||||||
|
public @NonNull List<NotificationMessage> getMessages() {
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessages(@NonNull List<NotificationMessage> setterArg) {
|
||||||
|
if (setterArg == null) {
|
||||||
|
throw new IllegalStateException("Nonnull field \"messages\" is null.");
|
||||||
|
}
|
||||||
|
this.messages = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor is non-public to enforce null safety; use Builder. */
|
||||||
|
MessagingNotification() {}
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private @Nullable String title;
|
||||||
|
|
||||||
|
public @NonNull Builder setTitle(@NonNull String setterArg) {
|
||||||
|
this.title = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable Long id;
|
||||||
|
|
||||||
|
public @NonNull Builder setId(@NonNull Long setterArg) {
|
||||||
|
this.id = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String channelId;
|
||||||
|
|
||||||
|
public @NonNull Builder setChannelId(@NonNull String setterArg) {
|
||||||
|
this.channelId = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable List<NotificationMessage> messages;
|
||||||
|
|
||||||
|
public @NonNull Builder setMessages(@NonNull List<NotificationMessage> setterArg) {
|
||||||
|
this.messages = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull MessagingNotification build() {
|
||||||
|
MessagingNotification pigeonReturn = new MessagingNotification();
|
||||||
|
pigeonReturn.setTitle(title);
|
||||||
|
pigeonReturn.setId(id);
|
||||||
|
pigeonReturn.setChannelId(channelId);
|
||||||
|
pigeonReturn.setMessages(messages);
|
||||||
|
return pigeonReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
ArrayList<Object> toList() {
|
||||||
|
ArrayList<Object> toListResult = new ArrayList<Object>(4);
|
||||||
|
toListResult.add(title);
|
||||||
|
toListResult.add(id);
|
||||||
|
toListResult.add(channelId);
|
||||||
|
toListResult.add(messages);
|
||||||
|
return toListResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NonNull MessagingNotification fromList(@NonNull ArrayList<Object> list) {
|
||||||
|
MessagingNotification pigeonResult = new MessagingNotification();
|
||||||
|
Object title = list.get(0);
|
||||||
|
pigeonResult.setTitle((String) title);
|
||||||
|
Object id = list.get(1);
|
||||||
|
pigeonResult.setId((id == null) ? null : ((id instanceof Integer) ? (Integer) id : (Long) id));
|
||||||
|
Object channelId = list.get(2);
|
||||||
|
pigeonResult.setChannelId((String) channelId);
|
||||||
|
Object messages = list.get(3);
|
||||||
|
pigeonResult.setMessages((List<NotificationMessage>) messages);
|
||||||
|
return pigeonResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NotificationsImplementationApiCodec extends StandardMessageCodec {
|
||||||
|
public static final NotificationsImplementationApiCodec INSTANCE = new NotificationsImplementationApiCodec();
|
||||||
|
|
||||||
|
private NotificationsImplementationApiCodec() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
|
||||||
|
switch (type) {
|
||||||
|
case (byte) 128:
|
||||||
|
return MessagingNotification.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
|
case (byte) 129:
|
||||||
|
return NotificationMessage.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
|
case (byte) 130:
|
||||||
|
return NotificationMessageContent.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
|
default:
|
||||||
|
return super.readValueOfType(type, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
|
||||||
|
if (value instanceof MessagingNotification) {
|
||||||
|
stream.write(128);
|
||||||
|
writeValue(stream, ((MessagingNotification) value).toList());
|
||||||
|
} else if (value instanceof NotificationMessage) {
|
||||||
|
stream.write(129);
|
||||||
|
writeValue(stream, ((NotificationMessage) value).toList());
|
||||||
|
} else if (value instanceof NotificationMessageContent) {
|
||||||
|
stream.write(130);
|
||||||
|
writeValue(stream, ((NotificationMessageContent) value).toList());
|
||||||
|
} else {
|
||||||
|
super.writeValue(stream, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||||
|
public interface NotificationsImplementationApi {
|
||||||
|
|
||||||
|
void createNotificationChannel(@NonNull String title, @NonNull String id, @NonNull Boolean urgent);
|
||||||
|
|
||||||
|
void showMessagingNotification(@NonNull MessagingNotification notification);
|
||||||
|
|
||||||
|
/** The codec used by NotificationsImplementationApi. */
|
||||||
|
static @NonNull MessageCodec<Object> getCodec() {
|
||||||
|
return NotificationsImplementationApiCodec.INSTANCE;
|
||||||
|
}
|
||||||
|
/**Sets up an instance of `NotificationsImplementationApi` to handle messages through the `binaryMessenger`. */
|
||||||
|
static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable NotificationsImplementationApi api) {
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.NotificationsImplementationApi.createNotificationChannel", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
String titleArg = (String) args.get(0);
|
||||||
|
String idArg = (String) args.get(1);
|
||||||
|
Boolean urgentArg = (Boolean) args.get(2);
|
||||||
|
try {
|
||||||
|
api.createNotificationChannel(titleArg, idArg, urgentArg);
|
||||||
|
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.NotificationsImplementationApi.showMessagingNotification", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
MessagingNotification notificationArg = (MessagingNotification) args.get(0);
|
||||||
|
try {
|
||||||
|
api.showMessagingNotification(notificationArg);
|
||||||
|
wrapped.add(0, null);
|
||||||
|
}
|
||||||
|
catch (Throwable exception) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(exception);
|
||||||
|
wrapped = wrappedError;
|
||||||
|
}
|
||||||
|
reply.reply(wrapped);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- For testing -->
|
||||||
|
<cache-path name="file_picker" path="file_picker/"/>
|
||||||
|
</paths>
|
@ -0,0 +1,17 @@
|
|||||||
|
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
||||||
|
|
||||||
|
class AndroidNotificationsImplementation extends NotificationsImplementation {
|
||||||
|
final NotificationsImplementationApi _api = NotificationsImplementationApi();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> createNotificationChannel(String title, String id, bool urgent) async {
|
||||||
|
return _api.createNotificationChannel(title, id, urgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> showMessagingNotification(MessagingNotification notification) async {
|
||||||
|
return _api.showMessagingNotification(notification);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ import 'package:moxplatform_android/src/contacts_android.dart';
|
|||||||
import 'package:moxplatform_android/src/crypto_android.dart';
|
import 'package:moxplatform_android/src/crypto_android.dart';
|
||||||
import 'package:moxplatform_android/src/isolate_android.dart';
|
import 'package:moxplatform_android/src/isolate_android.dart';
|
||||||
import 'package:moxplatform_android/src/media_android.dart';
|
import 'package:moxplatform_android/src/media_android.dart';
|
||||||
|
import 'package:moxplatform_android/src/notifications_android.dart';
|
||||||
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
||||||
|
|
||||||
class MoxplatformAndroidPlugin extends MoxplatformInterface {
|
class MoxplatformAndroidPlugin extends MoxplatformInterface {
|
||||||
@ -12,6 +13,7 @@ class MoxplatformAndroidPlugin extends MoxplatformInterface {
|
|||||||
MoxplatformInterface.crypto = AndroidCryptographyImplementation();
|
MoxplatformInterface.crypto = AndroidCryptographyImplementation();
|
||||||
MoxplatformInterface.handler = AndroidIsolateHandler();
|
MoxplatformInterface.handler = AndroidIsolateHandler();
|
||||||
MoxplatformInterface.media = AndroidMediaScannerImplementation();
|
MoxplatformInterface.media = AndroidMediaScannerImplementation();
|
||||||
|
MoxplatformInterface.notifications = AndroidNotificationsImplementation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -41,4 +41,5 @@ dependencies:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
pigeon: 10.1.4
|
||||||
very_good_analysis: ^3.0.1
|
very_good_analysis: ^3.0.1
|
||||||
|
@ -9,4 +9,7 @@ export 'src/isolate.dart';
|
|||||||
export 'src/isolate_stub.dart';
|
export 'src/isolate_stub.dart';
|
||||||
export 'src/media.dart';
|
export 'src/media.dart';
|
||||||
export 'src/media_stub.dart';
|
export 'src/media_stub.dart';
|
||||||
|
export 'src/notifications.dart';
|
||||||
|
export 'src/notifications.g.dart';
|
||||||
|
export 'src/notifications_stub.dart';
|
||||||
export 'src/service.dart';
|
export 'src/service.dart';
|
||||||
|
@ -6,6 +6,8 @@ import 'package:moxplatform_platform_interface/src/isolate.dart';
|
|||||||
import 'package:moxplatform_platform_interface/src/isolate_stub.dart';
|
import 'package:moxplatform_platform_interface/src/isolate_stub.dart';
|
||||||
import 'package:moxplatform_platform_interface/src/media.dart';
|
import 'package:moxplatform_platform_interface/src/media.dart';
|
||||||
import 'package:moxplatform_platform_interface/src/media_stub.dart';
|
import 'package:moxplatform_platform_interface/src/media_stub.dart';
|
||||||
|
import 'package:moxplatform_platform_interface/src/notifications.dart';
|
||||||
|
import 'package:moxplatform_platform_interface/src/notifications_stub.dart';
|
||||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||||
|
|
||||||
abstract class MoxplatformInterface extends PlatformInterface {
|
abstract class MoxplatformInterface extends PlatformInterface {
|
||||||
@ -17,6 +19,7 @@ abstract class MoxplatformInterface extends PlatformInterface {
|
|||||||
static MediaScannerImplementation media = StubMediaScannerImplementation();
|
static MediaScannerImplementation media = StubMediaScannerImplementation();
|
||||||
static CryptographyImplementation crypto = StubCryptographyImplementation();
|
static CryptographyImplementation crypto = StubCryptographyImplementation();
|
||||||
static ContactsImplementation contacts = StubContactsImplementation();
|
static ContactsImplementation contacts = StubContactsImplementation();
|
||||||
|
static NotificationsImplementation notifications = StubNotificationsImplementation();
|
||||||
|
|
||||||
/// Return the current platform name.
|
/// Return the current platform name.
|
||||||
Future<String?> getPlatformName();
|
Future<String?> getPlatformName();
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:moxplatform_platform_interface/src/notifications.g.dart';
|
||||||
|
|
||||||
|
abstract class NotificationsImplementation {
|
||||||
|
Future<void> createNotificationChannel(String title, String id, bool urgent);
|
||||||
|
|
||||||
|
Future<void> showMessagingNotification(MessagingNotification notification);
|
||||||
|
}
|
@ -0,0 +1,216 @@
|
|||||||
|
// Autogenerated from Pigeon (v10.1.4), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class NotificationMessageContent {
|
||||||
|
NotificationMessageContent({
|
||||||
|
this.body,
|
||||||
|
this.mime,
|
||||||
|
this.path,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The textual body of the message.
|
||||||
|
String? body;
|
||||||
|
|
||||||
|
/// The path and mime type of the media to show.
|
||||||
|
String? mime;
|
||||||
|
|
||||||
|
String? path;
|
||||||
|
|
||||||
|
Object encode() {
|
||||||
|
return <Object?>[
|
||||||
|
body,
|
||||||
|
mime,
|
||||||
|
path,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static NotificationMessageContent decode(Object result) {
|
||||||
|
result as List<Object?>;
|
||||||
|
return NotificationMessageContent(
|
||||||
|
body: result[0] as String?,
|
||||||
|
mime: result[1] as String?,
|
||||||
|
path: result[2] as String?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotificationMessage {
|
||||||
|
NotificationMessage({
|
||||||
|
required this.sender,
|
||||||
|
required this.jid,
|
||||||
|
required this.content,
|
||||||
|
required this.timestamp,
|
||||||
|
this.avatarPath,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The sender of the message.
|
||||||
|
String sender;
|
||||||
|
|
||||||
|
/// The jid of the sender.
|
||||||
|
String jid;
|
||||||
|
|
||||||
|
/// The body of the message.
|
||||||
|
NotificationMessageContent content;
|
||||||
|
|
||||||
|
/// Milliseconds since epoch.
|
||||||
|
int timestamp;
|
||||||
|
|
||||||
|
/// The path to the avatar to use
|
||||||
|
String? avatarPath;
|
||||||
|
|
||||||
|
Object encode() {
|
||||||
|
return <Object?>[
|
||||||
|
sender,
|
||||||
|
jid,
|
||||||
|
content.encode(),
|
||||||
|
timestamp,
|
||||||
|
avatarPath,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static NotificationMessage decode(Object result) {
|
||||||
|
result as List<Object?>;
|
||||||
|
return NotificationMessage(
|
||||||
|
sender: result[0]! as String,
|
||||||
|
jid: result[1]! as String,
|
||||||
|
content: NotificationMessageContent.decode(result[2]! as List<Object?>),
|
||||||
|
timestamp: result[3]! as int,
|
||||||
|
avatarPath: result[4] as String?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessagingNotification {
|
||||||
|
MessagingNotification({
|
||||||
|
required this.title,
|
||||||
|
required this.id,
|
||||||
|
required this.channelId,
|
||||||
|
required this.messages,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The title of the conversation.
|
||||||
|
String title;
|
||||||
|
|
||||||
|
/// The id of the notification.
|
||||||
|
int id;
|
||||||
|
|
||||||
|
/// The id of the notification channel the notification should appear on.
|
||||||
|
String channelId;
|
||||||
|
|
||||||
|
/// Messages to show.
|
||||||
|
List<NotificationMessage?> messages;
|
||||||
|
|
||||||
|
Object encode() {
|
||||||
|
return <Object?>[
|
||||||
|
title,
|
||||||
|
id,
|
||||||
|
channelId,
|
||||||
|
messages,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static MessagingNotification decode(Object result) {
|
||||||
|
result as List<Object?>;
|
||||||
|
return MessagingNotification(
|
||||||
|
title: result[0]! as String,
|
||||||
|
id: result[1]! as int,
|
||||||
|
channelId: result[2]! as String,
|
||||||
|
messages: (result[3] as List<Object?>?)!.cast<NotificationMessage?>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NotificationsImplementationApiCodec extends StandardMessageCodec {
|
||||||
|
const _NotificationsImplementationApiCodec();
|
||||||
|
@override
|
||||||
|
void writeValue(WriteBuffer buffer, Object? value) {
|
||||||
|
if (value is MessagingNotification) {
|
||||||
|
buffer.putUint8(128);
|
||||||
|
writeValue(buffer, value.encode());
|
||||||
|
} else if (value is NotificationMessage) {
|
||||||
|
buffer.putUint8(129);
|
||||||
|
writeValue(buffer, value.encode());
|
||||||
|
} else if (value is NotificationMessageContent) {
|
||||||
|
buffer.putUint8(130);
|
||||||
|
writeValue(buffer, value.encode());
|
||||||
|
} else {
|
||||||
|
super.writeValue(buffer, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||||
|
switch (type) {
|
||||||
|
case 128:
|
||||||
|
return MessagingNotification.decode(readValue(buffer)!);
|
||||||
|
case 129:
|
||||||
|
return NotificationMessage.decode(readValue(buffer)!);
|
||||||
|
case 130:
|
||||||
|
return NotificationMessageContent.decode(readValue(buffer)!);
|
||||||
|
default:
|
||||||
|
return super.readValueOfType(type, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotificationsImplementationApi {
|
||||||
|
/// Constructor for [NotificationsImplementationApi]. The [binaryMessenger] named argument is
|
||||||
|
/// available for dependency injection. If it is left null, the default
|
||||||
|
/// BinaryMessenger will be used which routes to the host platform.
|
||||||
|
NotificationsImplementationApi({BinaryMessenger? binaryMessenger})
|
||||||
|
: _binaryMessenger = binaryMessenger;
|
||||||
|
final BinaryMessenger? _binaryMessenger;
|
||||||
|
|
||||||
|
static const MessageCodec<Object?> codec = _NotificationsImplementationApiCodec();
|
||||||
|
|
||||||
|
Future<void> createNotificationChannel(String arg_title, String arg_id, bool arg_urgent) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.NotificationsImplementationApi.createNotificationChannel', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_title, arg_id, arg_urgent]) 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> showMessagingNotification(MessagingNotification arg_notification) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.NotificationsImplementationApi.showMessagingNotification', 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
import 'package:moxplatform_platform_interface/src/notifications.g.dart';
|
||||||
|
import 'package:moxplatform_platform_interface/src/notifications.dart';
|
||||||
|
|
||||||
|
class StubNotificationsImplementation extends NotificationsImplementation {
|
||||||
|
@override
|
||||||
|
Future<void> createNotificationChannel(String title, String id, bool urgent) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> showMessagingNotification(MessagingNotification notification) async {}
|
||||||
|
}
|
77
pigeons/notifications.dart
Normal file
77
pigeons/notifications.dart
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import 'package:pigeon/pigeon.dart';
|
||||||
|
|
||||||
|
@ConfigurePigeon(
|
||||||
|
PigeonOptions(
|
||||||
|
dartOut: 'packages/moxplatform_platform_interface/lib/src/notifications.g.dart',
|
||||||
|
//kotlinOut: 'packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/Notifications.g.kt',
|
||||||
|
//kotlinOptions: KotlinOptions(
|
||||||
|
// package: 'me.polynom.moxplatform_android',
|
||||||
|
//),
|
||||||
|
javaOut: 'packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/Notifications.java',
|
||||||
|
javaOptions: JavaOptions(
|
||||||
|
package: 'me.polynom.moxplatform_android',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
class NotificationMessageContent {
|
||||||
|
const NotificationMessageContent(
|
||||||
|
this.body,
|
||||||
|
this.mime,
|
||||||
|
this.path,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// The textual body of the message.
|
||||||
|
final String? body;
|
||||||
|
|
||||||
|
/// The path and mime type of the media to show.
|
||||||
|
final String? mime;
|
||||||
|
final String? path;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotificationMessage {
|
||||||
|
const NotificationMessage(
|
||||||
|
this.sender,
|
||||||
|
this.content,
|
||||||
|
this.jid,
|
||||||
|
this.timestamp,
|
||||||
|
this.avatarPath,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// The sender of the message.
|
||||||
|
final String sender;
|
||||||
|
|
||||||
|
/// The jid of the sender.
|
||||||
|
final String jid;
|
||||||
|
|
||||||
|
/// The body of the message.
|
||||||
|
final NotificationMessageContent content;
|
||||||
|
|
||||||
|
/// Milliseconds since epoch.
|
||||||
|
final int timestamp;
|
||||||
|
|
||||||
|
/// The path to the avatar to use
|
||||||
|
final String? avatarPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessagingNotification {
|
||||||
|
const MessagingNotification(this.title, this.id, this.messages, this.channelId);
|
||||||
|
|
||||||
|
/// The title of the conversation.
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
/// The id of the notification.
|
||||||
|
final int id;
|
||||||
|
|
||||||
|
/// The id of the notification channel the notification should appear on.
|
||||||
|
final String channelId;
|
||||||
|
|
||||||
|
/// Messages to show.
|
||||||
|
final List<NotificationMessage?> messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostApi()
|
||||||
|
abstract class NotificationsImplementationApi {
|
||||||
|
void createNotificationChannel(String title, String id, bool urgent);
|
||||||
|
|
||||||
|
void showMessagingNotification(MessagingNotification notification);
|
||||||
|
}
|
@ -4,3 +4,4 @@ environment:
|
|||||||
sdk: '>=2.18.0 <3.0.0'
|
sdk: '>=2.18.0 <3.0.0'
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
melos: ^3.1.1
|
melos: ^3.1.1
|
||||||
|
pigeon: 10.1.4
|
||||||
|
Reference in New Issue
Block a user