package com.rosetta.messenger import android.app.KeyguardManager import android.content.Context import android.content.Intent import android.os.Build import android.os.Bundle import android.util.Log import android.view.WindowManager import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.runtime.* import com.rosetta.messenger.network.CallActionResult import com.rosetta.messenger.network.CallForegroundService import com.rosetta.messenger.network.CallManager import com.rosetta.messenger.network.CallPhase import com.rosetta.messenger.ui.chats.calls.CallOverlay import com.rosetta.messenger.ui.theme.RosettaAndroidTheme /** * Лёгкая Activity для показа входящего звонка на lock screen. * Показывается поверх экрана блокировки, без auth/splash. * При Accept → переходит в MainActivity. При Decline → закрывается. */ class IncomingCallActivity : ComponentActivity() { companion object { private const val TAG = "IncomingCallActivity" } override fun onCreate(savedInstanceState: Bundle?) { try { super.onCreate(savedInstanceState) } catch (e: Throwable) { Log.e(TAG, "super.onCreate CRASHED", e) callLog("super.onCreate CRASHED: ${e.message}") finish() return } callLog("onCreate START") // Показываем поверх lock screen и включаем экран callLog("setting lock screen flags, SDK=${Build.VERSION.SDK_INT}") window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { setShowWhenLocked(true) setTurnScreenOn(true) } else { @Suppress("DEPRECATION") window.addFlags( WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON ) } // Dismiss keyguard if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val km = getSystemService(Context.KEYGUARD_SERVICE) as? KeyguardManager km?.requestDismissKeyguard(this, null) } else { @Suppress("DEPRECATION") window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) } try { CallManager.initialize(applicationContext) callLog("CallManager initialized, phase=${CallManager.state.value.phase}") } catch (e: Throwable) { callLog("CallManager.initialize CRASHED: ${e.message}") Log.e(TAG, "CallManager init failed", e) } callLog("calling setContent") setContent { val callState by CallManager.state.collectAsState() // Ждём до 10 сек пока WebSocket доставит сигнал (CallManager перейдёт из IDLE) var wasIncoming by remember { mutableStateOf(false) } LaunchedEffect(callState.phase) { callLog("phase changed: ${callState.phase}") if (callState.phase == CallPhase.INCOMING) wasIncoming = true // Закрываем только когда звонок завершился if (callState.phase == CallPhase.IDLE && wasIncoming) { callLog("IDLE after INCOMING → finish()") finish() } // НЕ закрываемся при CONNECTING/ACTIVE — остаёмся на экране звонка // IncomingCallActivity показывает полный CallOverlay, не нужно переходить в MainActivity } // Показываем INCOMING в IDLE только до первого реального входящего состояния. // Иначе после Decline/END на мгновение мелькает "Unknown". val shouldShowProvisionalIncoming = callState.phase == CallPhase.IDLE && !wasIncoming && (callState.peerPublicKey.isNotBlank() || callState.peerTitle.isNotBlank() || callState.peerUsername.isNotBlank()) val displayState = if (shouldShowProvisionalIncoming) { callState.copy(phase = CallPhase.INCOMING, statusText = "Incoming call...") } else { callState } RosettaAndroidTheme(darkTheme = true) { CallOverlay( state = displayState, isDarkTheme = true, isExpanded = true, onAccept = { callLog("onAccept tapped, phase=${callState.phase}") if (callState.phase == CallPhase.INCOMING) { val result = CallManager.acceptIncomingCall() callLog("acceptIncomingCall result=$result") // Остаёмся на IncomingCallActivity — она покажет CONNECTING → ACTIVE } else { callLog("onAccept: phase=${callState.phase}, trying accept anyway") CallManager.acceptIncomingCall() } }, onDecline = { callLog("onDecline tapped") CallManager.declineIncomingCall() finish() }, onEnd = { callLog("onEnd tapped") CallManager.endCall() finish() }, onToggleMute = { CallManager.toggleMute() }, onToggleSpeaker = { CallManager.toggleSpeaker() } ) } } } private fun openMainActivity() { callLog("openMainActivity") val intent = Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP putExtra(CallForegroundService.EXTRA_OPEN_CALL_FROM_NOTIFICATION, true) } startActivity(intent) } private fun callLog(msg: String) { Log.d(TAG, msg) try { val ctx = applicationContext ?: return val dir = java.io.File(ctx.filesDir, "crash_reports") if (!dir.exists()) dir.mkdirs() val f = java.io.File(dir, "call_notification_log.txt") val ts = java.text.SimpleDateFormat("HH:mm:ss.SSS", java.util.Locale.getDefault()).format(java.util.Date()) f.appendText("$ts [IncomingCallActivity] $msg\n") } catch (e: Throwable) { Log.e(TAG, "callLog write failed: ${e.message}") } } }