Skip to content
Open

ДЗ #262

Show file tree
Hide file tree
Changes from 8 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-android:2.9.2'
}
6 changes: 6 additions & 0 deletions app/src/main/java/otus/homework/coroutines/Cat.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package otus.homework.coroutines

data class Cat(
val fact: String,
val imageUrl: String
)
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: Int,
@field:SerializedName("height")
val height: Int
)
86 changes: 51 additions & 35 deletions app/src/main/java/otus/homework/coroutines/CatsPresenter.kt
Original file line number Diff line number Diff line change
@@ -1,35 +1,51 @@
package otus.homework.coroutines

import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class CatsPresenter(
private val catsService: CatsService
) {

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
}
}
//package otus.homework.coroutines
//
//import kotlinx.coroutines.CoroutineName
//import kotlinx.coroutines.CoroutineScope
//import kotlinx.coroutines.Dispatchers
//import kotlinx.coroutines.Job
//import kotlinx.coroutines.async
//import kotlinx.coroutines.launch
//
//
//
//class CatsPresenter(
// private val catsService: CatsService,
// private val catsImagesService: CatsImageService,
// private val presenterScope: CoroutineScope = CoroutineScope(Dispatchers.Main + CoroutineName("CatsCoroutine"))
//) {
//
// private var _catsView: ICatsView? = null
// private var job: Job? = null
//
// fun onInitComplete() {
// job = presenterScope.launch {
// try {
// val fact = async { catsService.getCatFact() }.await()
// val catImages:List<CatImage> = async { catsImagesService.getCatImage() }.await()
// _catsView?.populate(fact, catImages.first())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

не стыкуются типы, во вью populate принимает Cat

//
// } catch (ex: Exception) {
// if (ex is java.net.SocketTimeoutException) {
// _catsView?.onError("Unable to get response from server")
// }
// if (ex is CancellationException) throw ex
//
// CrashMonitor.trackWarning("Error")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

В остальных случаях логируем исключение в otus.homework.coroutines.CrashMonitor и показываем Toast с exception.message

// }
// }
// }
//
// fun onStop() {
// job?.cancel()
// }
//
//
// fun attachView(catsView: ICatsView) {
// _catsView = catsView
// }
//
// fun detachView() {
// _catsView = null
// }
//}
13 changes: 10 additions & 3 deletions app/src/main/java/otus/homework/coroutines/CatsService.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package otus.homework.coroutines

import retrofit2.Call

import retrofit2.http.GET

interface CatsService {

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

interface CatsImageService {

@GET("v1/images/search")
suspend fun getCatImage() : List<CatImage>

}
38 changes: 28 additions & 10 deletions app/src/main/java/otus/homework/coroutines/CatsView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,49 @@ package otus.homework.coroutines

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import com.squareup.picasso.Picasso
import androidx.constraintlayout.widget.ConstraintLayout


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

var presenter :CatsPresenter? = null
// var presenter: CatsPresenter? = null

override fun populate(cat: Cat) {
findViewById<TextView>(R.id.fact_textView).text = cat.fact
Picasso.get().load(cat.imageUrl).into(findViewById<ImageView>(R.id.catImageView));
}

override fun onError(text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}

override fun onFinishInflate() {
super.onFinishInflate()
fun showErrorToast(errorMsg: String) {
Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show()
}

fun setOnButtonClick(onButtonClick: () -> Unit) {
findViewById<Button>(R.id.button).setOnClickListener {
presenter?.onInitComplete()
Log.d("TAG", "Button clicked")
onButtonClick()
}
}

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

interface ICatsView {
public interface ICatsView {

fun populate(cat: Cat)

fun onError(errorMsg: String)
}

fun populate(fact: Fact)
}
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.coroutines

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.CoroutineExceptionHandler

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


private val _uiState = MutableLiveData<Result>()

private val exceptionHandler = CoroutineExceptionHandler { _, e ->
CrashMonitor.trackWarning("${e.message}")
_uiState.value = Result.Error(
Throwable()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

)
}

val uiState: LiveData<Result>
get() = _uiState

fun populate () {
viewModelScope.launch(exceptionHandler) {
val fact = async { catsService.getCatFact() }
val image = async { catsImageService.getCatImage()[0] }
val cat = Cat(fact.await().fact, image.await().url)

_uiState.value = Result.Success(cat)
}
}

companion object {
fun provideFactory(
catsService: CatsService,
catsImageService: CatsImageService
): ViewModelProvider.Factory = viewModelFactory {
initializer {
CatsViewModel(catsService, catsImageService)
}
}
}
}
5 changes: 4 additions & 1 deletion app/src/main/java/otus/homework/coroutines/CrashMonitor.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package otus.homework.coroutines

import android.util.Log

object CrashMonitor {

/**
* Pretend this is Crashlytics/AppCenter
*/
fun trackWarning() {
fun trackWarning(warningMessage: String) {
Log.d("TAG", "Crash logged: $warningMessage")
}
}
18 changes: 15 additions & 3 deletions app/src/main/java/otus/homework/coroutines/DiContainer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@ package otus.homework.coroutines

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import kotlin.getValue

class DiContainer {

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

val service by lazy { retrofit.create(CatsService::class.java) }
}
val catsService by lazy { catsFactRetrofit.create(CatsService::class.java) }

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

val catsImageService by lazy { catsImageRetrofit.create(CatsImageService::class.java) }


}
57 changes: 46 additions & 11 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,64 @@ package otus.homework.coroutines

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import otus.homework.coroutines.Result
import kotlin.getValue

class MainActivity : AppCompatActivity() {

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

private val diContainer = DiContainer()

private val viewModel by lazy {
ViewModelProvider(
this,
CatsViewModel.provideFactory(diContainer.catsService, diContainer.catsImageService)
)[CatsViewModel::class.java]
}

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()
}
viewModel.populate()

view.setOnButtonClick {
viewModel.populate()


// catsPresenter = CatsPresenter(diContainer.catsService, diContainer.catsImageService)
// view.presenter = catsPresenter
// catsPresenter.attachView(view)
// catsPresenter.onInitComplete()

override fun onStop() {
if (isFinishing) {
catsPresenter.detachView()
}
super.onStop()


val catObserver = Observer <Result> { catResult ->
when(catResult) {
is Result.Error -> view.showErrorToast(catResult.throwable.message.toString())
is Result.Success -> view.populate(catResult.catData)


}

}
viewModel.uiState.observe(this, catObserver)

// fun onStart() {
// super.onStart()
// viewModel.populate()
// }
//
// override fun onStop() {
// super.onStop()
// }
}
}
}


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 {
data class Success(val catData: Cat) : Result()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Success по условиям задачи должен быть дженериком

data class Error(val throwable: Throwable) : Result()
}
Loading