Skip to main content

Initialization of the MasterController (Android/Kotlin and Flutter)

We communicate with the Master Controller by sending and receiving events. To simplify this process in our demo app, we use a wrapper class called MasterControllerAdapter for Swift, MCHandler for Kotlin, and McWrapperHandler for Flutter in our example app. These classes provide methods for interacting with the Master Controller, so we do not need to understand all the intricacies of its implementation.

Here are some important points about the MasterControllerAdapter/MCHandler/McWrapperHandler class:

  • It serves as a single point of connection with the Master Controller, preventing the need to scatter connection code throughout the app.
  • It offers dedicated methods for sending events.
  • It provides a single method for receiving events from the Master Controller.
  • Observers can be added or removed to handle received events in different classes as needed.

Android/Kotlin Implementation

We mainly use three methods to communicate with the Master Controller:

  1. postEvent(event: EventFrameworkEvent): This method sends an event to the Master Controller. We use it when we expect a result event after triggering an event on the Master Controller side. By implementing the then block on postEvent, we can retrieve and handle the result event. The postEvent() method returns a Future object, and its get() method returns an EventFrameworkEvent, which is the superclass of all events.

  2. forwardEvent(event: EventFrameworkEvent): Similar to postEvent(), this method sends events to the Master Controller. However, it operates as a "Fire and Forget" mechanism, meaning it does not expect a result event for the triggered event. We use this method when no result event is expected. Here you can find the events table.

  3. executeEvent(eventFrameworkEvent: EventFrameworkEvent?): This method receives events from the Master Controller.

MCHandler extends from SynchronousEventHandler (wrapper class available in MasterController library) which makes the methods for communicating with MasterController available to MCHandler.

MCHandler
class MCHandler private constructor(
private val context: Context
) : SynchronousEventHandler(context) {
private var mcEventHandler: SynchronousEventHandler? = null
/**
* Holds MCEventObserver to notify about Master Controller events
*/
private var observers: MutableList<MCEventObserver> = mutableListOf()
}

MCHandler is a singleton class with a getInstance() method that allows us to obtain its instance. When we create an instance of MCHandler, the constructor of SynchronousEventHandler is called, executing the necessary steps to initialize the Master Controller.

getInstance Method
companion object {
private var instance: MCHandler? = null
fun getInstance(context: Context): MCHandler {
synchronized(this) {
if (instance == null) {
instance = MCHandler(context)
instance?.setEventHandlerInstance(instance!!)
}
return instance!!
}
}
}

fun setEventHandlerInstance(eventHandler: SynchronousEventHandler) {
this.mcEventHandler = eventHandler
}

MCHandler class extends the SynchronousEventHandler class, which is an abstract class. Therefore, we need to override a method called executeEvent. This method is invoked whenever the Master Controller sends any event, meaning it is responsible for receiving events from the Master Controller.

executeEvent Method
@Synchronized
override fun executeEvent(event: EventFrameworkEvent?) {
if (observers.isNotEmpty()) {
observers.forEach { observer ->
event?.let {
observer.onEventReceived(it)
logDebug(
observer.javaClass.name + " is notified by " + observer.javaClass.name,
"processEvent",
TAG
)
}
}
} else {
logDebug(
"No observer attached to received event ==> $event",
"processEvent",
TAG
)
}
}

We need to add an event observer to get the callback in your current class whenever Master Controller sends any event. There is a method named addMCEventObserver which is responsible for adding an event observer.

addMCEventObserver Method
/**
* Adds MCEventObserver instance to observers List.
*
* @param o - MCEventObserver instance
*/

@Synchronized
fun addMCEventObserver(o: MCEventObserver) {
observers.add(o)
logDebug(
o.javaClass.name + " has been added as MC Event observer",
"addMCEventObserver",
TAG
)
}

NOTE: It is important to remove the observer whenever we no longer need to listen for MasterController events.

There is a method called removeMCEventObserver that handles the removal of the observer.

removeMCEventObserver
/**
* Removes MCEventObserver instance from observers List.
*/

@Synchronized
fun removeMCEventObserver(observer: MCEventObserver) {
observers.remove(observer)
logDebug(
observer.javaClass.name + " has been removed as MC Event observer",
"removeMCEventObserver",
TAG
)
}

Android/Kotlin Usage Examples

Below is a code snippet demonstrating how to use the provided methods in their respective implementation classes: Here is a code snippet for obtaining the instance of MCHandler:

Getting instance of MCHandler
mcHandler = MCHandler.getInstance(context of current Activity);

By adding an event observer to MCHandler, we can start listening to MC events. Below is a code snippet for adding an event observer:

Adding MC Event Observer
mcHandler?.addMCEventObserver(reference of MCEventObserver)

Following is the callback method where you receive a call when the MasterController sends any event (if an observer is added).

MC Event Callback Method
override fun onEventReceived(eventFrameworkEvent: EventFrameworkEvent) {
// Write code to handle the event
}

By removing the observer we can stop listening to MC events. Here is the code snippet:

Removing Event observer
mcHandler?.removeMCEventObserver(context of Activity)

Below is the diagram to understand the flow mentioned above in the form of code:


Flutter/Dart Implementation

For Flutter, we use McWrapperHandler in our demo app, which provides similar functionality to the Android MCHandler. The McWrapperHandler class serves as a wrapper around the McWrapperApi and provides methods for communicating with the Master Controller.

Key Features of McWrapperHandler:

  1. Singleton Pattern: Ensures a single instance throughout the app lifecycle
  2. Event Sending: Uses the send() method to communicate with Master Controller
  3. Event Receiving: Implements McWrapperApiEventReceiver to handle incoming events
  4. Observer Pattern: Allows multiple observers to listen for Master Controller events
  5. Lifecycle Management: Provides start, stop, suspend, and resume functionality
McWrapperHandler Structure (Flutter/Dart)
class McWrapperHandler extends McWrapperApiEventReceiver {
late McWrapperApi _mcWrapperApi;
static final McWrapperHandler _instance = McWrapperHandler._internal();

List<McWrapperApiEventReceiver> receivers = [];

McWrapperApi get api => _mcWrapperApi;

factory McWrapperHandler() {
return _instance;
}
}

Flutter/Dart Usage Examples

Getting Instance and Initialization

Getting McWrapperHandler Instance (Flutter/Dart)
// Get the singleton instance
final mcHandler = McWrapperHandler();

// Or get from service locator (see below for further details)
// final mcHandler = locator<McWrapperHandler>();

// Initialize the wrapper
await mcHandler.initializerMethod();

Sending Events to Master Controller

Sending Events (Flutter/Dart)
Future<void> sendEventToMC() async {
try {
// Create your event
final startEvent = StartEventT();

// Send event and handle response
final response = await mcHandler.send(startEvent);

response.fold(
(error) {
print("❌ Error sending event: $error");
},
(result) {
print("✅ Event sent successfully: $result");
// Handle the result event
},
);
} catch (e) {
print("❌ Exception: $e");
}
}

Adding Event Observers

Adding Event Observer (Flutter/Dart)
class MyEventReceiver extends McWrapperApiEventReceiver {
@override
void onReceive(Object event) {
print("📥 Received event: ${event.runtimeType}");

// Handle specific event types
if (event is TriggerBannerEventT) {
handleTransactionEvent(event);
} else if (event is StartResultEventT) {
handleStartResult(event);
}
}

void handleTransactionEvent(TriggerBannerEventT event) {
// Handle transaction events
}

void handleStartResult(StartResultEventT event) {
// Handle start result events
}
}

// Add the observer
void setupEventHandling() {
final mcHandler = locator<McWrapperHandler>();
final eventReceiver = MyEventReceiver();

mcHandler.addObserver(eventReceiver);
}

Removing Event Observers

Removing Event Observer (Flutter/Dart)
void cleanupEventHandling() {
final mcHandler = locator<McWrapperHandler>();
final eventReceiver = myEventReceiverInstance;

mcHandler.removeObserver(eventReceiver);
}

Lifecycle Management

Lifecycle Management (Flutter/Dart)
class MyApp extends StatefulWidget with WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
final mcHandler = locator<McWrapperHandler>();

switch (state) {
case AppLifecycleState.resumed:
mcHandler.resumeWrapper();
break;
case AppLifecycleState.paused:
mcHandler.suspendWrapper();
break;
case AppLifecycleState.detached:
mcHandler.stopWrapper();
break;
default:
break;
}
}
}

Flutter Service Locator Integration

In Flutter, we use a service locator (GetIt) to manage dependencies and provide global access to the McWrapperHandler instance. This approach offers several benefits:

  • Singleton Management: Ensures only one instance of McWrapperHandler exists throughout the app
  • Global Access: Any part of the app can access the handler without passing it through constructors
  • Dependency Injection: Simplifies testing by allowing easy substitution of mock implementations
  • Lifecycle Management: Centralized control over when services are created and destroyed
  • Decoupling: Reduces dependencies between classes by providing a central registry
Service Locator Setup (Flutter/Dart)
GetIt locator = GetIt.instance;

Future<void> setupDependencies() async {
// Initialize McWrapperHandler
final mcHandler = McWrapperHandler();
await mcHandler.initializerMethod();

// Register in service locator as singleton
locator.registerSingleton<McWrapperHandler>(mcHandler);

// Add observers for transaction handling
final transactionReceiver = TransactionEventReceiver();
mcHandler.addObserver(transactionReceiver);
locator.registerLazySingleton(() => transactionReceiver);
}

// Access from anywhere in the app
void useFromAnywhereInApp() {
final mcHandler = locator<McWrapperHandler>();
// Use the handler...
}

Important Notes

NOTE:

  • Always remove observers when they are no longer needed to prevent memory leaks
  • Handle app lifecycle events to properly suspend/resume the wrapper