Skip to content

HW-1: Coroutines #248

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 1 commit 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.8.7'
implementation "androidx.activity:activity-ktx:1.9.0"
implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2"
}
35 changes: 0 additions & 35 deletions app/src/main/java/otus/homework/coroutines/CatsPresenter.kt

This file was deleted.

10 changes: 0 additions & 10 deletions app/src/main/java/otus/homework/coroutines/CatsService.kt

This file was deleted.

32 changes: 0 additions & 32 deletions app/src/main/java/otus/homework/coroutines/CatsView.kt

This file was deleted.

3 changes: 2 additions & 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,7 @@ object CrashMonitor {
/**
* Pretend this is Crashlytics/AppCenter
*/
fun trackWarning() {
fun trackWarning(message: String) {
Utils.log { "[CrashMonitor]: $message" }
}
}
16 changes: 0 additions & 16 deletions app/src/main/java/otus/homework/coroutines/DiContainer.kt

This file was deleted.

10 changes: 0 additions & 10 deletions app/src/main/java/otus/homework/coroutines/Fact.kt

This file was deleted.

60 changes: 47 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,29 +2,63 @@ package otus.homework.coroutines

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import androidx.lifecycle.ViewModelProvider
import kotlinx.coroutines.Job
import otus.homework.coroutines.presentation.CatsPresenter
import otus.homework.coroutines.presentation.ViewModelFactory
import kotlin.coroutines.CoroutineContext

class MainActivity : AppCompatActivity() {

lateinit var catsPresenter: CatsPresenter
private lateinit var viewModel: CatsPresenter

private val diContainer = DiContainer()
private val job = Job()
private val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main

private val scope = CoroutineScope(coroutineContext)

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

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

catsPresenter = CatsPresenter(diContainer.service)
view.presenter = catsPresenter
catsPresenter.attachView(view)
catsPresenter.onInitComplete()
}
viewModel = ViewModelProvider(this, ViewModelFactory())[CatsPresenter::class.java]

val imageView = findViewById<ImageView>(R.id.imageView)
val textView = findViewById<TextView>(R.id.fact_textView)

override fun onStop() {
if (isFinishing) {
catsPresenter.detachView()
viewModel.catFact.observe(this) { text ->
textView.text = text ?: getString(R.string.empty_dash)
}
viewModel.catImage.observe(this) { catImage ->
catImage?.into(imageView)
}
viewModel.errorHandle.observe(this) { message ->
message?.let { Toast.makeText(this, message, Toast.LENGTH_SHORT).show() }
}
super.onStop()

findViewById<Button>(R.id.button).apply {
setOnClickListener {
scope.launch {
viewModel.updateImage()
}
scope.launch {
viewModel.updateFact()
}
}
}
}

override fun onDestroy() {
job.cancel()
super.onDestroy()
}
}
7 changes: 7 additions & 0 deletions app/src/main/java/otus/homework/coroutines/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package otus.homework.coroutines

object Utils {
inline fun log(block: () -> String) {
println("[Thread: ${Thread.currentThread().name}]: " + block())
}
}
74 changes: 74 additions & 0 deletions app/src/main/java/otus/homework/coroutines/data/BaseRepositiory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package otus.homework.coroutines.data

import com.google.gson.JsonSyntaxException
import org.json.JSONObject
import otus.homework.coroutines.CrashMonitor
import otus.homework.coroutines.Utils
import retrofit2.Response

abstract class BaseRepository {

protected val network = NetworkService().createRetrofit

sealed class Result<out T : Any?> {
data class Success<out T : Any?>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
}

protected suspend fun <T : Any> handleApi(
block: suspend () -> Response<T>,
): Result<T> {
try {
val response = block.invoke()
return when {
response.isSuccessful -> handleSuccess(response)
else -> handleError(response)
}
} catch (e: Exception) {
Utils.log { "[Repository]: handled error - ${e.message}" }
return Result.Error(e)
}
}

private fun <T : Any> handleSuccess(
response: Response<T>
): Result<T> {
response.body()?.let {
Utils.log { "[Repository]: request is successful" }
return Result.Success(it)
}

CrashMonitor.trackWarning("[Repository]: request success - empty body")

return Result.Error(RuntimeException("Success with empty body"))
}

private fun <T : Any> handleError(
response: Response<T>
): Result<T> {
response.errorBody()?.let { body ->
try {
val json = body.string()
val error = JSONObject(json)
val message =
if (error.has("message"))
error.getString("message")
else
"Error empty body"

CrashMonitor.trackWarning("[Repository]: request error - $message")

return Result.Error(
RuntimeException(message)
)
} catch (ignored: JsonSyntaxException) {
Utils.log { "[Repository]: JsonSyntaxException" }
return Result.Error(RuntimeException("Internal json error"))
}
}

CrashMonitor.trackWarning("[Repository]: request failed - Unknown Error")

return Result.Error(RuntimeException("Unknown Error"))
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/otus/homework/coroutines/data/CatFactResponse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package otus.homework.coroutines.data

import com.google.gson.annotations.SerializedName

data class CatFactResponse(
@SerializedName("fact")
val fact: String,
@SerializedName("length")
val length: Int
)
11 changes: 11 additions & 0 deletions app/src/main/java/otus/homework/coroutines/data/CatsApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package otus.homework.coroutines.data

import kotlinx.coroutines.Deferred
import retrofit2.Response
import retrofit2.http.GET

interface CatsApi {

@GET("fact")
fun getCatFact(): Deferred<Response<CatFactResponse>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package otus.homework.coroutines.data

import com.squareup.picasso.Picasso
import com.squareup.picasso.RequestCreator
import otus.homework.coroutines.domain.CatsRepository

class CatsRepositoryImpl : BaseRepository(), CatsRepository {

override suspend fun getCatFact(): String {
val result: Result<CatFactResponse> = handleApi {
network.getCatFact().await()
}
return when (result) {
is Result.Success -> result.data.fact
is Result.Error -> throw result.exception
}
}

override suspend fun getCatImage(): RequestCreator? {
return Picasso.get().load(NetworkService.SOURCE_CATS_IMAGES)
}
}
23 changes: 23 additions & 0 deletions app/src/main/java/otus/homework/coroutines/data/NetworkService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package otus.homework.coroutines.data

import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

class NetworkService {

companion object {
// const val SOURCE_CATS_IMAGES = "https://cataas.com/cat"
const val SOURCE_CATS_IMAGES = "https://aws.random.cat/meow"
}

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

val createRetrofit : CatsApi by lazy { retrofit.create(CatsApi::class.java) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package otus.homework.coroutines.domain

import com.squareup.picasso.RequestCreator

class CatsInteractor(
private val catsRepository: CatsRepository
) {
suspend fun getCatFact(): String =
catsRepository.getCatFact()

suspend fun getCatImage(): RequestCreator? =
catsRepository.getCatImage()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package otus.homework.coroutines.domain

import com.squareup.picasso.RequestCreator

interface CatsRepository {
suspend fun getCatFact(): String
suspend fun getCatImage(): RequestCreator?
}
Loading