Repo created
This commit is contained in:
parent
75dc487a7a
commit
39c29d175b
6317 changed files with 388324 additions and 2 deletions
23
feature/widget/message-list-glance/build.gradle.kts
Normal file
23
feature/widget/message-list-glance/build.gradle.kts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
plugins {
|
||||
id(ThunderbirdPlugins.Library.android)
|
||||
alias(libs.plugins.compose)
|
||||
}
|
||||
|
||||
android {
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
namespace = "net.thunderbird.feature.widget.message.list"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.legacy.ui.legacy)
|
||||
implementation(projects.legacy.core)
|
||||
|
||||
implementation(libs.androidx.glance.appwidget)
|
||||
implementation(libs.androidx.glance.material3)
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
|
||||
debugImplementation(libs.androidx.glance.appwidget.preview)
|
||||
debugImplementation(libs.androidx.glance.preview)
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package net.thunderbird.feature.widget.message.list.ui
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.glance.preview.ExperimentalGlancePreviewApi
|
||||
import androidx.glance.preview.Preview
|
||||
import app.k9mail.legacy.message.controller.MessageReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import net.thunderbird.feature.widget.message.list.MessageListItem
|
||||
|
||||
@OptIn(ExperimentalGlancePreviewApi::class)
|
||||
@Preview(widthDp = 250, heightDp = 180)
|
||||
@Composable
|
||||
internal fun MessageListWidgetContentPreview() {
|
||||
MessageListWidgetContent(
|
||||
mails = generateMessageListItems(),
|
||||
onOpenApp = {},
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalGlancePreviewApi::class)
|
||||
@Preview(widthDp = 250, heightDp = 180)
|
||||
@Composable
|
||||
internal fun MessageListWidgetContentEmptyPreview() {
|
||||
MessageListWidgetContent(
|
||||
mails = persistentListOf(),
|
||||
onOpenApp = {},
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateMessageListItems(): ImmutableList<MessageListItem> {
|
||||
return persistentListOf(
|
||||
generateMessageListItem(
|
||||
displayName = "Alice",
|
||||
displayDate = "1 Jan",
|
||||
subject = "Subject 1",
|
||||
preview = "Preview 1",
|
||||
color = Color.BLUE,
|
||||
isRead = false,
|
||||
),
|
||||
generateMessageListItem(
|
||||
displayName = "Bob",
|
||||
displayDate = "2 Jan",
|
||||
subject = "Subject 2",
|
||||
preview = "Preview 2",
|
||||
color = Color.RED,
|
||||
isRead = true,
|
||||
),
|
||||
generateMessageListItem(
|
||||
displayName = "Charlie",
|
||||
displayDate = "3 Jan",
|
||||
subject = "Subject 3",
|
||||
preview = "Preview 3",
|
||||
color = Color.RED,
|
||||
isRead = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateMessageListItem(
|
||||
displayName: String,
|
||||
displayDate: String,
|
||||
subject: String,
|
||||
preview: String,
|
||||
color: Int,
|
||||
isRead: Boolean,
|
||||
): MessageListItem {
|
||||
return MessageListItem(
|
||||
displayName = displayName,
|
||||
displayDate = displayDate,
|
||||
subject = subject,
|
||||
preview = preview,
|
||||
isRead = isRead,
|
||||
hasAttachments = false,
|
||||
threadCount = 0,
|
||||
accountColor = color,
|
||||
uniqueId = 0,
|
||||
messageReference = MessageReference("accountUuid", 123, "messageServerId"),
|
||||
sortSubject = subject,
|
||||
sortMessageDate = 0,
|
||||
sortInternalDate = 0,
|
||||
sortIsStarred = false,
|
||||
sortDatabaseId = 0,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package net.thunderbird.feature.widget.message.list
|
||||
|
||||
import org.koin.dsl.module
|
||||
|
||||
val featureWidgetMessageListModule = module {
|
||||
factory {
|
||||
MessageListLoader(
|
||||
preferences = get(),
|
||||
messageListRepository = get(),
|
||||
messageHelper = get(),
|
||||
generalSettingsManager = get(),
|
||||
outboxFolderManager = get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package net.thunderbird.feature.widget.message.list
|
||||
|
||||
import net.thunderbird.core.android.account.SortType
|
||||
import net.thunderbird.feature.search.legacy.LocalMessageSearch
|
||||
|
||||
internal data class MessageListConfig(
|
||||
val search: LocalMessageSearch,
|
||||
val showingThreadedList: Boolean,
|
||||
val sortType: SortType,
|
||||
val sortAscending: Boolean,
|
||||
val sortDateAscending: Boolean,
|
||||
)
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package net.thunderbird.feature.widget.message.list
|
||||
|
||||
import app.k9mail.legacy.message.controller.MessageReference
|
||||
|
||||
internal data class MessageListItem(
|
||||
val displayName: String,
|
||||
val displayDate: String,
|
||||
val subject: String,
|
||||
val preview: String,
|
||||
val isRead: Boolean,
|
||||
val hasAttachments: Boolean,
|
||||
val threadCount: Int,
|
||||
val accountColor: Int,
|
||||
val messageReference: MessageReference,
|
||||
val uniqueId: Long,
|
||||
|
||||
val sortSubject: String?,
|
||||
val sortMessageDate: Long,
|
||||
val sortInternalDate: Long,
|
||||
val sortIsStarred: Boolean,
|
||||
val sortDatabaseId: Long,
|
||||
)
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package net.thunderbird.feature.widget.message.list
|
||||
|
||||
import app.k9mail.legacy.mailstore.MessageDetailsAccessor
|
||||
import app.k9mail.legacy.mailstore.MessageMapper
|
||||
import app.k9mail.legacy.message.controller.MessageReference
|
||||
import com.fsck.k9.helper.MessageHelper
|
||||
import com.fsck.k9.ui.helper.DisplayAddressHelper
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.core.preference.GeneralSettingsManager
|
||||
import net.thunderbird.feature.mail.folder.api.OutboxFolderManager
|
||||
|
||||
internal class MessageListItemMapper(
|
||||
private val messageHelper: MessageHelper,
|
||||
private val account: LegacyAccount,
|
||||
private val generalSettingsManager: GeneralSettingsManager,
|
||||
private val outboxFolderManager: OutboxFolderManager,
|
||||
) : MessageMapper<MessageListItem> {
|
||||
private val calendar: Calendar = Calendar.getInstance()
|
||||
|
||||
override fun map(message: MessageDetailsAccessor): MessageListItem {
|
||||
val fromAddresses = message.fromAddresses
|
||||
val toAddresses = message.toAddresses
|
||||
val previewResult = message.preview
|
||||
val previewText = if (previewResult.isPreviewTextAvailable) previewResult.previewText else ""
|
||||
val uniqueId = createUniqueId(account, message.id)
|
||||
val showRecipients = DisplayAddressHelper.shouldShowRecipients(outboxFolderManager, account, message.folderId)
|
||||
val displayAddress = if (showRecipients) toAddresses.firstOrNull() else fromAddresses.firstOrNull()
|
||||
val displayName = if (showRecipients) {
|
||||
messageHelper.getRecipientDisplayNames(
|
||||
addresses = toAddresses.toTypedArray(),
|
||||
isShowCorrespondentNames = generalSettingsManager.getConfig().display.isShowCorrespondentNames,
|
||||
isChangeContactNameColor = generalSettingsManager.getConfig().display.isChangeContactNameColor,
|
||||
).toString()
|
||||
} else {
|
||||
messageHelper.getSenderDisplayName(displayAddress).toString()
|
||||
}
|
||||
|
||||
return MessageListItem(
|
||||
displayName = displayName,
|
||||
displayDate = formatDate(message.messageDate),
|
||||
subject = message.subject.orEmpty(),
|
||||
preview = previewText,
|
||||
isRead = message.isRead,
|
||||
hasAttachments = message.hasAttachments,
|
||||
threadCount = message.threadCount,
|
||||
accountColor = account.chipColor,
|
||||
messageReference = MessageReference(account.uuid, message.folderId, message.messageServerId),
|
||||
uniqueId = uniqueId,
|
||||
sortSubject = message.subject,
|
||||
sortMessageDate = message.messageDate,
|
||||
sortInternalDate = message.internalDate,
|
||||
sortIsStarred = message.isStarred,
|
||||
sortDatabaseId = message.id,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("ImplicitDefaultLocale")
|
||||
private fun formatDate(date: Long): String {
|
||||
calendar.timeInMillis = date
|
||||
val dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH)
|
||||
val month = calendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault())
|
||||
|
||||
return String.format("%d %s", dayOfMonth, month)
|
||||
}
|
||||
|
||||
private fun createUniqueId(account: LegacyAccount, messageId: Long): Long {
|
||||
return ((account.accountNumber + 1).toLong() shl ACCOUNT_NUMBER_BIT_SHIFT) + messageId
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val ACCOUNT_NUMBER_BIT_SHIFT = 52
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
package net.thunderbird.feature.widget.message.list
|
||||
|
||||
import app.k9mail.legacy.mailstore.MessageListRepository
|
||||
import com.fsck.k9.Preferences
|
||||
import com.fsck.k9.helper.MessageHelper
|
||||
import com.fsck.k9.mailstore.MessageColumns
|
||||
import com.fsck.k9.search.getAccounts
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.core.android.account.SortType
|
||||
import net.thunderbird.core.logging.legacy.Log
|
||||
import net.thunderbird.core.preference.GeneralSettingsManager
|
||||
import net.thunderbird.feature.mail.folder.api.OutboxFolderManager
|
||||
import net.thunderbird.feature.search.legacy.sql.SqlWhereClause
|
||||
|
||||
internal class MessageListLoader(
|
||||
private val preferences: Preferences,
|
||||
private val messageListRepository: MessageListRepository,
|
||||
private val messageHelper: MessageHelper,
|
||||
private val generalSettingsManager: GeneralSettingsManager,
|
||||
private val outboxFolderManager: OutboxFolderManager,
|
||||
) {
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
fun getMessageList(config: MessageListConfig): List<MessageListItem> {
|
||||
return try {
|
||||
getMessageListInfo(config)
|
||||
} catch (e: Exception) {
|
||||
Log.e(e, "Error while fetching message list")
|
||||
|
||||
// TODO: Return an error object instead of an empty list
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMessageListInfo(config: MessageListConfig): List<MessageListItem> {
|
||||
val accounts = config.search.getAccounts(preferences)
|
||||
val messageListItems = accounts
|
||||
.flatMap { account ->
|
||||
loadMessageListForAccount(account, config)
|
||||
}
|
||||
.sortedWith(config)
|
||||
|
||||
return messageListItems
|
||||
}
|
||||
|
||||
private fun loadMessageListForAccount(account: LegacyAccount, config: MessageListConfig): List<MessageListItem> {
|
||||
val accountUuid = account.uuid
|
||||
val sortOrder = buildSortOrder(config)
|
||||
val mapper = MessageListItemMapper(messageHelper, account, generalSettingsManager, outboxFolderManager)
|
||||
|
||||
return if (config.showingThreadedList) {
|
||||
val (selection, selectionArgs) = buildSelection(config)
|
||||
messageListRepository.getThreadedMessages(accountUuid, selection, selectionArgs, sortOrder, mapper)
|
||||
} else {
|
||||
val (selection, selectionArgs) = buildSelection(config)
|
||||
messageListRepository.getMessages(accountUuid, selection, selectionArgs, sortOrder, mapper)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildSelection(config: MessageListConfig): Pair<String, Array<String>> {
|
||||
val whereClause = SqlWhereClause.Builder()
|
||||
.withConditions(config.search.conditions)
|
||||
.build()
|
||||
|
||||
return whereClause.selection to whereClause.selectionArgs.toTypedArray()
|
||||
}
|
||||
|
||||
private fun buildSortOrder(config: MessageListConfig): String {
|
||||
val sortColumn = when (config.sortType) {
|
||||
SortType.SORT_ARRIVAL -> MessageColumns.INTERNAL_DATE
|
||||
SortType.SORT_ATTACHMENT -> "(${MessageColumns.ATTACHMENT_COUNT} < 1)"
|
||||
SortType.SORT_FLAGGED -> "(${MessageColumns.FLAGGED} != 1)"
|
||||
SortType.SORT_SENDER -> MessageColumns.SENDER_LIST // FIXME
|
||||
SortType.SORT_SUBJECT -> "${MessageColumns.SUBJECT} COLLATE NOCASE"
|
||||
SortType.SORT_UNREAD -> MessageColumns.READ
|
||||
SortType.SORT_DATE -> MessageColumns.DATE
|
||||
}
|
||||
|
||||
val sortDirection = if (config.sortAscending) " ASC" else " DESC"
|
||||
val secondarySort = if (config.sortType == SortType.SORT_DATE || config.sortType == SortType.SORT_ARRIVAL) {
|
||||
""
|
||||
} else {
|
||||
if (config.sortDateAscending) {
|
||||
"${MessageColumns.DATE} ASC, "
|
||||
} else {
|
||||
"${MessageColumns.DATE} DESC, "
|
||||
}
|
||||
}
|
||||
|
||||
return "$sortColumn$sortDirection, $secondarySort${MessageColumns.ID} DESC"
|
||||
}
|
||||
|
||||
private fun List<MessageListItem>.sortedWith(config: MessageListConfig): List<MessageListItem> {
|
||||
val comparator = when (config.sortType) {
|
||||
SortType.SORT_DATE -> {
|
||||
compareBy(config.sortAscending) { it.sortMessageDate }
|
||||
}
|
||||
|
||||
SortType.SORT_ARRIVAL -> {
|
||||
compareBy(config.sortAscending) { it.sortInternalDate }
|
||||
}
|
||||
|
||||
SortType.SORT_SUBJECT -> {
|
||||
compareStringBy<MessageListItem>(config.sortAscending) { it.sortSubject.orEmpty() }
|
||||
.thenByDate(config)
|
||||
}
|
||||
|
||||
SortType.SORT_SENDER -> {
|
||||
compareStringBy<MessageListItem>(config.sortAscending) { it.displayName }
|
||||
.thenByDate(config)
|
||||
}
|
||||
|
||||
SortType.SORT_UNREAD -> {
|
||||
compareBy<MessageListItem>(config.sortAscending) { it.isRead }
|
||||
.thenByDate(config)
|
||||
}
|
||||
|
||||
SortType.SORT_FLAGGED -> {
|
||||
compareBy<MessageListItem>(!config.sortAscending) { it.sortIsStarred }
|
||||
.thenByDate(config)
|
||||
}
|
||||
|
||||
SortType.SORT_ATTACHMENT -> {
|
||||
compareBy<MessageListItem>(!config.sortAscending) { it.hasAttachments }
|
||||
.thenByDate(config)
|
||||
}
|
||||
}.thenByDescending { it.sortDatabaseId }
|
||||
|
||||
return this.sortedWith(comparator)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> compareBy(sortAscending: Boolean, crossinline selector: (T) -> Comparable<*>?): Comparator<T> {
|
||||
return if (sortAscending) {
|
||||
compareBy(selector)
|
||||
} else {
|
||||
compareByDescending(selector)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> compareStringBy(sortAscending: Boolean, crossinline selector: (T) -> String): Comparator<T> {
|
||||
return if (sortAscending) {
|
||||
compareBy(String.CASE_INSENSITIVE_ORDER, selector)
|
||||
} else {
|
||||
compareByDescending(String.CASE_INSENSITIVE_ORDER, selector)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Comparator<MessageListItem>.thenByDate(config: MessageListConfig): Comparator<MessageListItem> {
|
||||
return if (config.sortDateAscending) {
|
||||
thenBy { it.sortMessageDate }
|
||||
} else {
|
||||
thenByDescending { it.sortMessageDate }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package net.thunderbird.feature.widget.message.list
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.core.app.PendingIntentCompat
|
||||
import androidx.glance.GlanceId
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import androidx.glance.appwidget.provideContent
|
||||
import com.fsck.k9.CoreResourceProvider
|
||||
import com.fsck.k9.activity.MessageList.Companion.intentDisplaySearch
|
||||
import kotlin.random.Random.Default.nextInt
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import net.thunderbird.core.android.account.SortType
|
||||
import net.thunderbird.core.preference.GeneralSettingsManager
|
||||
import net.thunderbird.feature.search.legacy.SearchAccount.Companion.createUnifiedInboxAccount
|
||||
import net.thunderbird.feature.widget.message.list.ui.MessageListWidgetContent
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
internal class MessageListWidget : GlanceAppWidget(), KoinComponent {
|
||||
|
||||
private val messageListLoader: MessageListLoader by inject()
|
||||
private val coreResourceProvider: CoreResourceProvider by inject()
|
||||
private val generalSettingsManager: GeneralSettingsManager by inject()
|
||||
|
||||
companion object {
|
||||
private var lastMailList = emptyList<MessageListItem>()
|
||||
private const val MESSAGE_COUNT = 100
|
||||
}
|
||||
|
||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
||||
provideContent {
|
||||
var mails by remember { mutableStateOf(lastMailList) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val unifiedInboxSearch = createUnifiedInboxAccount(
|
||||
unifiedInboxTitle = coreResourceProvider.searchUnifiedInboxTitle(),
|
||||
unifiedInboxDetail = coreResourceProvider.searchUnifiedInboxDetail(),
|
||||
).relatedSearch
|
||||
val messageListConfig = MessageListConfig(
|
||||
search = unifiedInboxSearch,
|
||||
showingThreadedList = generalSettingsManager.getConfig()
|
||||
.display.inboxSettings.isThreadedViewEnabled,
|
||||
sortType = SortType.SORT_DATE,
|
||||
sortAscending = false,
|
||||
sortDateAscending = false,
|
||||
)
|
||||
val list = messageListLoader.getMessageList(messageListConfig)
|
||||
mails = list.subList(0, list.size.coerceAtMost(MESSAGE_COUNT))
|
||||
lastMailList = mails
|
||||
}
|
||||
}
|
||||
|
||||
MessageListWidgetContent(
|
||||
mails = mails.toImmutableList(),
|
||||
onOpenApp = { openApp(context) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openApp(context: Context) {
|
||||
val unifiedInboxAccount = createUnifiedInboxAccount(
|
||||
unifiedInboxTitle = coreResourceProvider.searchUnifiedInboxTitle(),
|
||||
unifiedInboxDetail = coreResourceProvider.searchUnifiedInboxDetail(),
|
||||
)
|
||||
val intent = intentDisplaySearch(
|
||||
context = context,
|
||||
search = unifiedInboxAccount.relatedSearch,
|
||||
noThreading = true,
|
||||
newTask = true,
|
||||
clearTop = true,
|
||||
).apply {
|
||||
action = nextInt().toString()
|
||||
}
|
||||
PendingIntentCompat.getActivity(
|
||||
context,
|
||||
nextInt(),
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT,
|
||||
false,
|
||||
)!!.send()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package net.thunderbird.feature.widget.message.list
|
||||
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import androidx.glance.appwidget.GlanceAppWidgetReceiver
|
||||
|
||||
class MessageListWidgetReceiver : GlanceAppWidgetReceiver() {
|
||||
override val glanceAppWidget: GlanceAppWidget = MessageListWidget()
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
package net.thunderbird.feature.widget.message.list.ui
|
||||
|
||||
import android.app.PendingIntent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.app.PendingIntentCompat
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.GlanceTheme
|
||||
import androidx.glance.LocalContext
|
||||
import androidx.glance.action.clickable
|
||||
import androidx.glance.appwidget.cornerRadius
|
||||
import androidx.glance.background
|
||||
import androidx.glance.layout.Alignment
|
||||
import androidx.glance.layout.Box
|
||||
import androidx.glance.layout.Column
|
||||
import androidx.glance.layout.Row
|
||||
import androidx.glance.layout.Spacer
|
||||
import androidx.glance.layout.fillMaxWidth
|
||||
import androidx.glance.layout.height
|
||||
import androidx.glance.layout.padding
|
||||
import androidx.glance.layout.width
|
||||
import androidx.glance.layout.wrapContentHeight
|
||||
import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextStyle
|
||||
import com.fsck.k9.activity.MessageList
|
||||
import kotlin.random.Random
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import net.thunderbird.feature.widget.message.list.MessageListItem
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
internal fun MessageListItemView(item: MessageListItem) {
|
||||
val context = LocalContext.current
|
||||
Row(
|
||||
GlanceModifier.Companion.fillMaxWidth().wrapContentHeight().clickable {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val intent = MessageList.Companion.actionDisplayMessageIntent(context, item.messageReference)
|
||||
PendingIntentCompat.getActivity(
|
||||
context,
|
||||
Random.Default.nextInt(),
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT,
|
||||
false,
|
||||
)!!
|
||||
.send()
|
||||
}
|
||||
},
|
||||
) {
|
||||
Spacer(GlanceModifier.Companion.width(8.dp).background(Color(item.accountColor)))
|
||||
Column(GlanceModifier.Companion.fillMaxWidth().padding(vertical = 4.dp, horizontal = 4.dp)) {
|
||||
Row(GlanceModifier.Companion.fillMaxWidth()) {
|
||||
Row(GlanceModifier.Companion.defaultWeight(), horizontalAlignment = Alignment.Companion.Start) {
|
||||
Text(
|
||||
item.subject,
|
||||
style = TextStyle(color = GlanceTheme.colors.primary, fontSize = 16.sp),
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
Spacer(GlanceModifier.Companion.width(4.dp))
|
||||
Row(horizontalAlignment = Alignment.Companion.End) {
|
||||
Box(
|
||||
GlanceModifier.Companion.background(GlanceTheme.colors.primaryContainer).cornerRadius(8.dp)
|
||||
.padding(2.dp),
|
||||
) {
|
||||
Text(
|
||||
item.threadCount.toString(),
|
||||
style = TextStyle(color = GlanceTheme.colors.primary, fontSize = 13.sp),
|
||||
)
|
||||
}
|
||||
Spacer(GlanceModifier.Companion.width(4.dp))
|
||||
Text(item.displayDate, style = TextStyle(color = GlanceTheme.colors.primary))
|
||||
}
|
||||
}
|
||||
Spacer(GlanceModifier.Companion.height(2.dp))
|
||||
Row {
|
||||
Text(
|
||||
item.displayName,
|
||||
style = TextStyle(color = GlanceTheme.colors.primary, fontSize = 15.sp),
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
Spacer(GlanceModifier.Companion.height(2.dp))
|
||||
Row {
|
||||
Text(
|
||||
item.preview,
|
||||
style = TextStyle(color = GlanceTheme.colors.primary, fontSize = 13.sp),
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package net.thunderbird.feature.widget.message.list.ui
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.app.PendingIntentCompat
|
||||
import androidx.glance.ColorFilter
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.GlanceTheme
|
||||
import androidx.glance.Image
|
||||
import androidx.glance.ImageProvider
|
||||
import androidx.glance.LocalContext
|
||||
import androidx.glance.action.clickable
|
||||
import androidx.glance.appwidget.lazy.LazyColumn
|
||||
import androidx.glance.appwidget.lazy.items
|
||||
import androidx.glance.background
|
||||
import androidx.glance.layout.Column
|
||||
import androidx.glance.layout.Row
|
||||
import androidx.glance.layout.Spacer
|
||||
import androidx.glance.layout.fillMaxSize
|
||||
import androidx.glance.layout.fillMaxWidth
|
||||
import androidx.glance.layout.height
|
||||
import androidx.glance.layout.padding
|
||||
import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextStyle
|
||||
import app.k9mail.core.ui.legacy.designsystem.atom.icon.Icons
|
||||
import com.fsck.k9.activity.MessageCompose
|
||||
import kotlin.random.Random.Default.nextInt
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import net.thunderbird.feature.widget.message.list.MessageListItem
|
||||
import net.thunderbird.feature.widget.message.list.R
|
||||
|
||||
@Composable
|
||||
internal fun MessageListWidgetContent(
|
||||
mails: ImmutableList<MessageListItem>,
|
||||
onOpenApp: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
GlanceTheme(GlanceTheme.colors) {
|
||||
Column(GlanceModifier.fillMaxSize().background(GlanceTheme.colors.surface)) {
|
||||
Row(
|
||||
GlanceModifier.padding(horizontal = 8.dp, vertical = 12.dp).fillMaxWidth()
|
||||
.background(GlanceTheme.colors.primaryContainer)
|
||||
.clickable {
|
||||
onOpenApp()
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
context.getString(R.string.message_list_glance_widget_inbox_title),
|
||||
style = TextStyle(color = GlanceTheme.colors.primary, fontSize = 20.sp),
|
||||
)
|
||||
Spacer(GlanceModifier.defaultWeight())
|
||||
Image(
|
||||
ImageProvider(Icons.Outlined.Edit),
|
||||
context.getString(R.string.message_list_glance_widget_compose_action),
|
||||
GlanceModifier.padding(2.dp).padding(end = 6.dp).clickable {
|
||||
val intent = Intent(context, MessageCompose::class.java).apply {
|
||||
action = MessageCompose.ACTION_COMPOSE
|
||||
}
|
||||
PendingIntentCompat.getActivity(
|
||||
context,
|
||||
nextInt(),
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT,
|
||||
false,
|
||||
)!!.send()
|
||||
},
|
||||
colorFilter = ColorFilter.tint(GlanceTheme.colors.primary),
|
||||
)
|
||||
}
|
||||
LazyColumn(GlanceModifier.fillMaxSize()) {
|
||||
items(mails) {
|
||||
Column {
|
||||
MessageListItemView(it)
|
||||
Spacer(
|
||||
GlanceModifier.height(2.dp).fillMaxWidth()
|
||||
.background(GlanceTheme.colors.surfaceVariant),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="message_list_glance_widget_compose_action">Compose</string>
|
||||
<string name="message_list_glance_widget_inbox_title">Unified Inbox</string>
|
||||
<string name="message_list_glance_widget_label">Message List</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialLayout="@layout/glance_default_loading_layout"
|
||||
android:minHeight="180dp"
|
||||
android:minWidth="250dp"
|
||||
android:minResizeWidth="110dp"
|
||||
android:minResizeHeight="110dp"
|
||||
android:previewImage="@drawable/message_list_glance_widget_preview"
|
||||
android:resizeMode="horizontal|vertical"
|
||||
android:updatePeriodMillis="86400000"
|
||||
android:widgetCategory="home_screen|keyguard"
|
||||
/>
|
||||
Loading…
Add table
Add a link
Reference in a new issue