Релиз 1.1.6: сессии, аватарки и интерфейсные исправления

This commit is contained in:
2026-03-10 23:25:03 +05:00
parent 9d0cab3f53
commit 810913b28e
9 changed files with 202 additions and 32 deletions

View File

@@ -23,8 +23,8 @@ val gitShortSha = safeGitOutput("rev-parse", "--short", "HEAD") ?: "unknown"
// ═══════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════
// Rosetta versioning — bump here on each release // Rosetta versioning — bump here on each release
// ═══════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════
val rosettaVersionName = "1.1.5" val rosettaVersionName = "1.1.6"
val rosettaVersionCode = 17 // Increment on each release val rosettaVersionCode = 18 // Increment on each release
android { android {
namespace = "com.rosetta.messenger" namespace = "com.rosetta.messenger"

View File

@@ -72,6 +72,8 @@ class MainActivity : FragmentActivity() {
companion object { companion object {
private const val TAG = "MainActivity" private const val TAG = "MainActivity"
// Process-memory session cache: lets app return without password while process is alive.
private var cachedDecryptedAccount: DecryptedAccount? = null
// 🔔 FCM Логи для отображения в UI // 🔔 FCM Логи для отображения в UI
private val _fcmLogs = mutableStateListOf<String>() private val _fcmLogs = mutableStateListOf<String>()
@@ -87,8 +89,18 @@ class MainActivity : FragmentActivity() {
} }
} }
fun clearFcmLogs() { fun clearFcmLogs() {
_fcmLogs.clear() _fcmLogs.clear()
}
private fun cacheSessionAccount(account: DecryptedAccount?) {
cachedDecryptedAccount = account
}
private fun getCachedSessionAccount(): DecryptedAccount? = cachedDecryptedAccount
private fun clearCachedSessionAccount() {
cachedDecryptedAccount = null
} }
} }
@@ -152,14 +164,12 @@ class MainActivity : FragmentActivity() {
var showSplash by remember { mutableStateOf(true) } var showSplash by remember { mutableStateOf(true) }
var showOnboarding by remember { mutableStateOf(true) } var showOnboarding by remember { mutableStateOf(true) }
var hasExistingAccount by remember { mutableStateOf<Boolean?>(null) } var hasExistingAccount by remember { mutableStateOf<Boolean?>(null) }
var currentAccount by remember { mutableStateOf<DecryptedAccount?>(null) } var currentAccount by remember { mutableStateOf(getCachedSessionAccount()) }
var accountInfoList by remember { mutableStateOf<List<AccountInfo>>(emptyList()) } var accountInfoList by remember { mutableStateOf<List<AccountInfo>>(emptyList()) }
var startCreateAccountFlow by remember { mutableStateOf(false) } var startCreateAccountFlow by remember { mutableStateOf(false) }
// Check for existing accounts and build AccountInfo list // Check for existing accounts and build AccountInfo list
// Also force logout so user always sees unlock screen on app restart
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
accountManager.logout() // Always start logged out
val accounts = accountManager.getAllAccounts() val accounts = accountManager.getAllAccounts()
hasExistingAccount = accounts.isNotEmpty() hasExistingAccount = accounts.isNotEmpty()
accountInfoList = accounts.map { it.toAccountInfo() } accountInfoList = accounts.map { it.toAccountInfo() }
@@ -239,6 +249,7 @@ class MainActivity : FragmentActivity() {
onAuthComplete = { account -> onAuthComplete = { account ->
startCreateAccountFlow = false startCreateAccountFlow = false
currentAccount = account currentAccount = account
cacheSessionAccount(account)
hasExistingAccount = true hasExistingAccount = true
// Save as last logged account // Save as last logged account
account?.let { account?.let {
@@ -256,6 +267,7 @@ class MainActivity : FragmentActivity() {
// Set currentAccount to null immediately to prevent UI // Set currentAccount to null immediately to prevent UI
// lag // lag
currentAccount = null currentAccount = null
clearCachedSessionAccount()
scope.launch { scope.launch {
com.rosetta.messenger.network.ProtocolManager com.rosetta.messenger.network.ProtocolManager
.disconnect() .disconnect()
@@ -283,6 +295,7 @@ class MainActivity : FragmentActivity() {
// Set currentAccount to null immediately to prevent UI // Set currentAccount to null immediately to prevent UI
// lag // lag
currentAccount = null currentAccount = null
clearCachedSessionAccount()
scope.launch { scope.launch {
com.rosetta.messenger.network.ProtocolManager com.rosetta.messenger.network.ProtocolManager
.disconnect() .disconnect()
@@ -315,6 +328,7 @@ class MainActivity : FragmentActivity() {
hasExistingAccount = accounts.isNotEmpty() hasExistingAccount = accounts.isNotEmpty()
// 8. Navigate away last // 8. Navigate away last
currentAccount = null currentAccount = null
clearCachedSessionAccount()
} catch (e: Exception) { } catch (e: Exception) {
android.util.Log.e("DeleteAccount", "Failed to delete account", e) android.util.Log.e("DeleteAccount", "Failed to delete account", e)
} }
@@ -353,6 +367,7 @@ class MainActivity : FragmentActivity() {
// 9. If current account is deleted, return to main login screen // 9. If current account is deleted, return to main login screen
if (currentAccount?.publicKey == targetPublicKey) { if (currentAccount?.publicKey == targetPublicKey) {
currentAccount = null currentAccount = null
clearCachedSessionAccount()
} }
} catch (e: Exception) { } catch (e: Exception) {
android.util.Log.e("DeleteAccount", "Failed to delete account from sidebar", e) android.util.Log.e("DeleteAccount", "Failed to delete account from sidebar", e)
@@ -367,6 +382,7 @@ class MainActivity : FragmentActivity() {
// Switch to another account: logout current, then show unlock. // Switch to another account: logout current, then show unlock.
currentAccount = null currentAccount = null
clearCachedSessionAccount()
scope.launch { scope.launch {
com.rosetta.messenger.network.ProtocolManager.disconnect() com.rosetta.messenger.network.ProtocolManager.disconnect()
accountManager.logout() accountManager.logout()
@@ -375,6 +391,7 @@ class MainActivity : FragmentActivity() {
onAddAccount = { onAddAccount = {
startCreateAccountFlow = true startCreateAccountFlow = true
currentAccount = null currentAccount = null
clearCachedSessionAccount()
scope.launch { scope.launch {
com.rosetta.messenger.network.ProtocolManager.disconnect() com.rosetta.messenger.network.ProtocolManager.disconnect()
accountManager.logout() accountManager.logout()
@@ -387,6 +404,7 @@ class MainActivity : FragmentActivity() {
isDarkTheme = isDarkTheme, isDarkTheme = isDarkTheme,
onExit = { onExit = {
currentAccount = null currentAccount = null
clearCachedSessionAccount()
scope.launch { scope.launch {
ProtocolManager.disconnect() ProtocolManager.disconnect()
accountManager.logout() accountManager.logout()

View File

@@ -17,17 +17,18 @@ object ReleaseNotes {
val RELEASE_NOTICE = """ val RELEASE_NOTICE = """
Update v$VERSION_PLACEHOLDER Update v$VERSION_PLACEHOLDER
Подключение Профили и аватарки
- Ускорен старт соединения и handshake при входе в аккаунт - Добавлен полноэкранный просмотр аватарок в чужом профиле (включая системные аккаунты)
- Логика reconnect синхронизирована с desktop-поведением - Исправлено отображение даты аватарки: устранён некорректный год (например, 58154)
- Обновлён серверный endpoint на основной production (wss)
Группы Сессии и вход
- Добавлено предзагруженное кэширование участников группы - Добавлен кэш сессии в памяти процесса: повторный пароль не запрашивается, пока процесс жив
- Убран скачок "0 members" при повторном открытии группы - Кэш сессии корректно очищается при выходе, переключении и удалении аккаунта
Интерфейс Интерфейс
- Исправлено вертикальное выравнивание verified-галочки в списке чатов - Исправлено центрирование blur-фона у системных аватарок
- Унифицировано определение темы для verified-галочек
- В списке чатов verified-галочки сделаны синими в светлой теме (включая system light)
""".trimIndent() """.trimIndent()
fun getNotice(version: String): String = fun getNotice(version: String): String =

View File

@@ -935,7 +935,7 @@ fun ChatsListScreen(
VerifiedBadge( VerifiedBadge(
verified = if (accountVerified > 0) accountVerified else 1, verified = if (accountVerified > 0) accountVerified else 1,
size = 15, size = 15,
badgeTint = Color(0xFFACD2F9) badgeTint = PrimaryBlue
) )
} }
} }
@@ -3891,7 +3891,9 @@ fun DialogItemContent(
VerifiedBadge( VerifiedBadge(
verified = if (dialog.verified > 0) dialog.verified else 1, verified = if (dialog.verified > 0) dialog.verified else 1,
size = 16, size = 16,
modifier = Modifier.offset(y = (-2).dp) modifier = Modifier.offset(y = (-2).dp),
isDarkTheme = isDarkTheme,
badgeTint = PrimaryBlue
) )
} }
// 🔒 Красная иконка замочка для заблокированных пользователей // 🔒 Красная иконка замочка для заблокированных пользователей

View File

@@ -2,6 +2,11 @@ package com.rosetta.messenger.ui.components
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Shader
import android.os.Build import android.os.Build
import android.renderscript.Allocation import android.renderscript.Allocation
import android.renderscript.Element import android.renderscript.Element
@@ -32,6 +37,7 @@ import com.rosetta.messenger.repository.AvatarRepository
import com.rosetta.messenger.utils.AvatarFileManager import com.rosetta.messenger.utils.AvatarFileManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlin.math.abs
/** /**
* Компонент для отображения размытого фона аватарки * Компонент для отображения размытого фона аватарки
@@ -63,7 +69,11 @@ fun BoxScope.BlurredAvatarBackground(
LaunchedEffect(avatarKey, publicKey) { LaunchedEffect(avatarKey, publicKey) {
val currentAvatars = avatars val currentAvatars = avatars
val newOriginal = withContext(Dispatchers.IO) { val newOriginal = withContext(Dispatchers.IO) {
if (currentAvatars.isNotEmpty()) { // Keep system account blur source identical to the visible avatar drawable.
// This prevents offset/parallax mismatches when server avatar payload differs.
if (MessageRepository.isSystemAccount(publicKey)) {
loadSystemAvatarBitmap(context, publicKey)
} else if (currentAvatars.isNotEmpty()) {
AvatarFileManager.base64ToBitmap(currentAvatars.first().base64Data) AvatarFileManager.base64ToBitmap(currentAvatars.first().base64Data)
} else { } else {
loadSystemAvatarBitmap(context, publicKey) loadSystemAvatarBitmap(context, publicKey)
@@ -183,7 +193,78 @@ private fun loadSystemAvatarBitmap(context: Context, publicKey: String): Bitmap?
val drawable = AppCompatResources.getDrawable(context, resId) ?: return null val drawable = AppCompatResources.getDrawable(context, resId) ?: return null
val width = drawable.intrinsicWidth.takeIf { it > 0 } ?: 256 val width = drawable.intrinsicWidth.takeIf { it > 0 } ?: 256
val height = drawable.intrinsicHeight.takeIf { it > 0 } ?: 256 val height = drawable.intrinsicHeight.takeIf { it > 0 } ?: 256
return drawable.toBitmap(width = width, height = height, config = Bitmap.Config.ARGB_8888) val source = drawable.toBitmap(width = width, height = height, config = Bitmap.Config.ARGB_8888)
return recenterSystemBlurSource(source)
}
private data class FocusCenter(val x: Float, val y: Float)
private fun recenterSystemBlurSource(source: Bitmap): Bitmap {
val focusCenter = detectSystemFocusCenter(source) ?: return source
val dx = source.width / 2f - focusCenter.x
val dy = source.height / 2f - focusCenter.y
if (abs(dx) < 0.5f && abs(dy) < 0.5f) return source
val output = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
val shader = BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
val matrix = Matrix().apply { setTranslate(dx, dy) }
shader.setLocalMatrix(matrix)
val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
this.shader = shader
isFilterBitmap = true
isDither = true
}
canvas.drawRect(0f, 0f, source.width.toFloat(), source.height.toFloat(), paint)
return output
}
private fun detectSystemFocusCenter(source: Bitmap): FocusCenter? {
val width = source.width
val height = source.height
if (width < 2 || height < 2) return null
val cornerColors = intArrayOf(
source.getPixel(0, 0),
source.getPixel(width - 1, 0),
source.getPixel(0, height - 1),
source.getPixel(width - 1, height - 1)
)
val bgR = cornerColors.map { android.graphics.Color.red(it) }.average().toFloat()
val bgG = cornerColors.map { android.graphics.Color.green(it) }.average().toFloat()
val bgB = cornerColors.map { android.graphics.Color.blue(it) }.average().toFloat()
val step = (minOf(width, height) / 140).coerceAtLeast(1)
val threshold = 54f
var weightedX = 0f
var weightedY = 0f
var totalWeight = 0f
var y = 0
while (y < height) {
var x = 0
while (x < width) {
val pixel = source.getPixel(x, y)
val dr = abs(android.graphics.Color.red(pixel) - bgR)
val dg = abs(android.graphics.Color.green(pixel) - bgG)
val db = abs(android.graphics.Color.blue(pixel) - bgB)
val weight = (dr + dg + db) - threshold
if (weight > 0f) {
weightedX += x * weight
weightedY += y * weight
totalWeight += weight
}
x += step
}
y += step
}
if (totalWeight <= 0f) return null
return FocusCenter(
x = weightedX / totalWeight,
y = weightedY / totalWeight
)
} }
/** /**

View File

@@ -1,7 +1,6 @@
package com.rosetta.messenger.ui.components package com.rosetta.messenger.ui.components
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.* import androidx.compose.material3.*
@@ -16,6 +15,7 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import com.rosetta.messenger.R import com.rosetta.messenger.R
import com.rosetta.messenger.ui.onboarding.PrimaryBlue import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import com.rosetta.messenger.ui.theme.LocalRosettaIsDarkTheme
/** /**
* Значок верификации пользователя * Значок верификации пользователя
@@ -23,14 +23,14 @@ import com.rosetta.messenger.ui.onboarding.PrimaryBlue
* *
* @param verified Уровень верификации (0 = нет, 1 = стандартная, 2+ = особая) * @param verified Уровень верификации (0 = нет, 1 = стандартная, 2+ = особая)
* @param size Размер значка в dp * @param size Размер значка в dp
* @param isDarkTheme Тема приложения (если не передано - используется системная) * @param isDarkTheme Тема приложения (если не передано - вычисляется из MaterialTheme)
*/ */
@Composable @Composable
fun VerifiedBadge( fun VerifiedBadge(
verified: Int, verified: Int,
size: Int = 16, size: Int = 16,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isDarkTheme: Boolean = isSystemInDarkTheme(), isDarkTheme: Boolean = LocalRosettaIsDarkTheme.current,
badgeTint: Color? = null badgeTint: Color? = null
) { ) {
if (verified <= 0) return if (verified <= 0) return

View File

@@ -19,6 +19,7 @@ import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
@@ -51,6 +52,7 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.onSizeChanged
@@ -192,6 +194,9 @@ fun OtherProfileScreen(
var isBlocked by remember { mutableStateOf(false) } var isBlocked by remember { mutableStateOf(false) }
var showAvatarMenu by remember { mutableStateOf(false) } var showAvatarMenu by remember { mutableStateOf(false) }
var showImageViewer by remember { mutableStateOf(false) } var showImageViewer by remember { mutableStateOf(false) }
var showAvatarViewer by remember { mutableStateOf(false) }
var avatarViewerBitmap by remember { mutableStateOf<android.graphics.Bitmap?>(null) }
var avatarViewerTimestamp by remember { mutableStateOf(0L) }
var imageViewerInitialIndex by remember { mutableIntStateOf(0) } var imageViewerInitialIndex by remember { mutableIntStateOf(0) }
var selectedTab by remember { mutableStateOf(OtherProfileTab.MEDIA) } var selectedTab by remember { mutableStateOf(OtherProfileTab.MEDIA) }
val pagerState = rememberPagerState( val pagerState = rememberPagerState(
@@ -212,12 +217,12 @@ fun OtherProfileScreen(
snapshotFlow { pagerState.currentPage }.collect { page -> snapshotFlow { pagerState.currentPage }.collect { page ->
selectedTab = OtherProfileTab.entries[page] selectedTab = OtherProfileTab.entries[page]
// Swipe-back only on first tab (Media); on other tabs pager handles swipe // Swipe-back only on first tab (Media); on other tabs pager handles swipe
onSwipeBackEnabledChanged(page == 0 && !showImageViewer) onSwipeBackEnabledChanged(page == 0 && !showImageViewer && !showAvatarViewer)
} }
} }
LaunchedEffect(showImageViewer) { LaunchedEffect(showImageViewer, showAvatarViewer) {
onSwipeBackEnabledChanged(!showImageViewer && pagerState.currentPage == 0) onSwipeBackEnabledChanged(!showImageViewer && !showAvatarViewer && pagerState.currentPage == 0)
} }
val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF) val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF)
@@ -1123,6 +1128,43 @@ fun OtherProfileScreen(
} }
} }
}, },
onAvatarLongPress = {
coroutineScope.launch {
when {
hasAvatar && avatars.isNotEmpty() -> {
val first = avatars.first()
avatarViewerTimestamp = first.timestamp
val bitmap = withContext(Dispatchers.IO) {
AvatarFileManager.base64ToBitmap(first.base64Data)
}
if (bitmap != null) {
avatarViewerBitmap = bitmap
showAvatarViewer = true
}
}
user.publicKey == MessageRepository.SYSTEM_SAFE_PUBLIC_KEY -> {
val bitmap = withContext(Dispatchers.IO) {
BitmapFactory.decodeResource(context.resources, R.drawable.safe_account)
}
if (bitmap != null) {
avatarViewerTimestamp = 0L
avatarViewerBitmap = bitmap
showAvatarViewer = true
}
}
user.publicKey == MessageRepository.SYSTEM_UPDATES_PUBLIC_KEY -> {
val bitmap = withContext(Dispatchers.IO) {
BitmapFactory.decodeResource(context.resources, R.drawable.updates_account)
}
if (bitmap != null) {
avatarViewerTimestamp = 0L
avatarViewerBitmap = bitmap
showAvatarViewer = true
}
}
}
}
},
backgroundBlurColorId = backgroundBlurColorId backgroundBlurColorId = backgroundBlurColorId
) )
@@ -1135,6 +1177,16 @@ fun OtherProfileScreen(
isDarkTheme = isDarkTheme isDarkTheme = isDarkTheme
) )
} }
FullScreenAvatarViewer(
isVisible = showAvatarViewer,
onDismiss = { showAvatarViewer = false },
displayName = user.title.ifBlank { user.publicKey.take(10) },
avatarTimestamp = avatarViewerTimestamp,
avatarBitmap = avatarViewerBitmap,
publicKey = user.publicKey,
isDarkTheme = isDarkTheme
)
} }
} }
@@ -1815,6 +1867,7 @@ private fun CollapsingOtherProfileHeader(
onBlockToggle: () -> Unit, onBlockToggle: () -> Unit,
avatarRepository: AvatarRepository? = null, avatarRepository: AvatarRepository? = null,
onClearChat: () -> Unit, onClearChat: () -> Unit,
onAvatarLongPress: () -> Unit = {},
backgroundBlurColorId: String = "avatar" backgroundBlurColorId: String = "avatar"
) { ) {
val density = LocalDensity.current val density = LocalDensity.current
@@ -1943,7 +1996,12 @@ private fun CollapsingOtherProfileHeader(
shape = RoundedCornerShape(cornerRadius) shape = RoundedCornerShape(cornerRadius)
clip = true clip = true
} }
.background(avatarColors.backgroundColor), .background(avatarColors.backgroundColor)
.pointerInput(Unit) {
detectTapGestures(
onLongPress = { onAvatarLongPress() }
)
},
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
if (publicKey == MessageRepository.SYSTEM_SAFE_PUBLIC_KEY) { if (publicKey == MessageRepository.SYSTEM_SAFE_PUBLIC_KEY) {

View File

@@ -2074,7 +2074,13 @@ fun FullScreenAvatarViewer(
val dateText = remember(avatarTimestamp) { val dateText = remember(avatarTimestamp) {
if (avatarTimestamp > 0) { if (avatarTimestamp > 0) {
val sdf = SimpleDateFormat("MMMM d, yyyy 'at' h:mm a", Locale.ENGLISH) val sdf = SimpleDateFormat("MMMM d, yyyy 'at' h:mm a", Locale.ENGLISH)
sdf.format(Date(avatarTimestamp * 1000)) val normalizedTimestampMs = when {
avatarTimestamp >= 1_000_000_000_000_000_000L -> avatarTimestamp / 1_000_000
avatarTimestamp >= 1_000_000_000_000_000L -> avatarTimestamp / 1_000
avatarTimestamp >= 1_000_000_000_000L -> avatarTimestamp
else -> avatarTimestamp * 1_000
}
sdf.format(Date(normalizedTimestampMs))
} else "" } else ""
} }

View File

@@ -16,6 +16,8 @@ import androidx.core.view.WindowCompat
import com.rosetta.messenger.ui.utils.NavigationModeUtils import com.rosetta.messenger.ui.utils.NavigationModeUtils
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
val LocalRosettaIsDarkTheme = staticCompositionLocalOf { true }
private val DarkColorScheme = darkColorScheme( private val DarkColorScheme = darkColorScheme(
primary = DarkPrimary, primary = DarkPrimary,
secondary = Accent, secondary = Accent,
@@ -78,9 +80,11 @@ fun RosettaAndroidTheme(
} }
} }
MaterialTheme( CompositionLocalProvider(LocalRosettaIsDarkTheme provides darkTheme) {
colorScheme = colorScheme, MaterialTheme(
typography = Typography, colorScheme = colorScheme,
content = content typography = Typography,
) content = content
)
}
} }