Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:56:56 +01:00
parent 75dc487a7a
commit 39c29d175b
6317 changed files with 388324 additions and 2 deletions

View 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)
}

View file

@ -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,
)
}

View file

@ -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(),
)
}
}

View file

@ -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,
)

View file

@ -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,
)

View file

@ -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
}
}

View file

@ -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 }
}
}

View file

@ -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()
}
}

View file

@ -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()
}

View file

@ -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,
)
}
}
}
}

View file

@ -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

View file

@ -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>

View file

@ -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"
/>

View file

@ -0,0 +1,12 @@
plugins {
id(ThunderbirdPlugins.Library.android)
}
dependencies {
implementation(projects.legacy.ui.legacy)
implementation(projects.legacy.core)
}
android {
namespace = "app.k9mail.feature.widget.message.list"
}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<service
android:name=".MessageListWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS"
/>
</application>
</manifest>

View file

@ -0,0 +1,86 @@
package app.k9mail.feature.widget.message.list
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.widget.RemoteViews
import androidx.core.app.PendingIntentCompat
import com.fsck.k9.CoreResourceProvider
import com.fsck.k9.activity.MessageCompose
import com.fsck.k9.activity.MessageList
import com.fsck.k9.activity.MessageList.Companion.intentDisplaySearch
import net.thunderbird.feature.search.legacy.SearchAccount.Companion.createUnifiedInboxAccount
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
abstract class BaseMessageListWidgetProvider : AppWidgetProvider(), KoinComponent {
private val messageListWidgetManager: MessageListWidgetManager by inject()
private val coreResourceProvider: CoreResourceProvider by inject()
override fun onEnabled(context: Context) {
messageListWidgetManager.onWidgetAdded()
}
override fun onDisabled(context: Context) {
messageListWidgetManager.onWidgetRemoved()
}
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
}
private fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
val views = RemoteViews(context.packageName, R.layout.message_list_widget_layout)
views.setTextViewText(R.id.folder, context.getString(R.string.message_list_widget_inbox_title))
val intent = Intent(context, MessageListWidgetService::class.java)
views.setRemoteAdapter(R.id.listView, intent)
val viewAction = viewActionTemplatePendingIntent(context)
views.setPendingIntentTemplate(R.id.listView, viewAction)
val composeAction = composeActionPendingIntent(context)
views.setOnClickPendingIntent(R.id.new_message, composeAction)
val headerClickAction = viewUnifiedInboxPendingIntent(context)
views.setOnClickPendingIntent(R.id.top_controls, headerClickAction)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
private fun viewActionTemplatePendingIntent(context: Context): PendingIntent {
val intent = MessageList.actionDisplayMessageTemplateIntent(
context,
openInUnifiedInbox = true,
messageViewOnly = true,
)
return PendingIntentCompat.getActivity(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT, true)!!
}
private fun viewUnifiedInboxPendingIntent(context: Context): PendingIntent {
val unifiedInboxAccount = createUnifiedInboxAccount(
unifiedInboxTitle = coreResourceProvider.searchUnifiedInboxTitle(),
unifiedInboxDetail = coreResourceProvider.searchUnifiedInboxDetail(),
)
val intent = intentDisplaySearch(
context = context,
search = unifiedInboxAccount.relatedSearch,
noThreading = true,
newTask = true,
clearTop = true,
)
return PendingIntentCompat.getActivity(context, -1, intent, PendingIntent.FLAG_UPDATE_CURRENT, false)!!
}
private fun composeActionPendingIntent(context: Context): PendingIntent {
val intent = Intent(context, MessageCompose::class.java).apply {
action = MessageCompose.ACTION_COMPOSE
}
return PendingIntentCompat.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, false)!!
}
}

View file

@ -0,0 +1,16 @@
package app.k9mail.feature.widget.message.list
import org.koin.dsl.module
val messageListWidgetModule = module {
single { MessageListWidgetManager(context = get(), messageListRepository = get(), config = get()) }
factory {
MessageListLoader(
preferences = get(),
messageListRepository = get(),
messageHelper = get(),
generalSettingsManager = get(),
outboxFolderManager = get(),
)
}
}

View file

@ -0,0 +1,12 @@
package app.k9mail.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,
)

View file

@ -0,0 +1,22 @@
package app.k9mail.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,
)

View file

@ -0,0 +1,75 @@
package app.k9mail.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
}
}

View file

@ -0,0 +1,156 @@
package app.k9mail.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
else -> 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 }
}
}

View file

@ -0,0 +1,136 @@
package app.k9mail.feature.widget.message.list
import android.content.Context
import android.graphics.Typeface
import android.text.SpannableString
import android.text.style.StyleSpan
import android.view.View
import android.widget.RemoteViews
import android.widget.RemoteViewsService.RemoteViewsFactory
import androidx.core.content.ContextCompat
import com.fsck.k9.CoreResourceProvider
import com.fsck.k9.activity.MessageList
import net.thunderbird.core.android.account.SortType
import net.thunderbird.core.preference.GeneralSettingsManager
import net.thunderbird.feature.search.legacy.LocalMessageSearch
import net.thunderbird.feature.search.legacy.SearchAccount
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@Suppress("TooManyFunctions")
internal class MessageListRemoteViewFactory(private val context: Context) : RemoteViewsFactory, KoinComponent {
private val messageListLoader: MessageListLoader by inject()
private val coreResourceProvider: CoreResourceProvider by inject()
private val generalSettingsManager: GeneralSettingsManager by inject()
private lateinit var unifiedInboxSearch: LocalMessageSearch
private var messageListItems = emptyList<MessageListItem>()
private var senderAboveSubject = false
private var readTextColor = 0
private var unreadTextColor = 0
override fun onCreate() {
unifiedInboxSearch = SearchAccount.createUnifiedInboxAccount(
unifiedInboxTitle = coreResourceProvider.searchUnifiedInboxTitle(),
unifiedInboxDetail = coreResourceProvider.searchUnifiedInboxDetail(),
).relatedSearch
senderAboveSubject = generalSettingsManager.getConfig().display.inboxSettings.isMessageListSenderAboveSubject
readTextColor = ContextCompat.getColor(context, R.color.message_list_widget_text_read)
unreadTextColor = ContextCompat.getColor(context, R.color.message_list_widget_text_unread)
}
override fun onDataSetChanged() {
loadMessageList()
}
private fun loadMessageList() {
// TODO: Use same sort order that is used for the Unified Inbox inside the app
val messageListConfig = MessageListConfig(
search = unifiedInboxSearch,
showingThreadedList = generalSettingsManager.getConfig()
.display
.inboxSettings
.isThreadedViewEnabled,
sortType = SortType.SORT_DATE,
sortAscending = false,
sortDateAscending = false,
)
messageListItems = messageListLoader.getMessageList(messageListConfig)
}
override fun onDestroy() = Unit
override fun getCount(): Int = messageListItems.size
override fun getViewAt(position: Int): RemoteViews {
val remoteView = RemoteViews(context.packageName, R.layout.message_list_widget_list_item)
val item = messageListItems[position]
val displayName = if (item.isRead) item.displayName else bold(item.displayName)
val subject = if (item.isRead) item.subject else bold(item.subject)
if (senderAboveSubject) {
remoteView.setTextViewText(R.id.sender, displayName)
remoteView.setTextViewText(R.id.mail_subject, subject)
} else {
remoteView.setTextViewText(R.id.sender, subject)
remoteView.setTextViewText(R.id.mail_subject, displayName)
}
remoteView.setTextViewText(R.id.mail_date, item.displayDate)
remoteView.setTextViewText(R.id.mail_preview, item.preview)
if (item.threadCount > 1) {
remoteView.setTextViewText(R.id.thread_count, item.threadCount.toString())
remoteView.setInt(R.id.thread_count, "setVisibility", View.VISIBLE)
} else {
remoteView.setInt(R.id.thread_count, "setVisibility", View.GONE)
}
val textColor = getTextColor(item)
remoteView.setTextColor(R.id.sender, textColor)
remoteView.setTextColor(R.id.mail_subject, textColor)
remoteView.setTextColor(R.id.mail_date, textColor)
remoteView.setTextColor(R.id.mail_preview, textColor)
if (item.hasAttachments) {
remoteView.setInt(R.id.attachment, "setVisibility", View.VISIBLE)
} else {
remoteView.setInt(R.id.attachment, "setVisibility", View.GONE)
}
val intent = MessageList.actionDisplayMessageTemplateFillIntent(item.messageReference)
remoteView.setOnClickFillInIntent(R.id.mail_list_item, intent)
remoteView.setInt(R.id.chip, "setBackgroundColor", item.accountColor)
return remoteView
}
override fun getLoadingView(): RemoteViews {
return RemoteViews(context.packageName, R.layout.message_list_widget_list_item_loading).apply {
// Set the text here instead of in the layout so the app language override is used
setTextViewText(R.id.loadingText, context.getString(R.string.message_list_widget_list_item_loading))
}
}
override fun getViewTypeCount(): Int = 2
override fun getItemId(position: Int): Long = messageListItems[position].uniqueId
override fun hasStableIds(): Boolean = true
private fun bold(text: String): CharSequence {
return SpannableString(text).apply {
setSpan(StyleSpan(Typeface.BOLD), 0, text.length, 0)
}
}
private fun getTextColor(messageListItem: MessageListItem): Int {
return if (messageListItem.isRead) readTextColor else unreadTextColor
}
}

View file

@ -0,0 +1,5 @@
package app.k9mail.feature.widget.message.list
interface MessageListWidgetConfig {
val providerClass: Class<out BaseMessageListWidgetProvider>
}

View file

@ -0,0 +1,110 @@
package app.k9mail.feature.widget.message.list
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import app.k9mail.legacy.mailstore.MessageListChangedListener
import app.k9mail.legacy.mailstore.MessageListRepository
import com.fsck.k9.core.BuildConfig
import net.thunderbird.core.logging.legacy.Log
class MessageListWidgetManager(
private val context: Context,
private val messageListRepository: MessageListRepository,
private val config: MessageListWidgetConfig,
) {
private var appWidgetManager: AppWidgetManager? = null
private var listenerAdded = false
private val listener = MessageListChangedListener {
onMessageListChanged()
}
fun init() {
appWidgetManager = AppWidgetManager.getInstance(context)
if (appWidgetManager == null) {
Log.v("Message list widget is not supported on this device.")
}
if (isAtLeastOneMessageListWidgetAdded()) {
resetMessageListWidget()
registerMessageListChangedListener()
}
}
@Suppress("TooGenericExceptionCaught")
private fun onMessageListChanged() {
try {
triggerMessageListWidgetUpdate()
} catch (e: RuntimeException) {
if (BuildConfig.DEBUG) {
throw e
} else {
Log.e(e, "Error while updating message list widget")
}
}
}
internal fun onWidgetAdded() {
Log.v("Message list widget added")
registerMessageListChangedListener()
}
internal fun onWidgetRemoved() {
Log.v("Message list widget removed")
if (!isAtLeastOneMessageListWidgetAdded()) {
unregisterMessageListChangedListener()
}
}
@Synchronized
private fun registerMessageListChangedListener() {
if (!listenerAdded) {
listenerAdded = true
messageListRepository.addListener(listener)
Log.v("Message list widget is now listening for message list changes…")
}
}
@Synchronized
private fun unregisterMessageListChangedListener() {
if (listenerAdded) {
listenerAdded = false
messageListRepository.removeListener(listener)
Log.v("Message list widget stopped listening for message list changes.")
}
}
private fun isAtLeastOneMessageListWidgetAdded(): Boolean {
return getAppWidgetIds().isNotEmpty()
}
private fun triggerMessageListWidgetUpdate() {
val appWidgetIds = getAppWidgetIds()
if (appWidgetIds.isNotEmpty()) {
appWidgetManager?.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.listView)
}
}
private fun resetMessageListWidget() {
val appWidgetIds = getAppWidgetIds()
if (appWidgetIds.isNotEmpty()) {
val intent = Intent(context, config.providerClass).apply {
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds)
}
context.sendBroadcast(intent)
}
}
private fun getAppWidgetIds(): IntArray {
val componentName = ComponentName(context, config.providerClass)
return appWidgetManager?.getAppWidgetIds(componentName) ?: intArrayOf()
}
}

View file

@ -0,0 +1,10 @@
package app.k9mail.feature.widget.message.list
import android.content.Intent
import android.widget.RemoteViewsService
class MessageListWidgetService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
return MessageListRemoteViewFactory(applicationContext)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View file

@ -0,0 +1,53 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<LinearLayout
android:id="@+id/top_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/message_list_widget_header_background"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<TextView
android:id="@+id/folder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingBottom="12dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="12dp"
android:textSize="20sp"
android:textColor="@color/message_list_widget_header_text"
tools:text="Unified Inbox"
/>
<ImageButton
android:id="@+id/new_message"
android:layout_width="56dp"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/message_list_widget_compose_action"
android:scaleType="center"
android:src="@drawable/ic_edit"
/>
</LinearLayout>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:divider="@color/message_list_widget_divider"
android:dividerHeight="0.5dp"
/>
</LinearLayout>

View file

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mail_list_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:background="#fff"
>
<!-- A regular View breaks things for some reason, but a TextView does the job -->
<TextView
android:id="@+id/chip"
android:layout_width="8dip"
android:layout_height="match_parent"
tools:background="#0099CC"
android:visibility="visible"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/widget_padding"
>
<TextView
android:id="@+id/mail_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginStart="4dp"
tools:text="25 May"
/>
<ImageView
android:id="@+id/attachment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/mail_date"
android:layout_centerInParent="true"
android:layout_marginStart="4dp"
android:layout_toStartOf="@+id/mail_date"
android:src="@drawable/ic_attachment"
android:visibility="gone"
tools:visibility="visible"
/>
<TextView
android:id="@+id/thread_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/attachment"
android:layout_marginStart="4dp"
android:maxLines="1"
android:paddingRight="4dip"
android:paddingBottom="1dip"
android:paddingLeft="4dip"
android:textSize="16sp"
android:textColor="?android:attr/colorBackground"
android:background="@drawable/thread_count_box_light"
tools:text="3"
/>
<TextView
android:id="@+id/sender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_gravity="start"
android:layout_toStartOf="@id/thread_count"
android:ellipsize="end"
android:maxLines="1"
android:textSize="16sp"
tools:text="Kinda long subject that should be long enough to exceed the available display space"
/>
<TextView
android:id="@+id/mail_subject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/sender"
android:layout_alignParentStart="true"
android:ellipsize="end"
android:maxLines="1"
android:paddingBottom="2dp"
android:textSize="15sp"
tools:text="Wikipedia"
/>
<TextView
android:id="@+id/mail_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/mail_subject"
android:layout_alignParentStart="true"
android:maxLines="1"
android:textSize="13sp"
tools:text="Towel Day is celebrated every year on 25 May as a tribute to the author Douglas Adams by his fans."
/>
</RelativeLayout>
</LinearLayout>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/loadingText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:padding="16dp"
android:textSize="15sp"
tools:text="@string/message_list_widget_list_item_loading"
/>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:gravity="center"
android:padding="16dp"
android:text="@string/message_list_widget_initializing"
android:textSize="18sp"
/>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">أنشِئ</string>
<string name="message_list_widget_list_item_loading">يُحمل…</string>
<string name="message_list_widget_initializing">يُحمل…</string>
<string name="message_list_widget_inbox_title">البريد الوارد الموحَّد</string>
<string name="message_list_widget_label">قائمة الرسائل</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Tərtib et</string>
<string name="message_list_widget_label">Mesaj siyahısı</string>
<string name="message_list_widget_initializing">Yüklənir…</string>
<string name="message_list_widget_list_item_loading">Yüklənir…</string>
<string name="message_list_widget_inbox_title">Ümumi Gələnlər Qutusu</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Напісаць</string>
<string name="message_list_widget_list_item_loading">Загрузка…</string>
<string name="message_list_widget_initializing">Загрузка…</string>
<string name="message_list_widget_inbox_title">Усе атрыманыя</string>
<string name="message_list_widget_label">Спіс лістоў</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Съставяне</string>
<string name="message_list_widget_list_item_loading">Зареждане…</string>
<string name="message_list_widget_initializing">Зареждане…</string>
<string name="message_list_widget_inbox_title">Обединена кутия</string>
<string name="message_list_widget_label">Списък със съобщения</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="message_list_widget_list_item_loading">প্রক্রিয়ারত…</string>
<string name="message_list_widget_initializing">প্রক্রিয়ারত…</string>
<string name="message_list_widget_compose_action">তৈরি</string>
<string name="message_list_widget_inbox_title">সমন্বিত ইনবক্স</string>
<string name="message_list_widget_label">বার্তা তালিকা</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Skridaozañ</string>
<string name="message_list_widget_list_item_loading">O kargañ…</string>
<string name="message_list_widget_initializing">O kargañ…</string>
<string name="message_list_widget_inbox_title">Boest degemer unanet</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Sastavi</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Redacta</string>
<string name="message_list_widget_list_item_loading">S\'està carregant…</string>
<string name="message_list_widget_initializing">S\'està carregant…</string>
<string name="message_list_widget_inbox_title">Bústia d\'entrada unificada</string>
<string name="message_list_widget_label">Llista de missatges</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Cumpone</string>
<string name="message_list_widget_list_item_loading">Caricamentu…</string>
<string name="message_list_widget_initializing">Caricamentu…</string>
<string name="message_list_widget_inbox_title">Ricezzione cuncolta</string>
<string name="message_list_widget_label">Lista di i messaghji</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Nová</string>
<string name="message_list_widget_list_item_loading">Načítání…</string>
<string name="message_list_widget_initializing">Načítání…</string>
<string name="message_list_widget_inbox_title">Integrovaná doručená pošta</string>
<string name="message_list_widget_label">Seznam zpráv</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Ysgrifennu</string>
<string name="message_list_widget_list_item_loading">Yn llwytho…</string>
<string name="message_list_widget_initializing">Yn llwytho…</string>
<string name="message_list_widget_inbox_title">Mewnflwch Unedig</string>
<string name="message_list_widget_label">Rhestr Negeseuon</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Skriv</string>
<string name="message_list_widget_list_item_loading">Indlæser…</string>
<string name="message_list_widget_initializing">Indlæser…</string>
<string name="message_list_widget_inbox_title">Fælles indbakke</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Verfassen</string>
<string name="message_list_widget_list_item_loading">Ladevorgang…</string>
<string name="message_list_widget_initializing">Ladevorgang…</string>
<string name="message_list_widget_inbox_title">Gemeinsamer Posteingang</string>
<string name="message_list_widget_label">Nachrichtenliste</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Σύνθεση</string>
<string name="message_list_widget_list_item_loading">Φόρτωση…</string>
<string name="message_list_widget_initializing">Φόρτωση…</string>
<string name="message_list_widget_inbox_title">Ενιαία Εισερχόμενα</string>
<string name="message_list_widget_label">Λίστα μηνυμάτων</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Compose</string>
<string name="message_list_widget_list_item_loading">Loading…</string>
<string name="message_list_widget_initializing">Loading…</string>
<string name="message_list_widget_inbox_title">Unified Inbox</string>
<string name="message_list_widget_label">Message List</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Krei</string>
<string name="message_list_widget_list_item_loading">Ŝargado…</string>
<string name="message_list_widget_initializing">Ŝargado…</string>
<string name="message_list_widget_inbox_title">Unuigita ricevujo</string>
<string name="message_list_widget_label">Listo de la mesaĝoj</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Redactar</string>
<string name="message_list_widget_list_item_loading">Cargando…</string>
<string name="message_list_widget_initializing">Cargando…</string>
<string name="message_list_widget_inbox_title">Entrada unificada</string>
<string name="message_list_widget_label">Lista de mensajes</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Koosta</string>
<string name="message_list_widget_list_item_loading">Laadime…</string>
<string name="message_list_widget_initializing">Laadime…</string>
<string name="message_list_widget_inbox_title">Koondsisendkaust</string>
<string name="message_list_widget_label">Kirjade loend</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Idatzi</string>
<string name="message_list_widget_list_item_loading">Kargatzen…</string>
<string name="message_list_widget_initializing">Kargatzen…</string>
<string name="message_list_widget_inbox_title">Sarrerako ontzi bateratua</string>
<string name="message_list_widget_label">Mezuen zerrenda</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">نوشتن</string>
<string name="message_list_widget_list_item_loading">بارگیری…</string>
<string name="message_list_widget_initializing">بارگیری…</string>
<string name="message_list_widget_inbox_title">صندوق ورودی یکپارچه</string>
<string name="message_list_widget_label">سیاههٔ پیام‌ها</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Kirjoita</string>
<string name="message_list_widget_list_item_loading">Ladataan…</string>
<string name="message_list_widget_initializing">Ladataan…</string>
<string name="message_list_widget_inbox_title">Yhdistetty saapuneet</string>
<string name="message_list_widget_label">Viestilista</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Rédiger</string>
<string name="message_list_widget_list_item_loading">Chargement…</string>
<string name="message_list_widget_initializing">Chargement…</string>
<string name="message_list_widget_inbox_title">Boîte de réception unifiée</string>
<string name="message_list_widget_label">Liste des courriels</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Opstelle</string>
<string name="message_list_widget_list_item_loading">Lade…</string>
<string name="message_list_widget_initializing">Lade…</string>
<string name="message_list_widget_inbox_title">Gearfoege Postfek YN</string>
<string name="message_list_widget_label">Berjochtelist</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="message_list_widget_list_item_loading">Á lódáil…</string>
<string name="message_list_widget_label">Liosta Teachtaireachtaí</string>
<string name="message_list_widget_compose_action">Cum</string>
<string name="message_list_widget_initializing">Á lódáil…</string>
<string name="message_list_widget_inbox_title">Bosca Isteach Aontaithe</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Sgrìobh</string>
<string name="message_list_widget_list_item_loading">Ga luchdadh…</string>
<string name="message_list_widget_initializing">Ga luchdadh…</string>
<string name="message_list_widget_inbox_title">An t-oll-bhogsa</string>
<string name="message_list_widget_label">Liosta nan teachdaireachdan</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Redactar</string>
<string name="message_list_widget_list_item_loading">A cargar…</string>
<string name="message_list_widget_initializing">A cargar…</string>
<string name="message_list_widget_inbox_title">Entrada unificada</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">बनाएं</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Nova poruka</string>
<string name="message_list_widget_list_item_loading">Učitavanje…</string>
<string name="message_list_widget_initializing">Učitavanje…</string>
<string name="message_list_widget_inbox_title">Objedinjena dolazna pošta</string>
<string name="message_list_widget_label">Popis poruka</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Írás</string>
<string name="message_list_widget_list_item_loading">Betöltés…</string>
<string name="message_list_widget_initializing">Betöltés…</string>
<string name="message_list_widget_inbox_title">Egységes beérkezett üzenetek</string>
<string name="message_list_widget_label">Üzenetlista</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Գրել</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Menyusun</string>
<string name="message_list_widget_list_item_loading">Memuat…</string>
<string name="message_list_widget_initializing">Memuat…</string>
<string name="message_list_widget_inbox_title">Kotak Masuk Terpadu</string>
<string name="message_list_widget_label">Daftar Pesan</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Semja</string>
<string name="message_list_widget_list_item_loading">Hleður…</string>
<string name="message_list_widget_initializing">Hleður…</string>
<string name="message_list_widget_inbox_title">Sameinað innhólf</string>
<string name="message_list_widget_label">Skilaboðalisti</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Componi</string>
<string name="message_list_widget_list_item_loading">Caricamento…</string>
<string name="message_list_widget_initializing">Caricamento…</string>
<string name="message_list_widget_inbox_title">Posta combinata</string>
<string name="message_list_widget_label">Elenco messaggi</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">חבר</string>
<string name="message_list_widget_list_item_loading">טוען…</string>
<string name="message_list_widget_initializing">טוען…</string>
<string name="message_list_widget_inbox_title">תיבת דואר נכנס מאוחדת</string>
<string name="message_list_widget_label">רשימת הודעות</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">作成</string>
<string name="message_list_widget_list_item_loading">読み込み中…</string>
<string name="message_list_widget_initializing">読み込み中…</string>
<string name="message_list_widget_inbox_title">統合受信トレイ</string>
<string name="message_list_widget_label">メッセージ一覧</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">შექმნა</string>
<string name="message_list_widget_list_item_loading">ჩატვირთვა…</string>
<string name="message_list_widget_initializing">ჩატვირთვა…</string>
<string name="message_list_widget_inbox_title">გაერთიანებული შემავალი</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="message_list_widget_compose_action">Құрастыру</string>
<string name="message_list_widget_list_item_loading">Жүктелуде…</string>
<string name="message_list_widget_initializing">Жүктелуде…</string>
<string name="message_list_widget_label">Хабарламалар тізімі</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">작성</string>
<string name="message_list_widget_list_item_loading">로딩 중…</string>
<string name="message_list_widget_initializing">로딩 중…</string>
<string name="message_list_widget_inbox_title">통합 편지함</string>
<string name="message_list_widget_label">메시지 목록</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Sukurti</string>
<string name="message_list_widget_list_item_loading">Įkeliama…</string>
<string name="message_list_widget_initializing">Įkeliama…</string>
<string name="message_list_widget_inbox_title">Suvestiniai gautieji</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Rakstīt ziņu</string>
<string name="message_list_widget_list_item_loading">Ielādē…</string>
<string name="message_list_widget_initializing">Ielādē…</string>
<string name="message_list_widget_inbox_title">Apvienotā Iesūtne</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">രചിക്കുക</string>
<string name="message_list_widget_list_item_loading">ലോഡുചെയ്യുന്നു…</string>
<string name="message_list_widget_initializing">ലോഡുചെയ്യുന്നു…</string>
<string name="message_list_widget_inbox_title">ഏകീകൃത ഇൻ‌ബോക്സ്</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Skriv</string>
<string name="message_list_widget_list_item_loading">Laster…</string>
<string name="message_list_widget_initializing">Laster…</string>
<string name="message_list_widget_inbox_title">Samlet innboks</string>
<string name="message_list_widget_label">Meldingsliste</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Opstellen</string>
<string name="message_list_widget_list_item_loading">Laden…</string>
<string name="message_list_widget_initializing">Laden…</string>
<string name="message_list_widget_inbox_title">Samengevoegd Postvak IN</string>
<string name="message_list_widget_label">Berichtenlijst</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Skriv</string>
<string name="message_list_widget_list_item_loading">Lastar …</string>
<string name="message_list_widget_initializing">Lastar …</string>
<string name="message_list_widget_label">Meldingsliste</string>
<string name="message_list_widget_inbox_title">Samla innboks</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Nowa wiadomość</string>
<string name="message_list_widget_list_item_loading">Wczytywanie…</string>
<string name="message_list_widget_initializing">Wczytywanie…</string>
<string name="message_list_widget_inbox_title">Zintegrowana odbiorcza</string>
<string name="message_list_widget_label">Lista wiadomości</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Escrever</string>
<string name="message_list_widget_list_item_loading">Carregando…</string>
<string name="message_list_widget_initializing">Carregando…</string>
<string name="message_list_widget_inbox_title">Caixa de Entrada Unificada</string>
<string name="message_list_widget_label">Lista de Mensagens</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Compor</string>
<string name="message_list_widget_list_item_loading">A carregar…</string>
<string name="message_list_widget_initializing">A carregar…</string>
<string name="message_list_widget_inbox_title">Caixa de entrada unificada</string>
<string name="message_list_widget_label">Lista de mensagens</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Compune</string>
<string name="message_list_widget_list_item_loading">Se încarcă…</string>
<string name="message_list_widget_initializing">Se încarcă…</string>
<string name="message_list_widget_inbox_title">Căsuță poștală unificată</string>
<string name="message_list_widget_label">Lista de mesaje</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Создать</string>
<string name="message_list_widget_list_item_loading">Загрузка…</string>
<string name="message_list_widget_initializing">Загрузка…</string>
<string name="message_list_widget_inbox_title">Общие входящие</string>
<string name="message_list_widget_label">Список сообщений</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Napísať</string>
<string name="message_list_widget_list_item_loading">Načítavam…</string>
<string name="message_list_widget_initializing">Načítavam…</string>
<string name="message_list_widget_inbox_title">Jednotná schránka</string>
<string name="message_list_widget_label">Zoznam Správ</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Sestavi sporočilo</string>
<string name="message_list_widget_list_item_loading">Poteka nalaganje …</string>
<string name="message_list_widget_initializing">Poteka nalaganje …</string>
<string name="message_list_widget_inbox_title">Skupna mapa prejetih sporočil</string>
<string name="message_list_widget_label">Seznam sporočil</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Hartoni</string>
<string name="message_list_widget_list_item_loading">Po ngarkohet…</string>
<string name="message_list_widget_initializing">Po ngarkohet…</string>
<string name="message_list_widget_inbox_title">Kuti Poste e Njësuar</string>
<string name="message_list_widget_label">Listë Mesazhesh</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Састави</string>
<string name="message_list_widget_list_item_loading">Учитавање…</string>
<string name="message_list_widget_initializing">Учитавање…</string>
<string name="message_list_widget_inbox_title">Обједињено сандуче</string>
<string name="message_list_widget_label">Листа порука</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Skriv</string>
<string name="message_list_widget_list_item_loading">Läser in…</string>
<string name="message_list_widget_initializing">Läser in…</string>
<string name="message_list_widget_inbox_title">Samlad inkorg</string>
<string name="message_list_widget_label">Meddelandelista</string>
</resources>

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">எழுது</string>
<string name="message_list_widget_inbox_title">ஒருங்கிணைந்த இன்பாக்ச்</string>
<string name="message_list_widget_label">செய்தி பட்டியல்</string>
<string name="message_list_widget_list_item_loading">ஏற்றுகிறது…</string>
<string name="message_list_widget_initializing">ஏற்றுகிறது…</string>
</resources>

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Oluştur</string>
<string name="message_list_widget_list_item_loading">Yükleniyor…</string>
<string name="message_list_widget_initializing">Yükleniyor…</string>
<string name="message_list_widget_inbox_title">Birleşik Gelen Kutusu</string>
<string name="message_list_widget_label">İleti Listesi</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Написати</string>
<string name="message_list_widget_list_item_loading">Завантаження…</string>
<string name="message_list_widget_initializing">Завантаження…</string>
<string name="message_list_widget_inbox_title">Об\'єднані Вхідні</string>
<string name="message_list_widget_label">Список повідомлень</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">Soạn thư</string>
<string name="message_list_widget_list_item_loading">Đang tải…</string>
<string name="message_list_widget_initializing">Đang tải…</string>
<string name="message_list_widget_inbox_title">Hộp thư đồng nhất</string>
<string name="message_list_widget_label">Danh sách tin nhắn</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">撰写</string>
<string name="message_list_widget_list_item_loading">正在加载…</string>
<string name="message_list_widget_initializing">正在加载…</string>
<string name="message_list_widget_inbox_title">统一收件箱</string>
<string name="message_list_widget_label">邮件列表</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="message_list_widget_compose_action">新郵件</string>
<string name="message_list_widget_list_item_loading">載入中…</string>
<string name="message_list_widget_initializing">載入中…</string>
<string name="message_list_widget_inbox_title">全域收件匣</string>
<string name="message_list_widget_label">訊息清單</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="message_list_widget_header_background">#737373</color>
<color name="message_list_widget_header_text">#e4e4e4</color>
<color name="message_list_widget_divider">#e5e5e5</color>
<color name="message_list_widget_text_read">#444444</color>
<color name="message_list_widget_text_unread">#000000</color>
</resources>

Some files were not shown because too many files have changed in this diff Show more