伯索开放平台
首页API文档SDK文档伯索官网
首页API文档SDK文档伯索官网
  1. Android版本
  • 直播SDK接入
    • WEB版本
      • WEB版本
    • iOS版本
      • IOS版本
    • Android版本
      • Android版本
  • 微课SDK接入
    • WEB版本
      • WEB版本
    • iOS版本
      • IOS版本
    • Android版本
      • Android版本
  • 批注SDK接入
    • Web版本
      • Web版本
    • iOS版本
      • IOS版本
    • Android版本
      • Android版本
  • 签名加密
    • 签名加密
  • 回调接口接入流程
    • 回调接口接入流程
  • 文档解析接入方式
    • 文档解析接入方式
  • 历史课堂接入方式
    • 历史课堂接入方式
  • 历史课堂转视频接入方式
    • 历史课堂转视频接入方式
  • 历史课堂/微课下载接口
    • 历史课堂/微课下载接口
  1. Android版本

Android版本

📌

Android版本1.53.311

一. 准备阶段
应用需要用到AndroidX支持库,如果在使用Android Support库的请迁移,如果已经使用AndroidX请忽略。 迁移可参考下面的说明,或查阅 AndroidX迁移指南: |

1) 切换到单独的分支,迁移完成前最好不进行功能开发,然后将代码备份一份,防止迁移失败
2) 检查项目中每个module中support库的依赖,将版本升级到28.0.0,需要将compileSdkVersion升级到28,保证项目正常能编译通过
3) 在Android Studio中Refactor->Migrate to AndroidX完成迁移
4) 编译项目,如果有编译错误,解决即可(如第三方依赖库版本不支持androidx需要升级到新版本,例如ButterKnife)

二. 配置伯索的maven仓库

在项目的build.gradle或settings.gradle文件中添加伯索的maven仓库。

repositories {
        ...
        maven { url 'https://nexus.plaso.cn/repository/maven-public/'}
    }

三. app->build.gradle配置

在项目的app->build.gradle 中:①配置minSdkVersion需要大于等于24,targetSdkVersion 大于等于33 ②添加SDK相关的依赖。

android {
    ……
    defaultConfig {
        ……
        minSdkVersion 24  // 配置 >= 24
        targetSdkVersion 33 // 配置 >= 33
        ……
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
    }
}

dependencies {
    ...
    implementation( 'cn.plaso:styleupime:$version')
}

四. 实现IResourceProvider

IResourceProvider是定制化资料中心接口, APP提供该接口的实现,并传入SDK。IResourceProvider实现后我们需要创建一个StyleUpime对象,创建StyleUpime对象需要用到IResourceProvider,IResourceProvider不能为null,后续启动实时课堂需要使用StyleUpime对象。

override fun onCreate() {
    super.onCreate()
    ...
    resProvider = object : IResourceProvider {
        override fun supportResourceCenter(): Boolean {
            //是否支持定制化资料中心,返回true代表支持,false代表不支持
            return false
        }

        override fun showResourceCenter(p0: Boolean) {
             // SDK用户忽略 p0参数;
             //显示 APP 定制化资料中心,在此处实现跳转到定制化资料中心页面
        }

        override fun dismissResourceCenter() {
             //关闭 APP 定制化资料中心
        }

        override fun getExtFileName(extFileId: Any?, resouceCallback:IResourceCallback?) {
            //APP 根据传入的文件标识内容返回其可以访问的完整 URL
        }

        override fun signQuery(queryMap: MutableMap<String, Any>?, cb: SignCallback?): Boolean {
            //请求SDK Client进行参数签名,窗口模式下,插入文件,需要从服务器获取签名
            if (queryMap != null) {
                queryMap["appId"] = Config.appId //Config.appId填写从伯索获取的appId,这个参数是必需的
                queryMap["validTime"] = 120
                queryMap["validBegin"] =  System.currentTimeMillis() / 1000
                //根据queryMap,appKey生成一个query
                val signedQuery = SignHelper.sign(queryMap, Config.appKey) //Config.appKey填写从伯索获取的appKey
                cb?.onSignCompleted(signedQuery)
            } else {
                cb?.onSignCompleted(null)
            }
            return true
        }

        override fun getWebviewUrl(type: Int, callback: IUrlCallback?) {
            //请求SDK Client是否有教材包的链接需要在课堂内打开
            callback?.onGetUrl("https://www.plaso.cn")
        }
    }
    //创建一个StyleUpime对象
    upime = StyleUpime.create(this, "demo", resProvider)
}

在signQuery方法中我们需要生成一个query,生成方法参考如下形式:

object SignHelper {

    /**
     * Sign the [params] with specific [signKey]
     */
    fun sign(params: MutableMap<String, Any>, signKey: String): String {
        val keys = params.keys.sorted()//将Map中的key按顺序排列
        val sortedParams = StringBuilder()
        for ((index, key) in keys.withIndex()) {
            if (index != 0) {
                sortedParams.append("&")
            }
            sortedParams.append("$key=${params[key]}")
        }

        val signature = encrypt(sortedParams.(), signKey)
        params["signature"] = signature;
        return buildQuery(params);
    }

    private fun buildQuery(params: MutableMap<String, Any>): String {
        val result = StringBuilder()
        for ((index, key) in params.keys.withIndex()) {
            if (index != 0) {
                result.append("&")
            }
            result.append("$key=${URLEncoder.encode(params[key].toString(), "UTF-8")}")
        }
        return result.()
    }

    private fun encrypt(encryptText: String, signKey: String): String {
        val algorithm = "HmacSHA1"
        val charset = Charset.forName("UTF-8")
        val data = signKey.toByteArray(charset)

        val secretKey = SecretKeySpec(data, algorithm)
        val mac = Mac.getInstance(algorithm)
        mac.init(secretKey)
        val rst = mac.doFinal(encryptText.toByteArray(charset))

        return BigInteger(1, rst).(16).toUpperCase()
    }

五. 启动实时课堂

实时课堂支持配置日志和水印,在启动实时课堂之前我们可以根据自己的需要进行设置,如不需要可以忽略

private fun setUpimeParameter(){
    var parameter : UpimeParameter = UpimeParameter()
    parameter.waterMark = "waterMark"         
    parameter.waterMarkSize = size               
    parameter.logDir = logPath                
    parameter.logLevel = UpimeParameter.DEBUG 
    upime.setUpimeParameter(parameter) //upime是step.4创建的StyleUpime对象
}

UpimeParameter参数说明:

参数名参数类型是否必须默认值参数说明
appidString否-应用唯一标识
waterMarkString否-水印
logDirString否-日志路径
logLevelint否-1日志级别 DEBUG:0 INFO:1 WARN:2 ERROR:3

启动实时课堂时也需要生成一个query,query中包含创建实时课堂所需要的一些参数,query里面如果包含中文字符串,中文字符串必须进行utf8编码,可以参考demo SignHelper里面buildQuery函数的编码方式。 query创建方法参考以下形式:

private fun getQuery(): String? {
    ...
    val params = mutableMapOf<String, Any>().also {
        it["appId"] = Config.appId //Config.appId填写从伯索获取的appId
        it["appType"] = "liveclassSDK"
        it["beginTime"] = System.currentTimeMillis() / 1000
        it["endTime"] = endTime
        it["mediaType"] = meetingType
        it["meetingId"] = meetingId
        it["meetingType"] = if (useMeetingMode) "meeting" else "public"
        it["loginName"] = userName
        it["userName"] = userName
        it["userType"] = userType
        it["validTime"] = 1080000
        it["onlineMode"] = Integer.parseInt(onlineMode)
        it["d_sharpness"] = sharpness
        it["d_dimension"] = resolution
        if (videoStream != -1) {
            it["videoStream"] = videoStream
        }
        it["vendorType"] = rtcType
        it["enableNewClassExam"] = enableNewClassExam
        }
    //SignHelper.sign方法参考step.4,Config.appKey填写从伯索获取的appKey
    return SignHelper.sign(params, Config.appKey) 
}

query参数说明:

参数名参数类型是否必须默认值参数说明
appIdString是-应用唯一标识
appTypeString否-应用类型
beginTimeLong是-课堂开始时间
endTimeInt否-课堂结束时间(s)
mediaTypeString是-上课类型视频课堂传:“video” ;音频传:“audio”
meetingIdString是-会议号,可随意定义
meetingTypeString是-是不是会议模式 ;非会议模式传:"public" 会议模式传:"meeting"
loginNameString是-唯一标识该用户的id,不能为空,相同的loginName登录,后面一个会使前面一个登出;
userNameString否-用户名
userTypeString是-用户类型 老师传:"speaker" 学生传:"listener" 游客传:"visitor"
validTimeInt是-有效时间
onlineModeInt是-上台人数
d_sharpnessString是-清晰度 VD_360P传:"10" VD_720P传:"20" VD_1080P传:"30"
d_dimensionString是-分辨率 ;参考格式:"1280x720"
enableNewClassExamint否0老版随堂测:0;新版随堂测选择题:1;新版随堂测填空题:2;新版随堂测选择+填空:3
d_enableObjectEraserint否0点擦为0, 对象擦:1(手写),3(手写+文本框),5(手写+图形),7(手写+文本框+图形)

query生成完成后需要在启动实时课堂时传给SDK,需要构建一个ClassConfig对象,ClassConfig对象同样需要进行一些参数配置。ClassConfig对象创建完毕后只需要通过StyleUpime对象调用launchLiveClass方法即可启动实时课堂。

private fun launchLiveClass() {
   ...

    val query = getQuery()
    if (query != null) {
        val config = ClassConfig().also {
            it.classURL = query
            it.host = Config.server //"https://dev.plaso.cn"
            it.openFileMode = UpimeConfig.OPEN_FILE_MODE_WINDOW
            it.toolboxItems = UpimeConfig.ToolBoxItem.ALL.value

            it.supportBlueToothConnect = enableBlueTooth
            it.enableInteractPpt = enablePptInteract
            it.teachToolTypes = UpimeConfig.TeacherToolType.PureUpimeTeachToolTypeAll.value
            it.supportUndo = undoSupport
            it.useNewSmallBoard = useNewSmallBoard
            it.supportSelect = supportSelect;
            it.endRemindTime = remindTime.text.().toInt()
            var limitnum = 0
            if (!TextUtils.isEmpty(redPacketLimit.text?.())) {
                limitnum = redPacketLimit.text.().toInt()
            }
            if (limitnum > 0) {
                it.redPacketLimit = limitnum;
            }
            if (useMeetingMode && TextUtils.isDigitsOnly(etPermission.text.())) {
                it.defaultPermission = etPermission.text.().toInt()
            }
            if (isPhoneTeachingMethod) {
                it.mobileTeaching = true;
            }
            it.residentCamera = isResidentCamera;
            it.auxiliaryCamera = isAuxiliaryCamera;
            it.supportHighlighter = supportHighlighter;
            it.forbiddenScreenShot = forbiddenScreenShot;
        }
        //upime是step.4创建的StyleUpime对象
        upime.launchLiveClass(config, object : ILiveClassListener {
            override fun onLiveClassReady(upimeBoard: UpimeBoard?) {
               // 获得UpimeBoard对象,可用于资料中心的资料插入等操作
            }

            override fun onExited(exitCode: Int, mid: String) {
               //退出实时课堂后回调
            }

            override fun onSkinChanged(skinId: Int) {
               //实时课堂更换背景回调
            }

        })
    }
}

Classconfig参数说明:

参数名参数类型是否必须默认值参数说明
classURLstring是-实时课堂配置参数query ;如果query里面有中文字符串,此中文字符串需要编码后放到query里传入。
hoststring否-服务地址
userNamestring否-当前用户名称
classMemberArrayList否-课堂成员列表
allowLocalPPTboolean否true是否允许在实时课堂中插入本地PPT
enableSendMessageboolean否true消息界面是否显示消息输入框
hideOtherClientboolean否false是否显示学生不在班级的所有学生和助教
endRemindTimeint否0实时课堂添加双减的提示时间,到达这个时间之后,会提示用户 (:秒)
redPacketLimitint否0红包雨个数限制
defaultPermissionint否0会议模式默认权限
supportSelectboolean否true是否支持工具栏选择箭头
openFileModeint否null文件打开模式
toolboxItemsint否-百宝箱工具条目配置(工具请参考UpimeConfig.ToolBoxItem)
enableInteractPptboolean否false是否开启PPT交互
teachToolTypesint否0教具类型支持的类型(类型请参考UpimeConfig.TeacherToolType
supportUndoboolean否false是否支持撤销操作
useNewSmallBoardboolean否false是否使用新版小黑板
supportHighlighterBoolean否false是否使用荧光笔
forbiddenScreenShotBoolean否false是否防止课堂截屏
enableVoteBoolean否false是否支持投票工具
enableSaveBoardBoolean否false是否支持保存板书功能,如果支持需要对接实现onUpimeBoardSaved这个回调,保存数据到自己三方服务器

六. 参考代码Demo
请点击下载

使用步骤

  1. 解压缩
  2. 修改build.gradle文件,升级版本到1.53.311
dependencies {
    ……
    implementation 'cn.plaso:styleupime:1.53.311
'
}
  1. 修改文件: app/src/main/java/cn/plaso/liveclasssdkdemo/Config.kt, 填入伯索分配的appid和key后即可体验。
var appId: String = "<your-appId>"
     /**
      * appKey
      */
const val appKey:String = "<your-app-key>"

七.资料中心接入事项

一:
1.资料中心选择好的文件传参时,服务器传过来的文件路径不用带签名计算,签名需要的时候 ,实时签名。
2.insertObject里面UpimeObject.info根据type传string路径或者list;
例如:TYPE_IMAGE则直接把path传进去;
如果服务器传过来的地址是没有做计算签名的,那么这个时候需要客户端把把签名加上。
例如:TYPE_PPT,根据type传list, 服务器传过来的没有签名,则在getExtFileName回调里面把签名加上。
例如:TYPE_VIDEO,服务器传过来的没有签名,则在getExtFileName里面把签名加上。

二:
如果type传list,会回调到这个函数getExtFileName里,需要在getExtFileName里面做签名;
如果传string ,则不会回调到getExtFileName,签名根据 "步骤一" 操作计算。

八. 对接注意事项

在DemoApp.kt文件中传入appId 和appKey 值,详情参考下文例子;签名请通过在服务器中完成,Demo 中只是样例。

override fun signQuery(queryMap: MutableMap<String, Any>?, cb: SignCallback?): Boolean {
                if (queryMap != null) {
                    queryMap["appId"] = xxx //自己机构的appId
                    queryMap["validTime"] = 120
                    queryMap["validBegin"] =  System.currentTimeMillis() / 1000
                    // TODO get it from server
                    // this is only for test
                    val signedQuery = SignHelper.sign(queryMap, xxx) //自己机构的appKey
                    cb?.onSignCompleted(signedQuery)
                    } else {
                    cb?.onSignCompleted(null)
                    }

                return true
              }

(2). AndroidManifest.xml添加下面代码,不然课堂里面截图至相册功能可能会失败

<meta-data
android:name="ScopedStorage"
android:value="true" />

(3). targetSdkVersion 设置成 33;

修改于 2025-04-09 02:59:44
上一页
Android版本
下一页
微课SDK接入
Built with