介面參考
本頁列出 Android SDK 的公開業務模組、列表和控制代碼簽名。除原生擴充套件註明的包外,型別位於 com.deeprespond.im。簽名採用 Kotlin;Java 的回撥、Future、Options 與訂閱對應關係見Java 接入。引數預設值表示可以省略,Duration 來自 kotlin.time,JSON 來自 kotlinx.serialization.json。
suspend 方法在協程中呼叫;Flow / StateFlow 需要收集才會更新介面。網路和許可權失敗以 DRException 表示。分頁透過 nextCursor 繼續,列表與訂閱結束後要釋放。各方法的許可權、限流、傳送及取消語義見對應業務章節;返回物件的欄位見資料型別。
DRClient
// 实例属性
auth: AuthModule
connection: ConnectionModule
users: UsersModule
devices: DevicesModule
config: StateFlow<ClientConfig?>
conversations: ConversationsModule
messages: MessagesModule
friends: FriendsModule
groups: GroupsModule
files: FilesModule
push: PushModule
presence: PresenceModule
chatrooms: ChatroomsModule
calls: CallsModule
channels: ChannelsModule
reports: ReportsModule
appKey: String
events: SharedFlow<DREvent>
startupEvents: List<DREvent>
hasPendingWork: Boolean
fun java(): DRJavaClient
fun <T : DREvent> on(type: KClass<T>): Flow<T>
inline fun <reified T : DREvent> on(): Flow<T>
fun keepForeground(): AutoCloseable
suspend fun <T> keepForeground(block: suspend () -> T): T
suspend fun exportDiagnostics(): java.io.File
suspend fun destroy()
// DRClient 静态入口
fun initialize(context: Context, options: DRClientOptions): DRClient
val instance: DRClient
val instanceOrNull: DRClient?
fun isMainProcess(context: Context): Boolean初始化及選項見客戶端。startupEvents 讀取後清空;診斷 ZIP 在快取中儲存,24 小時過期。事件流有緩衝上限,業務資料以模組快照為準。
基礎與登入
使用規則與示例見相關章節。
AuthModule
interface AuthModule {
val state: StateFlow<AuthState>
val currentUser: CurrentUser?
val restored: StateFlow<Boolean>
suspend fun awaitRestored()
suspend fun loginWithTicket(ticket: String, username: String? = null): LoginResult
suspend fun loginWithPassword(username: String, password: String): LoginResult
suspend fun register(username: String, password: String, nickname: String? = null): SelfProfile
suspend fun logout(clearLocalData: Boolean = false)
suspend fun changePassword(currentPassword: String, newPassword: String)
suspend fun clearLocalData(username: String? = null, all: Boolean = false)
}ConnectionModule
interface ConnectionModule {
val state: StateFlow<DRConnectionState>
val serverTimeOffsetMs: Long
fun reconnectNow()
suspend fun waitUntilReady(timeout: Duration? = null)
fun diagnostics(): Diagnostics
}使用者與裝置
使用規則與示例見相關章節。
UsersModule
interface UsersModule {
val me: StateFlow<SelfProfile?>
suspend fun refreshMe(): SelfProfile
suspend fun updateMe(nickname: String? = null, avatarUrl: String? = null, attributes: Map<String, String?>? = null, clearAttributes: Boolean = false, version: Long? = null): SelfProfile
val myMute: MyMute
fun get(username: String): UserProfile?
suspend fun fetch(usernames: List<String>, force: Boolean = false): Map<String, UserProfile>
}DevicesModule
interface DevicesModule {
fun online(): List<OnlineDevice>
suspend fun sessions(): List<LoginSession>
suspend fun kick(sessionId: String)
}會話與訊息
使用規則與示例見相關章節。
ConversationList
interface ConversationList : LiveList<ConversationView>MessageList
interface MessageList : AutoCloseable {
val conversationKey: String
val snapshot: StateFlow<MessageListSnapshot>
suspend fun loadOlder()
suspend fun loadNewer()
suspend fun loadGap(gap: MessageGap)
suspend fun jumpTo(seq: Long)
fun markRead()
fun setViewing(atBottom: Boolean? = null, visible: Boolean? = null)
override fun close()
}ConversationsModule
interface ConversationsModule {
fun list(): ConversationList
fun listFlow(): Flow<LiveListSnapshot<ConversationView>>
suspend fun get(conversationId: String): ConversationView?
suspend fun openSingle(username: String): ConversationView
suspend fun refresh(conversationId: String): ConversationView
fun markRead(conversationId: String, seq: Long? = null)
suspend fun markAllRead(): MarkAllReadResult
suspend fun setMarkedUnread(conversationId: String, marked: Boolean)
suspend fun setPinned(conversationId: String, pinned: Boolean)
suspend fun remove(conversationId: String, clearMessages: Boolean = false)
suspend fun clearMessages(conversationId: String)
suspend fun setDraft(conversationKey: String, draft: Draft?)
val unreadTotal: StateFlow<Int>
fun getUnreadTotal(excludeMuted: Boolean = false): Int
fun getTyping(conversationKey: String): List<String>
fun typing(conversationKey: String)
}MessagesModule
interface MessagesModule {
fun open(conversationKey: String, aroundSeq: Long? = null): MessageList
suspend fun send(to: SendTarget, content: OutgoingContent, options: SendOptions = SendOptions()): MessageView
suspend fun resend(clientMsgId: String)
suspend fun discard(clientMsgId: String)
suspend fun sendOnline(to: SendTarget, body: JsonObject, ext: JsonObject? = null): OnlineResult
suspend fun sendOnline(to: SendTarget, body: Map<String, Any?>, ext: Map<String, Any?>? = null): OnlineResult
suspend fun recall(conversationId: String, seq: Long): Boolean
fun canRecall(message: MessageView): Boolean
suspend fun deleteForMe(conversationId: String, seqs: List<Long>)
suspend fun react(conversationId: String, seq: Long, key: String): Boolean
suspend fun unreact(conversationId: String, seq: Long, key: String): Boolean
suspend fun reactionUsers(conversationId: String, seq: Long, key: String, cursor: String? = null, limit: Int? = null): DRPage<ReactionUser>
suspend fun setPinned(conversationId: String, seq: Long, pinned: Boolean): Boolean
suspend fun lookup(conversationId: String, seqs: List<Long>): LookupResult
suspend fun searchLocal(keyword: String, conversationId: String? = null, limit: Int = 50): List<MessageView>
suspend fun receipts(conversationId: String, seqs: List<Long>): List<ReceiptCount>
suspend fun receiptUsers(conversationId: String, seq: Long, status: ReceiptStatus, cursor: String? = null, limit: Int? = null): DRPage<ReceiptUser>
fun getReceipt(conversationId: String, seq: Long): ReceiptCount?
}好友
使用規則與示例見相關章節。
FriendsModule
interface FriendsModule {
fun list(): LiveList<Friend>
fun listFlow(): Flow<LiveListSnapshot<Friend>>
fun get(username: String): Friend?
val listInfo: FriendListInfo?
suspend fun check(usernames: List<String>): List<FriendCheck>
suspend fun update(username: String, remark: String? = null, attributes: Map<String, String?>? = null, clearAttributes: Boolean = false): Friend
suspend fun remove(username: String)
val requests: FriendRequestsModule
val blacklist: FriendBlacklistModule
val settings: FriendSettingsModule
}FriendRequestsModule
interface FriendRequestsModule {
suspend fun received(status: FriendRequestStatus? = null, cursor: String? = null, limit: Int? = null): DRPage<FriendRequest>
suspend fun sent(status: FriendRequestStatus? = null, cursor: String? = null, limit: Int? = null): DRPage<FriendRequest>
val unreadCount: StateFlow<Int>
suspend fun markRead(readUntil: Long? = null)
suspend fun send(username: String, message: String? = null, remark: String? = null, addSource: String? = null): FriendRequestResult
suspend fun accept(username: String, remark: String? = null): Friend
suspend fun decline(username: String)
}FriendBlacklistModule
interface FriendBlacklistModule {
fun list(): LiveList<BlacklistEntry>
fun listFlow(): Flow<LiveListSnapshot<BlacklistEntry>>
fun has(username: String): Boolean
suspend fun add(username: String)
suspend fun remove(username: String)
}FriendSettingsModule
interface FriendSettingsModule {
suspend fun
suspend fun set(mode: FriendAddMode?)
}群組
使用規則與示例見相關章節。
GroupsModule
interface GroupsModule {
fun list(): LiveList<GroupView>
fun listFlow(): Flow<LiveListSnapshot<GroupView>>
suspend fun get(groupId: String, refresh: Boolean = false): GroupView?
suspend fun lookup(groupIds: List<String>): List<GroupInfo>
suspend fun publicGroups(namePrefix: String? = null, cursor: String? = null, limit: Int? = null): DRPage<GroupInfo>
suspend fun create(
name: String,
type: GroupType? = null,
avatarUrl: String? = null,
description: String? = null,
announcement: String? = null,
attributes: Map<String, String?>? = null,
memberInvite: MemberInvite? = null,
joinMode: JoinMode? = null,
members: List<String>? = null,
message: String? = null,
): CreateGroupResult
suspend fun update(
groupId: String,
type: GroupType? = null,
name: String? = null,
avatarUrl: String? = null,
description: String? = null,
announcement: String? = null,
attributes: Map<String, String?>? = null,
clearAttributes: Boolean = false,
memberInvite: MemberInvite? = null,
joinMode: JoinMode? = null,
version: Long? = null,
): GroupInfo
suspend fun transferOwner(groupId: String, username: String): GroupInfo
suspend fun setMuteAll(groupId: String, on: Boolean): GroupInfo
suspend fun leave(groupId: String)
suspend fun dismiss(groupId: String)
suspend fun onlineCount(groupId: String): GroupOnlineCount
fun members(groupId: String, role: GroupRole? = null, muted: Boolean? = null): LiveList<GroupMember>
fun membersFlow(groupId: String, role: GroupRole? = null, muted: Boolean? = null): Flow<LiveListSnapshot<GroupMember>>
suspend fun lookupMembers(groupId: String, usernames: List<String>): List<GroupMember>
suspend fun invite(groupId: String, usernames: List<String>, message: String? = null): List<GroupMemberResult>
suspend fun removeMembers(groupId: String, usernames: List<String>): List<GroupMemberResult>
suspend fun updateMember(groupId: String, username: String, groupNickname: String? = null, attributes: Map<String, String?>? = null, clearAttributes: Boolean = false): GroupMember
suspend fun setRole(groupId: String, username: String, role: GroupRole): GroupMember
suspend fun mute(groupId: String, username: String, duration: Duration? = null): GroupMember
suspend fun unmute(groupId: String, username: String): GroupMember
val blacklist: GroupBlacklistModule
val joinLink: GroupJoinLinkModule
val applications: GroupApplicationsModule
val invitations: GroupInvitationsModule
val requests: GroupRequestsModule
val settings: GroupSettingsModule
}GroupBlacklistModule
interface GroupBlacklistModule {
suspend fun list(groupId: String, cursor: String? = null, limit: Int? = null): DRPage<GroupBlacklistEntry>
suspend fun add(groupId: String, username: String, reason: String? = null)
suspend fun remove(groupId: String, username: String)
}GroupJoinLinkModule
interface GroupJoinLinkModule {
suspend fun get(groupId: String): JoinLink
suspend fun create(groupId: String, expiresIn: Duration? = null): JoinLink
suspend fun revoke(groupId: String)
suspend fun preview(link: String): JoinLinkPreview
suspend fun join(link: String): GroupInfo
}GroupApplicationsModule
interface GroupApplicationsModule {
suspend fun apply(groupId: String, message: String? = null): String
suspend fun withdraw(groupId: String)
suspend fun mine(status: GroupRequestStatus? = null, cursor: String? = null, limit: Int? = null): DRPage<GroupRequest>
}GroupInvitationsModule
interface GroupInvitationsModule {
suspend fun received(status: GroupRequestStatus? = null, cursor: String? = null, limit: Int? = null): DRPage<GroupRequest>
suspend fun accept(groupId: String): GroupInfo
suspend fun decline(groupId: String, reason: String? = null)
}GroupRequestsModule
interface GroupRequestsModule {
suspend fun list(groupId: String, kind: GroupRequestKind? = null, status: GroupRequestStatus? = null, cursor: String? = null, limit: Int? = null): DRPage<GroupRequest>
suspend fun approve(groupId: String, kind: GroupRequestKind, username: String): String
suspend fun decline(groupId: String, kind: GroupRequestKind, username: String, reason: String? = null)
suspend fun withdrawInvite(groupId: String, kind: GroupRequestKind, username: String)
}GroupSettingsModule
interface GroupSettingsModule {
suspend fun
suspend fun set(mode: InviteMode?)
}檔案
使用規則與示例見相關章節。
FilesModule
interface FilesModule {
fun upload(file: DRFile, purpose: UploadPurpose, kind: FileKind? = null, name: String? = null, groupId: String? = null, original: Boolean = false): UploadTask
fun isFileUrl(url: String): Boolean
suspend fun resolveUrl(url: String): DownloadUrl
suspend fun resolveUrls(urls: List<String>): List<ResolvedUrl>
fun download(source: DownloadSource, saveTo: File? = null, conflict: Conflict = Conflict.RENAME): DownloadTask
suspend fun localPath(url: String): File
suspend fun prepare(file: DRFile, kind: FileKind, original: Boolean = false): PreparedFile
suspend fun clearImageCache()
suspend fun groupFiles(groupId: String, cursor: String? = null, limit: Int? = null): GroupFilesPage
suspend fun deleteGroupFile(groupId: String, fileId: String)
}聊天室
使用規則與示例見相關章節。
ChatroomsModule
interface ChatroomsModule {
suspend fun list(namePrefix: String? = null, cursor: String? = null, limit: Int? = null): DRPage<ChatroomListItem>
suspend fun get(roomId: String): ChatroomInfo
suspend fun create(name: String, description: String? = null, avatarUrl: String? = null, announcement: String? = null, maxMembers: Int? = null, listed: Boolean? = null): ChatroomInfo
suspend fun onlineCounts(roomIds: List<String>): List<ChatroomOnlineCount>
suspend fun enter(roomId: String, ext: String? = null, leaveOthers: Boolean = false): ChatroomHandle
val entered: StateFlow<Map<String, ChatroomHandle>>
}ChatroomHandle
interface ChatroomHandle {
val roomId: String
val snapshot: StateFlow<ChatroomSnapshot>
val events: Flow<ChatroomEvent>
fun <T : ChatroomEvent> on(type: KClass<T>): Flow<T>
suspend fun send(content: OutgoingContent, priority: ChatroomPriority = ChatroomPriority.NORMAL, ext: JsonObject? = null): ChatroomMessageView
fun like(count: Int = 1)
suspend fun setAttributes(attributes: Map<String, String>, autoDelete: Boolean = false, force: Boolean = false): AttributeResult
suspend fun removeAttributes(keys: List<String>, force: Boolean = false): AttributeResult
suspend fun recall(messageId: String): Boolean
suspend fun members(cursor: String? = null, limit: Int? = null): DRPage<ChatroomMember>
suspend fun refresh()
suspend fun leave()
val manage: ChatroomManage
}ChatroomManage
interface ChatroomManage {
suspend fun update(patch: ChatroomPatch, version: Long? = null): ChatroomInfo
suspend fun setMuteAll(on: Boolean)
suspend fun setAdmin(username: String, admin: Boolean)
suspend fun transferOwner(username: String)
suspend fun mute(usernames: List<String>, duration: Duration? = null, reason: String? = null): List<ChatroomMemberResult>
suspend fun unmute(usernames: List<String>): List<ChatroomMemberResult>
suspend fun ban(usernames: List<String>, duration: Duration? = null, reason: String? = null): List<ChatroomMemberResult>
suspend fun unban(usernames: List<String>): List<ChatroomMemberResult>
suspend fun kick(usernames: List<String>): List<ChatroomMemberResult>
val allowlist: ChatroomAllowlist
suspend fun mutes(cursor: String? = null, limit: Int? = null): DRPage<ChatroomMuteEntry>
suspend fun bans(cursor: String? = null, limit: Int? = null): DRPage<ChatroomBanEntry>
suspend fun dismiss()
}ChatroomAllowlist
interface ChatroomAllowlist {
suspend fun list(cursor: String? = null, limit: Int? = null): DRPage<ChatroomAllowlistEntry>
suspend fun add(usernames: List<String>): List<ChatroomMemberResult>
suspend fun remove(usernames: List<String>): List<ChatroomMemberResult>
}音影片
使用規則與示例見相關章節。
CallsModule
interface CallsModule {
val supported: Boolean
suspend fun start(media: CallMedia, target: CallTarget, ext: Map<String, String>? = null): CallHandle
val current: StateFlow<CallHandle?>
val incoming: StateFlow<List<CallHandle>>
suspend fun get(callId: String): CallHandle
suspend fun history(cursor: String? = null, limit: Int? = null): DRPage<CallSummary>
suspend fun groupCall(groupId: String): CallHandle?
fun groupCallBanner(groupId: String): GroupCallBanner?
}CallHandle
interface CallHandle {
val callId: String
val snapshot: StateFlow<CallSnapshot>
val tracks: StateFlow<CallTracks>
val data: Flow<CallDataMessage>
suspend fun join()
suspend fun reject(reason: RejectReason = RejectReason.DECLINED)
suspend fun hangup()
suspend fun end()
suspend fun invite(usernames: List<String>): List<CallInviteResult>
suspend fun resume()
suspend fun setMicrophoneEnabled(on: Boolean)
suspend fun setCameraEnabled(on: Boolean)
suspend fun sendData(data: ByteArray)
}提醒設定
使用規則與示例見相關章節。
PushModule
interface PushModule {
val settings: StateFlow<PushSettings?>
suspend fun updateSettings(dnd: DndChange? = null, quietHours: QuietHoursChange? = null, preview: PreviewChange? = null): PushSettings
suspend fun mute(target: MuteTarget, mode: MuteMode, duration: Duration? = null)
suspend fun unmute(target: MuteTarget)
fun getMute(target: MuteTarget): MuteInfo?
fun shouldNotify(message: MessageView): NotifyDecision
}線上狀態
使用規則與示例見相關章節。
PresenceModule
interface PresenceModule {
fun subscribe(usernames: List<String>): AutoCloseable
fun get(username: String): PresenceState?
suspend fun query(usernames: List<String>): List<PresenceState>
}舉報
使用規則與示例見相關章節。
ReportsModule
interface ReportsModule {
suspend fun submit(target: ReportTarget, reason: ReportReason, description: String? = null): ReportSubmitResult
suspend fun mine(cursor: String? = null, limit: Int? = null): DRPage<Report>
}列表與檔案任務
LiveList 的 snapshot 包含 items、hasMore、loading、error。ConversationList 繼承 LiveList。上傳和下載的 await 只是等待結果,真正取消需要 cancel。
LiveList
interface LiveList<T> : AutoCloseable {
val snapshot: StateFlow<LiveListSnapshot<T>>
suspend fun loadMore()
override fun close()
}UploadTask
interface UploadTask {
val progress: StateFlow<Progress>
suspend fun await(): FileInfo
fun cancel()
}DownloadTask
interface DownloadTask {
val progress: StateFlow<Progress>
suspend fun await(): java.io.File
fun cancel()
}原生推送
包為 com.deeprespond.im.push,依賴 im-push。接入、廠商配置及點選校驗見離線推送。
DRPushController
interface DRPushController {
suspend fun permission(): PushPermission
suspend fun requestPermission(activity: ComponentActivity): PushPermission
fun permissionContract(): ActivityResultContract<Unit, PushPermission>
fun markPermissionRequested()
fun shouldShowRationale(activity: Activity): Boolean
val enabled: Boolean
suspend fun setEnabled(enabled: Boolean)
var language: String?
suspend fun register()
suspend fun currentRegistration(): PushDevice?
val registrations: SharedFlow<PushRegistration>
val opened: Flow<PushOpened>
suspend fun takeLaunchOpen(): PushOpened?
fun clearDeliveredNotifications()
fun cancelNotification(tag: String)
fun setBadge(count: Int?)
fun openNotificationSettings(activity: Activity, channelId: String? = null)
val channelStates: StateFlow<Map<String, Boolean>>
fun isHibernationExempt(): Boolean
fun openHibernationSettings(activity: Activity)
}DRPush
fun of(client: DRClient): DRPushController?
fun handleRemoteMessage(context: Context, message: RemoteMessage): Boolean
fun handleRegistered(context: Context, installationId: String)
fun handleNewToken(context: Context, token: String)
fun handleDeletedMessages(context: Context)
fun handleUnregistered(context: Context, installationId: String)
fun handleIntent(intent: Intent?): Boolean以上是 DRPush 的靜態方法。handleRemoteMessage / handleIntent 返回 true 表示 SDK 已接管;業務只處理返回 false 的內容。
原生通話
包為 com.deeprespond.im.call,依賴 im-call。媒體許可權、系統來電和檢視使用見音影片。
DRCallController
interface DRCallController {
val actions: Flow<CallAction>
fun canUseFullScreenIntent(): Boolean
fun openFullScreenIntentSettings(activity: Activity)
fun callChannelEnabled(): Boolean
fun intentCall(intent: Intent?): CallIntent?
}DRCallUI 與裝置擴充套件
// 静态入口
fun DRCallUI.of(client: DRClient): DRCallController?
// CallHandle 扩展
val CallHandle.audioEndpoints: StateFlow<AudioEndpoints>
suspend fun CallHandle.switchCamera()
suspend fun CallHandle.setSpeakerphoneOn(on: Boolean)
suspend fun CallHandle.setAudioEndpoint(endpoint: AudioEndpoint)
// 视频视图
DRCallVideoView(context: Context, attrs: AttributeSet? = null)
var scaleType: DRCallVideoView.ScaleType // FIT / FILL
var mirror: Boolean
fun bind(call: CallHandle, track: CallTrack?)
fun unbind()檔案、錄音與圖片擴充套件
// com.deeprespond.im
fun DRFile.Companion.fromUri(context: Context, uri: Uri, name: String? = null, mimeType: String? = null): DRFile
fun DRFile.Companion.fromFile(file: java.io.File, name: String? = null, mimeType: String? = null): DRFile
fun DRFile.Companion.fromBytes(bytes: ByteArray, name: String, mimeType: String? = null): DRFile
fun DRFile.withKeepMetadata(): DRFile
// com.deeprespond.im
suspend fun VoiceRecorder.start(context: Context, maxDuration: Duration = 60.seconds): Recording
val VoiceRecorder.isRecording: Boolean錄音任務和結果見資料型別。Coil 包為 com.deeprespond.im.coil:
DRImage(url: String, thumbnail: Boolean = false)
fun DRImage.Companion.of(message: MessageView, thumbnail: Boolean = false): DRImage?
fun DRImage.Companion.stableKey(url: String): String
fun ComponentRegistry.Builder.addDRImage(client: DRClient? = null): ComponentRegistry.Builder使用方法見檔案與錄音。
Recording
interface Recording {
val level: Flow<Float>
val ended: Flow<VoiceRecordingEnd>
suspend fun stop(): VoiceRecording
fun cancel()
}顯示輔助
包為 com.deeprespond.im.render。以下為接收 Android DRClient 的常用過載:
fun displayName(client: DRClient, username: String?, groupId: String? = null, locale: DRLocale = DRLocale.zhCN): String
fun conversationTitle(client: DRClient, view: DRObject, locale: DRLocale = DRLocale.zhCN): String
fun summarize(client: DRClient, message: MessageView, groupId: String? = null, locale: DRLocale = DRLocale.zhCN): String
fun renderTip(client: DRClient, message: MessageView, groupId: String? = null, locale: DRLocale = DRLocale.zhCN): String
fun renderRecall(client: DRClient, message: MessageView, groupId: String? = null, locale: DRLocale = DRLocale.zhCN): String另外兩個顯示入口:
fun renderCallRecord(message: DRObject, self: String, locale: DRLocale = DRLocale.zhCN): String
fun describeError(err: DRException, locale: DRLocale = DRLocale.zhCN): String其他渲染過載可以直接接收物件和名稱解析器。介面組合及生命週期見介面。
獨立 RTC 頻道
interface ChannelsModule {
val supported: Boolean
val current: StateFlow<ChannelHandle?>
suspend fun get(channelId: String): RTCChannel
suspend fun currentSession(): ChannelSession?
suspend fun members(channelId: String, cursor: String? = null, limit: Int? = null): DRPage<ChannelParticipant>
suspend fun join(channelId: String, options: ChannelJoinOptions = ChannelJoinOptions()): ChannelHandle
suspend fun resume(channelId: String, sessionId: String): ChannelHandle
}
interface ChannelHandle {
val channelId: String
val sessionId: String
val snapshot: StateFlow<ChannelSnapshot>
val tracks: StateFlow<List<ChannelTrack>>
suspend fun refresh()
suspend fun setMicrophoneEnabled(on: Boolean)
suspend fun setCameraEnabled(on: Boolean)
suspend fun leave()
}入口為 DRClient.channels: ChannelsModule。im-call 提供 DRChannelVideoView,其 bind(handle, track) / unbind() 負責畫面渲染,結束頁面仍須呼叫 leave。型別見資料型別,許可權和生命週期見頻道接入。
