Skip to content

Homework #244

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 3 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 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1"
implementation "androidx.activity:activity-ktx:1.7.2"
}
35 changes: 0 additions & 35 deletions app/src/main/java/otus/homework/coroutines/CatsPresenter.kt

This file was deleted.

4 changes: 2 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,10 @@
package otus.homework.coroutines

import retrofit2.Call
import retrofit2.Response
import retrofit2.http.GET

interface CatsService {

@GET("fact")
fun getCatFact() : Call<Fact>
suspend fun getCatFact(): Response<FactDto>
}
25 changes: 9 additions & 16 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,24 @@ 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

override fun onFinishInflate() {
super.onFinishInflate()
findViewById<Button>(R.id.button).setOnClickListener {
presenter?.onInitComplete()
}
fun populate(dataModel: DataModel) {
findViewById<TextView>(R.id.fact_textView).text = dataModel.fact
Picasso.get().load(dataModel.imageUrl).into(findViewById<ImageView>(R.id.image))
}

override fun populate(fact: Fact) {
findViewById<TextView>(R.id.fact_textView).text = fact.fact
fun showToast(text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
}

interface ICatsView {

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

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.net.SocketTimeoutException


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

private val _state = MutableStateFlow<Result>(Result.Initialization)
val state = _state.asStateFlow()

private var job: Job? = null

private val exceptionHandler by lazy {
CoroutineExceptionHandler { _, throwable ->
if (throwable is SocketTimeoutException) {
showToast("Не удалось получить ответ от сервером")
} else {
showToast(throwable.message ?: "No message")
CrashMonitor.trackWarning(throwable)
}
}
}

fun onInitComplete() {
job?.cancel()
job = viewModelScope.launch(exceptionHandler) {
val factDeferred = async { catsService.getCatFact().body()?.fact }
val imageDeferred = async { imageService.getImage().body()?.getOrNull(0)?.url }

val fact = factDeferred.await()
val image = imageDeferred.await()

if (!fact.isNullOrEmpty() && !image.isNullOrEmpty()) {
_state.emit(Result.Success(DataModel(fact.toString(), image)))
}
}
}

private fun showToast(text: String) {
viewModelScope.launch {
_state.emit(Result.Error(text))
}
}

class CatsViewModelFactory(
private val catsService: CatsService,
private val imageService: ImageService
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(CatsViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return CatsViewModel(catsService, imageService) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
}
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(t: Throwable) {
Log.d("CatsApp", t.toString())
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/otus/homework/coroutines/DataModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package otus.homework.coroutines

data class DataModel(
val fact: String,
val imageUrl: String
)
15 changes: 12 additions & 3 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 catsRetrofit by lazy {
Retrofit.Builder()
.baseUrl("https://catfact.ninja/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}

val service by lazy { retrofit.create(CatsService::class.java) }
}
private val imageRetrofit by lazy {
Retrofit.Builder()
.baseUrl("https://api.thecatapi.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}

val catsService: CatsService by lazy { catsRetrofit.create(CatsService::class.java) }

val imageService: ImageService by lazy { imageRetrofit.create(ImageService::class.java) }
}
12 changes: 12 additions & 0 deletions app/src/main/java/otus/homework/coroutines/Dto.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 FactDto(
@SerializedName("fact") val fact: String,
@SerializedName("length") val length: Int
)

data class ImageDto(
@SerializedName("url") var url: String
)
10 changes: 0 additions & 10 deletions app/src/main/java/otus/homework/coroutines/Fact.kt

This file was deleted.

10 changes: 10 additions & 0 deletions app/src/main/java/otus/homework/coroutines/ImageService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package otus.homework.coroutines

import retrofit2.Response
import retrofit2.http.GET

interface ImageService {

@GET("v1/images/search/")
suspend fun getImage(): Response<List<ImageDto>>
}
37 changes: 24 additions & 13 deletions app/src/main/java/otus/homework/coroutines/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@ package otus.homework.coroutines

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import androidx.activity.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {

lateinit var catsPresenter: CatsPresenter
private val viewModel: CatsViewModel by viewModels {
CatsViewModel.CatsViewModelFactory(diContainer.catsService, diContainer.imageService)
}

private val diContainer = DiContainer()

Expand All @@ -14,17 +22,20 @@ class MainActivity : AppCompatActivity() {

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

override fun onStop() {
if (isFinishing) {
catsPresenter.detachView()
viewModel.onInitComplete()
findViewById<Button>(R.id.button).setOnClickListener {
viewModel.onInitComplete()
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.state.collect { state ->
when (state) {
is Result.Initialization -> Unit
is Result.Success -> view.populate(state.dataModel)
is Result.Error -> view.showToast(state.text)
}
}
}
}
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 {
object Initialization : Result()
data class Success(val dataModel: DataModel) : Result()
data class Error(val text: String) : Result()
}
42 changes: 26 additions & 16 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,38 @@
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">

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

<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="200dp" />

<TextView
android:id="@+id/fact_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="24sp" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/more_facts"
app:layout_constraintTop_toBottomOf="@+id/fact_textView" />
</LinearLayout>

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/more_facts"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fact_textView" />

</otus.homework.coroutines.CatsView>