From 66f8abe95909e0ca129a7796b040c659ada879ff Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Sat, 28 Mar 2026 19:44:51 +0530 Subject: [PATCH 1/6] feat: stream smallplug analytics events via EventChannel --- .../ScgatewayFlutterPlugin.kt | 39 +++++++++++++- .../Classes/SwiftScgatewayFlutterPlugin.swift | 53 ++++++++++++++++--- scgateway/lib/scgateway_flutter_plugin.dart | 34 ++++++++++++ 3 files changed, 117 insertions(+), 9 deletions(-) diff --git a/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayFlutterPlugin.kt b/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayFlutterPlugin.kt index 16fd480..3f8121c 100644 --- a/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayFlutterPlugin.kt +++ b/scgateway/android/src/main/kotlin/com/example/scgateway_flutter_plugin/ScgatewayFlutterPlugin.kt @@ -14,9 +14,13 @@ import com.smallcase.gateway.data.models.* import com.smallcase.gateway.data.requests.InitRequest import com.smallcase.gateway.portal.SmallcaseGatewaySdk import com.smallcase.gateway.portal.SmallplugPartnerProps +import com.smallcase.gateway.data.listeners.Notification +import com.smallcase.gateway.data.listeners.NotificationCenter +import com.smallcase.gateway.portal.ScgNotification 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.EventChannel import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler @@ -46,13 +50,43 @@ class ScgatewayFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { /// This local reference serves to register the plugin with the Flutter Engine and unregister it /// when the Flutter Engine is detached from the Activity private lateinit var channel: MethodChannel - + private lateinit var eventChannel: EventChannel + private var eventSink: EventChannel.EventSink? = null + private var notificationObserver: ((Notification) -> Unit)? = null override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { this.context = flutterPluginBinding.applicationContext channel = MethodChannel(flutterPluginBinding.binaryMessenger, "scgateway_flutter_plugin") channel.setMethodCallHandler(this) + + eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "scgateway_flutter_plugin/smallplug_events") + eventChannel.setStreamHandler(object : EventChannel.StreamHandler { + override fun onListen(arguments: Any?, events: EventChannel.EventSink?) { + eventSink = events + notificationObserver = { notification -> + processSmallplugNotification(notification) + } + notificationObserver?.let { NotificationCenter.addObserver(it) } + } + override fun onCancel(arguments: Any?) { + notificationObserver?.let { NotificationCenter.removeObserver(it) } + notificationObserver = null + eventSink = null + } + }) + } + + private fun processSmallplugNotification(notification: Notification) { + val jsonString = notification.userInfo?.get(ScgNotification.STRINGIFIED_PAYLOAD_KEY) as? String ?: return + try { + val parsed = org.json.JSONObject(jsonString) + if (parsed.optString("type") != "smallplug_analytics_event") return + val eventData = parsed.optJSONObject("data") ?: return + uiThreadHandler.post { + eventSink?.success(eventData.toString()) + } + } catch (_: Exception) {} } override fun onMethodCall(@NonNull call: MethodCall, @NonNull rawResult: Result) { @@ -495,6 +529,9 @@ class ScgatewayFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { channel.setMethodCallHandler(null) + notificationObserver?.let { NotificationCenter.removeObserver(it) } + notificationObserver = null + eventSink = null } override fun onAttachedToActivity(binding: ActivityPluginBinding) { diff --git a/scgateway/ios/Classes/SwiftScgatewayFlutterPlugin.swift b/scgateway/ios/Classes/SwiftScgatewayFlutterPlugin.swift index 91e5c81..0299f23 100644 --- a/scgateway/ios/Classes/SwiftScgatewayFlutterPlugin.swift +++ b/scgateway/ios/Classes/SwiftScgatewayFlutterPlugin.swift @@ -11,20 +11,57 @@ enum IntentType: String { case authoriseHoldings = "AUTHORISE_HOLDINGS" } -public class SwiftScgatewayFlutterPlugin: NSObject, FlutterPlugin { - +public class SwiftScgatewayFlutterPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { + @MainActor let currentViewController: UIViewController = (UIApplication.shared.delegate?.window??.rootViewController)! - + + private var eventSink: FlutterEventSink? + private var notificationObserver: NSObjectProtocol? + public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "scgateway_flutter_plugin", binaryMessenger: registrar.messenger()) let instance = SwiftScgatewayFlutterPlugin() - - // let scLoansChannel = FlutterMethodChannel(name: "scloans", binaryMessenger: registrar.messenger()) - // let scLoansInstance = ScLoanFlutterPlugin() - + + let eventChannel = FlutterEventChannel(name: "scgateway_flutter_plugin/smallplug_events", binaryMessenger: registrar.messenger()) + eventChannel.setStreamHandler(instance) + registrar.addMethodCallDelegate(instance, channel: channel) - // registrar.addMethodCallDelegate(scLoansInstance, channel: scLoansChannel) + } + + // MARK: - FlutterStreamHandler (SmallPlug event streaming) + + public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { + self.eventSink = events + notificationObserver = NotificationCenter.default.addObserver( + forName: SCGateway.scgNotificationName, + object: nil, + queue: .main + ) { [weak self] notification in + self?.handleScgNotification(notification) + } + return nil + } + + public func onCancel(withArguments arguments: Any?) -> FlutterError? { + if let observer = notificationObserver { + NotificationCenter.default.removeObserver(observer) + notificationObserver = nil + } + eventSink = nil + return nil + } + + private func handleScgNotification(_ notification: Notification) { + guard let jsonString = notification.userInfo?[SCGNotification.strigifiedPayloadKey] as? String, + let data = jsonString.data(using: .utf8), + let parsed = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + parsed["type"] as? String == NotificationTypes.smallplugAnalyticsEvent, + let eventData = parsed["data"] as? [String: Any] else { return } + guard let sink = eventSink, + let eventJson = try? JSONSerialization.data(withJSONObject: eventData), + let eventString = String(data: eventJson, encoding: .utf8) else { return } + sink(eventString) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { diff --git a/scgateway/lib/scgateway_flutter_plugin.dart b/scgateway/lib/scgateway_flutter_plugin.dart index df4efab..6479d0f 100644 --- a/scgateway/lib/scgateway_flutter_plugin.dart +++ b/scgateway/lib/scgateway_flutter_plugin.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:flutter/services.dart'; import './color_ext.dart'; @@ -46,10 +47,43 @@ class SmallplugUiConfig { }; } +class SmallplugAnalyticsEvent { + final String eventName; + final Map data; + final double timestamp; + + SmallplugAnalyticsEvent({ + required this.eventName, + required this.data, + required this.timestamp, + }); + + factory SmallplugAnalyticsEvent.fromJson(Map json) { + return SmallplugAnalyticsEvent( + eventName: json['eventName'] as String? ?? '', + data: (json['data'] as Map?)?.cast() ?? {}, + timestamp: (json['timestamp'] as num?)?.toDouble() ?? 0, + ); + } +} + class ScgatewayFlutterPlugin { static const MethodChannel _channel = const MethodChannel('scgateway_flutter_plugin'); + static const EventChannel _smallplugEventChannel = + EventChannel('scgateway_flutter_plugin/smallplug_events'); + + /// Stream of SmallPlug / DM analytics events. + /// Subscribe before calling [launchSmallplug] or [launchSmallplugWithBranding]. + static Stream get smallplugEventStream { + return _smallplugEventChannel.receiveBroadcastStream().map((event) { + final Map json = + (event is String ? jsonDecode(event) : event) as Map; + return SmallplugAnalyticsEvent.fromJson(json); + }); + } + static const String _flutterPluginVersion = "4.0.0"; static Future getSdkVersion() async { From cce4f834331e352b0add6daf0733fa891718951b Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Sat, 28 Mar 2026 23:35:47 +0530 Subject: [PATCH 2/6] feat: stream smallplug analytics events via EventChannel --- scgateway/android/build.gradle | 2 +- scgateway/ios/scgateway_flutter_plugin.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scgateway/android/build.gradle b/scgateway/android/build.gradle index 4a06427..a9b7d44 100644 --- a/scgateway/android/build.gradle +++ b/scgateway/android/build.gradle @@ -64,5 +64,5 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.smallcase.gateway:sdk:6.0.5' + implementation 'com.smallcase.gateway:sdk-dhruv-propagate-dm-events-17a02a6:6.0.5-3186-release' } diff --git a/scgateway/ios/scgateway_flutter_plugin.podspec b/scgateway/ios/scgateway_flutter_plugin.podspec index deafa0a..5fbf2e1 100644 --- a/scgateway/ios/scgateway_flutter_plugin.podspec +++ b/scgateway/ios/scgateway_flutter_plugin.podspec @@ -15,7 +15,7 @@ Scgateway Flutter plugin. s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.dependency 'SCGateway', '7.1.4' + s.dependency 'SCGateway-dhruv-propagate-dm-events-eaf8710', '7.1.0-18-debug' s.xcconfig = {'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES'} s.vendored_frameworks = 'SCGateway.xcframework' s.platform = :ios, '13.0' From 39686a12921c44ea4ec64006ebed2ad44dd60068 Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Sun, 29 Mar 2026 00:39:09 +0530 Subject: [PATCH 3/6] flutter smallPlug event stream working state --- smart_investing/lib/main.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/smart_investing/lib/main.dart b/smart_investing/lib/main.dart index d68dbbc..9fb8022 100644 --- a/smart_investing/lib/main.dart +++ b/smart_investing/lib/main.dart @@ -1,6 +1,9 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:go_router/go_router.dart'; +import 'package:scgateway_flutter_plugin/scgateway_flutter_plugin.dart'; import 'app/SIGatewayPage.dart'; import 'app/SILoansPage.dart'; @@ -20,6 +23,7 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { late Brightness _brightness; + StreamSubscription? _smallplugEventSub; @override void initState() { @@ -31,11 +35,17 @@ class _MyAppState extends State { _brightness = dispatcher.platformBrightness; }); }; + _smallplugEventSub = + ScgatewayFlutterPlugin.smallplugEventStream.listen((event) { + print( + '[SmallplugEvent] ${event.eventName} | data: ${event.data} | ts: ${event.timestamp}'); + }); super.initState(); } @override void dispose() { + _smallplugEventSub?.cancel(); repository.dispose(); super.dispose(); } From 4fd86ffa99ae0e5afb267974d45bbbfc6a50816d Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Mon, 30 Mar 2026 11:21:31 +0530 Subject: [PATCH 4/6] bump up gateway iOS SDK version to 7.1.7 --- scgateway/ios/scgateway_flutter_plugin.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scgateway/ios/scgateway_flutter_plugin.podspec b/scgateway/ios/scgateway_flutter_plugin.podspec index deafa0a..4590987 100644 --- a/scgateway/ios/scgateway_flutter_plugin.podspec +++ b/scgateway/ios/scgateway_flutter_plugin.podspec @@ -15,7 +15,7 @@ Scgateway Flutter plugin. s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.dependency 'SCGateway', '7.1.4' + s.dependency 'SCGateway', '7.1.7' s.xcconfig = {'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES'} s.vendored_frameworks = 'SCGateway.xcframework' s.platform = :ios, '13.0' From 267ff2e671926f9922b3d7cbbda568b1ac4265e6 Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Mon, 30 Mar 2026 11:21:47 +0530 Subject: [PATCH 5/6] bump up gateway android SDK version to 6.0.6 --- scgateway/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scgateway/android/build.gradle b/scgateway/android/build.gradle index 4a06427..2ef41ed 100644 --- a/scgateway/android/build.gradle +++ b/scgateway/android/build.gradle @@ -64,5 +64,5 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.smallcase.gateway:sdk:6.0.5' + implementation 'com.smallcase.gateway:sdk:6.0.6' } From f4697913a8a83391bccb7b17dac7bfb53c6b20f7 Mon Sep 17 00:00:00 2001 From: Dhruv Porwal Date: Mon, 30 Mar 2026 11:53:41 +0530 Subject: [PATCH 6/6] chore(prod): scg: 7.0.6, si: 3.0.8 --- scgateway/pubspec.yaml | 2 +- smart_investing/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scgateway/pubspec.yaml b/scgateway/pubspec.yaml index 2ad17fa..db560ad 100644 --- a/scgateway/pubspec.yaml +++ b/scgateway/pubspec.yaml @@ -1,6 +1,6 @@ name: scgateway_flutter_plugin description: Scgateway Flutter plugin. -version: 7.0.5 +version: 7.0.6 homepage: https://github.com/smallcase/gw-mob-sdk-flutter # The following line prevents the package from being accidentally published to diff --git a/smart_investing/pubspec.yaml b/smart_investing/pubspec.yaml index ce8a433..0844e18 100644 --- a/smart_investing/pubspec.yaml +++ b/smart_investing/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 3.0.7 +version: 3.0.8 environment: sdk: ^3.8.1