feat: Add network permissions and security configuration for internet access
This commit is contained in:
@@ -2,6 +2,9 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
@@ -11,6 +14,7 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.RosettaAndroid"
|
android:theme="@style/Theme.RosettaAndroid"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ fun ConfirmSeedPhraseScreen(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.imePadding()
|
||||||
.padding(horizontal = 24.dp)
|
.padding(horizontal = 24.dp)
|
||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ fun ImportSeedPhraseScreen(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.imePadding()
|
||||||
.padding(horizontal = 24.dp),
|
.padding(horizontal = 24.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ fun SetPasswordScreen(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.imePadding()
|
||||||
.padding(horizontal = 24.dp)
|
.padding(horizontal = 24.dp)
|
||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ fun UnlockScreen(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.imePadding()
|
||||||
.padding(horizontal = 24.dp)
|
.padding(horizontal = 24.dp)
|
||||||
.statusBarsPadding(),
|
.statusBarsPadding(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -41,6 +43,13 @@ fun WelcomeScreen(
|
|||||||
val textColor by animateColorAsState(if (isDarkTheme) Color.White else Color.Black, animationSpec = themeAnimSpec)
|
val textColor by animateColorAsState(if (isDarkTheme) Color.White else Color.Black, animationSpec = themeAnimSpec)
|
||||||
val secondaryTextColor by animateColorAsState(if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666), animationSpec = themeAnimSpec)
|
val secondaryTextColor by animateColorAsState(if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666), animationSpec = themeAnimSpec)
|
||||||
|
|
||||||
|
// Sync navigation bar color with background
|
||||||
|
val view = LocalView.current
|
||||||
|
SideEffect {
|
||||||
|
val window = (view.context as? android.app.Activity)?.window
|
||||||
|
window?.navigationBarColor = backgroundColor.toArgb()
|
||||||
|
}
|
||||||
|
|
||||||
// Animation for Lottie
|
// Animation for Lottie
|
||||||
val lockComposition by rememberLottieComposition(LottieCompositionSpec.Asset("lottie/lock.json"))
|
val lockComposition by rememberLottieComposition(LottieCompositionSpec.Asset("lottie/lock.json"))
|
||||||
val lockProgress by animateLottieCompositionAsState(
|
val lockProgress by animateLottieCompositionAsState(
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
@@ -49,27 +51,50 @@ data class Chat(
|
|||||||
val isPinned: Boolean = false
|
val isPinned: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
// Avatar colors like in React Native app
|
// Avatar colors matching React Native app (Mantine inspired)
|
||||||
private val avatarColors = listOf(
|
// Light theme colors (background lighter, text darker)
|
||||||
Color(0xFF5E9FFF), // Blue
|
private val avatarColorsLight = listOf(
|
||||||
Color(0xFFFF7EB3), // Pink
|
Color(0xFF1971c2) to Color(0xFFd0ebff), // blue
|
||||||
Color(0xFF7B68EE), // Purple
|
Color(0xFF0c8599) to Color(0xFFc5f6fa), // cyan
|
||||||
Color(0xFF50C878), // Green
|
Color(0xFF9c36b5) to Color(0xFFeebefa), // grape
|
||||||
Color(0xFFFF6B6B), // Red
|
Color(0xFF2f9e44) to Color(0xFFd3f9d8), // green
|
||||||
Color(0xFF4ECDC4), // Teal
|
Color(0xFF4263eb) to Color(0xFFdbe4ff), // indigo
|
||||||
Color(0xFFFFB347), // Orange
|
Color(0xFF5c940d) to Color(0xFFe9fac8), // lime
|
||||||
Color(0xFFBA55D3) // Orchid
|
Color(0xFFd9480f) to Color(0xFFffe8cc), // orange
|
||||||
|
Color(0xFFc2255c) to Color(0xFFffdeeb), // pink
|
||||||
|
Color(0xFFe03131) to Color(0xFFffe0e0), // red
|
||||||
|
Color(0xFF099268) to Color(0xFFc3fae8), // teal
|
||||||
|
Color(0xFF6741d9) to Color(0xFFe5dbff) // violet
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cache для цветов аватаров - избегаем вычисления каждый раз
|
// Dark theme colors (background darker, text lighter)
|
||||||
private val avatarColorCache = mutableMapOf<String, Color>()
|
private val avatarColorsDark = listOf(
|
||||||
|
Color(0xFF7dd3fc) to Color(0xFF2d3548), // blue
|
||||||
|
Color(0xFF67e8f9) to Color(0xFF2d4248), // cyan
|
||||||
|
Color(0xFFd8b4fe) to Color(0xFF39334c), // grape
|
||||||
|
Color(0xFF86efac) to Color(0xFF2d3f32), // green
|
||||||
|
Color(0xFFa5b4fc) to Color(0xFF333448), // indigo
|
||||||
|
Color(0xFFbef264) to Color(0xFF383f2d), // lime
|
||||||
|
Color(0xFFfdba74) to Color(0xFF483529), // orange
|
||||||
|
Color(0xFFf9a8d4) to Color(0xFF482d3d), // pink
|
||||||
|
Color(0xFFfca5a5) to Color(0xFF482d2d), // red
|
||||||
|
Color(0xFF5eead4) to Color(0xFF2d4340), // teal
|
||||||
|
Color(0xFFc4b5fd) to Color(0xFF3a334c) // violet
|
||||||
|
)
|
||||||
|
|
||||||
fun getAvatarColor(name: String): Color {
|
// Cache для цветов аватаров
|
||||||
return avatarColorCache.getOrPut(name) {
|
data class AvatarColors(val textColor: Color, val backgroundColor: Color)
|
||||||
val index = name.hashCode().mod(avatarColors.size).let {
|
private val avatarColorCache = mutableMapOf<String, AvatarColors>()
|
||||||
if (it < 0) it + avatarColors.size else it
|
|
||||||
|
fun getAvatarColor(name: String, isDarkTheme: Boolean): AvatarColors {
|
||||||
|
val cacheKey = "${name}_${if (isDarkTheme) "dark" else "light"}"
|
||||||
|
return avatarColorCache.getOrPut(cacheKey) {
|
||||||
|
val colors = if (isDarkTheme) avatarColorsDark else avatarColorsLight
|
||||||
|
val index = name.hashCode().mod(colors.size).let {
|
||||||
|
if (it < 0) it + colors.size else it
|
||||||
}
|
}
|
||||||
avatarColors[index]
|
val (textColor, bgColor) = colors[index]
|
||||||
|
AvatarColors(textColor, bgColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,11 +138,97 @@ fun ChatsListScreen(
|
|||||||
onNewChat: () -> Unit,
|
onNewChat: () -> Unit,
|
||||||
onLogout: () -> Unit
|
onLogout: () -> Unit
|
||||||
) {
|
) {
|
||||||
val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF)
|
// Theme transition animation state
|
||||||
|
var isTransitioning by remember { mutableStateOf(false) }
|
||||||
|
var transitionProgress by remember { mutableStateOf(0f) }
|
||||||
|
var clickPosition by remember { mutableStateOf(Offset.Zero) }
|
||||||
|
var shouldUpdateStatusBar by remember { mutableStateOf(false) }
|
||||||
|
var hasInitialized by remember { mutableStateOf(false) }
|
||||||
|
var previousTheme by remember { mutableStateOf(isDarkTheme) }
|
||||||
|
var targetTheme by remember { mutableStateOf(isDarkTheme) }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
hasInitialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme transition animation
|
||||||
|
LaunchedEffect(isTransitioning) {
|
||||||
|
if (isTransitioning) {
|
||||||
|
shouldUpdateStatusBar = false
|
||||||
|
val duration = 800f
|
||||||
|
val startTime = System.currentTimeMillis()
|
||||||
|
while (transitionProgress < 1f) {
|
||||||
|
val elapsed = System.currentTimeMillis() - startTime
|
||||||
|
transitionProgress = (elapsed / duration).coerceAtMost(1f)
|
||||||
|
kotlinx.coroutines.delay(16)
|
||||||
|
}
|
||||||
|
shouldUpdateStatusBar = true
|
||||||
|
kotlinx.coroutines.delay(50)
|
||||||
|
isTransitioning = false
|
||||||
|
transitionProgress = 0f
|
||||||
|
shouldUpdateStatusBar = false
|
||||||
|
previousTheme = targetTheme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val view = androidx.compose.ui.platform.LocalView.current
|
||||||
|
|
||||||
|
// Animate navigation bar color starting at 80% of wave animation
|
||||||
|
LaunchedEffect(isTransitioning, transitionProgress) {
|
||||||
|
if (isTransitioning && transitionProgress >= 0.8f && !view.isInEditMode) {
|
||||||
|
val window = (view.context as android.app.Activity).window
|
||||||
|
val navProgress = ((transitionProgress - 0.8f) / 0.2f).coerceIn(0f, 1f)
|
||||||
|
|
||||||
|
val oldColor = if (previousTheme) 0xFF1A1A1A else 0xFFFFFFFF
|
||||||
|
val newColor = if (targetTheme) 0xFF1A1A1A else 0xFFFFFFFF
|
||||||
|
|
||||||
|
val r1 = (oldColor shr 16 and 0xFF)
|
||||||
|
val g1 = (oldColor shr 8 and 0xFF)
|
||||||
|
val b1 = (oldColor and 0xFF)
|
||||||
|
val r2 = (newColor shr 16 and 0xFF)
|
||||||
|
val g2 = (newColor shr 8 and 0xFF)
|
||||||
|
val b2 = (newColor and 0xFF)
|
||||||
|
|
||||||
|
val r = (r1 + (r2 - r1) * navProgress).toInt()
|
||||||
|
val g = (g1 + (g2 - g1) * navProgress).toInt()
|
||||||
|
val b = (b1 + (b2 - b1) * navProgress).toInt()
|
||||||
|
|
||||||
|
window.navigationBarColor = (0xFF000000 or (r.toLong() shl 16) or (g.toLong() shl 8) or b.toLong()).toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update status bar icons when animation finishes
|
||||||
|
LaunchedEffect(shouldUpdateStatusBar) {
|
||||||
|
if (shouldUpdateStatusBar && !view.isInEditMode) {
|
||||||
|
val window = (view.context as android.app.Activity).window
|
||||||
|
val insetsController = androidx.core.view.WindowCompat.getInsetsController(window, view)
|
||||||
|
insetsController.isAppearanceLightStatusBars = !isDarkTheme
|
||||||
|
insetsController.isAppearanceLightNavigationBars = !isDarkTheme
|
||||||
|
window.statusBarColor = android.graphics.Color.TRANSPARENT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val backgroundColor by animateColorAsState(
|
||||||
|
targetValue = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF),
|
||||||
|
animationSpec = if (!hasInitialized) snap() else tween(800, easing = FastOutSlowInEasing),
|
||||||
|
label = "backgroundColor"
|
||||||
|
)
|
||||||
val drawerBackgroundColor = if (isDarkTheme) Color(0xFF212121) else Color(0xFFFFFFFF)
|
val drawerBackgroundColor = if (isDarkTheme) Color(0xFF212121) else Color(0xFFFFFFFF)
|
||||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
val textColor by animateColorAsState(
|
||||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
targetValue = if (isDarkTheme) Color.White else Color.Black,
|
||||||
val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8)
|
animationSpec = if (!hasInitialized) snap() else tween(800, easing = FastOutSlowInEasing),
|
||||||
|
label = "textColor"
|
||||||
|
)
|
||||||
|
val secondaryTextColor by animateColorAsState(
|
||||||
|
targetValue = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666),
|
||||||
|
animationSpec = if (!hasInitialized) snap() else tween(800, easing = FastOutSlowInEasing),
|
||||||
|
label = "secondaryTextColor"
|
||||||
|
)
|
||||||
|
val dividerColor by animateColorAsState(
|
||||||
|
targetValue = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8),
|
||||||
|
animationSpec = if (!hasInitialized) snap() else tween(800, easing = FastOutSlowInEasing),
|
||||||
|
label = "dividerColor"
|
||||||
|
)
|
||||||
|
|
||||||
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
@@ -245,6 +356,31 @@ fun ChatsListScreen(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
|
// Base background - shows the OLD theme color during transition
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(if (isTransitioning) {
|
||||||
|
if (previousTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF)
|
||||||
|
} else backgroundColor)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Circular reveal overlay - draws the NEW theme color expanding
|
||||||
|
if (isTransitioning) {
|
||||||
|
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||||
|
val maxRadius = kotlin.math.hypot(size.width, size.height)
|
||||||
|
val radius = maxRadius * transitionProgress
|
||||||
|
|
||||||
|
// Draw the NEW theme color expanding from click point
|
||||||
|
drawCircle(
|
||||||
|
color = if (targetTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF),
|
||||||
|
radius = radius,
|
||||||
|
center = clickPosition
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ModalNavigationDrawer(
|
ModalNavigationDrawer(
|
||||||
drawerState = drawerState,
|
drawerState = drawerState,
|
||||||
drawerContent = {
|
drawerContent = {
|
||||||
@@ -283,13 +419,35 @@ fun ChatsListScreen(
|
|||||||
|
|
||||||
// Theme toggle
|
// Theme toggle
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onToggleTheme
|
onClick = {},
|
||||||
|
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||||
|
// This will be handled by clickable below
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
Icon(
|
Box(
|
||||||
if (isDarkTheme) Icons.Default.LightMode else Icons.Default.DarkMode,
|
modifier = Modifier
|
||||||
contentDescription = "Toggle theme",
|
.clickable {
|
||||||
tint = textColor
|
if (!isTransitioning) {
|
||||||
)
|
previousTheme = isDarkTheme
|
||||||
|
targetTheme = !isDarkTheme
|
||||||
|
// Use center of icon as click position
|
||||||
|
val screenWidth = view.width.toFloat()
|
||||||
|
val screenHeight = view.height.toFloat()
|
||||||
|
clickPosition = Offset(
|
||||||
|
screenWidth - 48.dp.value * view.resources.displayMetrics.density,
|
||||||
|
96.dp.value * view.resources.displayMetrics.density
|
||||||
|
)
|
||||||
|
isTransitioning = true
|
||||||
|
onToggleTheme()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
if (isDarkTheme) Icons.Default.LightMode else Icons.Default.DarkMode,
|
||||||
|
contentDescription = "Toggle theme",
|
||||||
|
tint = textColor
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,18 +592,19 @@ fun ChatsListScreen(
|
|||||||
.background(backgroundColor),
|
.background(backgroundColor),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
|
val avatarColors = getAvatarColor(accountName, isDarkTheme)
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(30.dp)
|
.size(30.dp)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.background(getAvatarColor(accountName)),
|
.background(avatarColors.backgroundColor),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = getInitials(accountName),
|
text = getInitials(accountName),
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = Color.White
|
color = avatarColors.textColor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -514,15 +673,50 @@ fun ChatsListScreen(
|
|||||||
},
|
},
|
||||||
containerColor = backgroundColor
|
containerColor = backgroundColor
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
// Empty state with Lottie animation
|
// Dev Console Button in bottom left corner
|
||||||
EmptyChatsState(
|
Box(
|
||||||
isDarkTheme = isDarkTheme,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
)
|
) {
|
||||||
|
// Console button
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = visible,
|
||||||
|
enter = fadeIn(tween(500, delayMillis = 400)) + slideInHorizontally(
|
||||||
|
initialOffsetX = { -it },
|
||||||
|
animationSpec = tween(500, delayMillis = 400)
|
||||||
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomStart)
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = { showDevConsole = true },
|
||||||
|
containerColor = if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFFF5F5F5),
|
||||||
|
contentColor = if (protocolState == ProtocolState.AUTHENTICATED)
|
||||||
|
Color(0xFF4CAF50)
|
||||||
|
else
|
||||||
|
Color(0xFFFF9800),
|
||||||
|
shape = CircleShape,
|
||||||
|
modifier = Modifier.size(48.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Terminal,
|
||||||
|
contentDescription = "Dev Console",
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty state with Lottie animation
|
||||||
|
EmptyChatsState(
|
||||||
|
isDarkTheme = isDarkTheme,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // Close Box for circular reveal
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -638,7 +832,7 @@ fun ChatItem(
|
|||||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||||
val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8)
|
val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8)
|
||||||
|
|
||||||
val avatarColor = getAvatarColor(chat.name)
|
val avatarColors = getAvatarColor(chat.name, isDarkTheme)
|
||||||
val initials = getInitials(chat.name)
|
val initials = getInitials(chat.name)
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@@ -654,14 +848,14 @@ fun ChatItem(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(56.dp)
|
.size(56.dp)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.background(avatarColor),
|
.background(avatarColors.backgroundColor),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = initials,
|
text = initials,
|
||||||
fontSize = 20.sp,
|
fontSize = 20.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
color = Color.White
|
color = avatarColors.textColor
|
||||||
)
|
)
|
||||||
|
|
||||||
// Online indicator
|
// Online indicator
|
||||||
|
|||||||
6
app/src/main/res/xml/network_security_config.xml
Normal file
6
app/src/main/res/xml/network_security_config.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config>
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain includeSubdomains="true">46.28.71.12</domain>
|
||||||
|
</domain-config>
|
||||||
|
</network-security-config>
|
||||||
Reference in New Issue
Block a user