Skip to content

Courutines HW #246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: development
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ 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.4.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
}
14 changes: 14 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,14 @@
package otus.homework.coroutines

import com.google.gson.annotations.SerializedName

data class CatImage(
@field:SerializedName("id")
val id: String,
@field:SerializedName("url")
val url: String,
@field:SerializedName("width")
val width: String,
@field:SerializedName("height")
val height: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package otus.homework.coroutines

data class CatPresentationModel(
val fact: String,
val imageUrl: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package otus.homework.coroutines

import retrofit2.http.GET

interface CatImagesService {
@GET("v1/images/search/")
suspend fun getCatImage(): List<CatImage>
}
38 changes: 11 additions & 27 deletions app/src/main/java/otus/homework/coroutines/CatsPresenter.kt
Original file line number Diff line number Diff line change
@@ -1,35 +1,19 @@
package otus.homework.coroutines

import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import android.content.Context
import android.util.Log
import android.widget.Toast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.SocketTimeoutException

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

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()!!)
}
}

override fun onFailure(call: Call<Fact>, t: Throwable) {
CrashMonitor.trackWarning()
}
})
}

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

fun detachView() {
_catsView = null
}
}
4 changes: 1 addition & 3 deletions app/src/main/java/otus/homework/coroutines/CatsService.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package otus.homework.coroutines

import retrofit2.Call
import retrofit2.http.GET

interface CatsService {

@GET("fact")
fun getCatFact() : Call<Fact>
suspend fun getCatFact() : Fact
}
33 changes: 24 additions & 9 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,45 @@ 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 {
) : ConstraintLayout(context, attrs, defStyleAttr) {

var presenter :CatsPresenter? = null
private lateinit var viewModel: CatsViewModel

override fun onFinishInflate() {
super.onFinishInflate()
findViewById<Button>(R.id.button).setOnClickListener {
presenter?.onInitComplete()
viewModel.fetchCatData { result ->
when (result) {
is Result.Success -> populate(result.data)
is Result.Error -> {
Toast.makeText(context, result.message, Toast.LENGTH_SHORT).show()
}
}
}
}
}

override fun populate(fact: Fact) {
findViewById<TextView>(R.id.fact_textView).text = fact.fact
fun setViewModel(catsViewModel: CatsViewModel) {
this.viewModel = catsViewModel
}
}

interface ICatsView {
fun populate(catModel: CatPresentationModel) {
findViewById<TextView>(R.id.fact_textView).text = catModel.fact
showCatImage(catModel.imageUrl)
}

fun populate(fact: Fact)
}
private fun showCatImage(imageUrl: String) {
val imageView: ImageView = findViewById(R.id.cat_fact_imageView)
Picasso.get().load(imageUrl).into(imageView)
}
}
53 changes: 53 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,53 @@
package otus.homework.coroutines

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

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

private val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
CrashMonitor.trackWarning(throwable as Exception)
}

fun fetchCatData(onResult: (Result<CatPresentationModel>) -> Unit) {
viewModelScope.launch(coroutineExceptionHandler) {
try {
val fact = loadCatFact()
val imageUrl = loadCatImage()

if (fact != null && imageUrl != null) {
val catModel = CatPresentationModel(fact.fact, imageUrl)
onResult(Result.Success(catModel))
} else {
onResult(Result.Error("Факт или изображение не найдены"))
}
} catch (e: Exception) {
onResult(Result.Error(e.message ?: "Произошла ошибка"))
}
}
}

private suspend fun loadCatFact(): Fact? {
return withContext(Dispatchers.IO) {
catsService.getCatFact()
}
}

private suspend fun loadCatImage(): String? {
return withContext(Dispatchers.IO) {
val catImageList = catImagesService.getCatImage()
catImageList.firstOrNull()?.url
}
}

override fun onCleared() {
super.onCleared()
}
}
2 changes: 1 addition & 1 deletion app/src/main/java/otus/homework/coroutines/CrashMonitor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ object CrashMonitor {
/**
* Pretend this is Crashlytics/AppCenter
*/
fun trackWarning() {
fun trackWarning(e: Exception) {
}
}
13 changes: 11 additions & 2 deletions app/src/main/java/otus/homework/coroutines/DiContainer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@ import retrofit2.converter.gson.GsonConverterFactory

class DiContainer {

private val retrofit by lazy {
private val retrofitCatFacts by lazy {
Retrofit.Builder()
.baseUrl("https://catfact.ninja/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}

val service by lazy { retrofit.create(CatsService::class.java) }
val catFactsService by lazy { retrofitCatFacts.create(CatsService::class.java) }

private val retrofitCatImagesSource by lazy {
Retrofit.Builder()
.baseUrl("https://api.thecatapi.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}

val catImagesSourceService by lazy { retrofitCatImagesSource.create(CatImagesService::class.java) }
}
36 changes: 22 additions & 14 deletions app/src/main/java/otus/homework/coroutines/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
package otus.homework.coroutines

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

lateinit var catsPresenter: CatsPresenter

private val diContainer = DiContainer()
private lateinit var catsView: CatsView
private lateinit var viewModel: CatsViewModel

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

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

viewModel = CatsViewModel(diContainer.catFactsService, diContainer.catImagesSourceService)
catsView.setViewModel(viewModel)
}

catsPresenter = CatsPresenter(diContainer.service)
view.presenter = catsPresenter
catsPresenter.attachView(view)
catsPresenter.onInitComplete()
override fun onResume() {
super.onResume()
fetchCatData()
}

override fun onStop() {
if (isFinishing) {
catsPresenter.detachView()
private fun fetchCatData() {
viewModel.fetchCatData { result ->
when (result) {
is Result.Success -> catsView.populate(result.data)
is Result.Error -> {
Toast.makeText(this, result.message, Toast.LENGTH_SHORT).show()
}
}
}
super.onStop()
}
}
}
16 changes: 16 additions & 0 deletions app/src/main/java/otus/homework/coroutines/PresenterScope.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package otus.homework.coroutines

import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.CoroutineScope

class PresenterScope : CoroutineScope {
private val job = Job()

override val coroutineContext = job + Dispatchers.Main + CoroutineName("CatsCoroutine")

fun clear() {
job.cancel()
}
}
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

sealed class Result<out T> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val message: String) : Result<Nothing>()
}

9 changes: 9 additions & 0 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
android:layout_height="match_parent"
tools:context=".MainActivity">

<ImageView
android:id="@+id/cat_fact_imageView"
android:layout_width="300dp"
android:layout_height="300dp"
app:layout_constraintBottom_toTopOf="@+id/fact_textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/fact_textView"
android:textColor="@color/black"
Expand Down