feat: Implement navigation bar visibility handling based on navigation mode

This commit is contained in:
2026-02-09 10:34:40 +05:00
parent e1c119f621
commit 8dfcf1c410
7 changed files with 225 additions and 31 deletions

View File

@@ -25,7 +25,9 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.draw.scale
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
@@ -43,9 +45,12 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.airbnb.lottie.compose.*
import com.rosetta.messenger.R
import com.rosetta.messenger.ui.theme.*
import com.rosetta.messenger.ui.utils.NavigationModeUtils
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.acos
@@ -154,8 +159,12 @@ fun OnboardingScreen(
val window = (view.context as android.app.Activity).window
val insetsController = WindowCompat.getInsetsController(window, view)
insetsController.isAppearanceLightStatusBars = !isDarkTheme
insetsController.isAppearanceLightNavigationBars = !isDarkTheme
window.statusBarColor = android.graphics.Color.TRANSPARENT
// Navigation bar: показываем только если есть нативные кнопки
NavigationModeUtils.applyNavigationBarVisibility(
insetsController, view.context, isDarkTheme
)
}
}
@@ -163,8 +172,16 @@ fun OnboardingScreen(
LaunchedEffect(Unit) {
if (!view.isInEditMode) {
val window = (view.context as android.app.Activity).window
window.navigationBarColor =
if (isDarkTheme) 0xFF1E1E1E.toInt() else 0xFFFFFFFF.toInt()
val insetsController = WindowCompat.getInsetsController(window, view)
if (NavigationModeUtils.hasNativeNavigationBar(view.context)) {
window.navigationBarColor =
if (isDarkTheme) 0xFF1E1E1E.toInt() else 0xFFFFFFFF.toInt()
} else {
// Жестовая навигация — прячем бар
insetsController.hide(WindowInsetsCompat.Type.navigationBars())
insetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
}
@@ -685,7 +702,7 @@ fun GooeyPagerIndicator(
modifier: Modifier = Modifier,
dotRadius: Dp = 2.8.dp,
dotSpacing: Dp = 12.dp,
indicatorHeight: Dp = 18.dp
indicatorHeight: Dp = 18.dp,
) {
if (pageCount <= 0) return
@@ -699,6 +716,17 @@ fun GooeyPagerIndicator(
val trackWidth = if (pageCount > 1) spacing * (pageCount - 1) else 0f
val startX = (size.width - trackWidth) / 2f
if (pageCount > 1) {
val trackInset = baseRadius * 0.75f
val trackHeight = baseRadius * 0.45f
drawRoundRect(
color = unselectedColor.copy(alpha = 0.14f),
topLeft = Offset(startX - trackInset, centerY - trackHeight / 2f),
size = Size(trackWidth + trackInset * 2f, trackHeight),
cornerRadius = CornerRadius(trackHeight, trackHeight)
)
}
val rawPosition =
(pagerState.currentPage + pagerState.currentPageOffsetFraction)
.coerceIn(0f, (pageCount - 1).toFloat())
@@ -706,35 +734,77 @@ fun GooeyPagerIndicator(
repeat(pageCount) { index ->
val center = Offset(startX + index * spacing, centerY)
drawCircle(color = unselectedColor, radius = baseRadius, center = center)
val distanceToActive = abs(rawPosition - index)
val influence = (1f - distanceToActive / 1.25f).coerceIn(0f, 1f)
val dotScale = 0.88f + influence * 0.16f
val dotAlpha = 0.35f + influence * 0.35f
drawCircle(
color = unselectedColor.copy(alpha = unselectedColor.alpha * dotAlpha),
radius = baseRadius * dotScale,
center = center
)
}
val from = floor(rawPosition.toDouble()).toInt().coerceIn(0, pageCount - 1)
val to = ceil(rawPosition.toDouble()).toInt().coerceIn(0, pageCount - 1)
val transition = rawPosition - from
val stretch = (1f - abs(transition - 0.5f) * 2f).coerceIn(0f, 1f)
val activeRadius = baseRadius * (1.08f + stretch * 0.34f)
val activeRadius = baseRadius * (1.05f + stretch * 0.3f)
var metaballPath: Path? = null
var anchorCenter: Offset? = null
var anchorRadius = 0f
if (from != to) {
val anchorIndex = if (transition < 0.5f) from else to
val anchorCenter = Offset(startX + anchorIndex * spacing, centerY)
val anchorRadius = baseRadius * (1.0f - stretch * 0.1f)
anchorCenter = Offset(startX + anchorIndex * spacing, centerY)
anchorRadius = baseRadius * (0.86f + (1f - stretch) * 0.2f)
createMetaballPath(
metaballPath =
createMetaballPath(
c1 = activeCenter,
r1 = activeRadius,
c2 = anchorCenter,
r2 = anchorRadius,
maxDistance = spacing * 1.28f,
viscosity = 0.32f,
handleSize = 2.25f
maxDistance = spacing * 1.36f,
viscosity = 0.36f + stretch * 0.12f,
handleSize = 2.45f
)
?.let { path ->
drawPath(path = path, color = selectedColor.copy(alpha = 0.92f))
}
}
drawCircle(
color = selectedColor.copy(alpha = 0.18f),
radius = activeRadius * 2.2f,
center = activeCenter
)
if (anchorCenter != null) {
drawCircle(
color = selectedColor.copy(alpha = 0.1f + stretch * 0.1f),
radius = anchorRadius * 1.9f,
center = anchorCenter
)
}
metaballPath?.let { path ->
drawPath(path = path, color = selectedColor.copy(alpha = 0.2f + stretch * 0.12f))
}
if (anchorCenter != null) {
drawCircle(
color = selectedColor.copy(alpha = 0.78f),
radius = anchorRadius,
center = anchorCenter
)
}
metaballPath?.let { path -> drawPath(path = path, color = selectedColor.copy(alpha = 0.92f)) }
drawCircle(color = selectedColor, radius = activeRadius, center = activeCenter)
drawCircle(
color = Color.White.copy(alpha = 0.28f),
radius = activeRadius * 0.38f,
center =
Offset(
x = activeCenter.x - activeRadius * 0.28f,
y = activeCenter.y - activeRadius * 0.32f
)
)
}
}