From f031e510b8a3c8bb4e25f7ccf6878d76cc9e3545 Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 29 Oct 2026 02:13:33 +0100 Subject: [PATCH] Model Telecom state, endpoint outcomes and ConnectionService requests --- .../kotlin/android/telecom/HostTelecom.kt | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 tools/chatgui-android/video-tests/src/main/kotlin/android/telecom/HostTelecom.kt diff --git a/tools/chatgui-android/video-tests/src/main/kotlin/android/telecom/HostTelecom.kt b/tools/chatgui-android/video-tests/src/main/kotlin/android/telecom/HostTelecom.kt new file mode 100644 index 00000000..4b4ea829 --- /dev/null +++ b/tools/chatgui-android/video-tests/src/main/kotlin/android/telecom/HostTelecom.kt @@ -0,0 +1,96 @@ +package android.telecom + +import android.content.ComponentName +import android.net.Uri +import android.os.Bundle +import android.os.OutcomeReceiver +import android.os.ParcelUuid +import java.util.concurrent.CopyOnWriteArrayList +import java.util.concurrent.Executor + +data class PhoneAccountHandle(val component: ComponentName, val id: String) +class PhoneAccount(val handle: PhoneAccountHandle) { + companion object { const val CAPABILITY_SELF_MANAGED = 1; fun builder(handle: PhoneAccountHandle, name: String) = Builder(handle) } + class Builder(private val handle: PhoneAccountHandle) { fun setCapabilities(flags: Int) = this; fun build() = PhoneAccount(handle) } +} +class TelecomManager { + companion object { + const val PRESENTATION_UNKNOWN = 3; const val EXTRA_OUTGOING_CALL_EXTRAS = "outgoing"; const val EXTRA_PHONE_ACCOUNT_HANDLE = "account" + var shared: TelecomManager? = TelecomManager(); var failLookup = false + } + var failures = emptySet() + val accounts = mutableListOf() + val outgoing = mutableListOf>() + val incoming = mutableListOf>() + fun registerPhoneAccount(account: PhoneAccount) { check("register" !in failures); accounts.add(account); check("register after" !in failures) } + fun placeCall(uri: Uri, extras: Bundle) { check("place" !in failures); outgoing.add(uri to extras); check("place after" !in failures) } + fun addNewIncomingCall(handle: PhoneAccountHandle, extras: Bundle) { check("incoming" !in failures); incoming.add(handle to extras) } +} +data class CallEndpoint(val name: String, val endpointType: Int, val identifier: ParcelUuid) { + companion object { const val TYPE_EARPIECE = 1; const val TYPE_SPEAKER = 2; const val TYPE_BLUETOOTH = 3; const val TYPE_WIRED_HEADSET = 4 } +} +class CallEndpointException(message: String) : Exception(message) +class CallAudioState(val route: Int, val supportedRouteMask: Int, val isMuted: Boolean = false) { + companion object { const val ROUTE_EARPIECE = 1; const val ROUTE_BLUETOOTH = 2; const val ROUTE_WIRED_HEADSET = 4; const val ROUTE_SPEAKER = 8 } +} +class DisconnectCause(val code: Int) { companion object { const val REJECTED = 1; const val ERROR = 2; const val LOCAL = 3 } } + +/** Реальные Connection callbacks и запросы управляются тестом; реального Telecom IPC/аудиоустройств нет. */ +open class Connection { + companion object { + const val PROPERTY_SELF_MANAGED = 1; const val CAPABILITY_SUPPORT_HOLD = 2; const val CAPABILITY_MUTE = 4 + val created = CopyOnWriteArrayList() + var nextFailures = emptySet() + fun stateToString(state: Int) = state.toString() + fun createCanceledConnection() = Connection().also { it.cancelled = true } + fun createFailedConnection(cause: DisconnectCause) = Connection().also { it.disconnectCause = cause } + } + var failures = nextFailures + var destroyed = false + private set + var cancelled = false + var disconnectCause: DisconnectCause? = null + var state = 0 + private set + var callAudioState: CallAudioState? = null + val legacyRequests = mutableListOf() + data class EndpointRequest(val endpoint: CallEndpoint, val executor: Executor, val outcome: OutcomeReceiver) { + fun succeed() { executor.execute { outcome.onResult(null) } } + fun fail() { executor.execute { outcome.onError(CallEndpointException("endpoint failed")) } } + } + val endpointRequests = mutableListOf() + init { created.add(this) } + private fun access(name: String) { check(name !in failures) { "Connection $name failed" } } + fun setConnectionProperties(value: Int) { access("properties") } + fun setConnectionCapabilities(value: Int) { access("capabilities") } + fun setAudioModeIsVoip(value: Boolean) { access("voip") } + fun setAddress(uri: Uri, presentation: Int) { access("address") } + fun setCallerDisplayName(name: String, presentation: Int) { access("name") } + private fun update(value: Int, name: String) { access(name); state = value; onStateChanged(value); access("$name after") } + fun setInitializing() = update(1, "initializing") + fun setDialing() = update(2, "dialing") + fun setRinging() = update(3, "ringing") + fun setActive() = update(4, "active") + fun setDisconnected(cause: DisconnectCause) { access("disconnected"); disconnectCause = cause; update(5, "disconnected") } + fun destroy() { access("destroy"); destroyed = true; access("destroy after") } + fun setAudioRoute(route: Int) { access("route"); legacyRequests.add(route); access("route after") } + fun requestCallEndpointChange(endpoint: CallEndpoint, executor: Executor, outcome: OutcomeReceiver) { + access("endpoint"); endpointRequests.add(EndpointRequest(endpoint, executor, outcome)); access("endpoint after") + } + open fun onAnswer() {} + open fun onReject() {} + open fun onDisconnect() {} + open fun onAbort() {} + open fun onShowIncomingCallUi() {} + open fun onStateChanged(state: Int) {} + open fun onCallAudioStateChanged(state: CallAudioState?) {} + open fun onCallEndpointChanged(endpoint: CallEndpoint) {} + open fun onAvailableCallEndpointsChanged(endpoints: MutableList) {} +} +class ConnectionRequest(val extras: Bundle?) +open class ConnectionService { + open fun onCreateIncomingConnectionFailed(account: PhoneAccountHandle?, request: ConnectionRequest?) {} + open fun onCreateOutgoingConnectionFailed(account: PhoneAccountHandle?, request: ConnectionRequest?) {} + open fun onCreateIncomingConnection(account: PhoneAccountHandle?, request: ConnectionRequest?): Connection? = null + open fun onCreateOutgoingConnection(account: PhoneAccountHandle?, request: ConnectionRequest?): Connection? = null +}