Add APICache + splashscreen init.
This commit is contained in:
parent
e9cb8cd0ac
commit
2ad8d5953a
16 changed files with 309 additions and 15 deletions
|
|
@ -1,29 +1,38 @@
|
|||
package com.pixelized.biblib
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.pixelized.biblib.utils.injection.Bob
|
||||
import com.pixelized.biblib.network.client.BibLibClient
|
||||
import com.pixelized.biblib.network.client.IBibLibClient
|
||||
import com.pixelized.biblib.repository.apiCache.APICacheRepository
|
||||
import com.pixelized.biblib.repository.apiCache.IAPICacheRepository
|
||||
import com.pixelized.biblib.repository.credential.CredentialRepository
|
||||
import com.pixelized.biblib.repository.credential.ICredentialRepository
|
||||
import com.pixelized.biblib.repository.googlesignin.GoogleSingInRepository
|
||||
import com.pixelized.biblib.repository.googlesignin.IGoogleSingInRepository
|
||||
import com.pixelized.biblib.repository.googleSignIn.GoogleSingInRepository
|
||||
import com.pixelized.biblib.repository.googleSignIn.IGoogleSingInRepository
|
||||
import com.pixelized.biblib.utils.BitmapCache
|
||||
import com.pixelized.biblib.utils.injection.Bob
|
||||
|
||||
class BibLibApplication : Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
Bob[BitmapCache::class] = BitmapCache(this)
|
||||
|
||||
Bob[SharedPreferences::class] = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
|
||||
Bob[Gson::class] = GsonBuilder().create()
|
||||
Bob[BitmapCache::class] = BitmapCache(this)
|
||||
|
||||
Bob[IBibLibClient::class] = BibLibClient()
|
||||
|
||||
Bob[IGoogleSingInRepository::class] = GoogleSingInRepository(this)
|
||||
Bob[ICredentialRepository::class] = CredentialRepository(this)
|
||||
Bob[ICredentialRepository::class] = CredentialRepository()
|
||||
Bob[IAPICacheRepository::class] = APICacheRepository()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SHARED_PREF = "BIB_LIB_SHARED_PREF"
|
||||
}
|
||||
}
|
||||
7
app/src/main/java/com/pixelized/biblib/model/Author.kt
Normal file
7
app/src/main/java/com/pixelized/biblib/model/Author.kt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package com.pixelized.biblib.model
|
||||
|
||||
data class Author(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val sort: String,
|
||||
)
|
||||
31
app/src/main/java/com/pixelized/biblib/model/Book.kt
Normal file
31
app/src/main/java/com/pixelized/biblib/model/Book.kt
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package com.pixelized.biblib.model
|
||||
|
||||
import java.util.*
|
||||
|
||||
data class Book(
|
||||
val id: Int,
|
||||
val title: String,
|
||||
val sort: String,
|
||||
val author: List<Author>,
|
||||
val haveCover: Boolean,
|
||||
val releaseDate: Date,
|
||||
val language: Language? = null,
|
||||
val rating: Int? = null,
|
||||
val genre: List<Genre>? = null,
|
||||
// details
|
||||
val series: Series? = null,
|
||||
val synopsis: String? = null,
|
||||
// source
|
||||
val isNew: Boolean = false,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Book) return false
|
||||
if (id != other.id) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return id
|
||||
}
|
||||
}
|
||||
6
app/src/main/java/com/pixelized/biblib/model/Genre.kt
Normal file
6
app/src/main/java/com/pixelized/biblib/model/Genre.kt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package com.pixelized.biblib.model
|
||||
|
||||
data class Genre(
|
||||
val id: String,
|
||||
val name: String,
|
||||
)
|
||||
6
app/src/main/java/com/pixelized/biblib/model/Language.kt
Normal file
6
app/src/main/java/com/pixelized/biblib/model/Language.kt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package com.pixelized.biblib.model
|
||||
|
||||
data class Language (
|
||||
val id : String,
|
||||
val code: String,
|
||||
)
|
||||
8
app/src/main/java/com/pixelized/biblib/model/Series.kt
Normal file
8
app/src/main/java/com/pixelized/biblib/model/Series.kt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package com.pixelized.biblib.model
|
||||
|
||||
data class Series(
|
||||
val id: String?,
|
||||
val name: String,
|
||||
val sort: String,
|
||||
val index: Int?,
|
||||
)
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
package com.pixelized.biblib.network.factory
|
||||
|
||||
import com.pixelized.biblib.model.*
|
||||
import com.pixelized.biblib.network.data.response.BookDetailResponse
|
||||
import com.pixelized.biblib.network.data.response.BookListResponse
|
||||
import com.pixelized.biblib.utils.exception.MandatoryFieldMissingException
|
||||
import com.pixelized.biblib.utils.extention.toBoolean
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class BookFactory {
|
||||
private val parser get() = SimpleDateFormat(FORMAT, Locale.getDefault())
|
||||
|
||||
fun fromListResponseToBook(response: BookListResponse.Book, isNew: Boolean = false): Book {
|
||||
fun error(name: String) =
|
||||
MandatoryFieldMissingException("#fromListResponseToBook()", name, response)
|
||||
|
||||
val id: Int? = response.book_id
|
||||
val title: String? = response.book_title
|
||||
val sort: String? = response.book_sort
|
||||
val authorId: List<String>? = response.author_id
|
||||
val authorName: List<String>? = response.author_name
|
||||
val authorSort: List<String>? = response.author_sort
|
||||
val authorIdSize = authorId?.size ?: 0
|
||||
val authorNameSize = authorName?.size ?: 0
|
||||
val authorSortSize = authorSort?.size ?: 0
|
||||
val author: List<Author>? =
|
||||
if (authorId != null && authorName != null && authorSort != null) {
|
||||
if (authorIdSize == authorNameSize && authorNameSize == authorSortSize) {
|
||||
(authorId.indices).map { index ->
|
||||
Author(
|
||||
authorId[index],
|
||||
authorName[index],
|
||||
authorSort[index]
|
||||
)
|
||||
}
|
||||
} else if (authorIdSize == 1) {
|
||||
listOf(
|
||||
Author(
|
||||
authorId.first(),
|
||||
authorName.joinToString { it },
|
||||
authorSort.joinToString { it })
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val cover: Boolean? = response.book_has_cover?.toBoolean()
|
||||
val releaseDate = response.book_date?.let { parser.parse(it) }
|
||||
val languageId = response.lang_id
|
||||
val languageCode = response.lang_code
|
||||
val language = if (languageId != null && languageCode != null) {
|
||||
Language(languageId, languageCode)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val rating = response.rating?.toIntOrNull()
|
||||
val seriesName = response.series_name
|
||||
val seriesSort = response.series_sort
|
||||
val seriesIndex: Int? = response.book_series_index?.toInt()
|
||||
val series: Series? = if (seriesName != null && seriesSort != null) {
|
||||
Series(id = null, name = seriesName, sort = seriesSort, index = seriesIndex)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return Book(
|
||||
id = id ?: throw error("id"),
|
||||
title = title ?: throw error("title"),
|
||||
sort = sort ?: throw error("sort"),
|
||||
author = author ?: throw error("author"),
|
||||
haveCover = cover ?: throw error("haveCover"),
|
||||
releaseDate = releaseDate ?: throw error("releaseDate"),
|
||||
series = series,
|
||||
language = language,
|
||||
rating = rating,
|
||||
isNew = isNew
|
||||
)
|
||||
}
|
||||
|
||||
fun fromDetailResponseToBook(response: BookDetailResponse, isNew: Boolean = false): Book {
|
||||
fun error(name: String) =
|
||||
MandatoryFieldMissingException("#fromDetailResponseToBook()", name, response)
|
||||
|
||||
val id: Int? = response.book_id
|
||||
val title: String? = response.book_title
|
||||
val sort: String? = response.book_sort
|
||||
val authorId: List<String>? = response.author_id
|
||||
val authorName: List<String>? = response.author_name
|
||||
val authorSort: List<String>? = response.author_sort
|
||||
val authorIdSize = authorId?.size
|
||||
val authorNameSize = authorName?.size
|
||||
val authorSortSize = authorSort?.size
|
||||
val author: List<Author>? =
|
||||
if (authorId != null && authorName != null && authorSort != null && authorIdSize == authorNameSize && authorNameSize == authorSortSize) {
|
||||
authorId.indices.map { index ->
|
||||
Author(
|
||||
authorId[index],
|
||||
authorName[index],
|
||||
authorSort[index]
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val cover: Boolean? = response.book_has_cover?.toBoolean()
|
||||
val releaseDate = response.book_date?.let { parser.parse(it) }
|
||||
val languageId = response.lang_id
|
||||
val languageCode = response.lang_code
|
||||
val language = if (languageId != null && languageCode != null) {
|
||||
Language(languageId, languageCode)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val rating = response.rating?.toIntOrNull()
|
||||
val seriesId = response.series_id
|
||||
val seriesName = response.series_name
|
||||
val seriesSort = response.series_sort
|
||||
val seriesIndex: Int? = response.book_series_index?.toInt()
|
||||
val series: Series? = if (seriesId != null && seriesName != null && seriesSort != null) {
|
||||
Series(id = seriesId, name = seriesName, sort = seriesSort, seriesIndex)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val tagId: List<String>? = response.tag_id
|
||||
val tagName: List<String>? = response.tag_name
|
||||
val tagIdSize: Int? = response.tag_id?.size
|
||||
val tagNameSize: Int? = response.tag_name?.size
|
||||
val tag: List<Genre>? = if (tagId != null && tagName != null && tagIdSize == tagNameSize) {
|
||||
tagId.indices.map { Genre(tagId[it], tagName[it]) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val synopsis: String? = response.comment
|
||||
|
||||
return Book(
|
||||
id = id ?: throw error("id"),
|
||||
title = title ?: throw error("title"),
|
||||
sort = sort ?: throw error("sort"),
|
||||
author = author ?: throw error("author"),
|
||||
haveCover = cover ?: throw error("cover"),
|
||||
releaseDate = releaseDate ?: throw error("releaseDate"),
|
||||
language = language ?: throw error("language"),
|
||||
rating = rating,
|
||||
series = series,
|
||||
genre = tag,
|
||||
synopsis = synopsis,
|
||||
isNew = isNew
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.pixelized.biblib.repository.apiCache
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.google.gson.Gson
|
||||
import com.pixelized.biblib.network.data.response.BookListResponse
|
||||
import com.pixelized.biblib.utils.injection.inject
|
||||
|
||||
class APICacheRepository : IAPICacheRepository {
|
||||
private val gson: Gson by inject()
|
||||
private val preferences: SharedPreferences by inject()
|
||||
|
||||
override var new: BookListResponse?
|
||||
get() = preferences.new?.let { gson.fromJson(it, BookListResponse::class.java) }
|
||||
set(value) = gson.toJson(value).let { preferences.new = it }
|
||||
|
||||
override var list: BookListResponse?
|
||||
get() = preferences.list?.let { gson.fromJson(it, BookListResponse::class.java) }
|
||||
set(value) = gson.toJson(value).let { preferences.list = it }
|
||||
|
||||
private var SharedPreferences.new: String?
|
||||
get() = getString(NEW, null)
|
||||
set(value) = edit { putString(NEW, value) }
|
||||
|
||||
private var SharedPreferences.list: String?
|
||||
get() = getString(LIST, null)
|
||||
set(value) = edit { putString(LIST, value) }
|
||||
|
||||
companion object {
|
||||
const val NEW = "NEW"
|
||||
const val LIST = "LIST"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.pixelized.biblib.repository.apiCache
|
||||
|
||||
import com.pixelized.biblib.network.data.response.BookListResponse
|
||||
|
||||
interface IAPICacheRepository {
|
||||
var new: BookListResponse?
|
||||
var list: BookListResponse?
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
package com.pixelized.biblib.repository.credential
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.pixelized.biblib.utils.injection.inject
|
||||
|
||||
class CredentialRepository(application: Application) : ICredentialRepository {
|
||||
private val preferences = application.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
|
||||
|
||||
class CredentialRepository : ICredentialRepository {
|
||||
private val preferences: SharedPreferences by inject()
|
||||
|
||||
override var login
|
||||
get() = preferences.login
|
||||
|
|
@ -41,7 +41,6 @@ class CredentialRepository(application: Application) : ICredentialRepository {
|
|||
set(value) = edit { putString(BEARER_TOKEN, value) }
|
||||
|
||||
companion object {
|
||||
private const val SHARED_PREF = "BIB_LIB_SHARED_PREF"
|
||||
private const val REMEMBER_CREDENTIAL = "REMEMBER_CREDENTIAL"
|
||||
private const val REMEMBER_USER = "REMEMBER_USER"
|
||||
private const val REMEMBER_PASSWORD = "REMEMBER_PASSWORD"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.biblib.repository.googlesignin
|
||||
package com.pixelized.biblib.repository.googleSignIn
|
||||
|
||||
import android.app.Application
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.biblib.repository.googlesignin
|
||||
package com.pixelized.biblib.repository.googleSignIn
|
||||
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInClient
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
||||
|
|
@ -15,7 +15,7 @@ import com.google.android.gms.common.api.ApiException
|
|||
import com.pixelized.biblib.network.data.query.AuthLoginQuery
|
||||
import com.pixelized.biblib.network.client.IBibLibClient
|
||||
import com.pixelized.biblib.repository.credential.ICredentialRepository
|
||||
import com.pixelized.biblib.repository.googlesignin.IGoogleSingInRepository
|
||||
import com.pixelized.biblib.repository.googleSignIn.IGoogleSingInRepository
|
||||
import com.pixelized.biblib.ui.viewmodel.authentication.IAuthentication.State
|
||||
import com.pixelized.biblib.utils.exception.MissingTokenException
|
||||
import com.pixelized.biblib.utils.injection.inject
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import androidx.compose.runtime.*
|
|||
import androidx.lifecycle.ViewModel
|
||||
import com.pixelized.biblib.network.client.IBibLibClient
|
||||
import com.pixelized.biblib.network.data.query.AuthLoginQuery
|
||||
import com.pixelized.biblib.repository.apiCache.IAPICacheRepository
|
||||
import com.pixelized.biblib.repository.credential.ICredentialRepository
|
||||
import com.pixelized.biblib.repository.googlesignin.IGoogleSingInRepository
|
||||
import com.pixelized.biblib.repository.googleSignIn.IGoogleSingInRepository
|
||||
import com.pixelized.biblib.ui.viewmodel.initialisation.IInitialisation.State.*
|
||||
import com.pixelized.biblib.utils.injection.inject
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -14,6 +15,7 @@ class InitialisationViewModel : ViewModel(), IInitialisation {
|
|||
private val credentialRepository: ICredentialRepository by inject()
|
||||
private val googleSignIn: IGoogleSingInRepository by inject()
|
||||
private val client: IBibLibClient by inject()
|
||||
private val apiCache: IAPICacheRepository by inject()
|
||||
|
||||
@Composable
|
||||
override fun LoadApplication(content: @Composable (IInitialisation.State) -> Unit) {
|
||||
|
|
@ -24,6 +26,9 @@ class InitialisationViewModel : ViewModel(), IInitialisation {
|
|||
delay(2000)
|
||||
|
||||
val loggedIn = loginWithGoogle() || loginWithCredential()
|
||||
if (loggedIn) {
|
||||
loadNewBooks() && loadAllBooks()
|
||||
}
|
||||
state.value = Finished(needLogin = loggedIn.not())
|
||||
}
|
||||
|
||||
|
|
@ -71,4 +76,20 @@ class InitialisationViewModel : ViewModel(), IInitialisation {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadNewBooks(): Boolean {
|
||||
val cached = apiCache.new
|
||||
val updated = client.service.new()
|
||||
return if (cached != updated) {
|
||||
apiCache.new = updated
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadAllBooks(): Boolean {
|
||||
apiCache.list = client.service.list()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.pixelized.biblib.utils.exception
|
||||
|
||||
class MandatoryFieldMissingException(method: String, name: String, json: Any) :
|
||||
RuntimeException("Parse exception: Mandatory field:'$name' is missing from:'$method', with data:$json")
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.pixelized.biblib.utils.extention
|
||||
|
||||
fun Int.toBoolean(): Boolean = this != 0
|
||||
|
||||
fun Int?.toBoolean(): Boolean? = if (this == null) null else this != 0
|
||||
Loading…
Add table
Add a link
Reference in a new issue