Introduction
I am running flutter v1.17.5 DeviceApps v1.0.10 and SystemAlertWindow v0.2.2+3 (intentially not the latest version). And I want to open my app from a system alert window that is running in the foreground, even when the app is closed.
I am using the SystemOverlayWindow plugin and the plugin is a activity SystemAlertWindowPlugin.java
In my Application.kt I register the plugins and pass the registry
public class Application: FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate();
FlutterFirebaseMessagingService.setPluginRegistrant(this);
SystemAlertWindowPlugin.setPluginRegistrant(this);
createNotificationChannels();
FlutterMain.startInitialization(this);
}
override fun registerWith(registry: PluginRegistry?) {
if (!registry!!.hasPlugin("io.flutter.plugins.firebasemessaging")) {
FirebaseMessagingPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
}
if (!registry!!.hasPlugin("in.jvapps.system_alert_window")) {
SystemAlertWindowPlugin.registerWith(registry!!.registrarFor("in.jvapps.system_alert_window"));
}
if (!registry!!.hasPlugin("fr.g123k.deviceapps")) {
DeviceAppsPlugin.registerWith(registry!!.registrarFor("fr.g123k.deviceapps"));
}
}
I also register another plugin called DeviceApps. This is the DeviceAppsPlugin DeviceAppsPlugin.java
Problem
In short
So the system overlay (which runs in the foreground) calls > the dart callback > invokes a method of the DeviceApps plugin > error occcurs
Long version
I have a static callback that is registered here and it gets called when I have a click interaction with the system alert window. But now I wan't to call the DeviceApps plugin in my dart code from the that static callback
So the method channel will invoke this and that will run the static callback defined in dart.
This is the static dart callback that is registered and called with a background channel
static Future<void> systemOverlayOnClickListner(String tag) async {
switch (tag) {
case 'button_app_to_foreground':
DeviceApps.openApp('com.companyname.appname'); // this is where I try to run the plugin
await SystemAlertWindow.closeSystemWindow();
break;
}
The callback will invoke a method of the DeviceApps plugin. And this causes problems because this method will try to get the package manager from the activity passed in it's constructor. But according to this error the activity is null.
E/MethodChannel#g123k/device_apps(24210): Failed to handle method call
E/MethodChannel#g123k/device_apps(24210): java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.PackageManager android.app.Activity.getPackageManager()' on a null object reference
E/MethodChannel#g123k/device_apps(24210): at fr.g123k.deviceapps.DeviceAppsPlugin.openApp(DeviceAppsPlugin.java:141)
So it will invoke the getPackageManager() on a null object.
The activity is only null when it is being called from this static callback that is called by the background channel. But not when I call it normally from the app scope. Why is this the case?
Conslusion
So in conclusion calling the plugin works fine when I call it from my app scope. But once the callback is called trough means of the background channel all of the sudden the activity is null.
I cannot just start a isolate in my app and send a message to that from my callback like how its done here. Because I need this code to work when the app is closed, and a app scoped isolate doesn't run in the background.
So how can I open my app from the callback?
This is the full stack trace
E/flutter (26735): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(error, Attempt to invoke virtual method 'android.content.pm.PackageManager android.app.Activity.getPackageManager()' on a null object reference, null)
E/flutter (26735): #0 StandardMethodCodec.decodeEnvelope
package:flutter/…/services/message_codecs.dart:569
E/flutter (26735): #1 MethodChannel._invokeMethod
package:flutter/…/services/platform_channel.dart:156
E/flutter (26735): <asynchronous suspension>
E/flutter (26735): #2 MethodChannel.invokeMethod
package:flutter/…/services/platform_channel.dart:329
E/flutter (26735): #3 DeviceApps.openApp
package:device_apps/device_apps.dart:81
E/flutter (26735): #4 SystemOverlayController.systemOverlayOnClickListner
package:appname/…/singletons/system_overlay_controller.dart:51
E/flutter (26735): #5 callbackDispatcher.<anonymous closure>
package:system_alert_window/system_alert_window.dart:136
E/flutter (26735): #6 MethodChannel._handleAsMethodCall
package:flutter/…/services/platform_channel.dart:409
E/flutter (26735): #7 MethodChannel.setMethodCallHandler.<anonymous closure>
package:flutter/…/services/platform_channel.dart:377
E/flutter (26735): #8 _DefaultBinaryMessenger.handlePlatformMessage
package:flutter/…/services/binding.dart:199
E/flutter (26735): #9 _invoke3.<anonymous closure> (dart:ui/hooks.dart:290:15)
E/flutter (26735): #10 _rootRun (dart:async/zone.dart:1184:13)
E/flutter (26735): #11 _CustomZone.run (dart:async/zone.dart:1077:19)
E/flutter (26735): #12 _CustomZone.runGuarded (dart:async/zone.dart:979:7)
E/flutter (26735): #13 _invoke3 (dart:ui/hooks.dart:289:10)
E/flutter (26735): #14 _dispatchPlatformMessage (dart:ui/hooks.dart:164:5
Example Repo
I even tried to add the open app method directly to a forked version of the system alert window. And implemented it into a example repo that you can find here, take the branch called my-branch.
https://github.com/michael-ottink/system_overlay_callback_null_activity
But it throws the exact same error. Even if I use exactly the same activity. So it has something to do with the background channel I think.
Run the app and then click the button to get the overlay. Bring the app to the background and click on open in the overlay. The error occurs.
Extra info
I think this is a similar issue only here they choose to not register the plugin because it is foreground only. Where in my case I want to fork either of these plugins and modify the them so that it also works in the background. How do I do that?