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
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ dependencies {
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.google.code.gson:gson:2.10'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.0'
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.activity:activity-ktx:1.2.3'

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

data class CatInfo(
val catFact: String,
val catImageUrl: String,
)
35 changes: 0 additions & 35 deletions app/src/main/java/otus/homework/coroutines/CatsPresenter.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package otus.homework.coroutines

import androidx.annotation.StringRes

sealed interface CatsScreenState {
data class Success<T>(val data: T) : CatsScreenState
data class Error(@StringRes val message: Int, val th: Throwable) : CatsScreenState
}
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 otus.homework.coroutines.dto.CatImage
import otus.homework.coroutines.dto.Fact
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>
}
24 changes: 19 additions & 5 deletions app/src/main/java/otus/homework/coroutines/CatsView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ 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 {

var presenter :CatsPresenter? = null
var presenter: CatsViewModel? = null

override fun onFinishInflate() {
super.onFinishInflate()
Expand All @@ -21,12 +24,23 @@ class CatsView @JvmOverloads constructor(
}
}

override fun populate(fact: Fact) {
findViewById<TextView>(R.id.fact_textView).text = fact.fact
override fun populate(state: CatsScreenState) {
when (state) {
is CatsScreenState.Success<*> -> {
(state.data as? CatInfo)?.let { catInfo ->
findViewById<TextView>(R.id.fact_textView).text = catInfo.catFact
Picasso.get().load(catInfo.catImageUrl).into(findViewById<ImageView>(R.id.image))
}
}

is CatsScreenState.Error -> {
Toast.makeText(context, state.message, Toast.LENGTH_SHORT).show()
}
}

}
}

interface ICatsView {

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

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import java.net.SocketTimeoutException

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

private var _catsView: ICatsView? = null

private val coroutineExceptionHandler = CoroutineExceptionHandler { _, exception ->
CrashMonitor.trackWarning(exception.message)
}

fun onInitComplete() {
viewModelScope.launch(coroutineExceptionHandler) {
try {
coroutineScope {
val catFact = async(Dispatchers.IO) { catsService.getCatFact() }
val catImage = async(Dispatchers.IO) { catsService.getCatImage() }
val state = CatsScreenState.Success(
CatInfo(
catFact = catFact.await().fact,
catImageUrl = catImage.await().first().url
)
)
_catsView?.populate(state)
}
} catch (e: SocketTimeoutException) {
_catsView?.populate(CatsScreenState.Error(R.string.error_message, e))
}
}
}

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


fun detachView() {
_catsView = null
viewModelScope.cancel()
}
}

class CatsViewModelFactory(private val catsService: CatsService) :
ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T =
CatsViewModel(catsService) as T
}
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(throwableMessage: String?) {
Log.d("CATS_COROUTINE", "trackWarning: $throwableMessage")
}
}
10 changes: 0 additions & 10 deletions app/src/main/java/otus/homework/coroutines/Fact.kt

This file was deleted.

18 changes: 11 additions & 7 deletions app/src/main/java/otus/homework/coroutines/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package otus.homework.coroutines

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

lateinit var catsPresenter: CatsPresenter
private val catsViewModel: CatsViewModel by viewModels<CatsViewModel> {
CatsViewModelFactory(
diContainer.service
)
}

private val diContainer = DiContainer()

Expand All @@ -15,15 +20,14 @@ 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()
view.presenter = catsViewModel
catsViewModel.attachView(view)
catsViewModel.onInitComplete()
}

override fun onStop() {
if (isFinishing) {
catsPresenter.detachView()
catsViewModel.detachView()
}
super.onStop()
}
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/otus/homework/coroutines/dto/CatImage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package otus.homework.coroutines.dto

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: Long,
@field:SerializedName("height")
val height: Long,
)
10 changes: 10 additions & 0 deletions app/src/main/java/otus/homework/coroutines/dto/Fact.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package otus.homework.coroutines.dto

import com.google.gson.annotations.SerializedName

data class Fact(
@field:SerializedName("fact")
val fact: String,
@field:SerializedName("length")
val length: Int
)
52 changes: 33 additions & 19 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,42 @@
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"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="350dp"
android:importantForAccessibility="no"
android:scaleType="centerInside"
tools:src="@tools:sample/backgrounds/scenic" />

<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:layout_gravity="center"
android:text="@string/more_facts" />
</LinearLayout>
</ScrollView>

<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>
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="error_message">Не удалось получить ответ от сервера</string>
</resources>