From 4330b775d6a574dc54b7244517e80d043b231f22 Mon Sep 17 00:00:00 2001 From: Serhii Stepanov Date: Tue, 6 Feb 2024 23:12:06 +0100 Subject: [PATCH] feat: add option to subscribe to overlay status: overlay showing or overlay closed --- .../CachedMessageChannels.java | 6 +-- .../FlutterOverlayWindowPlugin.java | 27 +++++------ .../OverlayService.java | 2 + .../OverlayStatusEmitter.java | 17 +++++++ lib/src/overlay_window.dart | 48 ++++++++++++++----- 5 files changed, 70 insertions(+), 30 deletions(-) create mode 100644 android/src/main/java/flutter/overlay/window/flutter_overlay_window/OverlayStatusEmitter.java diff --git a/android/src/main/java/flutter/overlay/window/flutter_overlay_window/CachedMessageChannels.java b/android/src/main/java/flutter/overlay/window/flutter_overlay_window/CachedMessageChannels.java index 5ae7e340..a26d4e43 100644 --- a/android/src/main/java/flutter/overlay/window/flutter_overlay_window/CachedMessageChannels.java +++ b/android/src/main/java/flutter/overlay/window/flutter_overlay_window/CachedMessageChannels.java @@ -1,11 +1,11 @@ package flutter.overlay.window.flutter_overlay_window; import androidx.annotation.Nullable; -import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.MethodChannel; public abstract class CachedMessageChannels { @Nullable - public static BasicMessageChannel mainAppMessageChannel; + public static MethodChannel mainAppMessageChannel; @Nullable - public static BasicMessageChannel overlayMessageChannel; + public static MethodChannel overlayMessageChannel; } diff --git a/android/src/main/java/flutter/overlay/window/flutter_overlay_window/FlutterOverlayWindowPlugin.java b/android/src/main/java/flutter/overlay/window/flutter_overlay_window/FlutterOverlayWindowPlugin.java index 8faec13b..919ac198 100644 --- a/android/src/main/java/flutter/overlay/window/flutter_overlay_window/FlutterOverlayWindowPlugin.java +++ b/android/src/main/java/flutter/overlay/window/flutter_overlay_window/FlutterOverlayWindowPlugin.java @@ -28,8 +28,7 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; -import io.flutter.plugin.common.BasicMessageChannel; -import io.flutter.plugin.common.JSONMessageCodec; +import io.flutter.plugin.common.JSONMethodCodec; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; @@ -187,37 +186,37 @@ private void registerMessageChannel(boolean isMainAppEngine) { private void unregisterMessageChannel(boolean isMainAppEngine) { if(isMainAppEngine) { if (CachedMessageChannels.mainAppMessageChannel == null) return; - CachedMessageChannels.mainAppMessageChannel.setMessageHandler(null); + CachedMessageChannels.mainAppMessageChannel.setMethodCallHandler(null); CachedMessageChannels.mainAppMessageChannel = null; } else { if(CachedMessageChannels.overlayMessageChannel == null) return; - CachedMessageChannels.overlayMessageChannel.setMessageHandler(null); + CachedMessageChannels.overlayMessageChannel.setMethodCallHandler(null); CachedMessageChannels.overlayMessageChannel = null; } } private void registerOverlayMessageChannel(io.flutter.plugin.common.BinaryMessenger overlyEngineBinaryMessenger) { - BasicMessageChannel overlayMessageChannel = new BasicMessageChannel<>(overlyEngineBinaryMessenger, OverlayConstants.MESSENGER_TAG, JSONMessageCodec.INSTANCE); - overlayMessageChannel.setMessageHandler((message, reply) -> { + MethodChannel overlayMessageChannel = new MethodChannel(overlyEngineBinaryMessenger, OverlayConstants.MESSENGER_TAG, JSONMethodCodec.INSTANCE); + overlayMessageChannel.setMethodCallHandler((call, result) -> { if (CachedMessageChannels.mainAppMessageChannel == null) { - reply.reply(false); + result.success(false); return; } - CachedMessageChannels.mainAppMessageChannel.send(message); - reply.reply(true); + CachedMessageChannels.mainAppMessageChannel.invokeMethod("message", call.arguments); + result.success(true); }); CachedMessageChannels.overlayMessageChannel = overlayMessageChannel; } private void registerMainAppMessageChannel(io.flutter.plugin.common.BinaryMessenger mainAppEngineBinaryMessenger) { - BasicMessageChannel mainAppMessageChannel = new BasicMessageChannel<>(mainAppEngineBinaryMessenger, OverlayConstants.MESSENGER_TAG, JSONMessageCodec.INSTANCE); - mainAppMessageChannel.setMessageHandler((message, reply) -> { + MethodChannel mainAppMessageChannel = new MethodChannel(mainAppEngineBinaryMessenger, OverlayConstants.MESSENGER_TAG, JSONMethodCodec.INSTANCE); + mainAppMessageChannel.setMethodCallHandler((call, result) -> { if (CachedMessageChannels.overlayMessageChannel == null) { - reply.reply(false); + result.success(false); return; } - CachedMessageChannels.overlayMessageChannel.send(message); - reply.reply(true); + CachedMessageChannels.overlayMessageChannel.invokeMethod("message", call.arguments); + result.success(true); }); CachedMessageChannels.mainAppMessageChannel = mainAppMessageChannel; } diff --git a/android/src/main/java/flutter/overlay/window/flutter_overlay_window/OverlayService.java b/android/src/main/java/flutter/overlay/window/flutter_overlay_window/OverlayService.java index 85086fea..c543ed5c 100644 --- a/android/src/main/java/flutter/overlay/window/flutter_overlay_window/OverlayService.java +++ b/android/src/main/java/flutter/overlay/window/flutter_overlay_window/OverlayService.java @@ -85,6 +85,7 @@ public void onDestroy() { flutterView.detachFromFlutterEngine(); flutterView = null; } + OverlayStatusEmitter.emitIsShowing(false); NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel(OverlayConstants.NOTIFICATION_ID); @@ -168,6 +169,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { flutterView.setOnTouchListener(this); windowManager.addView(flutterView, params); moveOverlay(dx, dy, null); + OverlayStatusEmitter.emitIsShowing(true); return START_STICKY; } diff --git a/android/src/main/java/flutter/overlay/window/flutter_overlay_window/OverlayStatusEmitter.java b/android/src/main/java/flutter/overlay/window/flutter_overlay_window/OverlayStatusEmitter.java new file mode 100644 index 00000000..9e60b2c8 --- /dev/null +++ b/android/src/main/java/flutter/overlay/window/flutter_overlay_window/OverlayStatusEmitter.java @@ -0,0 +1,17 @@ +package flutter.overlay.window.flutter_overlay_window; + +public abstract class OverlayStatusEmitter { + static private final String methodName = "isShowingOverlay"; + static private boolean lastEmittedStatus; + + static void emitIsShowing(boolean isShowing) { + if(isShowing == lastEmittedStatus) return; + lastEmittedStatus = isShowing; + if(CachedMessageChannels.mainAppMessageChannel != null) { + CachedMessageChannels.mainAppMessageChannel.invokeMethod(methodName, isShowing); + } + if(CachedMessageChannels.overlayMessageChannel != null) { + CachedMessageChannels.overlayMessageChannel.invokeMethod(methodName, isShowing); + } + } +} diff --git a/lib/src/overlay_window.dart b/lib/src/overlay_window.dart index d31d2a66..00e8ba70 100644 --- a/lib/src/overlay_window.dart +++ b/lib/src/overlay_window.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:developer'; -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_overlay_window/src/models/overlay_position.dart'; import 'package:flutter_overlay_window/src/overlay_config.dart'; @@ -9,13 +8,13 @@ import 'package:flutter_overlay_window/src/overlay_config.dart'; class FlutterOverlayWindow { FlutterOverlayWindow._(); - static final StreamController _controller = StreamController.broadcast(); - static const MethodChannel _channel = - MethodChannel("x-slayer/overlay_channel"); - static const MethodChannel _overlayChannel = - MethodChannel("x-slayer/overlay"); - static const BasicMessageChannel _overlayMessageChannel = - BasicMessageChannel("x-slayer/overlay_messenger", JSONMessageCodec()); + static final _controller = StreamController.broadcast(); + static final _controllerOverlayStatus = StreamController.broadcast(); + + static const _channel = MethodChannel("x-slayer/overlay_channel"); + static const _overlayChannel = MethodChannel("x-slayer/overlay"); + static const _overlayMessageChannel = + MethodChannel("x-slayer/overlay_messenger", JSONMethodCodec()); /// Open overLay content /// @@ -107,19 +106,29 @@ class FlutterOverlayWindow { /// /// Returns `true` if the [data] was sent successfully, otherwise `false`. static Future shareData(dynamic data) async { - final isSent = await _overlayMessageChannel.send(data); + final isSent = await _overlayMessageChannel.invokeMethod('', data); return isSent as bool; } /// Streams message shared between overlay and main app static Stream get overlayListener { - _overlayMessageChannel.setMessageHandler((message) async { - _controller.add(message); - return message; - }); + _registerOverlayMessageHandler(); return _controller.stream; } + /// Overlay status stream. + /// + /// Emit `true` when overlay is showing, and `false` when overlay is closed. + /// + /// Emit value only once for every state change. + /// + /// Doesn't emit a change when the overlay is already showing and [showOverlay] is called, + /// as in this case the overlay will almost immediately reopen. + static Stream get overlayStatusListener { + _registerOverlayMessageHandler(); + return _controllerOverlayStatus.stream; + } + /// Update the overlay flag while the overlay in action static Future updateFlag(OverlayFlag flag) async { final bool? _res = await _overlayChannel @@ -173,6 +182,19 @@ class FlutterOverlayWindow { return _res ?? false; } + static void _registerOverlayMessageHandler() { + _overlayMessageChannel.setMethodCallHandler((call) async { + switch (call.method) { + case 'isShowingOverlay': + _controllerOverlayStatus.add(call.arguments as bool); + break; + case 'message': + _controller.add(call.arguments); + break; + } + }); + } + /// Dispose overlay stream. /// /// Once disposed, only a complete restart of the application will re-initialize the listener.