@tencentcloud/sdk-mcp 1.4.1 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/resource/doc/callkit/core-sdk/android/answer-call.md +942 -0
- package/resource/doc/callkit/core-sdk/android/make-call.md +876 -0
- package/resource/doc/callkit/core-sdk/android/offline-push.md +368 -0
- package/resource/doc/callkit/core-sdk/flutter/answer-call.md +1132 -0
- package/resource/doc/callkit/core-sdk/flutter/make-call.md +1206 -0
- package/resource/doc/callkit/core-sdk/flutter/offline-push.md +357 -0
- package/resource/doc/callkit/core-sdk/ios/answer-call.md +1009 -0
- package/resource/doc/callkit/core-sdk/ios/make-call.md +1154 -0
- package/resource/doc/callkit/core-sdk/ios/offline-push.md +470 -0
- package/resource/doc/callkit/core-sdk/uni-app/answer-call.md +1146 -0
- package/resource/doc/callkit/core-sdk/uni-app/make-call.md +1267 -0
- package/resource/doc/callkit/preset/android/integration.md +1 -1
- package/resource/doc/callkit/preset/flutter/integration.md +1 -1
- package/resource/doc/callkit/preset/ios/integration.md +1 -1
- package/resource/doc/callkit/preset/react/api.md +1 -1
- package/resource/doc/callkit/preset/vue/api.md +1 -1
- package/tools/call/get_native_callkit_core_integration.js +67 -0
- package/tools/call/get_native_callkit_core_integration.js.map +1 -0
- package/tools/common/present_framework_choice.js +1 -1
- package/tools/common/present_framework_choice.js.map +1 -1
- package/tools/definitions/get_native_call_uikit_integration_def.js +9 -3
- package/tools/definitions/get_native_call_uikit_integration_def.js.map +1 -1
- package/tools/definitions/get_native_callkit_core_integration_def.js +63 -0
- package/tools/definitions/get_native_callkit_core_integration_def.js.map +1 -0
- package/tools/index.js +2 -0
- package/tools/index.js.map +1 -1
- package/utils/report-cls-client.js +1 -1
- package/utils/report-es-client.js +1 -1
|
@@ -0,0 +1,942 @@
|
|
|
1
|
+
本文档将帮助您使用 **AtomicXCore SDK** 的 **DeviceStore**、**CallStore** 以及核心组件 **CallCoreView**,快速完成接听电话功能。
|
|
2
|
+
|
|
3
|
+
## 核心功能
|
|
4
|
+
|
|
5
|
+
**AtomicXCore** 中用于搭建多人音视频通话场景所需要使用到的核心模块包含以下三个:
|
|
6
|
+
|
|
7
|
+
| 模块 | 功能描述 |
|
|
8
|
+
|------|----------|
|
|
9
|
+
| [CallCoreView](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.view/-call-core-view/index.html) | 通话视图核心组件。自动监听 CallStore 数据并完成画面渲染,同时提供布局切换、头像与图标配置等 UI 定制化能力。 |
|
|
10
|
+
| [CallStore](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.call/-call-store/index.html) | 通话生命周期管理:拨打电话、接通电话、拒接电话、挂断电话。实时获取参与通话人员音视频状态,通话计时、通话记录等数据。 |
|
|
11
|
+
| [DeviceStore](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.device/-device-store/index.html) | 音视频设备控制:麦克风(开关 / 音量)、摄像头(开关 / 切换 / 画质)、屏幕共享,设备状态实时监听。 |
|
|
12
|
+
|
|
13
|
+
## 准备工作
|
|
14
|
+
|
|
15
|
+
### 步骤1:开通服务
|
|
16
|
+
|
|
17
|
+
请参见 开通服务,获取体验版或付费版 SDK。
|
|
18
|
+
|
|
19
|
+
### 步骤2:集成 SDK
|
|
20
|
+
|
|
21
|
+
**安装组件**:请在您的 **build.gradle** 文件中添加 `api "io.trtc.uikit:atomicx-core:latest.release"` 和 `api "com.tencent.imsdk:imsdk-plus:8.7.7201"` 依赖,然后执行 **Gradle Sync**。
|
|
22
|
+
|
|
23
|
+
```kotlin
|
|
24
|
+
dependencies {
|
|
25
|
+
api "io.trtc.uikit:atomicx-core:latest.release"
|
|
26
|
+
api "com.tencent.imsdk:imsdk-plus:8.7.7201"
|
|
27
|
+
// 其他依赖...
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 步骤3:初始化与登录流程
|
|
32
|
+
|
|
33
|
+
启动通话服务需依次完成 CallStore 初始化与用户登录。CallStore 通过监听登录成功事件自动同步用户信息,从而进入就绪状态。流程图与示例代码如下:
|
|
34
|
+
|
|
35
|
+
```kotlin
|
|
36
|
+
class MainActivity : ComponentActivity() {
|
|
37
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
38
|
+
super.onCreate(savedInstanceState)
|
|
39
|
+
|
|
40
|
+
// CallStore 初始化
|
|
41
|
+
CallStore.shared
|
|
42
|
+
|
|
43
|
+
val sdkAppId = 1400000001 // 替换为您的 SDKAppID
|
|
44
|
+
val userId = "test_001" // 替换为您的 UserID
|
|
45
|
+
val userSig = "xxxxxxxxxxx" // 联调填临时 UserSig;MCP 已配置 sdkAppId、sdkSecretKey 时可由工具自动生成
|
|
46
|
+
|
|
47
|
+
LoginStore.shared.login(
|
|
48
|
+
this,
|
|
49
|
+
sdkAppId,
|
|
50
|
+
userId,
|
|
51
|
+
userSig,
|
|
52
|
+
object : CompletionHandler {
|
|
53
|
+
override fun onSuccess() {
|
|
54
|
+
TUICallEngine.createInstance(this@MainActivity).init(sdkAppId, userId, userSig, null)
|
|
55
|
+
Log.d("Login", "login success")
|
|
56
|
+
}
|
|
57
|
+
override fun onFailure(code: Int, desc: String) {
|
|
58
|
+
Log.e("Login", "login failed, code: $code, error: $desc")
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**登录接口参数说明:**
|
|
67
|
+
|
|
68
|
+
| 参数 | 类型 | 说明 |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| `userId` | String | 当前用户的唯一 ID,仅包含英文字母、数字、连字符和下划线。为避免多端登录冲突,请勿使用 1、123 等简单 ID |
|
|
71
|
+
| `sdkAppId` | int | 从控制台获取,通常是以 140 或 160 开头的 10 位整数 |
|
|
72
|
+
| `userSig` | String | IM 登录鉴权票据。联调可本地生成临时值(如 `GenerateTestUserSig.genTestUserSig`);**Tencent RTC MCP Server** 已配置 `sdkAppId` 与 `sdkSecretKey` 时可由工具自动生成。**生产环境**须由业务服务端使用密钥签发 UserSig,勿在客户端存储或下发 SecretKey。 |
|
|
73
|
+
|
|
74
|
+
## 实现接听通话
|
|
75
|
+
|
|
76
|
+
**接听通话前,请确保已完成登录,这是服务可用的必要前提**。接下来,我们将通过 6 个步骤带您实现"接听一通电话"的功能。
|
|
77
|
+
|
|
78
|
+
### 步骤1:创建通话页面
|
|
79
|
+
|
|
80
|
+
您需要创建一个通话页面,当收到来电时唤起通话页面,实现方式如下:
|
|
81
|
+
|
|
82
|
+
1. **创建通话页面:** 您可以新建一个 Activity 作为通话宿主页面,用于响应来电时的跳转逻辑。
|
|
83
|
+
2. **通话页面绑定 CallCoreView:** 通话视图核心组件,自动监听 CallStore 数据并完成画面渲染,同时提供布局切换、头像与图标配置等 UI 定制化能力。
|
|
84
|
+
|
|
85
|
+
```kotlin
|
|
86
|
+
import io.trtc.tuikit.atomicxcore.api.view.CallCoreView
|
|
87
|
+
|
|
88
|
+
class CallActivity : AppCompatActivity() {
|
|
89
|
+
private var callCoreView: CallCoreView? = null
|
|
90
|
+
|
|
91
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
92
|
+
super.onCreate(savedInstanceState)
|
|
93
|
+
callCoreView = CallCoreView(this)
|
|
94
|
+
setContentView(callCoreView)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**CallCoreView 视图组件功能说明:**
|
|
100
|
+
|
|
101
|
+
| 功能 | 说明 | 参考文档 |
|
|
102
|
+
|------|------|----------|
|
|
103
|
+
| **设置布局模式** | 支持自由切换布局模式。若未设置,将根据通话人数自动适配布局。 | 切换布局模式 |
|
|
104
|
+
| **设置头像** | 支持通过传入头像资源路径,为特定用户自定义头像。 | 自定义默认头像 |
|
|
105
|
+
| **设置音量提示图标** | 支持根据不同音量等级,配置个性化的音量指示图标。 | 自定义音量提示的图标 |
|
|
106
|
+
| **设置网络提示图标** | 支持根据实时网络质量,配置对应的网络状态提示图标。 | 自定义网络提示的图标 |
|
|
107
|
+
| **设置等待接听用户的动画** | 在多人通话场景下,支持传入 GIF 图像路径,为待接听状态的用户展示动画。 | 自定义 loading 动画 |
|
|
108
|
+
|
|
109
|
+
### 步骤2:添加接听和拒接按钮
|
|
110
|
+
|
|
111
|
+
您可以参考 [DeviceStore](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.device/-device-store/index.html)、[CallStore](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.call/-call-store/index.html) 提供的 API,自定义添加您的按钮。
|
|
112
|
+
|
|
113
|
+
- **DeviceStore 功能说明:** 麦克风(开关/音量)、摄像头(开关/切换/画质)、屏幕共享,设备状态实时监听。建议将对应方法绑定至按钮点击事件,并通过监听设备状态变更来实时刷新按钮的 UI 状态。
|
|
114
|
+
- **CallStore 功能说明:** 接听、挂断、拒接等核心通话控制能力。建议将对应方法绑定至按钮点击事件,并监听通话状态的变化,以确保按钮显示与当前通话阶段保持同步。
|
|
115
|
+
|
|
116
|
+
**图标资源下载:** 按钮图标可以直接从 [GitHub](https://github.com/Tencent-RTC/TUIKit_Android/tree/main/atomic_x/src/main/res-callview/drawable-xxhdpi) 下载。这些图标由我们的设计师专为 TUICallKit 打造,无版权风险,可放心使用。
|
|
117
|
+
|
|
118
|
+
| 文件名 | 用途 | 下载地址 |
|
|
119
|
+
|--------|------|----------|
|
|
120
|
+
| callview_ic_dialing.png | 接听按钮(绿色电话) | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_dialing.png |
|
|
121
|
+
| callview_ic_hangup.png | 挂断按钮(红色电话) | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_hangup.png |
|
|
122
|
+
| callview_ic_camera_disable.png | 摄像头关闭 | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_camera_disable.png |
|
|
123
|
+
| callview_ic_camera_enable.png | 摄像头开启 | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_camera_enable.png |
|
|
124
|
+
| callview_ic_mic_unmute.png | 麦克风开启 | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_mic_unmute.png |
|
|
125
|
+
| callview_ic_mic_mute.png | 麦克风静音 | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_mic_mute.png |
|
|
126
|
+
| callview_ic_handsfree_disable.png | 免提关闭 | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_handsfree_disable.png |
|
|
127
|
+
| callview_ic_handsfree_enable.png | 免提开启 | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_handsfree_enable.png |
|
|
128
|
+
|
|
129
|
+
**以添加接听、拒接按钮为例,实现方式如下:**
|
|
130
|
+
|
|
131
|
+
#### 2.1 创建接听/拒接按钮
|
|
132
|
+
|
|
133
|
+
创建底部按钮栏容器,添加"接听"与"拒接"按钮,在点击事件中分别调用 accept 和 reject 接口。
|
|
134
|
+
|
|
135
|
+
```kotlin
|
|
136
|
+
import io.trtc.tuikit.atomicxcore.api.device.DeviceStore
|
|
137
|
+
import io.trtc.tuikit.atomicxcore.api.call.CallStore
|
|
138
|
+
|
|
139
|
+
class CallActivity : AppCompatActivity() {
|
|
140
|
+
private var buttonContainer: LinearLayout? = null
|
|
141
|
+
private var buttonAccept: Button? = null
|
|
142
|
+
private var buttonReject: Button? = null
|
|
143
|
+
|
|
144
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
145
|
+
super.onCreate(savedInstanceState)
|
|
146
|
+
createButtonContainer()
|
|
147
|
+
addRejectAndAcceptButtons()
|
|
148
|
+
setContentView(buttonContainer)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private fun createButtonContainer() {
|
|
152
|
+
buttonContainer = LinearLayout(this).apply {
|
|
153
|
+
orientation = LinearLayout.HORIZONTAL
|
|
154
|
+
gravity = Gravity.CENTER
|
|
155
|
+
layoutParams = FrameLayout.LayoutParams(
|
|
156
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
157
|
+
FrameLayout.LayoutParams.WRAP_CONTENT
|
|
158
|
+
).apply {
|
|
159
|
+
gravity = Gravity.BOTTOM
|
|
160
|
+
bottomMargin = dpToPx(80)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private fun addRejectAndAcceptButtons() {
|
|
166
|
+
createAcceptButton()
|
|
167
|
+
createRejectButton()
|
|
168
|
+
buttonContainer?.addView(buttonAccept)
|
|
169
|
+
buttonContainer?.addView(buttonReject)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private fun createAcceptButton() {
|
|
173
|
+
buttonAccept = Button(this).apply {
|
|
174
|
+
text = "接听"
|
|
175
|
+
layoutParams = LinearLayout.LayoutParams(
|
|
176
|
+
0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f
|
|
177
|
+
).apply { marginEnd = dpToPx(8) }
|
|
178
|
+
setOnClickListener {
|
|
179
|
+
// 接听按钮点击事件绑定 accept
|
|
180
|
+
CallStore.shared.accept(null)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private fun createRejectButton() {
|
|
186
|
+
buttonReject = Button(this).apply {
|
|
187
|
+
text = "拒接"
|
|
188
|
+
layoutParams = LinearLayout.LayoutParams(
|
|
189
|
+
0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f
|
|
190
|
+
).apply { marginStart = dpToPx(8) }
|
|
191
|
+
setOnClickListener {
|
|
192
|
+
// 拒接按钮点击事件绑定 reject
|
|
193
|
+
CallStore.shared.reject(null)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### 2.2 监听通话结束,销毁界面
|
|
201
|
+
|
|
202
|
+
1.2 **拨打方取消通话或您拒接时销毁界面:** 无论拨打方取消呼叫,还是接收方拒接,均会触发 onCallEnded(通话结束)事件。建议监听此事件,以便在通话终止时及时关闭(销毁)通话界面。
|
|
203
|
+
|
|
204
|
+
```kotlin
|
|
205
|
+
import io.trtc.tuikit.atomicxcore.api.call.CallEndReason
|
|
206
|
+
import io.trtc.tuikit.atomicxcore.api.call.CallListener
|
|
207
|
+
import io.trtc.tuikit.atomicxcore.api.call.CallMediaType
|
|
208
|
+
import io.trtc.tuikit.atomicxcore.api.call.CallStore
|
|
209
|
+
|
|
210
|
+
class CallActivity : AppCompatActivity() {
|
|
211
|
+
private var callListener: CallListener? = null
|
|
212
|
+
|
|
213
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
214
|
+
super.onCreate(savedInstanceState)
|
|
215
|
+
// ... 其他初始化代码
|
|
216
|
+
// 1.通话结束事件监听
|
|
217
|
+
addListener()
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private fun addListener() {
|
|
221
|
+
callListener = object : CallListener() {
|
|
222
|
+
override fun onCallEnded(
|
|
223
|
+
callId: String,
|
|
224
|
+
mediaType: CallMediaType,
|
|
225
|
+
reason: CallEndReason,
|
|
226
|
+
userId: String
|
|
227
|
+
) {
|
|
228
|
+
// 2.通话结束关闭页面
|
|
229
|
+
finish()
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
callListener?.let { CallStore.shared.addListener(it) }
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**onCallEnded 事件参数详细说明**:
|
|
238
|
+
|
|
239
|
+
| 参数 | 类型 | 说明 |
|
|
240
|
+
|------|------|------|
|
|
241
|
+
| callId | String | 此次通话的唯一标识。 |
|
|
242
|
+
| mediaType | [CallMediaType](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.call/-call-media-type/index.html) | 通话媒体类型,用于指定发起音频通话还是视频通话。CallMediaType.Video : 视频通话。CallMediaType.Audio : 语音通话。 |
|
|
243
|
+
| reason | [CallEndReason](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.call/-call-end-reason/index.html) | 通话结束的原因。Unknown : 未知原因,无法确定结束原因。Hangup : 正常挂断,用户主动挂断通话。Reject : 拒绝接听,被叫方拒绝来电。NoResponse : 无响应,被叫方未在超时时间内接听。Offline : 对方离线,被叫方不在线。LineBusy : 对方忙线,被叫方正在通话中。Canceled : 通话取消,主叫方在对方接听前取消。OtherDeviceAccepted : 其他设备已接听,通话已在另一登录设备上接听。OtherDeviceReject : 其他设备已拒绝,通话已在另一登录设备上拒绝。EndByServer : 服务器结束,通话被服务器终止。 |
|
|
244
|
+
| userId | String | 触发结束的用户 ID 。 |
|
|
245
|
+
|
|
246
|
+
### 步骤3:申请音视频权限
|
|
247
|
+
|
|
248
|
+
建议在呼叫接通前,先行检测音视频权限。若权限缺失,请引导用户动态申请。具体方法如下:
|
|
249
|
+
|
|
250
|
+
1. **声明权限**:首先,在 `AndroidManifest.xml` 文件中声明应用需要的摄像头和麦克风权限。
|
|
251
|
+
|
|
252
|
+
```xml
|
|
253
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
254
|
+
|
|
255
|
+
<!-- 麦克风权限 -->
|
|
256
|
+
|
|
257
|
+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
258
|
+
|
|
259
|
+
<!-- 摄像头权限 -->
|
|
260
|
+
|
|
261
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
262
|
+
|
|
263
|
+
<application>
|
|
264
|
+
|
|
265
|
+
<!-- ... -->
|
|
266
|
+
|
|
267
|
+
<activity
|
|
268
|
+
|
|
269
|
+
android:name=".CallActivity"
|
|
270
|
+
|
|
271
|
+
android:exported="false"
|
|
272
|
+
|
|
273
|
+
android:screenOrientation="portrait" />
|
|
274
|
+
|
|
275
|
+
</application>
|
|
276
|
+
|
|
277
|
+
</manifest>
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
2. **动态权限申请:**建议在收到来电时,或结合具体业务场景(如点击呼叫前),按需发起音视频权限的申请。
|
|
281
|
+
|
|
282
|
+
```kotlin
|
|
283
|
+
// 动态申请权限
|
|
284
|
+
|
|
285
|
+
private fun requestAllCallPermissions() {
|
|
286
|
+
|
|
287
|
+
val allPermissions = arrayOf(
|
|
288
|
+
|
|
289
|
+
Manifest.permission.CAMERA,
|
|
290
|
+
|
|
291
|
+
Manifest.permission.RECORD_AUDIO
|
|
292
|
+
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
if (!checkPermissions(allPermissions)) {
|
|
296
|
+
|
|
297
|
+
ActivityCompat.requestPermissions(this, allPermissions, PERMISSION_REQUEST_CODE)
|
|
298
|
+
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// 处理权限申请结果
|
|
304
|
+
|
|
305
|
+
override fun onRequestPermissionsResult(
|
|
306
|
+
|
|
307
|
+
requestCode: Int,
|
|
308
|
+
|
|
309
|
+
permissions: Array<out String>,
|
|
310
|
+
|
|
311
|
+
grantResults: IntArray
|
|
312
|
+
|
|
313
|
+
) {
|
|
314
|
+
|
|
315
|
+
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
316
|
+
|
|
317
|
+
if (requestCode == PERMISSION_REQUEST_CODE) {
|
|
318
|
+
|
|
319
|
+
// 检查是否所有权限都已授予
|
|
320
|
+
|
|
321
|
+
var allGranted = true
|
|
322
|
+
|
|
323
|
+
for (result in grantResults) {
|
|
324
|
+
|
|
325
|
+
if (result != PackageManager.PERMISSION_GRANTED) {
|
|
326
|
+
|
|
327
|
+
allGranted = false
|
|
328
|
+
|
|
329
|
+
break
|
|
330
|
+
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (allGranted) {
|
|
336
|
+
|
|
337
|
+
Log.d("MainActivity", "权限申请成功")
|
|
338
|
+
|
|
339
|
+
} else {
|
|
340
|
+
|
|
341
|
+
Log.w("MainActivity", "部分权限被拒绝")
|
|
342
|
+
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### 步骤4: 来电播放提示
|
|
351
|
+
|
|
352
|
+
您可以监听当前用户的通话状态,在收到来电时播放铃声或振动,接听、挂断后停止播放来电提示,实现方法如下:
|
|
353
|
+
|
|
354
|
+
1. **数据层订阅:**订阅 `CallStore.observerState.selfInfo` , 建立当前登录用户信息的响应式监听。
|
|
355
|
+
|
|
356
|
+
2. **播放/停止来电提示:**若当前用户的通话状态 (`SelfInfo.Status`) 为等待接听状态(`CallParticipantStatus.Waiting`)播放铃声或振动,若当前用户的通话状态 (`SelfInfo.Status`) 为已接听状态(`CallParticipantStatus.Accept`)停止铃声或振动。
|
|
357
|
+
|
|
358
|
+
```kotlin
|
|
359
|
+
import kotlinx.coroutines.CoroutineScope
|
|
360
|
+
import kotlinx.coroutines.Dispatchers
|
|
361
|
+
import kotlinx.coroutines.Job
|
|
362
|
+
import kotlinx.coroutines.launch
|
|
363
|
+
|
|
364
|
+
class MainActivity : AppCompatActivity() {
|
|
365
|
+
|
|
366
|
+
private var stateJob: Job? = null
|
|
367
|
+
|
|
368
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
369
|
+
|
|
370
|
+
super.onCreate(savedInstanceState)
|
|
371
|
+
|
|
372
|
+
// 1.监听当前用户的通话状态
|
|
373
|
+
|
|
374
|
+
observeSelfStatus()
|
|
375
|
+
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// 监听当前用户的通话状态
|
|
379
|
+
|
|
380
|
+
private fun observeSelfStatus() {
|
|
381
|
+
|
|
382
|
+
stateJob = CoroutineScope(Dispatchers.Main).launch {
|
|
383
|
+
|
|
384
|
+
CallStore.shared.observerState.selfInfo.collect { selfInfo ->
|
|
385
|
+
|
|
386
|
+
// 2.播放/停止来电提示
|
|
387
|
+
|
|
388
|
+
if (selfInfo.status == CallParticipantStatus.Waiting) {
|
|
389
|
+
|
|
390
|
+
// 等待接听时,开始播放来电提示
|
|
391
|
+
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (selfInfo.status == CallParticipantStatus.Accept) {
|
|
395
|
+
|
|
396
|
+
// 接听后,停止播放来电提示
|
|
397
|
+
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### 步骤5:来电打开媒体设备
|
|
410
|
+
|
|
411
|
+
收到来电时,您可以通过 onCallReceived 事件获取本次通话的媒体类型。为了提供更佳的用户体验,建议在唤起通话界面时,根据通话类型预先开启对应的媒体设备。实现步骤如下:
|
|
412
|
+
|
|
413
|
+
1. **监听来电事件**:订阅 `onCallReceived` 事件。
|
|
414
|
+
|
|
415
|
+
2. **根据来电媒体类型打开设备:**若为语音通话仅开启麦克风,若为视频通话开启麦克风和摄像头。
|
|
416
|
+
|
|
417
|
+
```kotlin
|
|
418
|
+
import io.trtc.tuikit.atomicxcore.api.device.DeviceStore
|
|
419
|
+
import io.trtc.tuikit.atomicxcore.api.call.CallMediaType
|
|
420
|
+
import io.trtc.tuikit.atomicxcore.api.call.*
|
|
421
|
+
|
|
422
|
+
class MainActivity : AppCompatActivity() {
|
|
423
|
+
|
|
424
|
+
private var callListener: CallListener? = null
|
|
425
|
+
|
|
426
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
427
|
+
|
|
428
|
+
super.onCreate(savedInstanceState)
|
|
429
|
+
|
|
430
|
+
// ... 其他初始化代码
|
|
431
|
+
|
|
432
|
+
// 1.监听来电事件
|
|
433
|
+
|
|
434
|
+
addListener()
|
|
435
|
+
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private fun addListener() {
|
|
439
|
+
|
|
440
|
+
callListener = object : CallListener() {
|
|
441
|
+
|
|
442
|
+
override fun onCallReceived(callId: String, mediaType: CallMediaType, userData: String) {
|
|
443
|
+
|
|
444
|
+
super.onCallReceived(callId, mediaType, userData)
|
|
445
|
+
|
|
446
|
+
// 2.根据来电媒体类型打开设备
|
|
447
|
+
|
|
448
|
+
openDeviceForMediaType(mediaType)
|
|
449
|
+
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
callListener?.let { CallStore.shared.addListener(it) }
|
|
455
|
+
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private fun openDeviceForMediaType(mediaType: CallMediaType?) {
|
|
459
|
+
|
|
460
|
+
mediaType?.let {
|
|
461
|
+
|
|
462
|
+
DeviceStore.shared().openLocalMicrophone(null)
|
|
463
|
+
|
|
464
|
+
if (mediaType == CallMediaType.Video) {
|
|
465
|
+
|
|
466
|
+
val isFrontCamera = true
|
|
467
|
+
|
|
468
|
+
DeviceStore.shared().openLocalCamera(isFrontCamera, null)
|
|
469
|
+
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
**onCallReceived 事件详细说明:**
|
|
480
|
+
|
|
481
|
+
| 参数 | 类型 | 说明 |
|
|
482
|
+
|------|------|------|
|
|
483
|
+
| callId | String | 此次通话的唯一标识。 |
|
|
484
|
+
| mediaType | [CallMediaType](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.call/-call-media-type/index.html) | 通话媒体类型,用于指定发起音频通话还是视频通话。CallMediaType.Video : 视频通话。CallMediaType.Audio : 语音通话。 |
|
|
485
|
+
|
|
486
|
+
**openLocalCamera** **接口参数详细说明:**
|
|
487
|
+
|
|
488
|
+
| 参数名 | 类型 | 必填 | 说明 |
|
|
489
|
+
| ------ | ------ | ------ | ------ |
|
|
490
|
+
| isFront | Boolean | 是 | 是否开启前置摄像头。true : 开启前置摄像头。false :开启后置摄像头。 |
|
|
491
|
+
| completion | CompletionHandler | 否 | 操作完成回调,用于返回开启摄像头的结果。若开启失败则会返回错误码和错误信息。 |
|
|
492
|
+
|
|
493
|
+
**openLocalMicrophone 接口参数详细说明:**
|
|
494
|
+
|
|
495
|
+
| 参数名 | 类型 | 必填 | 说明 |
|
|
496
|
+
| ------ | ------ | ------ | ------ |
|
|
497
|
+
| completion | CompletionHandler | 否 | 操作完成回调,用于返回开启麦克风的结果。若开启失败则会返回错误码和错误信息。 |
|
|
498
|
+
|
|
499
|
+
### 步骤6:来电唤起通话界面
|
|
500
|
+
|
|
501
|
+
您在 步骤5 已订阅了 onCallReceived 事件,您可以在该事件中唤起通话页面。实现方式如下:
|
|
502
|
+
|
|
503
|
+
```kotlin
|
|
504
|
+
private fun addListener() {
|
|
505
|
+
|
|
506
|
+
callListener = object : CallListener() {
|
|
507
|
+
|
|
508
|
+
override fun onCallReceived(callId: String, mediaType: CallMediaType, userData: String) {
|
|
509
|
+
|
|
510
|
+
super.onCallReceived(callId, mediaType, userData)
|
|
511
|
+
|
|
512
|
+
// 唤起通话页面
|
|
513
|
+
|
|
514
|
+
val intent = Intent(this@MainActivity, CallActivity::class.java)
|
|
515
|
+
|
|
516
|
+
startActivity(intent)
|
|
517
|
+
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
callListener?.let { CallStore.shared.addListener(it) }
|
|
523
|
+
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### 运行效果
|
|
528
|
+
|
|
529
|
+
当您完成以上 6 步后,"接听一通电话"运行效果如下:
|
|
530
|
+
|
|
531
|
+
## 接入离线推送
|
|
532
|
+
|
|
533
|
+
离线推送效果如下,详细请参考 接入离线推送功能 完成功能接入。
|
|
534
|
+
|
|
535
|
+
| Notification | FCM Data Message |
|
|
536
|
+
| ------------- | ------------- |
|
|
537
|
+
| | |
|
|
538
|
+
|
|
539
|
+
**说明:**
|
|
540
|
+
|
|
541
|
+
## 定制页面
|
|
542
|
+
|
|
543
|
+
CallCoreView 提供了完善的 UI 定制能力,支持头像及音量提示等图标的自由替换。**为助力快速集成,您可以直接从 [GitHub](https://github.com/Tencent-RTC/TUIKit_Android/tree/main/atomic_x/src/main/res-callview/drawable-xxhdpi) 下载。这些图标由我们的设计师专为 TUICallKit 打造,无版权风险,可放心使用。**
|
|
544
|
+
|
|
545
|
+
### 自定义音量提示的图标
|
|
546
|
+
|
|
547
|
+
您可以调用 **CallCoreView** 组件的 [setVolumeLevelIcons](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.view/-call-core-view/set-volume-level-icons.html) 设置音量大小等级不同的提示图标。
|
|
548
|
+
|
|
549
|
+
**setVolumeLevelIcons 示例代码:**
|
|
550
|
+
|
|
551
|
+
```kotlin
|
|
552
|
+
private fun setIconResourcePath() {
|
|
553
|
+
val volumeLevelIcons = mapOf(VolumeLevel.Mute to "对应图标资源的路径")
|
|
554
|
+
val callCoreView = CallCoreView(context)
|
|
555
|
+
callCoreView.setVolumeLevelIcons(volumeLevelIcons)
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**setVolumeLevelIcons 接口参数详细说明:**
|
|
560
|
+
|
|
561
|
+
| 参数 | 类型 | 是否必填 | 说明 |
|
|
562
|
+
|------|------|----------|------|
|
|
563
|
+
| icons | Map<VolumeLevel, String> | 是 | 音量等级与图标资源的映射表。Key(VolumeLevel)表示音量等级,Value(String)表示对应音量等级的图标资源路径 |
|
|
564
|
+
|
|
565
|
+
**VolumeLevel 等级:**
|
|
566
|
+
|
|
567
|
+
| 等级 | 说明 |
|
|
568
|
+
|------|------|
|
|
569
|
+
| `VolumeLevel.Mute` | 麦克风关闭,静音状态 |
|
|
570
|
+
| `VolumeLevel.Low` | 音量范围 (0-25] |
|
|
571
|
+
| `VolumeLevel.Medium` | 音量范围 (25-50] |
|
|
572
|
+
| `VolumeLevel.High` | 音量范围 (50-75] |
|
|
573
|
+
| `VolumeLevel.Peak` | 音量范围 (75-100] |
|
|
574
|
+
|
|
575
|
+
**音量提示图标:**
|
|
576
|
+
|
|
577
|
+
| 文件名 | 说明 | 下载地址 |
|
|
578
|
+
|--------|------|----------|
|
|
579
|
+
| callview_ic_audio_input.png | 【图标含义】音量提示图标。<br>【推荐用法】您可以将该图标等级设置为 `VolumeLevel.Low` 或 `VolumeLevel.Medium`,当用户音量大于对应等级时显示。 | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_audio_input.png |
|
|
580
|
+
| callview_ic_self_mute.png | 【图标含义】静音图标。<br>【推荐用法】您可以将该图标等级设置为 `VolumeLevel.Mute`,当该用户静音时显示。 | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_self_mute.png |
|
|
581
|
+
|
|
582
|
+
### 自定义网络提示的图标
|
|
583
|
+
|
|
584
|
+
您可以调用 **CallCoreView** 组件的 [setNetworkQualityIcons](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.view/-call-core-view/set-network-quality-icons.html) 设置不同网络状态的提示图标。
|
|
585
|
+
|
|
586
|
+
**setNetworkQualityIcons 示例代码:**
|
|
587
|
+
|
|
588
|
+
```kotlin
|
|
589
|
+
private fun setNetworkQualityIcons() {
|
|
590
|
+
val networkIcons = mapOf(NetworkQuality.BAD to "对应图标的路径")
|
|
591
|
+
val callCoreView = CallCoreView(context)
|
|
592
|
+
callCoreView.setNetworkQualityIcons(networkIcons)
|
|
593
|
+
}
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
**setNetworkQualityIcons 接口参数详细说明:**
|
|
597
|
+
|
|
598
|
+
| 参数 | 类型 | 是否必填 | 说明 |
|
|
599
|
+
|------|------|----------|------|
|
|
600
|
+
| icons | Map<NetworkQuality, String> | 是 | 网络质量与图标资源的映射表。Key(NetworkQuality)表示网络质量等级,Value(String)表示对应网络状态的图标资源路径 |
|
|
601
|
+
|
|
602
|
+
**NetworkQuality 等级:**
|
|
603
|
+
|
|
604
|
+
| 等级 | 说明 |
|
|
605
|
+
|------|------|
|
|
606
|
+
| `NetworkQuality.UNKNOWN` | 未知网络状态 |
|
|
607
|
+
| `NetworkQuality.EXCELLENT` | 网络状态极佳 |
|
|
608
|
+
| `NetworkQuality.GOOD` | 网络状态较好 |
|
|
609
|
+
| `NetworkQuality.POOR` | 网络状态较差 |
|
|
610
|
+
| `NetworkQuality.BAD` | 网络状态差 |
|
|
611
|
+
| `NetworkQuality.VERY_BAD` | 网络状态极差 |
|
|
612
|
+
| `NetworkQuality.DOWN` | 网络断开 |
|
|
613
|
+
|
|
614
|
+
**网络较差的提示图标:**
|
|
615
|
+
|
|
616
|
+
| 文件名 | 说明 | 下载地址 |
|
|
617
|
+
|--------|------|----------|
|
|
618
|
+
| callview_ic_network_bad.png | 【图标含义】网络较差的提示图标。<br>【推荐用法】您可以将该图标等级设置为 `NetworkQuality.BAD`、`NetworkQuality.VERY_BAD` 或 `NetworkQuality.DOWN`,当网络较差时显示该图标。 | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_network_bad.png |
|
|
619
|
+
|
|
620
|
+
### 自定义默认头像
|
|
621
|
+
|
|
622
|
+
您可以调用 **CallCoreView** 的 [setParticipantAvatars](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.view/-call-core-view/set-participant-avatars.html) 接口设置用户头像。建议您监听响应式数据 allParticipants(所有参与通话的成员):当获取到用户头像时设置并展示;若用户未设置头像或加载失败,则显示默认头像(占位图)。
|
|
623
|
+
|
|
624
|
+
**setParticipantAvatars 示例代码:**
|
|
625
|
+
|
|
626
|
+
```kotlin
|
|
627
|
+
private fun setParticipantAvatars() {
|
|
628
|
+
val avatars: MutableMap<String, String> = mutableMapOf()
|
|
629
|
+
val userId = "" // 用户 ID
|
|
630
|
+
val avatarPath = "" // 用户默认头像资源的路径
|
|
631
|
+
avatars[userId] = avatarPath
|
|
632
|
+
val callCoreView = CallCoreView(context)
|
|
633
|
+
callCoreView.setParticipantAvatars(avatars)
|
|
634
|
+
}
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**setParticipantAvatars 接口参数详细说明:**
|
|
638
|
+
|
|
639
|
+
| 参数 | 类型 | 是否必填 | 说明 |
|
|
640
|
+
|------|------|----------|------|
|
|
641
|
+
| icons | Map<String, String> | 是 | 用户头像映射表。Key:用户的 userID。Value:该用户的头像资源绝对路径 |
|
|
642
|
+
|
|
643
|
+
**默认头像资源:**
|
|
644
|
+
|
|
645
|
+
| 文件名 | 说明 | 下载地址 |
|
|
646
|
+
|--------|------|----------|
|
|
647
|
+
| callview_ic_avatar.png | 【图标含义】默认头像。<br>【推荐用法】当用户头像加载失败或无头像时,您可以给该用户设置此默认头像。 | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_avatar.png |
|
|
648
|
+
|
|
649
|
+
### 自定义 loading 动画
|
|
650
|
+
|
|
651
|
+
您可以调用 **CallCoreView** 的 [setWaitingAnimation](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.view/-call-core-view/set-waiting-animation.html) 接口,为等待中用户设置等待动画获得更好的体验。
|
|
652
|
+
|
|
653
|
+
**setWaitingAnimation 示例代码:**
|
|
654
|
+
|
|
655
|
+
```kotlin
|
|
656
|
+
private fun setWaitingAnimation() {
|
|
657
|
+
val waitingAnimationPath = "" // 等待动画 GIF 图像资源的路径
|
|
658
|
+
val callCoreView = CallCoreView(context)
|
|
659
|
+
callCoreView.setWaitingAnimation(waitingAnimationPath)
|
|
660
|
+
}
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
**setWaitingAnimation 接口参数详细说明:**
|
|
664
|
+
|
|
665
|
+
| 参数 | 类型 | 是否必填 | 说明 |
|
|
666
|
+
|------|------|----------|------|
|
|
667
|
+
| path | String | 是 | GIF 格式图像资源的绝对路径 |
|
|
668
|
+
|
|
669
|
+
**等待接听的动画:**
|
|
670
|
+
|
|
671
|
+
| 文件名 | 说明 | 下载地址 |
|
|
672
|
+
|--------|------|----------|
|
|
673
|
+
| callview_ic_loading.gif | 【图标含义】用户等待接听动画。<br>【推荐用法】群组通话时设置的动画。设置后,当用户的状态为等待接听时,显示该动画。 | https://github.com/Tencent-RTC/TUIKit_Android/blob/main/atomic_x/src/main/res-callview/drawable-xxhdpi/callview_ic_loading.gif |
|
|
674
|
+
|
|
675
|
+
### 添加通话计时提示
|
|
676
|
+
|
|
677
|
+
通话计时可通过响应式数据 [activeCall](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.call/-call-state/active-call.html) 的 `duration` 字段实时获得,实时显示通话计时的实现方式如下:
|
|
678
|
+
|
|
679
|
+
1. **数据层订阅:** 订阅 `CallStore.observerState.activeCall`,建立当前活跃通话的响应式监听。
|
|
680
|
+
2. **绑定通话计时数据:** 将 `activeCall.duration` 字段绑定至 UI 控件。该字段为响应式数据,会自动驱动 UI 实时刷新,无需手动维护定时器。
|
|
681
|
+
|
|
682
|
+
```kotlin
|
|
683
|
+
import android.content.Context
|
|
684
|
+
import androidx.appcompat.widget.AppCompatTextView
|
|
685
|
+
import com.tencent.qcloud.tuicore.util.DateTimeUtil
|
|
686
|
+
import io.trtc.tuikit.atomicxcore.api.call.CallStore
|
|
687
|
+
import kotlinx.coroutines.CoroutineScope
|
|
688
|
+
import kotlinx.coroutines.Dispatchers
|
|
689
|
+
import kotlinx.coroutines.Job
|
|
690
|
+
import kotlinx.coroutines.launch
|
|
691
|
+
|
|
692
|
+
class TimerView(context: Context) : AppCompatTextView(context) {
|
|
693
|
+
private var subscribeStateJob: Job? = null
|
|
694
|
+
|
|
695
|
+
override fun onAttachedToWindow() {
|
|
696
|
+
super.onAttachedToWindow()
|
|
697
|
+
// 1.数据层订阅 activeCall
|
|
698
|
+
registerActiveCallObserver()
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
private fun registerActiveCallObserver() {
|
|
702
|
+
subscribeStateJob = CoroutineScope(Dispatchers.Main).launch {
|
|
703
|
+
CallStore.shared.observerState.activeCall.collect { activeCall ->
|
|
704
|
+
// 2.绑定通话计时数据,更新通话计时
|
|
705
|
+
updateDurationView(activeCall)
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
private fun updateDurationView(activeCall: CallInfo) {
|
|
711
|
+
val currentDuration = activeCall.duration
|
|
712
|
+
text = DateTimeUtil.formatSecondsTo00(currentDuration.toInt())
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
override fun onDetachedFromWindow() {
|
|
716
|
+
super.onDetachedFromWindow()
|
|
717
|
+
subscribeStateJob?.cancel()
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
## 更多功能
|
|
723
|
+
|
|
724
|
+
### 设置头像和昵称
|
|
725
|
+
|
|
726
|
+
通话开始前,您可以通过 [setSelfInfo](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.login/-login-store/set-self-info.html) 方法,设置自己的昵称和头像。
|
|
727
|
+
|
|
728
|
+
**setSelfInfo 示例代码:**
|
|
729
|
+
|
|
730
|
+
```kotlin
|
|
731
|
+
val user = UserProfile()
|
|
732
|
+
user.userID = "" // 您的 userId
|
|
733
|
+
user.avatarURL = "" // 头像的 url
|
|
734
|
+
user.nickname = "" // 需要设置的昵称
|
|
735
|
+
|
|
736
|
+
LoginStore.shared.setSelfInfo(user, object : CompletionHandler {
|
|
737
|
+
override fun onSuccess() {
|
|
738
|
+
// 设置成功回调
|
|
739
|
+
}
|
|
740
|
+
override fun onFailure(code: Int, desc: String) {
|
|
741
|
+
// 设置失败回调
|
|
742
|
+
}
|
|
743
|
+
})
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
**setSelfInfo 接口参数详细说明:**
|
|
747
|
+
|
|
748
|
+
| 参数 | 类型 | 是否必填 | 说明 |
|
|
749
|
+
|------|------|----------|------|
|
|
750
|
+
| userProfile | UserProfile | 是 | 用户信息结构体。`userID`(String):用户的 ID。`avatarURL`(String):用户头像的 URL。`nickname`(String):用户的昵称 |
|
|
751
|
+
| completion | CompletionHandler | 否 | 操作完成回调 |
|
|
752
|
+
|
|
753
|
+
### 切换布局模式
|
|
754
|
+
|
|
755
|
+
您可以通过 [setLayoutTemplate](https://liteav.sdk.cloudcachetci.com/doc/product/tuikit/atomic-x/android/zh/-atomic-x%20-core%20-a-p-i/io.trtc.tuikit.atomicxcore.api.view/-call-core-view/set-layout-template.html) 接口灵活切换布局模式。若未主动配置,**CallCoreView** 将根据通话人数自动适配:1v1 场景下默认采用 `Float` 模式,多人通话场景下则自动切换为 `Grid` 模式。
|
|
756
|
+
|
|
757
|
+
**三种布局模式对比:**
|
|
758
|
+
|
|
759
|
+
| 模式 | 布局逻辑 | 交互特性 |
|
|
760
|
+
|------|----------|----------|
|
|
761
|
+
| **Float** | 呼叫等待时全屏显示己方画面;接通后全屏显示对方画面,己方画面以悬浮小窗展示。 | 支持小窗拖拽移动,点击小窗可实现大小画面互换。 |
|
|
762
|
+
| **Grid** | 所有成员画面呈网格状平铺排列成宫格模式布局,适用 2 人以上通话。 | 支持点击特定成员画面放大查看。 |
|
|
763
|
+
| **Pip** | 1v1 场景固定显示对方画面;多人场景采用 Active Speaker 策略,自动全屏展示正在说话的用户。 | 等待时显示自己的画面,接通后还会显示通话计时。 |
|
|
764
|
+
|
|
765
|
+
**setLayoutTemplate 示例代码:**
|
|
766
|
+
|
|
767
|
+
```kotlin
|
|
768
|
+
private fun setLayoutTemplate() {
|
|
769
|
+
val callCoreView = CallCoreView(context)
|
|
770
|
+
val template = CallLayoutTemplate.Grid
|
|
771
|
+
callCoreView.setLayoutTemplate(template)
|
|
772
|
+
setContentView(callCoreView)
|
|
773
|
+
}
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
**setLayoutTemplate 接口参数详细说明:**
|
|
777
|
+
|
|
778
|
+
| 参数 | 类型 | 是否必填 | 说明 |
|
|
779
|
+
|------|------|----------|------|
|
|
780
|
+
| template | CallLayoutTemplate | 是 | CallCoreView 的布局模式:`CallLayoutTemplate.Float`、`CallLayoutTemplate.Grid`、`CallLayoutTemplate.Pip` |
|
|
781
|
+
|
|
782
|
+
### 实现画中画功能
|
|
783
|
+
|
|
784
|
+
画中画功能需 Android 8.0 (API 26) 及以上版本支持。建议您在进入画中画模式时,将 **CallCoreView** 切换为 `Pip` 布局获得更好的体验。
|
|
785
|
+
|
|
786
|
+
**进入画中画:**
|
|
787
|
+
|
|
788
|
+
```kotlin
|
|
789
|
+
private fun enterPictureInPictureModeWithBuild() {
|
|
790
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && hasPipModePermission()) {
|
|
791
|
+
val pictureInPictureParams = PictureInPictureParams.Builder()
|
|
792
|
+
val floatViewWidth = resources.getDimensionPixelSize(R.dimen.callkit_video_small_view_width)
|
|
793
|
+
val floatViewHeight = resources.getDimensionPixelSize(R.dimen.callkit_video_small_view_height)
|
|
794
|
+
val aspectRatio = Rational(floatViewWidth, floatViewHeight)
|
|
795
|
+
pictureInPictureParams.setAspectRatio(aspectRatio).build()
|
|
796
|
+
this.enterPictureInPictureMode(pictureInPictureParams.build())
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
**进入画中画成功后切换布局:**
|
|
802
|
+
|
|
803
|
+
```kotlin
|
|
804
|
+
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
|
|
805
|
+
super.onPictureInPictureModeChanged(isInPictureInPictureMode)
|
|
806
|
+
if (isInPictureInPictureMode) {
|
|
807
|
+
callCoreView.setLayoutTemplate(CallLayoutTemplate.Pip)
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
**从画中画返回到通话界面恢复布局:**
|
|
813
|
+
|
|
814
|
+
```kotlin
|
|
815
|
+
override fun onResume() {
|
|
816
|
+
super.onResume()
|
|
817
|
+
val allParticipants = CallStore.shared.observerState.allParticipants.value
|
|
818
|
+
if (allParticipants.size > 2) {
|
|
819
|
+
callCoreView?.setLayoutTemplate(CallLayoutTemplate.Grid)
|
|
820
|
+
} else {
|
|
821
|
+
callCoreView?.setLayoutTemplate(CallLayoutTemplate.Float)
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
> **注意**:需要在 `AndroidManifest.xml` 中为通话 Activity 声明独立任务栈,避免画中画时 UI 异常:
|
|
827
|
+
> ```xml
|
|
828
|
+
> <activity
|
|
829
|
+
> android:name=".view.CallActivity"
|
|
830
|
+
> android:launchMode="singleTask"
|
|
831
|
+
> android:taskAffinity="${applicationId}.call"/>
|
|
832
|
+
> ```
|
|
833
|
+
|
|
834
|
+
### 设置通话过程中屏幕常亮
|
|
835
|
+
|
|
836
|
+
通话过程中保持屏幕常亮是通话应用的基本需求。Android 提供了 `WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON` 标志来实现此功能。
|
|
837
|
+
|
|
838
|
+
```kotlin
|
|
839
|
+
class CallActivity : AppCompatActivity() {
|
|
840
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
841
|
+
super.onCreate(savedInstanceState)
|
|
842
|
+
window.addFlags(
|
|
843
|
+
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
|
|
844
|
+
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
|
|
845
|
+
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
|
|
846
|
+
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
|
847
|
+
)
|
|
848
|
+
setContentView(R.layout.activity_call)
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
### 开启后台采集音频/视频
|
|
854
|
+
|
|
855
|
+
**1. 配置权限与服务**(`AndroidManifest.xml`):
|
|
856
|
+
|
|
857
|
+
```xml
|
|
858
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
859
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
|
860
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>
|
|
861
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
|
|
862
|
+
<application>
|
|
863
|
+
<service
|
|
864
|
+
android:name=".CallForegroundService"
|
|
865
|
+
android:enabled="true"
|
|
866
|
+
android:exported="false"
|
|
867
|
+
android:foregroundServiceType="camera|microphone"/>
|
|
868
|
+
</application>
|
|
869
|
+
</manifest>
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
**2. 创建前台服务类:**
|
|
873
|
+
|
|
874
|
+
```kotlin
|
|
875
|
+
import android.app.Notification
|
|
876
|
+
import android.app.NotificationChannel
|
|
877
|
+
import android.app.NotificationManager
|
|
878
|
+
import android.app.Service
|
|
879
|
+
import android.content.Context
|
|
880
|
+
import android.content.Intent
|
|
881
|
+
import android.os.Build
|
|
882
|
+
import android.os.IBinder
|
|
883
|
+
import androidx.core.app.NotificationCompat
|
|
884
|
+
|
|
885
|
+
class CallForegroundService : Service() {
|
|
886
|
+
companion object {
|
|
887
|
+
private const val NOTIFICATION_ID = 1001
|
|
888
|
+
private const val CHANNEL_ID = "call_foreground_channel"
|
|
889
|
+
|
|
890
|
+
fun start(context: Context) {
|
|
891
|
+
val intent = Intent(context, CallForegroundService::class.java)
|
|
892
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
893
|
+
context.startForegroundService(intent)
|
|
894
|
+
} else {
|
|
895
|
+
context.startService(intent)
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
fun stop(context: Context) {
|
|
900
|
+
val intent = Intent(context, CallForegroundService::class.java)
|
|
901
|
+
context.stopService(intent)
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
override fun onCreate() {
|
|
906
|
+
super.onCreate()
|
|
907
|
+
createNotificationChannel()
|
|
908
|
+
startForeground(NOTIFICATION_ID, createNotification())
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
override fun onBind(intent: Intent?): IBinder? = null
|
|
912
|
+
|
|
913
|
+
private fun createNotification(): Notification {
|
|
914
|
+
return NotificationCompat.Builder(this, CHANNEL_ID)
|
|
915
|
+
.setContentTitle("正在通话中")
|
|
916
|
+
.setContentText("应用正在后台运行以保持通话")
|
|
917
|
+
.setSmallIcon(android.R.drawable.ic_menu_call)
|
|
918
|
+
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
919
|
+
.build()
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
private fun createNotificationChannel() {
|
|
923
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
924
|
+
val channel = NotificationChannel(
|
|
925
|
+
CHANNEL_ID, "通话保活服务", NotificationManager.IMPORTANCE_HIGH
|
|
926
|
+
)
|
|
927
|
+
val manager = getSystemService(NotificationManager::class.java)
|
|
928
|
+
manager.createNotificationChannel(channel)
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
## 常见问题
|
|
935
|
+
|
|
936
|
+
### 进入画中画模式后,应用内跳转到其他界面后画面异常?
|
|
937
|
+
|
|
938
|
+
Android 画中画基于任务栈运作,默认所有 Activity 共享同一任务栈,新启动的 Activity 会错误显示在画中画窗口内。解决方法:在 `AndroidManifest.xml` 中将通话 Activity 声明为独立任务栈(`launchMode="singleTask"` + `taskAffinity`)。
|
|
939
|
+
|
|
940
|
+
### 在通话邀请超时时间内,被邀请者如果离线再上线,能否收到来电事件?
|
|
941
|
+
|
|
942
|
+
单人通话时,超时时间内上线会触发来电邀请;群组通话时,超时时间内上线会拉起未处理的 20 条群消息,若存在通话邀请则触发来电邀请事件。
|