diff --git a/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/SuperHeroViewHolderTest.kt b/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/SuperHeroViewHolderTest.kt index 8204503..3f70a31 100644 --- a/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/SuperHeroViewHolderTest.kt +++ b/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/SuperHeroViewHolderTest.kt @@ -1,8 +1,10 @@ package com.karumi.jetpack.superheroes.ui.view import android.view.LayoutInflater +import androidx.databinding.DataBindingUtil import androidx.test.platform.app.InstrumentationRegistry import com.karumi.jetpack.superheroes.R +import com.karumi.jetpack.superheroes.databinding.SuperHeroRowBinding import com.karumi.jetpack.superheroes.domain.model.SuperHero import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroesPresenter import com.karumi.jetpack.superheroes.ui.view.adapter.SuperHeroViewHolder @@ -51,15 +53,17 @@ class SuperHeroViewHolderTest : ScreenshotTest { compareScreenshot(holder, R.dimen.super_hero_row_height) } - private fun givenASuperHeroViewHolder(): SuperHeroViewHolder { - val context = InstrumentationRegistry.getInstrumentation().targetContext - val inflater = LayoutInflater.from(context) - val view = inflater.inflate(R.layout.super_hero_row, null, false) - return SuperHeroViewHolder( - view, - mock(SuperHeroesPresenter::class.java) - ) - } + private fun givenASuperHeroViewHolder(): SuperHeroViewHolder = + runOnUi { + val context = InstrumentationRegistry.getInstrumentation().targetContext + val inflater = LayoutInflater.from(context) + val binding: SuperHeroRowBinding = + DataBindingUtil.inflate(inflater, R.layout.super_hero_row, null, false) + SuperHeroViewHolder( + binding, + mock(SuperHeroesPresenter::class.java) + ) + } private fun givenASuperHeroWithALongDescription(): SuperHero { val superHeroName = "Super Hero Name" @@ -94,4 +98,10 @@ class SuperHeroViewHolderTest : ScreenshotTest { superHeroDescription: String = "Super Hero Description", isAvenger: Boolean = false ): SuperHero = SuperHero(superHeroId, superHeroName, null, isAvenger, superHeroDescription) +} + +private fun runOnUi(block: () -> T): T { + var response: T? = null + InstrumentationRegistry.getInstrumentation().runOnMainSync { response = block() } + return response!! } \ No newline at end of file diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/common/BindingAdapters.kt b/app/src/main/java/com/karumi/jetpack/superheroes/common/BindingAdapters.kt new file mode 100644 index 0000000..3a78ab6 --- /dev/null +++ b/app/src/main/java/com/karumi/jetpack/superheroes/common/BindingAdapters.kt @@ -0,0 +1,12 @@ +package com.karumi.jetpack.superheroes.common + +import android.widget.ImageView +import androidx.databinding.BindingAdapter +import com.karumi.jetpack.superheroes.ui.utils.picasso + +@BindingAdapter("imageUrl") +fun setImageUrl(view: ImageView, url: String?) { + if (url != null) { + view.context.picasso.load(url).fit().centerCrop().fit().into(view) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/ui/presenter/EditSuperHeroPresenter.kt b/app/src/main/java/com/karumi/jetpack/superheroes/ui/presenter/EditSuperHeroPresenter.kt index bee63f1..b61a396 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/ui/presenter/EditSuperHeroPresenter.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/ui/presenter/EditSuperHeroPresenter.kt @@ -41,25 +41,21 @@ class EditSuperHeroPresenter( } override fun onSaveSuperHeroSelected( - name: String, - description: String, - isAvenger: Boolean + editableSuperHero: EditSuperHeroPresenter.EditableSuperHero ) { - saveSuperHero(name, description, isAvenger) + saveSuperHero(editableSuperHero) } private fun saveSuperHero( - name: String, - description: String, - isAvenger: Boolean + editableSuperHero: EditSuperHeroPresenter.EditableSuperHero ) = executor.submit { view?.showLoading() val superHero = superHero ?: return@submit saveSuperHero( superHero.copy( - name = name, - description = description, - isAvenger = isAvenger + name = editableSuperHero.name, + description = editableSuperHero.description, + isAvenger = editableSuperHero.isAvenger ) ) view?.close() @@ -72,6 +68,12 @@ class EditSuperHeroPresenter( this@EditSuperHeroPresenter.superHero = superHero } + data class EditableSuperHero( + var isAvenger: Boolean, + var name: String, + var description: String + ) + interface View { fun close() fun hideLoading() @@ -81,9 +83,5 @@ class EditSuperHeroPresenter( } interface EditSuperHeroListener { - fun onSaveSuperHeroSelected( - name: String, - description: String, - isAvenger: Boolean - ) + fun onSaveSuperHeroSelected(editableSuperHero: EditSuperHeroPresenter.EditableSuperHero) } \ No newline at end of file diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/ui/presenter/SuperHeroesPresenter.kt b/app/src/main/java/com/karumi/jetpack/superheroes/ui/presenter/SuperHeroesPresenter.kt index 6be0b79..534ad7a 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/ui/presenter/SuperHeroesPresenter.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/ui/presenter/SuperHeroesPresenter.kt @@ -13,7 +13,7 @@ class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes, private val executor: ExecutorService -) : LifecycleObserver { +) : SuperHeroesListener, LifecycleObserver { private val view: View? by weak(view) @@ -37,7 +37,7 @@ class SuperHeroesPresenter( } } - fun onSuperHeroClicked(superHero: SuperHero) { + override fun onSuperHeroClicked(superHero: SuperHero) { view?.openDetail(superHero.id) } @@ -48,4 +48,8 @@ class SuperHeroesPresenter( fun showSuperHeroes(superHeroes: List) fun openDetail(id: String) } +} + +interface SuperHeroesListener { + fun onSuperHeroClicked(superHero: SuperHero) } \ No newline at end of file diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/EditSuperHeroActivity.kt b/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/EditSuperHeroActivity.kt index 88bce13..96c9726 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/EditSuperHeroActivity.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/EditSuperHeroActivity.kt @@ -2,7 +2,6 @@ package com.karumi.jetpack.superheroes.ui.view import android.app.Activity import android.content.Intent -import android.os.Bundle import androidx.appcompat.widget.Toolbar import com.karumi.jetpack.superheroes.R import com.karumi.jetpack.superheroes.common.module @@ -11,7 +10,6 @@ import com.karumi.jetpack.superheroes.domain.model.SuperHero import com.karumi.jetpack.superheroes.domain.usecase.GetSuperHeroById import com.karumi.jetpack.superheroes.domain.usecase.SaveSuperHero import com.karumi.jetpack.superheroes.ui.presenter.EditSuperHeroPresenter -import com.karumi.jetpack.superheroes.ui.utils.setImageBackground import kotlinx.android.synthetic.main.edit_super_hero_activity.* import org.kodein.di.erased.bind import org.kodein.di.erased.instance @@ -37,17 +35,6 @@ class EditSuperHeroActivity : get() = toolbar private val superHeroId: String by lazy { intent?.extras?.getString(SUPER_HERO_ID_KEY) ?: "" } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - bt_save_edition.setOnClickListener { - presenter.onSaveSuperHeroSelected( - name = et_super_hero_name.text?.toString() ?: "", - description = et_super_hero_description.text?.toString() ?: "", - isAvenger = cb_is_avenger.isChecked - ) - } - } - override fun configureBinding(binding: EditSuperHeroActivityBinding) { binding.listener = presenter binding.isLoading = false @@ -71,8 +58,9 @@ class EditSuperHeroActivity : } override fun showSuperHero(superHero: SuperHero) = runOnUiThread { + binding.listener = presenter binding.superHero = superHero - iv_super_hero_photo.setImageBackground(superHero.photo) + binding.editableSuperHero = superHero.toEditable() } override val activityModules = module { @@ -82,4 +70,7 @@ class EditSuperHeroActivity : bind() with provider { GetSuperHeroById(instance()) } bind() with provider { SaveSuperHero(instance()) } } + + private fun SuperHero.toEditable() = + EditSuperHeroPresenter.EditableSuperHero(isAvenger, name, description) } \ No newline at end of file diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/SuperHeroDetailActivity.kt b/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/SuperHeroDetailActivity.kt index c333f5b..ad3e141 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/SuperHeroDetailActivity.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/SuperHeroDetailActivity.kt @@ -9,7 +9,6 @@ import com.karumi.jetpack.superheroes.databinding.SuperHeroDetailActivityBinding import com.karumi.jetpack.superheroes.domain.model.SuperHero import com.karumi.jetpack.superheroes.domain.usecase.GetSuperHeroById import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroDetailPresenter -import com.karumi.jetpack.superheroes.ui.utils.setImageBackground import kotlinx.android.synthetic.main.super_hero_detail_activity.* import org.kodein.di.erased.bind import org.kodein.di.erased.instance @@ -57,7 +56,6 @@ class SuperHeroDetailActivity : override fun showSuperHero(superHero: SuperHero) = runOnUiThread { title = superHero.name binding.superHero = superHero - iv_super_hero_photo.setImageBackground(superHero.photo) } override fun openEditSuperHero(superHeroId: String) = runOnUiThread { diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/adapter/SuperHeroViewHolder.kt b/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/adapter/SuperHeroViewHolder.kt index 04ef259..13e329a 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/adapter/SuperHeroViewHolder.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/adapter/SuperHeroViewHolder.kt @@ -1,43 +1,18 @@ package com.karumi.jetpack.superheroes.ui.view.adapter -import android.view.View -import android.widget.ImageView -import android.widget.TextView import androidx.recyclerview.widget.RecyclerView -import com.karumi.jetpack.superheroes.R +import com.karumi.jetpack.superheroes.databinding.SuperHeroRowBinding import com.karumi.jetpack.superheroes.domain.model.SuperHero -import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroesPresenter -import com.karumi.jetpack.superheroes.ui.utils.setImageBackground +import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroesListener class SuperHeroViewHolder( - itemView: View, - private val presenter: SuperHeroesPresenter -) : RecyclerView.ViewHolder(itemView) { - - private val photoImageView: ImageView = itemView.findViewById(R.id.iv_super_hero_photo) - private val nameTextView: TextView = itemView.findViewById(R.id.tv_super_hero_name) - private val avengersBadgeView: View = itemView.findViewById(R.id.iv_avengers_badge) + private val binding: SuperHeroRowBinding, + private val listener: SuperHeroesListener +) : RecyclerView.ViewHolder(binding.root) { fun render(superHero: SuperHero) { - hookListeners(superHero) - renderSuperHeroPhoto(superHero.photo) - renderSuperHeroName(superHero.name) - renderAvengersBadge(superHero.isAvenger) - } - - private fun hookListeners(superHero: SuperHero) { - itemView.setOnClickListener { presenter.onSuperHeroClicked(superHero) } - } - - private fun renderSuperHeroPhoto(photo: String?) { - photoImageView.setImageBackground(photo) - } - - private fun renderSuperHeroName(name: String) { - nameTextView.text = name - } - - private fun renderAvengersBadge(isAvenger: Boolean) { - avengersBadgeView.visibility = if (isAvenger) View.VISIBLE else View.GONE + binding.superHero = superHero + binding.listener = listener + binding.executePendingBindings() } } \ No newline at end of file diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/adapter/SuperHeroesAdapter.kt b/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/adapter/SuperHeroesAdapter.kt index a3988b4..fabd7f7 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/adapter/SuperHeroesAdapter.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/ui/view/adapter/SuperHeroesAdapter.kt @@ -2,13 +2,15 @@ package com.karumi.jetpack.superheroes.ui.view.adapter import android.view.LayoutInflater import android.view.ViewGroup +import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.RecyclerView import com.karumi.jetpack.superheroes.R +import com.karumi.jetpack.superheroes.databinding.SuperHeroRowBinding import com.karumi.jetpack.superheroes.domain.model.SuperHero -import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroesPresenter +import com.karumi.jetpack.superheroes.ui.presenter.SuperHeroesListener internal class SuperHeroesAdapter( - private val presenter: SuperHeroesPresenter + private val listener: SuperHeroesListener ) : RecyclerView.Adapter() { private val superHeroes: MutableList = ArrayList() @@ -17,11 +19,14 @@ internal class SuperHeroesAdapter( } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SuperHeroViewHolder { - val view = LayoutInflater.from(parent.context).inflate( - R.layout.super_hero_row, parent, + val binding: SuperHeroRowBinding = DataBindingUtil.inflate( + LayoutInflater.from(parent.context), + R.layout.super_hero_row, + parent, false ) - return SuperHeroViewHolder(view, presenter) + + return SuperHeroViewHolder(binding, listener) } override fun onBindViewHolder(holder: SuperHeroViewHolder, position: Int) { diff --git a/app/src/main/res/layout/edit_super_hero_activity.xml b/app/src/main/res/layout/edit_super_hero_activity.xml index b73e69f..8b033f4 100644 --- a/app/src/main/res/layout/edit_super_hero_activity.xml +++ b/app/src/main/res/layout/edit_super_hero_activity.xml @@ -15,6 +15,10 @@ name="superHero" type="com.karumi.jetpack.superheroes.domain.model.SuperHero" /> + + @@ -46,6 +50,7 @@ android:id="@+id/iv_super_hero_photo" android:layout_width="match_parent" android:layout_height="@dimen/super_hero_detail_header_height" + app:imageUrl="@{ superHero.photo }" app:layout_constraintTop_toTopOf="parent" tools:background="@color/color_primary_dark" tools:ignore="ContentDescription" /> @@ -68,7 +73,7 @@ android:layout_height="wrap_content" android:layout_margin="8dp" android:buttonTint="@color/white" - android:checked="@{ superHero.avenger }" + android:checked="@={ editableSuperHero.avenger, default = false }" android:enabled="@{ superHero != null }" android:text="@string/is_super_hero_an_avenger_checkbox" android:textColor="@color/white" @@ -92,7 +97,7 @@ android:hint="@string/edit_super_hero_name_hint" android:importantForAutofill="no" android:inputType="text" - android:text="@{ superHero.name }" + android:text="@={ editableSuperHero.name }" android:textColor="@color/white" tools:ignore="UnusedAttribute" /> @@ -116,7 +121,7 @@ android:importantForAutofill="no" android:inputType="textMultiLine" android:scrollbars="vertical" - android:text="@{ superHero.description }" + android:text="@={ editableSuperHero.description }" android:textColor="@color/white" tools:ignore="UnusedAttribute" /> @@ -150,6 +155,7 @@ android:layout_height="56dp" android:background="@color/royal_blue" android:enabled="@{ superHero != null }" + android:onClick="@{ () -> listener.onSaveSuperHeroSelected(editableSuperHero) }" android:text="@string/save_edited_super_hero_button" android:textColor="@color/white" app:layout_constraintBottom_toBottomOf="parent" diff --git a/app/src/main/res/layout/super_hero_detail_activity.xml b/app/src/main/res/layout/super_hero_detail_activity.xml index 1dbffa8..256e817 100644 --- a/app/src/main/res/layout/super_hero_detail_activity.xml +++ b/app/src/main/res/layout/super_hero_detail_activity.xml @@ -43,6 +43,7 @@ android:id="@+id/iv_super_hero_photo" android:layout_width="match_parent" android:layout_height="@dimen/super_hero_detail_header_height" + app:imageUrl="@{ superHero.photo }" app:layout_constraintTop_toBottomOf="@id/toolbar" tools:background="@color/color_primary_dark" tools:ignore="ContentDescription" /> diff --git a/app/src/main/res/layout/super_hero_row.xml b/app/src/main/res/layout/super_hero_row.xml index 9e2399d..3bec5ff 100644 --- a/app/src/main/res/layout/super_hero_row.xml +++ b/app/src/main/res/layout/super_hero_row.xml @@ -1,41 +1,61 @@ - + xmlns:tools="http://schemas.android.com/tools"> - + + + + + - + + + - - - - - - \ No newline at end of file + android:layout_height="@dimen/super_hero_row_height" + android:onClick="@{ () -> listener.onSuperHeroClicked(superHero) }"> + + + + + + + + + + +