# PlayerSDK 使用文档

[TOC]

## 一、概述

### SDK 介绍
PlayerSDK 是专门为鸿蒙平台设计的音视频直播播放 SDK，用于实现展动平台直播内容的播放。适用于需要集成直播功能的各类鸿蒙应用，如在线教育、直播电商等场景。其主要功能包括视频播放、文档显示、聊天互动、问答交流、投票调查、文件管理、用户管理及其他交互功能。

### 特性列表
- **视频播放**：流畅播放展动平台的直播视频流。
- **文档显示**：支持在直播过程中展示文档资料。
- **聊天互动**：提供实时聊天功能，方便用户交流。
- **问答交流**：实现用户提问和解答的交互。
- **投票调查**：支持发起和参与投票活动。
- **文件管理**：可进行文件的下载和管理。
- **用户管理**：对用户信息进行管理和操作。
- **其他交互**：包含如签到等互动功能。

### 支持的环境
- **操作系统**：HarmonyOS
- **编程语言**：ArkTS
- **最低版本要求**：5.0.1(13)
- **依赖项**：
  - `"@ohos/axios": "2.2.6"` 需要添加到根目录或者是sdk同级目录的`oh-package.json5`中

-  **依赖方式**：

   将@gensee/playersdk.har放到lib目录或者其他目录下，通过file:路径/genseesdk.har进行依赖。

  ```ts
    "dependencies": {
      "@gensee/playersdk": "file:libs/genseesdk.har"
    }
  ```

## 二、快速开始

### 1. 引入 SDK
确保在项目中正确引入 `@gensee/playersdk` 模块，可通过如下方式引入：

```typescript
import { PlayerSDK } from '@gensee/playersdk'
```

### 2. 初始化 SDK
在使用 PlayerSDK 之前，需要先进行初始化操作：

```ts
const playersdk = new PlayerSDK()
playersdk.init()
```

### 3. 输入参数加入直播
```typescript
// 创建初始化参数对象
let initParam = new InitParam()
// 设置域名或ip(必填)
initParam.domain = this.liveDomin
// 设置直播 ID（必填)
initParam.number = this.liveId
// 设置昵称（必填)
initParam.nickName = this.nickname
// 设置加入密码
initParam.joinPwd = this.passcode
// 设置分组号
initParam.groupCode = this.groupCode
// 设置traning或webcast
initParam.serviceType = this.serviceType
// 设置自定义用户ID
initParam.userId = this.userId
// 设置站点登录账号
initParam.loginAccount = this.userAccountName
// 设置站点登录密码
initParam.loginPwd = this.userAccountPW
// 设置认证 K 值
initParam.k = this.kValue
// 设置用户自定义参数
initParam.userData = this.userExtraData

// 使用初始化参数进行认证并加入直播
playersdk.initWithParam(initParam).then((result) => {
    // 认证成功，处理返回结果
    showToast("获取成功")
    // 然后可以通过joinLive加入到直播
    playersdk.joinLive(getContext(), result)
}).catch((e: BusinessError | GSError) => {
    // 处理认证失败的情况
    if (e instanceof GSError) {
        showToast(e.errMsg)
    } else {
        showToast("未知错误：" + e.message)
    }
})
```
接下来可通过`playersdk.setPlayerListener(this.livePlayerListener)`中的`onJoin`回调判断是否加入成功

``` typescript
  private livePlayerListener:LivePlayerListener = {
    onJoin: (result: JoinResultCode): void => {
      if (result == JoinResultCode.JOIN_OK) {
        showToast("加入成功")
        this.isBuffing = false

      } else {
        if (result == JoinResultCode.JOIN_TOO_EARLY) {
          showToast("加入失败：直播间还未开始")
        } else if (result == JoinResultCode.JOIN_CONNECT_FAILED) {
          showToast("加入失败：连接失败")
        } else if (result == JoinResultCode.JOIN_CONNECT_TIMEOUT) {
          showToast("加入失败：连接超时")
        } else if (result == JoinResultCode.JOIN_RTMP_FAILED) {
          showToast("加入失败：连接服务器失败")
        } else if (result == JoinResultCode.JOIN_LICENSE) {
          showToast("加入失败：直播间人数已满")
        } else if (result == JoinResultCode.JOIN_IP_FORBIDDEN) {
          showToast("加入失败：IP被封禁")
        } else {
          showToast("加入失败："+result)
        }

      }
    },
    //...

  } as LivePlayerListener
```

## 三、各个模块及 API 使用

### PlayerSDK 主功能

#### 1. 初始化
在使用 PlayerSDK 前，需要先进行初始化操作。

```typescript
// 创建 PlayerSDK 实例
const playersdk = new PlayerSDK();
// 调用 init 方法初始化
playersdk.init();
```

#### 2. 开始认证
使用 `initWithParam` 方法开始认证，传入初始化参数 `InitParam`，并处理认证结果。

```typescript
// 创建初始化参数对象
let initParam = new InitParam();
initParam.domain = "your_domain";
initParam.number = "your_live_id";
initParam.nickName = "your_nickname";
// 其他参数设置...

playersdk.initWithParam(initParam).then((result) => {
    // 认证成功，处理返回结果
    console.log("认证成功，直播间信息：", result);
    // 可以在这里进行后续操作，如显示对话框提示进入直播等
}).catch((e) => {
    // 处理认证失败的情况
    if (e instanceof GSError) {
        console.error("认证失败，错误信息：", e.errMsg);
    } else {
        console.error("未知错误：", e.message);
    }
});
```

#### 3. 加入直播间
调用 `joinLive` 方法加入直播间，需要传入上下文 `context` 和直播间参数信息 `liveAttributes`。

```typescript
playersdk.joinLive(context, liveAttributes)
```

#### 4. 接受连麦邀请
当收到 `onInvite` 消息后，使用 `inviteAck` 方法接受或拒绝本次连麦邀请，但需要注意在调用前确保应用有对应的麦克风和摄像头权限。
```typescript
// 假设本次邀请连麦类型为视频连麦，接受邀请
playersdk.inviteAck(InviteType.VIDEO, true);
```


#### 5. 设置服务器端口
如果因网络安全等要求，需要设置服务器端口，可使用 `setServerPort` 方法，此操作要在加入直播前进行。正常使用场景不需要进行设置。
```typescript
// 设置 HTTP 端口和 WebSocket 端口
playersdk.setServerPort(port1, port2);
```

#### 6. 获取当前登录用户信息
使用 `getSelfUserInfo` 方法获取当前登录的用户信息，若返回为空则表示未登录成功。

```typescript
const userInfo = playersdk.getSelfUserInfo();
if (userInfo) {
    console.log("当前登录用户信息：", userInfo);
} else {
    console.log("未登录成功");
}
```

#### 7. 设置回调监听
通过 `setPlayerListener` 方法设置 `LivePlayerListener` 回调监听，以处理直播间的各种状态变化。

```typescript
const livePlayerListener = {
    onJoin: (result) => {
        if (result === JoinResultCode.JOIN_OK) {
            console.log('加入直播间成功');
        } else {
            console.log('加入直播间失败');
        }
    },
    onReconnecting: () => {
        console.log('因网络原因开始重新连接服务器');
    },
    onLeave: (reason) => {
        console.log('退出直播间，原因：', reason);
    },
    // 实现其他回调方法...
};

playersdk.setPlayerListener(livePlayerListener);

```

#### 8. 离开直播间
当用户想退出直播间时，可以主动调用 `leaveLive`离开直播间
```ts
playersdk.leaveLive()
```

#### 9. 销毁SDK
不用SDK时，需主动释放SDK所占用的资源，需要在每次退出直播的时候进行清理。清理后如果再次进入直播，不需要重新创建PlayerSDK对象，直接复用即可。
```ts
playersdk.release()
```


### 视频播放
#### 视频播放概述
视频播放我们提供了一个UI控件 `VideoView`,直接将其添加到UI中，当有视频桢时即可自动绘制显示

#### 实例代码
```typescript
VideoView()
    .alignRules({
        top: { anchor: 'title', align: VerticalAlign.Bottom },
        middle: { anchor: '__container__', align: HorizontalAlign.Center }
    })
    .id("Player")
    .width('100%')
    .height('500lpx')
```
### 文档

#### 文档模块概述
为了方便客户使用，我们直接封装了一个 `DocView` 用于显示文档，在创建 `DocView` 的时候需要传入一个 `DocManager` 对象，并且这个对象需要一并设置给PlayerSDK，这样即可实现文档的显示。

#### 相关 API 使用

##### 绑定文档管理器
通过 `playersdk.bindDocManager` 方法将文档管理器与 `playersdk` 进行绑定，确保文档能够正常显示。

```typescript
private docManager = new DocManager()

playersdk.bindDocManager(this.docManager)
```
##### 显示文档视图
在UI组件中，使用 `DocView` 组件来显示文档。

```typescript
TabContent() { // 父控件随意，这里只是演示
  DocView({manager:this.docManager})
    .width('100%')
    .height('100%')
}
.tabBar('文档')
```
**参数说明**：
- `manager`：`DocManager` 类型的实例，用于管理文档的显示和切换。

### 连麦

#### 概述
PlayerSDK 支持视频与语音连麦功能，支持老师与学员之间的互动、提问。 SDK本身是无法主动发起连麦的，观看端只能被动接收连麦请求后同意或拒绝连麦。

用户可在接收到连麦邀请后选择是否接受，同意前需确保应用有对应权限，SDK 内部会自动处理麦克风和摄像头的开关。

#### 相关 API 使用
#### 连麦类型
```ts
export enum InviteType{
  AUDIO = 1, // 音频
  VIDEO = 2, // 视频
  MULTI = 3 // 混合(音频+视频)
}
```

##### 连麦请求通知
连麦请求通知是通过 `LivePlayerListener` 的 `onInvite()`回调中通知过来
```ts
private livePlayerListener:LivePlayerListener = {
  onInvite: (type: InviteType, isOpen: boolean): void => {
    // 收到连麦消息，邀请也可能是拒绝
  },
} as LivePlayerListener
```
##### 接受、拒绝连麦
通过`playersdk.inviteAck()`方法处理连麦请求，示例如下
```ts
// 接受连麦
playersdk.inviteAck(InviteType.MULTI,true) 
```
**注意事项：在应用进入后台的时候，必须结束当前的连麦，鸿蒙系统不允许后台应用使用麦克风，否则会产生崩溃。
在检测到应用进入后台后，需主动调用 `plyaersdk.inviteAck(InviteType.MULTI,false)` 结束全部连麦并关闭麦克风占用。**

这里附一个相对完整的连麦邀请处理逻辑示例代码：
```ts
private livePlayerListener:LivePlayerListener = {
  onInvite: (type: InviteType, isOpen: boolean): void => {
      if (isOpen) {
        if (type == InviteType.AUDIO) {
          this.inviteTypeStr = "音频"
        } else if (type == InviteType.VIDEO) {
          this.inviteTypeStr = "视频"
        } else {
          this.inviteTypeStr = "音频/视频"
        }
        AlertDialog.show(
          {
            title: this.inviteTypeStr+'连麦邀请',
            message: '老师邀请你参与连麦',
            autoCancel: true,
            alignment: DialogAlignment.Bottom,
            gridCount: 4,
            offset: { dx: 0, dy: -20 },
            primaryButton: {
              value: '取消',
              action: () => {
                // 拒绝连麦
                playersdk.inviteAck(type,false)
                this.inviteType = playersdk.getCurrInviteType()
              }
            },
            secondaryButton: {
              enabled: true,
              defaultFocus: true,
              style: DialogButtonStyle.HIGHLIGHT,
              value: '确认',
              action: async () => {
                try {
                  // 检测并请求权限
                  await reqPermissionsFromUser(["ohos.permission.MICROPHONE","ohos.permission.CAMERA"],getContext(this) as common.UIAbilityContext)
                  playersdk.inviteAck(type,true)
                } catch (e) {
                  // 没有权限拒绝连麦
                  playersdk.inviteAck(type,false)
                  showToast("没有获取到相应权限，请到设置中开启权限")
                }
                this.inviteType = playersdk.getCurrInviteType()
                console.info(TAG,"inviteType" + this.inviteType)

              }
            }
          }
        )
      } else {
        this.inviteType = playersdk.getCurrInviteType()
        if (this.inviteType == undefined) {
          showToast("已关闭连麦")
        } else {
          showToast("已关闭一路连麦")
        }
      }
    },
} as LivePlayerListener
```

### 聊天模块

#### 概述
聊天模块提供了在直播中进行私聊和公聊的功能，支持发送纯文本和富文本内容，并且可以设置聊天相关的回调监听。

#### 相关 API 使用

##### 获取聊天 API 实例
在使用聊天功能前，需要先获取 `LiveChatApi` 的实例。该实例需要通过 `playersdk` 主类获取聊天 API 对象：

```typescript
const liveChatApi = playersdk.getChatApi();
```

##### 发送公聊消息
可以使用 `chatToPublic` 方法发送公聊消息，该方法提供了较强的自定义属性的能力，但使用较为繁琐。示例如下：

```typescript
import { ChatMsg } from '@gensee/playersdk';

const chatMsg = new ChatMsg();
chatMsg.content = '这是一条公聊消息';
chatMsg.richText = richText // 是ChatSpan[]类型，用来区分表情和文本
chatMsg.senderId = this.selfUserInfo!.id
chatMsg.sender = this.selfUserInfo!.name
chatMsg.senderRole = this.selfUserInfo!.role
chatMsg.chatId = this.selfUserInfo!.chatId
chatMsg.id = uuid

liveChatApi.chatToPublic(chatMsg);
```
**该方法需要自行构建 `ChatMsg` 类，尤其是需要自行组装 `richText` ，还需自行设置发送人的信息，较为复杂，如果是只是简单的想发送纯文本信息，那么更推荐使用 `sendContent` 方法，使用方式见下文。
如果是需要发送带有表情的消息，那么推荐使用 `sendRichTextSpans` 方法也更为简单。**

##### 发送私聊消息
使用 `chatToPerson` 方法发送私聊消息，示例如下：

```typescript
import { PrivateChatMsg } from '@gensee/playersdk';

const privateChatMsg = new PrivateChatMsg();
// 设置消息内容等通用属性，与公聊一致
privateChatMsg.content = '这是一条私聊消息';
privateChatMsg.richText = richText // 是ChatSpan[]类型，用来区分表情和文本
privateChatMsg.senderId = this.selfUserInfo!.id
privateChatMsg.sender = this.selfUserInfo!.name
privateChatMsg.senderRole = this.selfUserInfo!.role
privateChatMsg.chatId = this.selfUserInfo!.chatId
// 设置接收用户信息
chatMsg.chatId = receiveUser.chatId
chatMsg.targetName = receiveUser.name

liveChatApi.chatToPerson(privateChatMsg);
```

##### 发送纯文本消息
为了方便客户使用，现在提供了 `sendContent` 方法发送纯文本消息，减少一些通用参数的设置。
该方法可指定 `receiveUser` 用于设置私聊对象 ，若不指定 `receiveUser` 则为公聊，示例如下：

```typescript
// 公聊纯文本消息
liveChatApi.sendContent('这是一条公聊纯文本消息');

// 私聊纯文本消息
import { UserInfo } from '@gensee/playersdk';
const receiveUser: UserInfo = this.targetUser;
liveChatApi.sendContent('这是一条私聊纯文本消息', receiveUser);
```

##### 发送富文本消息
使用 `sendRichTextSpans` 方法发送表情和文本混合消息，其示例如下：

```typescript
import { ChatSpan } from '@gensee/playersdk';

const richTextSpans: ChatSpan[] = [
  new ImgSpan('emotion/emotion.smile.gif'),// 参数必须为EmojiData中的model数据，否则其他端无法显示。
  new TextSpan("", '',"你好") // 第一个参数为字体颜色，第二个参数是字体大小，如果需要特殊指定填入需求的大小和颜色即可
];

// 公聊富文本消息
liveChatApi.sendRichTextSpans(richTextSpans);

// 私聊富文本消息
const receiveUser: UserInfo = this.targetUser;
liveChatApi.sendRichTextSpans(richTextSpans, receiveUser);
```
这里提供一个发送表情和文本的简单示例：

首先推荐使用鸿蒙提供的 `RichEditor` 这个控件来接受和显示输入的表情和文本数据

```ts
controllerRich: RichEditorController = new RichEditorController();

// 输入框
RichEditor({ controller: this.controllerRich })
    .height($r('app.integer.chat_with_expression_chat_input_height'))
    .layoutWeight(1)
    .margin({left:$r('app.integer.chat_with_expression_express_margin_left')})
    .borderRadius($r('app.integer.chat_with_expression_chat_border_radius'))
    .backgroundColor($r('app.string.chat_with_expression_input_background'))
    .key(this.focusKey)
    .id(this.focusKey)
    .defaultFocus(false)
    .onClick(async () => {
        this.isFaceDlgOpen = false;
        this.isFaceClick = false;
    })
```
在点击表情的时候通过 `controllerRich` 添加表情数据

```ts
// 将表情添加到输入框中
this.controllerRich!.addImageSpan($rawfile(this.EmojiItem?.imgSrc), {
    imageStyle: {
    size: [this.msgFontSize / 3.2, this.msgFontSize / 3.2], // 3.2 调整表情在输入框中的尺寸
    verticalAlign: ImageSpanAlignment.CENTER,
    layoutStyle: { margin: FaceGridConstants.EMOJI_MARGIN }
    }
});
```
最后在点击发送按钮的时候通过 `controllerRich` 获取输入的表情和文字，组装 `ChatSpan[]`

```ts
  async sendChatMsg(): Promise<void> {
    let msgBase:ChatSpan[] = []
    // 获取发送信息
    this.controllerRich.getSpans().forEach(item => {
      if (typeof (item as RichEditorImageSpanResult)['imageStyle'] !== 'undefined') {
        // 处理imagespan信息
        const imageMsg: ResourceStr | undefined = (item as RichEditorImageSpanResult).valueResourceStr;
        if (imageMsg !== undefined) {
          const spanItem: ImgSpan = new ImgSpan(imageMsg.toString().replace("resource://RAWFILE/",""));
          msgBase.push(spanItem);
        }
      } else {
        // 处理文字span信息
        const textMsg: string = (item as RichEditorTextSpanResult).value;
        const spanItem: TextSpan = new TextSpan("", '',textMsg);
        msgBase.push(spanItem);
      }
    })

    if (msgBase.length > 0) {
      this.controllerRich.deleteSpans();
      this.controllerRich.setCaretOffset(-1);
      if (this.privateChatUser) {
        // 发送给指定人
        playersdk.getChatApi().sendRichTextSpans(msgBase,this.privateChatUser)
      } else {
        // 发送给所有人
        playersdk.getChatApi().sendRichTextSpans(msgBase)
      }
    }

    this.scroller.scrollEdge(Edge.Bottom);
  }

```
具体示例可Demo项目代码。

##### 设置聊天接收回调监听
通过 `setChatListener` 方法设置聊天相关的回调监听，示例如下：

```typescript
import { ChatListener } from '@gensee/playersdk';

const chatListener: ChatListener = {
  // 收到公聊消息的通知
  onChatWithPublic: (msg:ChatMsg) => {
    console.log('收到聊天消息:', msg.content);
  },
  // ...
};

liveChatApi.setChatListener(chatListener);
```
`ChatListener` 类具体方法及解释见 四、回调说明模块。

##### 消息的显示
收到消息后，优先推荐显示的 `ChatMsg` 中的 `richTextSpans` 内容，其中包括了表情和文本，及文本的颜色和大小，可根据项目需求自由设置。示例如下：

```ts
Text(){
    ForEach(item.richTextSpans, (item: ChatSpan) => {
        // 分别使用ImageSpan、Span渲染图片、文字信息
        if (item instanceof ImgSpan) {
        ImageSpan($rawfile(item.src))
            .width('36lpx')
            .height('36lpx')
            .verticalAlign(ImageSpanAlignment.BOTTOM).objectFit(ImageFit.Cover)
        } else if (item instanceof TextSpan) {
        Span(item.text)
            .fontSize(item.fontSize)
            .fontColor(item.color)
        }
    })
}
```


### 问答模块

#### 概述
问答模块允许用户在直播过程中进行提问和同问操作，通过设置问答消息回调监听，以便及时处理问答的添加、更新、取消等事件。

#### 相关类和接口

##### `LiveQAApi` 接口
`LiveQAApi` 定义了问答模块的主要操作接口，包括设置回调监听、提问和同问功能。

```typescript
import { QAListener } from "../../callback/QAListener";

export interface LiveQAApi{
  /**
   * 设置问答消息回调监听
   * @param qaListener
   */
  setQAListener(qaListener:QAListener):void

  /**
   * 提问
   * @param question 问题内容
   */
  question(question:string):void

  /**
   * 同问（当前小班课功能，后续可能全产品覆盖）,server 要在5.2版本及以上支持
   * @param questionUUid 同问问题的问题id，也就是对应问答的id（UUID）
   */
  sameQuestion(questionUUid:string):void
}
```


#### API 使用示例

##### 获取问答模块 API
```typescript
const liveQAApi = playersdk.getQAApi();
```

##### 设置问答消息回调监听
```typescript
const qaListener: QAListener = {
  onQa: (qaMsg: QaMsg): void => {
    // 处理问答的添加或更新
    console.log('收到问答更新:', qaMsg);
  },
  //...
};

liveQAApi.setQAListener(qaListener);
```

##### 提问
```typescript
const questionContent = "这是一个问题";
liveQAApi.question(questionContent);
```

##### 同问
```typescript
const questionUUid = "12345678-1234-5678-1234-567812345678";
liveQAApi.sameQuestion(questionUUid);
```


### 投票模块

#### 概述
投票模块允许在直播或其他场景中发起投票活动，用户可以参与投票并提交答案。该模块提供了投票信息的解析、投票监听以及投票提交等功能。

#### API 使用示例


##### `LiveVoteApi` 接口
定义了投票模块的主要操作接口，包括设置投票监听和提交投票答案。

```typescript
export interface LiveVoteApi {
  /**
   * 设置投票回调监听
   * @param listener
   */
  setVoteListener(listener: VoteListener): void;

  /**
   * 提交答题卡
   * @param voteSheet
   */
  submitVote(voteSheet: VoteSheet): void;
}
```

##### 设置投票监听
在使用投票功能之前，需要设置投票监听，以便处理投票相关的事件。

```typescript
import { VoteListener } from "@gensee/playersdk";
import { playersdk } from "../pages/Index";

const voteListener: VoteListener = {
  onVotePublish: (vote: BaseVote): void => {
    // 处理投票发布事件
    console.log("收到投票发布通知", vote);
  },
  onVotePublishResult: (vote: BaseVote): void => {
    // 处理投票结果公布事件
    console.log("收到投票结果通知", vote);
  },
  onVoteNotifyFinished: (voteId: string): void => {
    // 处理投票结束事件
    console.log("投票结束，投票 ID：", voteId);
  },
  onThirdVote: (url: string): void => {
    // 处理第三方投票链接回调
    console.log("收到第三方投票链接：", url);
  },
};

playersdk.getVoteApi().setVoteListener(voteListener);
```

##### 参与投票
当收到投票发布通知后，用户可以参与投票并提交答案。

```typescript
import { VoteSheet } from "@gensee/playersdk";
import { BaseVote, QuestionType } from "@gensee/playersdk";

// 假设 vote 是收到的投票信息
const vote: BaseVote = ...;
// 这里需要传入要作答的投票信息
const voteSheet = new VoteSheet(vote);

// 设置单选题答案
voteSheet.setAnswer("questionId1", "answerId1");

// 设置多选题答案
voteSheet.setAnswer("questionId2", "answerId2");
voteSheet.setAnswer("questionId2", "answerId3");

// 设置文本题答案
voteSheet.setAnswer("questionId3", "这是文本题的答案");

// 提交投票答案
playersdk.getVoteApi().submitVote(voteSheet);
```

### 用户管理模块

#### 概述
用户管理模块主要负责对直播中的用户信息进行管理，包括设置用户相关回调监听、通过 ID 查询用户信息以及用户重命名等功能。

#### 接口定义
`LiveUserApi` 接口定义了用户管理模块对外暴露的 API，具体如下：

```typescript
export interface LiveUserApi{
  /**
   * 设置用户相关的回调监听
   * @param listener
   */
  setUserListener(listener:UserListener):void

  /**
   * 通过id查询完整的用户信息，结果会在UserListener中的onGetUserInfo回调
   * @param ids
   */
  getUserById(ids:number[]):void

  /**
   * 自己重命名
   * @param newName 新名字
   */
  reName(newName:string):void
}
```

#### API 详细说明

##### 设置用户相关的回调监听
```typescript
import { UserListener } from "@gensee/playersdk";
import { UserInfo } from "@gensee/playersdk";

const userListener: UserListener = {
  onUserJoin: (info: UserInfo): void => {
    // 处理新用户加入事件
  },
  // 其他回调...
};

playersdk.getUserApi().setUserListener(userListener);
```

##### 通过 ID 查询完整的用户信息
```typescript
const userIds = [10001, 10002, 10003];
playersdk.getUserApi().getUserById(userIds);
```
- 查询结果会通过 `UserListener` 中的 `onGetUserInfo` 方法回调，开发者需要在 `setUserListener` 中实现该方法来处理查询结果。

##### 重命名
```typescript

const newName = "NewUserName";
playersdk.getUserApi().reName(newName);
```
如果 `newName` 为空字符串，则该方法不会执行任何操作。

### 文件模块

#### 概述
文件模块提供了文件消息通知回调的设置以及文件下载的功能。

#### 接口定义
```typescript
export interface LiveFileApi {
  /**
   * 设置文件消息的通知回调
   * @param listener
   */
  setFileListener(listener: FileListener): void;

  /**
   * 开始下载文件
   * @param url 需要下载的文件地址
   * @param fullFilePath 手机储存位置
   */
  downloadFile(url: string, fullFilePath: string): void;
}
```

#### API 详细说明

##### 设置文件通知回调
```typescript
import { FileListener } from "genseesdk/src/main/ets/gensee/callback/FileListener";

const fileListener: FileListener = {
  onFileShare: (wCmdType: number, strFileName: string, strFileUrl: string) => {
    // 处理文件共享事件
    console.log(`文件共享: 类型 ${wCmdType}, 文件名 ${strFileName}, 下载地址 ${strFileUrl}`);
  },
  onFileShareDl: (bIsOK: boolean, strFileUrl: string, strSavePath: string) => {
    // 处理文件下载事件
    if (bIsOK) {
      console.log(`文件下载成功，保存路径: ${strSavePath}`);
    } else {
      console.log(`文件下载失败`);
    }
  }
};

playersdk.getFileApi().setFileListener(fileListener);
```

##### 下载文件到指定的手机储存位置
```ts

const liveFileApi = playersdk.getFileApi();
const fileUrl = strFileUrl;
const savePath = path;

liveFileApi.downloadFile(fileUrl, savePath);
```
具体示例可参考Demo项目的 `FileListView` 代码


### 其他交互模块

此模块主要提供直播中的互动功能，如点名、抽奖等。

#### 接口定义
```typescript

export interface LiveInterActiveApi{
  /**
   * 设置互动监听器，用于接收互动相关的回调信息
   * @param listener 互动监听器对象
   */
  setListener(listener:InterActiveListener):void;

  /**
   * 对点名操作进行响应
   * @param isAccept 是否接受点名，true 表示接受，false 表示拒绝
   */
  rollCallAck(isAccept:boolean) :void;
}
```

#### API 详细说明
##### 设置回调监听
```typescript
// 创建一个实现了 InterActiveListener 接口的监听器对象
const interActiveListener: InterActiveListener = {
  onRollCall: (time: number) => {
    console.log(`收到点名通知，时间为: ${time}`);
  },
  onLottery: (cmd: number, lotteryInfo: string) => {
    console.log(`收到抽奖通知，命令: ${cmd}，中奖人信息: ${lotteryInfo}`);
  }
};

// 获取互动模块的 API 实例
const liveInterActiveApi = playersdk.getInteractiveApi();

// 设置监听器
liveInterActiveApi.setListener(interActiveListener);
```

##### 对点名操作进行响应
```typescript
// 当需要对点名进行响应时，调用 rollCallAck 方法
const isAccept = true; // 接受点名
liveInterActiveApi.rollCallAck(isAccept);
```

## 四、各个模块回调监听说明
### 核心监听 LivePlayerListener

```ts
export interface LivePlayerListener {

  /**
   * 加入直播间结果，在调用joinLive后回调
   * @param result
   */
  onJoin: (result: JoinResultCode) => void;

  /**
   * 因网络原因导致网络不稳定开始重新连接服务器
   */
  onReconnecting: () => void;
  /**
   * 退出直播间
   * @param reason 离开的原因
   */
  onLeave: (reason: LeaveReason) => void;
  /**
   * 直播间开始缓冲或结束缓冲
   * @param isCaching
   */
  onCaching: (isCaching: boolean) => void;
  /**
   * SDK发生错误时回调
   * @param err 错误内容
   */
  onErr: (err: string) => void;
  /**
   * 老师或组织者进行文档切换时通知。docName 文档名称。
   * @param docType 文档类型,(tip:docType=0 代表文档关闭)
   * @param docName
   */
  onDocSwitch: (docType: number, docName: string) => void;

  /**
   * 直播间开始有了视频桢
   */
  onVideoBegin: () => void;
  /**
   * 直播间视频没有视频桢
   */
  onVideoEnd: () => void;
  /**
   * 视频大小回调
   * @param width
   * @param height
   */
  onVideoSize: (width: number, height: number) => void;

  /**
   * 音频电频值可以根据需要处理以展示音调的高低，但请不要直接在回调做复杂的逻辑处理或执行占用时间长的代码，范围 0-90。
   * audioLevel 0-90
   * @param level 0 - 90
   */
  onAudioLevel: (level: number) => void;
  /**
   * 直播状态变更通知
   * @param isPlaying true 直播中，false 为直播暂停
   */
  onPublish: (isPlaying: boolean) => void;

  /**
   * 直播间内广播消息通知
   * @param userid
   * @param content 广播消息内容
   * @param sendtime 发送时间
   */
  onPublicMsg: (userid: number, content: string, sendtime: number) => void;
  /**
   * 直播间文字直播
   * @param language 本次文字直播的文字语言
   * @param text 内容
   */
  onLiveText: (language: string, text: string) => void;
  /**
   * 直播间不同事件的通知
   * 目前有老师锁屏通知 key=screenlock
   * @param key
   * @param value
   */
  onRoomData: (key: string, value: string) => void;

  /**
   * 多媒体（音频、视频）通话邀请，
   * 在收到该消息后，需再调用playersdk.inviteAck()方法同意或拒绝连麦
   * @param type 为纯音频，纯视频，或混合类型
   * @param isOpen 是否是邀请或者取消
   */
  onInvite: (type: InviteType, isOpen: boolean) => void;

  /**
   * 桌面共享开启或关闭时响应
   * @param isAs
   */
  onScreenStatus: (isAs: boolean) => void;

  /**
   * 一般是组织者控制的时候通知给 web 观看者，Playersdk 在大讲堂或 webcast 中可以响应
   * @param mode 0 文档为主  1 视频最大化 2文档最大化 3 视频为主
   */
  onModuleFocus: (mode: FocusMode) => void;

  /**
   * 双师课堂切换的时候回调，app根据回调更新相关的信息或提示。
   * 双师课堂关闭用户列表（后台系统管理- 系统设置-小班课用户列表-不显示）的话，
   * 聊天消息是不显示的。开启用户列表，学员只显示当前分组和老师、助教的消息。
   * @param status 0 切换到分课堂 1 切换到总课堂（名师课堂）
   */
  onDoubleTeacherStatusChange: (status: number) => void;
}
```

### 聊天回调 ChatListener

```ts
export interface ChatListener{
  /**
   * 收到私聊消息的通知
   * @param msg 聊天消息
   */
  onChatWithPerson:(msg:PrivateChatMsg)=>void
  /**
   * 收到公聊消息的通知
   * @param msg 聊天消息
   */
  onChatWithPublic:(msg:ChatMsg)=>void

  /**
   * 自己被禁言的通知
   * @param isMute 是否禁言
   */
  onMute:(isMute:boolean)=>void
  /**
   * 房间全局聊天权限通知
   * @param isMute 是否禁言
   */
  onRoomMute:(isMute:boolean)=>void

  /**
   * 消息通过审核处理后返回的结果，此借口返回的聊天信息需要从列表中删除
   * 通过判断type去区分id的类型，如果是msgID的话需要删除这个msgid的消息
   * 如果是userId的话需要删除这个用户的全部消息
   * @param type 是id的类型，可能为msgID或者是userId
   * @param id
   */
  onChatCensor:(type:CensorType,id:string)=>void
}
```
### 问答回调 QAListener

```ts
export interface QAListener{

  /**
   * 问答的添加、更新都会走这个回调
   * @param qaMsg
   */
  onQa:(qaMsg:QaMsg)=>void
  /**
   * 问答被取消
   * @param questionId
   */
  onQaCancel:(questionId:string)=>void
  /**
   * 自己被禁止问答的通知
   * @param isMute 是否是禁言
   */
  onQaMute:(isMute:boolean) => void

  /**
   * 直播间总体是否被禁止提问的回调
   * @param isMute 是否是禁言
   */
  onRoomMute:(isMute:boolean) => void
}
```
### 投票回调 VoteListener
```ts
export interface VoteListener{
  /**
   * 收到了投票发布的通知
   * @param vote
   */
  onVotePublish:(vote:BaseVote)=>void
  /**
   * 投票结束的通知
   * @param voteId
   */
  onVoteNotifyFinished:(voteId:string)=>void

  /**
   * 投票结果的通知
   * @param vote
   */
  onVotePublishResult:(vote:BaseVote)=>void

  /**
   * 第三方投票链接回调
   * @param url
   */
  onThirdVote: (url: string) => void;
}
```
### 文件回调 FileListener
```ts
export interface FileListener{

  /**
   * 直播间文件共享功能事件回调
   * @param cmd 文件状态
   * @param fileName 文件名称
   * @param fileUrl 文件位置
   */
  onFileShare:(cmd: ShareFileCMD, fileName: string, fileUrl: string)=>void


  /**
   * 文件共享功能中的下载事件回调
   * @param bIsOK 下载结果
   * @param fileUrl 文件网络位置
   * @param savePath 本地存储地址
   */
  onFileShareDl: (bIsOK: boolean, fileUrl: string, savePath: string) => void;
}
```

### 其他交互回调 InterActiveListener
```ts
export interface InterActiveListener{

  /**
   * 点名
   * @param time 点名时长
   */
  onRollCall:(time:number)=>void


  /**
   * 抽奖
   * @param cmd
   * @param lotteryInfo 中奖信息，为中奖人信息
   */
  onLottery: (cmd: LotteryCMD, lotteryInfo: string) => void;
}
```
