Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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"
}
12 changes: 12 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,12 @@
package otus.homework.coroutines

import com.google.gson.annotations.SerializedName

data class CatImage(
@field:SerializedName("url")
val url: String,
@field:SerializedName("width")
val width: Int,
@field:SerializedName("height")
val height: Int,
)
53 changes: 41 additions & 12 deletions app/src/main/java/otus/homework/coroutines/CatsPresenter.kt
Original file line number Diff line number Diff line change
@@ -1,28 +1,53 @@
package otus.homework.coroutines

import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import android.content.Context
import android.widget.Toast
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import java.net.SocketTimeoutException

class CatsPresenter(
val context: Context,
private val catsService: CatsService
) {

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

fun onInitComplete() {
catsService.getCatFact().enqueue(object : Callback<Fact> {
presenterScope.launch {
try {
val getCatFactDiffered = async { catsService.getCatFact() }
val getCatImageDiffered = async { catsService.getCatImage() }

override fun onResponse(call: Call<Fact>, response: Response<Fact>) {
if (response.isSuccessful && response.body() != null) {
_catsView?.populate(response.body()!!)
}
}
val getCatFactResponse = getCatFactDiffered.await()
val getCatImageResponse = getCatImageDiffered.await().firstOrNull()

override fun onFailure(call: Call<Fact>, t: Throwable) {
CrashMonitor.trackWarning()
val catModelsMapper = CatModels(
fact = getCatFactResponse.fact,
url = getCatImageResponse?.url.orEmpty(),
width = getCatImageResponse?.width ?: 0,//значение не использую, но пусть будет
height = getCatImageResponse?.height ?: 0,//значение не использую, но пусть будет
)
_catsView?.populate(catModelsMapper)
} catch (e: Exception) {
when (e) {
is SocketTimeoutException -> {
Toast.makeText(context, R.string.timeout_error_text, Toast.LENGTH_SHORT)
.show()
}
else -> {
CrashMonitor.trackWarning()
Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show()
}
}
}
})
}
}

fun attachView(catsView: ICatsView) {
Expand All @@ -32,4 +57,8 @@ class CatsPresenter(
fun detachView() {
_catsView = null
}

fun onStop() {
presenterScope.cancel()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

так отменять джобу нельзя, мы отменим сразу скоуп и он больше не заведется при возврате на экран, можно например джобу положить в переменную и ее отменять тут

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

поправил

}
}
9 changes: 7 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,15 @@
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 getCatImage(
@Url url: String = "https://api.thecatapi.com/v1/images/search"
): List<CatImage>
}
20 changes: 13 additions & 7 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,36 @@ 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

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

var presenter :CatsPresenter? = null

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

override fun populate(fact: Fact) {
findViewById<TextView>(R.id.fact_textView).text = fact.fact
override fun populate(catModels: CatModels) {
findViewById<TextView>(R.id.fact_textView).text = catModels.fact
val imageView = findViewById<ImageView>(R.id.cat_image)

Picasso.get()
.load(catModels.url)
.into(imageView)
}
}

interface ICatsView {

fun populate(fact: Fact)
fun populate(catModels: CatModels)
}
52 changes: 52 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,52 @@
package otus.homework.reactivecats

import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import otus.homework.coroutines.CatModels
import otus.homework.coroutines.CatsService
import otus.homework.coroutines.CrashMonitor

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

private val _state = MutableLiveData<CatsResult>()
val state: LiveData<CatsResult> = _state

private val errorsHandler = CoroutineExceptionHandler { _, throwable ->
CrashMonitor.trackWarning()
_state.postValue(CatsResult.Errors(throwable))
}

init {
loadData()
}

fun loadData() {
viewModelScope.launch(errorsHandler) {
val getCatFactDiffered = async { catsService.getCatFact() }
val getCatImageDiffered = async { catsService.getCatImage() }

val getCatFactResponse = getCatFactDiffered.await()
val getCatImageResponse = getCatImageDiffered.await().firstOrNull()

val catModelsMapper = CatModels(
fact = getCatFactResponse.fact,
url = getCatImageResponse?.url.orEmpty(),
width = getCatImageResponse?.width ?: 0,//значение не использую, но пусть будет
height = getCatImageResponse?.height ?: 0,//значение не использую, но пусть будет
)
_state.value = CatsResult.Success(catModelsMapper)
}
}

sealed class CatsResult {
data class Success(val catModels: CatModels) : CatsResult()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

по заданию он должен быть дженериком

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

поправил

data class Errors(val e: Throwable) : CatsResult()
}
}
38 changes: 26 additions & 12 deletions app/src/main/java/otus/homework/coroutines/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,43 @@ package otus.homework.coroutines

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import otus.homework.reactivecats.CatsViewModel
import java.net.SocketTimeoutException
import kotlin.toString

class MainActivity : AppCompatActivity() {

lateinit var catsPresenter: CatsPresenter
//lateinit var catsPresenter: CatsPresenter

private val diContainer = DiContainer()

private val viewModel = CatsViewModel(diContainer.service)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

так создавать вьюмодели нельзя, она не переживет смену конфигурации корректно, или нужно использовать фабрику или например делегат by viewmodels

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

поправил


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val view = layoutInflater.inflate(R.layout.activity_main, null) as CatsView
setContentView(view)

catsPresenter = CatsPresenter(diContainer.service)
view.presenter = catsPresenter
catsPresenter.attachView(view)
catsPresenter.onInitComplete()
}
view.onButtonClick = {
viewModel.loadData()
}

override fun onStop() {
if (isFinishing) {
catsPresenter.detachView()
setContentView(view)
viewModel.state.observe(this) { result ->
when (result) {
is CatsViewModel.CatsResult.Success -> {
view.populate(result.catModels)
}

is CatsViewModel.CatsResult.Errors -> {
val message = when (result.e) {
is SocketTimeoutException -> getString(R.string.timeout_error_text)
else -> result.e.toString()
}

Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
}
super.onStop()
}
}
8 changes: 8 additions & 0 deletions app/src/main/java/otus/homework/coroutines/Models.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package otus.homework.coroutines

data class CatModels (
val fact: String,
val url: String,
val width: Int,
val height: Int,
)
16 changes: 13 additions & 3 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,26 @@
android:layout_height="match_parent"
tools:context=".MainActivity">

<ImageView
android:id="@+id/cat_image"
android:layout_width="0dp"
android:layout_height="200.dp"
android:scaleType="centerCrop"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
/>

<TextView
android:id="@+id/fact_textView"
android:textColor="@color/black"
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_constraintTop_toBottomOf="@id/cat_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintEnd_toEndOf="parent" />


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