Fix theming and polish

This commit is contained in:
andrew 2023-07-10 00:12:42 +09:30
parent bbc9cdfeeb
commit 0824713ac5
6 changed files with 408 additions and 274 deletions

View File

@ -13,9 +13,12 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
@ -31,9 +34,11 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@ -55,8 +60,8 @@ import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAt
import org.thoughtcrime.securesms.MediaPreviewActivity import org.thoughtcrime.securesms.MediaPreviewActivity
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.Storage
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.mms.ImageSlide
import org.thoughtcrime.securesms.mms.Slide import org.thoughtcrime.securesms.mms.Slide
import org.thoughtcrime.securesms.ui.AppTheme import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.Avatar import org.thoughtcrime.securesms.ui.Avatar
@ -68,6 +73,7 @@ import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
import org.thoughtcrime.securesms.ui.Divider import org.thoughtcrime.securesms.ui.Divider
import org.thoughtcrime.securesms.ui.HorizontalPagerIndicator import org.thoughtcrime.securesms.ui.HorizontalPagerIndicator
import org.thoughtcrime.securesms.ui.ItemButton import org.thoughtcrime.securesms.ui.ItemButton
import org.thoughtcrime.securesms.ui.Theme
import org.thoughtcrime.securesms.ui.blackAlpha40 import org.thoughtcrime.securesms.ui.blackAlpha40
import org.thoughtcrime.securesms.ui.colorDestructive import org.thoughtcrime.securesms.ui.colorDestructive
import org.thoughtcrime.securesms.ui.destructiveButtonColors import org.thoughtcrime.securesms.ui.destructiveButtonColors
@ -118,27 +124,32 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
@Composable @Composable
private fun MessageDetailsScreen() { private fun MessageDetailsScreen() {
val details by viewModel.details.observeAsState(MessageDetails()) val details by viewModel.details.observeAsState(MessageDetails())
MessageDetails( val threadDb = DatabaseComponent.get(this@MessageDetailActivity).threadDatabase()
details, AppTheme {
onReply = { setResultAndFinish(ON_REPLY) }, MessageDetails(
onResend = { setResultAndFinish(ON_RESEND) }, threadDb = threadDb,
onDelete = { setResultAndFinish(ON_DELETE) }, messageDetails = details,
onClickImage = { slide -> onReply = { setResultAndFinish(ON_REPLY) },
// only open to downloaded images onResend = { setResultAndFinish(ON_RESEND) },
if (slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED) { onDelete = { setResultAndFinish(ON_DELETE) },
// Restart download here (on IO thread) onClickImage = { slide ->
(slide.asAttachment() as? DatabaseAttachment)?.let { attachment -> // only open to downloaded images
onAttachmentNeedsDownload(attachment.attachmentId.rowId, details.mmsRecord!!.getId()) if (slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED) {
// Restart download here (on IO thread)
(slide.asAttachment() as? DatabaseAttachment)?.let { attachment ->
onAttachmentNeedsDownload(attachment.attachmentId.rowId, details.mmsRecord!!.getId())
}
} }
} if (!slide.isInProgress) MediaPreviewActivity.getPreviewIntent(
if (!slide.isInProgress) MediaPreviewActivity.getPreviewIntent( this,
this, slide,
slide, details.mmsRecord,
details.mmsRecord, threadDb.getRecipientForThreadId(details.mmsRecord!!.threadId),
DatabaseComponent.get(this).threadDatabase().getRecipientForThreadId(details.mmsRecord!!.threadId), ).let(::startActivity)
).let(::startActivity) },
} onAttachmentNeedsDownload = ::onAttachmentNeedsDownload,
) )
}
} }
private fun setResultAndFinish(code: Int) { private fun setResultAndFinish(code: Int) {
@ -149,254 +160,276 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
finish() finish()
} }
@Preview
@Composable
fun PreviewMessageDetails() {
MessageDetails(
MessageDetails(
attachments = listOf(),
sent = TitledText("Sent:", "6:12 AM Tue, 09/08/2022"),
received = TitledText("Received:", "6:12 AM Tue, 09/08/2022"),
error = TitledText("Error:", "Message failed to send"),
senderInfo = TitledText("Connor", "d4f1g54sdf5g1d5f4g65ds4564df65f4g65d54gdfsg"),
)
)
}
private fun onAttachmentNeedsDownload(attachmentId: Long, mmsId: Long) { private fun onAttachmentNeedsDownload(attachmentId: Long, mmsId: Long) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
JobQueue.shared.add(AttachmentDownloadJob(attachmentId, mmsId)) JobQueue.shared.add(AttachmentDownloadJob(attachmentId, mmsId))
} }
} }
@SuppressLint("ClickableViewAccessibility") }
@Composable
fun MessageDetails(
messageDetails: MessageDetails,
onReply: () -> Unit = {},
onResend: () -> Unit = {},
onDelete: () -> Unit = {},
onClickImage: (Slide) -> Unit = {},
) {
AppTheme {
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
messageDetails.record?.let { message ->
AndroidView(
modifier = Modifier.padding(horizontal = 32.dp),
factory = {
ViewVisibleMessageContentBinding.inflate(LayoutInflater.from(it)).mainContainerConstraint.apply {
bind(
message,
thread = DatabaseComponent.get(this@MessageDetailActivity).threadDatabase().getRecipientForThreadId(message.threadId)!!,
onAttachmentNeedsDownload = ::onAttachmentNeedsDownload,
suppressThumbnails = true
)
setOnTouchListener { _, event -> @Composable
if (event.actionMasked == ACTION_UP) onContentClick(event) fun PreviewMessageDetails() {
true AppTheme {
} MessageDetails(
} messageDetails = MessageDetails(
} attachments = listOf(),
) sent = TitledText("Sent:", "6:12 AM Tue, 09/08/2022"),
} received = TitledText("Received:", "6:12 AM Tue, 09/08/2022"),
Attachments(messageDetails.attachments) { onClickImage(it) } error = TitledText("Error:", "Message failed to send"),
MetadataCell(messageDetails) senderInfo = TitledText("Connor", "d4f1g54sdf5g1d5f4g65ds4564df65f4g65d54"),
Buttons( )
messageDetails.error != null, )
onReply,
onResend,
onDelete,
)
}
}
} }
}
@Composable @SuppressLint("ClickableViewAccessibility")
fun MetadataCell( @Composable
messageDetails: MessageDetails, fun MessageDetails(
threadDb: ThreadDatabase? = null,
messageDetails: MessageDetails,
onReply: () -> Unit = {},
onResend: (() -> Unit)? = null,
onDelete: () -> Unit = {},
onClickImage: (Slide) -> Unit = {},
onAttachmentNeedsDownload: (Long, Long) -> Unit = { _, _ -> }
) {
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
messageDetails.apply { messageDetails.record?.let { message ->
if (sent != null || received != null || senderInfo != null) CellWithPaddingAndMargin { AndroidView(
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { modifier = Modifier.padding(horizontal = 32.dp),
sent?.let { TitledText(it) } factory = {
received?.let { TitledText(it) } ViewVisibleMessageContentBinding.inflate(LayoutInflater.from(it)).mainContainerConstraint.apply {
error?.let { TitledErrorText(it) } bind(
senderInfo?.let { message,
TitledView("From:") { thread = threadDb?.getRecipientForThreadId(message.threadId)!!,
Row { onAttachmentNeedsDownload = onAttachmentNeedsDownload,
sender?.let { Avatar(it) } suppressThumbnails = true
TitledMonospaceText(it) )
}
setOnTouchListener { _, event ->
if (event.actionMasked == ACTION_UP) onContentClick(event)
true
}
}
}
)
}
Carousel(messageDetails.attachments) { onClickImage(it) }
MetadataCell(messageDetails)
Buttons(
onReply,
onResend,
onDelete,
)
}
}
@Composable
fun MetadataCell(
messageDetails: MessageDetails,
) {
messageDetails.apply {
if (sent != null || received != null || senderInfo != null) CellWithPaddingAndMargin {
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
sent?.let { TitledText(it) }
received?.let { TitledText(it) }
error?.let { TitledErrorText(it) }
senderInfo?.let {
TitledView(stringResource(id = R.string.message_details_header__from)) {
Row {
sender?.let { Avatar(it) }
TitledMonospaceText(it)
} }
} }
} }
} }
} }
} }
}
@Composable @Composable
fun Buttons( fun Buttons(
hasError: Boolean, onReply: () -> Unit = {},
onReply: () -> Unit = {}, onResend: (() -> Unit)? = null,
onResend: () -> Unit = {}, onDelete: () -> Unit = {},
onDelete: () -> Unit = {}, ) {
) { Cell {
Cell { Column {
Column { ItemButton(
stringResource(id = R.string.reply),
R.drawable.ic_message_details__reply,
onClick = onReply
)
Divider()
onResend?.let {
ItemButton( ItemButton(
"Reply", stringResource(id = R.string.resend),
R.drawable.ic_message_details__reply, R.drawable.ic_message_details__refresh,
onClick = onReply onClick = it
) )
Divider() Divider()
if (hasError) {
ItemButton(
"Resend",
R.drawable.ic_message_details__refresh,
onClick = onResend
)
Divider()
}
ItemButton(
"Delete",
R.drawable.ic_message_details__trash,
colors = destructiveButtonColors(),
onClick = onDelete
)
} }
} ItemButton(
} stringResource(id = R.string.delete),
R.drawable.ic_message_details__trash,
@Composable colors = destructiveButtonColors(),
fun Attachments(attachments: List<Attachment>, onClick: (Slide) -> Unit) { onClick = onDelete
when(attachments.firstOrNull()?.slide) {
is ImageSlide -> Carousel(attachments, onClick)
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Carousel(attachments: List<Attachment>, onClick: (Slide) -> Unit) {
val imageAttachments = attachments.filter { it.slide.hasImage() }
val pagerState = rememberPagerState { imageAttachments.size }
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
Row {
CarouselPrevButton(pagerState)
Box(modifier = Modifier.weight(1f)) {
CellPager(pagerState, imageAttachments, onClick)
HorizontalPagerIndicator(pagerState)
ExpandButton(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(8.dp)
) { onClick(imageAttachments[pagerState.currentPage].slide) }
}
CarouselNextButton(pagerState)
}
FileDetails(attachments, pagerState)
}
}
@OptIn(
ExperimentalFoundationApi::class,
ExperimentalGlideComposeApi::class
)
@Composable
private fun CellPager(
pagerState: PagerState,
imageAttachments: List<Attachment>,
onClick: (Slide) -> Unit
) {
CellNoMargin {
HorizontalPager(state = pagerState) { i ->
val slide = imageAttachments[i].slide
GlideImage(
contentScale = ContentScale.Crop,
modifier = Modifier
.aspectRatio(1f)
.clickable { onClick(slide) },
model = slide.uri,
contentDescription = slide.fileName.orNull() ?: "image"
)
}
}
}
@Composable
fun ExpandButton(modifier: Modifier, onClick: () -> Unit) {
Surface(
shape = CircleShape,
color = blackAlpha40,
modifier = modifier
) {
Icon(
painter = painterResource(id = R.drawable.ic_expand),
contentDescription = "",
modifier = Modifier.clickable { onClick() }
) )
} }
} }
}
@OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun FileDetails(attachments: List<Attachment>, pagerState: PagerState) { fun Carousel(attachments: List<Attachment>, onClick: (Slide) -> Unit) {
attachments[pagerState.currentPage].fileDetails.takeIf { it.isNotEmpty() }?.let { val imageAttachments = attachments.filter { it.hasImage() }.takeIf { it.isNotEmpty() } ?: return
CellWithPaddingAndMargin { val pagerState = rememberPagerState { imageAttachments.size }
FlowRow(
verticalArrangement = Arrangement.spacedBy(16.dp), Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
maxItemsInEachRow = 2 Row {
) { CarouselPrevButton(pagerState)
it.forEach { TitledText(it, Modifier.weight(1f)) } Box(modifier = Modifier.weight(1f)) {
} CellCarousel(pagerState, imageAttachments, onClick)
HorizontalPagerIndicator(pagerState)
ExpandButton(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(8.dp)
) { onClick(imageAttachments[pagerState.currentPage].slide) }
}
CarouselNextButton(pagerState)
}
FileDetails(attachments, pagerState)
}
}
@OptIn(
ExperimentalFoundationApi::class,
ExperimentalGlideComposeApi::class
)
@Composable
private fun CellCarousel(
pagerState: PagerState,
imageAttachments: List<Attachment>,
onClick: (Slide) -> Unit
) {
CellNoMargin {
HorizontalPager(state = pagerState) { i ->
val slide = imageAttachments[i].slide
GlideImage(
contentScale = ContentScale.Crop,
modifier = Modifier
.aspectRatio(1f)
.clickable { onClick(slide) },
model = slide.uri,
contentDescription = slide.fileName.orNull() ?: stringResource(id = R.string.image)
)
}
}
}
@Composable
fun ExpandButton(modifier: Modifier = Modifier, onClick: () -> Unit) {
Surface(
shape = CircleShape,
color = blackAlpha40,
modifier = modifier,
contentColor = Color.White,
) {
Icon(
painter = painterResource(id = R.drawable.ic_expand),
contentDescription = "",
modifier = Modifier.clickable { onClick() },
)
}
}
@Preview
@Composable
fun PreviewFileDetails() {
Theme(R.style.Ocean_Dark) {
FileDetails(
fileDetails = listOf(
TitledText("File Id:", "Screen Shot 2023-07-06 at 11.35.50 am.png"),
TitledText("File Type:", "image/png"),
TitledText("File Size:", "195.6kB"),
TitledText("Resolution:", "342x312"),
)
)
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun FileDetails(attachments: List<Attachment>, pagerState: PagerState) {
FileDetails(attachments[pagerState.currentPage].fileDetails)
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun FileDetails(fileDetails: List<TitledText>) {
if (fileDetails.isEmpty()) return
CellWithPaddingAndMargin {
FlowRow(verticalArrangement = Arrangement.spacedBy(16.dp)) {
fileDetails.forEach {
TitledText(
it,
modifier = Modifier
.widthIn(min = 100.dp) // set minimum width
.width(IntrinsicSize.Max) // make the text as wide as necessary
.weight(1f) // space evenly
)
} }
} }
} }
}
@Composable @Composable
fun TitledErrorText(titledText: TitledText, modifier: Modifier = Modifier) { fun TitledErrorText(titledText: TitledText, modifier: Modifier = Modifier) {
TitledText( TitledText(
titledText, titledText,
modifier = modifier, modifier = modifier,
valueStyle = LocalTextStyle.current.copy(color = colorDestructive) valueStyle = LocalTextStyle.current.copy(color = colorDestructive)
) )
} }
@Composable @Composable
fun TitledMonospaceText(titledText: TitledText, modifier: Modifier = Modifier) { fun TitledMonospaceText(titledText: TitledText, modifier: Modifier = Modifier) {
TitledText( TitledText(
titledText, titledText,
modifier = modifier, modifier = modifier,
valueStyle = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace) valueStyle = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace)
) )
} }
@Composable @Composable
fun TitledText( fun TitledText(
titledText: TitledText, titledText: TitledText,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
valueStyle: TextStyle = LocalTextStyle.current valueStyle: TextStyle = LocalTextStyle.current
) { ) {
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) { Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
Title(titledText.title) Title(titledText.title)
Text(titledText.value, style = valueStyle) Text(titledText.value, style = valueStyle)
}
}
@Composable
fun TitledView(title: String, modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
Title(title)
content()
}
}
@Composable
fun Title(text: String) {
Text(text, fontWeight = FontWeight.Bold)
} }
} }
@Composable
fun TitledView(title: String, modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
Title(title)
content()
}
}
@Composable
fun Title(text: String, modifier: Modifier = Modifier) {
Text(text, modifier = modifier, fontWeight = FontWeight.Bold)
}

View File

@ -1,27 +1,60 @@
package org.thoughtcrime.securesms.ui package org.thoughtcrime.securesms.ui
import androidx.compose.material.ButtonDefaults import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Colors
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
val colorDestructive = Color(0xffFF453A) val colorDestructive = Color(0xffFF453A)
val classicDark0 = Color(0xff111111) const val classicDark0 = 0xff111111
const val classicDark1 = 0xff1B1B1B
const val classicDark2 = 0xff2D2D2D
const val classicDark3 = 0xff414141
const val classicDark4 = 0xff767676
const val classicDark5 = 0xffA1A2A1
const val classicDark6 = 0xffFFFFFF
val classicDark1 = Color(0xff1B1B1B)
val classicDark2 = Color(0xff2D2D2D)
val classicDark3 = Color(0xff414141)
val classicDark4 = Color(0xff767676)
val classicDark5 = Color(0xffA1A2A1)
val classicDark6 = Color(0xffFFFFFF)
val classicLight0 = Color(0xff000000)
val classicLight1 = Color(0xff6D6D6D) const val classicLight0 = 0xff000000
val classicLight2 = Color(0xffA1A2A1) const val classicLight1 = 0xff6D6D6D
val classicLight3 = Color(0xffDFDFDF) const val classicLight2 = 0xffA1A2A1
val classicLight4 = Color(0xffF0F0F0) const val classicLight3 = 0xffDFDFDF
val classicLight5 = Color(0xffF9F9F9) const val classicLight4 = 0xffF0F0F0
val classicLight6 = Color(0xffFFFFFF) const val classicLight5 = 0xffF9F9F9
const val classicLight6 = 0xffFFFFFF
const val oceanDark0 = 0xff000000
const val oceanDark1 = 0xff1A1C28
const val oceanDark2 = 0xff252735
const val oceanDark3 = 0xff2B2D40
const val oceanDark4 = 0xff3D4A5D
const val oceanDark5 = 0xffA6A9CE
const val oceanDark6 = 0xff5CAACC
const val oceanDark7 = 0xffFFFFFF
const val oceanLight0 = 0xff000000
const val oceanLight1 = 0xff19345D
const val oceanLight2 = 0xff6A6E90
const val oceanLight3 = 0xff5CAACC
const val oceanLight4 = 0xffB3EDF2
const val oceanLight5 = 0xffE7F3F4
const val oceanLight6 = 0xffECFAFB
const val oceanLight7 = 0xffFCFFFF
val ocean_accent = Color(0xff57C9FA)
val oceanLights = arrayOf(oceanLight0, oceanLight1, oceanLight2, oceanLight3, oceanLight4, oceanLight5, oceanLight6, oceanLight7)
val oceanDarks = arrayOf(oceanDark0, oceanDark1, oceanDark2, oceanDark3, oceanDark4, oceanDark5, oceanDark6, oceanDark7)
val classicLights = arrayOf(classicLight0, classicLight1, classicLight2, classicLight3, classicLight4, classicLight5, classicLight6)
val classicDarks = arrayOf(classicDark0, classicDark1, classicDark2, classicDark3, classicDark4, classicDark5, classicDark6)
val oceanLightColors = oceanLights.map(::Color)
val oceanDarkColors = oceanDarks.map(::Color)
val classicLightColors = classicLights.map(::Color)
val classicDarkColors = classicDarks.map(::Color)
val blackAlpha40 = Color.Black.copy(alpha = 0.4f) val blackAlpha40 = Color.Black.copy(alpha = 0.4f)

View File

@ -1,32 +1,50 @@
package org.thoughtcrime.securesms.ui package org.thoughtcrime.securesms.ui
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.view.ContextThemeWrapper
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.FlowRowScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ButtonColors import androidx.compose.material.ButtonColors
import androidx.compose.material.Card import androidx.compose.material.Card
import androidx.compose.material.Colors
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.IconButton import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextButton import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -36,6 +54,13 @@ import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.components.ProfilePictureView import org.thoughtcrime.securesms.components.ProfilePictureView
import kotlin.math.roundToInt
private val Colors.cellColors: Colors
@Composable
get() = MaterialTheme.colors.copy(
surface = LocalExtraColors.current.settingsBackground,
)
@Composable @Composable
fun ItemButton( fun ItemButton(
@ -80,17 +105,18 @@ fun CellWithPaddingAndMargin(
margin: Dp = 32.dp, margin: Dp = 32.dp,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
Card( MaterialTheme(colors = MaterialTheme.colors.cellColors) {
shape = RoundedCornerShape(16.dp), Card(
elevation = 0.dp, shape = RoundedCornerShape(16.dp),
modifier = Modifier elevation = 0.dp,
.wrapContentHeight() modifier = Modifier
.fillMaxWidth() .wrapContentHeight()
.padding(horizontal = margin), .fillMaxWidth()
backgroundColor = LocalExtraColors.current.settingsBackground, .padding(horizontal = margin),
// probably wrong ) {
contentColor = MaterialTheme.colors.onSurface Box(Modifier.padding(padding)) { content() }
) { Box(Modifier.padding(padding)) { content() } } }
}
} }
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@ -108,7 +134,7 @@ fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) {
pagerState = pagerState, pagerState = pagerState,
pageCount = pagerState.pageCount, pageCount = pagerState.pageCount,
activeColor = Color.White, activeColor = Color.White,
inactiveColor = classicDark5) inactiveColor = classicDarkColors[5])
} }
} }
} }
@ -154,7 +180,6 @@ fun RowScope.CarouselButton(
fun Divider() { fun Divider() {
androidx.compose.material.Divider( androidx.compose.material.Divider(
modifier = Modifier.padding(horizontal = 16.dp), modifier = Modifier.padding(horizontal = 16.dp),
color = LocalExtraColors.current.divider
) )
} }

View File

@ -2,25 +2,36 @@ package org.thoughtcrime.securesms.ui
import android.content.Context import android.content.Context
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.StyleRes
import androidx.appcompat.view.ContextThemeWrapper
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.google.accompanist.themeadapter.appcompat.AppCompatTheme import com.google.accompanist.themeadapter.appcompat.AppCompatTheme
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.conversation.v2.PreviewMessageDetails
val LocalExtraColors = staticCompositionLocalOf<ExtraColors> { error("No Custom Attribute value provided") } val LocalExtraColors = staticCompositionLocalOf<ExtraColors> { error("No Custom Attribute value provided") }
data class ExtraColors( data class ExtraColors(
val cell: Color,
val divider: Color,
val settingsBackground: Color, val settingsBackground: Color,
) )
fun Context.getColorFromTheme(@AttrRes attr: Int, defaultValue: Int = 0x0): Color = fun Context.getColorFromTheme(@AttrRes attr: Int, defaultValue: Int = 0x0): Color = try {
MaterialColors.getColor(this, attr, defaultValue).let(::Color) MaterialColors.getColor(this, attr, defaultValue).let(::Color)
} catch (e: Exception) {
colorDestructive
}
@Composable @Composable
fun AppTheme( fun AppTheme(
@ -28,9 +39,7 @@ fun AppTheme(
) { ) {
val extraColors = LocalContext.current.run { val extraColors = LocalContext.current.run {
ExtraColors( ExtraColors(
cell = getColorFromTheme(R.attr.colorCellBackground), settingsBackground = getColorFromTheme(R.attr.colorSettingsBackground),
divider = getColorFromTheme(R.attr.dividerHorizontal).copy(alpha = 0.15f),
settingsBackground = getColorFromTheme(R.attr.colorSettingsBackground)
) )
} }
@ -40,3 +49,35 @@ fun AppTheme(
} }
} }
} }
@Preview
@Composable
fun PreviewMessageDetails(
@PreviewParameter(ThemeResPreviewParameterProvider::class) themeResId: Int
) {
Theme(themeResId) {
Box(modifier = Modifier.background(color = MaterialTheme.colors.background)) {
PreviewMessageDetails()
}
}
}
@Composable
fun Theme(@StyleRes themeResId: Int, content: @Composable () -> Unit) {
CompositionLocalProvider(
LocalContext provides ContextThemeWrapper(LocalContext.current, themeResId)
) {
AppTheme {
content()
}
}
}
class ThemeResPreviewParameterProvider : PreviewParameterProvider<Int> {
override val values = sequenceOf(
R.style.Classic_Dark,
R.style.Classic_Light,
R.style.Ocean_Dark,
R.style.Ocean_Light,
)
}

View File

@ -4,9 +4,12 @@
<string name="yes">Yes</string> <string name="yes">Yes</string>
<string name="no">No</string> <string name="no">No</string>
<string name="delete">Delete</string> <string name="delete">Delete</string>
<string name="resend">Resend</string>
<string name="reply">Reply</string>
<string name="ban">Ban</string> <string name="ban">Ban</string>
<string name="please_wait">Please wait…</string> <string name="please_wait">Please wait…</string>
<string name="save">Save</string> <string name="save">Save</string>
<string name="image">Image</string>
<string name="note_to_self">Note to Self</string> <string name="note_to_self">Note to Self</string>
<string name="version_s">Version %s</string> <string name="version_s">Version %s</string>
<!--Accessibility ID's--> <!--Accessibility ID's-->
@ -516,6 +519,7 @@
<string name="message_details_header__to">To:</string> <string name="message_details_header__to">To:</string>
<string name="message_details_header__from">From:</string> <string name="message_details_header__from">From:</string>
<string name="message_details_header__with">With:</string> <string name="message_details_header__with">With:</string>
<!-- AndroidManifest.xml --> <!-- AndroidManifest.xml -->
<string name="AndroidManifest__create_passphrase">Create passphrase</string> <string name="AndroidManifest__create_passphrase">Create passphrase</string>
<string name="AndroidManifest__select_contacts">Select contacts</string> <string name="AndroidManifest__select_contacts">Select contacts</string>

View File

@ -490,7 +490,6 @@
<item name="android:textColor">?android:textColorPrimary</item> <item name="android:textColor">?android:textColorPrimary</item>
<item name="android:textColorHint">@color/ocean_dark_5</item> <item name="android:textColorHint">@color/ocean_dark_5</item>
<item name="android:windowBackground">?colorPrimary</item> <item name="android:windowBackground">?colorPrimary</item>
<item name="android:colorBackground">@color/default_background_start</item>
<item name="android:navigationBarColor">@color/navigation_bar</item> <item name="android:navigationBarColor">@color/navigation_bar</item>
<item name="default_background_end">?colorPrimary</item> <item name="default_background_end">?colorPrimary</item>
<item name="default_background_start">?colorPrimaryDark</item> <item name="default_background_start">?colorPrimaryDark</item>
@ -575,7 +574,6 @@
<item name="android:textColorHint">@color/ocean_light_6</item> <item name="android:textColorHint">@color/ocean_light_6</item>
<item name="android:navigationBarColor">@color/ocean_light_navigation_bar</item> <item name="android:navigationBarColor">@color/ocean_light_navigation_bar</item>
<item name="android:windowBackground">?colorPrimary</item> <item name="android:windowBackground">?colorPrimary</item>
<item name="android:colorBackground">@color/default_background_start</item>
<item name="default_background_end">@color/ocean_light_7</item> <item name="default_background_end">@color/ocean_light_7</item>
<item name="default_background_start">@color/ocean_light_6</item> <item name="default_background_start">@color/ocean_light_6</item>
<item name="colorCellBackground">@color/ocean_light_5</item> <item name="colorCellBackground">@color/ocean_light_5</item>