mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-22 11:40:54 -05:00
begin search
This commit is contained in:
@@ -7,7 +7,7 @@ import retrofit2.Retrofit
|
||||
|
||||
class Client(baseUrl: String): KoinComponent {
|
||||
|
||||
private val converter: Converter by inject()
|
||||
private val converter: ConverterFactoryFactory by inject()
|
||||
private val client: HttpClient by inject()
|
||||
|
||||
private var retrofit: Retrofit = Retrofit.Builder()
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.owenlejeune.tvtime.api
|
||||
|
||||
import retrofit2.Converter
|
||||
|
||||
interface Converter {
|
||||
interface ConverterFactoryFactory {
|
||||
|
||||
fun get(): Converter.Factory
|
||||
|
||||
@@ -4,17 +4,14 @@ import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonDeserializer
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import retrofit2.Converter
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class GsonConverter: Converter, KoinComponent {
|
||||
class GsonConverter: ConverterFactoryFactory, KoinComponent {
|
||||
|
||||
private val deserializers: Map<Class<*>, JsonDeserializer<*>> by inject()
|
||||
|
||||
override fun get(): retrofit2.Converter.Factory {
|
||||
// val gson = GsonBuilder()
|
||||
// .registerTypeAdapter()
|
||||
// .create()
|
||||
override fun get(): Converter.Factory {
|
||||
val builder = GsonBuilder()
|
||||
|
||||
deserializers.forEach { deserializer ->
|
||||
|
||||
@@ -59,6 +59,10 @@ class TmdbClient: KoinComponent {
|
||||
return client.create(AccountApi::class.java)
|
||||
}
|
||||
|
||||
fun createSearchService(): SearchApi {
|
||||
return client.create(SearchApi::class.java)
|
||||
}
|
||||
|
||||
private inner class TmdbInterceptor: Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val apiParam = QueryParam("api_key", BuildConfig.TMDB_ApiKey)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3
|
||||
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Collection
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface SearchApi {
|
||||
|
||||
@GET("search/company")
|
||||
suspend fun searchCompanies(@Query("query") query: String, @Query("page") page: Int): Response<SearchResult<ProductionCompany>>
|
||||
|
||||
@GET("search/collection")
|
||||
suspend fun searchCollections(@Query("query") query: String, @Query("page") page: Int): Response<SearchResult<Collection>>
|
||||
|
||||
@GET("search/keyword")
|
||||
suspend fun searchKeywords(@Query("query") query: String, @Query("page") page: Int): Response<SearchResult<Keyword>>
|
||||
|
||||
@GET("search/movie")
|
||||
suspend fun searchMovies(@Query("query") query: String, @Query("page") page: Int): Response<SearchResult<SearchResultMovie>>
|
||||
|
||||
@GET("search/tv")
|
||||
suspend fun searchTv(@Query("query") query: String, @Query("page") page: Int): Response<SearchResult<SearchResultTv>>
|
||||
|
||||
@GET("search/person")
|
||||
suspend fun searchPeople(@Query("query") query: String, @Query("page") page: Int): Response<SearchResult<SearchResultPerson>>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3
|
||||
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Collection
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import retrofit2.Response
|
||||
import java.util.*
|
||||
|
||||
class SearchService: KoinComponent {
|
||||
|
||||
private val service: SearchApi by inject()
|
||||
|
||||
suspend fun searchCompanies(query: String, page: Int = 1): Response<SearchResult<ProductionCompany>> {
|
||||
return service.searchCompanies(query, page)
|
||||
}
|
||||
|
||||
suspend fun searchCollections(query: String, page: Int = 1): Response<SearchResult<Collection>> {
|
||||
return service.searchCollections(query, page)
|
||||
}
|
||||
|
||||
suspend fun searchKeywords(query: String, page: Int = 1): Response<SearchResult<Keyword>> {
|
||||
return service.searchKeywords(query, page)
|
||||
}
|
||||
|
||||
suspend fun searchMovies(query: String, page: Int = 1): Response<SearchResult<SearchResultMovie>> {
|
||||
return service.searchMovies(query, page)
|
||||
}
|
||||
|
||||
suspend fun searchTv(query: String, page: Int = 1): Response<SearchResult<SearchResultTv>> {
|
||||
return service.searchTv(query, page)
|
||||
}
|
||||
|
||||
suspend fun searchPeople(query: String, page: Int = 1): Response<SearchResult<SearchResultPerson>> {
|
||||
return service.searchPeople(query, page)
|
||||
}
|
||||
|
||||
suspend fun searchAll(query: String, onResultsUpdated: (List<Any>) -> Unit) {
|
||||
val results = TreeSet<Any> {a, b ->
|
||||
when (a) {
|
||||
is Keyword -> {
|
||||
if (b is Keyword) 0 else -3
|
||||
}
|
||||
is ProductionCompany -> {
|
||||
if (b is ProductionCompany) 0 else -2
|
||||
}
|
||||
is Collection -> {
|
||||
if (b is Collection) 0 else -1
|
||||
}
|
||||
is SortableSearchResult -> {
|
||||
when (b) {
|
||||
is SortableSearchResult -> {
|
||||
when {
|
||||
a.popularity > b.popularity -> 1
|
||||
a.popularity < b.popularity -> -1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
else -> 3
|
||||
}
|
||||
}
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
searchMovies(query).body()?.apply {
|
||||
withContext(Dispatchers.Main) {
|
||||
results.addAll(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
searchTv(query).body()?.apply {
|
||||
withContext(Dispatchers.Main) {
|
||||
results.addAll(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
searchPeople(query).body()?.apply {
|
||||
withContext(Dispatchers.Main) {
|
||||
results.addAll(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
searchCompanies(query).body()?.apply {
|
||||
withContext(Dispatchers.Main) {
|
||||
results.addAll(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
searchCollections(query).body()?.apply {
|
||||
withContext(Dispatchers.Main) {
|
||||
results.addAll(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
searchKeywords(query).body()?.apply {
|
||||
withContext(Dispatchers.Main) {
|
||||
results.addAll(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.deserializer
|
||||
|
||||
import com.google.gson.*
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KnownFor
|
||||
import com.owenlejeune.tvtime.ui.screens.MediaViewType
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class KnownForDeserializer: JsonDeserializer<KnownFor> {
|
||||
|
||||
companion object {
|
||||
const val MEDIA_TYPE = "media_type"
|
||||
}
|
||||
|
||||
override fun deserialize(
|
||||
json: JsonElement?,
|
||||
typeOfT: Type?,
|
||||
context: JsonDeserializationContext?
|
||||
): KnownFor {
|
||||
if (json?.isJsonObject == true) {
|
||||
val obj = json.asJsonObject
|
||||
if (obj.has(MEDIA_TYPE)) {
|
||||
val typeStr = obj.get(MEDIA_TYPE).asString
|
||||
return when (Gson().fromJson(typeStr, MediaViewType::class.java)) {
|
||||
MediaViewType.MOVIE -> Gson().fromJson(obj.toString(), KnownFor::class.java)
|
||||
MediaViewType.TV -> Gson().fromJson(obj.toString(), KnownFor::class.java)
|
||||
else -> throw JsonParseException("Not a valid MediaViewType: $typeStr")
|
||||
}
|
||||
}
|
||||
throw JsonParseException("JSON object has no property $MEDIA_TYPE")
|
||||
}
|
||||
throw JsonParseException("Not a JSON object")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.owenlejeune.tvtime.ui.screens.MediaViewType
|
||||
|
||||
class KnowForTv(
|
||||
backdropPath: String?,
|
||||
releaseDate: String,
|
||||
genreIds: List<Int>,
|
||||
id: Int,
|
||||
mediaType: MediaViewType,
|
||||
title: String,
|
||||
originalLanguage: String,
|
||||
originalTitle: String,
|
||||
overview: String,
|
||||
popularity: Float,
|
||||
posterPath: String?,
|
||||
voteAverage: Float,
|
||||
voteCount: Int,
|
||||
@SerializedName("original_country") val originalCountries: List<String>
|
||||
) : KnownFor(
|
||||
backdropPath,
|
||||
releaseDate,
|
||||
genreIds,
|
||||
id,
|
||||
mediaType,
|
||||
title,
|
||||
originalLanguage,
|
||||
originalTitle,
|
||||
overview,
|
||||
popularity,
|
||||
posterPath,
|
||||
voteAverage,
|
||||
voteCount
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.owenlejeune.tvtime.ui.screens.MediaViewType
|
||||
|
||||
abstract class KnownFor(
|
||||
@SerializedName("backdrop_path") val backdropPath: String?,
|
||||
@SerializedName("release_date", alternate = ["first_air_date"]) val releaseDate: String,
|
||||
@SerializedName("genre_ids") val genreIds: List<Int>,
|
||||
@SerializedName("id") val id: Int,
|
||||
@SerializedName("media_type") val mediaType: MediaViewType,
|
||||
@SerializedName("title", alternate = ["name"]) val title: String,
|
||||
@SerializedName("original_language") val originalLanguage: String,
|
||||
@SerializedName("original_title", alternate = ["original_name"]) val originalTitle: String,
|
||||
@SerializedName("overview") val overview: String,
|
||||
@SerializedName("popularity") val popularity: Float,
|
||||
@SerializedName("poster_path") val posterPath: String?,
|
||||
@SerializedName("vote_average") val voteAverage: Float,
|
||||
@SerializedName("vote_count") val voteCount: Int
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.owenlejeune.tvtime.ui.screens.MediaViewType
|
||||
|
||||
class KnownForMovie(
|
||||
backdropPath: String?,
|
||||
releaseDate: String,
|
||||
genreIds: List<Int>,
|
||||
id: Int,
|
||||
mediaType: MediaViewType,
|
||||
title: String,
|
||||
originalLanguage: String,
|
||||
originalTitle: String,
|
||||
overview: String,
|
||||
popularity: Float,
|
||||
posterPath: String?,
|
||||
voteAverage: Float,
|
||||
voteCount: Int,
|
||||
@SerializedName("video") val isVideo: Boolean,
|
||||
@SerializedName("adult") val isAdult: Boolean
|
||||
) : KnownFor(
|
||||
backdropPath,
|
||||
releaseDate,
|
||||
genreIds,
|
||||
id,
|
||||
mediaType,
|
||||
title,
|
||||
originalLanguage,
|
||||
originalTitle,
|
||||
overview,
|
||||
popularity,
|
||||
posterPath,
|
||||
voteAverage,
|
||||
voteCount
|
||||
)
|
||||
@@ -6,5 +6,5 @@ data class ProductionCompany(
|
||||
@SerializedName("id") val id: Int,
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("logo_path") val logoPath: String?,
|
||||
@SerializedName("origin_country") val originCountry: String
|
||||
@SerializedName("origin_country") val originCountry: String?
|
||||
)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
abstract class SearchResult<T> (
|
||||
@SerializedName("page") val page: Int,
|
||||
@SerializedName("total_pages") val totalPages: Int,
|
||||
@SerializedName("total_results") val totalResults: Int,
|
||||
@SerializedName("results") val results: List<T>
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
abstract class SearchResultMedia(
|
||||
var type: SearchResultType,
|
||||
@SerializedName("id") val id: Int,
|
||||
@SerializedName("overview") val overview: String,
|
||||
@SerializedName("name", alternate = ["title"]) val name: String,
|
||||
@SerializedName("vote_average") val voteAverage: Float,
|
||||
@SerializedName("vote_count") val voteCount: Int,
|
||||
@SerializedName("release_date", alternate = ["first_air_date", "air_date"]) val releaseDate: String,
|
||||
@SerializedName("backdrop_path") val backdropPath: String?,
|
||||
@SerializedName("genre_ids") val genreIds: List<Int>,
|
||||
@SerializedName("original_language") val originalLanguage: String,
|
||||
@SerializedName("original_name", alternate = ["original_title"]) val originalName: String,
|
||||
@SerializedName("poster_path") val posterPath: String?,
|
||||
popularity: Float
|
||||
): SortableSearchResult(popularity) {
|
||||
enum class SearchResultType {
|
||||
MOVIE,
|
||||
TV
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
class SearchResultMovie(
|
||||
id: Int,
|
||||
overview: String,
|
||||
name: String,
|
||||
voteAverage: Float,
|
||||
voteCount: Int,
|
||||
backdropPath: String?,
|
||||
genreIds: List<Int>,
|
||||
originalLanguage: String,
|
||||
originalName: String,
|
||||
posterPath: String?,
|
||||
popularity: Float,
|
||||
releaseDate: String,
|
||||
@SerializedName("adult") val isAdult: Boolean,
|
||||
@SerializedName("video") val video: Boolean,
|
||||
): SearchResultMedia(
|
||||
SearchResultType.MOVIE, id, overview, name, voteAverage, voteCount, releaseDate,
|
||||
backdropPath, genreIds, originalLanguage, originalName, posterPath, popularity
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
class SearchResultPerson(
|
||||
@SerializedName("profile_path") val profilePath: String,
|
||||
@SerializedName("adult") val isAdult: Boolean,
|
||||
@SerializedName("id") val id: Int,
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("known_for") val knownFor: List<KnownFor>,
|
||||
popularity: Float
|
||||
): SortableSearchResult(popularity)
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
class SearchResultTv(
|
||||
id: Int,
|
||||
overview: String,
|
||||
name: String,
|
||||
voteAverage: Float,
|
||||
voteCount: Int,
|
||||
backdropPath: String?,
|
||||
genreIds: List<Int>,
|
||||
originalLanguage: String,
|
||||
originalName: String,
|
||||
posterPath: String?,
|
||||
popularity: Float,
|
||||
releaseDate: String,
|
||||
@SerializedName("origin_country") val originCountry: List<String>,
|
||||
): SearchResultMedia(
|
||||
SearchResultType.TV, id, overview, name, voteAverage, voteCount, releaseDate,
|
||||
backdropPath, genreIds, originalLanguage, originalName, posterPath, popularity
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
abstract class SortableSearchResult(
|
||||
@SerializedName("popularity") val popularity: Float
|
||||
)
|
||||
@@ -3,20 +3,36 @@ package com.owenlejeune.tvtime.di.modules
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.owenlejeune.tvtime.BuildConfig
|
||||
import com.owenlejeune.tvtime.api.*
|
||||
import com.owenlejeune.tvtime.api.tmdb.TmdbClient
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.deserializer.KnownForDeserializer
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KnownFor
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.deserializer.ListItemDeserializer
|
||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListItem
|
||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||
import com.owenlejeune.tvtime.utils.ResourceUtils
|
||||
import org.koin.dsl.module
|
||||
import java.lang.reflect.Type
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
val networkModule = module {
|
||||
single { if (BuildConfig.DEBUG) DebugHttpClient() else ProdHttpClient() }
|
||||
single<Converter> { GsonConverter() }
|
||||
single<ConverterFactoryFactory> { GsonConverter() }
|
||||
factory { (baseUrl: String) -> Client(baseUrl) }
|
||||
|
||||
single<Map<Class<*>, JsonDeserializer<*>>> { mapOf(ListItem::class.java to ListItemDeserializer()) }
|
||||
single { TmdbClient() }
|
||||
single { get<TmdbClient>().createV4AuthenticationService() }
|
||||
single { get<TmdbClient>().createAccountService() }
|
||||
single { get<TmdbClient>().createGuestSessionService() }
|
||||
single { get<TmdbClient>().createAuthenticationService() }
|
||||
single { get<TmdbClient>().createMovieService() }
|
||||
single { get<TmdbClient>().createPeopleService() }
|
||||
single { get<TmdbClient>().createSearchService() }
|
||||
single { get<TmdbClient>().createTvService() }
|
||||
|
||||
single<Map<Class<*>, JsonDeserializer<*>>> {
|
||||
mapOf(
|
||||
ListItem::class.java to ListItemDeserializer(),
|
||||
KnownFor::class.java to KnownForDeserializer()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val preferencesModule = module {
|
||||
|
||||
Reference in New Issue
Block a user