2

I'm using Firebase and flutter local notifications to display notifications I get from my server. I wanted to display certain notifications inside the conversation part of the android notifications, so I made a native kotlin call using the method channel to create a shortcut for this:

Dart

  static Future<String?> _createConversationShortcut(
      String personName, String personIconPath) async {
    const platform = MethodChannel('...');
    String result;
    try {
      result = await platform.invokeMethod('createConversationShortcut', {
        'personName': personName,
        'personIcon': personIconPath,
      });
    } on PlatformException catch (e) {
      return null;
    }
    return result;
  }

I call the method _createConversationShortcut to create a shortcut natively and then use the shortcut ID in the AndroidNotificationDetails so that the notification is in the Conversation part

      flutterLocalNotificationsPlugin.show(
        int.parse(person.key!),
        notification,
        text,
        NotificationDetails(
          android: AndroidNotificationDetails(
            channel.id,
            channel.name,
            channel.description,
            icon: '@drawable/is_notification',
            category: 'msg',
            shortcutId: shortcutId,
            styleInformation: MessagingStyleInformation(
              person,
              groupConversation: false,
              conversationTitle: 'Neue Privatnachricht',
              messages: messages,
            ),
            //setAsGroupSummary: true,
          ),
        ),
        payload: json.encode(payload),
      );

Kotlin

class MainActivity: FlutterActivity() {

    private val CHANNEL = "..."

    private fun createConversationShortcut(personName: String, personIcon: String): String {
        val person = Person.Builder()
                .setName(personName)
                .setIcon(IconCompat.createWithContentUri(personIcon))
                .build()
        val shortcut = ShortcutInfoCompat.Builder(context, personName.toLowerCase(Locale.ROOT).replace(" ", "_") + "_shortcut")
                .setLongLived()
                .setIntent(Intent(Intent.ACTION_VIEW))
                .setPerson(person)
                .setShortLabel(personName)
                .build()
        ShortcutManagerCompat.addDynamicShortcuts(context, listOf(shortcut))
        return shortcut.id
    }

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        GeneratedPluginRegistrant.registerWith(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "createConversationShortcut") {
                val personName = call.argument<String>("personName")
                val personIcon = call.argument<String>("personIcon")
                if (personName != null && personIcon != null) {
                    val shortcutId = createConversationShortcut(personName, personIcon)
                    result.success(shortcutId)
                } else {
                    result.error("argument-error", "Too few arguments", null)
                }
            } else {
                result.notImplemented()
            }
        }
    }
}

This works perfectly when the app is in the foreground but when my app is in the backgroudn or closed I get an ImplementationError because MainActivity doesn't exist.

My question now is, how can I invoke the method channel, when the app is in the background or closed? I read that I need to create a service for that but I have no clue how I can set the method call handler from inside the service. Can anyone please help my with this?

EDIT:

I tried to start a service after the app is opened for the first time by making a Method call in the dart main file

override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
        if (call.method == "startHeadlessService"){
            if (!context.isMyServiceRunning(FlutterService::class.java)) {
                Log.d("pushNotificationService", "Service starting")
                val intent = Intent(context, FlutterService::class.java)
                context.startService(intent)
            } else {
                Log.d("pushNotificationService", "Service already Running")
            }
            result.success(null)
        } else {
            result.notImplemented()
        }
    }
}

and in the onStartCommand function of my service I tried to setup a methodchannel like this:

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    Log.d("TAG", "Hello foreground service")
    FlutterMain.startInitialization(this);
    FlutterMain.ensureInitializationComplete(this, null);
    val flutterEngine = FlutterEngine(this)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
        if (call.method == "createConversationShortcut") {
            val personName = call.argument<String>("personName")
            val personIcon = call.argument<String>("personIcon")
            if (personName != null && personIcon != null) {
                val shortcutId = createConversationShortcut(personName, personIcon)
                result.success(shortcutId)
            } else {
                result.error("argument-error", "Too few arguments", null)
            }
        }
    }
    return super.onStartCommand(intent, flags, startId)
}

But I still get the error:

Unhandled Exception: MissingPluginException(No implementation found for method saveBitmapAsCircle on channel ...)
arzi89
  • 31
  • 5
  • [flutter-run-an-app-as-a-background-service](https://stackoverflow.com/questions/51539893/flutter-run-an-app-as-a-background-service) help you with your background services. – Parth Prajapati Sep 30 '21 at 12:11
  • 1
    I already looked at all of this, but it's not really helping me with my problem as I'm not skilled with android app development. I just need a way to make a native method call to create a shortcut when I receive a notification when the app is not in the foreground – arzi89 Oct 01 '21 at 08:59

3 Answers3

1

I couldn't figure out how to make the method channel available without an active activity, but I found out that if you create a plugin the method channel is always accessible, even if the app is closed, so I made a plugin that can be used to create conversation shortcuts as an addition to the flutter_local_notifications plugin.

If anyone is interested: android_conversation_shortcut

arzi89
  • 31
  • 5
1

Use this package if you want to run smoothly: https://pub.dev/packages/background_fetch

Umair Ahmad
  • 31
  • 1
  • 5
0

Create your own mini-plugin. I couldn't do much of anything within my project, but as soon as I created the plugin I was able to execute the native functions in the background via the functions of the plugin. Assuming you already have the native code it shouldn't take very long, here are the instructions: https://docs.flutter.dev/packages-and-plugins/developing-packages