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.6.0"
}
6 changes: 6 additions & 0 deletions app/src/main/java/otus/homework/coroutines/CatFact.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package otus.homework.coroutines

data class CatFact(
val fact: Fact,
val imageUrl: String = "",
)
61 changes: 51 additions & 10 deletions app/src/main/java/otus/homework/coroutines/CatsPresenter.kt
Original file line number Diff line number Diff line change
@@ -1,28 +1,68 @@
package otus.homework.coroutines

import android.content.res.Resources
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.net.SocketTimeoutException

class CatsPresenter(
private val catsService: CatsService
private val catsService: CatsService,
private val resources: Resources,
) {

private var _catsView: ICatsView? = null

fun onInitComplete() {
catsService.getCatFact().enqueue(object : Callback<Fact> {
private val presenterScope = CoroutineScope(Dispatchers.Main + CoroutineName("CatsCoroutine"))
private var job: Job = Job()

fun load() {
job = presenterScope.launch {
runCatching {
val facts = catsService.getCatFact()
val imageUrl = catsService.loadImage().first().url

override fun onResponse(call: Call<Fact>, response: Response<Fact>) {
if (response.isSuccessful && response.body() != null) {
_catsView?.populate(response.body()!!)
}
CatFact(
fact = facts,
imageUrl = imageUrl,
)
}.onFailure { error ->
if (error is CancellationException) throw error
errorHandler(error)
}.onSuccess {
_catsView?.populate(it)
}
}
}

override fun onFailure(call: Call<Fact>, t: Throwable) {
CrashMonitor.trackWarning()
fun onInitComplete() {
job = presenterScope.launch {
try {
val facts = catsService.getCatFact()
_catsView?.populate(CatFact(facts))
} catch (e: Exception) {
errorHandler(e)
}
})
}
}

private fun errorHandler(e: Throwable) {
if (e is SocketTimeoutException) {
_catsView?.showToast(
resources.getString(
R.string.socet_timeout_error,
),
)
} else {
CrashMonitor.trackWarning()
_catsView?.showToast(e.message.orEmpty())
}
}

fun attachView(catsView: ICatsView) {
Expand All @@ -31,5 +71,6 @@ class CatsPresenter(

fun detachView() {
_catsView = null
job.cancel()
}
}
6 changes: 4 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,12 @@
package otus.homework.coroutines

import retrofit2.Call
import retrofit2.http.GET

interface CatsService {

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

@GET("https://api.thecatapi.com/v1/images/search")
suspend fun loadImage() : List<FactImage>
}
21 changes: 17 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,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,
Expand All @@ -17,16 +20,26 @@ class CatsView @JvmOverloads constructor(
override fun onFinishInflate() {
super.onFinishInflate()
findViewById<Button>(R.id.button).setOnClickListener {
presenter?.onInitComplete()
presenter?.load()
}
}

override fun populate(fact: Fact) {
findViewById<TextView>(R.id.fact_textView).text = fact.fact
override fun populate(fact: CatFact) {
findViewById<TextView>(R.id.fact_textView).text = fact.fact.fact
val imageView = findViewById<ImageView>(R.id.fact_image)
Picasso
.get()
.load(fact.imageUrl)
.into(imageView)
}

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

interface ICatsView {

fun populate(fact: Fact)
fun populate(fact: CatFact)
fun showToast(message: String)
}
66 changes: 66 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,66 @@
package otus.homework.coroutines

import android.content.res.Resources
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.net.SocketTimeoutException

class CatsViewModel(
private val catsService: CatsService,
private val resources: Resources,
) : ViewModel() {

private val _catsFact = MutableStateFlow<Result>(Result.Success(Unit))
val catsFact = _catsFact.asStateFlow()

private val exceptionHandler = CoroutineExceptionHandler { _, _ ->
CrashMonitor.trackWarning()
}

fun load() {
viewModelScope.launch(exceptionHandler) {
runCatching {
val facts = catsService.getCatFact()
val imageUrl = catsService.loadImage().first().url

CatFact(
fact = facts,
imageUrl = imageUrl,
)
}.onFailure { error ->
if (error is CancellationException) throw error
_catsFact.update {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

также перехват CancellationException

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

исправил

Result.Error(errorHandler(error))
}
}.onSuccess { fact ->
_catsFact.update {
Result.Success(fact)
}
}
}
}

private fun errorHandler(e: Throwable): String {
Log.d("Fact", e.message.toString())
return if (e is SocketTimeoutException) {
resources.getString(
R.string.socet_timeout_error,
)
} else {
e.message.orEmpty()
}
}
}


sealed interface Result {
data class Success<T>(val result: T) : Result
data class Error(val message: String) : Result
}
8 changes: 8 additions & 0 deletions app/src/main/java/otus/homework/coroutines/FactImage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package otus.homework.coroutines

import com.google.gson.annotations.SerializedName

data class FactImage(
@field:SerializedName("url")
val url: String,
)
4 changes: 2 additions & 2 deletions app/src/main/java/otus/homework/coroutines/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ class MainActivity : AppCompatActivity() {
val view = layoutInflater.inflate(R.layout.activity_main, null) as CatsView
setContentView(view)

catsPresenter = CatsPresenter(diContainer.service)
catsPresenter = CatsPresenter(diContainer.service, resources)
view.presenter = catsPresenter
catsPresenter.attachView(view)
catsPresenter.onInitComplete()
catsPresenter.load()
}

override fun onStop() {
Expand Down
19 changes: 15 additions & 4 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,31 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:padding="16dp"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">

<ImageView
android:id="@+id/fact_image"
android:layout_width="match_parent"
android:layout_height="260dp"
android:contentDescription="@null"
android:scaleType="centerCrop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/fact_textView"/>

<TextView
android:id="@+id/fact_textView"
android:textColor="@color/black"
android:textSize="24sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@id/fact_image" />

<Button
android:id="@+id/button"
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<resources>
<string name="app_name">Cat Facts </string>
<string name="more_facts">More Facts</string>
<string name="refresh_screen">Refresh Screen</string>
<string name="socet_timeout_error">Не удалось получить ответ от сервером</string>
</resources>