Browse Source

chatgui-android: входящий звонок поверх блокировки (inline-оверлей) + вибрация всегда кроме silent

proxy
evgeny 1 week ago
parent
commit
ceaf466b1c
  1. 25
      tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt
  2. 18
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/RingtonePlayer.kt
  3. 10
      tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/IncomingCallDialog.kt

25
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
}
/** Снять «показ поверх блокировки», выставленный для входящего звонка. */

18
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 {

10
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)

Loading…
Cancel
Save