diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt index bd21ef3..2266a65 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt @@ -217,7 +217,7 @@ fun ChatsListScreen( focusManager.clearFocus() } - // Update status bar and completely hide navigation bar + // Update status bar appearance LaunchedEffect(isDarkTheme) { if (!view.isInEditMode) { val window = (view.context as android.app.Activity).window @@ -228,13 +228,11 @@ fun ChatsListScreen( insetsController.isAppearanceLightStatusBars = !isDarkTheme window.statusBarColor = android.graphics.Color.TRANSPARENT - // Completely hide navigation bar - insetsController.hide( + // Navigation bar — keep visible, match theme + insetsController.show( androidx.core.view.WindowInsetsCompat.Type.navigationBars() ) - insetsController.systemBarsBehavior = - androidx.core.view.WindowInsetsControllerCompat - .BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + insetsController.isAppearanceLightNavigationBars = !isDarkTheme } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/components/metaball/NotchInfoUtils.kt b/app/src/main/java/com/rosetta/messenger/ui/components/metaball/NotchInfoUtils.kt index 88f3789..c79303e 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/components/metaball/NotchInfoUtils.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/components/metaball/NotchInfoUtils.kt @@ -5,6 +5,7 @@ import android.graphics.Path import android.graphics.RectF import android.os.Build import android.view.Gravity +import android.view.View import androidx.core.graphics.PathParser /** @@ -128,4 +129,56 @@ object NotchInfoUtils { return null } } + + /** + * Fallback: get notch info from DisplayCutout API (Android P+). + * Works on Pixel 7 Pro and other devices that don't expose config_mainBuiltInDisplayCutout. + * Requires a View that is attached to the window. + */ + fun getInfoFromCutout(view: View): NotchInfo? { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return null + try { + val insets = view.rootWindowInsets ?: return null + val cutout = insets.displayCutout ?: return null + val rects = cutout.boundingRects + if (rects.isEmpty()) return null + + val displayWidth = view.resources.displayMetrics.widthPixels + val density = view.resources.displayMetrics.density + + // Find the top cutout (front camera / punch-hole) + val topRect = rects.firstOrNull { it.top == 0 || it.top < it.height() * 2 } + ?: rects.first() + + val bounds = RectF( + topRect.left.toFloat(), + topRect.top.toFloat(), + topRect.right.toFloat(), + topRect.bottom.toFloat() + ) + + // Determine gravity + val centerX = bounds.centerX() + val gravity = when { + kotlin.math.abs(centerX - displayWidth / 2f) <= 4 * density -> Gravity.CENTER + centerX < displayWidth / 4f -> Gravity.START + centerX > displayWidth * 3f / 4f -> Gravity.END + else -> Gravity.CENTER // punch-hole near center + } + + val dp32 = 32 * density + val isLikelyCircle = bounds.width() <= dp32 || bounds.width() <= bounds.height() + + return NotchInfo( + gravity = gravity, + isAccurate = false, // no path data, just bounding rect + isLikelyCircle = isLikelyCircle, + path = null, + bounds = bounds, + rawPath = "cutout:${topRect}" + ) + } catch (e: Exception) { + return null + } + } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt b/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt index 42c5300..8c9f5fb 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt @@ -47,6 +47,7 @@ import androidx.compose.ui.graphics.nativeCanvas import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -364,6 +365,7 @@ fun ProfileMetaballOverlay( avatarContent: @Composable BoxScope.() -> Unit = {}, ) { val context = LocalContext.current + val view = LocalView.current val configuration = LocalConfiguration.current val screenWidth = configuration.screenWidthDp.dp val density = LocalDensity.current @@ -379,11 +381,14 @@ fun ProfileMetaballOverlay( val avatarSizeMinPx = with(density) { ProfileMetaballConstants.AVATAR_SIZE_MIN.toPx() } // Get REAL camera/notch info from system (like Telegram does) - val notchInfo = remember { NotchInfoUtils.getInfo(context) } + // Try resource-based first, fallback to DisplayCutout API (for Pixel 7 Pro etc.) + val notchInfo = remember(view) { + NotchInfoUtils.getInfo(context) ?: NotchInfoUtils.getInfoFromCutout(view) + } // Debug log notch info once LaunchedEffect(notchInfo, screenWidthPx, statusBarHeightPx, headerHeightPx) { - Log.d(TAG, "NotchInfo: gravity=${notchInfo?.gravity}, isCircle=${notchInfo?.isLikelyCircle}, bounds=${notchInfo?.bounds}") + Log.d(TAG, "NotchInfo: gravity=${notchInfo?.gravity}, isCircle=${notchInfo?.isLikelyCircle}, bounds=${notchInfo?.bounds}, raw=${notchInfo?.rawPath}") Log.d(TAG, "Screen: width=${screenWidthPx}px, statusBar=${statusBarHeightPx}px, header=${headerHeightPx}px") } @@ -845,6 +850,7 @@ fun ProfileMetaballOverlayCpu( avatarContent: @Composable BoxScope.() -> Unit = {}, ) { val context = LocalContext.current + val view = LocalView.current val configuration = LocalConfiguration.current val screenWidth = configuration.screenWidthDp.dp val density = LocalDensity.current @@ -856,7 +862,10 @@ fun ProfileMetaballOverlayCpu( val avatarSizeMinPx = with(density) { ProfileMetaballConstants.AVATAR_SIZE_MIN.toPx() } // Get notch info (debug override supported) - val notchInfo = remember { NotchInfoUtils.getInfo(context) } + // Try resource-based first, fallback to DisplayCutout API (for Pixel 7 Pro etc.) + val notchInfo = remember(view) { + NotchInfoUtils.getInfo(context) ?: NotchInfoUtils.getInfoFromCutout(view) + } val hasRealNotch = !MetaballDebug.forceNoNotch && notchInfo != null && notchInfo.gravity == Gravity.CENTER && notchInfo.bounds.width() > 0 val blackBarHeightPx = with(density) { BLACK_BAR_HEIGHT_DP.dp.toPx() } @@ -1231,6 +1240,24 @@ fun MetaballDebugPanel(modifier: Modifier = Modifier) { fontSize = 11.sp, fontWeight = FontWeight.Medium ) + + // Notch detection info + val view = LocalView.current + val notchRes = remember { NotchInfoUtils.getInfo(context) } + val notchCutout = remember(view) { NotchInfoUtils.getInfoFromCutout(view) } + val notchSource = when { + notchRes != null -> "resource" + notchCutout != null -> "DisplayCutout" + else -> "NONE" + } + val activeNotch = notchRes ?: notchCutout + Text( + text = "Notch: $notchSource" + + if (activeNotch != null) " | ${activeNotch.bounds.width().toInt()}x${activeNotch.bounds.height().toInt()}" + + " circle=${activeNotch.isLikelyCircle}" else " (black bar fallback!)", + color = if (activeNotch != null) ComposeColor(0xFF4CAF50) else ComposeColor(0xFFFF5722), + fontSize = 10.sp + ) } } diff --git a/baselineprofile/src/main/java/com/rosetta/messenger/baselineprofile/BaselineProfileGenerator.kt b/baselineprofile/src/main/java/com/rosetta/messenger/baselineprofile/BaselineProfileGenerator.kt index 931de90..3f3a7f6 100644 --- a/baselineprofile/src/main/java/com/rosetta/messenger/baselineprofile/BaselineProfileGenerator.kt +++ b/baselineprofile/src/main/java/com/rosetta/messenger/baselineprofile/BaselineProfileGenerator.kt @@ -1,6 +1,5 @@ package com.rosetta.messenger.baselineprofile -import androidx.benchmark.macro.ExperimentalBaselineProfilesApi import androidx.benchmark.macro.junit4.BaselineProfileRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest @@ -31,7 +30,7 @@ class BaselineProfileGenerator { val baselineProfileRule = BaselineProfileRule() @Test - fun generateBaselineProfile() = baselineProfileRule.collectBaselineProfile( + fun generateBaselineProfile() = baselineProfileRule.collect( packageName = "com.rosetta.messenger" ) { // 1. Cold start — запуск приложения