diff --git a/app/build.gradle b/app/build.gradle index a414e0e8..d32454a9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,4 +41,6 @@ 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.9.1") + implementation("androidx.fragment:fragment-ktx:1.8.8") } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt b/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt index e4b05120..252d67af 100644 --- a/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt +++ b/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt @@ -1,28 +1,82 @@ package otus.homework.coroutines -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.async +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.net.SocketTimeoutException class CatsPresenter( private val catsService: CatsService ) { - + private val scope = CoroutineScope( + Dispatchers.Main.immediate + SupervisorJob() + CoroutineName("CatsCoroutine") + ) private var _catsView: ICatsView? = null + + /** + * Не понял по условию задачи, что нужно делать, если в одном из запросов ошибка, + * а вдругом нет и на чьи ошибки надо показывать тосты. Тут тосты показываются только на ошибки + * в запросе фактов, и ошибка в одном из запросов не отменяет другой + * + * Во вьюмоделе сделаю, чтоб ошибка в любом из запросов отменяет оба запроса и ошибки из любого + * выводятся в текстовое поле + */ fun onInitComplete() { - catsService.getCatFact().enqueue(object : Callback { + scope.coroutineContext.cancelChildren() + val factJob = scope.async(Dispatchers.IO) { + try { + val response = catsService.getCatFact() + if (response.isSuccessful) { + response.body() + } else { + null + } + } catch (e: Throwable) { + if (e is CancellationException) throw e + when (e) { + is SocketTimeoutException -> { + withContext(Dispatchers.Main) { + _catsView?.showToast("Не удалось получить ответ от сервера") + } + } - override fun onResponse(call: Call, response: Response) { - if (response.isSuccessful && response.body() != null) { - _catsView?.populate(response.body()!!) + else -> { + CrashMonitor.trackWarning() + withContext(Dispatchers.Main) { + _catsView?.showToast(e.message) + } + } } + null } + } - override fun onFailure(call: Call, t: Throwable) { - CrashMonitor.trackWarning() + val imageJob = scope.async(Dispatchers.IO) { + try { + val response = catsService.getRandomImage() + if (response.isSuccessful) { + response.body()?.firstOrNull() + } else { + null + } + } catch (e: Throwable) { + if (e is CancellationException) throw e + null } - }) + } + + scope.launch { + val fact = factJob.await() + val image = imageJob.await() + _catsView?.populate(CatsUiModel(fact?.fact, image?.url)) + } } fun attachView(catsView: ICatsView) { @@ -31,5 +85,6 @@ class CatsPresenter( fun detachView() { _catsView = null + scope.coroutineContext.cancelChildren() } } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/CatsService.kt b/app/src/main/java/otus/homework/coroutines/CatsService.kt index 479b2cfb..94f15fb4 100644 --- a/app/src/main/java/otus/homework/coroutines/CatsService.kt +++ b/app/src/main/java/otus/homework/coroutines/CatsService.kt @@ -1,10 +1,15 @@ package otus.homework.coroutines -import retrofit2.Call +import retrofit2.Response import retrofit2.http.GET interface CatsService { @GET("fact") - fun getCatFact() : Call + suspend fun getCatFact(): Response + + @GET("https://api.thecatapi.com/v1/images/search") + suspend fun getRandomImage(): Response> + + } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/CatsUiModel.kt b/app/src/main/java/otus/homework/coroutines/CatsUiModel.kt new file mode 100644 index 00000000..a304a218 --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/CatsUiModel.kt @@ -0,0 +1,3 @@ +package otus.homework.coroutines + +data class CatsUiModel(val fact: String? = null, val image: String? = null) \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/CatsView.kt b/app/src/main/java/otus/homework/coroutines/CatsView.kt index be04b2a8..b5d87f92 100644 --- a/app/src/main/java/otus/homework/coroutines/CatsView.kt +++ b/app/src/main/java/otus/homework/coroutines/CatsView.kt @@ -3,8 +3,11 @@ 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, @@ -12,21 +15,50 @@ class CatsView @JvmOverloads constructor( defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr), ICatsView { - var presenter :CatsPresenter? = null + var presenter: CatsPresenter? = null + private lateinit var imageView: ImageView + private lateinit var textView: TextView + var onClick: (() -> Unit)? = null override fun onFinishInflate() { super.onFinishInflate() findViewById