feat: Add fallback method for notch info retrieval using DisplayCutout API
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 — запуск приложения
|
||||
|
||||
Reference in New Issue
Block a user