@tencent-rtc/mcp 1.7.3 → 1.7.4
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/CHANGELOG.md +13 -0
- package/README.md +13 -13
- package/knowledge/path-meta.js +23 -1
- package/package.json +1 -1
- package/resource/index/manifest.json +1 -1
- package/resource/knowledge/callkit/best-practice/android/video-chat-dating.md +497 -0
- package/resource/knowledge/callkit/best-practice/flutter/android-keep-call-alive-background.md +79 -0
- package/resource/knowledge/callkit/best-practice/flutter/background-incoming-call-no-notification.md +99 -0
- package/resource/knowledge/callkit/best-practice/flutter/ios-keep-call-alive-background.md +93 -0
- package/resource/knowledge/callkit/best-practice/flutter/ios-startup-crash-version-voip.md +68 -0
- package/resource/knowledge/callkit/core-sdk/miniprogram/call-engine.md +386 -0
- package/resource/knowledge/callkit/core-sdk/miniprogram/call-event.md +315 -0
- package/resource/knowledge/callkit/faq/uniapp-miniprogram.md +204 -0
- package/resource/knowledge/callkit/preset/harmonyos/api.md +362 -0
- package/resource/knowledge/callkit/preset/harmonyos/integration.md +318 -0
- package/resource/knowledge/callkit/preset/miniprogram/api-uniapp.md +430 -0
- package/resource/knowledge/callkit/preset/miniprogram/api.md +293 -0
- package/resource/knowledge/callkit/preset/miniprogram/integration.md +294 -0
- package/resource/knowledge/callkit/preset/miniprogram/uniapp-integration.md +224 -0
- package/resource/knowledge/callkit/preset/uni-app/api.md +339 -0
- package/resource/knowledge/callkit/preset/uni-app/integration.md +243 -0
- package/resource/knowledge/chat/preset/react/h5/integration.md +964 -0
- package/resource/knowledge/chat/preset/vue/h5/integration.md +717 -0
- package/resource/knowledge/rtcengine/web/plugin/23-advanced-support-detection.md +6 -6
- package/resource/knowledge/rtcengine/web/plugin/31-advanced-plugins-overview.md +8 -6
- package/resource/knowledge/rtcengine/web/plugin/42-advanced-realtime-transcriber.md +30 -10
- package/server.js +1 -1
- package/utils/report-cls-client.js +1 -1
- package/utils/report-es-client.js +1 -1
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Android 1v1 Video Chat Dating — Quick Start (VideoChat Demo Setup & Component Integration)
|
|
3
|
+
product: call
|
|
4
|
+
frameworks: [android]
|
|
5
|
+
scope: platform_specific
|
|
6
|
+
tags: [video chat, 1v1, VideoChat, dating, video-chat-solution, TUICallKit, Android, Demo, quick start, social, beauty filter, offline push, call reachability]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
This guide walks you through running the video calling Demo quickly. Following this document, you can get the Demo running in 10 minutes and experience a fully-featured 1v1 video chat dating app with a complete UI.
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
### Environment Setup
|
|
14
|
+
- Install [Android Studio](https://developer.android.com/studio).
|
|
15
|
+
|
|
16
|
+
- Two or more Android 5.0+ devices.
|
|
17
|
+
|
|
18
|
+
### Activate the Service
|
|
19
|
+
|
|
20
|
+
## Run the Demo
|
|
21
|
+
|
|
22
|
+
### Step 1: Download the Demo
|
|
23
|
+
|
|
24
|
+
You can download the [VideoChat Demo](https://github.com/Tencent-RTC/video-chat-solution) source code directly from GitHub, or run the following command:
|
|
25
|
+
``` java
|
|
26
|
+
git clone https://github.com/Tencent-RTC/video-chat-solution.git
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Step 2: Configure the Demo
|
|
30
|
+
1. **Configure SDKAppID and SecretKey (required):** Open the file `video-chat-solution/blob/main/android/app/src/main/java/io/trtc/uikit/demo/debug/GenerateTestUserSig.java` and fill in the `SDKAppID` and `SDKSecretKey` obtained during service activation.
|
|
31
|
+
|
|
32
|
+
2. **Configure Beauty Filters (optional):** The Demo includes Tencent basic beauty filters by default, which require a License to take effect. Refer to the [License Guide](https://cloud.tencent.com/document/product/616/79137) to obtain the LicenseURL and LicenseKey, and fill them into `video-chat-solution/android/app/src/main/java/io/trtc/uikit/demo/debug/GenerateTestUserSig.java`.
|
|
33
|
+
|
|
34
|
+
### Step 3: Run the Demo
|
|
35
|
+
1. **Import recommended test users:** For a quick trial, run the script `video-chat-solution/tools/import_users_to_video_chat.py` (fill in your AppID and SecretKey). The script will automatically register 6 recommended test accounts. After logging in, you can see them in the "Dating" module.
|
|
36
|
+
|
|
37
|
+
2. **Build and run:** Select a device and run the Demo.
|
|
38
|
+
|
|
39
|
+
3. **Demo running successfully:** After logging in, the following screen is displayed.
|
|
40
|
+
|
|
41
|
+
### Step 4: Video and Chat Interaction
|
|
42
|
+
1. **Interact with recommended users:** You can use another device B to log in with a "recommended user" account (e.g., UserID: VideoChatlinxiaoyu) to complete 1v1 chat and video interaction.
|
|
43
|
+
|
|
44
|
+
- **Device A:** Log in and follow the recommended user "Chen Kexin", then start chatting and video calling.
|
|
45
|
+
|
|
46
|
+
- **Device B:** Log in and start chatting and video calling.
|
|
47
|
+
|
|
48
|
+
## Quick Integration
|
|
49
|
+
|
|
50
|
+
For fast deployment, you can directly integrate the UI components from the Demo to implement core features like 1v1 chat and calling.
|
|
51
|
+
|
|
52
|
+
### Step 1: Integrate Components
|
|
53
|
+
1. Download the [VideoChat Demo](https://github.com/Tencent-RTC/video-chat-solution) source code from the GitHub repository, and copy the `video-chat` and `tuikit` components to your project directory.
|
|
54
|
+
|
|
55
|
+
2. Add the following code to `settings.gradle` to complete the integration:
|
|
56
|
+
|
|
57
|
+
``` java
|
|
58
|
+
// Import the top-level application module
|
|
59
|
+
include ':app'
|
|
60
|
+
|
|
61
|
+
// Import the 1v1 video chat dating UI module
|
|
62
|
+
include ':video-chat'
|
|
63
|
+
|
|
64
|
+
// Import the common module
|
|
65
|
+
include(":atomic_x")
|
|
66
|
+
project(":atomic_x").projectDir = file("./tuikit/atomic_x")
|
|
67
|
+
|
|
68
|
+
// Import the chat module
|
|
69
|
+
include ':tuichat'
|
|
70
|
+
project(':tuichat').projectDir = file("./tuikit/chat")
|
|
71
|
+
|
|
72
|
+
// Import the beauty filter module
|
|
73
|
+
include ':tebeautykit'
|
|
74
|
+
project(':tebeautykit').projectDir = file("./tuikit/tebeautykit")
|
|
75
|
+
|
|
76
|
+
// Import the effects player module
|
|
77
|
+
include ':tceffectplayerkit'
|
|
78
|
+
project(':tceffectplayerkit').projectDir = file("./tuikit/tceffectplayerkit")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Step 2: Complete Login
|
|
82
|
+
|
|
83
|
+
Authentication is required before using component features. Call the LoginStore `login` API with the sdkAppID, userID, and userSig obtained above:
|
|
84
|
+
``` java
|
|
85
|
+
|
|
86
|
+
import io.trtc.tuikit.atomicxcore.api.CompletionHandler
|
|
87
|
+
import io.trtc.tuikit.atomicxcore.api.login.LoginStore
|
|
88
|
+
|
|
89
|
+
LoginStore.shared.login(
|
|
90
|
+
context,
|
|
91
|
+
sdkAppID, // Int, obtained from the console
|
|
92
|
+
userID, // String
|
|
93
|
+
userSig, // String, generated from the console or server
|
|
94
|
+
object : CompletionHandler {
|
|
95
|
+
override fun onSuccess() {
|
|
96
|
+
// Login successful, navigate to conversation list or chat page
|
|
97
|
+
}
|
|
98
|
+
override fun onFailure(code: Int, desc: String) {
|
|
99
|
+
// Login failed, show error dialog
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
> **Note:**
|
|
106
|
+
> In production environments, it is recommended to generate UserSig on your server. The app requests a dynamic UserSig from the backend when needed. See Server-side UserSig Generation.
|
|
107
|
+
>
|
|
108
|
+
|
|
109
|
+
### Step 3: Build the Recommended Users List
|
|
110
|
+
|
|
111
|
+
1. **Add the page:** Add the "Recommended Users List (MeetPage)" to your project:
|
|
112
|
+
|
|
113
|
+
``` java
|
|
114
|
+
import com.tencent.qcloud.tuikit.videochat.page.meet.MeetPage
|
|
115
|
+
|
|
116
|
+
class MeetPageFragment : Fragment() {
|
|
117
|
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
118
|
+
super.onViewCreated(view, savedInstanceState)
|
|
119
|
+
addMeetPage()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private fun addMeetPage() {
|
|
123
|
+
val meetPage = MeetPage(requireContext())
|
|
124
|
+
val container = findViewById<FrameLayout>(R.id.meet_container)
|
|
125
|
+
container.addView(meetPage, MATCH_PARENT, MATCH_PARENT)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
2. **Provide data:** You can use `setPageUsers` to provide the recommended user list, and listen for pull-up refresh (`onLoadMoreRequested`) to append users and pull-down refresh (`onRefreshRequested`) to fully update the user list.
|
|
130
|
+
|
|
131
|
+
``` java
|
|
132
|
+
import com.tencent.qcloud.tuikit.videochat.page.meet.MeetPage
|
|
133
|
+
|
|
134
|
+
meetPage.setListener(object : MeetPage.Listener {
|
|
135
|
+
// Triggered on pull-down refresh
|
|
136
|
+
override fun onRefreshRequested() {
|
|
137
|
+
// Full update of user list
|
|
138
|
+
fetchUsers { users, hasMore ->
|
|
139
|
+
// Set user list
|
|
140
|
+
meetPage.setPageUsers(users, hasMore)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Triggered on pull-up refresh
|
|
145
|
+
override fun onLoadMoreRequested() {
|
|
146
|
+
// Append new users at the end
|
|
147
|
+
fetchNextPage { users, hasMore ->
|
|
148
|
+
// Set user list
|
|
149
|
+
meetPage.setPageUsers(users, hasMore)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
- **setPageUsers details:**
|
|
155
|
+
|
|
156
|
+
``` java
|
|
157
|
+
fun setPageUsers(users: List<V2TIMUserFullInfo>, hasMore: Boolean)
|
|
158
|
+
```
|
|
159
|
+
| **Parameter** | **Type** | **Description** |
|
|
160
|
+
| --- | --- | --- |
|
|
161
|
+
| users | List<[V2TIMUserFullInfo](https://im.sdk.qcloud.com/doc/zh-cn/classcom_1_1tencent_1_1imsdk_1_1v2_1_1V2TIMUserFullInfo.html)> | User list. [V2TIMUserFullInfo](https://im.sdk.qcloud.com/doc/zh-cn/classcom_1_1tencent_1_1imsdk_1_1v2_1_1V2TIMUserFullInfo.html) contains user ID, nickname, avatar, gender, and other key information. |
|
|
162
|
+
| hasMore | Boolean | Whether there is a next page:<br>- true: more pages available, scrolling to the bottom will trigger onLoadMoreRequested()<br>- false: no more data, scrolling to the bottom will not trigger the callback |
|
|
163
|
+
|
|
164
|
+
3. **Switch layout (optional):** MeetPage provides two layout modes — "Card" and "List". You can use `setLayoutTemplate` to choose your preferred layout.
|
|
165
|
+
|
|
166
|
+
``` java
|
|
167
|
+
import com.tencent.qcloud.tuikit.videochat.page.meet.MeetPage
|
|
168
|
+
|
|
169
|
+
class MeetPageFragment : Fragment() {
|
|
170
|
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
171
|
+
super.onViewCreated(view, savedInstanceState)
|
|
172
|
+
addMeetPage()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private fun addMeetPage() {
|
|
176
|
+
val meetPage = MeetPage(requireContext())
|
|
177
|
+
// Set layout
|
|
178
|
+
meetPage.setLayoutTemplate(MeetPage.Template.LIST) // List layout (default)
|
|
179
|
+
// meetPage.setLayoutTemplate(MeetPage.Template.GRID) // Card layout
|
|
180
|
+
val container = findViewById<FrameLayout>(R.id.meet_container)
|
|
181
|
+
container.addView(meetPage, MATCH_PARENT, MATCH_PARENT)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
- **setLayoutTemplate details:**
|
|
186
|
+
|
|
187
|
+
``` java
|
|
188
|
+
fun setLayoutTemplate(template: MeetPage.Template)
|
|
189
|
+
```
|
|
190
|
+
| **Parameter** | **Type** | **Description** |
|
|
191
|
+
| --- | --- | --- |
|
|
192
|
+
| template | MeetPage.Template | Layout type:<br>- MeetPage.Template.LIST: List layout.<br>- MeetPage.Template.GRID: Card layout. |
|
|
193
|
+
|
|
194
|
+
### Step 4: Build the Conversation List
|
|
195
|
+
|
|
196
|
+
The conversation list simply requires creating a `ConversationPage` object and adding it to your layout. The conversation list reads recent chat records from the database. When a user taps a chat record, `ConversationPage` navigates to the chat page by default.
|
|
197
|
+
|
|
198
|
+
- **Sample code:**
|
|
199
|
+
|
|
200
|
+
``` java
|
|
201
|
+
val conversationPage = ConversationPage()
|
|
202
|
+
|
|
203
|
+
// R.id.container is the FrameLayout in your layout that holds the conversation list Fragment
|
|
204
|
+
supportFragmentManager.beginTransaction()
|
|
205
|
+
.add(R.id.container, conversationPage)
|
|
206
|
+
.commit()
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Step 5: Build the Chat Page
|
|
210
|
+
|
|
211
|
+
The chat page displays and sends messages. You can build the page as follows:
|
|
212
|
+
|
|
213
|
+
- **Sample code:**
|
|
214
|
+
|
|
215
|
+
``` java
|
|
216
|
+
Bundle param = new Bundle();
|
|
217
|
+
param.putInt(TUIConstants.TUIChat.CHAT_TYPE, V2TIMConversation.V2TIM_C2C);
|
|
218
|
+
val userId = "jack" // The other party's UserId
|
|
219
|
+
param.putString(TUIConstants.TUIChat.CHAT_ID, userId);
|
|
220
|
+
TUICore.startActivity("TUIC2CChatActivity", param);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Step 6: Audio/Video Calling
|
|
224
|
+
|
|
225
|
+
In social entertainment scenarios, call requirements are complex and diverse. We recommend using the [AtomicxCore SDK](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) to flexibly customize your calling scenarios. Taking the video-chat module's call page (VideoCallPage) as an example, it is built on the SDK's CallStore module and deeply integrates core UI elements such as beauty filters, follow actions, and participant info display. You can use it as source code reference or integrate it directly into your project to quickly build a custom call interface.
|
|
226
|
+
|
|
227
|
+
- **Initiate a video call — sample code:** You can use [CallStore.calls](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/calls.html) to initiate a video call and launch the call page on success.
|
|
228
|
+
|
|
229
|
+
``` java
|
|
230
|
+
// Initiate a video call using AtomicxCore SDK's CallStore.calls
|
|
231
|
+
CallStore.shared.calls(list, mediaType, params, object : CompletionHandler {
|
|
232
|
+
override fun onFailure(code: Int, desc: String) {
|
|
233
|
+
completion?.onFailure(code, desc)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
override fun onSuccess() {
|
|
237
|
+
completion?.onSuccess()
|
|
238
|
+
// Call succeeded, launch VideoCallPage
|
|
239
|
+
val intent = Intent(context, VideoCallPage::class.java)
|
|
240
|
+
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
|
241
|
+
context.startActivity(intent)
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
```
|
|
245
|
+
- **AtomicxCore SDK call module — core API reference:**
|
|
246
|
+
|
|
247
|
+
| **Module** | **Description** |
|
|
248
|
+
|---------|---------|
|
|
249
|
+
| [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) | Core call view component. Automatically listens to CallStore data and renders the video. Provides layout switching, avatar, and icon customization capabilities. |
|
|
250
|
+
| [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) | Call lifecycle management: make, accept, reject, and hang up calls. Real-time access to participants' audio/video status, call duration, call records, etc. |
|
|
251
|
+
| [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) | Audio/video device control: microphone (on/off, volume), camera (on/off, switch, quality), screen sharing, real-time device status monitoring. |
|
|
252
|
+
|
|
253
|
+
## More Features
|
|
254
|
+
|
|
255
|
+
You can integrate the following features to enhance your app's competitiveness and deliver a better user experience.
|
|
256
|
+
| **Feature** | **Description** | **Integration Guide** |
|
|
257
|
+
| --- | --- | --- |
|
|
258
|
+
| Call Billing | We provide precise server-side call status callbacks. You can listen to call-start and call-end server callbacks to implement call billing. | - Post-call-start callback<br>- Post-call-end callback |
|
|
259
|
+
| Beauty Filters | We offer two beauty filter solutions: **Basic Beauty** (built-in) and **Advanced Beauty** (requires additional integration and fee). Choose based on your needs. | Integrate Beauty Filters |
|
|
260
|
+
| Voice Effects | TRTC audio voice-changing features and usage scenarios to help developers create rich audio interaction experiences. | Integrate Voice Effects |
|
|
261
|
+
| AI Real-time Translation | An essential feature for going global, helping users break language barriers. | Integrate AI Real-time Translation |
|
|
262
|
+
| Audio/Video Content Moderation | Essential for 1v1 social entertainment apps. Helps moderate audio/video streams and text content to prevent app removal from stores. | - [Audio/Video Content Moderation](https://cloud.tencent.com/document/product/647/77791)<br>- [Text Content Moderation](https://cloud.tencent.com/document/product/269/103732) |
|
|
263
|
+
|
|
264
|
+
## FAQ
|
|
265
|
+
|
|
266
|
+
### Why are the Demo's beauty filters not working?
|
|
267
|
+
|
|
268
|
+
The Demo includes built-in beauty filter functionality, but it requires License authentication. Please check whether the `LicenseURL` and `LicenseKey` in `video-chat-solution/android/app/src/main/java/io/trtc/uikit/demo/debug/GenerateTestUserSig.java` are correct. Refer to the [License Guide](https://cloud.tencent.com/document/product/616/79137) for details.
|
|
269
|
+
|
|
270
|
+
### How to customize the UI?
|
|
271
|
+
|
|
272
|
+
If you have extensive UI customization requirements, we recommend:
|
|
273
|
+
1. **Call module:** You can use the AtomicxCore SDK (see sample code: No-UI Integration) to customize the call module's interface. Core modules:
|
|
274
|
+
|
|
275
|
+
| **Module** | **Description** |
|
|
276
|
+
|---------|---------|
|
|
277
|
+
| [LoginStore](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/index.html) | Login management class for user login, logout, and user info management. Provides a complete set of login management APIs including login, logout, and profile configuration. |
|
|
278
|
+
| [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) | Core call view component. Automatically listens to CallStore data and renders the video. Provides layout switching, avatar, and icon customization capabilities. |
|
|
279
|
+
| [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) | Call lifecycle management: make, accept, reject, and hang up calls. Real-time access to participants' audio/video status, call duration, call records, etc. |
|
|
280
|
+
| [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) | Audio/video device control: microphone (on/off, volume), camera (on/off, switch, quality), screen sharing, real-time device status monitoring. |
|
|
281
|
+
|
|
282
|
+
2. **Chat module:** Refer to the [IM SDK No-UI Integration](https://cloud.tencent.com/document/product/269/75283).
|
|
283
|
+
|
|
284
|
+
### How to customize call status message display in chat?
|
|
285
|
+
|
|
286
|
+
When the call status changes, IM delivers a call status change message. The signaling protocol is as follows. Depending on whether you use our provided UI code, you have two solutions:
|
|
287
|
+
|
|
288
|
+
| **Question** | **Description** |
|
|
289
|
+
|---------|---------|
|
|
290
|
+
| How to identify "call initiated" | actionType = 1 and cmd = 'videoCall' or 'audioCall' |
|
|
291
|
+
| How to identify "call accepted" | actionType = 3 |
|
|
292
|
+
| How to identify "call ended" and "call duration" | actionType = 1 and cmd = 'hangup'; duration from the call_end field (unit: seconds) |
|
|
293
|
+
| How to identify "call cancelled" | actionType = 2 |
|
|
294
|
+
| How to identify "call rejected" | actionType = 4 |
|
|
295
|
+
| How to identify "call rejected due to busy line" | actionType = 4 and cmd = "line_busy" |
|
|
296
|
+
| How callee identifies "call timed out" | actionType = 5 |
|
|
297
|
+
|
|
298
|
+
- **Option 1: Modify the provided UI source code**
|
|
299
|
+
|
|
300
|
+
Locate the source file `tuikit/TUIChat/tuichat/src/main/java/com/tencent/qcloud/tuikit/tuichat/bean/CallModel.java` and modify the `getContentForSimplifyAppearance` function to customize call status message display.
|
|
301
|
+
|
|
302
|
+
``` java
|
|
303
|
+
/**
|
|
304
|
+
* Customize 1v1 call status message display text
|
|
305
|
+
*
|
|
306
|
+
* Location: CallModel.java -> getContentForSimplifyAppearance()
|
|
307
|
+
* Only modify the text within the participantType == CALL_PARTICIPANT_TYPE_C2C branch.
|
|
308
|
+
*
|
|
309
|
+
* Available fields within the method:
|
|
310
|
+
* - protocolType: call protocol type (initiate/accept/reject/cancel/hangup/timeout/busy)
|
|
311
|
+
* - participantRole: current user role (CALLER / CALLEE)
|
|
312
|
+
* - streamMediaType: media type (VOICE / VIDEO)
|
|
313
|
+
* - duration: call duration (seconds)
|
|
314
|
+
*/
|
|
315
|
+
|
|
316
|
+
// =================== Example: Customize 1v1 call display text ===================
|
|
317
|
+
|
|
318
|
+
// In the getContentForSimplifyAppearance() method,
|
|
319
|
+
// find the if (participantType == CALL_PARTICIPANT_TYPE_C2C) branch and modify:
|
|
320
|
+
|
|
321
|
+
if (participantType == CALL_PARTICIPANT_TYPE_C2C) {
|
|
322
|
+
// Determine media type for text prefix
|
|
323
|
+
val mediaPrefix = if (streamMediaType == CALL_STREAM_MEDIA_TYPE_VIDEO) {
|
|
324
|
+
"[Video Call]" // Your custom video call text
|
|
325
|
+
} else {
|
|
326
|
+
"[Voice Call]" // Your custom voice call text
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
val isCaller = (participantRole == CALL_PARTICIPANT_ROLE_CALLER)
|
|
330
|
+
|
|
331
|
+
// display is the call status message shown in chat
|
|
332
|
+
display = when (protocolType) {
|
|
333
|
+
// Call rejected (actionType = 4, excluding line_busy)
|
|
334
|
+
CALL_PROTOCOL_TYPE_REJECT -> {
|
|
335
|
+
if (isCaller) {
|
|
336
|
+
"$mediaPrefix Declined by the other party"
|
|
337
|
+
} else {
|
|
338
|
+
"$mediaPrefix Declined"
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Call cancelled (actionType = 2, caller cancelled)
|
|
343
|
+
CALL_PROTOCOL_TYPE_CANCEL -> {
|
|
344
|
+
if (isCaller) {
|
|
345
|
+
"$mediaPrefix Cancelled"
|
|
346
|
+
} else {
|
|
347
|
+
"$mediaPrefix Cancelled by the other party"
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Call ended (actionType = 1, cmd = "hangup")
|
|
352
|
+
// duration field is call duration in seconds
|
|
353
|
+
CALL_PROTOCOL_TYPE_HANGUP -> {
|
|
354
|
+
val formattedDuration = DateTimeUtil.formatSecondsTo00(duration)
|
|
355
|
+
"$mediaPrefix Duration $formattedDuration"
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Timed out (actionType = 5)
|
|
359
|
+
CALL_PROTOCOL_TYPE_TIMEOUT -> {
|
|
360
|
+
if (isCaller) {
|
|
361
|
+
"$mediaPrefix No answer"
|
|
362
|
+
} else {
|
|
363
|
+
"$mediaPrefix Missed"
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Busy line rejection (actionType = 4, top-level JSON contains "line_busy" field)
|
|
368
|
+
CALL_PROTOCOL_TYPE_LINE_BUSY -> {
|
|
369
|
+
if (isCaller) {
|
|
370
|
+
"$mediaPrefix The other party is busy"
|
|
371
|
+
} else {
|
|
372
|
+
"$mediaPrefix Busy, missed call"
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Call initiated (actionType = 1, cmd = "videoCall" or "audioCall")
|
|
377
|
+
CALL_PROTOCOL_TYPE_SEND -> {
|
|
378
|
+
"$mediaPrefix Call initiated"
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Call accepted (actionType = 3)
|
|
382
|
+
CALL_PROTOCOL_TYPE_ACCEPT -> {
|
|
383
|
+
"$mediaPrefix Accepted"
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
else -> {
|
|
387
|
+
context.getString(R.string.invalid_command)
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
- **Option 2: Implement custom call status message display**
|
|
393
|
+
|
|
394
|
+
You can set an [IM message listener](https://cloud.tencent.com/document/product/269/75318) to implement call status message display. Sample code:
|
|
395
|
+
|
|
396
|
+
``` java
|
|
397
|
+
// Set message listener
|
|
398
|
+
V2TIMManager.getMessageManager().addAdvancedMsgListener(advancedMsgListener)
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Receive new message
|
|
402
|
+
* @param msg message
|
|
403
|
+
*/
|
|
404
|
+
override fun onRecvNewMessage(msg: V2TIMMessage) {
|
|
405
|
+
// Get signaling info; returns null for non-signaling messages
|
|
406
|
+
val signalingInfo = V2TIMManager.getSignalingManager().getSignalingInfo(msg)
|
|
407
|
+
?: return
|
|
408
|
+
|
|
409
|
+
// Only handle 1v1 private chats (empty groupID = private chat, non-empty = group chat)
|
|
410
|
+
if (!signalingInfo.groupID.isNullOrEmpty()) {
|
|
411
|
+
return
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Parse the signaling data field (JSON format)
|
|
415
|
+
val gson = Gson()
|
|
416
|
+
val jsonData: Map<String, Any> = try {
|
|
417
|
+
gson.fromJson(signalingInfo.data, object : TypeToken<Map<String, Any>>() {}.type)
|
|
418
|
+
} catch (e: Exception) {
|
|
419
|
+
return
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Verify businessID to confirm this is a call signaling message
|
|
423
|
+
val businessId = jsonData["businessID"] as? String
|
|
424
|
+
if (businessId != "av_call" && businessId != "tuikit_calling") {
|
|
425
|
+
return
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Extract cmd from the data sub-object
|
|
429
|
+
val dataMap = jsonData["data"] as? Map<*, *>
|
|
430
|
+
val cmd = dataMap?.get("cmd") as? String
|
|
431
|
+
|
|
432
|
+
// Determine call status based on actionType
|
|
433
|
+
when (signalingInfo.actionType) {
|
|
434
|
+
|
|
435
|
+
// ========== actionType = 1: Send invitation ==========
|
|
436
|
+
V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_INVITE -> {
|
|
437
|
+
when (cmd) {
|
|
438
|
+
// Initiate video call
|
|
439
|
+
"videoCall" -> {
|
|
440
|
+
val mediaType = "video"
|
|
441
|
+
Log.i(TAG, "Call initiated | Type: $mediaType, Caller: ${msg.sender}")
|
|
442
|
+
}
|
|
443
|
+
// Initiate voice call
|
|
444
|
+
"audioCall" -> {
|
|
445
|
+
val mediaType = "audio"
|
|
446
|
+
Log.i(TAG, "Call initiated | Type: $mediaType, Caller: ${msg.sender}")
|
|
447
|
+
}
|
|
448
|
+
// Hang up (call ended)
|
|
449
|
+
"hangup" -> {
|
|
450
|
+
// Call duration from top-level call_end field, unit: seconds
|
|
451
|
+
val duration = jsonData["call_end"]
|
|
452
|
+
?.toString()?.toDoubleOrNull()?.toInt() ?: 0
|
|
453
|
+
Log.i(TAG, "Call ended | Duration: ${duration}s")
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// ========== actionType = 2: Cancel call (caller cancelled) ==========
|
|
459
|
+
V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_CANCEL_INVITE -> {
|
|
460
|
+
Log.i(TAG, "Call cancelled | Caller: ${msg.sender}")
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// ========== actionType = 3: Accept call (callee accepted) ==========
|
|
464
|
+
V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_ACCEPT_INVITE -> {
|
|
465
|
+
Log.i(TAG, "Call accepted")
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// ========== actionType = 4: Reject call (callee rejected) ==========
|
|
469
|
+
V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_REJECT_INVITE -> {
|
|
470
|
+
// Check if it's a busy line rejection: top-level JSON contains "line_busy" field
|
|
471
|
+
if (jsonData.containsKey("line_busy")) {
|
|
472
|
+
Log.i(TAG, "Rejected due to busy line")
|
|
473
|
+
} else {
|
|
474
|
+
Log.i(TAG, "Call rejected")
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ========== actionType = 5: Timed out ==========
|
|
479
|
+
V2TIMSignalingInfo.SIGNALING_ACTION_TYPE_INVITE_TIMEOUT -> {
|
|
480
|
+
Log.i(TAG, "Call timed out")
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Improve Call Reachability
|
|
487
|
+
1. **Integrate offline push to improve call reachability:**
|
|
488
|
+
|
|
489
|
+
- Mainland China: Integrate Notification
|
|
490
|
+
|
|
491
|
+
- Overseas: Integrate FCM
|
|
492
|
+
|
|
493
|
+
2. **Provide push controls:** Based on our experience, over 90% of users who miss call notifications have manually disabled app notification permissions. To ensure call reachability, we strongly recommend:
|
|
494
|
+
|
|
495
|
+
- **Send notifications prudently:** Avoid sending low-quality or irrelevant notifications to reduce the likelihood of users disabling notification permissions.
|
|
496
|
+
|
|
497
|
+
- **Provide granular controls:** Following the approach of apps like WeChat, provide separate toggles in your app settings for "Notification" and "FCM Data Message", rather than directing users to system settings to disable all notifications at once.
|
package/resource/knowledge/callkit/best-practice/flutter/android-keep-call-alive-background.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Keep TUICallKit Flutter Calls Alive When the Android App Goes to Background (Foreground Service Configuration)
|
|
3
|
+
product: call
|
|
4
|
+
frameworks: [flutter, android]
|
|
5
|
+
scope: platform_specific
|
|
6
|
+
tags: [TUICallKit, Flutter, Android, foreground service, CallForegroundService, keep alive, background, FOREGROUND_SERVICE_MICROPHONE, FOREGROUND_SERVICE_CAMERA, Android14, foregroundServiceType, MethodChannel]
|
|
7
|
+
version_range: ">=2.0.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Keep TUICallKit Flutter Calls Alive When the Android App Goes to Background (Foreground Service Configuration)
|
|
11
|
+
|
|
12
|
+
## 1. Key Takeaway
|
|
13
|
+
|
|
14
|
+
- Required: Android restricts background apps from accessing the microphone/camera. Without a foreground service to keep the app alive, audio/video capture will be interrupted by the system when the app goes to the background or the screen is locked.
|
|
15
|
+
- Root cause: Starting from Android 9 (API 28), accessing the microphone/camera requires declaring foreground service permissions. Starting from Android 14 (API 34), foreground services must explicitly declare `foregroundServiceType`; otherwise, starting a foreground service will throw an exception.
|
|
16
|
+
- Fix: TUICallKit Flutter does **not include** a built-in foreground service. You need to implement `CallForegroundService` in the Android native layer and invoke its `start()`/`stop()` from the Flutter side via `MethodChannel` at the appropriate time.
|
|
17
|
+
- Version constraint: When `targetSdkVersion >= 34`, if the `<service>` tag does not declare `android:foregroundServiceType="camera|microphone"`, calling `startForegroundService` will throw `MissingForegroundServiceTypeException` and crash.
|
|
18
|
+
|
|
19
|
+
## 2. Field Reference
|
|
20
|
+
|
|
21
|
+
| Field / Capability | Purpose | Notes |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `FOREGROUND_SERVICE` | Base permission to use foreground services | Required since Android 9 (API 28) |
|
|
24
|
+
| `FOREGROUND_SERVICE_MICROPHONE` / `FOREGROUND_SERVICE_CAMERA` | Declare microphone/camera usage in foreground services | Required since Android 14 (API 34) |
|
|
25
|
+
| `android:foregroundServiceType="camera\|microphone"` | Declare the service type in the `<service>` tag | Must match the above permissions; missing this on API 34+ causes a crash |
|
|
26
|
+
| `CallForegroundService` (must be implemented by you) | The foreground service class for keep-alive | Not built into TUICallKit; developers must create it and call `startForeground` |
|
|
27
|
+
| `MethodChannel` | Bridge between Flutter and Android native layer | Used to trigger `start()` from the Flutter side when a call starts or the app goes to background |
|
|
28
|
+
|
|
29
|
+
## 3. Flow
|
|
30
|
+
|
|
31
|
+
App goes to background / screen locked -> Android reclaims background microphone/camera access -> Call capture interrupted (no audio / black screen) -> Check: verify foreground service permissions and `<service>` declaration -> Identify: missing `CallForegroundService` or `start()` not triggered when going to background -> Fix: declare permissions + implement foreground service class + bridge via `MethodChannel` -> Verify: audio/video capture continues after going to background / locking screen; a persistent notification appears in the notification bar.
|
|
32
|
+
|
|
33
|
+
## 4. Standard Usage
|
|
34
|
+
|
|
35
|
+
1. Declare foreground service permissions in `AndroidManifest.xml` and set `foregroundServiceType` for the `<service>`:
|
|
36
|
+
|
|
37
|
+
``` xml
|
|
38
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
39
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
|
|
40
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
|
|
41
|
+
|
|
42
|
+
<application>
|
|
43
|
+
<service
|
|
44
|
+
android:name=".CallForegroundService"
|
|
45
|
+
android:enabled="true"
|
|
46
|
+
android:exported="false"
|
|
47
|
+
android:foregroundServiceType="camera|microphone" />
|
|
48
|
+
</application>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
2. Implement `CallForegroundService` in the Android native layer (extend `Service`, call `startForeground` with a persistent notification in `onCreate`), and provide static `start(context)` / `stop(context)` methods for external invocation.
|
|
52
|
+
|
|
53
|
+
3. Register a `MethodChannel` on the Flutter side. Call the native `CallForegroundService.start()` when a call starts (`calls` / `accept` succeeds) or the app is about to go to background. Call `stop()` when the call ends (`hangup` / `onCallEnd`) to avoid keeping the service running unnecessarily.
|
|
54
|
+
|
|
55
|
+
4. On iOS, configure `Background Modes` (check `Audio, AirPlay, and Picture in Picture` and `Voice over IP`) and set up `AVAudioSession` before the call. Use `MethodChannel` to trigger the corresponding background keep-alive logic at the appropriate time.
|
|
56
|
+
|
|
57
|
+
## 5. Verification
|
|
58
|
+
|
|
59
|
+
- After going to background or locking the screen, call audio/video capture is not interrupted; the remote participant can still hear/see the local participant.
|
|
60
|
+
- A persistent foreground service notification (e.g., "Call in progress") appears in the system notification bar.
|
|
61
|
+
- On devices with `targetSdkVersion >= 34`, no crash occurs due to missing `foregroundServiceType` declaration.
|
|
62
|
+
|
|
63
|
+
## 6. Common Mistakes
|
|
64
|
+
|
|
65
|
+
- Mistake 1: Assuming TUICallKit Flutter SDK has a built-in foreground service keep-alive capability that requires no manual implementation. In reality, developers must create `CallForegroundService` themselves; the SDK does not handle this automatically.
|
|
66
|
+
- Mistake 2: Declaring permissions and `<service>` in `AndroidManifest.xml` but never actually calling `start()` via `MethodChannel` during a call. The service is never started, making the permission declarations useless.
|
|
67
|
+
- Mistake 3: After upgrading `targetSdkVersion` to 34+, only declaring the `FOREGROUND_SERVICE` permission while missing `FOREGROUND_SERVICE_CAMERA` / `FOREGROUND_SERVICE_MICROPHONE` and the corresponding `foregroundServiceType`. This causes crashes on Android 14 devices after release.
|
|
68
|
+
|
|
69
|
+
## 7. Problem Definition (Search Anchor)
|
|
70
|
+
|
|
71
|
+
How to keep voice/video calls alive in a Flutter TUICallKit Android app when it goes to background or is minimized. Whether a microphone/camera foreground service with a persistent notification is required. Recommended integration approach for Android 14+.
|
|
72
|
+
|
|
73
|
+
## 8. Alternative Phrasings (Search Recall)
|
|
74
|
+
|
|
75
|
+
- Does a Flutter TUICallKit Android call drop when the app goes to background?
|
|
76
|
+
- How to configure a foreground service for TUICallKit Android?
|
|
77
|
+
- How to add FOREGROUND_SERVICE_MICROPHONE for Android 14?
|
|
78
|
+
- Flutter call app minimized and microphone/camera stopped working?
|
|
79
|
+
- Do I need to implement CallForegroundService myself?
|