Embed a custom plugin
Updated
Develop a custom plugin, such as a countdown plugin or a dice, and embed the plugin in the flexible classroom.
In online classroom applications, some customization is often necessary to meet the needs of a particular use case. Agora provides Widgets to help users develop plugins according to their specific needs and embed them into Flexible Classroom.
iOS
Implement a custom plugin
This section uses the countdown plugin as an example to introduce the basic steps of implementing a custom plugin through a widget and embedding the plugin in Flexible Classroom.
Implement the widget
Refer to the following steps to implement a custom widget:
-
Import the
AgoraWidgetlibrary:import AgoraWidget -
Initialize the widget's interface and data in the
onWidgetDidLoadmethod:public override func onWidgetDidLoad() { super.onWidgetDidLoad() initViews() initConstraints() updateRoomData() updateViewData() updateViewFrame() log( content: info.roomProperties?.jsonString() ?? "nil", extra: nil, type: .info) } -
Monitor
onWidgetRoomPropertiesUpdatedandonMessageReceivedmethods to update data:public override func onWidgetRoomPropertiesUpdated( _ properties: [String : Any], cause: [String : Any]?, keyPaths: [String]) { super.onWidgetRoomPropertiesUpdated( properties, cause: cause, keyPaths: keyPaths) updateRoomData() updateViewData() shouldStartTime() log( content: properties.jsonString() ?? "nil", extra: cause?.jsonString(), type: .info) } public override func onMessageReceived(_ message: String) { super.onMessageReceived(message) if let timestamp = message.toSyncTimestamp() { objectCreateTimestamp = timestamp initCurrentTimestamp() shouldStartTime() } log(content: message, type: .info) } -
If you need to change the size of the widget, send a message using
sendMessage; then the parent container that listens to the message updates the size of the widget:func updateViewFrame() { let size = ["width": countdownView.neededSize.width, "height": countdownView.neededSize.height] guard let message = ["size": size].jsonString() else { return } sendMessage(message) }
Register Widget in Agora Classroom SDK
Add a countdown widget to AgoraEduLaunchConfig.widgets using AgoraWidgetConfig, and register the implemented plugin to the Agora Classroom SDK during launch:
let launchConfig = AgoraEduLaunchConfig(
userName: userName,
userUuid: userUuid,
userRole: userRole,
roomName: roomName,
roomUuid: roomUuid,
roomType: roomType,
appId: appId,
token: token,
startTime: nil,
duration: nil,
region: region,
mediaOptions: mediaOptions,
userProperties: nil)
let widgetId = "countdownTimer"
launchConfig.widgets[widgetId] = AgoraWidgetConfig(with: AgoraCountdownTimerWidget.self, widgetId: widgetId)
AgoraClassroomSDK.launch(
launchConfig,
success: successBlock,
failure: failureBlock)Using Widgets in the Classroom
Refer to the following steps to integrate the countdown plugin in the classroom:
-
Add an observer of the widget activity through
AgoraEduContext.widget. Use theaddWidgetActivityObservermethod to listen for the callback of the activity usingAgoraClassToolsViewController:override func viewDidLoad() { super.viewDidLoad() contextPool.room.registerRoomEventHandler(self) contextPool.widget.add(self) } -
Listen to the activity's callback and create or destroy the widget:
extension AgoraClassToolsViewController: AgoraWidgetActivityObserver { // Create a widget when an `onWidgetActive` callback is received func onWidgetActive(_ widgetId: String) { createWidget(widgetId) } // When the `onWidgetInactive` callback is received, destroy the widget func onWidgetInactive(_ widgetId: String) { releaseWidget(widgetId) } } -
Create the Widget object of
AgoraCountdownTimerand implement the communication between the widget and the local client:// Call the createWidget method to create the Widget object of AgoraCountdownTimer func createWidget(_ widgetId: String) { let widgetController = contextPool.widget guard widgetIdList.contains(widgetId), // Call getWidgetConfig to get AgoraWidgetConfig of AgoraCountdownTimer let config = widgetController.getWidgetConfig(widgetId) else { return } if let _ = getWidget(widgetId) { return } // Add AgoraClassToolsViewController as the observer of widget syncFrame through addObserverForWidgetSyncFrame of AgoraWidgetContext, and monitor the callback of syncFrame widgetController.addObserver(forWidgetSyncFrame: self, widgetId: widgetId) // Add AgoraClassToolsViewController as an observer of widget message through addWidgetMessageObserver of AgoraWidgetContext, and monitor the callback of message widgetController.add(self, widgetId: widgetId) let widget = widgetController.create(config) view.addSubview(widget.view) switch widgetId { case PollWidgetId: pollWidget = widget case CountdownTimerWidgetId: countdownTimerWidget = widget case PopupQuizWidgetId: popupQuizWidget = widget default: return } sendWidgetCurrentTimestamp(widgetId) } func sendWidgetCurrentTimestamp(_ widgetId: String) { let syncTimestamp = contextPool.monitor.getSyncTimestamp() let tsDic = ["syncTimestamp": syncTimestamp] if let string = tsDic.jsonString() { // Call the sendMessage method to send the current timestamp to the widget contextPool.widget.sendMessage(toWidget: widgetId, message: string) } } -
Implement the
syncFramecallbackextension AgoraClassToolsViewController: AgoraWidgetSyncFrameObserver { // When syncFrame is updated, update the position and size of the widget func onWidgetSyncFrameUpdated( _ syncFrame: CGRect, widgetId: String, operatorUser: AgoraWidgetUserInfo?) { let size = getWidgetSize(widgetId) updateWidgetFrame(widgetId, size: size) } } -
Implement a callback method for listening to messages.
extension AgoraClassToolsViewController: AgoraWidgetMessageObserver { // When the message callback is received, AgoraClassToolsViewController acts as the parent container to update the size of the widget func onMessageReceived(_ message: String, widgetId: String) { if let size = parseSizeMessage(widgetId: widgetId, message: message) { updateWidgetFrame(widgetId, size: size) } } }
API reference
AgoraBaseWidget
AgoraBaseWidget is the base class of a Widget. Widgets that implement specific functions need to inherit from this class.
Members
| Parameter | Type | Description |
|---|---|---|
info | AgoraWidgetInfo | The basic information required by the widget is assigned by the widget controller. |
view | View | Widget's container view. |
updateWidgetRoomProperties
Update the widget's room properties. The updated room properties pass to the Controller through the onWidgetRoomPropertiesUpdated callback.
void updateWidgetRoomProperties(
Map<String: Any> properties,
Map<String: Any> cause,
Callback<Void> success,
Callback<Error> failure)Parameters
| Parameter | Type | Description |
|---|---|---|
properties | Map<String: Any> | Properties to update, supports keyPath. |
cause | Map<String: Any> | The reason for the update, can be empty. |
success | Callback<Void> | Request succeeded. |
failure | Callback<Error> | The request failed, returning an error. |
deleteWidgetRoomProperties
Delete the widget's room property. The deleted room property passes to the Controller through the onWidgetRoomPropertiesDeleted callback.
void deleteWidgetRoomProperties(
Array<String> keyPaths,
Map<String: Any> cause,
Callback<Void> success,
Callback<Error> failure)Parameters
| Parameter | Type | Description |
|---|---|---|
keyPaths | Array<String> | The key array of properties to delete, supports keyPath. |
cause | Map<String: Any> | The reason for the deletion, can be empty. |
success | Callback<Void> | Request succeeded. |
failure | Callback<Error> | The request failed, returning an error. |
updateWidgetUserProperties
Update the widget's user properties. The updated room properties pass to the Controller through the onWidgetUserPropertiesUpdated callback.
void updateWidgetUserProperties(
Map<String: Any> properties,
Map<String: Any> cause,
Callback<Void> success,
Callback<Error> failure)Parameters
| Parameter | Type | Description |
|---|---|---|
properties | Map<String: Any> | Properties to update, supports keyPath. Only need to pass in the value to update. |
cause | Map<String: Any> | The reason for the update, can be empty. |
success | Callback<Void> | Request succeeded. |
failure | Callback<Error> | The request failed, returning an error. |
deleteWidgetUserProperties
Delete the user properties of the widget. The deleted room property passes to the Controller through the onWidgetUserPropertiesDeleted callback.
void deleteWidgetUserProperties(
Array<String> keyPaths,
Map<String: Any> cause,
Callback<Void> success,
Callback<Error> failure)Parameters
| Parameter | Type | Description |
|---|---|---|
keyPaths | Array<String> | The key array of properties to delete, supports keyPath. |
cause | Map<String: Any> | The reason for the update, can be empty. |
success | Callback<Void> | Request succeeded. |
failure | Callback<Error> | The request failed, returning an error. |
sendMessage
Send a message to the observer.
void sendMessage(String message)Parameters
| Parameter | Type | Description |
|---|---|---|
message | String | Message content. |
onLoad
The widget loading is complete; it is loaded inside the Controller.
void onLoad()onMessageReceived
The Controller notifies the widget of received messages.
The sendMessageToWidget callback is fired after calling in the WidgetContext protocol.
void onMessageReceived(String message)Parameters
| Parameter | Type | Description |
|---|---|---|
message | String | Message content. |
onLocalUserInfoUpdated
The Controller notifies the widget of local user information updates.
void onLocalUserInfoUpdated(AgoraWidgetUserInfo localUserInfo)Parameters
| Parameter | Type | Description |
|---|---|---|
localUserInfo | AgoraWidgetUserInfo | Local user information. |
onRoomInfoUpdated
The Controller notifies the widget of room information updates.
void onRoomInfoUpdated(AgoraWidgetRoomInfo roomInfo)Parameters
| Parameter | Type | Description |
|---|---|---|
roomInfo | AgoraWidgetRoomInfo | Room information. |
onWidgetRoomPropertiesUpdated
The Controller notifies the widget of room property updates.
void onWidgetRoomPropertiesUpdated(
Map<String: Any> properties,
Map<String: Any> cause,
Array<String> keyPaths,
AgoraWidgetUserInfo operatorUser)Parameters
| Parameter | Type | Description |
|---|---|---|
properties | Map<String: Any> | Final complete properties. |
cause | Map<String: Any> | Reason, can be empty. |
keyPaths | Array<String> | Array of keys for properties that changed. |
operatorUser | AgoraWidgetUserInfo | Operator, can be empty. |
onWidgetRoomPropertiesDeleted
Controller notifies the widget of room attribute deletion.
void onWidgetRoomPropertiesDeleted(
Map<String: Any> properties,
Map<String: Any> cause,
Array<String> keyPaths,
AgoraWidgetUserInfo operatorUser)Parameters
| Parameter | Type | Description |
|---|---|---|
properties | Map<String: Any> | Final complete properties. |
cause | Map<String: Any> | Reason, can be empty. |
keyPaths | Array<String> | Array of keys for properties to be deleted. |
operatorUser | AgoraWidgetUserInfo | Operator, can be empty. |
onWidgetUserPropertiesUpdated
The Controller notifies the widget of user attribute updates.
void onWidgetUserPropertiesUpdated(
Map<String: Any> properties,
Map<String: Any> cause,
Array<String> keyPaths,
AgoraWidgetUserInfo operatorUser)Parameters
| Parameter | Type | Description |
|---|---|---|
properties | Map<String: Any> | Final complete properties. |
cause | Map<String: Any> | Reason, can be empty. |
keyPaths | Array<String> | Array of keys for properties that changed. |
operatorUser | AgoraWidgetUserInfo | Operator, can be empty. |
onWidgetUserPropertiesDeleted
The Controller notifies the widget of user attribute deletions.
void onWidgetUserPropertiesDeleted(
Map<String: Any> properties,
Map<String: Any> cause,
Array<String> keys)Parameters
| Parameter | Type | Description |
|---|---|---|
properties | Map<String: Any> | Final complete properties. |
cause | Map<String: Any> | Reason, can be empty. |
keys | Array<String> | Array of keys for properties to delete. |
