Merge pull request #6731 from thundernest/add_contact_repository
Add ContactRepository
This commit is contained in:
commit
e2db2337af
28 changed files with 349 additions and 180 deletions
|
@ -28,7 +28,7 @@ val mainModule = module {
|
|||
single { get<Context>().resources }
|
||||
single { get<Context>().contentResolver }
|
||||
single { LocalStoreProvider() }
|
||||
single { Contacts(contactDataSource = get()) }
|
||||
single { Contacts() }
|
||||
single { LocalKeyStore(directoryProvider = get()) }
|
||||
single { TrustManagerFactory.createInstance(get()) }
|
||||
single { LocalKeyStoreManager(get()) }
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package com.fsck.k9.helper
|
||||
|
||||
import app.k9mail.core.android.common.contact.ContactRepository
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
|
||||
interface ContactNameProvider {
|
||||
fun getNameForAddress(address: String): String?
|
||||
}
|
||||
|
||||
class RealContactNameProvider(private val contacts: Contacts) : ContactNameProvider {
|
||||
class RealContactNameProvider(
|
||||
private val contactRepository: ContactRepository,
|
||||
) : ContactNameProvider {
|
||||
override fun getNameForAddress(address: String): String? {
|
||||
return contacts.getNameFor(EmailAddress(address))
|
||||
return contactRepository.getContactFor(EmailAddress(address))?.name
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,63 +1,11 @@
|
|||
package com.fsck.k9.helper
|
||||
|
||||
import android.net.Uri
|
||||
import app.k9mail.core.android.common.contact.ContactDataSource
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import com.fsck.k9.mail.Address
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Helper class to access the contacts stored on the device.
|
||||
*/
|
||||
open class Contacts(
|
||||
private val contactDataSource: ContactDataSource,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Check whether the provided email address belongs to one of the contacts.
|
||||
*
|
||||
* @param emailAddress The email address to look for.
|
||||
* @return <tt>true</tt>, if the email address belongs to a contact.
|
||||
* <tt>false</tt>, otherwise.
|
||||
*/
|
||||
fun isInContacts(emailAddress: EmailAddress): Boolean = contactDataSource.hasContactFor(emailAddress)
|
||||
|
||||
/**
|
||||
* Check whether one of the provided email addresses belongs to one of the contacts.
|
||||
*
|
||||
* @param emailAddresses The email addresses to search in contacts
|
||||
* @return <tt>true</tt>, if one of the email addresses belongs to a contact.
|
||||
* <tt>false</tt>, otherwise.
|
||||
*/
|
||||
fun isAnyInContacts(emailAddresses: List<EmailAddress>): Boolean =
|
||||
emailAddresses.any { emailAddress -> isInContacts(emailAddress) }
|
||||
|
||||
fun getContactUri(emailAddress: EmailAddress): Uri? {
|
||||
val contact = contactDataSource.getContactFor(emailAddress)
|
||||
return contact?.uri
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the contact an email address belongs to.
|
||||
*
|
||||
* @param emailAddress The email address to search for.
|
||||
* @return The name of the contact the email address belongs to. Or
|
||||
* <tt>null</tt> if there's no matching contact.
|
||||
*/
|
||||
open fun getNameFor(emailAddress: EmailAddress): String? {
|
||||
if (nameCache.containsKey(emailAddress)) {
|
||||
return nameCache[emailAddress]
|
||||
}
|
||||
|
||||
val contact = contactDataSource.getContactFor(emailAddress)
|
||||
return if (contact != null) {
|
||||
nameCache[emailAddress] = contact.name
|
||||
contact.name
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
class Contacts {
|
||||
/**
|
||||
* Mark contacts with the provided email addresses as contacted.
|
||||
*/
|
||||
|
@ -65,33 +13,4 @@ open class Contacts(
|
|||
// TODO: Keep track of this information in a local database. Then use this information when sorting contacts for
|
||||
// auto-completion.
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URI to the picture of the contact with the supplied email address.
|
||||
*
|
||||
* @param emailAddress An email address, the contact database is searched for.
|
||||
*
|
||||
* @return URI to the picture of the contact with the supplied email address. `null` if
|
||||
* no such contact could be found or the contact doesn't have a picture.
|
||||
*/
|
||||
fun getPhotoUri(emailAddress: EmailAddress): Uri? {
|
||||
return try {
|
||||
val contact = contactDataSource.getContactFor(emailAddress)
|
||||
contact?.photoUri
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Couldn't fetch photo for contact with email ${emailAddress.address}")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val nameCache = HashMap<EmailAddress, String?>()
|
||||
|
||||
/**
|
||||
* Clears the cache for names and photo uris
|
||||
*/
|
||||
fun clearCache() {
|
||||
nameCache.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ import org.koin.dsl.module
|
|||
|
||||
val helperModule = module {
|
||||
single { ClipboardManager(get()) }
|
||||
single { MessageHelper(resourceProvider = get(), contacts = get()) }
|
||||
single { MessageHelper(resourceProvider = get(), contactRepository = get()) }
|
||||
factory<KeyStoreDirectoryProvider> { AndroidKeyStoreDirectoryProvider(context = get()) }
|
||||
factory { get<Context>().getSystemService(Context.ALARM_SERVICE) as AlarmManager }
|
||||
single { AlarmManagerCompat(alarmManager = get()) }
|
||||
factory<ContactNameProvider> { RealContactNameProvider(contacts = get()) }
|
||||
factory<ContactNameProvider> { RealContactNameProvider(contactRepository = get()) }
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.text.SpannableString
|
|||
import android.text.SpannableStringBuilder
|
||||
import android.text.TextUtils
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import app.k9mail.core.android.common.contact.ContactRepository
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import com.fsck.k9.CoreResourceProvider
|
||||
import com.fsck.k9.K9.contactNameColor
|
||||
|
@ -16,23 +17,23 @@ import java.util.regex.Pattern
|
|||
|
||||
class MessageHelper(
|
||||
private val resourceProvider: CoreResourceProvider,
|
||||
private val contacts: Contacts,
|
||||
private val contactRepository: ContactRepository,
|
||||
) {
|
||||
|
||||
fun getSenderDisplayName(address: Address?): CharSequence {
|
||||
if (address == null) {
|
||||
return resourceProvider.contactUnknownSender()
|
||||
}
|
||||
val contactHelper = if (isShowContactName) contacts else null
|
||||
return toFriendly(address, contactHelper)
|
||||
val repository = if (isShowContactName) contactRepository else null
|
||||
return toFriendly(address, repository)
|
||||
}
|
||||
|
||||
fun getRecipientDisplayNames(addresses: Array<Address>?): CharSequence {
|
||||
if (addresses == null || addresses.isEmpty()) {
|
||||
return resourceProvider.contactUnknownRecipient()
|
||||
}
|
||||
val contactHelper = if (isShowContactName) contacts else null
|
||||
val recipients = toFriendly(addresses, contactHelper)
|
||||
val repository = if (isShowContactName) contactRepository else null
|
||||
val recipients = toFriendly(addresses, repository)
|
||||
return SpannableStringBuilder(resourceProvider.contactDisplayNamePrefix()).append(recipients)
|
||||
}
|
||||
|
||||
|
@ -59,28 +60,28 @@ class MessageHelper(
|
|||
* @param contacts A [Contacts] instance or `null`.
|
||||
* @return A "friendly" name for this [Address].
|
||||
*/
|
||||
fun toFriendly(address: Address, contacts: Contacts?): CharSequence {
|
||||
fun toFriendly(address: Address, contactRepository: ContactRepository?): CharSequence {
|
||||
return toFriendly(
|
||||
address,
|
||||
contacts,
|
||||
contactRepository,
|
||||
isShowCorrespondentNames,
|
||||
isChangeContactNameColor,
|
||||
contactNameColor,
|
||||
)
|
||||
}
|
||||
|
||||
fun toFriendly(addresses: Array<Address>?, contacts: Contacts?): CharSequence? {
|
||||
var contacts = contacts
|
||||
fun toFriendly(addresses: Array<Address>?, contactRepository: ContactRepository?): CharSequence? {
|
||||
var repository = contactRepository
|
||||
if (addresses == null) {
|
||||
return null
|
||||
}
|
||||
if (addresses.size >= TOO_MANY_ADDRESSES) {
|
||||
// Don't look up contacts if the number of addresses is very high.
|
||||
contacts = null
|
||||
repository = null
|
||||
}
|
||||
val stringBuilder = SpannableStringBuilder()
|
||||
for (i in addresses.indices) {
|
||||
stringBuilder.append(toFriendly(addresses[i], contacts))
|
||||
stringBuilder.append(toFriendly(addresses[i], repository))
|
||||
if (i < addresses.size - 1) {
|
||||
stringBuilder.append(',')
|
||||
}
|
||||
|
@ -92,15 +93,15 @@ class MessageHelper(
|
|||
@JvmStatic
|
||||
fun toFriendly(
|
||||
address: Address,
|
||||
contacts: Contacts?,
|
||||
contactRepository: ContactRepository?,
|
||||
showCorrespondentNames: Boolean,
|
||||
changeContactNameColor: Boolean,
|
||||
contactNameColor: Int,
|
||||
): CharSequence {
|
||||
if (!showCorrespondentNames) {
|
||||
return address.address
|
||||
} else if (contacts != null) {
|
||||
val name = contacts.getNameFor(EmailAddress(address.address))
|
||||
} else if (contactRepository != null) {
|
||||
val name = contactRepository.getContactFor(EmailAddress(address.address))?.name
|
||||
if (name != null) {
|
||||
return if (changeContactNameColor) {
|
||||
val coloredName = SpannableString(name)
|
||||
|
|
|
@ -80,7 +80,7 @@ val coreNotificationModule = module {
|
|||
clock = get(),
|
||||
)
|
||||
}
|
||||
factory { NotificationContentCreator(resourceProvider = get(), contacts = get()) }
|
||||
factory { NotificationContentCreator(resourceProvider = get(), contactRepository = get()) }
|
||||
factory { BaseNotificationDataCreator() }
|
||||
factory { SingleMessageNotificationDataCreator() }
|
||||
factory { SummaryNotificationDataCreator(singleMessageNotificationDataCreator = get()) }
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package com.fsck.k9.notification
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import app.k9mail.core.android.common.contact.ContactRepository
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.K9
|
||||
import com.fsck.k9.helper.Contacts
|
||||
import com.fsck.k9.helper.MessageHelper
|
||||
import com.fsck.k9.mail.Message
|
||||
import com.fsck.k9.mailstore.LocalMessage
|
||||
|
@ -11,7 +11,7 @@ import com.fsck.k9.message.extractors.PreviewResult.PreviewType
|
|||
|
||||
internal class NotificationContentCreator(
|
||||
private val resourceProvider: NotificationResourceProvider,
|
||||
private val contacts: Contacts,
|
||||
private val contactRepository: ContactRepository,
|
||||
) {
|
||||
fun createFromMessage(account: Account, message: LocalMessage): NotificationContent {
|
||||
val sender = getMessageSender(account, message)
|
||||
|
@ -69,14 +69,14 @@ internal class NotificationContentCreator(
|
|||
}
|
||||
|
||||
private fun getMessageSender(account: Account, message: Message): String? {
|
||||
val localContacts = if (K9.isShowContactName) contacts else null
|
||||
val localContactRepository = if (K9.isShowContactName) contactRepository else null
|
||||
var isSelf = false
|
||||
|
||||
val fromAddresses = message.from
|
||||
if (!fromAddresses.isNullOrEmpty()) {
|
||||
isSelf = account.isAnIdentity(fromAddresses)
|
||||
if (!isSelf) {
|
||||
return MessageHelper.toFriendly(fromAddresses.first(), localContacts).toString()
|
||||
return MessageHelper.toFriendly(fromAddresses.first(), localContactRepository).toString()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,10 @@ internal class NotificationContentCreator(
|
|||
// show To: if the message was sent from me
|
||||
val recipients = message.getRecipients(Message.RecipientType.TO)
|
||||
if (!recipients.isNullOrEmpty()) {
|
||||
val recipientDisplayName = MessageHelper.toFriendly(recipients.first(), localContacts).toString()
|
||||
val recipientDisplayName = MessageHelper.toFriendly(
|
||||
address = recipients.first(),
|
||||
contactRepository = localContactRepository,
|
||||
).toString()
|
||||
return resourceProvider.recipientDisplayName(recipientDisplayName)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package com.fsck.k9.helper
|
|||
|
||||
import android.graphics.Color
|
||||
import android.text.SpannableString
|
||||
import app.k9mail.core.android.common.contact.Contact
|
||||
import app.k9mail.core.android.common.contact.ContactRepository
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
|
@ -9,49 +11,25 @@ import assertk.assertions.isInstanceOf
|
|||
import com.fsck.k9.RobolectricTest
|
||||
import com.fsck.k9.helper.MessageHelper.Companion.toFriendly
|
||||
import com.fsck.k9.mail.Address
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.stub
|
||||
|
||||
class MessageHelperTest : RobolectricTest() {
|
||||
|
||||
private lateinit var contacts: Contacts
|
||||
private lateinit var contactsWithFakeContact: Contacts
|
||||
private lateinit var contactsWithFakeSpoofContact: Contacts
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
contacts = mock()
|
||||
contactsWithFakeContact = object : Contacts(mock()) {
|
||||
override fun getNameFor(emailAddress: EmailAddress): String? {
|
||||
return if ("test@testor.com" == emailAddress.address) {
|
||||
"Tim Testor"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
contactsWithFakeSpoofContact = object : Contacts(mock()) {
|
||||
override fun getNameFor(emailAddress: EmailAddress): String? {
|
||||
return if ("test@testor.com" == emailAddress.address) {
|
||||
"Tim@Testor"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private val contactRepository: ContactRepository = mock()
|
||||
|
||||
@Test
|
||||
fun testToFriendlyShowsPersonalPartIfItExists() {
|
||||
val address = Address("test@testor.com", "Tim Testor")
|
||||
assertThat(toFriendly(address, contacts)).isEqualTo("Tim Testor")
|
||||
assertThat(toFriendly(address, contactRepository)).isEqualTo("Tim Testor")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToFriendlyShowsEmailPartIfNoPersonalPartExists() {
|
||||
val address = Address("test@testor.com")
|
||||
assertThat(toFriendly(address, contacts)).isEqualTo("test@testor.com")
|
||||
assertThat(toFriendly(address, contactRepository)).isEqualTo("test@testor.com")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -59,21 +37,25 @@ class MessageHelperTest : RobolectricTest() {
|
|||
val address1 = Address("test@testor.com", "Tim Testor")
|
||||
val address2 = Address("foo@bar.com", "Foo Bar")
|
||||
val addresses = arrayOf(address1, address2)
|
||||
assertThat(toFriendly(addresses, contacts).toString()).isEqualTo("Tim Testor,Foo Bar")
|
||||
assertThat(toFriendly(addresses, contactRepository).toString()).isEqualTo("Tim Testor,Foo Bar")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToFriendlyWithContactLookup() {
|
||||
val address = Address("test@testor.com")
|
||||
assertThat(toFriendly(address, contactsWithFakeContact)).isEqualTo("Tim Testor")
|
||||
val address = Address(EMAIL_ADDRESS.address)
|
||||
setupContactRepositoryWithFakeContact(EMAIL_ADDRESS)
|
||||
|
||||
assertThat(toFriendly(address, contactRepository)).isEqualTo("Tim Testor")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToFriendlyWithChangeContactColor() {
|
||||
val address = Address("test@testor.com")
|
||||
val address = Address(EMAIL_ADDRESS.address)
|
||||
setupContactRepositoryWithFakeContact(EMAIL_ADDRESS)
|
||||
|
||||
val friendly = toFriendly(
|
||||
address = address,
|
||||
contacts = contactsWithFakeContact,
|
||||
contactRepository = contactRepository,
|
||||
showCorrespondentNames = true,
|
||||
changeContactNameColor = true,
|
||||
contactNameColor = Color.RED,
|
||||
|
@ -84,10 +66,12 @@ class MessageHelperTest : RobolectricTest() {
|
|||
|
||||
@Test
|
||||
fun testToFriendlyWithoutCorrespondentNames() {
|
||||
val address = Address("test@testor.com", "Tim Testor")
|
||||
val address = Address(EMAIL_ADDRESS.address, "Tim Testor")
|
||||
setupContactRepositoryWithFakeContact(EMAIL_ADDRESS)
|
||||
|
||||
val friendly = toFriendly(
|
||||
address = address,
|
||||
contacts = contactsWithFakeContact,
|
||||
contactRepository = contactRepository,
|
||||
showCorrespondentNames = false,
|
||||
changeContactNameColor = false,
|
||||
contactNameColor = 0,
|
||||
|
@ -98,34 +82,66 @@ class MessageHelperTest : RobolectricTest() {
|
|||
@Test
|
||||
fun toFriendly_spoofPreventionOverridesPersonal() {
|
||||
val address = Address("test@testor.com", "potus@whitehouse.gov")
|
||||
val friendly = toFriendly(address, contacts)
|
||||
val friendly = toFriendly(address, contactRepository)
|
||||
assertThat(friendly).isEqualTo("test@testor.com")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toFriendly_atPrecededByOpeningParenthesisShouldNotTriggerSpoofPrevention() {
|
||||
val address = Address("gitlab@gitlab.example", "username (@username)")
|
||||
val friendly = toFriendly(address, contacts)
|
||||
val friendly = toFriendly(address, contactRepository)
|
||||
assertThat(friendly).isEqualTo("username (@username)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toFriendly_nameStartingWithAtShouldNotTriggerSpoofPrevention() {
|
||||
val address = Address("address@domain.example", "@username")
|
||||
val friendly = toFriendly(address, contacts)
|
||||
val friendly = toFriendly(address, contactRepository)
|
||||
assertThat(friendly).isEqualTo("@username")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toFriendly_spoofPreventionDoesntOverrideContact() {
|
||||
val address = Address("test@testor.com", "Tim Testor")
|
||||
val address = Address(EMAIL_ADDRESS.address, "Tim Testor")
|
||||
setupContactRepositoryWithSpoofContact(EMAIL_ADDRESS)
|
||||
|
||||
val friendly = toFriendly(
|
||||
address = address,
|
||||
contacts = contactsWithFakeSpoofContact,
|
||||
contactRepository = contactRepository,
|
||||
showCorrespondentNames = true,
|
||||
changeContactNameColor = false,
|
||||
contactNameColor = 0,
|
||||
)
|
||||
assertThat(friendly).isEqualTo("Tim@Testor")
|
||||
}
|
||||
|
||||
private fun setupContactRepositoryWithFakeContact(emailAddress: EmailAddress) {
|
||||
contactRepository.stub {
|
||||
on { getContactFor(emailAddress) } doReturn
|
||||
Contact(
|
||||
id = 1L,
|
||||
name = "Tim Testor",
|
||||
emailAddress = emailAddress,
|
||||
uri = mock(),
|
||||
photoUri = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupContactRepositoryWithSpoofContact(emailAddress: EmailAddress) {
|
||||
contactRepository.stub {
|
||||
on { getContactFor(emailAddress) } doReturn
|
||||
Contact(
|
||||
id = 1L,
|
||||
name = "Tim@Testor",
|
||||
emailAddress = emailAddress,
|
||||
uri = mock(),
|
||||
photoUri = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val EMAIL_ADDRESS = EmailAddress("test@testor.com")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package com.fsck.k9.notification
|
||||
|
||||
import app.k9mail.core.android.common.contact.ContactRepository
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.RobolectricTest
|
||||
import com.fsck.k9.controller.MessageReference
|
||||
import com.fsck.k9.helper.Contacts
|
||||
import com.fsck.k9.mail.Address
|
||||
import com.fsck.k9.mail.Message.RecipientType
|
||||
import com.fsck.k9.mailstore.LocalMessage
|
||||
|
@ -26,8 +26,8 @@ private const val RECIPIENT_ADDRESS = "bob@example.com"
|
|||
private const val RECIPIENT_NAME = "Bob"
|
||||
|
||||
class NotificationContentCreatorTest : RobolectricTest() {
|
||||
private val contactRepository = createFakeContentRepository()
|
||||
private val resourceProvider = TestNotificationResourceProvider()
|
||||
private val contacts = mock<Contacts>()
|
||||
private val contentCreator = createNotificationContentCreator()
|
||||
private val messageReference = createMessageReference()
|
||||
private val account = createFakeAccount()
|
||||
|
@ -138,11 +138,16 @@ class NotificationContentCreatorTest : RobolectricTest() {
|
|||
}
|
||||
|
||||
private fun createNotificationContentCreator(): NotificationContentCreator {
|
||||
return NotificationContentCreator(resourceProvider, contacts)
|
||||
return NotificationContentCreator(
|
||||
resourceProvider,
|
||||
contactRepository,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createFakeAccount(): Account = mock()
|
||||
|
||||
private fun createFakeContentRepository(): ContactRepository = mock()
|
||||
|
||||
private fun createMessageReference(): MessageReference {
|
||||
return MessageReference(ACCOUNT_UUID, FOLDER_ID, UID)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package com.fsck.k9.notification
|
||||
|
||||
import app.k9mail.core.android.common.contact.ContactRepository
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.K9
|
||||
import com.fsck.k9.helper.Contacts
|
||||
import com.fsck.k9.mail.Flag
|
||||
import com.fsck.k9.mail.K9MailLib
|
||||
import com.fsck.k9.mail.Message
|
||||
|
@ -12,7 +12,9 @@ import com.fsck.k9.mailstore.LocalFolder.isModeMismatch
|
|||
import com.fsck.k9.mailstore.LocalMessage
|
||||
import timber.log.Timber
|
||||
|
||||
class K9NotificationStrategy(private val contacts: Contacts) : NotificationStrategy {
|
||||
class K9NotificationStrategy(
|
||||
private val contactRepository: ContactRepository,
|
||||
) : NotificationStrategy {
|
||||
|
||||
override fun shouldNotifyForMessage(
|
||||
account: Account,
|
||||
|
@ -86,7 +88,7 @@ class K9NotificationStrategy(private val contacts: Contacts) : NotificationStrat
|
|||
}
|
||||
|
||||
if (account.isNotifyContactsMailOnly &&
|
||||
!contacts.isAnyInContacts(message.from.map { EmailAddress(it.address) })
|
||||
!contactRepository.hasAnyContactFor(message.from.asList().mapNotNull { EmailAddress(it.address) })
|
||||
) {
|
||||
Timber.v("No notification: Message is not from a known contact")
|
||||
return false
|
||||
|
|
|
@ -26,6 +26,8 @@ import androidx.fragment.app.FragmentManager
|
|||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.fragment.app.commitNow
|
||||
import app.k9mail.core.android.common.contact.CachingRepository
|
||||
import app.k9mail.core.android.common.contact.ContactRepository
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.K9
|
||||
import com.fsck.k9.K9.SplitViewMode
|
||||
|
@ -34,7 +36,6 @@ import com.fsck.k9.account.BackgroundAccountRemover
|
|||
import com.fsck.k9.activity.compose.MessageActions
|
||||
import com.fsck.k9.controller.MessageReference
|
||||
import com.fsck.k9.controller.MessagingController
|
||||
import com.fsck.k9.helper.Contacts
|
||||
import com.fsck.k9.helper.ParcelableUtil
|
||||
import com.fsck.k9.mailstore.SearchStatusManager
|
||||
import com.fsck.k9.preferences.GeneralSettingsManager
|
||||
|
@ -89,6 +90,7 @@ open class MessageList :
|
|||
private val accountRemover: BackgroundAccountRemover by inject()
|
||||
private val generalSettingsManager: GeneralSettingsManager by inject()
|
||||
private val messagingController: MessagingController by inject()
|
||||
private val contactRepository: ContactRepository by inject()
|
||||
|
||||
private val permissionUiHelper: PermissionUiHelper = K9PermissionUiHelper(this)
|
||||
|
||||
|
@ -532,7 +534,10 @@ open class MessageList :
|
|||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
Contacts.clearCache()
|
||||
|
||||
if (contactRepository is CachingRepository) {
|
||||
(contactRepository as CachingRepository).clearCache()
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onSaveInstanceState(outState: Bundle) {
|
||||
|
|
|
@ -3,13 +3,16 @@ package com.fsck.k9.contacts
|
|||
import android.content.ContentResolver
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import app.k9mail.core.android.common.contact.ContactRepository
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import com.fsck.k9.helper.Contacts
|
||||
import timber.log.Timber
|
||||
|
||||
internal class ContactPhotoLoader(private val contentResolver: ContentResolver, private val contacts: Contacts) {
|
||||
internal class ContactPhotoLoader(
|
||||
private val contentResolver: ContentResolver,
|
||||
private val contactRepository: ContactRepository,
|
||||
) {
|
||||
fun loadContactPhoto(emailAddress: String): Bitmap? {
|
||||
val photoUri = contacts.getPhotoUri(EmailAddress(emailAddress)) ?: return null
|
||||
val photoUri = contactRepository.getContactFor(EmailAddress(emailAddress))?.photoUri ?: return null
|
||||
return try {
|
||||
contentResolver.openInputStream(photoUri).use { inputStream ->
|
||||
BitmapFactory.decodeStream(inputStream)
|
||||
|
|
|
@ -6,7 +6,7 @@ val contactsModule = module {
|
|||
single { ContactLetterExtractor() }
|
||||
factory { ContactLetterBitmapConfig(context = get(), themeManager = get()) }
|
||||
factory { ContactLetterBitmapCreator(letterExtractor = get(), config = get()) }
|
||||
factory { ContactPhotoLoader(contentResolver = get(), contacts = get()) }
|
||||
factory { ContactPhotoLoader(contentResolver = get(), contactRepository = get()) }
|
||||
factory { ContactPictureLoader(context = get(), contactLetterBitmapCreator = get()) }
|
||||
factory { ContactImageBitmapDecoderFactory(contactPhotoLoader = get()) }
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ val messageDetailsUiModule = module {
|
|||
messageRepository = get(),
|
||||
folderRepository = get(),
|
||||
contactSettingsProvider = get(),
|
||||
contacts = get(),
|
||||
contactRepository = get(),
|
||||
clipboardManager = get(),
|
||||
accountManager = get(),
|
||||
participantFormatter = get(),
|
||||
|
|
|
@ -4,11 +4,11 @@ import android.app.PendingIntent
|
|||
import android.content.res.Resources
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.k9mail.core.android.common.contact.ContactRepository
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.controller.MessageReference
|
||||
import com.fsck.k9.helper.ClipboardManager
|
||||
import com.fsck.k9.helper.Contacts
|
||||
import com.fsck.k9.mail.Address
|
||||
import com.fsck.k9.mailstore.CryptoResultAnnotation
|
||||
import com.fsck.k9.mailstore.Folder
|
||||
|
@ -33,7 +33,7 @@ internal class MessageDetailsViewModel(
|
|||
private val messageRepository: MessageRepository,
|
||||
private val folderRepository: FolderRepository,
|
||||
private val contactSettingsProvider: ContactSettingsProvider,
|
||||
private val contacts: Contacts,
|
||||
private val contactRepository: ContactRepository,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
private val accountManager: AccountManager,
|
||||
private val participantFormatter: MessageDetailsParticipantFormatter,
|
||||
|
@ -104,7 +104,7 @@ internal class MessageDetailsViewModel(
|
|||
Participant(
|
||||
displayName = displayName,
|
||||
emailAddress = emailAddress,
|
||||
contactLookupUri = contacts.getContactUri(EmailAddress(emailAddress)),
|
||||
contactLookupUri = contactRepository.getContactFor(EmailAddress(emailAddress))?.uri,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@ import android.widget.ImageView
|
|||
import android.widget.LinearLayout
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import app.k9mail.core.android.common.contact.ContactRepository
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.Account.ShowPictures
|
||||
import com.fsck.k9.helper.Contacts
|
||||
import com.fsck.k9.mail.Message
|
||||
import com.fsck.k9.mailstore.AttachmentViewInfo
|
||||
import com.fsck.k9.mailstore.MessageViewInfo
|
||||
|
@ -37,7 +37,7 @@ class MessageTopView(
|
|||
attrs: AttributeSet?,
|
||||
) : LinearLayout(context, attrs), KoinComponent {
|
||||
|
||||
private val contacts: Contacts by inject()
|
||||
private val contactRepository: ContactRepository by inject()
|
||||
|
||||
private lateinit var layoutInflater: LayoutInflater
|
||||
|
||||
|
@ -262,7 +262,7 @@ class MessageTopView(
|
|||
return false
|
||||
}
|
||||
val senderEmailAddress = getSenderEmailAddress(message) ?: return false
|
||||
return contacts.isInContacts(EmailAddress(senderEmailAddress))
|
||||
return contactRepository.hasContactFor(EmailAddress(senderEmailAddress))
|
||||
}
|
||||
|
||||
private fun getSenderEmailAddress(message: Message): String? {
|
||||
|
|
|
@ -174,7 +174,7 @@
|
|||
<ID>LongParameterList:ImapSync.kt$ImapSync$( syncConfig: SyncConfig, remoteFolder: ImapFolder, unsyncedMessages: List<ImapMessage>, smallMessages: MutableList<ImapMessage>, largeMessages: MutableList<ImapMessage>, progress: AtomicInteger, todo: Int, listener: SyncListener, )</ID>
|
||||
<ID>LongParameterList:MessageDatabaseHelpers.kt$( folderId: Long, deleted: Boolean = false, uid: String? = null, subject: String = "", date: Long = 0L, flags: String = "", senderList: String = "", toList: String = "", ccList: String = "", bccList: String = "", replyToList: String = "", attachmentCount: Int = 0, internalDate: Long = 0L, messageIdHeader: String? = null, previewType: DatabasePreviewType = DatabasePreviewType.NONE, preview: String = "", mimeType: String = "text/plain", normalizedSubjectHash: Long = 0L, empty: Boolean = false, read: Boolean = false, flagged: Boolean = false, answered: Boolean = false, forwarded: Boolean = false, messagePartId: Long = 0L, encryptionType: String? = null, newMessage: Boolean = false, )</ID>
|
||||
<ID>LongParameterList:MessageDatabaseHelpers.kt$( type: Int = 0, root: Int? = null, parent: Int = -1, seq: Int = 0, mimeType: String = "text/plain", decodedBodySize: Int = 0, displayName: String? = null, header: String? = null, encoding: String = "7bit", charset: String? = null, dataLocation: Int = 0, data: ByteArray? = null, preamble: String? = null, epilogue: String? = null, boundary: String? = null, contentId: String? = null, serverExtra: String? = null, directory: File? = null, )</ID>
|
||||
<ID>LongParameterList:MessageDetailsViewModel.kt$MessageDetailsViewModel$( private val resources: Resources, private val messageRepository: MessageRepository, private val folderRepository: FolderRepository, private val contactSettingsProvider: ContactSettingsProvider, private val contacts: Contacts, private val clipboardManager: ClipboardManager, private val accountManager: AccountManager, private val participantFormatter: MessageDetailsParticipantFormatter, private val folderNameFormatter: FolderNameFormatter, )</ID>
|
||||
<ID>LongParameterList:MessageDetailsViewModel.kt$MessageDetailsViewModel$( private val resources: Resources, private val messageRepository: MessageRepository, private val folderRepository: FolderRepository, private val contactSettingsProvider: ContactSettingsProvider, private val contactRepository: ContactRepository, private val clipboardManager: ClipboardManager, private val accountManager: AccountManager, private val participantFormatter: MessageDetailsParticipantFormatter, private val folderNameFormatter: FolderNameFormatter, )</ID>
|
||||
<ID>LongParameterList:MessageListAdapterTest.kt$MessageListAdapterTest$( account: Account = Account(SOME_ACCOUNT_UUID), subject: String? = "irrelevant", threadCount: Int = 0, messageDate: Long = 0L, internalDate: Long = 0L, displayName: CharSequence = "irrelevant", displayAddress: Address? = Address.parse("irrelevant@domain.example").first(), previewText: String = "irrelevant", isMessageEncrypted: Boolean = false, isRead: Boolean = false, isStarred: Boolean = false, isAnswered: Boolean = false, isForwarded: Boolean = false, hasAttachments: Boolean = false, uniqueId: Long = 0L, folderId: Long = 0L, messageUid: String = "irrelevant", databaseId: Long = 0L, threadRoot: Long = 0L, )</ID>
|
||||
<ID>LongParameterList:MessageListAdapterTest.kt$MessageListAdapterTest$( fontSizes: FontSizes = createFontSizes(), previewLines: Int = 0, stars: Boolean = true, senderAboveSubject: Boolean = false, showContactPicture: Boolean = true, showingThreadedList: Boolean = true, backGroundAsReadIndicator: Boolean = false, showAccountChip: Boolean = false, density: UiDensity = UiDensity.Default, )</ID>
|
||||
<ID>LongParameterList:MessagePartDatabaseHelpers.kt$( type: Int = MessagePartType.UNKNOWN, root: Long? = null, parent: Long = -1, seq: Int = 0, mimeType: String? = null, decodedBodySize: Int? = null, displayName: String? = null, header: String? = null, encoding: String? = null, charset: String? = null, dataLocation: Int = DataLocation.MISSING, data: ByteArray? = null, preamble: String? = null, epilogue: String? = null, boundary: String? = null, contentId: String? = null, serverExtra: String? = null, )</ID>
|
||||
|
@ -548,7 +548,7 @@
|
|||
<ID>ReturnCount:ListUnsubscribeHelper.kt$ListUnsubscribeHelper$fun getPreferredListUnsubscribeUri(message: Message): UnsubscribeUri?</ID>
|
||||
<ID>ReturnCount:ListUnsubscribeHelper.kt$ListUnsubscribeHelper$private fun extractUri(headerValue: String?): Uri?</ID>
|
||||
<ID>ReturnCount:MailSyncWorker.kt$MailSyncWorker$override fun doWork(): Result</ID>
|
||||
<ID>ReturnCount:MessageHelper.kt$MessageHelper.Companion$@JvmStatic fun toFriendly( address: Address, contacts: Contacts?, showCorrespondentNames: Boolean, changeContactNameColor: Boolean, contactNameColor: Int, ): CharSequence</ID>
|
||||
<ID>ReturnCount:MessageHelper.kt$MessageHelper.Companion$@JvmStatic fun toFriendly( address: Address, contactRepository: ContactRepository?, showCorrespondentNames: Boolean, changeContactNameColor: Boolean, contactNameColor: Int, ): CharSequence</ID>
|
||||
<ID>ReturnCount:MessageList.kt$MessageList$private fun decodeExtrasToLaunchData(intent: Intent): LaunchData</ID>
|
||||
<ID>ReturnCount:MessageList.kt$MessageList$private fun onCustomKeyDown(event: KeyEvent): Boolean</ID>
|
||||
<ID>ReturnCount:MessageList.kt$MessageList$public override fun onCreate(savedInstanceState: Bundle?)</ID>
|
||||
|
@ -666,7 +666,6 @@
|
|||
<ID>TooGenericExceptionCaught:CommandSync.kt$CommandSync$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:ContactPhotoLoader.kt$ContactPhotoLoader$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:ContactPictureLoader.kt$ContactPictureLoader$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:Contacts.kt$Contacts$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:GeneralSettingsViewModel.kt$GeneralSettingsViewModel$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:ImapFolderPusher.kt$ImapFolderPusher$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:ImapSync.kt$ImapSync$e: Exception</ID>
|
||||
|
|
|
@ -1,9 +1,27 @@
|
|||
package app.k9mail.core.android.common.contact
|
||||
|
||||
import app.k9mail.core.common.cache.Cache
|
||||
import app.k9mail.core.common.cache.ExpiringCache
|
||||
import app.k9mail.core.common.cache.SynchronizedCache
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
|
||||
internal val contactModule = module {
|
||||
single<Cache<EmailAddress, Contact?>>(named(CACHE_NAME)) {
|
||||
SynchronizedCache(
|
||||
delegateCache = ExpiringCache(clock = get()),
|
||||
)
|
||||
}
|
||||
factory<ContactDataSource> {
|
||||
ContentResolverContactDataSource(context = get())
|
||||
}
|
||||
factory<ContactRepository> {
|
||||
CachingContactRepository(
|
||||
cache = get(named(CACHE_NAME)),
|
||||
dataSource = get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal const val CACHE_NAME = "ContactCache"
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package app.k9mail.core.android.common.contact
|
||||
|
||||
import app.k9mail.core.common.cache.Cache
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
|
||||
interface ContactRepository {
|
||||
|
||||
fun getContactFor(emailAddress: EmailAddress): Contact?
|
||||
|
||||
fun hasContactFor(emailAddress: EmailAddress): Boolean
|
||||
|
||||
fun hasAnyContactFor(emailAddresses: List<EmailAddress>): Boolean
|
||||
}
|
||||
|
||||
interface CachingRepository {
|
||||
fun clearCache()
|
||||
}
|
||||
|
||||
internal class CachingContactRepository(
|
||||
private val cache: Cache<EmailAddress, Contact?>,
|
||||
private val dataSource: ContactDataSource,
|
||||
) : ContactRepository, CachingRepository {
|
||||
|
||||
override fun getContactFor(emailAddress: EmailAddress): Contact? {
|
||||
if (cache.hasKey(emailAddress)) {
|
||||
return cache[emailAddress]
|
||||
}
|
||||
|
||||
return dataSource.getContactFor(emailAddress).also {
|
||||
cache[emailAddress] = it
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasContactFor(emailAddress: EmailAddress): Boolean {
|
||||
if (cache.hasKey(emailAddress)) {
|
||||
return cache[emailAddress] != null
|
||||
}
|
||||
|
||||
return dataSource.hasContactFor(emailAddress)
|
||||
}
|
||||
|
||||
override fun hasAnyContactFor(emailAddresses: List<EmailAddress>): Boolean =
|
||||
emailAddresses.any { emailAddress -> hasContactFor(emailAddress) }
|
||||
|
||||
override fun clearCache() {
|
||||
cache.clear()
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ import org.robolectric.RobolectricTestRunner
|
|||
import org.robolectric.RuntimeEnvironment
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
internal class CoreCommonAndroidModuleTest {
|
||||
internal class CoreCommonAndroidModuleKtTest {
|
||||
|
||||
@Test
|
||||
fun `should have a valid di module`() {
|
|
@ -0,0 +1,143 @@
|
|||
package app.k9mail.core.android.common.contact
|
||||
|
||||
import app.k9mail.core.common.cache.InMemoryCache
|
||||
import app.k9mail.core.common.mail.EmailAddress
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isFalse
|
||||
import assertk.assertions.isNull
|
||||
import assertk.assertions.isTrue
|
||||
import kotlin.test.Test
|
||||
import org.junit.Before
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.doReturnConsecutively
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.stub
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
internal class CachingContactRepositoryTest {
|
||||
|
||||
private val dataSource = mock<ContactDataSource>()
|
||||
private val cache = InMemoryCache<EmailAddress, Contact?>()
|
||||
|
||||
private val testSubject = CachingContactRepository(cache = cache, dataSource = dataSource)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
cache.clear()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getContactFor() returns null if no contact exists`() {
|
||||
val result = testSubject.getContactFor(CONTACT_EMAIL_ADDRESS)
|
||||
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getContactFor() returns contact if it exists`() {
|
||||
dataSource.stub { on { getContactFor(CONTACT_EMAIL_ADDRESS) } doReturn CONTACT }
|
||||
|
||||
val result = testSubject.getContactFor(CONTACT_EMAIL_ADDRESS)
|
||||
|
||||
assertThat(result).isEqualTo(CONTACT)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getContactFor() caches contact`() {
|
||||
dataSource.stub {
|
||||
on { getContactFor(CONTACT_EMAIL_ADDRESS) } doReturnConsecutively listOf(
|
||||
CONTACT,
|
||||
CONTACT.copy(id = 567L),
|
||||
)
|
||||
}
|
||||
|
||||
val result1 = testSubject.getContactFor(CONTACT_EMAIL_ADDRESS)
|
||||
val result2 = testSubject.getContactFor(CONTACT_EMAIL_ADDRESS)
|
||||
|
||||
assertThat(result1).isEqualTo(result2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getContactFor() caches null`() {
|
||||
dataSource.stub {
|
||||
on { getContactFor(CONTACT_EMAIL_ADDRESS) } doReturnConsecutively listOf(
|
||||
null,
|
||||
CONTACT,
|
||||
)
|
||||
}
|
||||
|
||||
val result1 = testSubject.getContactFor(CONTACT_EMAIL_ADDRESS)
|
||||
val result2 = testSubject.getContactFor(CONTACT_EMAIL_ADDRESS)
|
||||
|
||||
assertThat(result1).isEqualTo(result2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getContactFor() returns cached contact`() {
|
||||
cache[CONTACT_EMAIL_ADDRESS] = CONTACT
|
||||
|
||||
val result = testSubject.getContactFor(CONTACT_EMAIL_ADDRESS)
|
||||
|
||||
assertThat(result).isEqualTo(CONTACT)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasContactFor() returns false if no contact exists`() {
|
||||
val result = testSubject.hasContactFor(CONTACT_EMAIL_ADDRESS)
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasContactFor() returns false if cached contact is null`() {
|
||||
cache[CONTACT_EMAIL_ADDRESS] = null
|
||||
|
||||
val result = testSubject.hasContactFor(CONTACT_EMAIL_ADDRESS)
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasContactFor() returns true if contact exists`() {
|
||||
dataSource.stub { on { hasContactFor(CONTACT_EMAIL_ADDRESS) } doReturn true }
|
||||
|
||||
val result = testSubject.hasContactFor(CONTACT_EMAIL_ADDRESS)
|
||||
|
||||
assertThat(result).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasAnyContactFor() returns false if no contact exists`() {
|
||||
val result = testSubject.hasAnyContactFor(listOf(CONTACT_EMAIL_ADDRESS))
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasAnyContactFor() returns false if list is empty`() {
|
||||
val result = testSubject.hasAnyContactFor(listOf())
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasAnyContactFor() returns true if contact exists`() {
|
||||
dataSource.stub { on { hasContactFor(CONTACT_EMAIL_ADDRESS) } doReturn true }
|
||||
|
||||
val result = testSubject.hasAnyContactFor(listOf(CONTACT_EMAIL_ADDRESS))
|
||||
|
||||
assertThat(result).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clearCache() clears cache`() {
|
||||
cache[CONTACT_EMAIL_ADDRESS] = CONTACT
|
||||
|
||||
testSubject.clearCache()
|
||||
|
||||
assertThat(cache[CONTACT_EMAIL_ADDRESS]).isNull()
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package app.k9mail.core.android.common.contact
|
||||
|
||||
import app.k9mail.core.android.common.coreCommonAndroidModule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
|
@ -9,11 +10,14 @@ import org.robolectric.RobolectricTestRunner
|
|||
import org.robolectric.RuntimeEnvironment
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
internal class ContactKoinModuleTest {
|
||||
internal class ContactKoinModuleKtTest {
|
||||
@Test
|
||||
fun `should have a valid di module`() {
|
||||
koinApplication {
|
||||
modules(coreCommonAndroidModule)
|
||||
|
||||
modules(contactModule)
|
||||
|
||||
androidContext(RuntimeEnvironment.getApplication())
|
||||
checkModules()
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package app.k9mail.core.common.cache
|
||||
|
||||
interface Cache<KEY : Any, VALUE : Any> {
|
||||
interface Cache<KEY : Any, VALUE : Any?> {
|
||||
|
||||
operator fun get(key: KEY): VALUE?
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package app.k9mail.core.common.cache
|
|||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
|
||||
class ExpiringCache<KEY : Any, VALUE : Any>(
|
||||
class ExpiringCache<KEY : Any, VALUE : Any?>(
|
||||
private val clock: Clock,
|
||||
private val delegateCache: Cache<KEY, VALUE> = InMemoryCache(),
|
||||
private var lastClearTime: Instant = clock.now(),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package app.k9mail.core.common.cache
|
||||
|
||||
class InMemoryCache<KEY : Any, VALUE : Any>(
|
||||
class InMemoryCache<KEY : Any, VALUE : Any?>(
|
||||
private val cache: MutableMap<KEY, VALUE> = mutableMapOf(),
|
||||
) : Cache<KEY, VALUE> {
|
||||
override fun get(key: KEY): VALUE? {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package app.k9mail.core.common.cache
|
||||
|
||||
class SynchronizedCache<KEY : Any, VALUE : Any>(
|
||||
class SynchronizedCache<KEY : Any, VALUE : Any?>(
|
||||
private val delegateCache: Cache<KEY, VALUE>,
|
||||
) : Cache<KEY, VALUE> {
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import org.junit.Test
|
|||
import org.koin.dsl.koinApplication
|
||||
import org.koin.test.check.checkModules
|
||||
|
||||
internal class CoreCommonModuleTest {
|
||||
internal class CoreCommonModuleKtTest {
|
||||
|
||||
@Test
|
||||
fun `should have a valid di module`() {
|
|
@ -10,7 +10,7 @@ import kotlin.test.Test
|
|||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
|
||||
data class CacheTestData<KEY : Any, VALUE : Any>(
|
||||
data class CacheTestData<KEY : Any, VALUE : Any?>(
|
||||
val name: String,
|
||||
val createCache: () -> Cache<KEY, VALUE>,
|
||||
) {
|
||||
|
@ -18,14 +18,14 @@ data class CacheTestData<KEY : Any, VALUE : Any>(
|
|||
}
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class CacheTest(data: CacheTestData<Any, Any>) {
|
||||
class CacheTest(data: CacheTestData<Any, Any?>) {
|
||||
|
||||
private val testSubject = data.createCache()
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
fun data(): Collection<CacheTestData<Any, Any>> {
|
||||
fun data(): Collection<CacheTestData<Any, Any?>> {
|
||||
return listOf(
|
||||
CacheTestData("InMemoryCache") { InMemoryCache() },
|
||||
CacheTestData("ExpiringCache") { ExpiringCache(TestClock(), InMemoryCache()) },
|
||||
|
|
Loading…
Reference in a new issue