""" Android Telecom API — TelecomManager, Call, CallAudioState, DisconnectCause, PhoneAccount, Connection 方法签名完全对齐 Android SDK """ import sys import time from typing import Any, Dict, List, Optional, Set, Tuple, Callable from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) __all__ = [ "TelecomManager", "Call", "CallAudioState", "DisconnectCause", "PhoneAccount", "Connection", ] # ═══════════════════════════════════════════════════════════════ # TelecomManager # ═══════════════════════════════════════════════════════════════ class TelecomManager(Stub): """TelecomManager — 电信管理器 (桌面存根)""" ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER" ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS" ACTION_SETTINGS = "android.telecom.action.SETTINGS" ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" ACTION_CALL_FORWARDING = "android.telecom.action.CALL_FORWARDING" ACTION_DEFAULT_CALL_SCREENING_APP = "android.telecom.action.DEFAULT_CALL_SCREENING_APP" ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED" ACTION_NEW_UNKNOWN_CALL = "android.telecom.action.NEW_UNKNOWN_CALL" ACTION_TTY_PREFERRED = "android.telecom.action.TTY_PREFERRED" ACTION_TTY_ENABLED = "android.telecom.action.TTY_ENABLED" ACTION_TTY_DISABLED = "android.telecom.action.TTY_DISABLED" ACTION_CONNECTION_SERVICE_CONFIGURE = "android.telecom.action.CONNECTION_SERVICE_CONFIGURE" ACTION_CALL_DIAGNOSTIC_STATE_CHANGED = "android.telecom.action.CALL_DIAGNOSTIC_STATE_CHANGED" DURATION_SHORT = "short" DURATION_LONG = "long" EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER" EXTRA_CALL_CREATED_TIME_MILLIS = "android.telecom.extra.CALL_CREATED_TIME_MILLIS" EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE" EXTRA_CALL_NETWORK_TYPE = "android.telecom.extra.CALL_NETWORK_TYPE" EXTRA_CALL_SUBSTATE = "android.telecom.extra.CALL_SUBSTATE" EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME" EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE" EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE" EXTRA_DISCONNECT_CAUSE_DESCRIPTION = "android.telecom.extra.DISCONNECT_CAUSE_DESCRIPTION" EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE" EXTRA_PRIORITY = "android.telecom.extra.PRIORITY" EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE" EXTRA_HANDOVER_FROM = "android.telecom.extra.HANDOVER_FROM" EXTRA_HANDOVER_TO = "android.telecom.extra.HANDOVER_TO" EXTRA_IS_HANDOVER = "android.telecom.extra.IS_HANDOVER" TTY_MODE_OFF = 0 TTY_MODE_FULL = 1 TTY_MODE_HCO = 2 TTY_MODE_VCO = 3 PRESENTATION_ALLOWED = 1 PRESENTATION_RESTRICTED = 2 PRESENTATION_UNKNOWN = 3 PRESENTATION_PAYPHONE = 4 def __init__(self, context: Any = None): super().__init__() self._context = context self._default_dialer = "com.android.dialer" self._phone_accounts: Dict[str, Any] = {} def getDefaultDialerPackage(self) -> str: """获取默认拨号器包名""" return self._default_dialer def getCurrentTtyMode(self) -> int: """获取当前 TTY 模式""" return self.TTY_MODE_OFF def getCallCapablePhoneAccounts(self) -> List[Any]: """获取可通话的电话账户""" return list(self._phone_accounts.values()) def getPhoneAccount(self, accountHandle: Any) -> Optional[Any]: """获取指定电话账户""" return self._phone_accounts.get(str(accountHandle)) def getPhoneAccountList(self) -> List[Any]: """获取电话账户列表""" return list(self._phone_accounts.values()) def getSimConfig(self, accountHandle: Any) -> Optional[Any]: """获取 SIM 配置""" return None def registerPhoneAccount(self, account: "PhoneAccount"): """注册电话账户""" self._phone_accounts[str(account.getAccountHandle())] = account def unregisterPhoneAccount(self, accountHandle: Any): """注销电话账户""" self._phone_accounts.pop(str(accountHandle), None) def clearPhoneAccounts(self): """清除所有电话账户""" self._phone_accounts.clear() def clearAccounts(self): """清除所有账户""" self._phone_accounts.clear() def showInCallScreen(self, showDialpad: bool): """显示通话界面""" pass def acceptRingingCall(self): """接听响铃呼叫""" pass def silenceRinger(self): """静音铃声""" pass def isInCall(self) -> bool: """检查是否正在通话""" return False def isInManagedCall(self) -> bool: """检查是否在托管通话中""" return False def addNewIncomingCall(self, phoneAccount: "PhoneAccount", extras: Any = None): """添加新的来电""" pass def addNewUnknownCall(self, phoneAccount: "PhoneAccount", extras: Any = None): """添加新的未知呼叫""" pass def handleCreateConferenceComplete( self, callId: str, response: Any ): """处理创建会议完成""" pass def transferCall(self, callId: str, targetPhoneAccount: Any): """转移呼叫""" pass def setUserSelectedOutgoingPhoneAccount( self, accountHandle: Any ): """设置用户选择的呼出电话账户""" pass def toString(self) -> str: return f"TelecomManager@{hex(id(self))}" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # Call # ═══════════════════════════════════════════════════════════════ class Call(Stub): """Call — 通话 (桌面存根)""" STATE_ACTIVE = 1 STATE_HOLDING = 2 STATE_DIALING = 3 STATE_RINGING = 4 STATE_CONNECTING = 5 STATE_DISCONNECTED = 6 STATE_DISCONNECTING = 7 STATE_SELECT_PHONE_ACCOUNT = 8 STATE_NEW = 9 STATE_PULLING_CALL = 10 def __init__(self): super().__init__() self._state = self.STATE_NEW self._details: Optional["Call.Details"] = None self._conferenceable_calls: List["Call"] = [] self._callbacks: List[Callable] = [] self._listeners: List[Callable] = [] def getState(self) -> int: return self._state def getDetails(self) -> Optional["Call.Details"]: return self._details def getConferenceableCalls(self) -> List["Call"]: return list(self._conferenceable_calls) def registerCallback(self, callback: Callable): if callback not in self._callbacks: self._callbacks.append(callback) def unregisterCallback(self, callback: Callable): if callback in self._callbacks: self._callbacks.remove(callback) def addListener(self, listener: Callable): if listener not in self._listeners: self._listeners.append(listener) def removeListener(self, listener: Callable): if listener in self._listeners: self._listeners.remove(listener) def answer(self, videoState: int = 0): """接听通话""" self._state = self.STATE_ACTIVE def reject(self, rejectWithMessage: bool = False, textMessage: str = ""): """拒绝通话""" self._state = self.STATE_DISCONNECTED def disconnect(self): """挂断通话""" self._state = self.STATE_DISCONNECTED def hold(self): """保持通话""" self._state = self.STATE_HOLDING def unhold(self): """取消保持""" self._state = self.STATE_ACTIVE def playDtmfTone(self, digit: str): """播放 DTMF 音""" pass def stopDtmfTone(self): """停止 DTMF 音""" pass def postDialWait(self, remaining: str): """后拨号等待""" pass def mergeConference(self): """合并会议""" pass def splitFromConference(self): """从会议分离""" pass def sendCallEvent(self, event: str, extras: Any = None): """发送通话事件""" pass def putExtra(self, key: str, value: Any): """设置额外信息""" pass # ═══════════════════════════════════════════════════════════ # Details # ═══════════════════════════════════════════════════════════ class Details(Stub): """Call.Details — 通话详细信息""" DIRECTION_INCOMING = 1 DIRECTION_OUTGOING = 2 CAPABILITY_HOLD = 1 CAPABILITY_SUPPORT_HOLD = 2 CAPABILITY_MERGE_CONFERENCE = 4 CAPABILITY_SWAP_CONFERENCE = 8 CAPABILITY_RESPOND_VIA_TEXT = 16 CAPABILITY_MUTE = 32 CAPABILITY_MANAGE_CONFERENCE = 64 CAPABILITY_SUPPORTS_VT_LOCAL = 128 CAPABILITY_SUPPORTS_VT_REMOTE = 256 CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 512 CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 1024 CAPABILITY_SUPPORTS_DOWNGRADE_VT_TO_AUDIO = 2048 CAPABILITY_SUPPORTS_VIDEO_CALLING = 4096 CAPABILITY_SUPPORTS_VT_LOCAL_RX = 8192 CAPABILITY_SUPPORTS_VT_LOCAL_TX = 16384 CAPABILITY_SUPPORTS_VT_REMOTE_RX = 32768 CAPABILITY_SUPPORTS_VT_REMOTE_TX = 65536 CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 131072 CAPABILITY_CAN_PAUSE_VIDEO = 262144 CAPABILITY_SELF_MANAGED = 524288 CAPABILITY_SUPPORTS_VOIP = 1048576 CAPABILITY_ADD_PARTICIPANT = 2097152 CAPABILITY_CAN_UPGRADE_TO_VIDEO = 4194304 PROPERTY_HIGH_DEF_AUDIO = 1 PROPERTY_WIFI = 2 PROPERTY_EMERGENCY_CALLBACK = 4 PROPERTY_IS_EXTERNAL_CALL = 8 PROPERTY_HAS_CDMA_VOICE_PRIVACY = 16 PROPERTY_SELF_MANAGED = 32 PROPERTY_ASSISTED_DIALING = 64 PROPERTY_NETWORK_IDENTIFIED = 128 PROPERTY_CALL_AUTO_FORWARDED = 256 PROPERTY_CALL_TYPE_UNKNOWN = 512 PROPERTY_IS_VOIP_AUDIO_MODE = 1024 PROPERTY_IS_DOWNGRADED_CALL = 2048 PROPERTY_ENTITLEMENT_CHANNEL = 4096 PROPERTY_PULLED_CALL = 8192 PROPERTY_CROSS_SIM = 16384 PROPERTY_VIDEO_CALL = 32768 PROPERTY_RTT = 65536 def __init__(self): super().__init__() self._handle: Any = None self._gateway_info: Any = None self._caller_display_name: str = "" self._call_direction = self.DIRECTION_INCOMING self._account_handle: Any = None self._call_capabilities = 0 self._call_properties = 0 self._caller_info: Any = None self._creation_time_millis = 0 self._connect_time_millis = 0 self._disconnect_cause: Any = None self._video_state = 0 self._intent_extras: Any = None self._extras: Any = None def getHandle(self) -> Optional[Any]: return self._handle def getGatewayInfo(self) -> Optional[Any]: return self._gateway_info def getCallerDisplayName(self) -> str: return self._caller_display_name def getCallDirection(self) -> int: return self._call_direction def getAccountHandle(self) -> Optional[Any]: return self._account_handle def getCallCapabilities(self) -> int: return self._call_capabilities def getCallProperties(self) -> int: return self._call_properties def getCallerInfo(self) -> Optional[Any]: return self._caller_info def getCreationTimeMillis(self) -> int: return self._creation_time_millis def getConnectTimeMillis(self) -> int: return self._connect_time_millis def getDisconnectCause(self) -> Optional["DisconnectCause"]: return self._disconnect_cause def getVideoState(self) -> int: return self._video_state def getIntentExtras(self) -> Optional[Any]: return self._intent_extras def getExtras(self) -> Optional[Any]: return self._extras def toString(self) -> str: return f"Call.Details(handle={self._handle})" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF def toString(self) -> str: return f"Call(state={self._state})" def equals(self, other) -> bool: if not isinstance(other, Call): return False return self._state == other._state and self._details == other._details def hashCode(self) -> int: return self._state & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # CallAudioState # ═══════════════════════════════════════════════════════════════ class CallAudioState(Stub): """CallAudioState — 通话音频状态""" ROUTE_EARPIECE = 1 ROUTE_SPEAKER = 2 ROUTE_BLUETOOTH = 4 ROUTE_WIRED_HEADSET = 8 ROUTE_WIRED_OR_EARPIECE = 9 def __init__(self): super().__init__() self._route = self.ROUTE_EARPIECE self._supported_route_mask = ( self.ROUTE_EARPIECE | self.ROUTE_SPEAKER | self.ROUTE_WIRED_HEADSET ) def getRoute(self) -> int: return self._route def getSupportedRouteMask(self) -> int: return self._supported_route_mask def toString(self) -> str: return f"CallAudioState(route={self._route})" def equals(self, other) -> bool: if not isinstance(other, CallAudioState): return False return self._route == other._route def hashCode(self) -> int: return self._route & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # DisconnectCause # ═══════════════════════════════════════════════════════════════ class DisconnectCause(Stub): """DisconnectCause — 断连原因""" LOCAL = 1 REMOTE = 2 BUSY = 3 CANCELED = 4 NO_ANSWER = 5 MISSED = 6 ERROR = 7 REJECTED = 8 NUMBER_UNREACHABLE = 9 RESTRICTED = 10 OTHER = 11 ANSWERED_ELSEWHERE = 12 CALL_PULLED = 13 def __init__(self, causeCode: int = OTHER): super().__init__() self._cause_code = causeCode self._reason: str = "" self._description: str = "" def getCode(self) -> int: return self._cause_code def getReason(self) -> str: return self._reason def getDescription(self) -> str: return self._description def toString(self) -> str: return f"DisconnectCause(code={self._cause_code})" def equals(self, other) -> bool: if not isinstance(other, DisconnectCause): return False return self._cause_code == other._cause_code def hashCode(self) -> int: return self._cause_code & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # PhoneAccount # ═══════════════════════════════════════════════════════════════ class PhoneAccount(Stub): """PhoneAccount — 电话账户""" CAP_HOLD = 1 CAP_ADD_CALL = 2 CAP_MULTI_ENDPOINT = 4 CAP_CONFERENCE = 8 CAP_VIDEO_CALLING = 16 CAP_CALL_COMPOSER = 32 CAP_CONNECTION_MANAGER = 64 EXTRA_LOG_SELF_MANAGED_CALLS = "android.telecom.extra.LOG_SELF_MANAGED_CALLS" EXTRA_ADD_SELF_MANAGED_CALLS = "android.telecom.extra.ADD_SELF_MANAGED_CALLS" SCREENING_ALLOW = 0 SCREENING_STOP = 1 SCREENING_SILENCE = 2 SCREENING_DISALLOW = 3 SCHEME_SIP = "sip" SCHEME_TEL = "tel" SCHEME_VOICEMAIL = "voicemail" class Builder(Stub): """PhoneAccount.Builder — 电话账户构建器""" def __init__(self, accountHandle: Any, label: str): super().__init__() self._account = PhoneAccount() self._account._account_handle = accountHandle self._account._label = label def setCapabilities(self, capabilities: int) -> "PhoneAccount.Builder": self._account._capabilities = capabilities return self def setAddress(self, address: Any) -> "PhoneAccount.Builder": self._account._address = address return self def setSubscriptionAddress(self, address: Any) -> "PhoneAccount.Builder": self._account._subscription_address = address return self def setSupportedUriSchemes( self, uriSchemes: List[str] ) -> "PhoneAccount.Builder": self._account._supported_uri_schemes = list(uriSchemes) return self def setIcon(self, icon: Any) -> "PhoneAccount.Builder": self._account._icon = icon return self def setHighlightColor(self, color: int) -> "PhoneAccount.Builder": self._account._highlight_color = color return self def setShortDescription(self, description: str) -> "PhoneAccount.Builder": self._account._short_description = description return self def addSupportedUriScheme( self, uriScheme: str ) -> "PhoneAccount.Builder": if uriScheme not in self._account._supported_uri_schemes: self._account._supported_uri_schemes.append(uriScheme) return self def setExtras(self, extras: Any) -> "PhoneAccount.Builder": self._account._extras = extras return self def build(self) -> "PhoneAccount": return self._account def toString(self) -> str: return f"PhoneAccount.Builder(label={self._account._label})" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF def __init__(self): super().__init__() self._account_handle: Any = None self._label: str = "" self._capabilities: int = 0 self._address: Any = None self._subscription_address: Any = None self._supported_uri_schemes: List[str] = [self.SCHEME_TEL] self._icon: Any = None self._highlight_color: int = 0 self._short_description: str = "" self._extras: Any = None def getAccountHandle(self) -> Optional[Any]: return self._account_handle def getLabel(self) -> str: return self._label def getCapabilities(self) -> int: return self._capabilities def getAddress(self) -> Optional[Any]: return self._address def getSubscriptionAddress(self) -> Optional[Any]: return self._subscription_address def getSupportedUriSchemes(self) -> List[str]: return list(self._supported_uri_schemes) def getIcon(self) -> Optional[Any]: return self._icon def getHighlightColor(self) -> int: return self._highlight_color def getShortDescription(self) -> str: return self._short_description def getExtras(self) -> Optional[Any]: return self._extras def toString(self) -> str: return f"PhoneAccount(label={self._label})" def equals(self, other) -> bool: if not isinstance(other, PhoneAccount): return False return self._account_handle == other._account_handle def hashCode(self) -> int: return hash(str(self._account_handle)) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # Connection # ═══════════════════════════════════════════════════════════════ class Connection(Stub): """Connection — 通话连接 (桌面存根)""" STATE_INITIALIZING = 0 STATE_ACTIVE = 1 STATE_DIALING = 2 STATE_RINGING = 3 STATE_HOLDING = 4 STATE_DISCONNECTED = 5 STATE_PULLING_CALL = 6 CAPABILITY_HOLD = 1 CAPABILITY_SUPPORT_HOLD = 2 CAPABILITY_MERGE_CONFERENCE = 4 CAPABILITY_SWAP_CONFERENCE = 8 CAPABILITY_RESPOND_VIA_TEXT = 16 CAPABILITY_MUTE = 32 CAPABILITY_MANAGE_CONFERENCE = 64 CAPABILITY_SUPPORTS_VT_LOCAL = 128 CAPABILITY_SUPPORTS_VT_REMOTE = 256 CAPABILITY_SUPPORTS_VIDEO_CALLING = 4096 CAPABILITY_SELF_MANAGED = 524288 CAPABILITY_SUPPORTS_VOIP = 1048576 CAPABILITY_ADD_PARTICIPANT = 2097152 PROPERTY_SELF_MANAGED = 32 PROPERTY_IS_VOIP_AUDIO_MODE = 1024 PROPERTY_HIGH_DEF_AUDIO = 1 PROPERTY_WIFI = 2 def __init__(self): super().__init__() self._state = self.STATE_INITIALIZING self._video_state = 0 self._connection_capabilities = 0 self._connection_properties = 0 self._audio_mode_is_voip = False self._conferenceable_connections: List["Connection"] = [] self._extras: Dict[str, Any] = {} def setVideoState(self, videoState: int): self._video_state = videoState def setActive(self): self._state = self.STATE_ACTIVE def setRinging(self): self._state = self.STATE_RINGING def setDialing(self): self._state = self.STATE_DIALING def setHolding(self): self._state = self.STATE_HOLDING def setDisconnected(self, disconnectCause: "DisconnectCause"): self._state = self.STATE_DISCONNECTED def setPostDialWait(self, remaining: str): pass def setConferenceableConnections( self, conferenceable: List["Connection"] ): self._conferenceable_connections = list(conferenceable) def setAudioModeIsVoip(self, isVoip: bool): self._audio_mode_is_voip = isVoip def setConnectionCapabilities(self, capabilities: int): self._connection_capabilities = capabilities def setConnectionProperties(self, properties: int): self._connection_properties = properties def putExtra(self, key: str, value: Any): self._extras[key] = value def onShowIncomingCallUi(self): pass def onCallAudioStateChanged(self, audioState: "CallAudioState"): pass def destroy(self): self._state = self.STATE_DISCONNECTED def onCallFilteringCompleted(self, completion: Any): pass def onSilence(self): pass def toString(self) -> str: return f"Connection(state={self._state})" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF