- ВКонтакте
- РћРТвЂВВВВВВВВнокласснРСвЂВВВВВВВВРєРСвЂВВВВВВВВ
- РњРѕР№ Р В Р’В Р РЋРЎв„ўР В Р’В Р РЋРІР‚ВВВВВВВВРЎР‚
- Viber
- Skype
- Telegram
Вопрос связанный с 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
и т.д.