Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@ dependencies {
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.10.0'
}
17 changes: 17 additions & 0 deletions app/src/main/java/otus/homework/coroutines/CatImage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package otus.homework.coroutines

import com.google.gson.annotations.SerializedName

data class CatsImage(
@field:SerializedName("id")
val id: String,

@field:SerializedName("url")
val url: String,

@field:SerializedName("width")
val width: Int,

@field:SerializedName("height")
val height: Int
)
6 changes: 6 additions & 0 deletions app/src/main/java/otus/homework/coroutines/CatsInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package otus.homework.coroutines

data class CatsInfo(
val fact: Fact,
val img: CatsImage,
)
72 changes: 60 additions & 12 deletions app/src/main/java/otus/homework/coroutines/CatsPresenter.kt
Original file line number Diff line number Diff line change
@@ -1,35 +1,83 @@
package otus.homework.coroutines

import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlin.coroutines.cancellation.CancellationException


class CatsPresenter(
private val catsService: CatsService
) {

private val presenterScope =
CoroutineScope(Dispatchers.Main + CoroutineName("CatsCoroutine"))
private var job: Job? = null
private var _catsView: ICatsView? = null

fun onInitComplete() {
catsService.getCatFact().enqueue(object : Callback<Fact> {

override fun onResponse(call: Call<Fact>, response: Response<Fact>) {
if (response.isSuccessful && response.body() != null) {
_catsView?.populate(response.body()!!)
job?.cancel()
job = presenterScope.launch {
try {
coroutineScope {
val fact = async {
catsService.getCatFact()
}
val image = async {
catsService.getRandomImg(IMG_URL).getOrNull(0)
?: throw NullPointerException()
}
val factResult = fact.await()
val imageResult = image.await()
_catsView?.populate(
catsInfo =
CatsInfo(
fact = factResult,
img = imageResult,
)
)
}
} catch (e: Exception) {
handelError(e)
}
}
}

override fun onFailure(call: Call<Fact>, t: Throwable) {
CrashMonitor.trackWarning()
private fun handelError(e: Exception) {
CrashMonitor.trackWarning()
val errorMsg = when (e) {
is java.net.SocketTimeoutException -> "Не удалось получить ответ от сервером"
is CancellationException -> ""
else -> e.message
}
errorMsg?.let { errorMsg ->
if (errorMsg.isNotEmpty()) {
_catsView?.showToast(errorMsg)
}
})
}
}

fun attachView(catsView: ICatsView) {
_catsView = catsView
}

fun clear() {
job?.cancel()
}

fun detachView() {
_catsView = null
}

fun onDestroy() {
presenterScope.cancel()
}

companion object {
const val IMG_URL = "https://api.thecatapi.com/v1/images/search"
}
}
7 changes: 5 additions & 2 deletions app/src/main/java/otus/homework/coroutines/CatsService.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package otus.homework.coroutines

import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Url

interface CatsService {

@GET("fact")
fun getCatFact() : Call<Fact>
suspend fun getCatFact() : Fact

@GET
suspend fun getRandomImg(@Url url: String) : Array<CatsImage>
}
22 changes: 18 additions & 4 deletions app/src/main/java/otus/homework/coroutines/CatsView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,44 @@ package otus.homework.coroutines
import android.content.Context
import android.util.AttributeSet
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout
import com.squareup.picasso.Picasso

class CatsView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), ICatsView {

var presenter :CatsPresenter? = null
var presenter: CatsPresenter? = null
private lateinit var imageView: ImageView

override fun onFinishInflate() {
super.onFinishInflate()
findViewById<Button>(R.id.button).setOnClickListener {
presenter?.onInitComplete()
}
imageView = findViewById(R.id.cat_imageView)
}

override fun populate(fact: Fact) {
findViewById<TextView>(R.id.fact_textView).text = fact.fact
override fun populate(catsInfo: CatsInfo) {
findViewById<TextView>(R.id.fact_textView).text = catsInfo.fact.fact
Picasso.get()
.load(catsInfo.img.url)
.resize(catsInfo.img.width, catsInfo.img.width)
.into(imageView)
}

override fun showToast(text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
}

interface ICatsView {

fun populate(fact: Fact)
fun populate(catsInfo: CatsInfo)
fun showToast(text: String)
}
71 changes: 71 additions & 0 deletions app/src/main/java/otus/homework/coroutines/CatsViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package otus.homework.coroutines

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlin.coroutines.cancellation.CancellationException

class CatsViewModel(
private val catsService: CatsService
) : ViewModel() {

private var job: Job? = null
private val _state = MutableStateFlow<Result<CatsInfo>>(Result.None)
internal val state: StateFlow<Result<CatsInfo>> = _state.asStateFlow()
private val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
CrashMonitor.trackWarning()
handelError(throwable)
}

fun onInitComplete() {
job?.cancel()
_state.value = Result.None
job = viewModelScope.launch(exceptionHandler) {
coroutineScope {
val fact = async {
catsService.getCatFact()
}
val image = async {
catsService.getRandomImg(IMG_URL).getOrNull(0)
?: throw NullPointerException()
}
val factResult = fact.await()
val imageResult = image.await()
_state.value = Result.Success(CatsInfo(
fact = factResult,
img = imageResult,
))
}
}
}

private fun handelError(e: Throwable) {
CrashMonitor.trackWarning()
if (e is CancellationException) throw e
val errorMsg = when (e) {
is java.net.SocketTimeoutException -> "Не удалось получить ответ от сервером"
else -> e.message
}
errorMsg?.let { errorMsg ->
if (errorMsg.isNotEmpty()) {
_state.value = Result.Error(errorMsg)
}
}
}

fun clear() {
job?.cancel()
_state.value = Result.None
}

companion object {
const val IMG_URL = "https://api.thecatapi.com/v1/images/search"
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/otus/homework/coroutines/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@ class MainActivity : AppCompatActivity() {

override fun onStop() {
if (isFinishing) {
catsPresenter.clear()
catsPresenter.detachView()
}
super.onStop()
}

override fun onDestroy() {
super.onDestroy()
catsPresenter.onDestroy()
}
}
7 changes: 7 additions & 0 deletions app/src/main/java/otus/homework/coroutines/Result.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package otus.homework.coroutines

internal sealed class Result<out T> {
object None: Result<Nothing>()
data class Success<out T>(val data: T) : Result<T>()
data class Error(val errorMsg: String) : Result<Nothing>()
}
12 changes: 10 additions & 2 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,26 @@
android:textSize="24sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<ImageView
android:id="@+id/cat_imageView"
android:layout_width="0.dp"
android:layout_height="0.dp"
android:contentDescription=""
app:layout_constraintTop_toBottomOf="@+id/fact_textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/more_facts"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fact_textView" />
app:layout_constraintTop_toBottomOf="@+id/cat_imageView" />

</otus.homework.coroutines.CatsView>