iOS

# 实现插件特性

本文展示如何实现菊风视频能力平台提供的插件功能。

# 前提条件

开始前,请确保你具备以下条件:

  • 已下载插件包

  • 已集成 Juphoon Plugin。目前只有该版本支持插件集成。

  • 插件 API说明在以下内容中,请查看。

# Plugin 进阶应用

# 1. 监听程序进入前后台

/**
 * 初始化,需要在 Application 的onCreate方法调用
 *
 * @param application Application 对象
 */
 void init(Application application);

示例代码:

JCCGuestManager.getInstance().init(this);

# 2. 定制化配置

/**
 * 设置定制化的配置参数
 *
 * @param customConfig 配置参数
 */
public void setCustomConfig(JCCGuestCustomConfig customConfig);

JCCGuestCustomConfig (opens new window) 内部根据不同界面进行分类区分

/**
 * 定制化配置参数
 */
public class JCCGuestCustomConfig {
    /**
     * 设置来电页面配置
     *
     * @param incomingConfig 来电页面配置
     */
    public void setIncomingConfig(JCCIncomingConfig incomingConfig);

    /**
     * 设置呼叫页面配置
     *
     * @param waitingConfig 呼叫页面配置
     */
    public void setWaitingConfig(JCCWaitingConfig waitingConfig);

    /**
     * 设置通话页面配置
     *
     * @param talkingConfig 通话页面配置
     */
    public void setTalkingConfig(JCCTalkingConfig talkingConfig);
    
    /**
     * 设置音频播放来源
     * 音频播放来源 {@link JCCAudioSourceType}
     *
     * @param audioSourceType 音频播放来源
     */
    public void setAudioSourceType(@JCCAudioSourceType int audioSourceType);
}

# 呼叫页面配置

/**
 * 呼叫等待定制化配置参数
 */
public class JCCWaitingConfig {
    /**
     * 设置音频文件路径
     *
     * @param waitingAudioPath 呼叫时,需要播放的音频文件路径,不传默认不播放音乐
     */
    public void setWaitingAudioPath(String waitingAudioPath);
}

示例代码:

JCCGuestCustomConfig config = new JCCGuestCustomConfig();
JCCWaitingConfig waitingConfig = new JCCWaitingConfig();
waitingConfig.setWaitingAudioPath("path");
config.setWaitingConfig(waitingConfig);
GuestManager.getInstance().setCustomConfig(config);

# 来电页面配置

/**
 * 回呼场景:来电定制化配置参数
 */
public class JCCIncomingConfig {
    /**
     * 设置来电播放音乐路径
     *
     * @param incomingAudioPath 播放音乐路径
     */
    public void setIncomingAudioPath(String incomingAudioPath);
    /**
     * 设置接听超时,单位:秒,默认60秒
     *
     * @param answerTimeout 接听超时
     */
    public void setAnswerTimeout(int answerTimeout);
    /**
     * 设置是否使用自定义来电页面,默认为false
     *
     * @param useCustomIncoming 是否使用自定义来电页面
     */
    public void setUseCustomIncoming(boolean useCustomIncoming);
}

示例代码:

JCCGuestCustomConfig config = new JCCGuestCustomConfig();
JCCIncomingConfig incomingConfig = new JCCIncomingConfig();
incomingConfig.setIncomingAudioPath("path");
incomingConfig.setAnswerTimeout(60);
config.setIncomingConfig(incomingConfig);
GuestManager.getInstance().setCustomConfig(config);

# 通话页面配置

/**
 * 通话界面定制化配置参数
 */
public class JCCTalkingConfig {
    /**
     * 底部工具栏按钮枚举定义 JCCToolBarButtons
     */
    public enum JCCToolBarButtons {
        JCCEndButton,          // 挂断按钮
        JCCSpeakerButton,          // 扬声器按钮
        JCCMicButton,              // 麦克风按钮
        JCCCameraButton,           // 摄像头按钮
        JCCFlipButton,             // 翻转按钮
        JCCScreenShareButton,      // 屏幕共享按钮
        JCCChatButton,             // 文本消息按钮
        JCCLocalRecordButton,       // 本地录制按钮
        JCCCollaborationButton,   // 协作共享按钮
        JCCRemoteRecordButton     //远程录制
    }
    // 工具栏背景色
    public void setToolBarBackground(String toolBarBackground);
    // 顶部栏背景色
    public void setTopBarBackground(String topBarBackground);
    // 标题内容,默认显示通话中
    public void setTitle(String title);
    // JCToolbarButtons配置数组,默认4个按钮:JCCEndButton,JCCSpeakerButton,JCCMicButton,JCCCameraButton
    public void setToolBarButtons(JCCToolBarButtons[] toolBarButtons);
    //是否显示统计按钮,默认false
    public void setShowStatistic(boolean showStatistic);
    //是否显示最小化按钮,默认false
    public void setShowMinimized(boolean showMinimized);
    //自定义图层,需实现IBaseView协议,用来接收生命周期等事件,尺寸为全屏幕大小
    public void setCustomView(View customView);
    //是否使用第三方排队机,如果为true,则使用插件中排队机,默认为false
    public void setUseThirdACD(boolean useThirdACD);
    //是否显示签名取消按钮,默认false
    public void setShowCancelSign(boolean showCancelSign);
    //呼叫保持的音乐文件路径
    public void setHeldAudioPath(String heldAudioPath);
    //是否显示共享的画面,默认为true
    public boolean isShowShareCanvas();
    // 远端画笔颜色,默认#FFFF0000
    private String remotePaintColor = "#FFFF0000";
	public void setRemotePaintColor(String remotePaintColor);
    // 本端画笔颜色,默认#FF0E67FF
  	public void setLocalPaintColor(String localPaintColor);
    //电子签名是否全屏显示
    public void setSignFullScreen(boolean signFullScreen);
    //设置对讲模式小视频窗口显示宽度,单位 dp
    public void setPipSmallVideoWindowWidth(int pipSmallVideoWindowWidth);
    //设置对讲模式小视频窗口显示高度,单位 dp
    public void setPipSmallVideoWindowHeight(int pipSmallVideoWindowHeight);
    //设置演讲者模式小视频窗口显示宽度,单位 dp
    public void setSpeakSmallVideoWindowWidth(int speakSmallVideoWindowWidth);
    //设置演讲者模式小视频窗口显示高度,单位 dp
    public void setSpeakSmallVideoWindowHeight(int speakSmallVideoWindowHeight);
 // 设置签名画笔大小
    public void setDefaultDoodleStrokeWidth(int defaultDoodleStrokeWidth);
    // 设置关闭摄像头后小窗口展示图片
    public void setVideoHoldSmallIcon(Bitmap videoHoldSmallIcon);
    // 设置关闭摄像头后大窗口展示图片
    public void setVideoHoldLargeIcon(Bitmap videoHoldLargeIcon);
    // 设置关闭摄像头后窗口背景色
    public void setVideoHoldBackgroundColor(String videoHoldBackgroundColor);
}
/**
 * 自定义图层实现IBaseView接口,可监听通话界面生命同期
 */
public interface IBaseView {
    /**
     * onCreate
     */
    void onCreate();
    /**
     * onStart
     */
    void onStart();
    /**
     * onResume
     */
    void onResume();
    /**
     * onStop
     */
    void onStop();
    /**
     * onPause
     */
    void onPause();
    /**
     * onDestroy
     */
    void onDestroy();
}

示例代码:

JCCGuestCustomConfig config = new JCCGuestCustomConfig();
JCCTalkingConfig talkingConfig = new JCCTalkingConfig();
...
  talkingConfig.setShowMinimized(true);
...
config.setTalkingConfig(talkingConfig);
GuestManager.getInstance().setCustomConfig(config);

# 3. 在通话页面中添加自定义视图

自定义图层需要实现IBaseView协议。

/**
 * 自定义图层实现IBaseView接口,可监听通话界面的生命周期
 */
public class CustomLayout extends RelativeLayout implements IBaseView {
    private Context mContext;

    public CustomLayout(Context context) {
        super(context);
        initLayout(context);
    }

    public CustomLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initLayout(context);
    }

    public CustomLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initLayout(context);
    }

    private void initLayout(Context context) {
        this.mContext = context;
        View view = LayoutInflater.from(context).inflate(R.layout.custom_layout, this, true);
    }

    @Override
    public void onCreate() {

    }

    @Override
    public void onStart() {

    }

    @Override
    public void onResume() {

    }

    @Override
    public void onStop() {

    }

    @Override
    public void onPause() {

    }

    @Override
    public void onDestroy() {

    }
}

示例代码:

JCCGuestCustomConfig config = new JCCGuestCustomConfig();
JCCTalkingConfig talkingConfig = new JCCTalkingConfig();
CustomLayout customLayout = new CustomLayout(this);
talkingConfig.setCustomView(customLayout);
config.setTalkingConfig(talkingConfig);

# 4. 在通话过程中弹窗

# Activity 方式

全透明activity添加弹框布局加载显示

/****** 申明activity的theme *******/
    <style name="translucent">
        <item name="android:windowBackground">@color/translucent_background</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
    </style>
/****** activity引用theme *******/
	<activity
        android:name=".TestActivity"
        android:theme="@style/translucent"
        android:screenOrientation="portrait"></activity>
/****** 继承Activity *******/  
public class TestActivity extends Activity
/****** 直接启动activity *******/
startActivity(new Intent(context, TestActivity.class));
/********** 设置自定义 view **********/
JCCGuestCustomConfig customConfig = new JCCGuestCustomConfig();
CustomView customview = new CustomView(this);
customConfig.getTalkingConfig().setCustomView(customview);
JCCGuestManager.getInstance().setCustomConfig(customConfig);

/********** 弹窗 **********/
View contentView = LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout, null);
mPopWindow = new PopupWindow(contentView);
mPopWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
mPopWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
mPopWindow.showAtLocation(customview, 0, 0, 0);

# 悬浮窗方式

/********** Androidmanifest.xml 配置权限 **********/
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

/********** 请求权限 **********/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (!Settings.canDrawOverlays(this)) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, 1000);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case 1000: {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (Settings.canDrawOverlays(this)) {
                    //若用户开启了overlay权限,则打开window
                    openWindow();
                } else {
                    Toast.makeText(this, "不开启overlay权限", Toast.LENGTH_SHORT).show();
                }
            }
            break;
        }
    }
}

/********** 弹窗 **********/
//获取WindowManager实例
final WindowManager windowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
//获取窗口布局参数实例
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
//设置窗口布局参数属性
params.width = 100;
params.height = 100;
//设置window的显示特性
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
//设置窗口类型
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
//获取窗口布局实例
Button button = new Button(this);
button.setText("测试弹窗");
//显示窗口
windowManager.addView(button, params);

# 5. 对接第三方排队机

# 定制化配置中设置使用第三方排队机

使用第三方的排队机,需要在配置项中进行设置,屏蔽掉插件中提供的排队机。

JCCGuestCustomConfig config = new JCCGuestCustomConfig();
JCCTalkingConfig talkingConfig = new JCCTalkingConfig();
talkingConfig.setUseThirdACD(true);//设置使用第三方排队机
config.setTalkingConfig(talkingConfig);
JCCGuestManager.getInstance().setCustomConfig(config);

# 监听通话状态回调

上层通过监听通话状态的回调,呼叫时调用第三方排队机界面,接通时调用showTalkingView()方法启动通话界面,并销毁掉第三方排队机界面。

/**
 * @note 通话状态中通话中时,将第三方排队界面关闭,调用启动通话界面接口
 */
public void onCallStateChanged(@JRTCGuest.GuestCallStateChangeType int type, @JRTCGuest.CallIncomingType int incomingType, JRTCInviter inviter, @JRTCGuest.CallTermReason int reason) {
    switch(type){
        case JCGuest.GUEST_CHANGE_TYPE_CALLING:
            //调用第三方排队机界面
            break;
        case JCGuest.GUEST_CHANGE_TYPE_TALKING:
            //启动通话界面
			JCCGuestManager.getInstance().showTalkingView();
            break;
    }
}

# 6. 使用签名

上层通过消息或其他方式触发签名流程,签名后的图片会保存在设备中,签名成功,会将签名结果路径返回

# 调起签名页

/**
 * @note 通话界面调用
 */
JCCGuestManager.getInstance().showSignView();

# 获得签名结果

/**
 * 电子签名结果回调
 * 
 * @param result 电子签名是否成功
 * - true: 电子签名成功
 * - false: 电子签名失败,filePath为空
 * @param filePath 文件路径
 */
@Override
public void onSignRequestComplete(boolean result, String filePath) {

}

# 7.开启国密

# 示例代码

//帐户分录, 如果支持国密S3则需要设置certificate,否则不设置
String accountEntry = "AccountEntry:sarc -h arc@AccountEntry -p 198 -S 3;";
//S3国密密钥
String certificate = "";
//证书路径
File file = new File(Environment.getExternalStorageDirectory() + "/CA_gm_juphoon.der");
FileInputStream in = null;
	try {
		writeBytesToFile(getAssets().open("CA_gm_juphoon.der"), file);
		in = new FileInputStream(file);
        byte[] b = new byte[0];
        b = new byte[in.available()];
        int read = in.read(b);
        certificate = Base64.encodeToString(b, Base64.NO_WRAP);
	} catch (IOException e) {
		e.printStackTrace();
	}
JCCGuestConfig config = new JCCGuestConfig();
config.setCertificate(certificate);
config.setAccountEntry(accountEntry);
config.setServer("server");       //接入服务器的地址
config.setAppKey("appKey");  //appKey
config.setAppName("JCCGuest");//设置应用名
config.setUserName("test"); //用户id
config.setPassword("123456"); //用户密码
config.setNickName("昵称"); //设置用户昵称
...
JCCGuestManager.getInstance().login(context, config);

# 8. 开启云加速

  1. 从管理平台申请加速云接入,并获得加速云接入地址,接口key和接入key密钥
  2. 登录参数中需要将上述获得的参数填入
  3. 接入地址说明 (opens new window)

示例代码:

JCCGuestConfig config = new JCCGuestConfig();
...
config.setServer("加速云接入地址");
config.setAccelerateKey("加速云Key");
config.setAccelerateKeySecret("加速云Key密钥");
...
JCCGuestManager.getInstance().login(context, config);

# 9. 同屏共享

# 开启同屏共享

img

点击工具栏中的同屏共享按钮,在确认框中点击允许,开启同屏共享请求。

# 同屏共享录屏

img

在远端成员同意请求后,会弹出录屏的请求框, 允许录屏后就开始了同屏共享。

# 同屏共享工具

左上方的工具栏可以停止共享或者暂停共享。右边有个涂鸦按钮,点击可以开启涂鸦操作。

img

# 同屏共享接口调用

暂停恢复共享

/**
* 暂停/继续共享
* @note
* 只有自己发起的共享可以使用该接口暂停,多次调用会覆盖
* @param suspend true 暂停共享, false 继续共享
* @param tip 暂停共享后提示文字
* @return 接口调用结果
* - true: 接口调用成功, 会收到 {@link JRTCRoomCallback#onRoomPropertyChanged onRoomPropertyChanged} 回调,可通过{@link #isSuspendScreenShare isSuspendScreenShare} 判断当前屏幕共享是否暂停
* - false: 接口调用异常
*/
public abstract boolean suspendScreenShare(boolean suspend, String tip);

查询当前是否在共享中

/**
* 是否共享暂停
* @return
* - true: 暂停共享
* - false: 未暂停共享
*/
public abstract boolean isSuspendScreenShare();

# API 说明

详见 API文档 (opens new window)

# 1. 设置呼叫参数

/**
* 设置呼叫参数,在接听场景下会根据此配置来设置通话参数
*
* @note 需在接听前设置
*
* @param callParam 呼叫参数
*/
public void setCallParam(JCCGuestCallParam callParam)/**
* 设置呼叫参数,在接听场景下会根据此配置来设置通话参数
*
* @note 需在接听前设置
*
* @param callParam 呼叫参数
*/
public void setCallParam(JCCGuestCallParam callParam)

# 2. 启用通话页面

/**
* 启动通话界面
* 
* 在使用第三方排队机时通过此接口调起通话界面
* 当收到 onCallStateChanged 回调并且状态为 talking 时调用此接口
*/
public void showTalkingView()/**
* 启动通话界面
* 
* 在使用第三方排队机时通过此接口调起通话界面
* 当收到 onCallStateChanged 回调并且状态为 talking 时调用此接口
*/
public void showTalkingView()

# 3. 启动电子签名页面

/**
* 通话中唤起电子签名
* 
* 当签名完成后会收到 onSignRequestComplete 回调
*/
public void showSignView()/**
* 通话中唤起电子签名
* 
* 当签名完成后会收到 onSignRequestComplete 回调
*/
public void showSignView()

# 4. 发送在线消息

/**
* 发送在线消息
*
* @return 调用成功或失败
*/
public int sendOnlineMessage(String message, String userId)/**
* 发送在线消息
*
* @return 调用成功或失败
*/
public int sendOnlineMessage(String message, String userId)

# 5. 发送通话内消息

/**
* 透明通道发送消息给某个成员,toUserId传空则发送给所有成员,消息内容不能大于4K
* @param type     消息类型
* @param content  消息内容,不能大于4K
* @param toUserId 指定成员id
* @return 接口调用成功失败
* - true:接口调用成功
* - false:接口调用失败
*/
public boolean sendMessageInCall(String type, String content, String toUserId)/**
* 透明通道发送消息给某个成员,toUserId传空则发送给所有成员,消息内容不能大于4K
* @param type     消息类型
* @param content  消息内容,不能大于4K
* @param toUserId 指定成员id
* @return 接口调用成功失败
* - true:接口调用成功
* - false:接口调用失败
*/
public boolean sendMessageInCall(String type, String content, String toUserId)

# 6. 设置视频角度

/**
 * 设置视频窗体角度
 *
 * @param videoAngle 视频角度
 * @see com.juphoon.guest.plugin.JCCEnum.JCCMediaDeviceVideoAngle
 */
public void setVideoAngle(@JCCEnum.JCCMediaDeviceVideoAngle int videoAngle)/**
 * 设置视频窗体角度
 *
 * @param videoAngle 视频角度
 * @see com.juphoon.guest.plugin.JCCEnum.JCCMediaDeviceVideoAngle
 */
public void setVideoAngle(@JCCEnum.JCCMediaDeviceVideoAngle int videoAngle)

# 7. 结束通话

/**
* 结束通话
*
* @return 接口调用成功失败
* - true:接口调用成功
* - false:接口调用失败
*/
public boolean term()/**
* 结束通话
*
* @return 接口调用成功失败
* - true:接口调用成功
* - false:接口调用失败
*/
public boolean term()

# 8. 查询业务组号

    /**
     * 查询业务组号
     * @return 调用成功或失败
     */
    public boolean queryAllGroups()    /**
     * 查询业务组号
     * @return 调用成功或失败
     */
    public boolean queryAllGroups()

# 9. 接听通话

/**
 * 接听
 *
 * @return 接口调用成功失败
 * - true:接口调用成功
 * - false:接口调用失败
 */
public boolean answer()/**
 * 接听
 *
 * @return 接口调用成功失败
 * - true:接口调用成功
 * - false:接口调用失败
 */
public boolean answer()

# 10. 获取当前通话id

/**
 * 获取当前通话id
 */
public String getCallId()/**
 * 获取当前通话id
 */
public String getCallId()

# 11. 文件上传

/**
 * 上传文件
 * @note 如果上传视频文件,该接口可能会失败,该接口默认时长是 0
 *
 * @param callId   通话ID
 * @param fileType 文件类型
 * @param filePath 文件路径
 * @param fileName 文件名,请确保文件名全局唯一,相同文件名会互相覆盖
 * @see {@link JCCFileType}
 * @deprecated 该接口即将废弃,请使用 {@link #uploadFile(JCCUploadFileParam) uploadFile} 替换
 */
@Deprecated
void uploadFile(String callId, @JCCFileType int fileType, String filePath, String fileName);

/**
 * 上传文件
 *
 * @param param 文件上传信息
 * @see {@link JCCUploadFileParam}
 */
void uploadFile(JCCUploadFileParam param);/**
 * 上传文件
 * @note 如果上传视频文件,该接口可能会失败,该接口默认时长是 0
 *
 * @param callId   通话ID
 * @param fileType 文件类型
 * @param filePath 文件路径
 * @param fileName 文件名,请确保文件名全局唯一,相同文件名会互相覆盖
 * @see {@link JCCFileType}
 * @deprecated 该接口即将废弃,请使用 {@link #uploadFile(JCCUploadFileParam) uploadFile} 替换
 */
@Deprecated
void uploadFile(String callId, @JCCFileType int fileType, String filePath, String fileName);

/**
 * 上传文件
 *
 * @param param 文件上传信息
 * @see {@link JCCUploadFileParam}
 */
void uploadFile(JCCUploadFileParam param);

示例代码

String callId = JCCGuestManager.getInstance().getCallId();
JCCGuestManager.getInstance().uploadFile(callId, JCFileParams.FILE_TYPE_IMAGE, "filePath", "sign.jpg");String callId = JCCGuestManager.getInstance().getCallId();
JCCGuestManager.getInstance().uploadFile(callId, JCFileParams.FILE_TYPE_IMAGE, "filePath", "sign.jpg");

# 回调

# 1. 注册回调

/**
 * 注册回调
 */
public void addJCCGuestCallback(JCCGuestCallBack callback)

# 2. 取消回调

/**
 * 取消回调
 */
public void removeJCCGuestCallback(JCCGuestCallBack callback)

# 3. 登录结果回调

/**
 * 登录结果回调
 * @param result 登录结果
 * @param reason 失败原因
 */
public void onLogin(boolean result, @JRTCEnum.ReasonCode int reason);

# 4. 登出结果回调

/**
 * 登出结果回调
 * @param reason 登出原因
 */
public void onLogout(int reason);

# 5. 查询业务组号回调

/**
 * 查询业务组号回调
 * @param result 查询结果
 * @param groups 组号列表
 */
public void onGetAllGroups(boolean result, List<JCCGroupItem> groups);

# JCCGroupItem参数

/**
 * 业务号码
 */
public String callNumber;
/**
 * 技能组名
 */
public String groupName;
/**
 * 业务描述
 */
public String memo;

# 6. 通话状态改变回调

/**
 * 通话挂断原因回调
 * @param reason 通话状态改变类型
 * @param inviter 邀请成员
 * @param reason 通话挂断原因
 */
void onCallStateChanged(@JCCGuestCallStateChangeType int type, JCCInviter inviter,@JCCCallTermReason int reason);

# JCCInviter参数

/**
 * 邀请成员信息
 */
public class JCCInviter {
    /**
     * 用户ID
     */
    private String userId;
    /**
     * 用户昵称
     */
    private String displayName;
    /**
     * 邀请者业务号(主要用于坐席邀请访客)
     */
    private String businessNumber;
    /**
     * 扩展信息
     */
    private String extraInfo;

# 7. 电子签名结果回调

/**
 * 电子签名结果回调
 * 
 * @param result 电子签名是否成功
 * - true: 电子签名成功
 * - false: 电子签名失败,filePath为空
 * @param filePath 文件路径
 */
public void onSignRequestComplete(boolean result, String filePath);

# 8. 在线消息收发回调

/**
 * 发送在线消息结果
 *
 * @param result    发送结果是否成功
 * - true:发送成功
 * - false:发送失败
 * @param operatorId 操作 id
 */
void onOnlineMessageSendResult(boolean result, int operatorId);

/**
 * 收到在线消息
 *
 * @param message   消息内容
 * @param userId    对方 id
 */
void onOnlineMessageReceived(String message, String userId);

# 9. 通话中消息回调

/**
 * 接收通话中消息的回调
 *
 * @param type       消息类型
 * @param content    消息内容
 * @param fromUserId 消息发送成员的userId
 */
void onMessageInCallReceived(String type, String content, String fromUserId);

# 10. 上传文件结果回调

/**
 * 文件上传成功
 * @param fileName 文件名
 * @param response 参数
 */
void onUploadFileResult(String fileName,String response);

/**
 * 文件上传失败
 * @param fileName 文件名
 * @param e 错误原因
 */
void onUploadFileError(String fileName,Exception e);

# 枚举

# 1. 账号状态

/**
 * 账号状态
 */
@IntDef({STATE_NOT_INIT, STATE_IDLE, STATE_LOGINING, STATE_LOGINED, STATE_LOGOUTING})
@Retention(RetentionPolicy.SOURCE)
public @interface JCCClientState {
}

/**
 * 未初始化
 */
public static final int STATE_NOT_INIT = JCClient.STATE_NOT_INIT;
/**
 * 未登录
*/
public static final int STATE_IDLE = JCClient.STATE_IDLE;
/**
 * 登录中
*/
public static final int STATE_LOGINING = JCClient.STATE_LOGINING;
/**
 * 登录成功
 */
public static final int STATE_LOGINED = JCClient.STATE_LOGINED;
/**
 * 登出中
 */
public static final int STATE_LOGOUTING = JCClient.STATE_LOGOUTING;/**
 * 账号状态
 */
@IntDef({STATE_NOT_INIT, STATE_IDLE, STATE_LOGINING, STATE_LOGINED, STATE_LOGOUTING})
@Retention(RetentionPolicy.SOURCE)
public @interface JCCClientState {
}

/**
 * 未初始化
 */
public static final int STATE_NOT_INIT = JCClient.STATE_NOT_INIT;
/**
 * 未登录
*/
public static final int STATE_IDLE = JCClient.STATE_IDLE;
/**
 * 登录中
*/
public static final int STATE_LOGINING = JCClient.STATE_LOGINING;
/**
 * 登录成功
 */
public static final int STATE_LOGINED = JCClient.STATE_LOGINED;
/**
 * 登出中
 */
public static final int STATE_LOGOUTING = JCClient.STATE_LOGOUTING;

# 2. 登录登出原因

/**
 * 登录登出原因枚举
 */
@IntDef({REASON_NONE, REASON_OTHER, REASON_CALL_FUNCTION_ERROR, REASON_INVALID_PARAM, REASON_CLI_SDK_NOT_INIT, REASON_CLI_STATE_CANNOT_LOGIN,
	REASON_CLI_TIMEOUT, REASON_CLI_NETWORK, REASON_CLI_APP_KEY_ERROR, REASON_CLI_AUTH, REASON_CLI_NO_USER, REASON_CLI_SERVER_LOGOUT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface JCCReasonCode {
}
/**
 * 正常
 */
public static final int REASON_NONE = JRTCEnum.REASON_NONE;
/**
 * 其他错误
 */
public static final int REASON_OTHER = JRTCEnum.REASON_OTHER;
/**
 * 函数调用失败
 */
public static final int REASON_CALL_FUNCTION_ERROR = JRTCEnum.REASON_CALL_FUNCTION_ERROR;
/**
 * 参数错误
 */
public static final int REASON_INVALID_PARAM = JRTCEnum.REASON_INVALID_PARAM;
/**
 * sdk 未初始化
 */
public static final int REASON_CLI_SDK_NOT_INIT = JRTCEnum.REASON_CLI_SDK_NOT_INIT;
/**
 * 当前状态无法再次登录
 */
public static final int REASON_CLI_STATE_CANNOT_LOGIN = JRTCEnum.REASON_CLI_STATE_CANNOT_LOGIN;
/**
 * 超时
 */
public static final int REASON_CLI_TIMEOUT = JRTCEnum.REASON_CLI_TIMEOUT;
/**
 * 网络异常
 */
public static final int REASON_CLI_NETWORK = JRTCEnum.REASON_CLI_NETWORK;
/**
 * appKey 错误
 */
public static final int REASON_CLI_APP_KEY_ERROR = JRTCEnum.REASON_CLI_APP_KEY_ERROR;
/**
 * 账号密码错误
 */
public static final int REASON_CLI_AUTH = JRTCEnum.REASON_CLI_AUTH;
/**
 * 无该用户
 */
public static final int REASON_CLI_NO_USER = JRTCEnum.REASON_CLI_NO_USER;
/**
 * 强制登出
 */
public static final int REASON_CLI_SERVER_LOGOUT = JRTCEnum.REASON_CLI_SERVER_LOGOUT;/**
 * 登录登出原因枚举
 */
@IntDef({REASON_NONE, REASON_OTHER, REASON_CALL_FUNCTION_ERROR, REASON_INVALID_PARAM, REASON_CLI_SDK_NOT_INIT, REASON_CLI_STATE_CANNOT_LOGIN,
	REASON_CLI_TIMEOUT, REASON_CLI_NETWORK, REASON_CLI_APP_KEY_ERROR, REASON_CLI_AUTH, REASON_CLI_NO_USER, REASON_CLI_SERVER_LOGOUT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface JCCReasonCode {
}
/**
 * 正常
 */
public static final int REASON_NONE = JRTCEnum.REASON_NONE;
/**
 * 其他错误
 */
public static final int REASON_OTHER = JRTCEnum.REASON_OTHER;
/**
 * 函数调用失败
 */
public static final int REASON_CALL_FUNCTION_ERROR = JRTCEnum.REASON_CALL_FUNCTION_ERROR;
/**
 * 参数错误
 */
public static final int REASON_INVALID_PARAM = JRTCEnum.REASON_INVALID_PARAM;
/**
 * sdk 未初始化
 */
public static final int REASON_CLI_SDK_NOT_INIT = JRTCEnum.REASON_CLI_SDK_NOT_INIT;
/**
 * 当前状态无法再次登录
 */
public static final int REASON_CLI_STATE_CANNOT_LOGIN = JRTCEnum.REASON_CLI_STATE_CANNOT_LOGIN;
/**
 * 超时
 */
public static final int REASON_CLI_TIMEOUT = JRTCEnum.REASON_CLI_TIMEOUT;
/**
 * 网络异常
 */
public static final int REASON_CLI_NETWORK = JRTCEnum.REASON_CLI_NETWORK;
/**
 * appKey 错误
 */
public static final int REASON_CLI_APP_KEY_ERROR = JRTCEnum.REASON_CLI_APP_KEY_ERROR;
/**
 * 账号密码错误
 */
public static final int REASON_CLI_AUTH = JRTCEnum.REASON_CLI_AUTH;
/**
 * 无该用户
 */
public static final int REASON_CLI_NO_USER = JRTCEnum.REASON_CLI_NO_USER;
/**
 * 强制登出
 */
public static final int REASON_CLI_SERVER_LOGOUT = JRTCEnum.REASON_CLI_SERVER_LOGOUT;

# 3. 通话状态

/**
 * 通话状态
 */
@IntDef({CALL_STATE_IDLE, CALL_STATE_CALLING, CALL_STATE_WAITING, CALL_STATE_INCOMING, CALL_STATE_TALKING})
@Retention(RetentionPolicy.SOURCE)
public @interface CallState {
}

/**
 * 空闲状态
 */
public static final int CALL_STATE_IDLE = 0;
/**
 * 呼叫中
 */
public static final int CALL_STATE_CALLING = 1;
/**
 * 呼叫等待中
 */
public static final int CALL_STATE_WAITING = 2;
/**
 * 收到来电
 */
public static final int CALL_STATE_INCOMING = 3;
/**
 * 通话中
 */
public static final int CALL_STATE_TALKING = 4;/**
 * 通话状态
 */
@IntDef({CALL_STATE_IDLE, CALL_STATE_CALLING, CALL_STATE_WAITING, CALL_STATE_INCOMING, CALL_STATE_TALKING})
@Retention(RetentionPolicy.SOURCE)
public @interface CallState {
}

/**
 * 空闲状态
 */
public static final int CALL_STATE_IDLE = 0;
/**
 * 呼叫中
 */
public static final int CALL_STATE_CALLING = 1;
/**
 * 呼叫等待中
 */
public static final int CALL_STATE_WAITING = 2;
/**
 * 收到来电
 */
public static final int CALL_STATE_INCOMING = 3;
/**
 * 通话中
 */
public static final int CALL_STATE_TALKING = 4;

# 4. 通话结束原因

/**
* 通话结束的原因
*/
@IntDef({TERM_REASON_NONE, TERM_REASON_QUIT, TERM_REASON_OVER, TERM_REASON_OFFLINE, TERM_REASON_INCOMING_CANCEL, TERM_REASON_INCOMING_TIMEOUT, TERM_REASON_TIME_OUT,
	TERM_REASON_INVALID_PARAM, TERM_REASON_CALL_FUNCTION_ERROR, TERM_REASON_NOT_LOGIN, TERM_REASON_OTHER,TERM_REASON_ANSWER_TIMEOUT})
@Retention(RetentionPolicy.SOURCE)
public @interface JCCTermReason {
}
/**
* 无错误
*/
public static final int TERM_REASON_NONE = 0;
/**
* 本端结束通话
*/
public static final int TERM_REASON_QUIT = 1;
/**
* 对端结束通话
*/
public static final int TERM_REASON_OVER = 2;
/**
* 网络异常导致本端结束通话
*/
public static final int TERM_REASON_OFFLINE = 3;
/**
* 呼叫来电取消
*/
public static final int TERM_REASON_INCOMING_CANCEL = 4;
/**
* 呼叫来电超时
*/
public static final int TERM_REASON_INCOMING_TIMEOUT = 5;
/**
* 访客心跳断
*/
public static final int TERM_REASON_TIME_OUT = 6;
/**
* 参数错误
*/
public static final int TERM_REASON_INVALID_PARAM = 97;
/**
* 函数调用失败
*/
public static final int TERM_REASON_CALL_FUNCTION_ERROR = 98;
/**
* 用户未登录
*/
public static final int TERM_REASON_NOT_LOGIN = 99;
/**
 * 其他原因
*/
public static final int TERM_REASON_OTHER = 100;
/**
* 接听超时
*/
public static final int TERM_REASON_ANSWER_TIMEOUT = 101;/**
* 通话结束的原因
*/
@IntDef({TERM_REASON_NONE, TERM_REASON_QUIT, TERM_REASON_OVER, TERM_REASON_OFFLINE, TERM_REASON_INCOMING_CANCEL, TERM_REASON_INCOMING_TIMEOUT, TERM_REASON_TIME_OUT,
	TERM_REASON_INVALID_PARAM, TERM_REASON_CALL_FUNCTION_ERROR, TERM_REASON_NOT_LOGIN, TERM_REASON_OTHER,TERM_REASON_ANSWER_TIMEOUT})
@Retention(RetentionPolicy.SOURCE)
public @interface JCCTermReason {
}
/**
* 无错误
*/
public static final int TERM_REASON_NONE = 0;
/**
* 本端结束通话
*/
public static final int TERM_REASON_QUIT = 1;
/**
* 对端结束通话
*/
public static final int TERM_REASON_OVER = 2;
/**
* 网络异常导致本端结束通话
*/
public static final int TERM_REASON_OFFLINE = 3;
/**
* 呼叫来电取消
*/
public static final int TERM_REASON_INCOMING_CANCEL = 4;
/**
* 呼叫来电超时
*/
public static final int TERM_REASON_INCOMING_TIMEOUT = 5;
/**
* 访客心跳断
*/
public static final int TERM_REASON_TIME_OUT = 6;
/**
* 参数错误
*/
public static final int TERM_REASON_INVALID_PARAM = 97;
/**
* 函数调用失败
*/
public static final int TERM_REASON_CALL_FUNCTION_ERROR = 98;
/**
* 用户未登录
*/
public static final int TERM_REASON_NOT_LOGIN = 99;
/**
 * 其他原因
*/
public static final int TERM_REASON_OTHER = 100;
/**
* 接听超时
*/
public static final int TERM_REASON_ANSWER_TIMEOUT = 101;

# 5. 加密方式

/**
 * 入会加密方式
 */
@IntDef({SECURITY_TYPE_DISABLE,SECURITY_TYPE_SRTP,SECURITY_TYPE_SM4})
@Retention(RetentionPolicy.SOURCE)
public @interface JCCSecurityType {
}

/**
 * 不加密
 */
public static final int SECURITY_TYPE_DISABLE = JRTCRoom.SECURITY_TYPE_DISABLE;
/**
 * SRTP
 */
public static final int SECURITY_TYPE_SRTP = JRTCRoom.SECURITY_TYPE_SRTP;
/**
 * SM4
*/
public static final int SECURITY_TYPE_SM4 = JRTCRoom.SECURITY_TYPE_SM4;/**
 * 入会加密方式
 */
@IntDef({SECURITY_TYPE_DISABLE,SECURITY_TYPE_SRTP,SECURITY_TYPE_SM4})
@Retention(RetentionPolicy.SOURCE)
public @interface JCCSecurityType {
}

/**
 * 不加密
 */
public static final int SECURITY_TYPE_DISABLE = JRTCRoom.SECURITY_TYPE_DISABLE;
/**
 * SRTP
 */
public static final int SECURITY_TYPE_SRTP = JRTCRoom.SECURITY_TYPE_SRTP;
/**
 * SM4
*/
public static final int SECURITY_TYPE_SM4 = JRTCRoom.SECURITY_TYPE_SM4;

# 6. 视频旋转角度

/**
 * 视频旋转角度
 */
@IntDef({VIDEO_ANGLE_AUDO,VIDEO_ANGLE_0,VIDEO_ANGLE_90,VIDEO_ANGLE_180,VIDEO_ANGLE_270})
@Retention(RetentionPolicy.SOURCE)
public @interface JCCMediaDeviceVideoAngle {
}

/**
 * 自动
 */
public static final int VIDEO_ANGLE_AUDO = -1;
/**
 * 0°
 */
public static final int VIDEO_ANGLE_0 = 0;
/**
 * 90°
 */
public static final int VIDEO_ANGLE_90 = 90;
/**
 * 180°
 */
public static final int VIDEO_ANGLE_180 = 180;
/**
 * 270°
 */
public static final int VIDEO_ANGLE_270 = 270;