Classroom and Proctor SDK

Updated

Easily update the look and feel of your classrooms.

Introduction to classrooms and UI components

This section tells you about the components that manage data and the user interface in Flexible Classroom.

Data exchange process

In the Agora Classroom SDK, the code of the user interfaces is separated from the code of core business logic. The Classroom SDK contains two libraries, AgoraEduUI and AgoraEduCore. These two libraries connect with each other through Agora Edu Context. Supposing that we want to implement a button for turning on or off the camera, you can call the openLocalDevicemethod of AgoraEduMediaContext in AgoraEduUI, and listen to the event which indicates the device state change thrown by AgoraEduMediaHandler.

The data flow is as follows:

The structure of classrooms and UI components

AgoraEduUI contains the code of UI components in Flexible Classroom. The source code of AgoraEduUI is in /SDKs/AgoraEduUI in the CloudClass-iOS repository. The project structure is as follows:

FolderDescription
/ScenesArranges UI components for different classroom types of Flexible Classroom, such as One-to-one Classroom, Small Classroom, and Lecture Hall.
/ComponentsThe UI components for modular features in Flexible Classroom, such as the user list and navigation bar.
/ConfigsThe UI configurations of Flexible Classroom. The UI configurations, such as background color, font color, and border width, are automatically adapted according to AgoraUIMode. Developers can define their own AgoraUIMode in this folder.
/ViewsThe basic UI components used in Flexible Classroom, such as the video rendering window and the dialog window.
/ModelsThe corresponding data model and parsing method defined for parsing widget data in AgoraEduUI.

Type description

  • UIScene

    • A UIScene represents a classroom type. The type of UIManager is UIViewController.
    • A UIScene manages multiple UIComponent and is responsible for the communication between UIComponent.
    • Each UIScene has a contextPool for using the abilities provided by AgoraEduContext.
  • UIComponent

    • A UIComponent represents a UI component. The type of UIController is UIViewController.

    • The view of UIComponent is a subview of UIScene.contentView, which is the placeholder for this function.

    • UIComponent is in the /Components folder of the AgoraEduUI library, and is divided into the following two types:

      • FlatComponents: UI component of tile type.
      • SuspendComponents: UI component of dialog type.

UI structure

Taking Small Classroom on the teacher side as an example, the layout of UI components is as follows:

Customizing the classroom UI

To customize the classroom UI, follow these steps:

1. Get the source code for Flexible Classroom

To customize the classroom UI based on the default UI of Flexible Classroom, download the source code for Flexible Classroom from CloudClass-iOS and apaas-extapp-ios.

  1. Clone the repositories:

    git clone https://github.com/AgoraIO-Community/CloudClass-iOS.git
    git clone https://github.com/AgoraIO-Community/apaas-extapp-ios.git
  2. Update to the supported version of Flexible Classroom:

      cd CloudClass-iOS
      git checkout release/2.8.11
      cd ../apaas-extapp-ios
      git checkout release/2.8.11
  3. (Optional) Upload your project to a GitHub repository:

    1. From inside each repository, run git remote add <shortname> <url>, pointing to your project.

    2. Create a branch based on the latest release branch of Flexible Classroom and push it to your project.

  4. Add the following code in your project's Podfile to make reference to AgoraClassroomSDK_iOS.podspec, AgoraEduContext.podspec, AgoraEduUI.podspec, AgoraWidgets.podspec and other dependencies.

    # third libs
    pod 'Protobuf', '3.17.0'
    pod 'CocoaLumberjack', '3.6.1'
    pod 'AliyunOSSiOS', '2.10.8'
    pod 'Armin', '1.1.0'
    pod 'SSZipArchive', '2.4.2'
    pod 'SwifterSwift', '5.2.0'
    pod 'Masonry', '1.1.0'
    pod 'SDWebImage', '5.12.0'
    
    # agora libs
    pod 'AgoraRtcEngine_iOS/RtcBasic', '3.6.2'
    pod 'AgoraMediaPlayer_iOS', '1.3.0'
    pod 'AgoraRtm_iOS', '1.4.8'
    pod 'Agora_Chat_iOS', '1.0.6'
    pod 'Whiteboard', '2.16.39'
    
    # open source libs
    pod 'AgoraClassroomSDK_iOS', :path => '../CloudClass-iOS/AgoraClassroomSDK_iOS.podspec'
    pod 'AgoraEduUI', :path => '../CloudClass-iOS/AgoraEduUI.podspec'
    pod 'AgoraWidgets', :path => '../apaas-extapp-ios/AgoraWidgets.podspec'
    
    # close source libs
    pod 'AgoraUIBaseViews', '2.8.0'
    pod 'AgoraEduCore', '2.8.0'
    pod 'AgoraWidget', '2.8.0'

2. Edit the existing UI components

Example one: Change the background color of the navigation bar

You can change the background color of the navigation bar in one of the following ways:

  • Method one: Edit the code in RoomStateUIComponent.

  • Method two: Edit the backgroundColor variable of FcrUIComponentStateBar in UIConfigs.

The following code is an example of the method two:

Before

struct FcrUIComponentStateBar: FcrUIComponentProtocol {
    var visible: Bool = true
    var enable: Bool = true
    // The color of the navigation bar is systemForegroundColor。
    var backgroundColor: UIColor = FcrUIColorGroup.systemForegroundColor

    /**Scene Builder Set**/
    var networkState = FcrUIItemStateBarNetworkState()
    var roomName = FcrUIItemStateBarRoomName()
    var scheduleTime = FcrUIItemStateBarScheduleTime()
    /**iOS**/
    let sepLine = FcrUIItemSepLine()

    let borderWidth = FcrUIFrameGroup.borderWidth
    let borderColor = FcrUIColorGroup.systemDividerColor
}

After

struct FcrUIComponentStateBar: FcrUIComponentProtocol {
    var visible: Bool = true
    var enable: Bool = true
    // The color of the navigation bar is systemTeal。
    var backgroundColor: UIColor = .systemTeal

    /**Scene Builder Set**/
    var networkState = FcrUIItemStateBarNetworkState()
    var roomName = FcrUIItemStateBarRoomName()
    var scheduleTime = FcrUIItemStateBarScheduleTime()
    /**iOS**/
    let sepLine = FcrUIItemSepLine()

    let borderWidth = FcrUIFrameGroup.borderWidth
    let borderColor = FcrUIColorGroup.systemDividerColor
}

Example two: Change the icons used in the user list componet

The code of the user list component is in the following two files:

  • iOS/SDKs/AgoraEduUI/Classes/Components/SuspendComponents/FcrRosterUIController.swift
  • iOS/SDKs/AgoraEduUI/Classes/Views/UserList/AgoraUserListItemCell.swift

As the following picture shows, the student list includes six columns: Student Name, Stage, Auth, Camera, Micr, and Rewards:

There are two states, namely true and false for stage and whiteboard authorization. The camera and microphone have four states: not on stage + inoperable, on stage + device off, on stage + device on + streaming permission off, on stage + device on + streaming permission on.

The data of the user list component comes from:

  • The changes in the total number of students and number of students "on the stage" are reported by callbacks in AgoraEduUserHandler.
  • The changes in the microphone state and camera state are reported by callbacks in AgoraEduStreamHandler.
  • The whiteboard authorization state is reported by the widget with the ID of netlessBoard.

Taking the camera state as an example, the data flow is as follows:

  1. When the stream state changes, the onStreamUpdated callback in AgoraEduStreamHandler is triggered, and then the data is updated through the updateModel method.
  2. After the data is updated, tableView.reloadData() is called to refresh each cell of the tableView.
  3. Finally, the icon is updated in the updateState method of AgoraUserListItemCell.

If you want to update the camera icon, refer to the following steps:

  1. Put the new camera icons new_camera_on and new_camera_off in the AgoraEduUI/AgoraEduUI/Assets/images.xcassets/NameRoll folder:

  2. Update the code in the AgoraUserListItemCell.swift file:

Before

// colors
let onColor = UIColor(hex: 0x0073FF)
let offColor = UIColor(hex: 0xF04C36)
let disabledColor = UIColor(hex: 0xE2E2EE)

// state
case .camera:
    if !model.stageState.isOn {
        // unCohost
        let image = UIImage.agedu_named("ic_nameroll_camera_on")
        if let i = image?.withRenderingMode(.alwaysTemplate) {
            cameraButton.setImageForAllStates(i)
        }
        cameraButton.tintColor = disabledColor
    } else if !model.cameraState.deviceOn {
        // cohost + device off
        let image = UIImage.agedu_named("ic_nameroll_camera_off")
        if let i = image?.withRenderingMode(.alwaysTemplate) {
            cameraButton.setImageForAllStates(i)
        }
        cameraButton.tintColor = disabledColor
    } else if !model.cameraState.streamOn {
        // cohost + device on + no video stream privilege
        let image = UIImage.agedu_named("ic_nameroll_camera_off")
        if let i = image?.withRenderingMode(.alwaysTemplate) {
            cameraButton.setImageForAllStates(i)
        }
        cameraButton.tintColor = offColor
    } else {
        // cohost + device on + video stream privilege
        let image = UIImage.agedu_named("ic_nameroll_camera_on")
        if let i = image?.withRenderingMode(.alwaysTemplate) {
            cameraButton.setImageForAllStates(i)
        }
        cameraButton.tintColor = onColor
    }
    cameraButton.isUserInteractionEnabled = model.cameraState.isEnable

After

case .camera:
    if !model.stageState.isOn {
        // unCohost
        let image = UIImage.agedu_named("new_camera_on")
        if let i = image?.withRenderingMode(.alwaysTemplate) {
            cameraButton.setImageForAllStates(i)
        }
        cameraButton.tintColor = disabledColor
    } else if !model.cameraState.deviceOn {
        // cohost + device off
        let image = UIImage.agedu_named("new_camera_off")
        if let i = image?.withRenderingMode(.alwaysTemplate) {
            cameraButton.setImageForAllStates(i)
        }
        cameraButton.tintColor = disabledColor
    } else if !model.cameraState.streamOn {
        // cohost + device on + no video stream privilege
        let image = UIImage.agedu_named("new_camera_off")
        if let i = image?.withRenderingMode(.alwaysTemplate) {
            cameraButton.setImageForAllStates(i)
        }
        cameraButton.tintColor = offColor
    } else {
        // cohost + device on + video stream privilege
        let image = UIImage.agedu_named("new_camera_on")
        if let i = image?.withRenderingMode(.alwaysTemplate) {
            cameraButton.setImageForAllStates(i)
        }
        cameraButton.tintColor = onColor
    }
    cameraButton.isUserInteractionEnabled = model.cameraState.isEnable

3. Add a UI component

The basic steps for adding a new UI component are as follows:

  1. Add a UIComponent class in the iOS/SDKs/AgoraEduUI/Classes/Components folder.
  2. Add an object of this UIComponent class in UIScene and add the view.