mirror of
https://github.com/oxen-io/session-android.git
synced 2025-03-08 11:29:25 +00:00
Fix theming and polish
This commit is contained in:
parent
bbc9cdfeeb
commit
0824713ac5
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user