diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml index f17e2a2..69ae5ab 100644 --- a/app/src/main/res/navigation/main_navigation.xml +++ b/app/src/main/res/navigation/main_navigation.xml @@ -1,6 +1,10 @@ + + diff --git a/core/src/main/java/dev/carlos/core/domain/network/NetworkErrors.kt b/core/src/main/java/dev/carlos/core/domain/network/NetworkErrors.kt index 83e931f..39c676e 100644 --- a/core/src/main/java/dev/carlos/core/domain/network/NetworkErrors.kt +++ b/core/src/main/java/dev/carlos/core/domain/network/NetworkErrors.kt @@ -1,7 +1,10 @@ package dev.carlos.core.domain.network -enum class RequestError { - NO_NETWORK, - BAD_RESPONSE, - UNKNOWN_PROBLEM +import androidx.annotation.StringRes +import dev.carlos.core.R + +enum class RequestError(@StringRes val message: Int) { + NO_NETWORK(R.string.network_error_no_network), + BAD_RESPONSE(R.string.network_error_bad_response), + UNKNOWN_PROBLEM(R.string.network_error_unknown) } diff --git a/core/src/main/java/dev/carlos/core/extensions/DataExtensions.kt b/core/src/main/java/dev/carlos/core/extensions/DataExtensions.kt index 1176e23..c9b501b 100644 --- a/core/src/main/java/dev/carlos/core/extensions/DataExtensions.kt +++ b/core/src/main/java/dev/carlos/core/extensions/DataExtensions.kt @@ -5,7 +5,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.Observer fun LiveData.observeNonNull(owner: LifecycleOwner, func: (T) -> Unit) { - observe(owner, Observer { + observe(owner, { it?.let { func(it) } diff --git a/core/src/main/java/dev/carlos/core/extensions/StringsExtensions.kt b/core/src/main/java/dev/carlos/core/extensions/StringsExtensions.kt new file mode 100644 index 0000000..e61c927 --- /dev/null +++ b/core/src/main/java/dev/carlos/core/extensions/StringsExtensions.kt @@ -0,0 +1,5 @@ +package dev.carlos.core.extensions + +import java.util.* + +fun String.capitalize() = replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } diff --git a/core/src/main/res/drawable/bg_card.xml b/core/src/main/res/drawable/bg_card.xml new file mode 100644 index 0000000..f686445 --- /dev/null +++ b/core/src/main/res/drawable/bg_card.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/core/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml index 09837df..9c8f149 100644 --- a/core/src/main/res/values/colors.xml +++ b/core/src/main/res/values/colors.xml @@ -7,4 +7,7 @@ #FF018786 #FF000000 #FFFFFFFF - \ No newline at end of file + #EEEEEE + #00000000 + #FFFFFF + diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 75f77d2..696b69b 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -1,3 +1,5 @@ - core - \ No newline at end of file + Respuesta inesperada + Sin conexión + Error desconocido + diff --git a/shortform/src/main/java/dev/carlos/shortform/feature/ShortformFragment.kt b/shortform/src/main/java/dev/carlos/shortform/feature/ShortformFragment.kt index b787a69..3f8cc3e 100644 --- a/shortform/src/main/java/dev/carlos/shortform/feature/ShortformFragment.kt +++ b/shortform/src/main/java/dev/carlos/shortform/feature/ShortformFragment.kt @@ -1,4 +1,90 @@ package dev.carlos.shortform.feature -class ShortformFragment { +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager +import dev.carlos.core.domain.network.RequestError +import dev.carlos.core.domain.network.RequestState +import dev.carlos.core.extensions.gone +import dev.carlos.core.extensions.observeNonNull +import dev.carlos.core.extensions.visible +import dev.carlos.shortform.R +import dev.carlos.shortform.data.models.ShortformModel +import dev.carlos.shortform.databinding.FragmentShortformDefinitionBinding +import dev.carlos.shortform.viewmodels.ShortformViewmodel +import dev.carlos.shortform.widgets.LongformAdapter +import org.koin.androidx.viewmodel.ext.android.viewModel + +class ShortformFragment : Fragment() { + private val viewModel: ShortformViewmodel by viewModel() + private val listAdapter by lazy { LongformAdapter() } + private lateinit var binding: FragmentShortformDefinitionBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View = inflater.inflate(R.layout.fragment_shortform_definition, container, false) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setBinding(view) + setRecycler() + setObservers() + } + + private fun setBinding(view: View) { + binding = FragmentShortformDefinitionBinding.bind(view) + } + + private fun setObservers() { + viewModel.acronymDefinition.observeNonNull(this) { + handleResult(it) + } + } + + private fun handleResult(state: RequestState) { + when (state) { + is RequestState.Success<*> -> handleSuccess(state.data as ShortformModel) + is RequestState.Empty -> handleEmpty() + is RequestState.Error -> handleError(state.type) + is RequestState.Loading -> handleLoading() + } + } + + private fun handleLoading() { + binding.shortformDefinitionLoading.visible() + binding.shortformDefinitionError.gone() + } + + private fun handleSuccess(shortform: ShortformModel) { + binding.shortformDefinitionError.gone() + binding.shortformDefinitionLoading.gone() + listAdapter.addAll(shortform.results) + } + + private fun handleEmpty() { + binding.shortformDefinitionErrorLabel.text = getString(R.string.shortform_definition_no_definition) + binding.shortformDefinitionError.visible() + binding.shortformDefinitionLoading.gone() + } + + private fun handleError(error: RequestError) { + binding.shortformDefinitionErrorLabel.text = getString(error.message) + binding.shortformDefinitionError.visible() + binding.shortformDefinitionLoading.gone() + } + + private fun setRecycler() { + binding.shortformDefinitionRecycler.apply { + layoutManager = LinearLayoutManager(context) + adapter = listAdapter.apply { + onItemClick = { + Toast.makeText(context, it.value, Toast.LENGTH_SHORT).show() + } + } + } + } } \ No newline at end of file diff --git a/shortform/src/main/java/dev/carlos/shortform/widgets/LongformAdapter.kt b/shortform/src/main/java/dev/carlos/shortform/widgets/LongformAdapter.kt new file mode 100644 index 0000000..6543abe --- /dev/null +++ b/shortform/src/main/java/dev/carlos/shortform/widgets/LongformAdapter.kt @@ -0,0 +1,60 @@ +package dev.carlos.shortform.widgets + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import dev.carlos.core.extensions.autoNotify +import dev.carlos.core.extensions.capitalize +import dev.carlos.shortform.R +import dev.carlos.shortform.data.models.ShortformModel +import dev.carlos.shortform.databinding.ItemLongformCardBinding +import kotlin.properties.Delegates + +class LongformAdapter : RecyclerView.Adapter() { + + private var items by Delegates.observable(emptyList()) { _, oldList, newList -> + autoNotify(oldList, newList) { old, new -> old.value == new.value } + notifyDataSetChanged() + } + + var onItemClick: (ShortformModel.LongformModel) -> Unit = { } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = + LongformViewHolder( + LayoutInflater + .from(parent.context) + .inflate(R.layout.item_longform_card, parent, false), + onItemClick + ) + + fun getItem(position: Int) = items[position] + + fun addAll(list: List) { + items = list + } + + override fun getItemCount() = items.size + + override fun onBindViewHolder(viewholder: RecyclerView.ViewHolder, position: Int) = + when (viewholder) { + is LongformViewHolder -> viewholder.bind(items[position]) + else -> throw NoWhenBranchMatchedException("Undefined viewholder") + } +} + +class LongformViewHolder( + private val view: View, + private val onItemClick: (ShortformModel.LongformModel) -> Unit +) : RecyclerView.ViewHolder(view) { + + private val binding = ItemLongformCardBinding.bind(view) + + fun bind(longform: ShortformModel.LongformModel) = with(view) { + binding.itemLongformName.text = longform.value.capitalize() + binding.itemLongformSince.text = view.resources.getString(R.string.item_longform_since, longform.since) + binding.itemLongformContainer.setOnClickListener { + onItemClick(longform) + } + } +} \ No newline at end of file diff --git a/shortform/src/main/res/layout/fragment_shortform_definition.xml b/shortform/src/main/res/layout/fragment_shortform_definition.xml new file mode 100644 index 0000000..0958fae --- /dev/null +++ b/shortform/src/main/res/layout/fragment_shortform_definition.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/shortform/src/main/res/layout/item_longform_card.xml b/shortform/src/main/res/layout/item_longform_card.xml new file mode 100644 index 0000000..e580f44 --- /dev/null +++ b/shortform/src/main/res/layout/item_longform_card.xml @@ -0,0 +1,38 @@ + + + + + + + + diff --git a/shortform/src/main/res/navigation/shortform_navigation.xml b/shortform/src/main/res/navigation/shortform_navigation.xml new file mode 100644 index 0000000..2ed5d1b --- /dev/null +++ b/shortform/src/main/res/navigation/shortform_navigation.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/shortform/src/main/res/values/strings.xml b/shortform/src/main/res/values/strings.xml index 5423e6c..be206d9 100644 --- a/shortform/src/main/res/values/strings.xml +++ b/shortform/src/main/res/values/strings.xml @@ -1,3 +1,5 @@ - acronyms + Desde %1$d + Buscar acrónimo + No existe una definición \ No newline at end of file