Skip to content

Commit

Permalink
Migrate DetailActivity from views to jetpack compose.
Browse files Browse the repository at this point in the history
  • Loading branch information
houssemeddine daoud committed Feb 18, 2023
1 parent 3ba6fb1 commit 2d61638
Show file tree
Hide file tree
Showing 10 changed files with 645 additions and 532 deletions.
29 changes: 24 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ android {

buildFeatures {
dataBinding = true
compose = true
}

hilt {
Expand Down Expand Up @@ -73,6 +74,14 @@ android {
lint {
abortOnError = false
}

kotlinOptions {
jvmTarget = "1.8"
}

composeOptions {
kotlinCompilerExtensionVersion = "1.4.0"
}
}

dependencies {
Expand Down Expand Up @@ -109,6 +118,7 @@ dependencies {
// image loading
implementation(libs.glide)
implementation(libs.glide.palette)
implementation(libs.glide.compose)

// bundler
implementation(libs.bundler)
Expand All @@ -120,11 +130,6 @@ dependencies {
implementation(libs.recyclerview)
implementation(libs.baseAdapter)

// custom views
implementation(libs.rainbow)
implementation(libs.androidRibbon)
implementation(libs.progressView)

// unit test
testImplementation(libs.junit)
testImplementation(libs.turbine)
Expand All @@ -136,4 +141,18 @@ dependencies {
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso)
androidTestImplementation(libs.android.test.runner)

// compose
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom)
implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.foundation.layout)
implementation(libs.androidx.compose.material.iconsExtended)
implementation(libs.androidx.compose.material3)
debugImplementation(libs.androidx.compose.ui.tooling)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.ui.util)
implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.compose.runtime.livedata)
implementation(libs.androidx.constraintlayout.compose)
}
112 changes: 0 additions & 112 deletions app/src/main/kotlin/com/skydoves/pokedex/binding/ViewBinding.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,14 @@

package com.skydoves.pokedex.binding

import android.graphics.Color
import android.graphics.Typeface
import android.view.Gravity
import android.view.View
import android.view.WindowManager
import android.widget.Toast
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatImageView
import androidx.databinding.BindingAdapter
import com.bumptech.glide.Glide
import com.github.florent37.glidepalette.BitmapPalette
import com.github.florent37.glidepalette.GlidePalette
import com.google.android.material.card.MaterialCardView
import com.skydoves.androidribbon.RibbonRecyclerView
import com.skydoves.androidribbon.ribbonView
import com.skydoves.pokedex.core.model.PokemonInfo
import com.skydoves.pokedex.utils.PokemonTypeUtils
import com.skydoves.pokedex.utils.SpacesItemDecoration
import com.skydoves.progressview.ProgressView
import com.skydoves.rainbow.Rainbow
import com.skydoves.rainbow.RainbowOrientation
import com.skydoves.rainbow.color
import com.skydoves.whatif.whatIfNotNullOrEmpty

object ViewBinding {
Expand Down Expand Up @@ -68,38 +53,6 @@ object ViewBinding {
).into(view)
}

@JvmStatic
@BindingAdapter("paletteImage", "paletteView")
fun bindLoadImagePaletteView(view: AppCompatImageView, url: String, paletteView: View) {
val context = view.context
Glide.with(context)
.load(url)
.listener(
GlidePalette.with(url)
.use(BitmapPalette.Profile.MUTED_LIGHT)
.intoCallBack { palette ->
val light = palette?.lightVibrantSwatch?.rgb
val domain = palette?.dominantSwatch?.rgb
if (domain != null) {
if (light != null) {
Rainbow(paletteView).palette {
+color(domain)
+color(light)
}.background(orientation = RainbowOrientation.TOP_BOTTOM)
} else {
paletteView.setBackgroundColor(domain)
}
if (context is AppCompatActivity) {
context.window.apply {
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
statusBarColor = domain
}
}
}
}.crossfade(true)
).into(view)
}

@JvmStatic
@BindingAdapter("gone")
fun bindGone(view: View, shouldBeGone: Boolean) {
Expand All @@ -109,69 +62,4 @@ object ViewBinding {
View.VISIBLE
}
}

@JvmStatic
@BindingAdapter("onBackPressed")
fun bindOnBackPressed(view: View, onBackPress: Boolean) {
val context = view.context
if (onBackPress && context is OnBackPressedDispatcherOwner) {
view.setOnClickListener {
context.onBackPressedDispatcher.onBackPressed()
}
}
}

@JvmStatic
@BindingAdapter("bindPokemonTypes")
fun bindPokemonTypes(recyclerView: RibbonRecyclerView, types: List<PokemonInfo.TypeResponse>?) {
types.whatIfNotNullOrEmpty {
recyclerView.clear()
for (type in it) {
with(recyclerView) {
addRibbon(
ribbonView(context) {
setText(type.type.name)
setTextColor(Color.WHITE)
setPaddingLeft(84f)
setPaddingRight(84f)
setPaddingTop(2f)
setPaddingBottom(10f)
setTextSize(16f)
setRibbonRadius(120f)
setTextStyle(Typeface.BOLD)
setRibbonBackgroundColorResource(
PokemonTypeUtils.getTypeColor(type.type.name)
)
}.apply {
maxLines = 1
gravity = Gravity.CENTER
}
)
addItemDecoration(SpacesItemDecoration())
}
}
}
}

@JvmStatic
@BindingAdapter("progressView_labelText")
fun bindProgressViewLabelText(progressView: ProgressView, text: String?) {
progressView.labelText = text
}

@JvmStatic
@BindingAdapter("progressView_progress")
fun bindProgressViewProgress(progressView: ProgressView, value: Int?) {
if (value != null) {
progressView.progress = value.toFloat()
}
}

@JvmStatic
@BindingAdapter("progressView_max")
fun bindProgressViewMax(progressView: ProgressView, value: Int?) {
if (value != null) {
progressView.max = value.toFloat()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.skydoves.pokedex.ui.details

import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

@Composable
internal fun AnimatedProgressBar(
progressValue: Int,
maximumValue: Int,
progressColor: Color,
modifier: Modifier,
) {

var value by remember { mutableStateOf(0) }
LaunchedEffect(progressValue) {
value = progressValue
}
// state represent the percent of the progress value compared with maximum value
val percent by remember(value) {
derivedStateOf {
value.toFloat() / maximumValue.toFloat()
}
}
val animatedPercent by animateFloatAsState(
targetValue = percent, tween(
durationMillis = 1500, delayMillis = 0, easing = LinearOutSlowInEasing
)
)
val shape = RoundedCornerShape(50)
Box(
modifier = modifier
.height(18.dp)
.background(
color = Color.White, shape = shape
)
) {
Box(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(animatedPercent)
.background(
color = progressColor, shape = shape
)
)
Text(
text = "$progressValue/$maximumValue",
fontSize = 12.sp,
color = Color.White,
modifier = Modifier
.fillMaxWidth(animatedPercent)
.padding(end = 5.dp)
.align(Alignment.CenterStart),
textAlign = TextAlign.End
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,51 @@ package com.skydoves.pokedex.ui.details
import android.os.Bundle
import androidx.activity.viewModels
import androidx.annotation.VisibleForTesting
import com.skydoves.bindables.BindingActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.ComposeView
import com.skydoves.bundler.bundleNonNull
import com.skydoves.bundler.intentOf
import com.skydoves.pokedex.R
import com.skydoves.pokedex.core.model.Pokemon
import com.skydoves.pokedex.databinding.ActivityDetailBinding
import com.skydoves.transformationlayout.TransformationCompat
import com.skydoves.transformationlayout.TransformationCompat.onTransformationEndContainerApplyParams
import com.skydoves.transformationlayout.TransformationLayout
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class DetailActivity : BindingActivity<ActivityDetailBinding>(R.layout.activity_detail) {
class DetailActivity : AppCompatActivity() {

@set:Inject
internal lateinit var detailViewModelFactory: DetailViewModel.AssistedFactory

@get:VisibleForTesting
internal val viewModel: DetailViewModel by viewModels {
DetailViewModel.provideFactory(detailViewModelFactory, pokemon.name)
DetailViewModel.provideFactory(detailViewModelFactory, pokemon)
}

private val pokemon: Pokemon by bundleNonNull(EXTRA_POKEMON)

override fun onCreate(savedInstanceState: Bundle?) {
onTransformationEndContainerApplyParams(this)
super.onCreate(savedInstanceState)
binding.pokemon = pokemon
binding.vm = viewModel
setContentView(
ComposeView(this).apply {
setContent {
MaterialTheme {
val uiState by viewModel.uiState.collectAsState()
DetailScreen(
uiState = uiState,
onBackIconPressed = {
finish()
}
)
}
}
}
)
}

companion object {
Expand Down

0 comments on commit 2d61638

Please sign in to comment.