Релиз 1.4.3: полноэкранные входящие звонки, аватарки в уведомлениях, фиксы
Some checks failed
Android Kernel Build / build (push) Failing after 4m6s
Some checks failed
Android Kernel Build / build (push) Failing after 4m6s
Звонки: - IncomingCallActivity — полноэкранный UI входящего звонка поверх lock screen - fullScreenIntent на нотификации для Android 12+ - ForegroundService синхронизируется при смене фазы и имени - Запрос fullScreenIntent permission на Android 14+ - dispose() PeerConnection при завершении звонка - Защита от CREATE_ROOM без ключей (звонок на другом устройстве) - Дедупликация push + WebSocket сигналов - setIncomingFromPush — CallManager сразу в INCOMING по push - Accept ждёт до 5 сек если WebSocket не доставил сигнал - Decline работает во всех фазах (не только INCOMING) - Баннер активного звонка внутри диалога Уведомления: - Аватарки и имена по publicKey в уведомлениях (message + call) - Настройка "Avatars in Notifications" в разделе Notifications UI: - Ограничение fontScale до 1.3x (вёрстка не ломается на огромном тексте) - Новые обои: Light 1-3 для светлой темы, убраны старые back_* - ContentScale.Crop для превью обоев (без растяжения) CI/CD: - NDK/CMake в CI, local.properties, ANDROID_NDK_HOME - Ограничение JVM heap для CI раннера Диагностика: - Логирование call notification flow в crash_reports (rosettadev1) - FCM токен в crash_reports
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package com.rosetta.messenger
|
||||
// commit
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@@ -83,6 +85,10 @@ class MainActivity : FragmentActivity() {
|
||||
private lateinit var preferencesManager: PreferencesManager
|
||||
private lateinit var accountManager: AccountManager
|
||||
|
||||
// Флаг: Activity открыта для ответа на звонок с lock screen — пропускаем auth
|
||||
// mutableStateOf чтобы Compose реагировал на изменение (избежать race condition)
|
||||
private var openedForCall by mutableStateOf(false)
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MainActivity"
|
||||
// Process-memory session cache: lets app return without password while process is alive.
|
||||
@@ -120,6 +126,7 @@ class MainActivity : FragmentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
handleCallLockScreen(intent)
|
||||
|
||||
preferencesManager = PreferencesManager(this)
|
||||
accountManager = AccountManager(this)
|
||||
@@ -161,6 +168,21 @@ class MainActivity : FragmentActivity() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Android 14+: запрос fullScreenIntent для входящих звонков
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
val nm = getSystemService(Context.NOTIFICATION_SERVICE) as android.app.NotificationManager
|
||||
if (!nm.canUseFullScreenIntent()) {
|
||||
try {
|
||||
startActivity(
|
||||
android.content.Intent(
|
||||
android.provider.Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT,
|
||||
android.net.Uri.parse("package:$packageName")
|
||||
)
|
||||
)
|
||||
} catch (_: Throwable) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -212,6 +234,9 @@ class MainActivity : FragmentActivity() {
|
||||
showSplash -> "splash"
|
||||
showOnboarding && hasExistingAccount == false ->
|
||||
"onboarding"
|
||||
// При открытии по звонку с lock screen — пропускаем auth
|
||||
openedForCall && hasExistingAccount == true ->
|
||||
"main"
|
||||
isLoggedIn != true && hasExistingAccount == false ->
|
||||
"auth_new"
|
||||
isLoggedIn != true && hasExistingAccount == true ->
|
||||
@@ -433,6 +458,66 @@ class MainActivity : FragmentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
handleCallLockScreen(intent)
|
||||
}
|
||||
|
||||
private var callLockScreenJob: kotlinx.coroutines.Job? = null
|
||||
|
||||
/**
|
||||
* Показать Activity поверх экрана блокировки при входящем звонке.
|
||||
* При завершении звонка флаги снимаются чтобы не нарушать обычное поведение.
|
||||
*/
|
||||
private fun handleCallLockScreen(intent: Intent?) {
|
||||
val isCallIntent = intent?.getBooleanExtra(
|
||||
com.rosetta.messenger.network.CallForegroundService.EXTRA_OPEN_CALL_FROM_NOTIFICATION, false
|
||||
) == true
|
||||
if (isCallIntent) {
|
||||
openedForCall = true
|
||||
// Включаем экран и показываем поверх lock screen
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
setShowWhenLocked(true)
|
||||
setTurnScreenOn(true)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
window.addFlags(
|
||||
android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
|
||||
android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
||||
)
|
||||
}
|
||||
// Убираем lock screen полностью
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as? android.app.KeyguardManager
|
||||
keyguardManager?.requestDismissKeyguard(this, null)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD)
|
||||
}
|
||||
// Снять флаги когда звонок закончится (отменяем предыдущий коллектор если был)
|
||||
callLockScreenJob?.cancel()
|
||||
callLockScreenJob = lifecycleScope.launch {
|
||||
com.rosetta.messenger.network.CallManager.state.collect { state ->
|
||||
if (state.phase == com.rosetta.messenger.network.CallPhase.IDLE) {
|
||||
openedForCall = false
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
setShowWhenLocked(false)
|
||||
setTurnScreenOn(false)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
window.clearFlags(
|
||||
android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
|
||||
android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
||||
)
|
||||
}
|
||||
callLockScreenJob?.cancel()
|
||||
callLockScreenJob = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
// 🔥 Приложение стало видимым - отключаем уведомления
|
||||
@@ -1347,7 +1432,8 @@ fun MainScreen(
|
||||
chatWallpaperId = chatWallpaperId,
|
||||
avatarRepository = avatarRepository,
|
||||
onImageViewerChanged = { isLocked -> isChatSwipeLocked = isLocked },
|
||||
isCallActive = callUiState.isVisible
|
||||
isCallActive = callUiState.isVisible,
|
||||
onOpenCallOverlay = { isCallOverlayExpanded = true }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user