Move CursorExtensions to :core:android:common module

This commit is contained in:
Wolf Montwé 2023-03-09 12:07:45 +01:00
parent 3974688b63
commit 3ed19f0011
No known key found for this signature in database
GPG key ID: 6D45B21512ACBF72
18 changed files with 201 additions and 85 deletions

View file

@ -1,41 +0,0 @@
package com.fsck.k9.helper
import android.database.Cursor
fun <T> Cursor.map(block: (Cursor) -> T): List<T> {
return List(count) { index ->
moveToPosition(index)
block(this)
}
}
fun Cursor.getStringOrNull(columnName: String): String? {
val columnIndex = getColumnIndex(columnName)
return if (isNull(columnIndex)) null else getString(columnIndex)
}
fun Cursor.getIntOrNull(columnName: String): Int? {
val columnIndex = getColumnIndex(columnName)
return if (isNull(columnIndex)) null else getInt(columnIndex)
}
fun Cursor.getLongOrNull(columnName: String): Long? {
val columnIndex = getColumnIndex(columnName)
return if (isNull(columnIndex)) null else getLong(columnIndex)
}
fun Cursor.getStringOrThrow(columnName: String): String {
return getStringOrNull(columnName) ?: error("Column $columnName must not be null")
}
fun Cursor.getIntOrThrow(columnName: String): Int {
return getIntOrNull(columnName) ?: error("Column $columnName must not be null")
}
fun Cursor.getLongOrThrow(columnName: String): Long {
return getLongOrNull(columnName) ?: error("Column $columnName must not be null")
}
fun Cursor.getBoolean(columnIndex: Int): Boolean {
return getString(columnIndex).toBoolean()
}

View file

@ -1,10 +1,10 @@
package com.fsck.k9.mailstore
import android.content.ContentValues
import com.fsck.k9.helper.getIntOrThrow
import com.fsck.k9.helper.getLongOrThrow
import com.fsck.k9.helper.getStringOrNull
import com.fsck.k9.helper.getStringOrThrow
import app.k9mail.core.android.common.database.getIntOrThrow
import app.k9mail.core.android.common.database.getLongOrThrow
import app.k9mail.core.android.common.database.getStringOrNull
import app.k9mail.core.android.common.database.getStringOrThrow
import kotlinx.datetime.Clock
class OutboxStateRepository(

View file

@ -2,10 +2,10 @@ package com.fsck.k9.storage.messages
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import app.k9mail.core.android.common.database.getIntOrNull
import app.k9mail.core.android.common.database.getLongOrNull
import app.k9mail.core.android.common.database.getStringOrNull
import com.fsck.k9.K9
import com.fsck.k9.helper.getIntOrNull
import com.fsck.k9.helper.getLongOrNull
import com.fsck.k9.helper.getStringOrNull
import com.fsck.k9.mailstore.LockableDatabase
import java.util.UUID
import timber.log.Timber

View file

@ -2,8 +2,8 @@ package com.fsck.k9.storage.messages
import android.database.Cursor
import androidx.core.database.getLongOrNull
import app.k9mail.core.android.common.database.map
import com.fsck.k9.Account.FolderMode
import com.fsck.k9.helper.map
import com.fsck.k9.mail.FolderClass
import com.fsck.k9.mail.FolderType
import com.fsck.k9.mailstore.FolderDetailsAccessor

View file

@ -2,6 +2,7 @@ package com.fsck.k9.storage.migrations
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import app.k9mail.core.android.common.database.map
import com.fsck.k9.controller.MessagingControllerCommands.PendingAppend
import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand
import com.fsck.k9.controller.MessagingControllerCommands.PendingDelete
@ -11,7 +12,6 @@ import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveAndMarkAsRe
import com.fsck.k9.controller.MessagingControllerCommands.PendingMoveOrCopy
import com.fsck.k9.controller.MessagingControllerCommands.PendingSetFlag
import com.fsck.k9.controller.PendingCommandSerializer
import com.fsck.k9.helper.map
import com.squareup.moshi.Moshi
import timber.log.Timber

View file

@ -2,8 +2,8 @@ package com.fsck.k9.storage.migrations
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import app.k9mail.core.android.common.database.map
import com.fsck.k9.Account
import com.fsck.k9.helper.map
import com.fsck.k9.mailstore.MigrationsHelper
import com.fsck.k9.preferences.Protocols
import timber.log.Timber

View file

@ -3,7 +3,7 @@ package com.fsck.k9.storage.migrations
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import androidx.core.database.getLongOrNull
import com.fsck.k9.helper.map
import app.k9mail.core.android.common.database.map
import com.fsck.k9.mailstore.MigrationsHelper
private const val EXTRA_HIGHEST_KNOWN_UID = "imapHighestKnownUid"

View file

@ -3,7 +3,7 @@ package com.fsck.k9.storage.migrations
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import androidx.core.database.getStringOrNull
import com.fsck.k9.helper.map
import app.k9mail.core.android.common.database.map
/**
* Write the address fields to use ASCII 1 instead of ASCII 0 as separator.

View file

@ -2,10 +2,10 @@ package com.fsck.k9.storage.messages
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import com.fsck.k9.helper.getIntOrNull
import com.fsck.k9.helper.getLongOrNull
import com.fsck.k9.helper.getStringOrNull
import com.fsck.k9.helper.map
import app.k9mail.core.android.common.database.getIntOrNull
import app.k9mail.core.android.common.database.getLongOrNull
import app.k9mail.core.android.common.database.getStringOrNull
import app.k9mail.core.android.common.database.map
fun SQLiteDatabase.createFolder(
name: String = "irrelevant",

View file

@ -2,9 +2,9 @@ package com.fsck.k9.storage.messages
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import com.fsck.k9.helper.getLongOrNull
import com.fsck.k9.helper.getStringOrNull
import com.fsck.k9.helper.map
import app.k9mail.core.android.common.database.getLongOrNull
import app.k9mail.core.android.common.database.getStringOrNull
import app.k9mail.core.android.common.database.map
fun SQLiteDatabase.createExtraValue(
name: String = "irrelevant",

View file

@ -2,10 +2,10 @@ package com.fsck.k9.storage.messages
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import com.fsck.k9.helper.getIntOrNull
import com.fsck.k9.helper.getLongOrNull
import com.fsck.k9.helper.getStringOrNull
import com.fsck.k9.helper.map
import app.k9mail.core.android.common.database.getIntOrNull
import app.k9mail.core.android.common.database.getLongOrNull
import app.k9mail.core.android.common.database.getStringOrNull
import app.k9mail.core.android.common.database.map
import com.fsck.k9.mailstore.DatabasePreviewType
import com.fsck.k9.mailstore.LockableDatabase
import com.fsck.k9.mailstore.MigrationsHelper

View file

@ -3,10 +3,10 @@ package com.fsck.k9.storage.messages
import android.content.ContentValues
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import com.fsck.k9.helper.getIntOrNull
import com.fsck.k9.helper.getLongOrNull
import com.fsck.k9.helper.getStringOrNull
import com.fsck.k9.helper.map
import app.k9mail.core.android.common.database.getIntOrNull
import app.k9mail.core.android.common.database.getLongOrNull
import app.k9mail.core.android.common.database.getStringOrNull
import app.k9mail.core.android.common.database.map
fun SQLiteDatabase.createMessagePart(
type: Int = MessagePartType.UNKNOWN,

View file

@ -2,8 +2,8 @@ package com.fsck.k9.storage.messages
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import com.fsck.k9.helper.getLongOrNull
import com.fsck.k9.helper.map
import app.k9mail.core.android.common.database.getLongOrNull
import app.k9mail.core.android.common.database.map
fun SQLiteDatabase.createThread(
messageId: Long,

View file

@ -2,9 +2,9 @@ package com.fsck.k9.storage.notifications
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import com.fsck.k9.helper.getIntOrNull
import com.fsck.k9.helper.getLongOrNull
import com.fsck.k9.helper.map
import app.k9mail.core.android.common.database.getIntOrNull
import app.k9mail.core.android.common.database.getLongOrNull
import app.k9mail.core.android.common.database.map
fun SQLiteDatabase.createNotification(
messageId: Long,

View file

@ -9,8 +9,8 @@ import android.net.Uri
import android.provider.ContactsContract
import androidx.core.content.ContextCompat
import app.k9mail.core.android.common.database.EmptyCursor
import app.k9mail.core.android.common.database.getLongValue
import app.k9mail.core.android.common.database.getStringValue
import app.k9mail.core.android.common.database.getLongOrThrow
import app.k9mail.core.android.common.database.getStringOrNull
import app.k9mail.core.common.mail.EmailAddress
interface ContactDataSource {
@ -28,13 +28,13 @@ internal class ContentResolverContactDataSource(
override fun getContactFor(emailAddress: EmailAddress): Contact? {
getCursorFor(emailAddress).use { cursor ->
if (cursor.moveToFirst()) {
val contactId = cursor.getLongValue(ContactsContract.CommonDataKinds.Email._ID)
val lookupKey = cursor.getStringValue(ContactsContract.Contacts.LOOKUP_KEY)
val contactId = cursor.getLongOrThrow(ContactsContract.CommonDataKinds.Email._ID)
val lookupKey = cursor.getStringOrNull(ContactsContract.Contacts.LOOKUP_KEY)
val uri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey)
val name = cursor.getStringValue(ContactsContract.CommonDataKinds.Identity.DISPLAY_NAME)
val name = cursor.getStringOrNull(ContactsContract.CommonDataKinds.Identity.DISPLAY_NAME)
val photoUri = cursor.getStringValue(ContactsContract.CommonDataKinds.Photo.PHOTO_URI)
val photoUri = cursor.getStringOrNull(ContactsContract.CommonDataKinds.Photo.PHOTO_URI)
?.let { photoUriString -> Uri.parse(photoUriString) }
return Contact(

View file

@ -2,12 +2,36 @@ package app.k9mail.core.android.common.database
import android.database.Cursor
fun Cursor.getStringValue(key: String): String? {
val columnIndex = getColumnIndex(key)
return getString(columnIndex)
fun <T> Cursor.map(block: (Cursor) -> T): List<T> {
return List(count) { index ->
moveToPosition(index)
block(this)
}
}
fun Cursor.getLongValue(key: String): Long {
val columnIndex = getColumnIndex(key)
return getLong(columnIndex)
fun Cursor.getStringOrNull(columnName: String): String? {
val columnIndex = getColumnIndex(columnName)
return if (isNull(columnIndex)) null else getString(columnIndex)
}
fun Cursor.getIntOrNull(columnName: String): Int? {
val columnIndex = getColumnIndex(columnName)
return if (isNull(columnIndex)) null else getInt(columnIndex)
}
fun Cursor.getLongOrNull(columnName: String): Long? {
val columnIndex = getColumnIndex(columnName)
return if (isNull(columnIndex)) null else getLong(columnIndex)
}
fun Cursor.getStringOrThrow(columnName: String): String {
return getStringOrNull(columnName) ?: error("Column $columnName must not be null")
}
fun Cursor.getIntOrThrow(columnName: String): Int {
return getIntOrNull(columnName) ?: error("Column $columnName must not be null")
}
fun Cursor.getLongOrThrow(columnName: String): Long {
return getLongOrNull(columnName) ?: error("Column $columnName must not be null")
}

View file

@ -0,0 +1,100 @@
package app.k9mail.core.android.common.database
import android.database.Cursor
import android.database.MatrixCursor
import assertk.assertThat
import assertk.assertions.hasMessage
import assertk.assertions.isEqualTo
import assertk.assertions.isFailure
import assertk.assertions.isNull
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.ParameterizedRobolectricTestRunner
data class CursorExtensionsAccessTestData<T : Any>(
val name: String,
val value: T,
val access: (Cursor, String) -> T?,
val throwingAccess: (Cursor, String) -> T,
) {
override fun toString(): String = name
}
@RunWith(ParameterizedRobolectricTestRunner::class)
class CursorExtensionsKtAccessTest(data: CursorExtensionsAccessTestData<Any>) {
private val testValue = data.value
private val testAction = data.access
private val testThrowingAction = data.throwingAccess
@Test
fun `testAction should return null if column is null`() {
val cursor = MatrixCursor(arrayOf("column")).apply {
addRow(arrayOf(null))
}
val result = cursor.map { testAction(it, "column") }
assertThat(result[0]).isNull()
}
@Test
fun `testAction should return value if column is not null`() {
val cursor = MatrixCursor(arrayOf("column")).apply {
addRow(arrayOf(testValue))
}
val result = cursor.map { testAction(it, "column") }
assertThat(result[0]).isEqualTo(testValue)
}
@Test
fun `testThrowingAction should throw if column is null`() {
val cursor = MatrixCursor(arrayOf("column")).apply {
addRow(arrayOf(null))
}
assertThat {
cursor.map { testThrowingAction(it, "column") }
}.isFailure().hasMessage("Column column must not be null")
}
@Test
fun `testThrowingAction should return value if column is not null`() {
val cursor = MatrixCursor(arrayOf("column")).apply {
addRow(arrayOf(testValue))
}
val result = cursor.map { testThrowingAction(it, "column") }
assertThat(result[0]).isEqualTo(testValue)
}
companion object {
@JvmStatic
@ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
fun data(): Collection<CursorExtensionsAccessTestData<Any>> {
return listOf(
CursorExtensionsAccessTestData(
name = "getString",
value = "value",
access = { cursor, column -> cursor.getStringOrNull(column) },
throwingAccess = { cursor, column -> cursor.getStringOrThrow(column) },
),
CursorExtensionsAccessTestData(
name = "getInt",
value = Int.MAX_VALUE,
access = { cursor, column -> cursor.getIntOrNull(column) },
throwingAccess = { cursor, column -> cursor.getIntOrThrow(column) },
),
CursorExtensionsAccessTestData(
name = "getLong",
value = Long.MAX_VALUE,
access = { cursor, column -> cursor.getLongOrNull(column) },
throwingAccess = { cursor, column -> cursor.getLongOrThrow(column) },
),
)
}
}
}

View file

@ -0,0 +1,33 @@
package app.k9mail.core.android.common.database
import android.database.MatrixCursor
import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class CursorExtensionsKtTest {
@Test
fun `map should return an empty list if cursor is empty`() {
val cursor = MatrixCursor(arrayOf("column"))
val result = cursor.map { it.getStringOrNull("column") }
assertThat(result).isEqualTo(emptyList<String>())
}
@Test
fun `map should return a list of mapped values`() {
val cursor = MatrixCursor(arrayOf("column")).apply {
addRow(arrayOf("value1"))
addRow(arrayOf("value2"))
}
val result = cursor.map { it.getStringOrNull("column") }
assertThat(result).isEqualTo(listOf("value1", "value2"))
}
}