diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt index 2f95b669..057fe18d 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt @@ -50,7 +50,13 @@ class MainActivity : ComponentActivity() { requestNotificationPermission() requestFgsMicPermission() - handleIncomingCallIntent(intent) + + /* Входящий звонок: showWhenLocked/turnScreenOn ДО setContent, иначе полноэкранный + intent мигает и UI не отрисовывается поверх keyguard (холодный старт). */ + if (incomingCallId(intent) != 0L) { + LogManager.addLog("INFO", "MAIN", "launched for incoming call 0x%016x".format(incomingCallId(intent))) + showOverLockScreen() + } /* Edge-to-edge with dark status/navigation bar icons (light app theme) */ enableEdgeToEdge( @@ -359,23 +365,26 @@ class MainActivity : ComponentActivity() { override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) setIntent(intent) - handleIncomingCallIntent(intent) + if (incomingCallId(intent) != 0L) { + LogManager.addLog("INFO", "MAIN", "launched for incoming call 0x%016x".format(incomingCallId(intent))) + showOverLockScreen() + } } - /** Full-screen intent входящего звонка: показать activity поверх блокировки. */ - private fun handleIncomingCallIntent(intent: Intent?) { - val callId = intent?.getLongExtra(IncomingCallManager.EXTRA_CALL_ID, 0L) ?: 0L - if (callId == 0L) return - LogManager.addLog("INFO", "MAIN", "launched for incoming call 0x%016x".format(callId)) + private fun incomingCallId(intent: Intent?): Long = + intent?.getLongExtra(IncomingCallManager.EXTRA_CALL_ID, 0L) ?: 0L + + /** Показать активити поверх блокировки и включить экран (входящий звонок). */ + private fun showOverLockScreen() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { setShowWhenLocked(true) setTurnScreenOn(true) - overLock = true } else { @Suppress("DEPRECATION") window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) } + overLock = true } /** Снять «показ поверх блокировки», выставленный для входящего звонка. */ diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/RingtonePlayer.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/RingtonePlayer.kt index 862dc011..aa6ebbd1 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/RingtonePlayer.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/RingtonePlayer.kt @@ -11,7 +11,6 @@ import android.net.Uri import android.os.Build import android.os.VibrationEffect import android.os.Vibrator -import android.provider.Settings import com.utun.chat.ChatApplication import com.utun.chat.R @@ -19,12 +18,13 @@ import com.utun.chat.R * Мелодия входящего звонка через профиль звонка (STREAM_RING) + ручная вибрация. * * Поведение по системному режиму звонка (AudioManager.getRingerMode): - * - NORMAL : звук + вибрация, если включена настройка «вибрировать при вызове» - * (Settings.System.VIBRATE_WHEN_RINGING); + * - NORMAL : звук + вибрация; * - VIBRATE: без звука, только вибрация; * - SILENT : без звука и вибрации. * * Вибрация всегда запускается вручную через Vibrator (класс Ringtone сам не вибрирует). + * Настройка «вибрировать при вызове» (VIBRATE_WHEN_RINGING) устарела и на новых + * Android/Samsung не отражает реальное поведение телефонии — поэтому не используется. * * API 28+ : звук через Ringtone (RingtoneManager), громкость — «громкость вызова». * API 26–27: звук через MediaPlayer(STREAM_RING, loop) + audio focus. @@ -48,8 +48,7 @@ class RingtonePlayer { val ringerMode = am.ringerMode val playSound = ringerMode == AudioManager.RINGER_MODE_NORMAL - val playVibrate = ringerMode == AudioManager.RINGER_MODE_VIBRATE - || (playSound && systemVibrateWhenRinging()) + val playVibrate = ringerMode != AudioManager.RINGER_MODE_SILENT LogManager.addLog("INFO", "Ringtone", "ringerMode=%d sound=%b vibrate=%b".format(ringerMode, playSound, playVibrate)) @@ -120,14 +119,7 @@ class RingtonePlayer { LogManager.addLog("INFO", "Ringtone", "stopped") } - /* ── вибрация (решение принято в play(): VIBRATE-режим или VIBRATE_WHEN_RINGING) ── */ - - private fun systemVibrateWhenRinging(): Boolean = try { - Settings.System.getInt( - ChatApplication.instance.contentResolver, Settings.System.VIBRATE_WHEN_RINGING, 0) != 0 - } catch (e: Exception) { - false - } + /* ── вибрация (решение принято в play(): вибрируем всегда, кроме SILENT) ── */ private fun startVibrate() { val v = try { diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/IncomingCallDialog.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/IncomingCallDialog.kt index e70e51f7..c601accb 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/IncomingCallDialog.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/IncomingCallDialog.kt @@ -15,9 +15,12 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.compose.ui.window.Dialog import com.utun.chat.viewmodel.ChatViewModel +/** + * Входящий звонок — inline-оверлей внутри окна активити (не отдельное Dialog-окно): + * так он отрисовывается и поверх keyguard при пробуждении полноэкранным intent'ом. + */ @Composable fun IncomingCallDialog( viewModel: ChatViewModel @@ -25,7 +28,10 @@ fun IncomingCallDialog( val st = viewModel.callState.collectAsState().value ?: return val name = st.peerName.ifEmpty { "0x%016x".format(st.peerNodeId) } - Dialog(onDismissRequest = { }) { + Box( + modifier = Modifier.fillMaxSize().background(Color(0xCC000000)), + contentAlignment = Alignment.Center + ) { Surface( shape = MaterialTheme.shapes.large, color = Color(0xFF1B2733)