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.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
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.PagerState
import androidx.compose.foundation.pager.rememberPagerState
@ -31,9 +34,11 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
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.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.database.Storage
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.mms.ImageSlide
import org.thoughtcrime.securesms.mms.Slide
import org.thoughtcrime.securesms.ui.AppTheme
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.HorizontalPagerIndicator
import org.thoughtcrime.securesms.ui.ItemButton
import org.thoughtcrime.securesms.ui.Theme
import org.thoughtcrime.securesms.ui.blackAlpha40
import org.thoughtcrime.securesms.ui.colorDestructive
import org.thoughtcrime.securesms.ui.destructiveButtonColors
@ -118,27 +124,32 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
@Composable
private fun MessageDetailsScreen() {
val details by viewModel.details.observeAsState(MessageDetails())
MessageDetails(
details,
onReply = { setResultAndFinish(ON_REPLY) },
onResend = { setResultAndFinish(ON_RESEND) },
onDelete = { setResultAndFinish(ON_DELETE) },
onClickImage = { slide ->
// only open to downloaded images
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())
val threadDb = DatabaseComponent.get(this@MessageDetailActivity).threadDatabase()
AppTheme {
MessageDetails(
threadDb = threadDb,
messageDetails = details,
onReply = { setResultAndFinish(ON_REPLY) },
onResend = { setResultAndFinish(ON_RESEND) },
onDelete = { setResultAndFinish(ON_DELETE) },
onClickImage = { slide ->
// only open to downloaded images
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(
this,
slide,
details.mmsRecord,
DatabaseComponent.get(this).threadDatabase().getRecipientForThreadId(details.mmsRecord!!.threadId),
).let(::startActivity)
}
)
if (!slide.isInProgress) MediaPreviewActivity.getPreviewIntent(
this,
slide,
details.mmsRecord,
threadDb.getRecipientForThreadId(details.mmsRecord!!.threadId),
).let(::startActivity)
},
onAttachmentNeedsDownload = ::onAttachmentNeedsDownload,
)
}
}
private fun setResultAndFinish(code: Int) {
@ -149,254 +160,276 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
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) {
lifecycleScope.launch(Dispatchers.IO) {
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 ->
if (event.actionMasked == ACTION_UP) onContentClick(event)
true
}
}
}
)
}
Attachments(messageDetails.attachments) { onClickImage(it) }
MetadataCell(messageDetails)
Buttons(
messageDetails.error != null,
onReply,
onResend,
onDelete,
)
}
}
@Composable
fun PreviewMessageDetails() {
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"),
error = TitledText("Error:", "Message failed to send"),
senderInfo = TitledText("Connor", "d4f1g54sdf5g1d5f4g65ds4564df65f4g65d54"),
)
)
}
}
@Composable
fun MetadataCell(
messageDetails: MessageDetails,
@SuppressLint("ClickableViewAccessibility")
@Composable
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 {
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("From:") {
Row {
sender?.let { Avatar(it) }
TitledMonospaceText(it)
}
messageDetails.record?.let { message ->
AndroidView(
modifier = Modifier.padding(horizontal = 32.dp),
factory = {
ViewVisibleMessageContentBinding.inflate(LayoutInflater.from(it)).mainContainerConstraint.apply {
bind(
message,
thread = threadDb?.getRecipientForThreadId(message.threadId)!!,
onAttachmentNeedsDownload = onAttachmentNeedsDownload,
suppressThumbnails = true
)
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
fun Buttons(
hasError: Boolean,
onReply: () -> Unit = {},
onResend: () -> Unit = {},
onDelete: () -> Unit = {},
) {
Cell {
Column {
@Composable
fun Buttons(
onReply: () -> Unit = {},
onResend: (() -> Unit)? = null,
onDelete: () -> Unit = {},
) {
Cell {
Column {
ItemButton(
stringResource(id = R.string.reply),
R.drawable.ic_message_details__reply,
onClick = onReply
)
Divider()
onResend?.let {
ItemButton(
"Reply",
R.drawable.ic_message_details__reply,
onClick = onReply
stringResource(id = R.string.resend),
R.drawable.ic_message_details__refresh,
onClick = it
)
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
)
}
}
}
@Composable
fun Attachments(attachments: List<Attachment>, onClick: (Slide) -> Unit) {
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() }
ItemButton(
stringResource(id = R.string.delete),
R.drawable.ic_message_details__trash,
colors = destructiveButtonColors(),
onClick = onDelete
)
}
}
}
@OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class)
@Composable
fun FileDetails(attachments: List<Attachment>, pagerState: PagerState) {
attachments[pagerState.currentPage].fileDetails.takeIf { it.isNotEmpty() }?.let {
CellWithPaddingAndMargin {
FlowRow(
verticalArrangement = Arrangement.spacedBy(16.dp),
maxItemsInEachRow = 2
) {
it.forEach { TitledText(it, Modifier.weight(1f)) }
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Carousel(attachments: List<Attachment>, onClick: (Slide) -> Unit) {
val imageAttachments = attachments.filter { it.hasImage() }.takeIf { it.isNotEmpty() } ?: return
val pagerState = rememberPagerState { imageAttachments.size }
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
Row {
CarouselPrevButton(pagerState)
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
fun TitledErrorText(titledText: TitledText, modifier: Modifier = Modifier) {
TitledText(
titledText,
modifier = modifier,
valueStyle = LocalTextStyle.current.copy(color = colorDestructive)
)
}
@Composable
fun TitledErrorText(titledText: TitledText, modifier: Modifier = Modifier) {
TitledText(
titledText,
modifier = modifier,
valueStyle = LocalTextStyle.current.copy(color = colorDestructive)
)
}
@Composable
fun TitledMonospaceText(titledText: TitledText, modifier: Modifier = Modifier) {
TitledText(
titledText,
modifier = modifier,
valueStyle = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace)
)
}
@Composable
fun TitledMonospaceText(titledText: TitledText, modifier: Modifier = Modifier) {
TitledText(
titledText,
modifier = modifier,
valueStyle = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace)
)
}
@Composable
fun TitledText(
titledText: TitledText,
modifier: Modifier = Modifier,
valueStyle: TextStyle = LocalTextStyle.current
) {
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
Title(titledText.title)
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 TitledText(
titledText: TitledText,
modifier: Modifier = Modifier,
valueStyle: TextStyle = LocalTextStyle.current
) {
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
Title(titledText.title)
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, modifier: Modifier = Modifier) {
Text(text, modifier = modifier, fontWeight = FontWeight.Bold)
}

View File

@ -1,27 +1,60 @@
package org.thoughtcrime.securesms.ui
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Colors
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
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)
val classicLight2 = Color(0xffA1A2A1)
val classicLight3 = Color(0xffDFDFDF)
val classicLight4 = Color(0xffF0F0F0)
val classicLight5 = Color(0xffF9F9F9)
val classicLight6 = Color(0xffFFFFFF)
const val classicLight0 = 0xff000000
const val classicLight1 = 0xff6D6D6D
const val classicLight2 = 0xffA1A2A1
const val classicLight3 = 0xffDFDFDF
const val classicLight4 = 0xffF0F0F0
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)

View File

@ -1,32 +1,50 @@
package org.thoughtcrime.securesms.ui
import androidx.annotation.DrawableRes
import androidx.appcompat.view.ContextThemeWrapper
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.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.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
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.shape.RoundedCornerShape
import androidx.compose.material.ButtonColors
import androidx.compose.material.Card
import androidx.compose.material.Colors
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
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.unit.Dp
import androidx.compose.ui.unit.dp
@ -36,6 +54,13 @@ import kotlinx.coroutines.launch
import network.loki.messenger.R
import org.session.libsession.utilities.recipients.Recipient
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
fun ItemButton(
@ -80,17 +105,18 @@ fun CellWithPaddingAndMargin(
margin: Dp = 32.dp,
content: @Composable () -> Unit
) {
Card(
shape = RoundedCornerShape(16.dp),
elevation = 0.dp,
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
.padding(horizontal = margin),
backgroundColor = LocalExtraColors.current.settingsBackground,
// probably wrong
contentColor = MaterialTheme.colors.onSurface
) { Box(Modifier.padding(padding)) { content() } }
MaterialTheme(colors = MaterialTheme.colors.cellColors) {
Card(
shape = RoundedCornerShape(16.dp),
elevation = 0.dp,
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
.padding(horizontal = margin),
) {
Box(Modifier.padding(padding)) { content() }
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@ -108,7 +134,7 @@ fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) {
pagerState = pagerState,
pageCount = pagerState.pageCount,
activeColor = Color.White,
inactiveColor = classicDark5)
inactiveColor = classicDarkColors[5])
}
}
}
@ -154,7 +180,6 @@ fun RowScope.CarouselButton(
fun Divider() {
androidx.compose.material.Divider(
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 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.CompositionLocalProvider
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
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.android.material.color.MaterialColors
import network.loki.messenger.R
import org.thoughtcrime.securesms.conversation.v2.PreviewMessageDetails
val LocalExtraColors = staticCompositionLocalOf<ExtraColors> { error("No Custom Attribute value provided") }
data class ExtraColors(
val cell: Color,
val divider: 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)
} catch (e: Exception) {
colorDestructive
}
@Composable
fun AppTheme(
@ -28,9 +39,7 @@ fun AppTheme(
) {
val extraColors = LocalContext.current.run {
ExtraColors(
cell = getColorFromTheme(R.attr.colorCellBackground),
divider = getColorFromTheme(R.attr.dividerHorizontal).copy(alpha = 0.15f),
settingsBackground = getColorFromTheme(R.attr.colorSettingsBackground)
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="no">No</string>
<string name="delete">Delete</string>
<string name="resend">Resend</string>
<string name="reply">Reply</string>
<string name="ban">Ban</string>
<string name="please_wait">Please wait…</string>
<string name="save">Save</string>
<string name="image">Image</string>
<string name="note_to_self">Note to Self</string>
<string name="version_s">Version %s</string>
<!--Accessibility ID's-->
@ -516,6 +519,7 @@
<string name="message_details_header__to">To:</string>
<string name="message_details_header__from">From:</string>
<string name="message_details_header__with">With:</string>
<!-- AndroidManifest.xml -->
<string name="AndroidManifest__create_passphrase">Create passphrase</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:textColorHint">@color/ocean_dark_5</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="default_background_end">?colorPrimary</item>
<item name="default_background_start">?colorPrimaryDark</item>
@ -575,7 +574,6 @@
<item name="android:textColorHint">@color/ocean_light_6</item>
<item name="android:navigationBarColor">@color/ocean_light_navigation_bar</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_start">@color/ocean_light_6</item>
<item name="colorCellBackground">@color/ocean_light_5</item>