Skip to content

Adil Ziganshin HW Coroutines #243

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
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ 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.8.7'
implementation 'androidx.fragment:fragment-ktx:1.8.5'
}
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: Fact,
val picture: CatPicture
)
8 changes: 8 additions & 0 deletions app/src/main/java/otus/homework/coroutines/CatPicture.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package otus.homework.coroutines

data class CatPicture(
val id: String,
val url: String,
val width: Int,
val height: Int
)
40 changes: 22 additions & 18 deletions app/src/main/java/otus/homework/coroutines/CatsPresenter.kt
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
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 java.net.SocketTimeoutException

class CatsPresenter(
private val catsService: CatsService
) {
class CatsPresenter {

private var _catsView: ICatsView? = null

fun onInitComplete() {
catsService.getCatFact().enqueue(object : Callback<Fact> {
fun showCatFactAndPicture(cat: Cat) {
_catsView?.populate(cat)
}

fun showOrLogError(exception: Throwable, context: Context) {
if (exception is SocketTimeoutException) {
Toast.makeText(context, context.getString(R.string.could_not_get_response_from_server),
Toast.LENGTH_SHORT).show()
}
}

override fun onResponse(call: Call<Fact>, response: Response<Fact>) {
if (response.isSuccessful && response.body() != null) {
_catsView?.populate(response.body()!!)
}
}
fun showLoading() {
_catsView?.showLoading()
}

override fun onFailure(call: Call<Fact>, t: Throwable) {
CrashMonitor.trackWarning()
}
})
fun setOnClickListener(onClick: () -> Unit) {
Log.d("CatsPresenter.setOnClickListener", "onClick = '$onClick'")
_catsView?.setButtonOnClickListener(onClick)
}

fun attachView(catsView: ICatsView) {
Expand All @@ -32,4 +36,4 @@ class CatsPresenter(
fun detachView() {
_catsView = null
}
}
}
5 changes: 2 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,9 @@
package otus.homework.coroutines

import retrofit2.Call
import retrofit2.http.GET

interface CatsService {

@GET("fact")
fun getCatFact() : Call<Fact>
}
suspend fun getCatFact(): Fact
}
32 changes: 26 additions & 6 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,51 @@ 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.ProgressBar
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
) : ConstraintLayout(context, attrs, defStyleAttr), ICatsView {

var presenter :CatsPresenter? = null
var presenter: CatsPresenter? = null
var onButtonClick: (() -> Unit)? = null

override fun onFinishInflate() {
super.onFinishInflate()
findViewById<Button>(R.id.button).setOnClickListener {
presenter?.onInitComplete()
Log.d("onFinishInflate", "onClick = $onButtonClick")
onButtonClick?.invoke()
}
}

override fun populate(fact: Fact) {
findViewById<TextView>(R.id.fact_textView).text = fact.fact
override fun populate(cat: Cat) {
findViewById<ProgressBar>(R.id.progress_bar).visibility = GONE
findViewById<TextView>(R.id.fact_textView).text = cat.fact.text
val imageView: ImageView = findViewById(R.id.picture_imageView)
Picasso.get().load(cat.picture.url).into(imageView)
}

override fun showLoading() {
findViewById<ProgressBar>(R.id.progress_bar).visibility = VISIBLE
}

override fun setButtonOnClickListener(onClick: () -> Unit) {
Log.d("CatsView.setButtonOnClickListener", "onClick = '$onClick'")
onButtonClick = onClick
}
}

interface ICatsView {

fun populate(fact: Fact)
}
fun populate(cat: Cat)
fun showLoading()
fun setButtonOnClickListener(onClick: () -> Unit)
}
31 changes: 31 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,31 @@
package otus.homework.coroutines

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

sealed class Result {
data class Success(val cat: Cat): Result()
data class Error(val exception: Throwable): Result()
object Loading: Result()
}

class CatsViewModel: ViewModel() {
private var _uiState: MutableStateFlow<Result> = MutableStateFlow(Result.Loading)
val uiState: StateFlow<Result> = _uiState
fun getCat() {
val handler = CoroutineExceptionHandler { _, exception ->
_uiState.value = Result.Error(exception)
CrashMonitor.trackWarning()
}
viewModelScope.launch(handler) {
_uiState.value = Result.Loading
val di = DiContainer()
val cat = Cat(di.factsService.getCatFact(), di.picsService.getCatPicture()[0])
_uiState.value = Result.Success(cat)
}
}
}
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 @@ -7,4 +7,4 @@ object CrashMonitor {
*/
fun trackWarning() {
}
}
}
13 changes: 10 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,19 @@ import retrofit2.converter.gson.GsonConverterFactory

class DiContainer {

private val retrofit by lazy {
private val factsRetrofit by lazy {
Retrofit.Builder()
.baseUrl("https://catfact.ninja/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val factsService by lazy { factsRetrofit.create(CatsService::class.java) }

val service by lazy { retrofit.create(CatsService::class.java) }
}
private val picsRetrofit by lazy {
Retrofit.Builder()
.baseUrl("https://api.thecatapi.com/v1/images/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val picsService by lazy { picsRetrofit.create(PicsService::class.java) }
}
5 changes: 2 additions & 3 deletions app/src/main/java/otus/homework/coroutines/Fact.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.google.gson.annotations.SerializedName

data class Fact(
@field:SerializedName("fact")
val fact: String,
@field:SerializedName("length")
val text: String,
val length: Int
)
)
30 changes: 22 additions & 8 deletions app/src/main/java/otus/homework/coroutines/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,37 @@ package otus.homework.coroutines

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {

lateinit var catsPresenter: CatsPresenter

private val diContainer = DiContainer()
private lateinit var catsPresenter: CatsPresenter
private val viewModel: CatsViewModel by viewModels()

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

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

catsPresenter = CatsPresenter(diContainer.service)
viewModel.getCat()
lifecycleScope.launch {
viewModel.uiState.collect {
when (it) {
is Result.Success -> catsPresenter.showCatFactAndPicture(it.cat)
is Result.Error -> catsPresenter.showOrLogError(it.exception, this@MainActivity)
is Result.Loading -> catsPresenter.showLoading()
}
}
}
view.presenter = catsPresenter
catsPresenter.attachView(view)
catsPresenter.onInitComplete()
catsPresenter.setOnClickListener {
Log.d("MainActivity.setOnClickListener", "onClick set")
viewModel.getCat()
}
}

override fun onStop() {
Expand All @@ -27,4 +41,4 @@ class MainActivity : AppCompatActivity() {
}
super.onStop()
}
}
}
9 changes: 9 additions & 0 deletions app/src/main/java/otus/homework/coroutines/PicsService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package otus.homework.coroutines

import retrofit2.http.GET

interface PicsService {

@GET("search")
suspend fun getCatPicture(): List<CatPicture>
}
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.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren

class PresenterScope : CoroutineScope {
private val job = SupervisorJob()
override val coroutineContext = job + Dispatchers.Main + CoroutineName("CatsCoroutine")

fun cancelJobs() {
job.cancelChildren()
}
}
21 changes: 21 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,26 @@
android:layout_height="match_parent"
tools:context=".MainActivity">

<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/picture_imageView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone" />

<ImageView
android:id="@+id/picture_imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/fact_textView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:contentDescription="@string/cat" />

<TextView
android:id="@+id/fact_textView"
android:textColor="@color/black"
Expand All @@ -23,6 +43,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/more_facts"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fact_textView" />
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<resources>
<string name="app_name">Cat Facts </string>
<string name="more_facts">More Facts</string>
<string name="could_not_get_response_from_server">Не удалось получить ответ от сервера</string>
<string name="cat">Cat</string>
</resources>