From bfe4deba1d31f20595bea89e8160f19bb1e7d1d6 Mon Sep 17 00:00:00 2001 From: m_kolobanova Date: Sat, 20 Sep 2025 14:02:03 +0300 Subject: [PATCH 1/4] Migrate from callback to suspend fun and coroutines --- .../otus/homework/coroutines/CatsPresenter.kt | 43 ++++++++++++------- .../otus/homework/coroutines/CatsService.kt | 2 +- .../otus/homework/coroutines/CrashMonitor.kt | 3 +- .../otus/homework/coroutines/MainActivity.kt | 12 +++++- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt b/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt index e4b05120..5d40301f 100644 --- a/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt +++ b/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt @@ -1,28 +1,35 @@ 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.cancel +import kotlinx.coroutines.launch +import otus.homework.coroutines.CrashMonitor.trackWarning +import java.net.SocketTimeoutException + class CatsPresenter( - private val catsService: CatsService + private val catsService: CatsService, + private val onShowToast: (String?) -> Unit ) { private var _catsView: ICatsView? = null - + private val presenterScope = CoroutineScope(Dispatchers.Main + CoroutineName("CatsCoroutine")) fun onInitComplete() { - catsService.getCatFact().enqueue(object : Callback { - - override fun onResponse(call: Call, response: Response) { - if (response.isSuccessful && response.body() != null) { - _catsView?.populate(response.body()!!) - } + presenterScope.launch { + try { + val fact = catsService.getCatFact() + _catsView?.populate(fact) + } catch (e: SocketTimeoutException) { + println("Caught $e") + onShowToast("Не удалось получить ответ от сервера") + + } catch (e: Exception) { + trackWarning(e.message) + onShowToast(e.message) } - - override fun onFailure(call: Call, t: Throwable) { - CrashMonitor.trackWarning() - } - }) + } } fun attachView(catsView: ICatsView) { @@ -32,4 +39,8 @@ class CatsPresenter( fun detachView() { _catsView = null } + + fun onStop() { + presenterScope.cancel() + } } \ 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..7373cf82 100644 --- a/app/src/main/java/otus/homework/coroutines/CatsService.kt +++ b/app/src/main/java/otus/homework/coroutines/CatsService.kt @@ -6,5 +6,5 @@ import retrofit2.http.GET interface CatsService { @GET("fact") - fun getCatFact() : Call + suspend fun getCatFact(): Fact } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/CrashMonitor.kt b/app/src/main/java/otus/homework/coroutines/CrashMonitor.kt index 32e6b018..40b650af 100644 --- a/app/src/main/java/otus/homework/coroutines/CrashMonitor.kt +++ b/app/src/main/java/otus/homework/coroutines/CrashMonitor.kt @@ -5,6 +5,7 @@ object CrashMonitor { /** * Pretend this is Crashlytics/AppCenter */ - fun trackWarning() { + fun trackWarning(message: String?) { + println("CrashMonitor: $message") } } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/MainActivity.kt b/app/src/main/java/otus/homework/coroutines/MainActivity.kt index a9dafb3b..ba898ffb 100644 --- a/app/src/main/java/otus/homework/coroutines/MainActivity.kt +++ b/app/src/main/java/otus/homework/coroutines/MainActivity.kt @@ -2,6 +2,10 @@ package otus.homework.coroutines import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import android.widget.Toast +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job class MainActivity : AppCompatActivity() { @@ -14,8 +18,7 @@ class MainActivity : AppCompatActivity() { val view = layoutInflater.inflate(R.layout.activity_main, null) as CatsView setContentView(view) - - catsPresenter = CatsPresenter(diContainer.service) + catsPresenter = CatsPresenter(diContainer.service, ::onShowToast) view.presenter = catsPresenter catsPresenter.attachView(view) catsPresenter.onInitComplete() @@ -24,7 +27,12 @@ class MainActivity : AppCompatActivity() { override fun onStop() { if (isFinishing) { catsPresenter.detachView() + catsPresenter.onStop() } super.onStop() } + + private fun onShowToast(message: String?) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show() + } } \ No newline at end of file From 536a6e9a06d67e15b04fb9fc19c2b043cc6c8dbf Mon Sep 17 00:00:00 2001 From: m_kolobanova Date: Sat, 20 Sep 2025 15:05:21 +0300 Subject: [PATCH 2/4] Add request random cats image --- .../otus/homework/coroutines/CatsPresenter.kt | 9 +++++++-- .../java/otus/homework/coroutines/CatsService.kt | 1 + .../java/otus/homework/coroutines/CatsView.kt | 14 +++++++++++--- .../java/otus/homework/coroutines/DiContainer.kt | 9 +++++++++ .../otus/homework/coroutines/ImagesService.kt | 10 ++++++++++ .../java/otus/homework/coroutines/MainActivity.kt | 5 +++-- .../otus/homework/coroutines/{ => model}/Fact.kt | 2 +- .../java/otus/homework/coroutines/model/Image.kt | 15 +++++++++++++++ .../java/otus/homework/coroutines/model/Model.kt | 6 ++++++ app/src/main/res/layout/activity_main.xml | 14 +++++++++++--- 10 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/otus/homework/coroutines/ImagesService.kt rename app/src/main/java/otus/homework/coroutines/{ => model}/Fact.kt (81%) create mode 100644 app/src/main/java/otus/homework/coroutines/model/Image.kt create mode 100644 app/src/main/java/otus/homework/coroutines/model/Model.kt diff --git a/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt b/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt index 5d40301f..c20130de 100644 --- a/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt +++ b/app/src/main/java/otus/homework/coroutines/CatsPresenter.kt @@ -3,14 +3,17 @@ package otus.homework.coroutines import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import otus.homework.coroutines.CrashMonitor.trackWarning +import otus.homework.coroutines.model.Model import java.net.SocketTimeoutException class CatsPresenter( private val catsService: CatsService, + private val imagesService: ImagesService, private val onShowToast: (String?) -> Unit ) { @@ -19,8 +22,10 @@ class CatsPresenter( fun onInitComplete() { presenterScope.launch { try { - val fact = catsService.getCatFact() - _catsView?.populate(fact) + val fact = async{catsService.getCatFact()} + val image = async{imagesService.getImage().firstOrNull()} + + _catsView?.populate(Model(fact.await(), image.await()?.url)) } catch (e: SocketTimeoutException) { println("Caught $e") onShowToast("Не удалось получить ответ от сервера") diff --git a/app/src/main/java/otus/homework/coroutines/CatsService.kt b/app/src/main/java/otus/homework/coroutines/CatsService.kt index 7373cf82..63f35fe1 100644 --- a/app/src/main/java/otus/homework/coroutines/CatsService.kt +++ b/app/src/main/java/otus/homework/coroutines/CatsService.kt @@ -1,5 +1,6 @@ package otus.homework.coroutines +import otus.homework.coroutines.model.Fact import retrofit2.Call import retrofit2.http.GET diff --git a/app/src/main/java/otus/homework/coroutines/CatsView.kt b/app/src/main/java/otus/homework/coroutines/CatsView.kt index be04b2a8..d6af218d 100644 --- a/app/src/main/java/otus/homework/coroutines/CatsView.kt +++ b/app/src/main/java/otus/homework/coroutines/CatsView.kt @@ -3,8 +3,13 @@ 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 androidx.constraintlayout.widget.ConstraintLayout +import com.squareup.picasso.Picasso +import otus.homework.coroutines.model.Fact +import otus.homework.coroutines.model.Image +import otus.homework.coroutines.model.Model class CatsView @JvmOverloads constructor( context: Context, @@ -21,12 +26,15 @@ class CatsView @JvmOverloads constructor( } } - override fun populate(fact: Fact) { - findViewById(R.id.fact_textView).text = fact.fact + override fun populate(model: Model) { + + findViewById(R.id.fact_textView).text = model.fact.fact + val Imageview = findViewById(R.id.imageView) + Picasso.get().load(model.imageUrl).into(Imageview) } } interface ICatsView { - fun populate(fact: Fact) + fun populate(model: Model) } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/DiContainer.kt b/app/src/main/java/otus/homework/coroutines/DiContainer.kt index 23ddc3b2..3596419c 100644 --- a/app/src/main/java/otus/homework/coroutines/DiContainer.kt +++ b/app/src/main/java/otus/homework/coroutines/DiContainer.kt @@ -12,5 +12,14 @@ class DiContainer { .build() } + private val imagesRetrofit by lazy { + Retrofit.Builder() + .baseUrl("https://api.thecatapi.com/") + .addConverterFactory(GsonConverterFactory.create()) + .build() + } + val service by lazy { retrofit.create(CatsService::class.java) } + + val imagesService by lazy { imagesRetrofit.create(ImagesService::class.java) } } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/ImagesService.kt b/app/src/main/java/otus/homework/coroutines/ImagesService.kt new file mode 100644 index 00000000..879c2f8d --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/ImagesService.kt @@ -0,0 +1,10 @@ +package otus.homework.coroutines + +import otus.homework.coroutines.model.Image +import retrofit2.http.GET + +interface ImagesService { + + @GET("v1/images/search") + suspend fun getImage(): List +} \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/MainActivity.kt b/app/src/main/java/otus/homework/coroutines/MainActivity.kt index ba898ffb..17f420ae 100644 --- a/app/src/main/java/otus/homework/coroutines/MainActivity.kt +++ b/app/src/main/java/otus/homework/coroutines/MainActivity.kt @@ -10,7 +10,6 @@ import kotlinx.coroutines.Job class MainActivity : AppCompatActivity() { lateinit var catsPresenter: CatsPresenter - private val diContainer = DiContainer() override fun onCreate(savedInstanceState: Bundle?) { @@ -18,7 +17,9 @@ class MainActivity : AppCompatActivity() { val view = layoutInflater.inflate(R.layout.activity_main, null) as CatsView setContentView(view) - catsPresenter = CatsPresenter(diContainer.service, ::onShowToast) + catsPresenter = CatsPresenter(diContainer.service, + diContainer.imagesService, + ::onShowToast) view.presenter = catsPresenter catsPresenter.attachView(view) catsPresenter.onInitComplete() diff --git a/app/src/main/java/otus/homework/coroutines/Fact.kt b/app/src/main/java/otus/homework/coroutines/model/Fact.kt similarity index 81% rename from app/src/main/java/otus/homework/coroutines/Fact.kt rename to app/src/main/java/otus/homework/coroutines/model/Fact.kt index 643a5a33..8c1f3817 100644 --- a/app/src/main/java/otus/homework/coroutines/Fact.kt +++ b/app/src/main/java/otus/homework/coroutines/model/Fact.kt @@ -1,4 +1,4 @@ -package otus.homework.coroutines +package otus.homework.coroutines.model import com.google.gson.annotations.SerializedName diff --git a/app/src/main/java/otus/homework/coroutines/model/Image.kt b/app/src/main/java/otus/homework/coroutines/model/Image.kt new file mode 100644 index 00000000..4c8c9fe9 --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/model/Image.kt @@ -0,0 +1,15 @@ +package otus.homework.coroutines.model + +import com.google.gson.annotations.SerializedName + +data class Image( + @field:SerializedName("id") + val id: String, + @field:SerializedName("url") + val url: String, + @field:SerializedName("width") + val width: Int, + @field:SerializedName("height") + val height: Int, + +) \ No newline at end of file diff --git a/app/src/main/java/otus/homework/coroutines/model/Model.kt b/app/src/main/java/otus/homework/coroutines/model/Model.kt new file mode 100644 index 00000000..1185cca6 --- /dev/null +++ b/app/src/main/java/otus/homework/coroutines/model/Model.kt @@ -0,0 +1,6 @@ +package otus.homework.coroutines.model + +data class Model( + val fact: Fact, + val imageUrl: String? +) \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 9508066d..a84fe7bd 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -7,16 +7,23 @@ android:layout_height="match_parent" tools:context=".MainActivity"> + + app:layout_constraintTop_toBottomOf="@+id/imageView" />