Initial commit

This commit is contained in:
2024-07-24 13:17:25 +02:00
commit 0e15203725
197 changed files with 5944 additions and 0 deletions

1
data/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

34
data/build.gradle.kts Normal file
View File

@ -0,0 +1,34 @@
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
alias(libs.plugins.convention.android.library)
alias(libs.plugins.kotlin.serialization)
}
android {
namespace = "dev.adriankuta.pixabay.data"
buildFeatures {
buildConfig = true
}
defaultConfig {
buildConfigField("String", "PIXABAY_API_KEY", "\"<REPLACE_WITH_PIXABAY_API_KEY>\"")
}
}
dependencies {
implementation(libs.retrofit)
implementation(platform(libs.okhttp.bom))
implementation(libs.okhttp)
implementation(libs.logging.interceptor)
implementation(libs.androidx.paging.compose)
implementation(libs.kotlinx.serialization.json)
implementation(libs.retrofit2.kotlinx.serialization.converter)
implementation(libs.androidx.room.runtime)
ksp(libs.androidx.room.compiler)
implementation(libs.androidx.room.ktx)
implementation(libs.androidx.room.paging)
}

View File

@ -0,0 +1,52 @@
package dev.adriankuta.pixabay.data.di
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dev.adriankuta.pixabay.data.BuildConfig
import dev.adriankuta.pixabay.data.network.PixabayService
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
@Module
internal class NetworkModule {
@Singleton
@Provides
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
val logging = HttpLoggingInterceptor()
logging.level =
if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
return logging
}
@Singleton
@Provides
fun provideOkHttpClient(
httpLoggingInterceptor: HttpLoggingInterceptor
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.build()
}
@Singleton
@Provides
fun providePixabayApi(
okHttpClient: OkHttpClient
): PixabayService {
val networkJson = Json { ignoreUnknownKeys = true }
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://pixabay.com/")
.addConverterFactory(networkJson.asConverterFactory("application/json".toMediaType()))
.build()
.create(PixabayService::class.java)
}
}

View File

@ -0,0 +1,18 @@
package dev.adriankuta.pixabay.data.di
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dev.adriankuta.pixabay.data.repository.ImageRepository
import dev.adriankuta.pixabay.data.repository.PixabayImageRepository
@InstallIn(SingletonComponent::class)
@Module
internal abstract class NetworkRepositoresModule {
@Binds
abstract fun bindPhotoRepository(pixabayImageRepository: PixabayImageRepository): ImageRepository
}

View File

@ -0,0 +1,31 @@
package dev.adriankuta.pixabay.data.di
import android.content.Context
import androidx.room.Room
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import dev.adriankuta.pixabay.data.room.AppDatabase
import dev.adriankuta.pixabay.data.room.dao.PixabayImagesDao
@InstallIn(SingletonComponent::class)
@Module
internal class PersistanceModule {
@Provides
fun provideRoomDb(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"pixabay.db"
).build()
}
@Provides
fun providePixabayImagesDao(appDatabase: AppDatabase) = appDatabase.imagesDao()
@Provides
fun provideRemoteKeysDao(appDatabase: AppDatabase) = appDatabase.remoteKeysDao()
}

View File

@ -0,0 +1,28 @@
package dev.adriankuta.pixabay.data.dto.response
import kotlinx.serialization.Serializable
@Serializable
internal data class PixabayImageResponse(
val id: Int,
val pageURL: String,
val type: String,
val tags: String,
val previewURL: String,
val previewWidth: Int,
val previewHeight: Int,
val webformatURL: String,
val webformatWidth: Int,
val webformatHeight: Int,
val largeImageURL: String,
val imageWidth: Int,
val imageHeight: Int,
val imageSize: Int,
val views: Int,
val downloads: Int,
val likes: Int,
val comments: Int,
val user_id: Int,
val user: String,
val userImageURL: String
)

View File

@ -0,0 +1,8 @@
package dev.adriankuta.pixabay.data.dto.response
import kotlinx.serialization.Serializable
@Serializable
internal data class SearchPixabayImagesResponse(
val hits: List<PixabayImageResponse>
)

View File

@ -0,0 +1,78 @@
package dev.adriankuta.pixabay.data.model
import dev.adriankuta.pixabay.data.dto.response.PixabayImageResponse
import dev.adriankuta.pixabay.data.room.entity.PixabayImageEntity
data class PixabayImage(
val id: Int,
val pageURL: String,
val type: String,
val tags: String,
val previewURL: String,
val previewWidth: Int,
val previewHeight: Int,
val webformatURL: String,
val webformatWidth: Int,
val webformatHeight: Int,
val largeImageURL: String,
val imageWidth: Int,
val imageHeight: Int,
val imageSize: Int,
val views: Int,
val downloads: Int,
val likes: Int,
val comments: Int,
val user_id: Int,
val user: String,
val userImageURL: String
) {
internal constructor(entity: PixabayImageEntity) :
this(
id = entity.id,
pageURL = entity.pageURL,
type = entity.type,
tags = entity.tags,
previewURL = entity.previewURL,
previewWidth = entity.previewWidth,
previewHeight = entity.previewHeight,
webformatURL = entity.webformatURL,
webformatWidth = entity.webformatWidth,
webformatHeight = entity.webformatHeight,
largeImageURL = entity.largeImageURL,
imageWidth = entity.imageWidth,
imageHeight = entity.imageHeight,
imageSize = entity.imageSize,
views = entity.views,
downloads = entity.downloads,
likes = entity.likes,
comments = entity.comments,
user_id = entity.userId,
user = entity.user,
userImageURL = entity.userImageURL
)
internal constructor(entity: PixabayImageResponse) :
this(
id = entity.id,
pageURL = entity.pageURL,
type = entity.type,
tags = entity.tags,
previewURL = entity.previewURL,
previewWidth = entity.previewWidth,
previewHeight = entity.previewHeight,
webformatURL = entity.webformatURL,
webformatWidth = entity.webformatWidth,
webformatHeight = entity.webformatHeight,
largeImageURL = entity.largeImageURL,
imageWidth = entity.imageWidth,
imageHeight = entity.imageHeight,
imageSize = entity.imageSize,
views = entity.views,
downloads = entity.downloads,
likes = entity.likes,
comments = entity.comments,
user_id = entity.user_id,
user = entity.user,
userImageURL = entity.userImageURL
)
}

View File

@ -0,0 +1,23 @@
package dev.adriankuta.pixabay.data.network
import dev.adriankuta.pixabay.data.BuildConfig
import dev.adriankuta.pixabay.data.dto.response.SearchPixabayImagesResponse
import retrofit2.http.GET
import retrofit2.http.Query
internal interface PixabayService {
@GET("api")
suspend fun searchImages(
@Query("q") query: String,
@Query("page") page: Int,
@Query("per_page") pageSize: Int,
@Query("key") key: String = BuildConfig.PIXABAY_API_KEY
): SearchPixabayImagesResponse
@GET("api")
suspend fun searchImageById(
@Query("id") query: String,
@Query("key") key: String = BuildConfig.PIXABAY_API_KEY
): SearchPixabayImagesResponse
}

View File

@ -0,0 +1,52 @@
package dev.adriankuta.pixabay.data.paging
import androidx.paging.PagingSource
import androidx.paging.PagingState
import dev.adriankuta.pixabay.data.model.PixabayImage
import dev.adriankuta.pixabay.data.network.PixabayService
import dev.adriankuta.pixabay.data.repository.PixabayImageRepository.Companion.NETWORK_PAGE_SIZE
import retrofit2.HttpException
import java.io.IOException
private const val STARTING_PAGE_INDEX = 1
internal class PixabayPagingSource(
private val pixabayService: PixabayService,
private val query: String
) : PagingSource<Int, PixabayImage>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, PixabayImage> {
val position = params.key ?: STARTING_PAGE_INDEX
return try {
val response = pixabayService.searchImages(query, position, params.loadSize)
val results = response.hits.map { PixabayImage(it) }
val nextKey = if (results.isEmpty()) {
null
} else {
// initial load size = 3 * NETWORK_PAGE_SIZE
// ensure we're not requesting duplicating items, at the 2nd request
position + (params.loadSize / NETWORK_PAGE_SIZE)
}
LoadResult.Page(
data = results,
prevKey = if (position == STARTING_PAGE_INDEX) null else position - 1,
nextKey = nextKey
)
} catch (exception: IOException) {
return LoadResult.Error(exception)
} catch (exception: HttpException) {
return LoadResult.Error(exception)
}
}
// The refresh key is used for subsequent refresh calls to PagingSource.load after the initial load
override fun getRefreshKey(state: PagingState<Int, PixabayImage>): Int? {
// We need to get the previous key (or next key if previous is null) of the page
// that was closest to the most recently accessed index.
// Anchor position is the most recently accessed index
return state.anchorPosition?.let { anchorPosition ->
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
}
}
}

View File

@ -0,0 +1,111 @@
@file:OptIn(ExperimentalPagingApi::class)
package dev.adriankuta.pixabay.data.paging
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import dev.adriankuta.pixabay.data.model.PixabayImage
import dev.adriankuta.pixabay.data.network.PixabayService
import dev.adriankuta.pixabay.data.room.AppDatabase
import dev.adriankuta.pixabay.data.room.entity.PixabayImageEntity
import dev.adriankuta.pixabay.data.room.entity.RemoteKeys
import retrofit2.HttpException
import java.io.IOException
private const val STARTING_PAGE_INDEX = 1
internal class PixabayRemoteMediator(
private val query: String,
private val database: AppDatabase,
private val pixabayService: PixabayService
) : RemoteMediator<Int, PixabayImageEntity>() {
private val imagesDao = database.imagesDao()
private val remoteKeysDao = database.remoteKeysDao()
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, PixabayImageEntity>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: STARTING_PAGE_INDEX
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
// If remoteKeys is null, that means the refresh result is not in the database yet.
val prevKey = remoteKeys?.prevKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
prevKey
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
// If remoteKeys is null, that means the refresh result is not in the database yet.
// We can return Success with endOfPaginationReached = false because Paging
// will call this method again if RemoteKeys becomes non-null.
// If remoteKeys is NOT NULL but its nextKey is null, that means we've reached
// the end of pagination for append.
val nextKey = remoteKeys?.nextKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
nextKey
}
}
try {
val response = pixabayService.searchImages(query, page, state.config.pageSize)
val imageEntities = response.hits.map { PixabayImageEntity(it, query) }
val endOfPaginationReached = response.hits.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
imagesDao.clearAll()
remoteKeysDao.clearRemoteKeys()
}
val prevKey = if (page == STARTING_PAGE_INDEX) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = imageEntities.map {
RemoteKeys(id = it.id, prevKey = prevKey, nextKey = nextKey)
}
remoteKeysDao.insertAll(keys)
imagesDao.insertAll(imageEntities)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, PixabayImageEntity>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { pixabayImage ->
remoteKeysDao.remoteKeysImageId(pixabayImage.id)
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, PixabayImageEntity>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { pixabayImage ->
remoteKeysDao.remoteKeysImageId(pixabayImage.id)
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, PixabayImageEntity>
): RemoteKeys? {
// The paging library is trying to load data after the anchor position
// Get the item closest to the anchor position
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.id?.let { imageId ->
remoteKeysDao.remoteKeysImageId(imageId)
}
}
}
}

View File

@ -0,0 +1,14 @@
package dev.adriankuta.pixabay.data.repository
import androidx.paging.PagingData
import dev.adriankuta.pixabay.data.model.PixabayImage
import kotlinx.coroutines.flow.Flow
interface ImageRepository {
suspend fun searchImageById(id: Int): PixabayImage
suspend fun searchImages(query: String, page: Int, pageSize: Int): List<PixabayImage>
fun getSearchResultStream(query: String): Flow<PagingData<PixabayImage>>
}

View File

@ -0,0 +1,80 @@
@file:OptIn(ExperimentalPagingApi::class)
package dev.adriankuta.pixabay.data.repository
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.map
import dev.adriankuta.pixabay.data.model.PixabayImage
import dev.adriankuta.pixabay.data.network.PixabayService
import dev.adriankuta.pixabay.data.paging.PixabayPagingSource
import dev.adriankuta.pixabay.data.paging.PixabayRemoteMediator
import dev.adriankuta.pixabay.data.room.AppDatabase
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import timber.log.Timber
import javax.inject.Inject
internal class PixabayImageRepository @Inject constructor(
private val database: AppDatabase,
private val pixabayService: PixabayService
) : ImageRepository {
override suspend fun searchImageById(id: Int): PixabayImage {
return runCatching {
pixabayService.searchImageById(id.toString())
}.mapCatching { response ->
PixabayImage(response.hits.first())
}.onFailure { e ->
Timber.e(e)
}.getOrThrow()
}
override suspend fun searchImages(query: String, page: Int, pageSize: Int): List<PixabayImage> {
return runCatching {
pixabayService.searchImages(query, page, pageSize)
}.mapCatching { response ->
response.hits.map { PixabayImage(it) }
}.onFailure { e ->
Timber.e(e)
}.getOrThrow()
}
override fun getSearchResultStream(query: String): Flow<PagingData<PixabayImage>> {
return if (USE_CACHE_PAGER)
getOnlinePagerWithCache(query)
else
getOnlinePager(query)
}
private fun getOnlinePager(query: String) = Pager(
config = PagingConfig(
pageSize = NETWORK_PAGE_SIZE,
enablePlaceholders = false
),
pagingSourceFactory = { PixabayPagingSource(pixabayService, query) }
).flow
private fun getOnlinePagerWithCache(query: String) = Pager(
config = PagingConfig(
pageSize = NETWORK_PAGE_SIZE,
enablePlaceholders = false
),
remoteMediator = PixabayRemoteMediator(
query,
database,
pixabayService
),
pagingSourceFactory = { database.imagesDao().imagesByQuery(query) }
).flow
.map { pagingSource ->
pagingSource.map { PixabayImage(it) }
}
companion object {
const val NETWORK_PAGE_SIZE = 30
const val USE_CACHE_PAGER = false
}
}

View File

@ -0,0 +1,19 @@
package dev.adriankuta.pixabay.data.room
import androidx.room.Database
import androidx.room.RoomDatabase
import dev.adriankuta.pixabay.data.room.dao.PixabayImagesDao
import dev.adriankuta.pixabay.data.room.dao.RemoteKeysDao
import dev.adriankuta.pixabay.data.room.entity.PixabayImageEntity
import dev.adriankuta.pixabay.data.room.entity.RemoteKeys
@Database(
entities = [
PixabayImageEntity::class,
RemoteKeys::class
], version = 1
)
internal abstract class AppDatabase : RoomDatabase() {
abstract fun imagesDao(): PixabayImagesDao
abstract fun remoteKeysDao(): RemoteKeysDao
}

View File

@ -0,0 +1,25 @@
package dev.adriankuta.pixabay.data.room.dao
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import dev.adriankuta.pixabay.data.room.entity.PixabayImageEntity
import dev.adriankuta.pixabay.data.room.entity.PixabayImageEntity.Companion.COLUMN_QUERY_USED
import dev.adriankuta.pixabay.data.room.entity.PixabayImageEntity.Companion.TABLE_NAME
@Dao
internal interface PixabayImagesDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(pixabayImages: List<PixabayImageEntity>)
@Query("SELECT * FROM $TABLE_NAME WHERE $COLUMN_QUERY_USED LIKE :query ORDER BY entry_id ASC")
fun imagesByQuery(query: String): PagingSource<Int, PixabayImageEntity>
@Query("DELETE FROM $TABLE_NAME")
suspend fun clearAll()
}

View File

@ -0,0 +1,22 @@
package dev.adriankuta.pixabay.data.room.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import dev.adriankuta.pixabay.data.room.entity.RemoteKeys
import dev.adriankuta.pixabay.data.room.entity.RemoteKeys.Companion.COLUMN_ID
import dev.adriankuta.pixabay.data.room.entity.RemoteKeys.Companion.TABLE_NAME
@Dao
internal interface RemoteKeysDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(remoteKey: List<RemoteKeys>)
@Query("SELECT * FROM $TABLE_NAME WHERE $COLUMN_ID = :id")
suspend fun remoteKeysImageId(id: Int): RemoteKeys?
@Query("DELETE FROM $TABLE_NAME")
suspend fun clearRemoteKeys()
}

View File

@ -0,0 +1,89 @@
package dev.adriankuta.pixabay.data.room.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import dev.adriankuta.pixabay.data.dto.response.PixabayImageResponse
import dev.adriankuta.pixabay.data.room.entity.PixabayImageEntity.Companion.TABLE_NAME
@Entity(tableName = TABLE_NAME)
internal data class PixabayImageEntity(
@PrimaryKey(autoGenerate = true)
@ColumnInfo("entry_id") val entryId: Int = 0,
@ColumnInfo(COLUMN_ID) val id: Int,
@ColumnInfo(COLUMN_QUERY_USED) val queryUsed: String,
@ColumnInfo(COLUMN_PAGE_URL) val pageURL: String,
@ColumnInfo(COLUMN_TYPE) val type: String,
@ColumnInfo(COLUMN_TAGS) val tags: String,
@ColumnInfo(COLUMN_PREVIEW_URL) val previewURL: String,
@ColumnInfo(COLUMN_PREVIEW_WIDTH) val previewWidth: Int,
@ColumnInfo(COLUMN_PREVIEW_HEIGHT) val previewHeight: Int,
@ColumnInfo(COLUMN_WEB_FORMAT_URL) val webformatURL: String,
@ColumnInfo(COLUMN_WEB_FORMAT_WIDTH) val webformatWidth: Int,
@ColumnInfo(COLUMN_WEB_FORMAT_HEIGHT) val webformatHeight: Int,
@ColumnInfo(COLUMN_LARGE_IMAGE_URL) val largeImageURL: String,
@ColumnInfo(COLUMN_IMAGE_WIDTH) val imageWidth: Int,
@ColumnInfo(COLUMN_IMAGE_HEIGHT) val imageHeight: Int,
@ColumnInfo(COLUMN_IMAGE_SIZE) val imageSize: Int,
@ColumnInfo(COLUMN_VIEWS) val views: Int,
@ColumnInfo(COLUMN_DOWNLOADS) val downloads: Int,
@ColumnInfo(COLUMN_LIKES) val likes: Int,
@ColumnInfo(COLUMN_COMMENTS) val comments: Int,
@ColumnInfo(COLUMN_USER_ID) val userId: Int,
@ColumnInfo(COLUMN_USER) val user: String,
@ColumnInfo(COLUMN_USER_IMAGE_URL) val userImageURL: String,
) {
constructor(pixabayImageResponse: PixabayImageResponse, queryUsed: String) :
this(
id = pixabayImageResponse.id,
queryUsed = queryUsed,
pageURL = pixabayImageResponse.pageURL,
type = pixabayImageResponse.type,
tags = pixabayImageResponse.tags,
previewURL = pixabayImageResponse.previewURL,
previewWidth = pixabayImageResponse.previewWidth,
previewHeight = pixabayImageResponse.previewHeight,
webformatURL = pixabayImageResponse.webformatURL,
webformatWidth = pixabayImageResponse.webformatWidth,
webformatHeight = pixabayImageResponse.webformatHeight,
largeImageURL = pixabayImageResponse.largeImageURL,
imageWidth = pixabayImageResponse.imageWidth,
imageHeight = pixabayImageResponse.imageHeight,
imageSize = pixabayImageResponse.imageSize,
views = pixabayImageResponse.views,
downloads = pixabayImageResponse.downloads,
likes = pixabayImageResponse.likes,
comments = pixabayImageResponse.comments,
userId = pixabayImageResponse.user_id,
user = pixabayImageResponse.user,
userImageURL = pixabayImageResponse.userImageURL
)
companion object {
const val TABLE_NAME = "pixabay_images"
const val COLUMN_ID = "id"
const val COLUMN_QUERY_USED = "query_used"
const val COLUMN_PAGE_URL = "page_url"
const val COLUMN_TYPE = "type"
const val COLUMN_TAGS = "tags"
const val COLUMN_PREVIEW_URL = "preview_url"
const val COLUMN_PREVIEW_WIDTH = "preview_width"
const val COLUMN_PREVIEW_HEIGHT = "preview_height"
const val COLUMN_WEB_FORMAT_URL = "web_format_url"
const val COLUMN_WEB_FORMAT_WIDTH = "web_format_width"
const val COLUMN_WEB_FORMAT_HEIGHT = "web_format_height"
const val COLUMN_LARGE_IMAGE_URL = "large_image_url"
const val COLUMN_IMAGE_WIDTH = "image_width"
const val COLUMN_IMAGE_HEIGHT = "image_height"
const val COLUMN_IMAGE_SIZE = "image_size"
const val COLUMN_VIEWS = "views"
const val COLUMN_DOWNLOADS = "downloads"
const val COLUMN_LIKES = "likes"
const val COLUMN_COMMENTS = "comments"
const val COLUMN_USER_ID = "user_id"
const val COLUMN_USER = "user"
const val COLUMN_USER_IMAGE_URL = "user_image_url"
}
}

View File

@ -0,0 +1,24 @@
package dev.adriankuta.pixabay.data.room.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import dev.adriankuta.pixabay.data.room.entity.RemoteKeys.Companion.TABLE_NAME
@Entity(tableName = TABLE_NAME)
internal data class RemoteKeys(
@PrimaryKey
@ColumnInfo(name = COLUMN_ID)
val id: Int,
@ColumnInfo(name = COLUMN_PREV_KEY)
val prevKey: Int?,
@ColumnInfo(name = COLUMN_NEXT_KEY)
val nextKey: Int?
) {
companion object {
const val TABLE_NAME = "remote_keys"
const val COLUMN_ID = "id"
const val COLUMN_PREV_KEY = "prev_key"
const val COLUMN_NEXT_KEY = "next_key"
}
}