Вопрос связанный с android разработкой, взаимодействие с микрофоном во время звонков TG в частности

Написал простенький класс для AccessibilityService:

package com.example.audiostreamer.services

import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.AccessibilityServiceInfo
import android.util.Log
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

class TelegramCallAccessibilityService : AccessibilityService() {
    companion object {
        private const val TAG = "TGAccessibility"
        private val _isInTelegramCall = MutableStateFlow(false)
        val isInTelegramCall: StateFlow<Boolean> = _isInTelegramCall

        private val TELEGRAM_PACKAGES = listOf(
            "org.telegram.messenger",
            "org.telegram.messenger.web",
            "org.telegram.messenger.beta"
        )

        private val CALL_UI_INDICATORS = listOf(
            "call", "voip", "voice", "mute", "speaker",
            "hangup", "end_call", "decline",
            "incoming", "outgoing", "audio_call",
            "telegram_call", "voice_call"
        )
    }

    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        if (event.packageName == null) return

        val packageName = event.packageName?.toString()
        val className = event.className?.toString()

        Log.d(TAG, "Event: type=${event.eventType}, package=$packageName, class=$className")

        when (event.eventType) {
            AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
            AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED -> {
                if (TELEGRAM_PACKAGES.any { packageName?.startsWith(it) == true }) {
                    val root = rootInActiveWindow
                    val isCallActive = isCallScreenActive(root)
                    Log.d(TAG, "Checking Telegram UI - Call Active: $isCallActive")

                    if (isCallActive && !_isInTelegramCall.value) {
                        Log.d(TAG, "Telegram call detected - UI elements found")
                        _isInTelegramCall.value = true
                    } else if (!isCallActive && _isInTelegramCall.value && root != null) {
                        Log.d(TAG, "Call UI no longer detected - ending call state")
                        _isInTelegramCall.value = false
                    }
                    root?.recycle()
                } else if (_isInTelegramCall.value) {
                    Log.d(TAG, "Left Telegram - ending call state")
                    _isInTelegramCall.value = false
                }
            }
        }
    }

    private fun isCallScreenActive(root: AccessibilityNodeInfo?): Boolean {
        if (root == null) return false

        try {
            val allText = getAllText(root).lowercase()
            Log.d(TAG, "Screen text: $allText")

            if (CALL_UI_INDICATORS.any { it in allText }) {
                Log.d(TAG, "Call indicator found in text")
                return true
            }

            return findCallElements(root)
        } catch (e: Exception) {
            Log.e(TAG, "Error checking call screen: ${e.message}")
            return false
        }
    }

    private fun getAllText(node: AccessibilityNodeInfo?): String {
        if (node == null) return ""

        val textBuilder = StringBuilder()
        try {
            node.text?.toString()?.let { textBuilder.append(it).append(" ") }
            node.contentDescription?.toString()?.let { textBuilder.append(it).append(" ") }

            for (i in 0 until node.childCount) {
                val child = node.getChild(i)
                textBuilder.append(getAllText(child)).append(" ")
                child?.recycle()
            }
        } catch (e: Exception) {
            Log.e(TAG, "Error getting text: ${e.message}")
        }

        return textBuilder.toString()
    }

    private fun findCallElements(node: AccessibilityNodeInfo?): Boolean {
        if (node == null) return false

        try {
            val viewId = node.viewIdResourceName ?: ""
            if (CALL_UI_INDICATORS.any { indicator ->
                    viewId.lowercase().contains(indicator.lowercase())
                }) {
                Log.d(TAG, "Found call UI element: $viewId")
                return true
            }

            for (i in 0 until node.childCount) {
                val child = node.getChild(i)
                if (findCallElements(child)) {
                    return true
                }
                child?.recycle()
            }
        } catch (e: Exception) {
            Log.e(TAG, "Error in findCallElements: ${e.message}")
        }

        return false
    }

    override fun onServiceConnected() {
        Log.d(TAG, "TelegramCallAccessibilityService connected")
        serviceInfo = AccessibilityServiceInfo().apply {
            eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED or
                    AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
            feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC
            flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS or
                    AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS or
                    AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY or
                    AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS
            notificationTimeout = 100
        }
    }

    override fun onInterrupt() {
        Log.d(TAG, "TelegramCallAccessibilityService interrupted")
        _isInTelegramCall.value = false
    }
}

И мне нужен совет как взаимодействуя с текущей записью с микрофона на устройстве либо же в момент инициализации записи звука с микрофона, звук записи не пропадал если совершается звонок в ТГ. Желательно простой пример в коде как я могу продолжить писать звук если ТГ уж очень агрессивно забирает взаимодействие с микрофоном. В нынешней моей реализации например я пишу звук - начинается звонок в тг - звук с микро пропадает - звонок завершается либо мутится микрофон - запись звука продолжается. Какие есть идеи советы по искоренению прерывания прослушивания микро? Скажу сразу я пробовал применять

MediaRecorder.AudioSource.*
VOICE_COMMUNICATION
VOICE_CALL

и т.д.


Ответы (0 шт):