Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

영화 상세 화면 작업 #32

Open
wants to merge 4 commits into
base: dlwls5201
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
754 changes: 724 additions & 30 deletions ToyProject/.idea/workspace.xml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package com.example.movieapplication

import android.app.Application
import com.example.movieapplication.di.appModule
import com.example.movieapplication.di.dataModule
import com.example.movieapplication.di.networkModule
import com.example.movieapplication.di.viewModuleModule
import com.example.movieapplication.di.*
import org.koin.android.ext.koin.androidContext
import org.koin.android.logger.AndroidLogger
import org.koin.core.context.startKoin
Expand Down Expand Up @@ -40,7 +37,7 @@ class MovieApplication : Application() {

modules(
listOf(
appModule, dataModule, networkModule, viewModuleModule
appModule, dataModule, domainModule, networkModule, viewModuleModule
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,9 @@ package com.example.movieapplication.base

import androidx.lifecycle.ViewModel
import io.reactivex.disposables.CompositeDisposable
import kotlinx.coroutines.Dispatchers

abstract class BaseViewModel : ViewModel() {

/*private val coroutineExceptionHanlder = CoroutineExceptionHandler { _, exception ->

}*/

protected val ioDispatchers = Dispatchers.IO
protected val uiDispatchers = Dispatchers.Main

val compositeDisposable = CompositeDisposable()

override fun onCleared() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.example.movieapplication.data.model


import com.example.movieapplication.presenter.model.MovieItem
import com.example.movieapplication.domain.model.MovieEntity
import com.google.gson.annotations.SerializedName

data class MovieResponse(
Expand Down Expand Up @@ -46,15 +46,13 @@ data class MovieResponse(
)
}

fun MovieResponse.Result.mapToPresenter(width: Int, height: Int) = MovieItem(
fun MovieResponse.Result.mapToDomain() = MovieEntity.MovieItemEntity(
id = id,
title = title,
posterPath = if (posterPath.isNullOrEmpty()) {
""
} else {
posterPath
},
releaseDate = releaseDate,
width = width,
height = height
releaseDate = releaseDate
)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,67 +1,20 @@
package com.example.movieapplication.data.repository

import com.example.movieapplication.AppProvider
import com.example.movieapplication.R
import com.example.movieapplication.data.model.ErrorResponse
import com.example.movieapplication.data.model.ResultWrapper
import com.example.movieapplication.data.model.mapToPresenter
import com.example.movieapplication.data.source.remote.MovieApi
import com.example.movieapplication.presenter.model.Movie
import com.google.gson.Gson
import retrofit2.HttpException
import timber.log.Timber
import java.io.IOException
import com.example.movieapplication.data.model.mapToDomain
import com.example.movieapplication.data.source.MovieApi
import com.example.movieapplication.domain.model.MovieEntity
import com.example.movieapplication.domain.repository.MovieRepository

class MovieRepositoryImpl(
private val movieApi: MovieApi,
private val appProvider: AppProvider
private val movieApi: MovieApi
) : MovieRepository {

private val imageWidthRatio = 2f
private val imageHeightRatio = 3f
override suspend fun getPopularMovie(page: Int): MovieEntity {
val response = movieApi.getPopular(page)

private val deviceWidth = appProvider.getDeviceWidth()
private val imageMargin = appProvider.getDimens(R.dimen.item_movie_horizontal_margin)

private val imageWidth = (deviceWidth - (imageMargin * 3)) / 2
private val imageHeight = imageWidth * (imageHeightRatio / imageWidthRatio)

override suspend fun getPopularMovie(page: Int): ResultWrapper<Movie> {
return try {
val response = movieApi.getPopular(page)
val movie = Movie(
totalPages = response.totalPages,
movies = response.results.map {
it.mapToPresenter(imageWidth.toInt(), imageHeight.toInt())
}
)
ResultWrapper.Success(movie)
} catch (throwable: Throwable) {
Timber.e(throwable)
when (throwable) {
is IOException -> ResultWrapper.NetworkError()
is HttpException -> {
val code = throwable.code()
val errors = convertErrorBody(throwable).errors
ResultWrapper.HttpException(code, errors.first())
}
else -> {
ResultWrapper.HttpException(-1, unknownError)
}
}
}
}

private val unknownError = "Unknown Error"
private val gson = Gson()

private fun convertErrorBody(throwable: HttpException): ErrorResponse {
return try {
throwable.response()?.errorBody()?.let { errorBody ->
gson.fromJson(errorBody.string(), ErrorResponse::class.java)
} ?: ErrorResponse(listOf(unknownError))
} catch (exception: Exception) {
ErrorResponse(listOf(unknownError))
}
return MovieEntity(
totalPages = response.totalPages,
movies = response.results.map { it.mapToDomain() }
)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.movieapplication.data.source.remote
package com.example.movieapplication.data.source

import com.example.movieapplication.data.model.MovieResponse
import retrofit2.http.GET
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package com.example.movieapplication.di

import com.example.movieapplication.data.repository.MovieRepository
import com.example.movieapplication.data.repository.MovieRepositoryImpl
import com.example.movieapplication.data.source.remote.MovieApi
import com.example.movieapplication.data.source.MovieApi
import com.example.movieapplication.domain.repository.MovieRepository
import org.koin.dsl.module
import retrofit2.Retrofit

val dataModule = module {

single<MovieRepository> {
MovieRepositoryImpl(get(), get())
MovieRepositoryImpl(get())
}

single { get<Retrofit>().create(MovieApi::class.java) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example.movieapplication.di

import com.example.movieapplication.domain.GetPopularMovieUseCase
import org.koin.dsl.module

val domainModule = module {

single {
GetPopularMovieUseCase(get(), get())
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.example.movieapplication.domain

import com.example.movieapplication.AppProvider
import com.example.movieapplication.R
import com.example.movieapplication.data.model.ErrorResponse
import com.example.movieapplication.domain.model.mapToPresenter
import com.example.movieapplication.domain.repository.MovieRepository
import com.example.movieapplication.domain.result.ResultWrapper
import com.example.movieapplication.presenter.model.Movie
import com.google.gson.Gson
import retrofit2.HttpException
import timber.log.Timber
import java.io.IOException

class GetPopularMovieUseCase(
private val movieRepository: MovieRepository,
private val appProvider: AppProvider
) {

suspend fun get(page: Int): ResultWrapper<Movie> {
return try {
val imageWidthRatio = 2f
val imageHeightRatio = 3f

val deviceWidth = appProvider.getDeviceWidth()
val imageMargin = appProvider.getDimens(R.dimen.item_movie_horizontal_margin)

val imageWidth = (deviceWidth - (imageMargin * 3)) / 2
val imageHeight = imageWidth * (imageHeightRatio / imageWidthRatio)

val response = movieRepository.getPopularMovie(page)

val movie = Movie(
totalPages = response.totalPages,
movies = response.movies.map {
it.mapToPresenter(imageWidth.toInt(), imageHeight.toInt())
}
)
ResultWrapper.Success(movie)
} catch (throwable: Throwable) {
Timber.e(throwable)
when (throwable) {
is IOException -> ResultWrapper.NetworkError()
is HttpException -> {
val code = throwable.code()
val errors = convertErrorBody(throwable).errors
ResultWrapper.HttpException(code, errors.first())
}
else -> {
ResultWrapper.HttpException(-1, unknownError)
}
}
}
}

private val unknownError = "Unknown Error"
private val gson = Gson()

private fun convertErrorBody(throwable: HttpException): ErrorResponse {
return try {
throwable.response()?.errorBody()?.let { errorBody ->
gson.fromJson(errorBody.string(), ErrorResponse::class.java)
} ?: ErrorResponse(listOf(unknownError))
} catch (exception: Exception) {
ErrorResponse(listOf(unknownError))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.movieapplication.domain.model

import com.example.movieapplication.presenter.model.MovieItem

data class MovieEntity(
val totalPages: Int = 0,
val movies: List<MovieItemEntity> = listOf()
) {
data class MovieItemEntity(
val id: Int = -1,
val title: String = "",
val posterPath: String = "",
val releaseDate: String = ""
)
}

fun MovieEntity.MovieItemEntity.mapToPresenter(width: Int, height: Int) = MovieItem(
id = id,
title = title,
posterPath = posterPath,
releaseDate = releaseDate,
width = width,
height = height
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.movieapplication.domain.repository

import com.example.movieapplication.domain.model.MovieEntity

interface MovieRepository {

suspend fun getPopularMovie(page: Int = 1): MovieEntity
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.movieapplication.data.model
package com.example.movieapplication.domain.result

sealed class ResultWrapper<out T> {
data class Success<out T>(val value: T): ResultWrapper<T>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,22 @@ import com.example.movieapplication.base.BaseFragment
import com.example.movieapplication.databinding.FragmentMovieBinding
import com.example.movieapplication.presenter.adapter.MovieAdapter
import com.example.movieapplication.presenter.adapter.itemdecoration.MovieItemDecoration
import com.example.movieapplication.presenter.model.MovieItem
import org.koin.androidx.viewmodel.ext.android.viewModel

class MovieFragment : BaseFragment<FragmentMovieBinding>(R.layout.fragment_movie) {

private val movieViewModel: MovieViewModel by viewModel()

private val movieAdapter by lazy { MovieAdapter() }
private val movieAdapter by lazy {
MovieAdapter().apply {
onItemClickListener = object : MovieAdapter.OnItemClickListener {
override fun onItemClick(data: MovieItem) {

}
}
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.viewModelScope
import com.example.movieapplication.base.BaseViewModel
import com.example.movieapplication.data.model.ResultWrapper
import com.example.movieapplication.data.repository.MovieRepository
import com.example.movieapplication.domain.GetPopularMovieUseCase
import com.example.movieapplication.domain.result.ResultWrapper
import com.example.movieapplication.presenter.model.MovieItem
import com.example.movieapplication.utils.Event
import kotlinx.coroutines.launch

class MovieViewModel(private val movieRepo: MovieRepository) : BaseViewModel() {
class MovieViewModel(
private val getPopularMovieUseCase: GetPopularMovieUseCase
) : BaseViewModel() {

private var isBottomLoading = false
private var totalPages = 0
Expand All @@ -38,9 +40,8 @@ class MovieViewModel(private val movieRepo: MovieRepository) : BaseViewModel() {
showLoading()

viewModelScope.launch {
hideLoading()

val result = movieRepo.getPopularMovie(page = 1)
val result = getPopularMovieUseCase.get(page = 1)
when (result) {
is ResultWrapper.Success -> {
totalPages = result.value.totalPages
Expand All @@ -54,6 +55,8 @@ class MovieViewModel(private val movieRepo: MovieRepository) : BaseViewModel() {
showError(result.error)
}
}

hideLoading()
}
}

Expand All @@ -74,7 +77,7 @@ class MovieViewModel(private val movieRepo: MovieRepository) : BaseViewModel() {

viewModelScope.launch {

val result = movieRepo.getPopularMovie(page)
val result = getPopularMovieUseCase.get(page)
when (result) {
is ResultWrapper.Success -> {
_movies.value = result.value.movies
Expand Down
Loading