Skip to content

Commit

Permalink
Merge pull request #20 from reactivedroid/feature/clean-arch
Browse files Browse the repository at this point in the history
More towards clean code architecture
  • Loading branch information
reactivedroid authored Mar 31, 2022
2 parents 671f82c + 5c25ce3 commit 6b2c7d9
Show file tree
Hide file tree
Showing 22 changed files with 242 additions and 119 deletions.
2 changes: 2 additions & 0 deletions app/src/main/java/com/android/tvflix/TvFlixApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.android.tvflix

import android.app.Application
import com.android.tvflix.config.AppConfig
import com.google.firebase.FirebaseApp
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber
import javax.inject.Inject
Expand All @@ -13,6 +14,7 @@ class TvFlixApplication : Application() {

override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/android/tvflix/db/TvFlixDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.room.RoomDatabase
import com.android.tvflix.db.favouriteshow.FavoriteShow
import com.android.tvflix.db.favouriteshow.ShowDao

@Database(entities = [FavoriteShow::class], version = 2, exportSchema = false)
@Database(entities = [FavoriteShow::class], version = 3, exportSchema = false)
abstract class TvFlixDatabase : RoomDatabase() {

abstract fun showDao(): ShowDao
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ data class FavoriteShow(
val imageUrl: String?,
var summary: String?,
var rating: String?,
var runtime: Int?,
val isFavorite: Boolean
var runtime: Int?
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.android.tvflix.domain

import com.android.tvflix.db.favouriteshow.FavoriteShow
import com.android.tvflix.di.IoDispatcher
import com.android.tvflix.favorite.FavoriteShowsRepository
import kotlinx.coroutines.CoroutineDispatcher
import javax.inject.Inject

class AddToFavoritesUseCase @Inject
constructor(
private val favoriteShowsRepository: FavoriteShowsRepository,
@IoDispatcher ioDispatcher: CoroutineDispatcher
) : SuspendUseCase<FavoriteShow, Unit>(ioDispatcher) {
override suspend fun execute(parameters: FavoriteShow) {
favoriteShowsRepository.insertIntoFavorites(parameters)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.android.tvflix.domain

import com.android.tvflix.db.favouriteshow.FavoriteShow
import com.android.tvflix.di.IoDispatcher
import com.android.tvflix.favorite.FavoriteShowsRepository
import com.android.tvflix.home.HomeViewModel
import kotlinx.coroutines.CoroutineDispatcher
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Inject

class GetFavoriteShowsUseCase
@Inject
constructor(
private val favoriteShowsRepository: FavoriteShowsRepository,
@IoDispatcher ioDispatcher: CoroutineDispatcher
) : SuspendUseCase<Unit, List<Long>>(ioDispatcher) {
override suspend fun execute(parameters: Unit): List<Long> {
return favoriteShowsRepository.allFavoriteShowIds()
}
}
34 changes: 34 additions & 0 deletions app/src/main/java/com/android/tvflix/domain/GetSchedulesUseCase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.android.tvflix.domain

import com.android.tvflix.di.IoDispatcher
import com.android.tvflix.model.respositores.SchedulesRepository
import com.android.tvflix.network.home.Episode
import kotlinx.coroutines.CoroutineDispatcher
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Inject


class GetSchedulesUseCase
@Inject
constructor(
private val schedulesRepository: SchedulesRepository,
@IoDispatcher ioDispatcher: CoroutineDispatcher
) :
SuspendUseCase<Unit, List<Episode>>(ioDispatcher) {
override suspend fun execute(parameters: Unit): List<Episode> {
return schedulesRepository.getSchedule(country = COUNTRY, currentDate = currentDate)
}

companion object {
private const val QUERY_DATE_FORMAT = "yyyy-MM-dd"
const val COUNTRY = "US"
private val currentDate: String
get() {
val simpleDateFormat = SimpleDateFormat(QUERY_DATE_FORMAT, Locale.US)
val calendar = Calendar.getInstance()
return simpleDateFormat.format(calendar.time)
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.android.tvflix.domain

import com.android.tvflix.db.favouriteshow.FavoriteShow
import com.android.tvflix.di.IoDispatcher
import com.android.tvflix.favorite.FavoriteShowsRepository
import com.android.tvflix.network.home.Show
import kotlinx.coroutines.CoroutineDispatcher
import javax.inject.Inject

class RemoveFromFavoritesUseCase
@Inject
constructor(
private val favoriteShowsRepository: FavoriteShowsRepository,
@IoDispatcher ioDispatcher: CoroutineDispatcher
) : SuspendUseCase<FavoriteShow, Unit>(ioDispatcher) {
override suspend fun execute(parameters: FavoriteShow) {
favoriteShowsRepository.removeFromFavorites(parameters)
}
}
35 changes: 35 additions & 0 deletions app/src/main/java/com/android/tvflix/domain/SuspendUseCase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.android.tvflix.domain

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext

/**
* Executes business logic synchronously or asynchronously using Coroutines.
*
* The [execute] method of [SuspendUseCase] is a suspend function
*/
abstract class SuspendUseCase<in P, R>(private val coroutineDispatcher: CoroutineDispatcher) {

/** Executes the use case asynchronously and returns a [Result].
*
* @return a [Result].
*
* @param parameters the input parameters to run the use case with
*/
val tag: String = this.javaClass.simpleName

suspend operator fun invoke(parameters: P): R {
// Moving all use case's executions to the injected dispatcher
// In production code, this is usually the Default dispatcher (background thread)
// In tests, this becomes a TestCoroutineDispatcher
return withContext(coroutineDispatcher) {
execute(parameters)
}
}

/**
* Override this to set the code to be executed.
*/
@Throws(RuntimeException::class)
protected abstract suspend fun execute(parameters: P): R
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect

@AndroidEntryPoint
class FavoriteShowsActivity : AppCompatActivity(), FavoriteShowsAdapter.Callback {
class FavoriteShowsActivity : AppCompatActivity() {
private val favoriteShowsViewModel: FavoriteShowsViewModel by viewModels()
private val binding by lazy { ActivityFavoriteShowsBinding.inflate(layoutInflater) }

Expand Down Expand Up @@ -62,7 +62,7 @@ class FavoriteShowsActivity : AppCompatActivity(), FavoriteShowsAdapter.Callback
binding.progress.isVisible = false
val layoutManager = GridLayoutManager(this, COLUMNS_COUNT)
binding.shows.layoutManager = layoutManager
val favoriteShowsAdapter = FavoriteShowsAdapter(favoriteShows.toMutableList(), this)
val favoriteShowsAdapter = FavoriteShowsAdapter(favoriteShows.toMutableList())
binding.shows.adapter = favoriteShowsAdapter
val spacing = resources.getDimensionPixelSize(R.dimen.show_grid_spacing)
binding.shows.addItemDecoration(GridItemDecoration(spacing, COLUMNS_COUNT))
Expand All @@ -81,10 +81,6 @@ class FavoriteShowsActivity : AppCompatActivity(), FavoriteShowsAdapter.Callback
binding.favoriteHint.isVisible = true
}

override fun onFavoriteClicked(show: FavoriteShow) {
favoriteShowsViewModel.onFavoriteClick(show)
}

companion object {
private const val FAVORITE_ICON_START_OFFSET = 13
private const val FAVORITE_ICON_END_OFFSET = 14
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,23 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestOptions

class FavoriteShowsAdapter(
private val favoriteShows: MutableList<FavoriteShow>,
private val callback: Callback
private val favoriteShows: MutableList<FavoriteShow>
) : RecyclerView.Adapter<FavoriteShowsAdapter.FavoriteShowHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FavoriteShowHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val showListItemBinding = ShowListItemBinding.inflate(layoutInflater, parent, false)
val holder = FavoriteShowHolder(showListItemBinding)
holder.binding.favorite.setOnClickListener { onFavouriteIconClicked(holder.absoluteAdapterPosition) }
holder.binding.showFavoriteIcon = false
return holder
}

private fun onFavouriteIconClicked(position: Int) {
if (position != RecyclerView.NO_POSITION) {
val show = favoriteShows[position]
val updatedShow = show.copy(isFavorite = !show.isFavorite)
favoriteShows[position] = updatedShow
notifyItemChanged(position)
callback.onFavoriteClicked(show)
}
}

override fun onBindViewHolder(holder: FavoriteShowHolder, position: Int) {
val favoriteShow = favoriteShows[position]
Glide.with(holder.itemView.context).load(favoriteShow.imageUrl)
.apply(RequestOptions.placeholderOf(R.color.grey))
.transition(DrawableTransitionOptions.withCrossFade())
.into(holder.binding.showImage)
configureFavoriteIcon(holder.binding.favorite, favoriteShow.isFavorite)
}

private fun configureFavoriteIcon(favoriteIcon: ImageView, favorite: Boolean) {
if (favorite) {
val favoriteDrawable = AppCompatResources
.getDrawable(favoriteIcon.context, R.drawable.favorite)
favoriteIcon.setImageDrawable(favoriteDrawable)
} else {
val unFavoriteDrawable = AppCompatResources
.getDrawable(favoriteIcon.context, R.drawable.favorite_border)
favoriteIcon.setImageDrawable(unFavoriteDrawable)
}
}

override fun getItemCount(): Int {
Expand All @@ -63,8 +38,4 @@ class FavoriteShowsAdapter(

class FavoriteShowHolder(val binding: ShowListItemBinding) :
RecyclerView.ViewHolder(binding.root)

interface Callback {
fun onFavoriteClicked(show: FavoriteShow)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ constructor(private val showDao: ShowDao) {
imageUrl = show.image!!["original"],
summary = show.summary,
rating = show.rating!!["average"],
runtime = show.runtime!!,
isFavorite = true
runtime = show.runtime!!
)
showDao.insert(favoriteShow)
}
Expand All @@ -36,8 +35,7 @@ constructor(private val showDao: ShowDao) {
imageUrl = show.image!!["original"],
summary = show.summary,
rating = show.rating!!["average"],
runtime = show.runtime!!,
isFavorite = false
runtime = show.runtime!!
)
showDao.remove(favoriteShow)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,4 @@ constructor(
)
Timber.d(throwable)
}

fun onFavoriteClick(show: FavoriteShow) {
viewModelScope.launch(ioDispatcher) {
if (!show.isFavorite) {
favoriteShowsRepository.insertIntoFavorites(show)
_favoriteShowsStateFlow.emit(FavoriteShowState.AddedToFavorites(show))
} else {
favoriteShowsRepository.removeFromFavorites(show)
_favoriteShowsStateFlow.emit(FavoriteShowState.RemovedFromFavorites(show))
}
}
}
}
11 changes: 6 additions & 5 deletions app/src/main/java/com/android/tvflix/home/HomeActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import com.android.tvflix.R
import com.android.tvflix.config.FavoritesFeatureFlag
import com.android.tvflix.databinding.ActivityHomeBinding
import com.android.tvflix.domain.GetSchedulesUseCase
import com.android.tvflix.favorite.FavoriteShowsActivity
import com.android.tvflix.shows.AllShowsActivity
import com.android.tvflix.utils.GridItemDecoration
Expand Down Expand Up @@ -57,10 +58,6 @@ class HomeActivity : AppCompatActivity(), ShowsAdapter.Callback {
lifecycleScope.launchWhenStarted {
homeViewModel.homeViewStateFlow.collect { setViewState(it) }
}
binding.popularShowHeader.text = String.format(
getString(R.string.popular_shows_airing_today),
homeViewModel.country
)
}

private fun setViewState(homeViewState: HomeViewState) {
Expand All @@ -71,7 +68,11 @@ class HomeActivity : AppCompatActivity(), ShowsAdapter.Callback {
showError(homeViewState.message!!)
}
is HomeViewState.Success -> {
binding.progress.isVisible = false
with(binding) {
progress.isVisible = false
popularShowHeader.text = homeViewState.homeViewData.heading
popularShowHeader.isVisible = true
}
showPopularShows(homeViewState.homeViewData)
}
is HomeViewState.AddedToFavorites ->
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/android/tvflix/home/HomeViewData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.android.tvflix.home

import com.android.tvflix.network.home.Show

data class HomeViewData(val episodes: List<EpisodeViewData>) {
data class HomeViewData(val heading: String, val episodes: List<EpisodeViewData>) {
data class EpisodeViewData(
val id: Long,
val showViewData: ShowViewData,
Expand Down
Loading

0 comments on commit 6b2c7d9

Please sign in to comment.